version 是不可变快照,label 是可移动指针。 每次 create_prompt 同名 prompt 都会生成一个递增的 version(v1、v2、v3…),版本内容永不改变;label(如 productionstaging)是贴在某个 version 上的标签,可以随时撕下来贴到另一个 version。生产代码永远按 label 拉取,发布即「移动 label」,回滚即「把 label 移回旧 version」——代码一行不改。

一、两种 Prompt 类型:text 与 chat

Langfuse 的 prompt 有两种 typetext(单段字符串模板,适合 completion 风格或作为 system 片段)和 chat(消息数组,每条含 rolecontent,对应 OpenAI/Anthropic 的 messages 格式)。模板变量统一用双花括号 {{variable}} 占位,由 compile() 在运行时填充。

# 安装最新 v3 SDK(v3 起 client 为单例,用 get_client 获取)
pip install -U "langfuse>=3.0.0" openai

# 三个环境变量(项目 Settings 页生成 key)
export LANGFUSE_PUBLIC_KEY="pk-lf-..."
export LANGFUSE_SECRET_KEY="sk-lf-..."
export LANGFUSE_HOST="https://cloud.langfuse.com"   # 自托管换成你的地址
from langfuse import get_client

langfuse = get_client()  # 自动读取上面三个环境变量,进程内单例

# ---------- 1) 创建一个 text 类型 prompt ----------
langfuse.create_prompt(
    name="summary-prompt",                 # prompt 的唯一名字(跨版本不变)
    type="text",
    prompt="请用一句话总结下面的文本,保持 {{tone}} 的语气:\n\n{{document}}",
    labels=["production"],                  # 创建时直接打上 production 标签
    config={                                # config 是任意 JSON,随版本一起存
        "model": "gpt-4o-mini",
        "temperature": 0.3,
    },
)

# ---------- 2) 创建一个 chat 类型 prompt ----------
langfuse.create_prompt(
    name="support-agent",
    type="chat",
    prompt=[
        {"role": "system", "content": "你是 {{product}} 的客服,语气友好、回答简洁。"},
        {"role": "user", "content": "{{question}}"},
    ],
    labels=["production"],
    config={"model": "gpt-4o", "temperature": 0.7},
)

print("两个 prompt 已创建并发布到 production")

二、拉取 Prompt:label / version、缓存与 fallback

运行时用 get_prompt(name) 拉取。不传任何选择参数时默认拉 production label 的版本。生产环境真正要关心的是另外两个参数:cache_ttl_seconds(本地缓存有效期)和 fallback(Langfuse 不可达时的兜底内容)。配置好这两个,拉 prompt 就不会给主链路加延迟,也不会因为 Langfuse 抖动而阻断业务。

from langfuse import get_client
from openai import OpenAI

langfuse = get_client()
oai = OpenAI()

# ---------- 按 label 拉取(线上推荐写法)----------
# 默认就是 label="production";显式写出来意图更清晰
prompt = langfuse.get_prompt(
    "support-agent",
    label="production",
    # 缓存 5 分钟:首次拉远端,之后 300s 内命中本地缓存(0 延迟);
    # 过期后后台异步刷新,不阻塞当前请求 —— 默认 60s,生产可调大
    cache_ttl_seconds=300,
    # 兜底:万一 Langfuse 不可达且本地无缓存,用它而不是抛异常阻断业务
    # chat 类型 fallback 必须是消息数组;text 类型则传字符串
    fallback=[
        {"role": "system", "content": "你是客服,请简洁回答。"},
        {"role": "user", "content": "{{question}}"},
    ],
)

# ---------- 按 version 拉取(调试/复现特定版本时用)----------
prompt_v2 = langfuse.get_prompt("support-agent", version=2)

# ---------- 按自定义 label 拉取(灰度环境)----------
prompt_staging = langfuse.get_prompt("support-agent", label="staging")

print("当前版本号:", prompt.version, "| 是否命中 fallback:", prompt.is_fallback)
拉取方式写法适用场景升级行为
按 production labelget_prompt(name)生产主链路(默认)label 移动后自动生效,代码不改
按自定义 labelget_prompt(name, label="staging")灰度 / A-B / 预发环境对应 label 移动后生效
按 version 锁定get_prompt(name, version=3)调试、复现 bug、回归测试永远拿到该不可变快照,绝不漂移
latest labelget_prompt(name, label="latest")拿最新创建的版本(含未发布)每次创建都会变,慎用于生产
口诀线上跟 label 走,调试钉 version 死

三、compile 编译模板变量

拉到的 prompt 还是带 {{变量}} 的模板,调 compile(**变量) 把值填进去。text 类型返回编译后的字符串,chat 类型返回可直接喂给 OpenAI/Anthropic 的 messages 列表。模型参数从 prompt.config 取,做到「Prompt 文本和模型配置一起版本化」。

# ---------- chat 类型:compile 后直接喂给 openai ----------
messages = prompt.compile(
    product="Langfuse",
    question="怎么回滚一个 prompt 版本?",
)
# messages 形如:
# [{"role": "system", "content": "你是 Langfuse 的客服,语气友好、回答简洁。"},
#  {"role": "user",   "content": "怎么回滚一个 prompt 版本?"}]

response = oai.chat.completions.create(
    model=prompt.config["model"],            # 模型也来自版本化的 config
    temperature=prompt.config["temperature"],
    messages=messages,
)
print(response.choices[0].message.content)

# ---------- text 类型:compile 返回字符串 ----------
text_prompt = langfuse.get_prompt("summary-prompt")   # 默认 production
compiled_text = text_prompt.compile(
    tone="专业",
    document="Langfuse 是一个开源的 LLM 工程平台……",
)
print(compiled_text)  # 已填好变量的完整字符串

Prompt Management 真正的价值在闭环:把拉到的 prompt 对象关联到对应的 generation,Langfuse 就能在 Dashboard 里按 prompt 名 + 版本 聚合调用次数、Token、成本、延迟,以及后续打的质量分数。这样「v3 比 v2 贵了 20% 但准确率高 5 个点」这种判断才有数据支撑。

from langfuse import get_client, observe
from langfuse.openai import openai   # 用 langfuse 包装版,自动捕获 generation

langfuse = get_client()

@observe()
def answer(question: str) -> str:
    # 1) 拉取已版本化的 prompt(自带缓存与 fallback)
    prompt = langfuse.get_prompt("support-agent", label="production")

    # 2) compile 填充变量
    messages = prompt.compile(product="Langfuse", question=question)

    # 3) 调用模型,关键:把 prompt 对象通过 langfuse_prompt 关联到这次 generation
    #    这样该 generation 会被标注上 prompt 名 + version,可在 Dashboard 按版本聚合
    resp = openai.chat.completions.create(
        model=prompt.config["model"],
        temperature=prompt.config["temperature"],
        messages=messages,
        langfuse_prompt=prompt,          # <-- link prompt -> generation
    )
    return resp.choices[0].message.content

print(answer("怎么做 prompt 灰度发布?"))

# 如果你不用 langfuse.openai 包装,而是手动建 generation,则这样关联:
with langfuse.start_as_current_generation(
    name="manual-gen",
    model=prompt.config["model"],
    prompt=prompt,                       # <-- 同样把 prompt 对象传进来即可关联
) as gen:
    gen.update(output="...模型输出...")

langfuse.flush()  # 脚本/短任务结束前 flush,确保事件上报
推荐做法
  • 生产代码统一 get_prompt(name, label="production"),发布即移动 label,回滚即把 label 移回旧版本
  • 为 get_prompt 设置合理的 cache_ttl_seconds(如 60~300s)并提供 fallback,让主链路零额外延迟、宕机不阻断
  • 每次调用都用 langfuse_prompt= / prompt= 把版本关联到 generation,积累版本级指标
  • 把 model、temperature 等放进 prompt 的 config,和文本一起版本化,避免代码与 Prompt 配置漂移
不推荐
  • 不要把 Prompt 文本继续硬编码在代码里——那样改一个字就得发版,也没有版本与指标
  • 不要在生产用 label="latest",它会随任何一次 create 漂移,等于没有发布控制
  • 不要把 cache_ttl_seconds 设成 0 来「保证最新」——会给每个请求加一跳同步网络往返
  • 不要忽略 is_fallback=True:那是降级信号,不监控就等于在裸奔
常见误区
  • compile 缺变量不报错,错拼变量名会把 {{var}} 字面量发给模型
  • chat 类型的 fallback 必须是消息数组、text 类型必须是字符串,类型不匹配会出错
  • 短生命周期脚本忘记 flush(),事件还在队列里进程就退出了,Dashboard 看不到数据

在 Langfuse Dashboard 的 Prompts 页能看到 production label 指向预期 version,且对应 generation 能按版本展示成本/Token/延迟,回滚只需在 UI 移动 label、代码零改动。

拉 prompt 让接口变慢了

典型表现
接入 get_prompt 后每个请求都多了几十到几百毫秒
判断标准
P99 延迟里 get_prompt 的网络耗时趋近于 0
解决方向
设置 cache_ttl_seconds(如 300),首次后命中本地缓存,过期靠后台异步刷新;切勿设为 0

Langfuse 抖动导致业务报错

典型表现
Langfuse 短暂不可达时,get_prompt 抛异常把主链路打挂
判断标准
Langfuse 宕机时业务仍能用兜底 prompt 正常返回
解决方向
给 get_prompt 传 fallback(chat 传数组 / text 传字符串),并对 prompt.is_fallback 加告警

说不清线上跑的是哪版 prompt

典型表现
改了 prompt 后效果变化,但无法定位是哪个版本、也无法对比
判断标准
每条 generation 都带 prompt 名 + version,可在 Dashboard 按版本聚合
解决方向
调用时用 langfuse_prompt=prompt(包装版)或 prompt=prompt(手动 generation)做关联

改了 prompt 没生效

典型表现
在 UI 创建了新版本,但线上还在跑旧内容
判断标准
新版本被打上 production label 后,缓存过期或重启即生效
解决方向
确认新版本已贴 production label(移动指针),并等待 cache_ttl_seconds 过期或重启进程清缓存

Prompt 不是字符串常量,而是带版本、可灰度、可回滚、可度量的产品资产——这正是它该从代码里搬进 Langfuse 的理由。

— Langfuse Prompt Management 设计理念