我们 CRM 项目里有一个单独的 AI 运行时服务: crm-ai-runtime。
这个服务使用 Python + FastAPI + LangGraph,负责线索分析、客户总结、营销助手等 AI 场景的执行。 Java 服务负责业务数据、权限、配置以及运行记录,真正的 Agent 编排和模型调用都放在 Python Runtime 里。
功能刚跑起来的时候,其实没有什么问题。
真正麻烦的是上线以后。
"这次线索分析结果为什么不对?"
"这个请求为什么跑了这么久?"
"Agent 到底查了什么,又调用了几次模型?"
那时候我手里能看到的,基本只有最终返回结果、数据库里的 run 记录,以及一堆 Uvicorn 日志。
LangGraph 中间经过了哪些节点,工具调用了几轮,检索返回了什么,模型每一轮消耗多少 token,都很难快速定位。
临时加日志当然也能排查,但这种方式只能解决眼前的问题。
所以后面我专门把这条链路的可观测重新整理了一遍,最后接入了 Langfuse。
这篇主要记录一下我在实际项目里是怎么做的。
一、先搞清楚到底要观测什么
我的线索分析不是一次简单的 LLM 调用,而是一张 LangGraph 状态图。
大体过程是:
读取线索上下文 → 判断是否需要联网检索 → 检索客户公开信息 → 组装消息 → ReAct Agent → 保存结果 → 校验并输出
workflow 里的节点大概是这样注册的:
plaintext
builder = StateGraph( LeadAnalysisState, context_schema=AgentExecutionContext,)builder.add_node( "prepare_context", observable_node( "prepare_context", "读取线索上下文", )(lead_analysis_nodes.prepare_context),)builder.add_node( "company_web_search", observable_node( "company_web_search", "检索客户公开信息", )(lead_analysis_nodes.company_web_search),)builder.add_node("analysis_agent", analysis_agent)
真正执行图的入口在 executor.py。
一次请求进来之后,会先补齐 run_id,再根据当前场景构建 Agent、Skills、MCP 和工具上下文,然后启动 LangGraph。
plaintext
async for part in bundle.graph.astream( bundle.input_state, config=self._runtime_config(request), context=bundle.execution_context, stream_mode=[ "messages", "updates", "values", "custom", ], version="v2",): await accumulator.consume(part)
LangGraph 的 thread_id 则是按照业务维度拼出来的:
plaintext
sceneCode:tenantId:userId:conversationId
它同时也是 Postgres Checkpointer 恢复会话状态的重要标识。
所以真正需要观测的不是"一次模型请求",而是至少三层:
第一层:一次完整的 Run。
谁发起的、哪个租户、哪个场景、哪次会话、最后成功还是失败。
**第二层:LangGraph 节点。**
哪个节点慢、哪个节点报错、条件分支最后走到了哪里。
**第三层:Agent 内部执行。**
模型调用、工具调用、检索次数、token 使用情况。
把这三层想清楚以后,再选观测工具就简单很多。
二、为什么后来换成了 Langfuse
项目早期其实接过 LangSmith。
对 LangChain / LangGraph 项目来说,LangSmith 接入很方便,我原来的代码里也有一套 trace 工具。
后来决定调整,并不是因为 LangSmith 本身不好用,而是我们的 CRM 项目有几个比较明确的要求。
第一是数据边界。
CRM Agent 处理的不是公开 Demo 数据,而是真实的客户资料、跟进内容以及业务上下文。 Trace 如果把模型输入、检索内容、工具参数全部记录下来,本质上就是另外一份业务数据。
我们更希望整个观测链路部署在自己的环境里。
第二是留存控制。
我希望 AI Runtime 自己的 run 记录能够长期和 trace 对应,而不是把业务运行记录和 AI 调试记录完全割裂开。
第三是后续评测。
除了排障,我还想把用户反馈、结构化输出质量甚至后面的业务结果继续挂回这一次 trace。
最后选择 Langfuse,主要就是因为它支持自托管,同时和 LangChain / LangGraph 的 Callback 机制能够比较自然地接起来。
对我来说,这比单纯看一个漂亮的 Trace 页面重要得多。
三、部署没有花太多精力,但生产环境不能只看"能跑"
开发和内部验证阶段,我直接使用 Docker Compose。
plaintext
curl -fsSL \ https://raw.githubusercontent.com/langfuse/langfuse/main/docker-compose.yml \ -o docker-compose.ymldocker compose up -d
服务起来以后,在 Langfuse 中创建 Organization 和 Project,再给 AI Runtime 创建 public key 和 secret key。
Runtime 侧配置类似这样:
plaintext
LANGFUSE_PUBLIC_KEY=pk-lf-xxxLANGFUSE_SECRET_KEY=sk-lf-xxxLANGFUSE_BASE_URL=http://langfuse.internal:3000
这里有一点我觉得比"docker compose up"本身更值得注意:
测试环境能启动,不代表生产部署就结束了。
Trace 量上来以后,Langfuse 后面的 ClickHouse、Postgres、Redis/Valkey、对象存储都属于有状态组件。 真正放生产环境,要考虑的还是那些传统问题:磁盘、备份、容量、监控以及恢复。
AI 可观测没有脱离传统运维,它只是换了一类数据。
四、真正的接入点只有一个:LangGraph Config
Langfuse 和 LangGraph 接起来最核心的代码,其实非常少。
LangChain 本身有 Callback 机制,LangGraph 执行时又允许通过 config 把 Callback 往下传,所以我的做法没有侵入每一个节点。
plaintext
from langfuse.langchain import CallbackHandlerdef _runtime_config(self, request): config = { "configurable": { "thread_id": self._thread_id(request), }, "recursion_limit": self.settings.recursion_limit, } if trace_enabled(request): config["callbacks"] = [ CallbackHandler() ] return config
然后在一次真正的 Agent 执行外层,把业务维度传进去:
plaintext
from langfuse import propagate_attributeswith propagate_attributes( trace_name=f"crm-ai-{request.scene_code}", user_id=str(request.user_id) if request.user_id else None, session_id=str(request.conversation_id) if request.conversation_id else None, tags=[ "crm", "ai-runtime", request.scene_code, ], metadata=runtime_trace_metadata(request),): async for part in bundle.graph.astream( bundle.input_state, config=self._runtime_config(request), context=bundle.execution_context, stream_mode=[ "messages", "updates", "values", "custom", ], version="v2", ): await accumulator.consume(part)
我比较看重这里的 session_id。
我没有用 run_id,而是使用 CRM 自己的 conversation_id。
原因很简单:
一次 Run 是一次请求。
一个 Session 是一段会话。
如果同一个客户连续和营销助手交互了多轮,我排查问题时通常不是只看最后一轮,而是需要把前后几次 Agent 执行连起来看。
用 conversation_id 做 Session,刚好符合这个业务语义。
五、真正花时间的不是 Callback,而是 Metadata
Callback 接进去以后,LangGraph、模型和工具调用已经能看到不少信息。
但如果 Trace 只有技术信息:
plaintext
trace_idmodellatencytokens
实际排障还是不够。
比如运营过来给我一个业务单号,说某个客户分析有问题。
我真正想做的是直接按照业务信息把那一次执行找到,而不是先去服务器日志里查 run_id,再拿 run_id 查 Trace。
所以我把业务侧真正有用的维度统一放进了 runtime_trace_metadata()。
主要包括:
tenantId
userId
sceneCode
conversationId
业务 runId
agentCode
model
请求来源
这些字段不炫技,但实际排障时非常有用。
比如:
按 sceneCode 看某一个 AI 场景的执行情况;
按 conversationId 把一次连续会话串起来;
按 runId 从 CRM 业务运行记录直接找到 Langfuse Trace;
按 model 对比不同模型在同一场景下的耗时和 token 使用。
到这里我才开始觉得,这套东西真正从"LLM 调试工具"变成了项目里的可观测系统。
六、节点可观测我没有重新造一套 Trace
我的 workflow 里原来就有一个 observable_node 包装器。
它最初不是为 Langfuse 写的,而是为了给前端 SSE 推送图节点状态。
比如 Agent 执行到:
正在读取线索上下文......
正在检索客户公开信息......
正在生成分析结果......
这层业务名称我保留了下来。
但接入 Langfuse 以后,我没有再给每一个节点手工新建一套重复 Trace。
原因是 LangGraph 本身通过 Callback 已经能够形成执行层级。
我的原则变成了:
框架已经能采集的,不重复埋点。
只有框架不知道的业务语义,再手工补。
这样有两个好处。
一个是代码简单。
另一个是不会出现同一个节点在 Trace 里套两三层,最后观测系统本身反而变得不好看。
像联网搜索、外部 MCP 调用这一类我确实关心的 IO 操作,如果需要额外统计,再单独补 Observation 就够了。
七、生产环境我最关心的反而是脱敏
自托管之后,很容易产生一个错觉:
"反正数据都在内网,那就全部记录吧。"
我觉得这个思路风险很大。
CRM 里的客户名称、联系人信息、跟进记录、合同信息、Authorization Header、API Key,都有可能经过 Agent 上下文。
如果 Trace 把这些东西原样复制一遍,本质上等于主动再造了一套敏感数据存储。
所以我的 trace 默认不是"什么都记",而是:
默认只记录结构信息。
消息长度、上下文有哪些 key、工具名称、事件数量、结果长度、模型名称、场景、租户、Run ID 等。
原始 payload 默认关闭,需要排查时再按环境开启。
我在项目里保留了一个 trace_capture_payload 开关。
同时所有准备进入 Trace 的字典都会先经过 clean_for_trace。
类似这些字段会直接替换:
plaintext
authorizationapiKeyaccessTokenrefreshTokensecretpassword
长文本同样会做长度限制。
这部分代码其实比接 Langfuse 更早就有了。
现在回头看,我反而觉得这个顺序是对的:
先定义什么数据允许进入观测系统,再决定使用哪一个观测平台。
八、不是所有场景都需要全量 Trace
第二个上线前就要考虑的问题是采样。
像线索深度分析、客户总结这种任务,本身频率没有那么高,但执行链比较长,我希望尽量保留完整 Trace。
营销助手、普通对话这一类高频场景,如果所有请求长期全量保存,数据增长速度会明显更快。
所以我的 Trace 开关实际上不只是:
plaintext
trace_enabled = true / false
而是继续叠加场景规则:
plaintext
LEAD_ANALYZECUSTOMER_DEEP_SUMMARY
这一类核心场景可以默认开启。
高频对话则根据环境和场景做采样。
如果请求失败、发生异常,或者业务侧主动标记为需要排查,也可以考虑提高保留优先级。
这样比所有请求一刀切更符合实际项目。
九、接完以后,排问题的方式确实变了
Langfuse 对我最大的帮助,不是多了一个 Dashboard,而是改变了 Agent 问题的排查顺序。
以前遇到一个"分析慢"的问题,我通常要先从接口日志开始找。
现在第一步会直接看这一次 Trace:
总耗时是多少?
哪个 LangGraph 节点占时间最多?
是模型慢,还是外部检索慢?
Agent 一共调用了多少轮模型?
工具有没有重复调用?
有没有失败重试?
有一次线索分析明显偏慢,从 Trace 上看,真正占时间的不是 LLM,而是客户公开信息检索。
后面再顺着这个节点查代码,就比从整条请求日志开始翻快很多。
还有一类问题是 Agent 空转。
最终答案看起来可能没报错,但 Trace 展开以后会发现,同一个工具被连续调用多次,查询参数变化又不大。
这种问题单看最终 JSON 很难发现。
有了完整的模型调用和工具调用链以后,就可以再去判断到底是:
Prompt 没有限制重试策略;
Tool Description 写得不够清楚;
检索没有给 Agent 明确的终止信号;
还是 LangGraph 本身的退出条件设计有问题。
这才是我觉得 Agent 可观测真正有价值的地方。
它不是告诉你"这个接口报错了",而是告诉你Agent 是怎么一步一步跑到这个结果的。
十、几个我现在会特别注意的坑
1. 先确认 SDK 版本,再抄代码
Langfuse Python SDK 这几年接口变化比较快。
网上还能搜到很多旧版本的 CallbackHandler、Decorator 和 Trace API 写法。
我的处理方式很简单:
项目锁定明确的 SDK 版本,升级时单独处理可观测适配,不直接复制旧博客里的代码。
2. Callback 跟着一次执行走
我把 Callback 放到当前 LangGraph invocation 的 config 里,而不是为了省几行代码把所有东西做成全局魔法。
这样哪些请求开启 Trace、带哪些 metadata,都在 Runtime 执行层有明确入口。
3. Metadata 只放索引信息
Metadata 不是业务对象的备份区。
我现在更倾向于只放 ID、类型、数量、长度、状态这种适合搜索和聚合的信息。
大段客户资料、完整 Prompt、完整检索结果,要不要采集应该由单独策略控制。
4. Trace ID 最好和业务 Run 建立关系
这一点是我后面准备继续完善的。
CRM 本身已经有 agent_run 运行记录。
最理想的状态是:
业务后台看到一次 Agent Run
↓
直接跳到对应 Langfuse Trace
Langfuse 看到异常 Trace
↓
反查 CRM 里的业务请求
这样业务系统和 AI 观测系统才真正连起来。
十一、下一步:从排障走向评测
到目前为止,我主要解决的还是运行时可观测:
Trace、Session、节点执行、模型调用、工具调用、token 和异常。
但我觉得这只是第一步。
后面更值得做的是把业务结果重新回填到 AI 执行记录中。
比如线索分析给出了一个意向等级。
真正有意义的问题不是:
"模型当时为什么输出 A?"
而是:
"模型判断为高意向的这些线索,后面到底有多少真的进入了商机?"
如果后续真实业务结果能够通过 Score 或自己的评测链路重新关联到 Trace, 那么 Langfuse 的用途就从"排查一次 Agent 为什么跑错",继续往"这个 Agent 长期到底有没有效果"发展了。
这个方向对我来说,比单纯做更多日志更有价值。
最后
我在一线科技企业深耕十二载,见证过太多因技术更迭而跃迁的案例。那些率先拥抱 AI 的同事,早已在效率与薪资上形成代际优势,我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在大模型的学习中的很多困惑。
我整理出这套 AI 大模型突围资料包:
- ✅AI大模型学习路线图
- ✅Agent行业报告
- ✅100集大模型视频教程
- ✅大模型书籍PDF
- ✅DeepSeek教程
- ✅AI产品经理入门资料
完整的大模型学习和面试资料已经上传带到CSDN的官方了,有需要的朋友可以扫描下方二维码免费领取【保证100%免费】👇👇

为什么说现在普通人就业/升职加薪的首选是AI大模型?
人工智能技术的爆发式增长,正以不可逆转之势重塑就业市场版图。从DeepSeek等国产大模型引发的科技圈热议,到全国两会关于AI产业发展的政策聚焦,再到招聘会上排起的长队,AI的热度已从技术领域渗透到就业市场的每一个角落。

智联招聘的最新数据给出了最直观的印证:2025年2月,AI领域求职人数同比增幅突破200% ,远超其他行业平均水平;整个人工智能行业的求职增速达到33.4%,位居各行业榜首,其中人工智能工程师岗位的求职热度更是飙升69.6%。
AI产业的快速扩张,也让人才供需矛盾愈发突出。麦肯锡报告明确预测,到2030年中国AI专业人才需求将达600万人,人才缺口可能高达400万人,这一缺口不仅存在于核心技术领域,更蔓延至产业应用的各个环节。


资料包有什么?
①从入门到精通的全套视频教程⑤⑥
包含提示词工程、RAG、Agent等技术点

② AI大模型学习路线图(还有视频解说)
全过程AI大模型学习路线

③学习电子书籍和技术文档
市面上的大模型书籍确实太多了,这些是我精选出来的

④各大厂大模型面试题目详解

⑤ 这些资料真的有用吗?
这份资料由我和鲁为民博士共同整理,鲁为民博士先后获得了北京清华大学学士和美国加州理工学院博士学位,在包括IEEE Transactions等学术期刊和诸多国际会议上发表了超过50篇学术论文、取得了多项美国和中国发明专利,同时还斩获了吴文俊人工智能科学技术奖。目前我正在和鲁博士共同进行人工智能的研究。
所有的视频教程由智泊AI老师录制,且资料与智泊AI共享,相互补充。这份学习大礼包应该算是现在最全面的大模型学习资料了。
资料内容涵盖了从入门到进阶的各类视频教程和实战项目,无论你是小白还是有些技术基础的,这份资料都绝对能帮助你提升薪资待遇,转行大模型岗位。


智泊AI始终秉持着"让每个人平等享受到优质教育资源 "的育人理念,通过动态追踪大模型开发、数据标注伦理等前沿技术趋势,构建起"前沿课程+智能实训+精准就业"的高效培养体系。
课堂上不光教理论,还带着学员做了十多个真实项目。学员要亲自上手搞数据清洗、模型调优这些硬核操作,把课本知识变成真本事!


如果说你是以下人群中的其中一类,都可以来智泊AI学习人工智能,找到高薪工作,一次小小的"投资"换来的是终身受益!
应届毕业生:无工作经验但想要系统学习AI大模型技术,期待通过实战项目掌握核心技术。
零基础转型:非技术背景但关注AI应用场景,计划通过低代码工具实现"AI+行业"跨界。
业务赋能 突破瓶颈:传统开发者(Java/前端等)学习Transformer架构与LangChain框架,向AI全栈工程师转型。
👉获取方式:
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓**
