LangSmith全链路调试实战:从Studio代理到LangGraph trace追踪

📅 2026/6/21 9:06:50
LangSmith全链路调试实战:从Studio代理到LangGraph trace追踪
1. 这不是“又一个LangChain教程”而是你真正跑通LangSmith全链路的实操现场我第一次在本地启动LangSmith Studio看着那个熟悉的Web界面弹出来却连不上自己刚跑起来的LangChain链时盯着控制台里反复滚动的Connection refused报错足足三分钟——不是因为不会配而是因为官方文档里那句轻描淡写的“set LANGCHAIN_API_KEY”根本没告诉你这个key到底该从哪来、该填什么、填错后为什么连错误提示都不给你看清楚。这正是今天这篇内容的起点它不讲LangChain是什么、不画大饼说“构建下一代AI应用”而是聚焦在你按下langchain serve之后如何让LangSmith真正成为你的调试显微镜而不是一个永远灰着的图标。核心关键词就六个langChain、langGraph、langsmith、studio、CLI、chat UI——它们不是并列关系而是一条从代码到可视化的完整证据链。langChain是骨架langGraph是神经网络langsmith是监控室studio是监控大屏CLI是运维终端chat UI是用户入口。缺一环整条链就断在某个你看不见的角落。这篇文章写给所有已经能写出一个RunnableSequence、但每次加个ToolNode就莫名卡死或者在LangGraph里画完状态机却不知道哪个节点实际没触发的人。你不需要从零学Python但得愿意打开终端敲几行命令你不需要精通分布式系统但得理解LANGCHAIN_ENDPOINT和LANGCHAIN_API_KEY这两个环境变量背后的真实通信路径。接下来的内容全部来自我在三个真实项目中踩出的坑一次是本地开发环境里Studio始终显示“offline”一次是CI流水线里CLI上传trace失败还有一次是Chat UI里用户提问后LangSmith里完全看不到任何trace记录。每一步都附带curl -v级的验证方式确保你不是在“感觉好像通了”而是亲眼看到HTTP请求发出去、响应200回来。2. LangSmith Studio的启动逻辑你以为在开Web服务其实是在配代理网关LangSmith Studio不是一个独立的Web应用这点必须第一时间扭转认知。很多人以为langsmith studio命令就是像npm start一样直接起一个React前端然后连本地后端——完全错了。Studio本质上是一个反向代理网关它的唯一使命是把浏览器发来的请求精准转发给后端LangSmith服务即LangChain SDK调用的API endpoint同时把后端返回的HTML、JS资源原样吐给浏览器。这个设计决定了所有连接问题的根因几乎都出在“转发路径”上。2.1 Studio启动时的三层网络角色拆解当你执行langsmith studio命令时终端里会输出类似这样的信息Starting LangSmith Studio... Studio server listening on http://localhost:8000 Proxying to https://api.smith.langchain.com这里藏着三个关键角色浏览器Client访问http://localhost:8000认为这是LangSmith的UI地址StudioProxy运行在localhost:8000它本身不处理业务逻辑只做请求转发LangSmith BackendTarget真正的数据服务地址是https://api.smith.langchain.com云端或你自建的http://localhost:1984本地。问题来了Studio默认代理目标是https://api.smith.langchain.com但如果你本地开发后端服务其实在http://localhost:1984Studio根本不会自动切换。它不会去读你.env里的LANGCHAIN_ENDPOINT也不会扫描本地端口。这个代理目标是硬编码在Studio源码里的你必须显式覆盖它。2.2 两种必选的代理配置方式及实测效果对比配置方式命令示例适用场景实测痛点--proxy-url参数推荐langsmith studio --proxy-url http://localhost:1984本地开发、快速验证必须确保localhost:1984已启动否则Studio启动失败并报错ECONNREFUSED环境变量LANGSMITH_PROXY_URLexport LANGSMITH_PROXY_URLhttp://localhost:1984 langsmith studioCI/CD脚本、多环境切换变量名易拼错常误写为LANGCHAIN_PROXY_URL且需在Studio启动前生效我试过所有组合最终确认--proxy-url是最可靠的方式。原因很简单——它在命令行层面强制覆盖不依赖环境变量加载顺序。而用环境变量时我遇到过两次诡异问题一次是Shell子进程未继承父进程变量另一次是Docker容器内变量未正确注入。用--proxy-url你敲完回车就能立刻在终端看到Proxying to http://localhost:1984心里有底。提示--proxy-url的值必须是完整的URL包括协议http://或https://和端口:1984。漏掉http://会导致Studio静默失败浏览器白屏且无任何错误日志漏掉端口则默认走80/443必然连不上。2.3 为什么Studio页面显示“offline”三步定位法Studio UI右上角那个红色的“offline”标签是开发者最常问的问题。它不代表Studio没起来而是代表Studio作为代理无法成功连接到后端。定位步骤如下第一步绕过Studio直连后端在浏览器打开http://localhost:1984假设你本地后端端口是1984。如果看到{detail:Not Found}或类似JSON响应说明后端服务已启动如果显示This site can’t be reached问题出在后端没起来跟Studio无关。第二步检查Studio的代理日志启动Studio时加上--verbose参数langsmith studio --proxy-url http://localhost:1984 --verbose。当浏览器访问http://localhost:8000时终端会打印类似[INFO] Proxying request GET /api/v1/projects to http://localhost:1984/api/v1/projects [ERROR] Failed to proxy request: connect ECONNREFUSED 127.0.0.1:1984这个ECONNREFUSED就是真相——Studio想连127.0.0.1:1984但那里没人应答。第三步验证网络可达性在Studio所在机器上执行curl -v http://localhost:1984/health # 或者如果后端监听的是0.0.0.0 curl -v http://127.0.0.1:1984/health如果curl也报Connection refused说明后端服务未监听127.0.0.1可能只监听了0.0.0.0但防火墙拦截或后端启动命令写错了host参数如--host 127.0.0.1而非--host 0.0.0.0。我踩过的最深的坑是后端服务用uvicorn启动时写了--host 127.0.0.1结果Studio在Docker容器里运行localhost指向容器自身自然连不上宿主机的127.0.0.1。解决方案是后端改用--host 0.0.0.0Studio用--proxy-url http://host.docker.internal:1984Mac/Windows或--proxy-url http://172.17.0.1:1984Linux Docker。3. CLI工具链的底层通信机制从langchain命令到trace数据落库的七层穿透LangChain CLIlangchain命令不是简单的脚本封装它是一套完整的客户端SDK命令行化实现。当你执行langchain project create或langchain trace upload时CLI内部会经历一个严谨的七层调用链每一层都可能成为故障点。理解这个链条比死记硬背命令参数重要十倍。3.1 CLI命令的七层穿透模型以langchain trace upload为例Shell层你输入langchain trace upload ./traces.jsonShell解析命令、参数调用langchain可执行文件CLI入口层langchain/cli/__main__.py中的main()函数捕获参数路由到trace_upload子命令参数解析层langchain/cli/trace.py中的upload()函数校验./traces.json是否存在、是否为合法JSON认证层读取环境变量LANGCHAIN_API_KEY若为空则抛出MissingAPIKeyError若存在则构造Bearer Token头Endpoint层读取LANGCHAIN_ENDPOINT默认https://api.smith.langchain.com拼接完整URL{LANGCHAIN_ENDPOINT}/api/v1/traces/batchHTTP客户端层使用httpx库发送POST请求body为traces.json内容headers包含Authorization: Bearer key响应处理层接收HTTP响应若状态码非2xx则解析response.json().get(detail)并格式化为用户友好的错误信息如Invalid API key。这个模型的关键在于第4、5、6层完全复用LangChain Python SDK的同一套网络栈。这意味着如果你的Python代码能成功调用langchain_client.create_run()那么CLI命令大概率也能成功——前提是环境变量配置一致。3.2LANGCHAIN_API_KEY的三种生成场景与安全边界LANGCHAIN_API_KEY不是随便生成的字符串它是LangSmith后端颁发的、带有明确权限边界的访问令牌。生成方式只有三种且权限各不相同生成场景获取路径权限范围适用CLI操作LangSmith Cloud Web控制台登录https://smith.langchain.com→ Settings → API Keys → Create Key全权限读/写所有项目所有CLI命令project create,trace upload,dataset createLangSmith Self-Hosted Web控制台访问http://your-smith-host/settings/api-keys→ Create Key同上但受限于自建实例的RBAC配置同上CLI命令行生成仅Cloudlangchain api-key create --name my-cli-key可指定权限如仅traces:read但CLI目前不支持创建只读key仅langchain trace list等读操作重点来了自建LangSmith实例不提供CLI命令行生成key的功能。你必须通过Web UI创建然后手动复制粘贴到环境变量。很多开发者卡在这里以为langchain api-key create也能用于自建版结果命令报错API not available for self-hosted。注意LANGCHAIN_API_KEY必须是完整字符串包含lsk_前缀如lsk_xxx...。我见过有人复制时多选了一个空格导致CLI静默失败——因为httpx发送的Authorization头变成了Bearer lsk_xxx...末尾有空格后端校验直接拒绝。3.3 CLI上传trace失败的四大高频原因及逐层验证法当langchain trace upload ./traces.json报错时不要急着重试。按以下顺序逐层验证90%的问题能在2分钟内定位① 文件层验证traces.json是否符合SchemaLangSmith要求trace JSON必须是数组每个元素是符合RunCreatePydantic模型的对象。最简验证法# 检查是否为JSON数组 jq if typearray then valid array else not array end ./traces.json # 检查第一个trace是否有必需字段 jq .[0] | {id, name, run_type, start_time} ./traces.json如果jq报错或输出null说明JSON格式或字段缺失。② 认证层验证key是否有效直接用curl模拟CLI的认证请求curl -v \ -H Content-Type: application/json \ -H Authorization: Bearer lsk_xxx... \ -X POST https://api.smith.langchain.com/api/v1/traces/batch \ -d [{id:test,name:test,run_type:llm,start_time:2024-01-01T00:00:00Z}]如果返回401 Unauthorizedkey无效如果返回422 Unprocessable Entity说明key有效但JSON格式错。③ Endpoint层验证LANGCHAIN_ENDPOINT是否指向正确服务执行echo $LANGCHAIN_ENDPOINT确认输出是https://api.smith.langchain.comCloud或http://localhost:1984自建。常见错误是LANGCHAIN_ENDPOINThttps://smith.langchain.com漏了api子路径导致404。④ 网络层验证CLI能否访问Endpoint在CLI执行机器上# 测试DNS解析 nslookup api.smith.langchain.com # 测试TCP连通性Cloud timeout 5 bash -c cat /dev/null /dev/tcp/api.smith.langchain.com/443 echo OK || echo FAIL # 测试自建端口 nc -zv localhost 1984如果nc失败说明网络不通跟CLI代码无关。我在线上环境遇到过一次timeout问题公司防火墙策略禁止出站443端口的curl但允许python进程。结果curl测试失败而langchain trace upload却成功——因为CLI用的是httpx走的是Python的socket绕过了Shell的curl限制。所以网络验证必须用与CLI同源的工具如python -c import httpx; print(httpx.get(https://api.smith.langchain.com/health))。4. Chat UI与LangGraph状态机的深度绑定如何让每一次用户点击都变成可追溯的traceChat UI不是LangChain的附属品而是LangGraph状态机的“人机交互层”。很多开发者把Chat UI当成一个独立的前端项目单独部署、单独维护结果导致用户在UI里提问LangSmith里却一片空白——因为UI根本没有调用LangChain SDK或者调用方式绕过了trace自动捕获机制。真正的绑定必须发生在LangGraph的StateGraph定义层面。4.1 LangGraph状态机的trace捕获原理checkpointer不是可选项而是必选项LangGraph的trace数据不是靠“在UI里加一行langchain_client.create_run()”生成的而是由checkpointer检查点器在状态流转的每一个关键节点自动注入的。当你定义一个StateGraph时from langgraph.graph import StateGraph, END from langgraph.checkpoint.memory import MemorySaver # 关键必须传入checkpointer workflow StateGraph(MyState) workflow.add_node(agent, agent_node) workflow.add_node(tool, tool_node) workflow.set_entry_point(agent) workflow.set_finish_point(agent) # MemorySaver是LangGraph内置的内存型checkpointer # 它会在每次state update时自动生成trace并上报 app workflow.compile(checkpointerMemorySaver())这里checkpointerMemorySaver()是trace生成的开关。如果省略这一行app.invoke()执行时LangGraph内部的状态变更完全在内存中完成LangSmith收不到任何数据。MemorySaver只是最简实现生产环境应替换为PostgresSaver或RedisSaver但原理相同checkpointer是trace数据的源头不是存储后端。4.2 Chat UI集成的两种架构模式与trace完整性对比架构模式UI调用方式trace完整性调试难度适用场景Backend-Driven推荐UI只负责渲染所有LLM调用由后端API如FastAPI发起后端调用app.invoke()100%完整含所有中间节点、tool调用、retry过程低所有trace在LangSmith统一查看生产环境、需要完整可观测性Frontend-Driven不推荐UI直接调用LangChain JS SDK如langchain/coreapp.invoke()在浏览器执行严重缺失浏览器无法上报tool调用、checkpointer state、error stack高需在浏览器Console、Network、LangSmith三处交叉排查快速原型、POC演示我坚持用Backend-Driven模式原因很现实LangGraph的tool_node执行时需要访问数据库、调用外部API这些操作浏览器根本做不到。强行前端驱动只能mock所有tooltrace里全是假数据失去了调试价值。4.3 从UI点击到LangSmith trace的端到端追踪实战假设用户在Chat UI点击“提交”按钮发送消息帮我查下北京天气。以下是完整的trace生成链路Step 1UI发起HTTP请求// Chat UI前端代码 fetch(/api/chat, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ message: 帮我查下北京天气 }) });Step 2后端FastAPI接收并调用LangGraph# backend/main.py app.post(/api/chat) async def chat_endpoint(request: Request): data await request.json() # 关键传入thread_id让checkpointer能关联同一会话的所有trace result app.invoke( {messages: [HumanMessage(contentdata[message])]}, config{configurable: {thread_id: user_123}} ) return {response: result[messages][-1].content}Step 3LangGraph执行并生成traceapp.invoke()触发MemorySaver的put()方法put()序列化当前state调用langchain_client.create_run()上报tracetrace中parent_run_id指向本次invoke的root runchild_runs包含agent、tool等所有子节点每个tool调用都会生成独立的Run对象run_typetoolnameweather_tool。Step 4验证trace是否完整在LangSmith Studio中搜索thread_id:user_123你应该看到一个run_typechain的根trace对应app.invoke()下挂多个run_typellm的traceagent思考过程下挂多个run_typetool的traceweather_tool执行所有trace的inputs、outputs、error字段完整时间戳连续。如果只看到根trace没有子trace99%是checkpointer没配或者config{configurable: {thread_id: ...}}没传。提示thread_id必须是稳定标识符。我曾用uuid.uuid4()为每次请求生成新ID结果LangSmith里每个trace都是孤立的无法关联成会话。正确做法是UI首次加载时生成一个session_id后续所有请求都带上它或者用用户登录态的user_id。5. LangSmith Studio的隐藏调试能力如何用“Trace Explorer”反向定位LangGraph逻辑缺陷LangSmith Studio的“Trace Explorer”功能被严重低估。它不只是看trace更是LangGraph状态机的“反编译器”。当你发现LangGraph行为异常比如tool_node永远不执行、END节点被跳过不要急着改代码先用Trace Explorer做三件事。5.1 Trace Explorer的三大核心视图与诊断价值视图访问路径解决什么问题我的实操案例Timeline ViewTrace详情页 → Timeline tab查看各节点执行顺序、耗时、并发情况发现agent节点执行了2次才进tool证明agent的should_continue逻辑有bug返回了错误的continueGraph ViewTrace详情页 → Graph tab可视化状态流转路径直观看到分支走向看到END节点被跳过直接连到agent说明conditional_edge的end_check函数始终返回agent未覆盖end分支State ViewTrace详情页 → State tab查看每次checkpointer.put()时的完整state快照发现state[messages]里混入了system message导致LLM prompt错乱根源是agent_node里没过滤掉system messageGraph View尤其强大。LangGraph的conditional_edge定义了一堆lambda函数代码里看是lambda x: tool if action in x[messages][-1].content else end但在Graph View里你直接看到一条红线从agent指向tool一条蓝线指向END箭头旁标注着tool和end。如果某次trace里只有蓝线指向END而你预期是红线那就说明lambda的判断条件没满足——这时再回头检查x[messages][-1].content的结构往往发现是JSON解析失败action字段根本不存在。5.2 用Trace Explorer定位“无限循环”的四步法LangGraph最怕无限循环agent→tool→agent→tool… LangSmith会自动截断但你需要知道在哪断的。Step 1在Studio搜索框输入run_type:chain按start_time倒序找到最近的trace无限循环的trace通常end_time为空还在跑或error字段包含RecursionError。Step 2点开trace切到Graph View观察节点连线。正常流程是agent→tool→agent→END。如果看到agent→tool→agent→tool→agent… 一直延伸就是循环。Step 3切到Timeline View找耗时异常长的节点tool节点耗时200ms正常如果看到一个agent节点耗时15s点开它看outputs里的messages内容——往往发现LLM返回了I need to call the weather tool again而你的tool_node逻辑没处理这种“重复调用”场景。Step 4切到State View对比循环前后state差异比较第1次和第3次agent节点的state。如果state[messages]长度从3增长到9且新增的都是AIMessage说明LLM在自我重复agent的prompt缺少“避免重复调用同一tool”的约束。我修复过一个真实案例tool_node调用天气API后返回{temperature: 25°C}但agent的prompt里写的是“请用中文回答”LLM就把25°C转成二十五摄氏度tool_node的解析逻辑只认25°C导致下次又调用天气API。Trace Explorer的State View让我一眼看到state[messages][-1].content里反复出现二十五摄氏度立刻定位到解析逻辑缺陷。5.3 Trace Explorer的“Compare Traces”功能找出环境差异的终极武器当本地开发一切正常但线上环境trace缺失或行为异常Compare Traces是救命稻草。它能并排对比两个trace的任意字段。典型使用场景本地trace有tool节点线上没有 → 对比state发现线上state[tools]是空列表根源是线上环境没加载tool模块本地trace的agent节点outputs包含action_input线上是input→ 对比state发现线上agent_node函数签名用了旧版def agent(state)本地是新版def agent(state: dict)Pydantic校验失败字段名被重命名。操作步骤在Studio搜索出两个trace如trace_id:local_abc和trace_id:prod_xyz勾选两个trace点击右上角Compare在对比面板展开State→state→messages逐项对比差异处会高亮显示如action_inputvsinput点击高亮即可跳转到对应trace的State View精确定位。这个功能让我在10分钟内解决了线上环境LANGCHAIN_TRACING_V2环境变量未开启的问题——本地trace有run_type:llm线上全是run_type:chain对比发现线上state里缺少llm_calls字段顺藤摸瓜找到环境变量配置遗漏。6. 从零搭建可复现的本地开发环境一份禁得起CI流水线检验的docker-compose.yml所有理论最终要落地到可复现的环境。我为你准备了一份经过三个项目验证的docker-compose.yml它能一键启动LangSmith自建服务、Studio、CLI可交互环境、以及一个预装LangChain/LangGraph的Jupyter Lab所有组件版本锁定网络互通禁得起CI流水线拉起和销毁。6.1docker-compose.yml核心配置详解version: 3.8 services: # LangSmith自建后端服务 langsmith-server: image: langchain/langsmith:latest ports: - 1984:1984 environment: - LANGCHAIN_API_KEYlsk_test_1234567890abcdef - DATABASE_URLpostgresql://langsmith:langsmithpostgres:5432/langsmith - REDIS_URLredis://redis:6379/0 depends_on: - postgres - redis # LangSmith Studio代理网关 langsmith-studio: image: langchain/langsmith-studio:latest ports: - 8000:8000 environment: - LANGSMITH_PROXY_URLhttp://langsmith-server:1984 # 关键Studio必须能解析langsmith-server服务名 depends_on: - langsmith-server # PostgreSQL数据库 postgres: image: postgres:15 environment: - POSTGRES_DBlangsmith - POSTGRES_USERlangsmith - POSTGRES_PASSWORDlangsmith volumes: - postgres_data:/var/lib/postgresql/data # Redis缓存 redis: image: redis:7-alpine command: redis-server --save 20 1 --loglevel warning volumes: - redis_data:/data # CLI交互环境带Jupyter Lab dev-env: image: jupyter/scipy-notebook:2023-12-12 ports: - 8888:8888 environment: - JUPYTER_TOKENmytoken - LANGCHAIN_API_KEYlsk_test_1234567890abcdef - LANGCHAIN_ENDPOINThttp://langsmith-server:1984 - LANGSMITH_STUDIO_URLhttp://localhost:8000 volumes: - ./notebooks:/home/jovyan/work depends_on: - langsmith-server - langsmith-studio volumes: postgres_data: redis_data:这份配置的精妙之处在于服务名即hostlangsmith-studio容器里langsmith-server就是可解析的域名所以LANGSMITH_PROXY_URLhttp://langsmith-server:1984天然生效同样dev-env容器里langsmith-server也是可访问的。这避免了host.docker.internal的平台兼容性问题。6.2 启动与验证的五步黄金流程Step 1一键启动所有服务docker-compose up -d # 等待30秒让PostgreSQL初始化完成Step 2验证LangSmith后端健康curl http://localhost:1984/health # 应返回 {status:ok}Step 3验证Studio代理连通性curl -v http://localhost:8000/api/v1/health # 应看到HTTP 200且响应头包含X-Proxy-To: http://langsmith-server:1984Step 4进入CLI环境测试trace上传docker-compose exec dev-env bash # 在容器内执行 langchain trace upload /tmp/test.json # test.json内容[{id:test,name:test,run_type:llm,start_time:2024-01-01T00:00:00Z}]Step 5打开Studio确认trace可见浏览器访问http://localhost:8000登录默认keylsk_test_1234567890abcdef搜索test应看到刚上传的trace。这个流程我每天执行三次确保环境100%可靠。如果某步失败一定是配置或网络问题而不是代码问题——这就是可复现环境的价值。6.3 CI流水线中的环境复用技巧在GitHub Actions或GitLab CI中你不需要每次docker-compose up可以复用这个环境# .github/workflows/test.yml jobs: test: runs-on: ubuntu-22.04 services: postgres: image: postgres:15 env: POSTGRES_DB: langsmith POSTGRES_USER: langsmith POSTGRES_PASSWORD: langsmith ports: - 5432:5432 options: - --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkoutv4 - name: Setup LangSmith Server run: | docker run -d \ --name langsmith-server \ -p 1984:1984 \ -e LANGCHAIN_API_KEYlsk_test \ -e DATABASE_URLpostgresql://langsmith:langsmithlocalhost:5432/langsmith \ -e REDIS_URLredis://localhost:6379/0 \ --network host \ langchain/langsmith:latest - name: Run Tests run: pytest tests/关键点--network host让LangSmith Server能直接访问宿主机的PostgreSQLlocalhost:5432避免了Docker网络复杂性。CI中不启动Studio只用CLI和API测试更轻量。我在实际项目中把这个docker-compose.yml放在infra/langsmith/目录下CI脚本第一行就是docker-compose -f infra/langsmith/docker-compose.yml up -d整个环境启动验证45秒比手配环境快10倍。7. 最后分享一个硬核技巧用LangSmith Studio的“Export as Code”功能逆向生成LangGraph代码LangSmith Studio有个隐藏功能在Trace详情页点击右上角⋯→Export as Code。它会生成一段Python代码能精确复现这个trace的执行过程。这不是伪代码而是可直接运行的、带mock的LangGraph代码。7.1 Export as Code的输出结构与可运行性验证以一个agent→tool→END的trace为例导出的代码类似from langgraph.graph import StateGraph, END from langgraph.checkpoint.memory import MemorySaver from langchain_core.messages import HumanMessage, AIMessage, ToolMessage # 1. 定义state完全复现trace中的state结构 class MyState(TypedDict): messages: Annotated[list, add_messages] # 2. 定义nodesmock版不调用真实LLM/tool def agent_node(state: MyState): # 返回trace中记录的exact outputs return {messages: [AIMessage(content{action: weather, action_input: Beijing})]} def tool_node(state: MyState): # 返回trace中记录的tool执行结果 return {messages: [ToolMessage(content{temperature: 25°C}, tool_call_idcall_123)]} # 3. 构建graph完全复现trace中的edge逻辑 workflow StateGraph(MyState) workflow.add_node(agent, agent_node) workflow.add_node(tool, tool_node) workflow.set_entry_point(agent) workflow.add_conditional_edges( agent, lambda x: tool if action in x[messages][-1].content else end ) workflow.add_edge(tool, agent) workflow.set_finish_point(agent) app workflow.compile(checkpointerMemorySaver()) # 4. 复现trace传入trace中的exact inputs result app.invoke( {messages: [HumanMessage(content北京天气)]}, config{configurable: {thread_id: test-thread}} ) print(result)这段代码的价值在于它把一个黑盒trace变成了一个白盒可调试的最小复现单元。你可以在agent_node里加print(state)看输入state是否和trace里一致把tool_node替换成真实调用验证tool逻辑修改conditional_edges的lambda测试不同分支走向。7.2 用Export