LangSmith与Langfuse-AI应用的可观测性
传统 Web 应用的监控日志包含足够的调试信息:HTTP 状态码、响应时间、报错堆栈。但对于 AI 应用,当用户反馈"回答质量很差,完全答非所问"时,HTTP 日志里只有 `200 OK`,响应时间 3.2 秒。服务正常在跑,没有报错,但问题到底出在哪一步?是向量检索没找到相关文档?是找到了但 Prompt 写得不好?还是 LLM 本身的问题?
LangSmith 与 Langfuse:AI 应用的可观测性
传统 Web 应用的监控日志包含足够的调试信息:HTTP 状态码、响应时间、报错堆栈。但对于 AI 应用,当用户反馈"回答质量很差,完全答非所问"时,HTTP 日志里只有 200 OK,响应时间 3.2 秒。服务正常在跑,没有报错,但问题到底出在哪一步?是向量检索没找到相关文档?是找到了但 Prompt 写得不好?还是 LLM 本身的问题?
传统日志回答不了这个问题。
1.1 为什么 AI 应用需要可观测性
AI应用可观测性三层架构及LangSmith与Langfuse工具对比
传统 Web 应用的调试逻辑很简单:看日志,找报错,定位代码行,修复。业务逻辑是确定性的,同样的输入永远得到同样的输出。
AI 应用不一样。一个 RAG 问答系统内部的调用链路可能是这样的:
每个节点都可能出问题,而且是概率性的问题:同一个问题,今天回答得好,明天可能因为检索到了不同的文档而回答得差。
调试 AI 应用需要能看到每次请求的完整链路:用户问了什么 → 改写成了什么 → 检索到了哪些文档(完整内容)→ 实际发给 LLM 的 Prompt 是什么 → LLM 返回了什么 → 最终输出是什么 → 花了多少 token → 每个环节耗时多少。
这就是 AI 应用的可观测性。
1.2 LangSmith:官方解决方案
LangSmith 是 LangChain 官方提供的可观测性平台,和 LangChain 生态无缝集成。
1.2.1 接入方式
零代码侵入,设置环境变量就自动开启追踪:
export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_ENDPOINT="https://api.smith.langchain.com"
export LANGCHAIN_API_KEY="ls__your_api_key"
export LANGCHAIN_PROJECT="my-rag-app"
这四行环境变量设置完成后,所有 LangChain 调用会自动被追踪,不需要修改任何代码。
1.2.2 核心功能
Trace(链路追踪)
每次请求都会生成一条完整的 Trace,展示整个调用链路的树状结构。可以看到:
- 每个 LLM 调用的完整输入(System Prompt + Human Message)
- LLM 的完整输出
- 每个环节的耗时
- token 消耗(input tokens、output tokens、总费用)
- 如果用了工具,工具的入参和返回值
这对调试来说价值极大。用户反馈某次回答质量差,找到那条 Trace,一眼就能看到是检索到的文档完全不相关,还是 Prompt 有问题,还是 LLM 输出就歪了。
Evaluate(评估)
可以对历史 Trace 打分,也可以配置自动评估器(用另一个 LLM 来评估输出质量)。评估结果会聚合成指标,让你看到整体质量趋势。
Dataset(测试集管理)
从生产 Trace 里挑选典型问题,构建测试集。每次改了 Prompt 或换了模型,在测试集上跑一遍,对比指标变化,确保没有回退。
Playground
直接在 LangSmith 界面上修改 Prompt,用保存的输入测试,不需要改代码重新部署。这对快速迭代 Prompt 非常有用。
1.2.3 核心指标
LangSmith 会自动收集:
- 延迟:p50/p95/p99 分位数,识别慢请求
- Token 消耗:input tokens、output tokens,控制成本
- 成功率:是否有报错或超时
- 每次调用的完整输入输出
1.2.4 接入代码示例
# 只需设置环境变量,其余代码不需要改动
import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "ls__your_api_key"
os.environ["LANGCHAIN_PROJECT"] = "rag-production"
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
llm = ChatOpenAI(model="gpt-4o-mini")
# 这次调用会被自动追踪,LangSmith 里能看到完整记录
response = llm.invoke([
SystemMessage(content="你是一个技术助手"),
HumanMessage(content="什么是向量数据库")
])
print(response.content)
如果想手动添加自定义 metadata,用 @traceable 装饰器:
from langsmith import traceable
@traceable(name="RAG问答", metadata={"version": "v2"})
def rag_answer(question: str, user_id: str) -> str:
# 这个函数的完整调用会被追踪
# metadata 里可以加业务信息,比如 user_id、AB 测试分组
docs = retriever.invoke(question)
context = "\n".join([d.page_content for d in docs])
prompt = f"""基于以下文档回答问题:
{context}
问题:{question}"""
response = llm.invoke([HumanMessage(content=prompt)])
return response.content
1.2.5 缺点
LangSmith 的主要问题:
闭源,数据在境外。 所有 Prompt、用户问题、LLM 输出都会发到 LangChain 的服务器(美国)。对于企业内部知识库或者涉及敏感信息的场景,这是硬伤。
免费额度有限。 免费版每月 5000 条 Trace,超过要付费。
1.3 Langfuse:开源替代方案
Langfuse 是 LangSmith 的开源替代,功能高度类似,但支持自部署。
1.3.1 和 LangSmith 的功能对比
| 功能 | LangSmith | Langfuse |
|---|---|---|
| 链路追踪 | 有 | 有 |
| 评估 | 有 | 有 |
| 测试集 | 有 | 有 |
| Prompt 管理 | 有 | 有 |
| 自部署 | 不支持 | 支持(Docker) |
| 开源 | 否 | 是 |
| 免费额度 | 5000 条/月 | 自部署无限制 |
1.3.2 接入方式
pip install langfuse
两种接入方式:
方式一:通过 LangChain CallbackHandler(零代码侵入)
import os
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..."
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..."
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com" # 或自部署地址
from langfuse.callback import CallbackHandler
langfuse_handler = CallbackHandler()
# 在 invoke 时传入 callback
response = llm.invoke(
[HumanMessage(content="什么是RAG")],
config={"callbacks": [langfuse_handler]}
)
方式二:手动 Trace(更灵活,可以追踪非 LangChain 代码)
from langfuse import Langfuse
langfuse = Langfuse()
def rag_answer(question: str) -> str:
# 创建一个 Trace
trace = langfuse.trace(
name="rag-question",
input={"question": question},
metadata={"user_id": "user123"}
)
# 追踪检索步骤
retrieval_span = trace.span(name="vector-retrieval")
docs = retriever.invoke(question)
retrieval_span.end(output={"doc_count": len(docs)})
# 追踪 LLM 步骤
generation = trace.generation(
name="llm-call",
model="gpt-4o-mini",
input=[{"role": "user", "content": question}]
)
response = llm.invoke([HumanMessage(content=question)])
generation.end(
output=response.content,
usage={
"input": response.response_metadata["token_usage"]["prompt_tokens"],
"output": response.response_metadata["token_usage"]["completion_tokens"]
}
)
# 关闭 Trace
trace.update(output={"answer": response.content})
return response.content
1.3.3 自部署
用 Docker Compose 在自己服务器上跑 Langfuse:
# 下载 docker-compose 配置
git clone https://github.com/langfuse/langfuse.git
cd langfuse
# 配置环境变量
cp .env.example .env
# 编辑 .env,设置 NEXTAUTH_SECRET、SALT 等
# 启动
docker compose up -d
然后把 LANGFUSE_HOST 改成自己的服务器地址,数据就不出内网了。
1.4 关键指标怎么看
有了可观测性工具,需要关注以下几个指标:
延迟分位数
不要只看平均值,要看 p95 和 p99。平均延迟 3 秒,但 p99 是 20 秒,说明有少部分请求非常慢,这些慢请求往往对应着检索质量很差(召回了大量无关文档,Prompt 变得很长)的情况。
Token 消耗趋势
每次调用消耗的 token 突然增加,说明可能是 Prompt 变长了(比如检索到的文档更多或更长),或者有 Prompt 注入攻击(用户输入了超长内容)。
Faithfulness 低的请求
结合 RAGAS 评估,把 Faithfulness 分数低的请求找出来,看完整链路,通常能发现检索问题或 Prompt 问题。
错误类型分布
超时、上下文过长(token limit exceeded)、内容过滤触发,这些错误的分布能帮助确定优化方向。
1.5 一个实际的调试场景
用户反馈:"问关于退款政策的问题,给出了一个完全错误的答案。"
有了 LangSmith,调试步骤:
- 在 LangSmith 里搜索关键词"退款",找到那条 Trace
- 查看检索结果:系统找到了 3 个文档,但其中 2 个是关于"退货"的,1 个是关于"发货"的,没有一个是关于"退款政策"的
- 根本原因确认:向量检索的语义理解有问题,"退款"和"退货"被当成了相似文本
没有可观测性,可能会误判为 Prompt 问题,花了几天调整 Prompt,而真正的根本原因是 embedding 模型对这两个词的区分不够精细。
1.6 上线前就要接好
可观测性接入的成本很低:LangSmith 设置 4 个环境变量,Langfuse 多装一个包,代码几乎不用改。这个成本在上线前就花掉,上线后的排查效率会高很多。
AI 应用的调试和传统应用不一样,不能只靠直觉和经验,需要数据。可观测性工具提供的就是这些数据:完整的链路追踪、token 消耗、延迟分布、质量评估。上线后遇到问题时,有了这些数据才能快速定位根因。