最近在做一个 Text-to-SQL 的 Agent 项目 EasySQL,这篇文章就把我langfuse的session使用整理一下,代码都是从项目里直接搬的,欢迎大佬们高抬贵手点点star,共建、交流。
Langfuse官方文档的原文定义
Many interactions with LLM applications span multiple traces and observations. Sessions in Langfuse are a special way to group these observations across traces together and see a simple session replay of the entire interaction. Optionally, traces can be grouped into sessions. Sessions are used to group traces that are part of the same user interaction. A common example is a thread in a chat interface.
翻译一下:一次用户与 LLM 应用的交互往往不只一个请求。比如用户在聊天界面中连续问了 3 个问题,每个问题是一个独立的 Trace(一次完整的 LLM调用链路),但它们属于同一次会话。Session 就是把这些相关的 Trace 归到一组。
Session
| session_id 格式 | 任意 US-ASCII 字符串,小于 200 字符 |
|---|---|
| 归组逻辑 | 所有携带相同 session_id 的 Trace 自动归入同一个 Session |
| 创建时机 | 无需显式创建 Session 对象,第一个携带该 session_id 的 Trace 出现时自动创建 |
| Trace 的关系 | 一个 Session 包含多个 Trace,一个 Trace 只属于一个 Session |
项目举例
没有Session时:这两个Trace(一个会话中的两个问答) 在Langfuse Dashboard中是散落的,你不知道它们之间有关联。有Session时:点进某个Session,能看到完整的多次对话回放——用户从第1个问题到第3个问题的完整链路。
无session的情况下,所有的trace都集中展示无法筛选
有session的情况,可以根据session展示,我这里就可以通过同个session来观察第二个问题有没有取得第一个问题的历史对话上下文,由此来判断代码中的处理是否合理
也可以在Dashboards中筛选
代码实现
核心代码在 query_service.py。也就是创建一个会话session id的地方,由于项目中已经设置了session id来区分不同的会话,所以这里可以直接复用,保证langfuse中的session id和数据库中的会话session id一致。这样做的好处是:当在 Langfuse Dashboard 中看到某个Session 有问题时,可以直接用这个 ID 去业务数据库中查对应的会话记录,两边的 ID 是一致的。(这也是我认为最重要的一点)
def _make_config(self, session_id: str, thread_id: str | None = None) -> RunnableConfig:
effective_thread_id = thread_id or session_id
config: RunnableConfig = {"configurable": {"thread_id": effective_thread_id}}
if self.callbacks:
config["callbacks"] = self.callbacks
config["metadata"] = { # ← 关键
"langfuse_session_id": session_id, # ← Session 隔离
"langfuse_tags": ["text2sql"], # ← 标签
}
logger.debug(f"LangFuse callbacks attached: {len(self.callbacks)} handler(s)")
return config
async for chunk in self.graph.astream(
input_state,
self._make_config(session.session_id, effective_thread_id),
stream_mode=["updates", "custom"],
):
工作原理
这里用的是 Langfuse 官方推荐的 metadata 传参方式。官方文档原文:
With the Python SDK, you can set trace attributes dynamically via metadata fields in chain invocation by passing a config dictionary.This is the simplest approach for adding trace context to your LangChain executions without additional setup.
官方示例:
response = chain.invoke(
{"topic": "cats"},
config={
"callbacks": [langfuse_handler],
"metadata": {
"langfuse_user_id": "random-user",
"langfuse_session_id": "random-session",
"langfuse_tags": ["random-tag-1", "random-tag-2"]
}
}
)
Session带来的监控意义
Session 级别的聚合分析
| 每个 Session 的 Trace 数量 | 用户平均问几轮才完成任务 | ** 轮次多 = 可能需要优化追问体验** |
|---|---|---|
| 每个 Session 的 总 Token | 一次完整交互的总消耗 | 评估单次用户交互的成本 |
| 每个 Session 的 总延迟 | 用户完成任务的总等待时间 | 评估端到端的用户体验 |
| 每个 Session 的 失败 Trace 比例 | 多轮中有几轮出错 | 定位哪些类型的追问容易失败 |
对比没有session
| 场景 | 没有 Session | 有 Session |
|---|---|---|
| 查看某用户的完整交互 | 需要自己按时间排序、手动关联 | 直接点进 Session 看回放 |
| 统计"用户平均几轮完成任务" | 无法统计 | Dashboard 直接提供 |
| 分析"追问时 SQL 质量是否下降" | 需要导出数据写脚本 | 按 Session 内的 Trace 顺序直接对比 |
| 定位"用户反馈体验差"的问题 | 只能逐条 Trace 查看 | 拿到 session_id 直接看全过程 |
总结
session是一个很小的知识点,但是使用的好对我们整个agent开发带来的增益是巨大。
以上所有代码示例均来自我的开源项目 EasySQL —— 一个 Text-to-SQL 智能体分析应用,项目地址:github.com/zaizaizhao/…。项目主要技术栈包括:
- LangGraph:构建多步骤 Agent 状态机,支持条件路由、Human-in-the-Loop 澄清、SQL 生成→验证→修复的迭代循环
- LangChain:LLM 调用抽象与 RunnableConfig 配置传递
- Langfuse:Callback + 手动 Span 双模式可观测性,实现全链路追踪与业务汇总
- PostgreSQL + AsyncPostgresSaver:LangGraph Checkpointer 状态持久化,支持多轮对话上下文
- FastAPI + Uvicorn:异步 API 服务层,提供流式 SSE 响应
- Milvus:向量数据库,用于 Schema 语义检索
- Neo4j: 图数据库,用于知识图谱构建
- Pydantic Settings:类型安全的配置管理,支持环境变量覆盖
项目示例
欢迎 Star ⭐ 和交流、共建!