
上一篇我们把评估体系立起来了golden set、关键词 工具双检、CI 通过率门槛。评估能告诉你结果对不对。但 FAIL case 往桌上一扔下一句问题更扎心为什么错了卡在哪一步是 Prompt 飘了还是工具参数传歪了只看最终content你只能猜。这时候需要的是可观测性——把 agent ↔ tools 的每一步摊开Tracing、结构化日志、关键指标。老规矩本文以官网最新文档核对过LangSmith Observability、Trace with LangChain。createAgent开箱就能挂 LangSmith本篇本地先用stream/streamEvents看清轨迹不必先有 API Key联调/生产再开自动上报。别再抄createReactAgent。一、Agent 调试为什么特别难传统 Web一次请求 → 一条调用链日志基本线性。Agent 不是难点说明多轮消息messages越积越长要看见每轮 LLM 的入参 / 出参工具调用链agent → tools → agent 循环每步 args 与 result 都得可见条件分支addConditionalEdges走了哪条路状态演进Checkpointer、interrupt/resume同一thread_id多次 invoke 怎么变的异步与流式stream/streamEvents的事件顺序 vs 最终 State没有 trace你往往只能console.log最后一轮答案——ReAct 中间那几跳全是黑盒。Eval FAIL为什么错?本地 streamEventsLangSmith Trace结构化日志还原决策路径二、本地先看清stream/streamEvents没开 LangSmith 也能调试。Compiled graphcreateAgent底层就是支持流式接口stream按模式吐 State 增量 / 更新适合看节点推进streamEvents更细的事件流LLM 起止、tool 起止等——本地还原轨迹的利器下面用createAgent 天气工具把轨迹打到控制台import{createAgent}fromlangchain;import{ChatOllama}fromlangchain/ollama;import{tool}fromlangchain/core/tools;import*aszfromzod;constgetWeathertool(async({city}:{city:string})${city}晴25°C,{name:get_weather,description:查询城市天气,schema:z.object({city:z.string()}),});constllmnewChatOllama({model:qwen2.5:7b,temperature:0});constagentcreateAgent({model:llm,tools:[getWeather],systemPrompt:需要天气信息时调用 get_weather。,});constinput{messages:[{role:user,content:北京天气怎么样}],};// 细粒度事件看清 LLM / tool 何时进出forawait(constevofagent.streamEvents(input,{version:v2})){if(ev.eventon_chat_model_end){constmsgev.data?.output;consttoolsmsg?.tool_calls?.map((c:{name:string})c.name);console.log([llm],tools?.length?tool_calls${tools}:final text);}if(ev.eventon_tool_end){console.log([tool],ev.name,→,String(ev.data?.output).slice(0,80));}}你会大致看到先 LLM 决定调get_weather→ tool 返回「北京晴25°C」→ 再 LLM 吐出人话总结。这比只盯最终content有用一百倍。手写StateGraphToolNode博客 7同样有stream/streamEvents——心智一模一样编排层出事件零件层还是 LangChain。三、LangSmith Tracing环境变量就能开LangSmith 是 LangChain 生态的可观测平台。对createAgent/ LangGraph设好环境变量后正常invoke就会自动上报——不必给业务代码撒满埋点。必配环境变量变量作用LANGSMITH_TRACING设为true启用自动 traceLANGSMITH_API_KEY账号 API Key只放.env别进 GitLANGSMITH_PROJECT项目名不设则进defaultLANGSMITH_ENDPOINT默认美区https://api.smith.langchain.comEU 等区域要改对应 endpoint应用入口集中读 env 即可dotenv / 部署平台注入都行// 应用启动最早处API Key 从 .env 注入勿写死在代码里process.env.LANGSMITH_TRACING??true;process.env.LANGSMITH_PROJECT??nodejs-agent-blog;// LANGSMITH_API_KEY 已由环境提供import{createAgent}fromlangchain;import{ChatOllama}fromlangchain/ollama;import{tool}fromlangchain/core/tools;import*aszfromzod;constgetWeathertool(async({city}:{city:string})${city}晴25°C,{name:get_weather,description:查询城市天气,schema:z.object({city:z.string()}),});constagentcreateAgent({model:newChatOllama({model:qwen2.5:7b,temperature:0}),tools:[getWeather],});// 开启 Tracing 后这次 invoke 自动出现在 LangSmithconstresultawaitagent.invoke({messages:[{role:user,content:北京天气怎么样}]},{configurable:{thread_id:trace-demo-1},metadata:{source:blog-12,user_query_preview:北京天气,},});Trace 长什么样Root: agent.invokeChild: ChatOllamaChild: get_weatherChild: ChatOllamaRoot Run一次agent.invoke/graph.invokeChild Run节点里的 LLM 调用、tool 执行thread_id/ metadata按会话筛选、按来源过滤和 Checkpointer 的会话钥匙对齐最好JS 里多记一句后台回调官网对 LangChain.js LangSmith 的建议环境建议普通 Node 长进程LANGCHAIN_CALLBACKS_BACKGROUNDtrue降低等待上报的延迟Serverless / 短命函数LANGCHAIN_CALLBACKS_BACKGROUNDfalse等 trace 刷完再退出否则请求结束了 span 还没送出去非 LangChain 调用也可以用 LangSmith 的traceable包一层——本篇不展开需要时查官网即可。四、结构化日志和 Tracing 互补Tracing 擅长「按请求还原整棵调用树」结构化日志擅长「按事件聚合、进 ELK/Loki、做告警」。别互相替代。建议每条日志至少有字段说明timestampISO 8601levelinfo / warn / errorthreadId会话 ID与 Checkpointer 一致runId单次 invoke 唯一 ID可选便于对齐 LangSmithevent如agent_start、tool_call、tool_result、agent_end、agent_errortoolName工具相关事件时带上durationMsagent_end/tool_result时记录耗时import{createAgent}fromlangchain;import{ChatOllama}fromlangchain/ollama;interfaceLogEvent{timestamp:string;level:info|warn|error;threadId:string;event:string;toolName?:string;durationMs?:number;[key:string]:unknown;}functionlog(event:OmitLogEvent,timestamp|level{level?:LogEvent[level]}){constentry:LogEvent{timestamp:newDate().toISOString(),level:event.level??info,...event,};console.log(JSON.stringify(entry));}asyncfunctioninvokeWithLogging(agent:ReturnTypetypeofcreateAgent,input:{messages:{role:string;content:string}[]},threadId:string){conststartDate.now();log({threadId,event:agent_start});try{constresultawaitagent.invoke(input,{configurable:{thread_id:threadId},});log({threadId,event:agent_end,durationMs:Date.now()-start,});returnresult;}catch(err){log({threadId,event:agent_error,level:error,durationMs:Date.now()-start,error:String(err),});throwerr;}}constagentcreateAgent({model:newChatOllama({model:qwen2.5:7b,temperature:0}),tools:[],});awaitinvokeWithLogging(agent,{messages:[{role:user,content:你好}]},session-abc-123);想在日志里看到tool_call/tool_result在上一节的streamEvents循环里命中on_tool_start/on_tool_end时同样log({ event: tool_call, toolName: ... })即可——一套事件两种出口控制台调试 JSON 采集。五、关键指标点到为止生产除了单次 trace还要聚合指标用途latencyp50 / p95发现慢查询token 用量控成本可从 trace / 回调里取tool 成功率工具挂了还是模型乱调error 率超时、429、模型不可用拦截率安全 Guard 挡住了多少后文安全篇大盘用 Prometheus / Grafana 或云厂商监控都行本篇不搭整套运维。六、本地 vs 生产怎么选兵器场景推荐手段开发机stream/streamEvents 结构化console可不接 LangSmith联调 / 预发LangSmith Tracing JSON 日志生产LangSmith或等价 APM 日志采集 指标大盘上线前务必验证LANGSMITH_TRACINGtrue且 Key 正确时dashboard 里真能看到 run。Serverless 别忘了后台回调那一项。常见坑生产未开 Tracing出问题只能凭用户口述猜调用链。日志没有threadId多会话并发时对不上同一会话的多次请求。只有裸console.log没法按event/toolName聚合做不了大盘。以为 Tracing 能替代日志或反过来一个看单次细节树一个看事件流与统计互补。LANGSMITH_API_KEY写进仓库放.env.gitignore。Serverless 用了后台回调还提前冻结进程trace 没刷完就丢设LANGCHAIN_CALLBACKS_BACKGROUNDfalse。只看最终答案调试 ReAct中间 tool 轨迹全黑——先上streamEvents。可观测性回答发生了什么。下一关更本质模型每一次推理窗口里到底塞了什么——历史消息、RAG 片段、工具结果一股脑堆进去窗口爆了或噪声太多再漂亮的 trace 也救不了胡话。