<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>LangChain 基础 on 扎塔-Zata</title><link>https://www.zata.cc/tags/langchain-%E5%9F%BA%E7%A1%80/</link><description>Recent content in LangChain 基础 on 扎塔-Zata</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>扎塔-Zata</copyright><lastBuildDate>Thu, 24 Sep 2026 17:09:06 +0800</lastBuildDate><atom:link href="https://www.zata.cc/tags/langchain-%E5%9F%BA%E7%A1%80/index.xml" rel="self" type="application/rss+xml"/><item><title>LangChain 常见报错与排查指南</title><link>https://www.zata.cc/p/langchain-%E5%B8%B8%E8%A7%81%E6%8A%A5%E9%94%99%E4%B8%8E%E6%8E%92%E6%9F%A5%E6%8C%87%E5%8D%97/</link><pubDate>Tue, 08 Sep 2026 13:30:00 +0800</pubDate><guid>https://www.zata.cc/p/langchain-%E5%B8%B8%E8%A7%81%E6%8A%A5%E9%94%99%E4%B8%8E%E6%8E%92%E6%9F%A5%E6%8C%87%E5%8D%97/</guid><description>&lt;img src="https://www.zata.cc/p/langchain-%E5%B8%B8%E8%A7%81%E6%8A%A5%E9%94%99%E4%B8%8E%E6%8E%92%E6%9F%A5%E6%8C%87%E5%8D%97/images/index/index.png" alt="Featured image of post LangChain 常见报错与排查指南" />&lt;h1 id="langchain-常见报错与排查指南">LangChain 常见报错与排查指南
&lt;/h1>&lt;p>LangChain 应用报错时，最浪费时间的不是修，而是不知道问题出在哪一层。密钥、模型、提示、解析、编排——每一层的症状可能长得一样（都是「输出不对」），但修法完全不同。&lt;/p>
&lt;p>最有效的第一步：&lt;strong>先看模型的原始输出&lt;/strong>。把解析器临时换成 &lt;code>StrOutputParser&lt;/code>（或直接 print 响应对象），原始输出正常，问题在解析层；原始输出本身就不对，问题在配置、提示或模型。这一步能省掉大半瞎猜时间。&lt;/p>
&lt;h2 id="先定位问题出在哪一层">先定位：问题出在哪一层
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>层&lt;/th>
&lt;th>典型症状&lt;/th>
&lt;th>排查手段&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>配置层&lt;/td>
&lt;td>AuthenticationError、模型不存在&lt;/td>
&lt;td>检查环境变量；用 curl 直接调平台 API 验证密钥&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>模型层&lt;/td>
&lt;td>输出质量差、不按指令来&lt;/td>
&lt;td>换 StrOutputParser 看原始输出；换模型对比&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>解析层&lt;/td>
&lt;td>OutputParserException&lt;/td>
&lt;td>确认原始输出没问题后，再调格式指令&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>编排层&lt;/td>
&lt;td>Agent 死循环、链路数据对不上&lt;/td>
&lt;td>&lt;code>verbose=True&lt;/code>、LangSmith 追踪、astream_events&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="七类高频报错">七类高频报错
&lt;/h2>&lt;h3 id="1-api-密钥错误authenticationerror">1. API 密钥错误（AuthenticationError）
&lt;/h3>&lt;p>&lt;strong>症状&lt;/strong>：提示 API 密钥无效、缺失或未设置。&lt;/p>
&lt;p>&lt;strong>原因&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>环境变量没设置或没导出（&lt;code>OPENAI_API_KEY&lt;/code>、&lt;code>DASHSCOPE_API_KEY&lt;/code> 等）&lt;/li>
&lt;li>密钥过期、被删或无效&lt;/li>
&lt;li>账户余额不足（百炼这类预付费平台尤其常见）&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>解决&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;code>print(os.getenv(&amp;quot;OPENAI_API_KEY&amp;quot;))&lt;/code> 确认进程内真的读到了&lt;/li>
&lt;li>检查密钥有效性，必要时重新生成&lt;/li>
&lt;li>确认账户状态和余额&lt;/li>
&lt;/ul>
&lt;h3 id="2-模型未找到notfounderror">2. 模型未找到（NotFoundError）
&lt;/h3>&lt;p>&lt;strong>症状&lt;/strong>：提示指定的模型不存在或无法访问。&lt;/p>
&lt;p>&lt;strong>原因&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>模型名拼写错误（比如把 &lt;code>qwen-plus&lt;/code> 写成 &lt;code>qwen_plus&lt;/code>）&lt;/li>
&lt;li>模型对当前 API 密钥或账户不可用（部分模型需要专门开通权限）&lt;/li>
&lt;li>API 服务区域问题&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>解决&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>核对模型名拼写；模型名是精确匹配，多一个横线少一个点都不行&lt;/li>
&lt;li>查提供商文档，确认模型可用性和访问权限&lt;/li>
&lt;/ul>
&lt;h3 id="3-提示模板错误templateformaterror--missinginputvariables">3. 提示模板错误（TemplateFormatError / MissingInputVariables）
&lt;/h3>&lt;p>&lt;strong>症状&lt;/strong>：渲染提示时报错，或最终提示里出现了没被替换的 &lt;code>{variable}&lt;/code>。&lt;/p>
&lt;p>&lt;strong>原因&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>模板占位符与调用时传入的输入键不匹配&lt;/li>
&lt;li>模板语法错误——f-string 风格里想输出字面量 &lt;code>{&lt;/code> 却没写成 &lt;code>{{&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>解决&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>确保调用链时提供了模板里所有占位符变量&lt;/li>
&lt;li>单独测试渲染：&lt;code>prompt.invoke({...})&lt;/code> 看渲染结果是否符合预期&lt;/li>
&lt;/ul>
&lt;h3 id="4-agent-输出解析错误outputparserexception">4. Agent 输出解析错误（OutputParserException）
&lt;/h3>&lt;p>&lt;strong>症状&lt;/strong>：LLM 输出不符合 Agent 期望的格式（0.x 时代的 ReAct Agent 未能正确输出 &amp;ldquo;Thought:&amp;rdquo; / &amp;ldquo;Action:&amp;rdquo; 等）。&lt;/p>
&lt;p>&lt;strong>原因&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>模型难以理解提示中的输出格式要求&lt;/li>
&lt;li>提示过于复杂，或工具描述含糊导致模型困惑&lt;/li>
&lt;li>模型能力不足&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>解决&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>简化输出格式要求；在提示里加几个符合格式的 few-shot 示例&lt;/li>
&lt;li>改进工具描述，写清楚功能和预期输入&lt;/li>
&lt;li>&lt;code>handle_parsing_errors=True&lt;/code>（0.x 的 AgentExecutor 参数）：把解析错误反馈给模型，让它自我纠正&lt;/li>
&lt;li>换更强的模型&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>⚠️ 版本注记：这套症状主要属于 0.x 时代的文本协议 Agent（ReAct）。1.x 的 &lt;code>create_agent&lt;/code> 走原生 tool calling，模型输出结构化 JSON，这类格式解析错误已大幅减少。自己写 Pydantic 解析时仍会遇到 OutputParserException，处理思路见本站《langchain_core 组件详解》。&lt;/p>
&lt;/blockquote>
&lt;h3 id="5-工具执行错误toolexception">5. 工具执行错误（ToolException）
&lt;/h3>&lt;p>&lt;strong>症状&lt;/strong>：Agent 正确选中了工具，但工具执行失败。&lt;/p>
&lt;p>&lt;strong>原因&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>传给工具的参数无效&lt;/li>
&lt;li>工具依赖的外部服务不可用（限流、网络问题）&lt;/li>
&lt;li>工具内部逻辑错误&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>解决&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>工具内部用 try-except 捕获异常，返回有意义的错误信息给 Agent，而不是让它崩掉&lt;/li>
&lt;li>在工具入口验证参数；把工具拿出来独立测试&lt;/li>
&lt;li>在提示里引导 Agent：工具失败时换一种方法，或如实报告&lt;/li>
&lt;/ul>
&lt;h3 id="6-上下文长度超出contextwindowexceeded--invalidrequesterror">6. 上下文长度超出（ContextWindowExceeded / InvalidRequestError）
&lt;/h3>&lt;p>&lt;strong>症状&lt;/strong>：提示 + 对话历史 + 输出预留的总 token 超过模型上限。&lt;/p>
&lt;p>&lt;strong>原因&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>对话历史无限累积&lt;/li>
&lt;li>RAG 检索了过多的文档块&lt;/li>
&lt;li>提示本身冗长&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>解决&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>⚠️ 0.x 的 &lt;code>ConversationBufferWindowMemory&lt;/code> / &lt;code>ConversationTokenBufferMemory&lt;/code> / &lt;code>ConversationSummaryBufferMemory&lt;/code> 已随 legacy memory 体系移除；现代等价做法是 LangGraph checkpointer + 消息裁剪（&lt;code>trim_messages&lt;/code>），或对长输入做分块摘要（本站《订阅摘要 Agent 实战》有完整案例）&lt;/li>
&lt;li>控制检索文档的数量和长度&lt;/li>
&lt;li>精简提示；或换长上下文模型&lt;/li>
&lt;/ul>
&lt;h3 id="7-agent-死循环maxiterationsexceeded">7. Agent 死循环（MaxIterationsExceeded）
&lt;/h3>&lt;p>&lt;strong>症状&lt;/strong>：Agent 反复执行同样的无效操作，直到达到迭代上限。&lt;/p>
&lt;p>&lt;strong>原因&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>提示不清晰，无法引导 Agent 走向正确方向&lt;/li>
&lt;li>工具集不完备，缺少解决任务所需的关键能力&lt;/li>
&lt;li>LLM 在复杂推理中迷失&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>解决&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>改进提示，给出明确的目标和终止条件&lt;/li>
&lt;li>审查工具集是否齐备&lt;/li>
&lt;li>谨慎加大迭代上限——0.x 是 AgentExecutor 的 &lt;code>max_iterations&lt;/code>，LangGraph 时代对应 config 里的 &lt;code>recursion_limit&lt;/code>；但先排查其他原因，调上限只是兜底&lt;/li>
&lt;li>引入人工介入：Agent 卡住时让用户兜底&lt;/li>
&lt;/ul>
&lt;h2 id="1x-时代最常见的报错import-一个已被移除的类">1.x 时代最常见的报错：import 一个已被移除的类
&lt;/h2>&lt;p>LangChain 1.0（2025 年 10 月发布）移除了全部 legacy 抽象。老教程代码今天最常见的死法不是逻辑错误，而是 import 直接报错：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">ImportError: cannot import name &amp;#39;LLMChain&amp;#39; from &amp;#39;langchain.chains&amp;#39;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>遇到这类报错，对照表查一下，基本都有明确的现代等价物：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>被移除的老写法&lt;/th>
&lt;th>现代等价物&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>LLMChain&lt;/code>&lt;/td>
&lt;td>LCEL 管道：&lt;code>prompt | llm | parser&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>RetrievalQA&lt;/code>&lt;/td>
&lt;td>LCEL 检索链，或 &lt;code>create_retrieval_tool&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ConversationChain&lt;/code>&lt;/td>
&lt;td>LangGraph &lt;code>StateGraph&lt;/code> + checkpointer&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>initialize_agent&lt;/code> / &lt;code>AgentExecutor&lt;/code>&lt;/td>
&lt;td>&lt;code>langchain.agents.create_agent&lt;/code>（1.x）/ &lt;code>langgraph.prebuilt.create_react_agent&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>create_openai_functions_agent&lt;/code> 等旧 Agent 构造器&lt;/td>
&lt;td>同上，统一到 &lt;code>create_agent&lt;/code>，协议统一走 tool calling&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>langchain.memory.*&lt;/code>（ConversationBufferMemory 等）&lt;/td>
&lt;td>LangGraph checkpointer + 消息裁剪（&lt;code>trim_messages&lt;/code>）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>load_summarize_chain&lt;/code>&lt;/td>
&lt;td>LangGraph map-reduce，或 LCEL 手写&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>langchain.text_splitter&lt;/code>&lt;/td>
&lt;td>&lt;code>langchain_text_splitters&lt;/code> 独立包&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>langchain_community&lt;/code> 里的 ChatOllama / HuggingFaceEmbeddings&lt;/td>
&lt;td>&lt;code>langchain-ollama&lt;/code> / &lt;code>langchain-huggingface&lt;/code> 独立包&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>langchain_core.pydantic_v1&lt;/code>&lt;/td>
&lt;td>直接用 pydantic v2&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>需要读存量老代码时，这份表反过来用就是「翻译词典」。&lt;/p>
&lt;h2 id="排查顺序清单">排查顺序清单
&lt;/h2>&lt;ol>
&lt;li>报错栈最底层是平台 API 的 4xx？→ 配置层，查密钥、模型名、余额&lt;/li>
&lt;li>拿原始输出：StrOutputParser 或 print，判断问题在模型层还是解析层&lt;/li>
&lt;li>是解析错？改格式指令 + few-shot + 重试&lt;/li>
&lt;li>Agent 行为怪？开 &lt;code>verbose&lt;/code> / 用 LangSmith 追踪，看每一步的消息流&lt;/li>
&lt;li>还查不出来？把最小可复现片段剥出来单独跑&lt;/li>
&lt;/ol>
&lt;h2 id="总结">总结
&lt;/h2>&lt;ul>
&lt;li>报错先分层：配置 → 模型 → 解析 → 编排，症状会骗人，层不会&lt;/li>
&lt;li>看原始输出是最快的定位手段&lt;/li>
&lt;li>1.x 时代大半「奇怪的报错」其实是 import 了已移除的类，对照表一查便知&lt;/li>
&lt;li>过程观测交给工具：LangSmith（见本站《LangSmith使用教程》）+ astream_events&lt;/li>
&lt;/ul></description></item><item><title>LangChain 与 MCP 极简教程：让 Agent 接入外部工具的另一种方式</title><link>https://www.zata.cc/p/langchain-%E4%B8%8E-mcp-%E6%9E%81%E7%AE%80%E6%95%99%E7%A8%8B%E8%AE%A9-agent-%E6%8E%A5%E5%85%A5%E5%A4%96%E9%83%A8%E5%B7%A5%E5%85%B7%E7%9A%84%E5%8F%A6%E4%B8%80%E7%A7%8D%E6%96%B9%E5%BC%8F/</link><pubDate>Tue, 08 Sep 2026 13:00:00 +0800</pubDate><guid>https://www.zata.cc/p/langchain-%E4%B8%8E-mcp-%E6%9E%81%E7%AE%80%E6%95%99%E7%A8%8B%E8%AE%A9-agent-%E6%8E%A5%E5%85%A5%E5%A4%96%E9%83%A8%E5%B7%A5%E5%85%B7%E7%9A%84%E5%8F%A6%E4%B8%80%E7%A7%8D%E6%96%B9%E5%BC%8F/</guid><description>&lt;img src="https://www.zata.cc/p/langchain-%E4%B8%8E-mcp-%E6%9E%81%E7%AE%80%E6%95%99%E7%A8%8B%E8%AE%A9-agent-%E6%8E%A5%E5%85%A5%E5%A4%96%E9%83%A8%E5%B7%A5%E5%85%B7%E7%9A%84%E5%8F%A6%E4%B8%80%E7%A7%8D%E6%96%B9%E5%BC%8F/images/index/index.png" alt="Featured image of post LangChain 与 MCP 极简教程：让 Agent 接入外部工具的另一种方式" />&lt;h1 id="langchain-与-mcp-极简教程">LangChain 与 MCP 极简教程
&lt;/h1>&lt;p>给 Agent 接工具，最常见的做法是把所有工具函数都写在应用代码里，再逐个注册给模型。工具少的时候没问题；一旦工具多起来、或者想跨项目复用，这条路就会越来越累——同样的工具，每个应用都要重新写一遍。&lt;/p>
&lt;p>MCP（Model Context Protocol，模型上下文协议）是 Anthropic 在 2024 年底推出的开放协议，解决的就是这个问题：把「工具的定义和执行」从应用代码里拆出来，放到独立的服务器上，客户端按需发现和调用。本文用一个最小的双文件例子——FastMCP 起一个数学工具服务，&lt;code>langchain.mcp&lt;/code> 把它接进 Agent——把这条链路跑通。&lt;/p>
&lt;p>客户端这一侧，2026 年 9 月有个值得注意的变化：&lt;strong>MCP 支持已经进到 LangChain 主干里&lt;/strong>。LangChain &lt;code>v1.4.0&lt;/code> 新增了 &lt;code>langchain.mcp&lt;/code> 命名空间，基于 FastMCP 实现，把原先独立的 &lt;code>langchain-mcp-adapters&lt;/code> 包整个取代了——&lt;code>MultiServerMCPClient&lt;/code> 收敛成一个 &lt;code>MCPAdapter&lt;/code>。所以现在装依赖是 &lt;code>pip install &amp;quot;langchain[mcp]&amp;quot;&lt;/code>，不再是单独装一个适配器包。本文代码全部按新写法给。&lt;/p>
&lt;h2 id="mcp-和-function-call-是什么关系">MCP 和 Function Call 是什么关系
&lt;/h2>&lt;p>先说结论：两者不是竞争关系，而是工作在两层的东西。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Function calling 是模型的能力&lt;/strong>：模型根据工具的名称、描述和参数 schema，决定「该调哪个工具、传什么参数」，并以结构化 JSON 输出。&lt;/li>
&lt;li>&lt;strong>MCP 是工具的供给协议&lt;/strong>：约定客户端如何发现服务器上有哪些工具（tool list）、如何调用（tool call）、如何取回结果。&lt;/li>
&lt;/ul>
&lt;p>也就是说，MCP 解决的不是「模型会不会调工具」，而是「工具从哪来、谁来维护、能不能复用」。写一次 MCP server，任何支持 MCP 的客户端（Claude Desktop、Cursor、你自己的 Agent）都能连。&lt;/p>
&lt;h2 id="最小实战两个文件跑通">最小实战：两个文件跑通
&lt;/h2>&lt;p>项目就两个文件：&lt;/p>
&lt;ul>
&lt;li>&lt;code>math_server.py&lt;/code>：MCP 服务端，提供数学工具&lt;/li>
&lt;li>&lt;code>client.py&lt;/code>：LangChain 客户端，把 MCP 工具接进 Agent&lt;/li>
&lt;/ul>
&lt;h3 id="服务端fastmcp-起服务">服务端：FastMCP 起服务
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># math_server.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">mcp.server.fastmcp&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">FastMCP&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">mcp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FastMCP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Math&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@mcp.tool&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Add two numbers&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@mcp.tool&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">multiply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Multiply two numbers&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mcp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">transport&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;stdio&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>两个细节值得注意：&lt;/p>
&lt;ul>
&lt;li>工具用 &lt;code>@mcp.tool()&lt;/code> 装饰器定义，函数签名和 docstring 就是模型看到的工具说明——和 LangChain &lt;code>@tool&lt;/code> 的玩法一致，类型注解必须写清楚。&lt;/li>
&lt;li>&lt;code>transport=&amp;quot;stdio&amp;quot;&lt;/code> 表示通过标准输入/输出通信，客户端会以子进程方式拉起这个脚本，本地工具用它最省事；要跨机器就走 HTTP（streamable HTTP / SSE）。&lt;/li>
&lt;/ul>
&lt;h3 id="客户端把-mcp-工具接进-agent">客户端：把 MCP 工具接进 Agent
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># client.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">asyncio&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pathlib&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Path&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.agents&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">create_agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.mcp&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">MCPAdapter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 模型走阿里云百炼的 OpenAI 兼容模式&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;DASHSCOPE_API_KEY&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;https://dashscope.aliyuncs.com/compatible-mode/v1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;qwen-plus&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 目标类型决定传输方式：Path = stdio 子进程，字符串 = 必须是 http(s) URL&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">adapter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">MCPAdapter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Path&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;math_server.py&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">run_agent&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">async&lt;/span> &lt;span class="k">with&lt;/span> &lt;span class="n">adapter&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 把 MCP 工具加载为 LangChain 工具&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tools&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">adapter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">list_tools&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 创建并运行 agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">agent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">create_agent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tools&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">agent&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ainvoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;what&amp;#39;s (3 + 5) x 12?&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">asyncio&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">run_agent&lt;/span>&lt;span class="p">()))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>MCPAdapter&lt;/code> 最省事的地方是&lt;strong>它从你交给它的目标自己推断传输方式&lt;/strong>，不需要再写 &lt;code>transport&lt;/code> 字段：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>传给 &lt;code>MCPAdapter&lt;/code> 的目标&lt;/th>
&lt;th>推断出的传输&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;quot;https://example.com/mcp&amp;quot;&lt;/code>&lt;/td>
&lt;td>Streamable HTTP&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Path(&amp;quot;math_server.py&amp;quot;)&lt;/code>&lt;/td>
&lt;td>stdio 子进程（每个 adapter 一个）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>一个 &lt;code>FastMCP&lt;/code> 实例&lt;/td>
&lt;td>进程内直连，不走子进程也不走 socket，跑测试最合适&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>{&amp;quot;mcpServers&amp;quot;: {...}}&lt;/code> 配置字典&lt;/td>
&lt;td>多服务器，工具名自动加 &lt;code>{server}_&lt;/code> 前缀&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>现成的 &lt;code>fastmcp.Client&lt;/code>&lt;/td>
&lt;td>完全自定义（transport、缓存、认证）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>有个坑值得单独说：&lt;strong>脚本路径必须传 &lt;code>Path&lt;/code> 而不是 &lt;code>str&lt;/code>&lt;/strong>。FastMCP 解析字符串时会先当文件路径试，再当 URL 试，所以一个字符串形式的 &lt;code>&amp;quot;math_server.py&amp;quot;&lt;/code> 有被当成本地脚本拉起的风险；&lt;code>MCPAdapter&lt;/code> 的做法更保守——直接拒绝所有不符合 URL 形状的字符串。想跑本地脚本，明确写 &lt;code>Path(...)&lt;/code>。&lt;/p>
&lt;h3 id="运行">运行
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install &lt;span class="s2">&amp;#34;langchain[mcp]&amp;#34;&lt;/span> langchain-openai mcp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">DASHSCOPE_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>your_api_key
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python client.py
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>客户端会自己拉起 &lt;code>math_server.py&lt;/code>，提出问题 &amp;ldquo;what&amp;rsquo;s (3 + 5) x 12?&amp;quot;，并输出计算结果。&lt;/p>
&lt;h2 id="运行时发生了什么">运行时发生了什么
&lt;/h2>&lt;ol>
&lt;li>&lt;code>MCPAdapter(Path(&amp;quot;math_server.py&amp;quot;))&lt;/code> 从目标类型推断出 stdio 传输&lt;/li>
&lt;li>进入 &lt;code>async with adapter&lt;/code> 时以子进程方式启动 &lt;code>math_server.py&lt;/code>，完成 MCP 握手&lt;/li>
&lt;li>&lt;code>adapter.list_tools()&lt;/code> 把服务器上的工具转换成 LangChain 工具列表&lt;/li>
&lt;li>&lt;code>create_agent(model, tools)&lt;/code> 用这些工具构建 Agent&lt;/li>
&lt;li>Agent 收到问题，决定调用哪个工具；实际执行发生在 MCP server 侧，结果经客户端回传给模型&lt;/li>
&lt;/ol>
&lt;p>关键点在第 3 步：对 Agent 来说，MCP 工具和本地 &lt;code>@tool&lt;/code> 定义的工具没有任何区别，适配器把协议细节全部藏掉了。另外返回的每个工具自己持有连接、每次调用各开一个会话，所以**&lt;code>async with&lt;/code> 退出后 agent 依然可用**——不必把整轮推理都关在上下文里。&lt;/p>
&lt;h2 id="function-call-vs-mcp">Function Call vs MCP
&lt;/h2>&lt;p>&lt;img src="https://www.zata.cc/p/langchain-%E4%B8%8E-mcp-%E6%9E%81%E7%AE%80%E6%95%99%E7%A8%8B%E8%AE%A9-agent-%E6%8E%A5%E5%85%A5%E5%A4%96%E9%83%A8%E5%B7%A5%E5%85%B7%E7%9A%84%E5%8F%A6%E4%B8%80%E7%A7%8D%E6%96%B9%E5%BC%8F/images/index/image.png"
width="720"
height="773"
srcset="https://www.zata.cc/p/langchain-%E4%B8%8E-mcp-%E6%9E%81%E7%AE%80%E6%95%99%E7%A8%8B%E8%AE%A9-agent-%E6%8E%A5%E5%85%A5%E5%A4%96%E9%83%A8%E5%B7%A5%E5%85%B7%E7%9A%84%E5%8F%A6%E4%B8%80%E7%A7%8D%E6%96%B9%E5%BC%8F/images/index/image_hu17208915534754346694.png 480w, https://www.zata.cc/p/langchain-%E4%B8%8E-mcp-%E6%9E%81%E7%AE%80%E6%95%99%E7%A8%8B%E8%AE%A9-agent-%E6%8E%A5%E5%85%A5%E5%A4%96%E9%83%A8%E5%B7%A5%E5%85%B7%E7%9A%84%E5%8F%A6%E4%B8%80%E7%A7%8D%E6%96%B9%E5%BC%8F/images/index/image_hu16087748710215217863.png 1024w"
loading="lazy"
alt="Function Calling 与 MCP 的链路对比"
class="gallery-image"
data-flex-grow="93"
data-flex-basis="223px"
>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>维度&lt;/th>
&lt;th>Function Calling&lt;/th>
&lt;th>MCP&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>解决的问题&lt;/td>
&lt;td>模型如何表达「调这个工具、参数是这些」&lt;/td>
&lt;td>工具如何被外部程序发现、提供和复用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>所在层&lt;/td>
&lt;td>模型 API 的能力&lt;/td>
&lt;td>客户端与服务端之间的协议&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>工具定义位置&lt;/td>
&lt;td>应用代码里（&lt;code>@tool&lt;/code> / JSON Schema）&lt;/td>
&lt;td>独立的 MCP server&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>复用性&lt;/td>
&lt;td>换个应用基本要重写&lt;/td>
&lt;td>一个 server 服务所有支持 MCP 的客户端&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>传输方式&lt;/td>
&lt;td>随模型 API 请求一起发送&lt;/td>
&lt;td>stdio / HTTP，工具在进程外执行&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>一句话总结：function calling 决定模型「怎么说」，MCP 决定工具「从哪来」；MCP 工具最终还是要靠 function calling 让模型真正用起来。&lt;/p>
&lt;h2 id="版本注记从-langchain-mcp-adapters-迁移过来">版本注记：从 langchain-mcp-adapters 迁移过来
&lt;/h2>&lt;p>这篇教程最早写在 MCP 适配器还是独立包的时期，当时的客户端长这样：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_mcp_adapters.client&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">MultiServerMCPClient&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langgraph.prebuilt&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">create_react_agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">MultiServerMCPClient&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;math&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;transport&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;stdio&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;python&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;math_server.py&amp;#34;&lt;/span>&lt;span class="p">]},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_tools&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">agent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">create_react_agent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tools&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>LangChain &lt;code>v1.4.0&lt;/code>（2026-09-01）之后，这套写法整体退休。对应关系如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>旧（&lt;code>langchain-mcp-adapters&lt;/code>）&lt;/th>
&lt;th>新（&lt;code>langchain.mcp&lt;/code>）&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>MultiServerMCPClient(...)&lt;/code>&lt;/td>
&lt;td>&lt;code>MCPAdapter(target)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>await client.get_tools()&lt;/code>&lt;/td>
&lt;td>&lt;code>await adapter.list_tools()&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>load_mcp_tools(session)&lt;/code>&lt;/td>
&lt;td>直接用 &lt;code>adapter.list_tools()&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>convert_mcp_tool_to_langchain_tool&lt;/code>&lt;/td>
&lt;td>&lt;code>as_langchain_tool&lt;/code>（改名，且变成协程）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tool_name_prefix&lt;/code>&lt;/td>
&lt;td>多服务器时自动加 &lt;code>{server}_&lt;/code> 前缀&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>handle_tool_errors&lt;/code> 开关&lt;/td>
&lt;td>移除。行为固定：&lt;code>isError=True&lt;/code> 变成 &lt;code>ToolMessage(status=&amp;quot;error&amp;quot;)&lt;/code>，传输故障直接抛异常&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Callbacks(on_elicitation=...)&lt;/code>&lt;/td>
&lt;td>取消回调，改为默认开启的 LangGraph &lt;code>interrupt()&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tool_interceptors&lt;/code>&lt;/td>
&lt;td>用 LangChain &lt;code>@wrap_tool_call&lt;/code> 中间件（能拦所有工具，不止 MCP 的）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>连接上的 &lt;code>auth&lt;/code> / &lt;code>headers&lt;/code>&lt;/td>
&lt;td>挪到 &lt;code>fastmcp.Client&lt;/code> 上&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>三个必须知道的边界：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>langchain.mcp&lt;/code> 目前是 beta&lt;/strong>（需要 &lt;code>langchain[mcp]&amp;gt;=1.4.0&lt;/code>），import 时会抛一次 &lt;code>LangChainBetaWarning&lt;/code>，API 可能变。&lt;/li>
&lt;li>&lt;strong>elicitation 变成 interrupt&lt;/strong>：服务端中途要输入时，不再是回调，而是暂停整个 run，用 &lt;code>Command(resume={&amp;quot;responses&amp;quot;: {key: 答案}})&lt;/code> 恢复。这是设计变化，不是 bug。&lt;/li>
&lt;li>&lt;strong>prompts / resources 还没有包装&lt;/strong>：&lt;code>load_mcp_prompt&lt;/code>、&lt;code>load_mcp_resources&lt;/code> 这些没有对应实现，需要的话直接用 FastMCP 客户端的 &lt;code>client.get_prompt(...)&lt;/code> / &lt;code>client.read_resource(...)&lt;/code>。sampling 和 roots 则是因为 MCP 协议本身在无会话时代移除了，会抛 &lt;code>NotImplementedError&lt;/code>。&lt;/li>
&lt;/ul>
&lt;p>依赖包名也顺便更新一下：现在装的是 &lt;code>langchain[mcp]&lt;/code>，不再需要 &lt;code>pip install langchain-mcp-adapters&lt;/code>。&lt;/p>
&lt;p>收藏的参考资料：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>资源&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://guangzhengli.com/blog/zh/model-context-protocol" target="_blank" rel="noopener"
>MCP 终极指南（很值得读）&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://docs.langchain.com/oss/python/langchain/mcp" target="_blank" rel="noopener"
>LangChain 官方 MCP 文档&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://docs.langchain.com/oss/python/migrate/langchain-mcp-adapters" target="_blank" rel="noopener"
>从 langchain-mcp-adapters 迁移&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://cobusgreyling.medium.com/using-langchain-with-model-context-protocol-mcp-e89b87ee3c4c" target="_blank" rel="noopener"
>Using LangChain With Model Context Protocol (MCP)&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://zhuanlan.zhihu.com/p/27327515233" target="_blank" rel="noopener"
>知乎：一文看懂 MCP（大模型上下文协议）&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://www.dailydoseofds.com/p/function-calling-mcp-for-llms/" target="_blank" rel="noopener"
>Function Call vs MCP&lt;/a>（本文配图出处）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="下一步">下一步
&lt;/h2>&lt;ul>
&lt;li>把数学工具换成你真正需要的东西：查数据库、调内部 API、文件处理，思路完全一样&lt;/li>
&lt;li>工具链路稳定之后，如果要做长周期、多步骤的 Agent 工程（文件系统、子智能体、上下文管理），看本站的《DeepAgents完全指南》&lt;/li>
&lt;/ul></description></item><item><title>langchain_core 组件详解：Prompt 模板与 Output Parsers</title><link>https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/</link><pubDate>Tue, 08 Sep 2026 11:30:00 +0800</pubDate><guid>https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/</guid><description>&lt;img src="https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/index.png" alt="Featured image of post langchain_core 组件详解：Prompt 模板与 Output Parsers" />&lt;h1 id="langchain_core-组件详解prompt-模板与-output-parsers">langchain_core 组件详解：Prompt 模板与 Output Parsers
&lt;/h1>&lt;p>把一个 LangChain 应用拆到最小，真正绕不开的是一条链：&lt;code>prompt | llm | parser&lt;/code>。prompt 决定喂给模型什么，llm 吐出一段自由文本，parser 再把文本变成程序能用的数据。langchain_core 把这条链的两头做成了两个模块——&lt;code>prompts&lt;/code> 管输入侧，&lt;code>output_parsers&lt;/code> 管输出侧。&lt;/p>
&lt;p>这篇是这两个模块笔记的合并整理：输入侧讲 Prompt 模板家族怎么选、FewShot 怎么从固定示例进化到动态选例；输出侧讲五种常用解析器和解析失败后的 async 重试实战；解析报错的排查方法放在末尾。带运行输出和截图的部分来自我的实践记录，代码跑在 LangChain 0.3 时期的 langchain_core 上，个别老 API 的位置会标 ⚠️ 说明。&lt;/p>
&lt;p>两个核心判断先放在这里：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>输入侧默认 ChatPromptTemplate。&lt;/strong> 它吃一组消息、吐一组消息，和现代聊天模型的接口天然对齐；字符串版 &lt;code>PromptTemplate&lt;/code> 退居二线，主要做内部格式化片段。&lt;/li>
&lt;li>&lt;strong>要结构化输出，优先 &lt;code>with_structured_output&lt;/code>。&lt;/strong> LangChain 1.x 时代的主流做法是把 schema 绑在模型上，走 tool calling 或 JSON mode 通道，结构由模型原生保证。&lt;code>PydanticOutputParser&lt;/code> 这一族文本解析器是备选：模型不支持原生结构化输出，或需要把格式说明精确嵌进提示词时才用。但 format_instructions、解析、重试这套思路依然值得吃透——排查结构化输出问题时，最终都要回到这条最原始的链上。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="一prompt-模板输入侧">一、Prompt 模板（输入侧）
&lt;/h2>&lt;p>&lt;code>langchain_core.prompts&lt;/code> 解决的问题只有一个：把动态变量填进固定结构，生成模型输入。模板类有一族，日常主力只有一个。&lt;/p>
&lt;h3 id="chatprompttemplate默认选择">ChatPromptTemplate：默认选择
&lt;/h3>&lt;p>&lt;code>ChatPromptTemplate&lt;/code> 生成的不是一段字符串，而是一组结构化消息（&lt;code>List[BaseMessage]&lt;/code>），正好对齐聊天模型的输入格式：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatPromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">template&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;system&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;You are a helpful AI assistant named &lt;/span>&lt;span class="si">{name}&lt;/span>&lt;span class="s2">.&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;human&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Tell me a joke about &lt;/span>&lt;span class="si">{topic}&lt;/span>&lt;span class="s2">.&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt_template&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatPromptTemplate&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_messages&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">template&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prompt_template&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Grok&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;topic&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;dogs&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">messages&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># [SystemMessage(content=&amp;#39;You are a helpful AI assistant named Grok.&amp;#39;),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># HumanMessage(content=&amp;#39;Tell me a joke about dogs.&amp;#39;)]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>from_messages&lt;/code> 接受 &lt;code>(&amp;quot;角色&amp;quot;, &amp;quot;模板&amp;quot;)&lt;/code> 元组，也接受消息模板对象和占位符，可以任意混搭。日常写角色字符串就够了；&lt;code>SystemMessagePromptTemplate&lt;/code>、&lt;code>HumanMessagePromptTemplate&lt;/code>、&lt;code>AIMessagePromptTemplate&lt;/code> 这些细粒度的类，只在需要程序化拼装单条消息时才用得上。&lt;/p>
&lt;p>真正解决独立问题的类是 &lt;code>MessagesPlaceholder&lt;/code>——往模板中间插一段动态消息列表，最典型的用途是对话历史：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatPromptTemplate&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">MessagesPlaceholder&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.messages&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">HumanMessage&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">AIMessage&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">chat_prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatPromptTemplate&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_messages&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;system&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;You are a helpful assistant.&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MessagesPlaceholder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">variable_name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;history&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;human&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{input}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">history&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">HumanMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;What&amp;#39;s the weather like?&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AIMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;It&amp;#39;s sunny!&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chat_prompt&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;history&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">history&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;What&amp;#39;s the forecast tomorrow?&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">messages&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># [SystemMessage(...), HumanMessage(content=&amp;#34;What&amp;#39;s the weather like?&amp;#34;),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># AIMessage(...), HumanMessage(...)]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注意 &lt;code>variable_name&lt;/code> 必须和 invoke 时传入的键一致，写模板时就对清楚。&lt;/p>
&lt;h3 id="prompt-模板家族速查表">Prompt 模板家族速查表
&lt;/h3>&lt;p>完整班底如下，大部分场景只用到前几行：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类&lt;/th>
&lt;th>功能&lt;/th>
&lt;th>适用场景&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>ChatPromptTemplate&lt;/code>&lt;/td>
&lt;td>构建对话消息列表，支持多角色&lt;/td>
&lt;td>聊天机器人、多轮对话（日常默认）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>MessagesPlaceholder&lt;/code>&lt;/td>
&lt;td>插入动态消息列表&lt;/td>
&lt;td>对话历史、动态上下文&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>PromptTemplate&lt;/code>&lt;/td>
&lt;td>格式化字符串提示，支持动态变量&lt;/td>
&lt;td>字符串模板、内部格式化片段&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>FewShotChatMessagePromptTemplate&lt;/code>&lt;/td>
&lt;td>把少样本示例格式化成对话消息&lt;/td>
&lt;td>对话任务需要示例引导&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>SystemMessagePromptTemplate&lt;/code>&lt;/td>
&lt;td>系统消息模板&lt;/td>
&lt;td>程序化拼装 system 消息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>HumanMessagePromptTemplate&lt;/code>&lt;/td>
&lt;td>用户消息模板&lt;/td>
&lt;td>程序化拼装 human 消息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AIMessagePromptTemplate&lt;/code>&lt;/td>
&lt;td>AI 回复模板&lt;/td>
&lt;td>少样本对话、预置示例回答&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ChatMessagePromptTemplate&lt;/code>&lt;/td>
&lt;td>自定义角色消息模板（如 &lt;code>&amp;quot;Jedi&amp;quot;&lt;/code>）&lt;/td>
&lt;td>非标准角色的创意对话&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>FewShotPromptTemplate&lt;/code>&lt;/td>
&lt;td>少样本示例，生成字符串&lt;/td>
&lt;td>⚠️ 官方已标记 deprecated&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>PipelinePromptTemplate&lt;/code>&lt;/td>
&lt;td>组合多个子模板&lt;/td>
&lt;td>⚠️ 已废弃&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>两个 deprecated 的类多说两句。&lt;code>FewShotPromptTemplate&lt;/code>（字符串版少样本）官方已标记 deprecated，现代做法是 ChatPromptTemplate + FewShotChatMessagePromptTemplate 组合，再配 ExampleSelector 做动态选例（见下节）；&lt;code>PipelinePromptTemplate&lt;/code> 原本用来嵌套组合多段子模板，同样废弃了，多段模板的组合用普通变量嵌套或 LCEL 就能解决，不再需要专门的类。&lt;/p>
&lt;p>安全上有一条红线：字符串模板默认用 f-string 语法，&lt;code>PromptTemplate&lt;/code> 也支持 jinja2，但&lt;strong>不要用 jinja2 加载来源不可信的模板&lt;/strong>——jinja2 模板可以执行代码，是一个真实的注入面。没有特殊语法需求，留在默认的 f-string。&lt;/p>
&lt;h3 id="fewshot从固定示例到动态选例">FewShot：从固定示例到动态选例
&lt;/h3>&lt;p>少样本提示的思路是用示例教模型任务模式。对话版写法是把示例格式化成 human/ai 消息对，作为一个整体嵌进主模板：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">FewShotChatMessagePromptTemplate&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ChatPromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">examples&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;question&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;What is 2+2?&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;answer&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;question&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;What is 3+3?&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;answer&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;6&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">example_prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatPromptTemplate&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_messages&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;human&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{question}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;ai&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{answer}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">few_shot_prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FewShotChatMessagePromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">examples&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">examples&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">example_prompt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">example_prompt&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">final_prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatPromptTemplate&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_messages&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;system&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;You are a math expert.&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">few_shot_prompt&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;human&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{input}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">final_prompt&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;What is 4+4?&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>固定示例有两个天然限制：示例一多就挤占上下文窗口，示例和当前输入不相关时还会稀释注意力。进阶做法是接一个 &lt;code>ExampleSelector&lt;/code>，按语义相似度动态挑出与当前输入最相近的几条示例再拼进提示——等于把 RAG 的检索思路用在了示例库上，官方实现里 &lt;code>SemanticSimilarityExampleSelector&lt;/code> 是最常用的一个。&lt;/p>
&lt;p>我试过的玩法是把相近对话入库，请求时按相似度取回再传入 FewShot 模板，跑通后的效果：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/image.png"
width="1456"
height="772"
srcset="https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/image_hu2773629058688531427.png 480w, https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/image_hu7467198816374065572.png 1024w"
loading="lazy"
alt="将相近对话使用RAG获取并传入"
class="gallery-image"
data-flex-grow="188"
data-flex-basis="452px"
>&lt;/p>
&lt;p>具体代码：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/image-1.png"
width="1064"
height="708"
srcset="https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/image-1_hu16535364611493225799.png 480w, https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/image-1_hu14518289420376064366.png 1024w"
loading="lazy"
alt="具体代码"
class="gallery-image"
data-flex-grow="150"
data-flex-basis="360px"
>&lt;/p>
&lt;p>这套做法的关键在示例库质量和 embedding 的匹配度，示例数量适中即可。再想提升效果，该调的是检索策略，不是往提示里硬塞更多示例。&lt;/p>
&lt;h2 id="二output-parsers输出侧">二、Output Parsers（输出侧）
&lt;/h2>&lt;p>&lt;img src="https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/index.png"
width="449"
height="464"
srcset="https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/index_hu2968718835055450482.png 480w, https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/index_hu7684224093898913705.png 1024w"
loading="lazy"
alt="output_parsers 包中的类"
class="gallery-image"
data-flex-grow="96"
data-flex-basis="232px"
>&lt;/p>
&lt;p>输出解析器负责把 LLM 的自由文本转换成结构化数据：从自然语言句子、JSON 字符串或列表里提取关键信息，转成 Python 对象、字典或列表这类程序友好的格式。没有它，每个应用都得自己写一遍&amp;quot;从文本里抠 JSON&amp;quot;的脏活，格式约定还会散落在提示词和解析代码两处，很难保持一致。&lt;/p>
&lt;p>在链上的位置就是开头那条：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">PromptTemplate → LLM → OutputParser
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>提示模板定义输入和输出格式要求，LLM 生成原始文本，解析器完成最后一步转换。输入侧要求什么格式，输出侧就得配对应的 parser，两头的约定靠 &lt;code>get_format_instructions()&lt;/code> 生成的格式说明对齐。&lt;/p>
&lt;h3 id="五种常用解析器">五种常用解析器
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>解析器&lt;/th>
&lt;th>干什么&lt;/th>
&lt;th>典型场景&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>StrOutputParser&lt;/code>&lt;/td>
&lt;td>原样返回字符串&lt;/td>
&lt;td>简单问答、文本生成&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>JsonOutputParser&lt;/code>&lt;/td>
&lt;td>解析为 JSON 字典&lt;/td>
&lt;td>API 返回数据、键值对提取&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>PydanticOutputParser&lt;/code>&lt;/td>
&lt;td>解析为 Pydantic 模型&lt;/td>
&lt;td>数据验证、复杂对象处理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>CommaSeparatedListOutputParser&lt;/code>&lt;/td>
&lt;td>逗号分隔文本 → 列表&lt;/td>
&lt;td>列表提取（选项、标签）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>自定义 &lt;code>BaseOutputParser&lt;/code>&lt;/td>
&lt;td>自己实现 &lt;code>parse&lt;/code>&lt;/td>
&lt;td>特殊格式（分号分隔、表格解析）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>下面五个示例共用同一个 llm 初始化（OpenAI 兼容接口，配置从 &lt;code>.env&lt;/code> 读取），后文不再重复：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">dotenv&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">load_dotenv&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">load_dotenv&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;model&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;api_key&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;base_url&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">streaming&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="stroutputparser什么都不做">StrOutputParser：什么都不做
&lt;/h4>&lt;p>最简单的解析器，直接返回字符串，不需要任何格式化指令：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.output_parsers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">StrOutputParser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;今天是星期几？（假设今天是 2025 年 3 月 16 日）&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">parser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">StrOutputParser&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prompt&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">llm&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">parser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>输出&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">今天是星期日。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="jsonoutputparser拿到字典">JsonOutputParser：拿到字典
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.output_parsers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">JsonOutputParser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">parser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">JsonOutputParser&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;以 JSON 格式返回两种水果及其颜色。&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{format_instructions}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">partial_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;format_instructions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">parser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_format_instructions&lt;/span>&lt;span class="p">()}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prompt&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">llm&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">parser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>输出&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">{&amp;#39;apple&amp;#39;: &amp;#39;red&amp;#39;, &amp;#39;banana&amp;#39;: &amp;#39;yellow&amp;#39;}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>get_format_instructions()&lt;/code> 自动生成&amp;quot;请按 JSON 格式返回&amp;quot;的说明嵌进模板，提示和解析器的约定永远对得上。但硬约束不存在：LLM 输出的不是合法 JSON 时直接抛异常。&lt;/p>
&lt;h4 id="pydanticoutputparser强类型--字段校验">PydanticOutputParser：强类型 + 字段校验
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.output_parsers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PydanticOutputParser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pydantic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BaseModel&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Field&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Book&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">title&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Field&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">description&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;书名&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">author&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Field&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">description&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;作者&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">year&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Field&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">description&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;出版年份&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">parser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PydanticOutputParser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pydantic_object&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">Book&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;推荐一本书，并以指定格式返回。&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{format_instructions}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">推荐什么书？&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">input_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;question&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">partial_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;format_instructions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">parser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_format_instructions&lt;/span>&lt;span class="p">()}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prompt&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">llm&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">parser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;question&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;推荐什么书？&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>输出&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Book(title=&amp;#39;《活着》&amp;#39;, author=&amp;#39;余华&amp;#39;, year=1993)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Pydantic 保证字段类型（&lt;code>year&lt;/code> 必须是整数），格式化指令里还会带上每个字段的 description。&lt;code>get_format_instructions()&lt;/code> 生成的说明长这样：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Please provide your response in the following JSON format:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;title&amp;#34;: &amp;#34;string&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;author&amp;#34;: &amp;#34;string&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;year&amp;#34;: &amp;#34;integer&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>实例运行截图：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/index-1.png"
width="430"
height="206"
srcset="https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/index-1_hu11117520121720596488.png 480w, https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/index-1_hu11890333685817076953.png 1024w"
loading="lazy"
alt="PydanticOutputParser实例输出"
class="gallery-image"
data-flex-grow="208"
data-flex-basis="500px"
>&lt;/p>
&lt;h4 id="commaseparatedlistoutputparser拿到列表">CommaSeparatedListOutputParser：拿到列表
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.output_parsers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">CommaSeparatedListOutputParser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">parser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">CommaSeparatedListOutputParser&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;列出三种编程语言，用逗号分隔。&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{format_instructions}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">partial_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;format_instructions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">parser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_format_instructions&lt;/span>&lt;span class="p">()}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prompt&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">llm&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">parser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># type: list&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>输出&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">[&amp;#39;Python&amp;#39;, &amp;#39;Java&amp;#39;, &amp;#39;C++&amp;#39;]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>实例：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/index-2.png"
width="480"
height="177"
srcset="https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/index-2_hu10523795283954327661.png 480w, https://www.zata.cc/p/langchain_core-%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3prompt-%E6%A8%A1%E6%9D%BF%E4%B8%8E-output-parsers/images/index/index-2_hu10667292785974072936.png 1024w"
loading="lazy"
alt="CommaSeparatedListOutputParser"
class="gallery-image"
data-flex-grow="271"
data-flex-basis="650px"
>&lt;/p>
&lt;h4 id="自定义解析器继承-baseoutputparser">自定义解析器：继承 BaseOutputParser
&lt;/h4>&lt;p>内置解析器覆盖不到的格式，继承 &lt;code>BaseOutputParser&lt;/code> 实现 &lt;code>parse&lt;/code> 方法就行，比如解析分号分隔的列表：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.output_parsers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BaseOutputParser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">SemicolonListParser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseOutputParser&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">item&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">item&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;;&amp;#34;&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">parser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">SemicolonListParser&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;列出三种城市，用分号分隔，例如：北京;上海;广州&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prompt&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">llm&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">parser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>输出&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">[&amp;#39;北京&amp;#39;, &amp;#39;上海&amp;#39;, &amp;#39;广州&amp;#39;]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="解析失败重试async-实战">解析失败重试：async 实战
&lt;/h3>&lt;p>文本解析器没有硬约束，LLM 输出不稳定或格式要求严格时，解析失败是常态而不是异常，重试应该直接设计进链路。下面是完整跑通的 async 版本：&lt;code>PydanticOutputParser&lt;/code> 解析动词提取结果，外层 for 循环控制最多三次尝试，全部失败后再兜底一次。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.output_parsers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PydanticOutputParser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.runnables&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RunnableSequence&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pydantic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BaseModel&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Field&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">dotenv&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">load_dotenv&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">asyncio&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 加载环境变量&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">load_dotenv&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;model&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">api_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;api_key&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">base_url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;base_url&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 初始化 LLM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">base_url&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">streaming&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 定义输出结构使用 Pydantic&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">ParseResult&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Field&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">description&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;解析出的参数结果&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建输出解析器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">output_parser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PydanticOutputParser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pydantic_object&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ParseResult&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建提示模板&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt_template&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_template&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;请解析以下输入并返回结果：&lt;/span>&lt;span class="si">{input}&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">返回格式：&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{format_instructions}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">partial_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;format_instructions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">output_parser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_format_instructions&lt;/span>&lt;span class="p">()}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建可运行序列&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># chain = RunnableSequence(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># prompt_template,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># llm,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># output_parser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># )&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prompt_template&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">llm&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">output_parser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 输入数据&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">user_input&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;提取这句话中的动词：&amp;#39;我喜欢跑步和游泳&amp;#39;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">run_parsing&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_attempts&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">attempt&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">max_attempts&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 使用 invoke 方法运行链&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ainvoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">user_input&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;第 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">attempt&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 次尝试成功&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;解析结果: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">model_dump&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">result&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="ne">Exception&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;第 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">attempt&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 次尝试失败，错误: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">attempt&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">max_attempts&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;正在重试...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;已达到最大尝试次数&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ainvoke&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">user_input&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;max_retries&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;最终修复结果: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">model_dump&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 使用 model_dump() 替代 dict()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">result&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="ne">Exception&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">final_error&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;最终尝试失败，错误: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">final_error&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在已有事件循环中运行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="n">run_parsing&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 如果在已有的事件循环中（比如 Jupyter），直接运行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">asyncio&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_event_loop&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">is_running&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="c1"># 在已有循环中运行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">asyncio&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>输出示例&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">解析结果： {&amp;#39;result&amp;#39;: &amp;#39;喜欢,跑步,游泳&amp;#39;}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关于这段代码有一点必须澄清（原笔记在这里有误导）：重试全部来自手工调用——外层 for 循环跑满三次，都失败后再兜底调一次。最后那次 &lt;code>ainvoke&lt;/code> 传的 &lt;code>config={&amp;quot;max_retries&amp;quot;: 1}&lt;/code> 并不是解析重试的开关——&lt;code>prompt | llm | parser&lt;/code> 这条链上，解析失败抛出的异常只会向上传给调用方，库不会自动重跑；&lt;code>max_retries&lt;/code> 至多影响底层 API 请求层面的重试（网络错误、限流），跟解析失败是两回事，真正需要 API 层重试应该设在 &lt;code>ChatOpenAI&lt;/code> 的初始化参数里。想让模型重新生成合规输出，只能像上面这样自己捕获异常、重新调用整条链。&lt;/p>
&lt;p>重跑整条链之所以有效，是因为 LLM 输出有随机性：同一个输入再来一次，经常就能给出合规格式。捕获异常后重跑，是成本最低的重试方案。&lt;/p>
&lt;h3 id="和-with_structured_output-怎么选">和 with_structured_output 怎么选
&lt;/h3>&lt;p>回到开头第二个判断。1.x 时代做结构化输出，第一选择是模型侧的 &lt;code>with_structured_output&lt;/code>——还是上面那个 Book schema，但玩法完全变了：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">structured_llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">with_structured_output&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Book&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">book&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">structured_llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;推荐一本书，返回书名、作者和出版年份&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 直接拿到 Book 实例&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>&lt;code>with_structured_output&lt;/code>&lt;/th>
&lt;th>文本解析器一族&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>结构由谁保证&lt;/td>
&lt;td>schema 绑在模型上，走 tool calling / JSON mode&lt;/td>
&lt;td>格式说明嵌进提示词，靠模型遵守约定&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>提示词&lt;/td>
&lt;td>不需要 format_instructions&lt;/td>
&lt;td>&lt;code>get_format_instructions()&lt;/code> 嵌入 partial_variables&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>解析&lt;/td>
&lt;td>库内部完成&lt;/td>
&lt;td>链上显式一环，失败自己处理&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>怎么选：模型支持 tool calling 或 JSON mode（主流模型都支持），直接 &lt;code>with_structured_output&lt;/code>，代码更少、格式更有保障；模型不支持原生结构化输出，或需要把格式说明精确控制进提示词（特定提示策略、兼容老链路），用文本解析器。&lt;code>StrOutputParser&lt;/code> 不受这场选型影响——拿纯文本时它仍是标准配件，也是后面排查解析问题的关键工具。&lt;/p>
&lt;h2 id="三解析报错排查">三、解析报错排查
&lt;/h2>&lt;h3 id="agent-场景outputparserexception-与-handle_parsing_errors">Agent 场景：OutputParserException 与 handle_parsing_errors
&lt;/h3>&lt;p>解析报错最密集的场景是 Agent。ReAct 式 Agent 靠解析模型文本来决定下一步动作（Thought / Action / Final Answer 的固定格式），模型一旦不合规矩就抛 &lt;code>OutputParserException&lt;/code>——典型的坏输出比如同一段回复里同时包含 &lt;code>Action&lt;/code> 和 &lt;code>Final Answer&lt;/code>。&lt;/p>
&lt;p>我跑 ReAct Agent 时撞到过这个错，解法是初始化代理时传 &lt;code>handle_parsing_errors=True&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">agent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">initialize_agent&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tools&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">tools&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">agent_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;zero-shot-react-description&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">verbose&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">handle_parsing_errors&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span> &lt;span class="c1"># 自动处理解析错误&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">agent&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;告诉我关于 LangChain 的信息&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>机制：解析失败时代理不抛异常，而是把错误信息反馈给模型，要求它重新生成符合预期的输出格式——相当于框架替你做了上一节手写的那层重试循环。&lt;/p>
&lt;p>⚠️ 此为 0.x 时代写法：&lt;code>initialize_agent&lt;/code> / AgentExecutor 属于 legacy API，LangChain 1.x 已将其移出主线。更根本的变化是，现代 tool-calling Agent 让模型直接输出结构化的 tool call，不再靠解析自由文本决定动作，这一类解析报错从源头就少了一大截。这段经验的价值在机制本身：解析失败时把错误反馈给模型让它自我修正，这个思路在哪个版本都适用。&lt;/p>
&lt;h3 id="调试心法先定位问题在模型还是解析器">调试心法：先定位问题在模型还是解析器
&lt;/h3>&lt;p>解析失败时第一个动作不是改提示词，是看原始输出。把链尾换成 &lt;code>StrOutputParser&lt;/code> 跑一遍，问题在哪一侧立刻清楚：原始输出本身不符合约定格式，问题在模型侧；原始输出看着合规但 parser 仍报错，问题在解析器侧——多半是格式约定和实际输出没对上。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">except&lt;/span> &lt;span class="ne">Exception&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;解析失败：&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 用 StrOutputParser 检查原始输出&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">raw_chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prompt&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">llm&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">StrOutputParser&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">raw_output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">raw_chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;原始输出：&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">raw_output&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>定位到模型侧之后，两个常用手段：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>把格式要求写得更硬。&lt;/strong> 在提示里重复强调，比如&amp;quot;严格按照 JSON 格式返回，不要添加额外说明&amp;quot;；格式说明用 &lt;code>get_format_instructions()&lt;/code> 生成后经 &lt;code>partial_variables&lt;/code> 嵌入，不要自己手写一份——手写的和解析器的约定迟早对不上。&lt;/li>
&lt;li>&lt;strong>降低 temperature。&lt;/strong> 对格式遵从度差的模型，降低采样温度能减少格式漂移。&lt;/li>
&lt;/ul>
&lt;h2 id="总结">总结
&lt;/h2>&lt;ul>
&lt;li>输入侧默认 &lt;code>ChatPromptTemplate&lt;/code> + &lt;code>MessagesPlaceholder&lt;/code>，细粒度消息类按需取用；FewShot 的进阶形态是 ExampleSelector 按相似度动态选例，示例库质量比数量重要。&lt;/li>
&lt;li>输出侧要结构化数据，1.x 优先 &lt;code>with_structured_output&lt;/code>；文本解析器是备选，五种里 &lt;code>PydanticOutputParser&lt;/code>（要校验）和 &lt;code>JsonOutputParser&lt;/code>（要字典）最常用，特殊格式继承 &lt;code>BaseOutputParser&lt;/code> 自己写 &lt;code>parse&lt;/code>。&lt;/li>
&lt;li>解析失败按常态路径处理：普通链上自己包一层循环重跑；Agent 场景（0.x）用 &lt;code>handle_parsing_errors=True&lt;/code> 让框架把错误喂回模型。&lt;/li>
&lt;li>排查顺序固定：先换 &lt;code>StrOutputParser&lt;/code> 看原始输出，定位模型侧还是解析器侧，再对应改提示（格式说明、temperature）或改解析约定。&lt;/li>
&lt;li>字符串模板留在默认的 f-string，不要用 jinja2 加载来源不可信的模板。&lt;/li>
&lt;/ul></description></item><item><title>LangChain 模型接入指南：OpenAI 兼容协议与多平台调用</title><link>https://www.zata.cc/p/langchain-%E6%A8%A1%E5%9E%8B%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97openai-%E5%85%BC%E5%AE%B9%E5%8D%8F%E8%AE%AE%E4%B8%8E%E5%A4%9A%E5%B9%B3%E5%8F%B0%E8%B0%83%E7%94%A8/</link><pubDate>Tue, 08 Sep 2026 11:00:00 +0800</pubDate><guid>https://www.zata.cc/p/langchain-%E6%A8%A1%E5%9E%8B%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97openai-%E5%85%BC%E5%AE%B9%E5%8D%8F%E8%AE%AE%E4%B8%8E%E5%A4%9A%E5%B9%B3%E5%8F%B0%E8%B0%83%E7%94%A8/</guid><description>&lt;img src="https://www.zata.cc/p/langchain-%E6%A8%A1%E5%9E%8B%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97openai-%E5%85%BC%E5%AE%B9%E5%8D%8F%E8%AE%AE%E4%B8%8E%E5%A4%9A%E5%B9%B3%E5%8F%B0%E8%B0%83%E7%94%A8/images/index/index.png" alt="Featured image of post LangChain 模型接入指南：OpenAI 兼容协议与多平台调用" />&lt;h1 id="langchain-模型接入指南openai-兼容协议与多平台调用">LangChain 模型接入指南：OpenAI 兼容协议与多平台调用
&lt;/h1>&lt;p>&lt;code>langchain_openai&lt;/code> 名字里带 openai，能做的事却比名字大得多：任何兼容 OpenAI 接口协议的模型服务，它都能接。而到今天，&lt;strong>OpenAI API 格式已经成了事实上的工业标准&lt;/strong>——阿里百炼、DeepSeek、vLLM、Ollama，无一例外都把&amp;quot;兼容 OpenAI 协议&amp;quot;当成默认姿势。&lt;/p>
&lt;p>所以在 LangChain 里接模型这件事，可以收敛成一套打法：用 &lt;code>ChatOpenAI&lt;/code> 和 &lt;code>OpenAIEmbeddings&lt;/code>，配合目标平台的三要素。我把这套打法叫**&amp;ldquo;换头&amp;rdquo;**——换平台时业务代码一行不动，只改三处：&lt;code>base_url&lt;/code>、&lt;code>api_key&lt;/code>、&lt;code>model&lt;/code>。一个原本基于 GPT-4 写的应用，改完这三处就直接跑在通义千问上。&lt;/p>
&lt;p>这篇文章做两件事：以阿里百炼为例，把对话、流式、嵌入、工具调用四类调用完整走一遍（嵌入那里有个很容易漏的坑）；然后给一份多平台速查表，覆盖 6 家 ChatModel 和 5 家 Embeddings 的包名与初始化要点，换平台时直接查表。&lt;/p>
&lt;h2 id="一百炼实战用-chatopenai-跑通四类调用">一、百炼实战：用 ChatOpenAI 跑通四类调用
&lt;/h2>&lt;h3 id="11-准备包key-和-base-url">1.1 准备：包、Key 和 Base URL
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install langchain-openai langchain-core
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>去&lt;a class="link" href="https://bailian.console.aliyun.com/" target="_blank" rel="noopener"
>阿里云百炼控制台&lt;/a>拿两样东西：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>API Key&lt;/strong>（&lt;code>sk-&lt;/code> 开头）&lt;/li>
&lt;li>&lt;strong>Base URL&lt;/strong>：百炼的 OpenAI 兼容端点是 &lt;code>https://dashscope.aliyuncs.com/compatible-mode/v1&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>组件上只需要认识两个类，正好对应 LLM 应用的两块基石：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类&lt;/th>
&lt;th>用途&lt;/th>
&lt;th>百炼上的对应模型&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>ChatOpenAI&lt;/code>&lt;/td>
&lt;td>对话、指令跟随、逻辑推理、工具调用（Function Calling）&lt;/td>
&lt;td>通义千问系列（&lt;code>qwen-plus&lt;/code>、&lt;code>qwen-max&lt;/code>、&lt;code>qwen-turbo&lt;/code>、&lt;code>qwen-long&lt;/code>）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>OpenAIEmbeddings&lt;/code>&lt;/td>
&lt;td>文本转向量，构建 RAG 的基础&lt;/td>
&lt;td>向量模型（&lt;code>text-embedding-v2&lt;/code>、&lt;code>text-embedding-v3&lt;/code> 等）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;code>ChatOpenAI&lt;/code> 走 Chat Completion 接口：输入是消息列表（&lt;code>SystemMessage&lt;/code>、&lt;code>HumanMessage&lt;/code>、&lt;code>AIMessage&lt;/code>），输出是 &lt;code>AIMessage&lt;/code>。千问的具体型号清单见百炼官方的&lt;a class="link" href="https://help.aliyun.com/zh/model-studio/getting-started/models" target="_blank" rel="noopener"
>模型列表&lt;/a>。&lt;/p>
&lt;h3 id="12-基础对话">1.2 基础对话
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.messages&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">HumanMessage&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">SystemMessage&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 为清晰起见先写成明文变量；生产环境放环境变量，做法见第三节&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ALIBABA_API_KEY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;sk-你的阿里百炼API_KEY&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ALIBABA_BASE_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://dashscope.aliyuncs.com/compatible-mode/v1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">chat_model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;qwen-plus&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 指向百炼的模型 ID&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ALIBABA_API_KEY&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ALIBABA_BASE_URL&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">temperature&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.7&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">streaming&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">messages&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SystemMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;你是一个专业的Python代码助手。&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">HumanMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;请用Python写一个冒泡排序，并解释其时间复杂度。&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chat_model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">messages&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>除了 &lt;code>model&lt;/code> / &lt;code>api_key&lt;/code> / &lt;code>base_url&lt;/code> 三处&amp;quot;换头&amp;quot;参数，其余写法与调 GPT-4 完全一致——这正是兼容协议的价值。&lt;/p>
&lt;h3 id="13-流式输出">1.3 流式输出
&lt;/h3>&lt;p>把 &lt;code>invoke&lt;/code> 换成 &lt;code>stream&lt;/code> 就是流式：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">chunk&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">chat_model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">messages&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunk&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">end&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">flush&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>chunk.content&lt;/code> 是每次生成的一小段文本。流式对大模型应用的用户体验提升非常直接，做对话类产品基本是必选项。&lt;/p>
&lt;h3 id="14-向量嵌入最不能漏的一行">1.4 向量嵌入：最不能漏的一行
&lt;/h3>&lt;p>做 RAG（知识库助手）时用 &lt;code>OpenAIEmbeddings&lt;/code> 调百炼的向量模型。这里有个坑，也是全文最值得记住的一行参数：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">OpenAIEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpenAIEmbeddings&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;text-embedding-v2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ALIBABA_API_KEY&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ALIBABA_BASE_URL&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">check_embedding_ctx_length&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 关键：关掉 OpenAI 特有的长度检查，防止报错&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">vector&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">embeddings&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embed_query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;阿里百炼是一个大模型服务平台。&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;生成的向量维度: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;向量前 5 位: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>OpenAIEmbeddings&lt;/code> 默认开着一段 OpenAI 特有的逻辑：发送前先按 OpenAI 的分词器做上下文长度检查，超长文本会在本地被切块再分批发送。接到百炼这种非 OpenAI 端点上，这套本地检查既无必要，又可能在调用时直接报错。显式传 &lt;code>check_embedding_ctx_length=False&lt;/code> 把它关掉就正常了。第一次接百炼嵌入如果莫名报错，先看是不是漏了这个参数。&lt;/p>
&lt;h3 id="15-工具调用bind_tools">1.5 工具调用：bind_tools
&lt;/h3>&lt;p>百炼的 Qwen 模型对 Function Calling 的支持很好，且完全走 OpenAI 的格式。用 &lt;code>bind_tools&lt;/code> 把工具绑定到模型上，模型决定调用时，请求会出现在返回消息的 &lt;code>tool_calls&lt;/code> 属性里，而不是正文里：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.tools&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">tool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@tool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_weather&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">city&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;查询某个城市的天气信息。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">city&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 的天气是晴天，气温 25 度。&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">llm_with_tools&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chat_model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bind_tools&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">get_weather&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llm_with_tools&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;杭州今天天气怎么样？&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;工具调用请求: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tool_calls&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>拿到 &lt;code>tool_calls&lt;/code> 之后，取出参数、真正执行工具、把结果作为 ToolMessage 回传给模型，就是一个完整的工具调用循环——LangChain 的 Agent 封装和 LangGraph 处理的就是这个循环。&lt;/p>
&lt;h2 id="二为什么推荐用-langchain_openai-接百炼">二、为什么推荐用 langchain_openai 接百炼
&lt;/h2>&lt;p>一个自然的疑问：阿里有官方 SDK，LangChain 里也有 &lt;code>ChatTongyi&lt;/code> 这种原生类，为什么绕道 &lt;code>langchain_openai&lt;/code>？理由有三。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>代码零迁移成本。&lt;/strong> 项目原本基于 GPT-4 开发的话，不需要改动任何业务逻辑，把环境变量里的 &lt;code>BASE_URL&lt;/code> 和 &lt;code>API_KEY&lt;/code> 换掉，应用就从 OpenAI 切到了百炼 Qwen。把 &lt;code>langchain_openai&lt;/code> 当默认接入层，各平台互为&amp;quot;平替&amp;quot;，切换成本约等于零。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>生态支持最完善。&lt;/strong> LangChain 社区对 OpenAI 类的支持是优先级最高的；走标准接口，后续接 LangGraph、LangSmith 这些工具也最顺。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>工具调用格式统一。&lt;/strong> OpenAI 的 Tool Calling 格式是当前的黄金标准。百炼通过兼容接口让 Qwen 像 GPT 一样精准地调用工具，不需要自己处理格式转换。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>反过来说，只有当你要用 DashScope 原生协议里、兼容层没覆盖的能力时，才需要回到原生 SDK——这条备选路线放在第五节。&lt;/p>
&lt;h2 id="三环境变量与-key-管理">三、环境变量与 Key 管理
&lt;/h2>&lt;h3 id="31-永久环境变量cmd--gui--zsh--bash">3.1 永久环境变量：CMD / GUI / zsh / bash
&lt;/h3>&lt;p>以百炼的 &lt;code>DASHSCOPE_API_KEY&lt;/code> 为例。这个名字不是随便起的：第五节的 &lt;code>ChatTongyi&lt;/code>、&lt;code>DashScopeEmbeddings&lt;/code> 都默认读它。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Windows CMD&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">setx DASHSCOPE_API_KEY &lt;span class="s2">&amp;#34;YOUR_DASHSCOPE_API_KEY&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> %DASHSCOPE_API_KEY% &lt;span class="c1"># 验证&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Windows 图形界面&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 系统属性 → 环境变量，手动添加，不再赘述&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># zsh&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;export DASHSCOPE_API_KEY=&amp;#39;YOUR_DASHSCOPE_API_KEY&amp;#39;&amp;#34;&lt;/span> &amp;gt;&amp;gt; ~/.zshrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> ~/.zshrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="nv">$DASHSCOPE_API_KEY&lt;/span> &lt;span class="c1"># 验证&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># bash&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;export DASHSCOPE_API_KEY=&amp;#39;YOUR_DASHSCOPE_API_KEY&amp;#39;&amp;#34;&lt;/span> &amp;gt;&amp;gt; ~/.bashrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> ~/.bashrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="nv">$DASHSCOPE_API_KEY&lt;/span> &lt;span class="c1"># 验证&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="32-env-模板多平台共存">3.2 .env 模板：多平台共存
&lt;/h3>&lt;p>写进 shell 配置不适合多项目切换，更常见的做法是每个项目放一个 &lt;code>.env&lt;/code>，用 &lt;code>python-dotenv&lt;/code> 加载：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 阿里云百炼&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">DASHSCOPE_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>sk-你的key
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">DASHSCOPE_BASE_URL&lt;/span>&lt;span class="o">=&lt;/span>https://dashscope.aliyuncs.com/compatible-mode/v1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># OpenAI 官方（或另一个兼容端点）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OPENAI_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>sk-你的key
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># OPENAI_BASE_URL=&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">dotenv&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">load_dotenv&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">load_dotenv&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;qwen-plus&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;DASHSCOPE_API_KEY&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;DASHSCOPE_BASE_URL&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">streaming&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>再进一步还有个偷懒技巧：openai 官方 SDK 本身就认 &lt;code>OPENAI_API_KEY&lt;/code> 和 &lt;code>OPENAI_BASE_URL&lt;/code> 两个环境变量。把这两个变量指向百炼，代码里连 &lt;code>api_key&lt;/code>、&lt;code>base_url&lt;/code> 都不用写，&lt;code>ChatOpenAI(model=&amp;quot;qwen-plus&amp;quot;)&lt;/code> 就能直接跑——切换平台时只动环境变量，一行代码不改。&lt;/p>
&lt;h3 id="33-百炼计费预警">3.3 百炼计费预警
&lt;/h3>&lt;p>百炼有个和 OpenAI 不一样的计费方式，要提前知道：它没办法单独充值消费，费用会直接从控制台里扣钱。所以动手跑代码之前，先把控制台的高消费预警设上。&lt;/p>
&lt;h2 id="四多平台速查表">四、多平台速查表
&lt;/h2>&lt;p>&amp;ldquo;换头&amp;quot;的前提是各家有自己的接入包。LangChain 0.2 之后，集成被拆成一个个独立的合作包，下面两张表按&amp;quot;提供商 → 安装包 → 初始化要点&amp;quot;整理。表里的型号名只是写作时存在过的示例，各家更新很快，使用前以官方现役型号为准。&lt;/p>
&lt;h3 id="41-chatmodel6-家">4.1 ChatModel（6 家）
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>提供商&lt;/th>
&lt;th>安装包&lt;/th>
&lt;th>初始化要点&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>OpenAI / 任意 OpenAI 兼容端点&lt;/td>
&lt;td>&lt;code>langchain-openai&lt;/code>&lt;/td>
&lt;td>&lt;code>ChatOpenAI(model=&amp;quot;gpt-4o-mini&amp;quot;)&lt;/code>，默认读 &lt;code>OPENAI_API_KEY&lt;/code>；接百炼则加 &lt;code>base_url=&amp;quot;https://dashscope.aliyuncs.com/compatible-mode/v1&amp;quot;&lt;/code>，&lt;code>model&lt;/code> 换成 &lt;code>qwen-plus&lt;/code> 等&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Anthropic&lt;/td>
&lt;td>&lt;code>langchain-anthropic&lt;/code>&lt;/td>
&lt;td>&lt;code>ChatAnthropic(model=&amp;quot;claude-sonnet-4-5-20250929&amp;quot;)&lt;/code>，默认读 &lt;code>ANTHROPIC_API_KEY&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Google&lt;/td>
&lt;td>&lt;code>langchain-google-genai&lt;/code> / &lt;code>langchain-google-vertexai&lt;/code>&lt;/td>
&lt;td>Gemini API 走 &lt;code>ChatGoogleGenerativeAI(model=&amp;quot;gemini-2.5-flash&amp;quot;)&lt;/code>，读 &lt;code>GOOGLE_API_KEY&lt;/code>；GCP 走 &lt;code>ChatVertexAI&lt;/code>，需要 gcloud 登录或 &lt;code>GOOGLE_APPLICATION_CREDENTIALS&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Hugging Face&lt;/td>
&lt;td>&lt;code>langchain-huggingface&lt;/code>&lt;/td>
&lt;td>&lt;code>HuggingFaceEndpoint(repo_id=&amp;quot;google/flan-t5-large&amp;quot;, task=&amp;quot;text-generation&amp;quot;)&lt;/code>，读 &lt;code>HUGGINGFACEHUB_API_TOKEN&lt;/code>；本地 pipeline 用 &lt;code>HuggingFacePipeline&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cohere&lt;/td>
&lt;td>&lt;code>langchain-cohere&lt;/code>&lt;/td>
&lt;td>&lt;code>ChatCohere(model=&amp;quot;command-r&amp;quot;)&lt;/code>，默认读 &lt;code>COHERE_API_KEY&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Ollama（本地）&lt;/td>
&lt;td>&lt;code>langchain-ollama&lt;/code>&lt;/td>
&lt;td>&lt;code>ChatOllama(model=&amp;quot;llama3.2&amp;quot;)&lt;/code>；服务默认在 &lt;code>http://localhost:11434&lt;/code>，不在默认地址时传 &lt;code>base_url&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="42-embeddings5-家">4.2 Embeddings（5 家）
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>提供商&lt;/th>
&lt;th>安装包&lt;/th>
&lt;th>初始化要点&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>OpenAI / 任意 OpenAI 兼容端点&lt;/td>
&lt;td>&lt;code>langchain-openai&lt;/code>&lt;/td>
&lt;td>&lt;code>OpenAIEmbeddings(model=&amp;quot;text-embedding-3-small&amp;quot;)&lt;/code>；接百炼：&lt;code>model=&amp;quot;text-embedding-v2&amp;quot;&lt;/code> + &lt;code>base_url&lt;/code> + &lt;code>check_embedding_ctx_length=False&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Google&lt;/td>
&lt;td>&lt;code>langchain-google-genai&lt;/code> / &lt;code>langchain-google-vertexai&lt;/code>&lt;/td>
&lt;td>&lt;code>GoogleGenerativeAIEmbeddings(model=&amp;quot;models/gemini-embedding-001&amp;quot;)&lt;/code>；Vertex 用 &lt;code>VertexAIEmbeddings(model_name=&amp;quot;text-embedding-005&amp;quot;)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Hugging Face（本地）&lt;/td>
&lt;td>&lt;code>langchain-huggingface&lt;/code> + &lt;code>sentence-transformers&lt;/code>&lt;/td>
&lt;td>&lt;code>HuggingFaceEmbeddings(model_name=&amp;quot;sentence-transformers/all-MiniLM-L6-v2&amp;quot;)&lt;/code>，可用 &lt;code>model_kwargs={'device': 'cuda'}&lt;/code> 指定设备&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cohere&lt;/td>
&lt;td>&lt;code>langchain-cohere&lt;/code>&lt;/td>
&lt;td>&lt;code>CohereEmbeddings(model=&amp;quot;embed-v4.0&amp;quot;)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Ollama（本地）&lt;/td>
&lt;td>&lt;code>langchain-ollama&lt;/code>&lt;/td>
&lt;td>&lt;code>OllamaEmbeddings(model=&amp;quot;nomic-embed-text&amp;quot;)&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="43-相对旧版速查的更新">4.3 相对旧版速查的更新
&lt;/h3>&lt;p>这张表由 2025 年初那版速查表更新而来，几处变化需要说明：&lt;/p>
&lt;ul>
&lt;li>&lt;code>langchain_community.llms.HuggingFaceHub&lt;/code> → &lt;code>langchain-huggingface&lt;/code> 包的 &lt;code>HuggingFaceEndpoint&lt;/code>。Hugging Face 的推理接入这几年变动很大（Inference API 已让位给 Inference Providers），这条路线以官方文档为准。&lt;/li>
&lt;li>&lt;code>langchain_community.chat_models.ChatOllama&lt;/code> → &lt;code>langchain-ollama&lt;/code> 包的 &lt;code>ChatOllama&lt;/code>，&lt;code>OllamaEmbeddings&lt;/code> 同步从 &lt;code>langchain_community.embeddings&lt;/code> 迁到 &lt;code>langchain-ollama&lt;/code>。&lt;/li>
&lt;li>&lt;code>langchain_community.embeddings.HuggingFaceEmbeddings&lt;/code> → &lt;code>langchain-huggingface&lt;/code> 的同名类。&lt;/li>
&lt;li>旧表里的 &lt;code>gpt-3.5-turbo&lt;/code>、&lt;code>claude-3-sonnet-20240229&lt;/code>、&lt;code>gemini-pro&lt;/code>、&lt;code>llama2&lt;/code> 等型号已过时，表中换成了更新的型号；它们同样会过期，以各家官方型号页为准。&lt;/li>
&lt;/ul>
&lt;h3 id="44-两个容易忽略的差异">4.4 两个容易忽略的差异
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>&lt;code>embed_query&lt;/code> 与 &lt;code>embed_documents&lt;/code> 分工不同&lt;/strong>：前者嵌入单条查询文本（用户搜索的那句话），返回一个向量；后者批量嵌入多个文档，返回向量列表。检索系统的两侧——入库文档和用户查询——必须用同一个嵌入模型，否则维度和语义空间都对不上。&lt;/li>
&lt;li>&lt;strong>向量维度因模型而异&lt;/strong>：&lt;code>text-embedding-3-small&lt;/code> 是 1536 维，&lt;code>text-embedding-3-large&lt;/code> 是 3072 维，常见的 sentence-transformers 模型多为 384 或 768 维。维度决定了向量库的结构和相似度计算，入库后换嵌入模型基本等于重建整个库。&lt;/li>
&lt;/ul>
&lt;h2 id="五备选路线原生-dashscope-与-init_chat_model">五、备选路线：原生 DashScope 与 init_chat_model
&lt;/h2>&lt;h3 id="51-chattongyi--dashscopeembeddingslegacy">5.1 ChatTongyi / DashScopeEmbeddings（legacy）
&lt;/h3>&lt;p>LangChain 社区包里保留了走 DashScope 原生 SDK 的两个类。⚠️ 以下是 0.x 时代的社区包写法，能用，但新项目建议优先走 OpenAI 兼容层：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install langchain-community dashscope
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ⚠️ 此为 langchain_community 0.x 时代写法&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.chat_models.tongyi&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatTongyi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.messages&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">HumanMessage&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">chatLLM&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatTongyi&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;qwen-max&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">streaming&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chatLLM&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stream&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">HumanMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;hi&amp;#34;&lt;/span>&lt;span class="p">)],&lt;/span> &lt;span class="n">streaming&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;chat resp:&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>嵌入对应 &lt;code>DashScopeEmbeddings&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ⚠️ 此为 langchain_community 0.x 时代写法&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.embeddings&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">DashScopeEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">DashScopeEmbeddings&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;text-embedding-v2&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">query_result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">embeddings&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embed_query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;This is a test document.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;文本向量长度：&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query_result&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">doc_results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">embeddings&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embed_documents&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Hi there!&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Oh, hello!&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;What&amp;#39;s your name?&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;My friends call me World&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Hello World!&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;文本向量数量：&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc_results&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="s2">&amp;#34;，文本向量长度：&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc_results&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>两个类都默认读 &lt;code>DASHSCOPE_API_KEY&lt;/code> 环境变量。什么时候才需要这条路线：要用 DashScope 原生协议里、兼容层没覆盖的能力时。纯粹跑对话和嵌入，没有理由不用兼容层。&lt;/p>
&lt;h3 id="52-init_chat_model">5.2 init_chat_model
&lt;/h3>&lt;p>LangChain 官方还提供了统一初始化入口 &lt;code>init_chat_model&lt;/code>，按模型名和 provider 一步创建实例，不用记各家类名：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.chat_models&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">init_chat_model&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">init_chat_model&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;qwen-plus&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_provider&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;openai&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 指定走 langchain_openai 的 ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;sk-你的key&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;https://dashscope.aliyuncs.com/compatible-mode/v1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其余参数原样透传给对应类，细节以官方文档为准。&lt;/p>
&lt;h2 id="总结">总结
&lt;/h2>&lt;p>把这篇文章压成几条可执行的结论：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>把 &lt;code>langchain_openai&lt;/code> 当默认接入层&lt;/strong>。OpenAI API 格式已是事实工业标准，&lt;code>ChatOpenAI&lt;/code> + &lt;code>OpenAIEmbeddings&lt;/code> 能接一切兼容端点。&lt;/li>
&lt;li>&lt;strong>换平台只改三处&lt;/strong>：&lt;code>base_url&lt;/code>、&lt;code>api_key&lt;/code>、&lt;code>model&lt;/code>，业务代码不动；配合环境变量或 &lt;code>.env&lt;/code>，可以做到一行代码不换平台。&lt;/li>
&lt;li>&lt;strong>接百炼做嵌入，&lt;code>check_embedding_ctx_length=False&lt;/code> 必加&lt;/strong>，这是最容易漏的一行。&lt;/li>
&lt;li>&lt;strong>跑代码前先设百炼高消费预警&lt;/strong>，它的费用直接从控制台账户扣，没有独立余额。&lt;/li>
&lt;li>&lt;strong>新项目直接装独立集成包&lt;/strong>（&lt;code>langchain-ollama&lt;/code>、&lt;code>langchain-huggingface&lt;/code> 等），&lt;code>langchain_community&lt;/code> 里的模型类当 legacy 看。&lt;/li>
&lt;/ol>
&lt;p>下一步：拿 1.2 的基础对话代码，把三个&amp;quot;换头&amp;quot;参数换成你手上的平台——百炼、DeepSeek 或本地 Ollama——跑通第一条消息。然后做两件事：用 &lt;code>bind_tools&lt;/code> 试一次工具调用，用 &lt;code>OpenAIEmbeddings&lt;/code> 把一段文本打成向量。这三步走完，LangChain 接模型这件事就算真正上手了。&lt;/p></description></item><item><title>LangSmith使用教程</title><link>https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/</link><pubDate>Fri, 30 May 2025 16:15:40 +0800</pubDate><guid>https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/</guid><description>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index.png" alt="Featured image of post LangSmith使用教程" />&lt;h1 id="langsmith使用教程">LangSmith使用教程
&lt;/h1>&lt;p>LangSmith 是 LangChain 官方的 LLM 应用观测平台：调试、追踪、评估、监控都在上面。LLM 应用最麻烦的一点是行为不透明——同一份提示，换个模型、换个参数、换个措辞，结果就可能完全不同，只靠 print 调试很快就会失控。我的判断是：追踪不是可选项，而是基础设施；在 LangChain 生态里，LangSmith 是接入成本最低的那个。&lt;/p>
&lt;p>本文是我 2025 年 5 月的上手记录（截图都是当时的实拍）；2026 年 9 月整合博客时修订了环境变量命名和评估 API，过时的旧写法在文中用注记标出。&lt;/p>
&lt;h2 id="核心概念">核心概念
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>概念&lt;/th>
&lt;th>一句话说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Tracing（追踪）&lt;/td>
&lt;td>记录应用每个组件的输入、输出、耗时、token，还原完整执行路径&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Debugging（调试）&lt;/td>
&lt;td>基于 trace 定位错误和非预期行为&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Evaluation（评估）&lt;/td>
&lt;td>用数据集 + 评估器量化应用表现，比较不同版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Datasets（数据集）&lt;/td>
&lt;td>评估用的输入/期望输出样本集合，可从生产 trace 提取&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Monitoring（监控）&lt;/td>
&lt;td>部署后持续追踪错误率、延迟、token 消耗&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Project（项目）&lt;/td>
&lt;td>trace 的归属单位，一个应用一个项目&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="接入设置">接入设置
&lt;/h2>&lt;h3 id="账户与-api-key">账户与 API Key
&lt;/h3>&lt;ol>
&lt;li>到 &lt;a class="link" href="https://smith.langchain.com/" target="_blank" rel="noopener"
>smith.langchain.com&lt;/a> 注册，登录后创建组织（Organization）。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image_hu16999795923421345222.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image_hu13664730940339820997.png 1024w"
loading="lazy"
alt="langsmit界面"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;/p>
&lt;ol start="2">
&lt;li>在组织的 Settings → API Keys 里创建密钥，妥善保管，不要公开分享。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-1.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-1_hu8868344347395063117.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-1_hu18255957453875288326.png 1024w"
loading="lazy"
alt="settings"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-2.png"
width="269"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-2_hu13006059943811164616.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-2_hu588751695666657627.png 1024w"
loading="lazy"
alt="apikey"
class="gallery-image"
data-flex-grow="28"
data-flex-basis="67px"
>&lt;/p>
&lt;h3 id="安装-sdk">安装 SDK
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install langsmith
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="环境变量">环境变量
&lt;/h3>&lt;blockquote>
&lt;p>2026-09 修订：官方现在的推荐前缀是 &lt;code>LANGSMITH_&lt;/code>。本文写作时用的还是 &lt;code>LANGCHAIN_&lt;/code> 旧前缀（下方注记），两者目前兼容，新项目直接用新写法。&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">LANGSMITH_TRACING_ENABLED&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">LANGSMITH_ENDPOINT&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;https://api.smith.langchain.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">LANGSMITH_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;YOUR_LANGSMITH_API_KEY&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">LANGSMITH_PROJECT&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;YOUR_PROJECT_NAME&amp;#34;&lt;/span> &lt;span class="c1"># 可选，默认 &amp;#34;default&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>LANGSMITH_TRACING_ENABLED&lt;/code>：总开关，设为 true 后 LangChain / LangGraph 应用自动上报&lt;/li>
&lt;li>&lt;code>LANGSMITH_PROJECT&lt;/code>：trace 归属的项目名，可在界面上创建和管理&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>⚠️ 旧写法：&lt;code>LANGCHAIN_TRACING_V2=&amp;quot;true&amp;quot;&lt;/code> / &lt;code>LANGCHAIN_ENDPOINT&lt;/code> / &lt;code>LANGCHAIN_API_KEY&lt;/code> / &lt;code>LANGCHAIN_PROJECT&lt;/code>。仍兼容，但已弃用。&lt;/p>
&lt;/blockquote>
&lt;h2 id="追踪-langchain-应用">追踪 LangChain 应用
&lt;/h2>&lt;p>环境变量设好之后，LangChain 应用&lt;strong>零代码改动&lt;/strong>自动上报——这是它比手动埋点省事的核心原因。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatPromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.output_parsers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">StrOutputParser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 模型：阿里云百炼的 OpenAI 兼容模式&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># （修订：原文这里误用了未导入的 Tongyi 类，统一改为博客其他文章一致的 ChatOpenAI 写法）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;qwen-plus&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;DASHSCOPE_API_KEY&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;https://dashscope.aliyuncs.com/compatible-mode/v1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatPromptTemplate&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_messages&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;system&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;You are a helpful assistant that translates &lt;/span>&lt;span class="si">{input_language}&lt;/span>&lt;span class="s2"> to &lt;/span>&lt;span class="si">{output_language}&lt;/span>&lt;span class="s2">.&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;human&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{text}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prompt&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">llm&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">StrOutputParser&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;input_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;English&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;output_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Chinese&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Hello, how are you?&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>运行后登录 LangSmith，在对应项目下就能看到这次运行的追踪记录：链的每个步骤、输入、输出、耗时、token，以及可能发生的任何错误。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-3.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-3_hu668023011181215320.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-3_hu14328061733863575152.png 1024w"
loading="lazy"
alt="trace"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-4.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-4_hu395046776489291782.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-4_hu3300973140154413831.png 1024w"
loading="lazy"
alt="记录"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;/p>
&lt;h2 id="追踪任意函数traceable">追踪任意函数：@traceable
&lt;/h2>&lt;p>不在 LangChain 链里的函数，用 &lt;code>@traceable&lt;/code> 装饰器也能纳入追踪：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langsmith&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">traceable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@traceable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;My Custom Function&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># name 可选，用于在 UI 中显示&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">my_data_processing_function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">upper&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@traceable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">my_llm_logic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 里面可以调 LLM、调其他被追踪函数，调用链会串在一条 trace 里&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">my_data_processing_function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">my_llm_logic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;This is some input text.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>被装饰的函数每次调用都会生成一条 trace，和 LangChain 链的 trace 在同一个视图里查看。&lt;/p>
&lt;h2 id="从-callbacks-到-astream_events观测方式的变化">从 Callbacks 到 astream_events：观测方式的变化
&lt;/h2>&lt;blockquote>
&lt;p>本节为 2026-09 整合时补充。&lt;/p>
&lt;/blockquote>
&lt;p>LangChain 0.x 时代，想监听应用内部发生的事，要写 CallbackHandler——继承 &lt;code>BaseCallbackHandler&lt;/code>，实现 &lt;code>on_llm_start&lt;/code> / &lt;code>on_llm_new_token&lt;/code> 等一串回调。能用，但啰嗦，和异步、流式代码搅在一起还容易出错。&lt;/p>
&lt;p>1.x 时代的官方答案是 &lt;strong>astream_events&lt;/strong>：一条流式事件管道，把链内部每个组件的开始/结束/token 都作为事件吐出来，不用写 handler 类：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">event&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">astream_events&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;input_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;English&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;output_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Chinese&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Hello, how are you?&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">version&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;v2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;event&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;on_chat_model_stream&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s2">&amp;#34;chunk&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">end&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">flush&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>分工可以这样理解：给用户看中间步骤、做流式 UI，用 astream_events；沉淀全量记录、做评估和监控，交给 LangSmith。两者不冲突。&lt;/p>
&lt;h2 id="查看追踪数据">查看追踪数据
&lt;/h2>&lt;p>登录平台后：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-5.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-5_hu587824023913815603.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-5_hu7873635904407332508.png 1024w"
loading="lazy"
alt="首页"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-6.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-6_hu9543388786062130606.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-6_hu279340018157319073.png 1024w"
loading="lazy"
alt="详情"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Projects 视图&lt;/strong>：所有项目的概览——运行次数、错误率&lt;/li>
&lt;li>&lt;strong>Traces 视图&lt;/strong>：项目下每条 trace 对应一次完整执行；点进去能看到 Chain / LLM / Tool / Retriever 的调用层级、输入输出、耗时&lt;/li>
&lt;li>出错时，错误信息和堆栈直接展示在 trace 里&lt;/li>
&lt;li>可以给 trace 打 metadata 和 tags，方便组织和筛选&lt;/li>
&lt;/ul>
&lt;h2 id="playground">Playground
&lt;/h2>&lt;p>Playground 是交互式的提示试验台：改提示、换模型、调参数（temperature、max_tokens），立即看输出；所有运行自动留 trace。compare 模式可以把两个版本的输出并排对比——调提示词时特别有用。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-7.png"
width="5104"
height="2662"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-7_hu5515896410810803528.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-7_hu16710636975432683832.png 1024w"
loading="lazy"
alt="playground"
class="gallery-image"
data-flex-grow="191"
data-flex-basis="460px"
>&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-8.png"
width="5104"
height="2662"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-8_hu17388606319755112034.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-8_hu16570506362763736550.png 1024w"
loading="lazy"
alt="compare"
class="gallery-image"
data-flex-grow="191"
data-flex-basis="460px"
>&lt;/p>
&lt;h2 id="prompt-hub">Prompt Hub
&lt;/h2>&lt;p>Prompt Hub 用来存放、版本化、复用提示模板。界面上创建模板，&lt;code>{xxx}&lt;/code> 占位符会自动识别成 inputs；修改模板会留 commit 记录，可以回溯。（&lt;a class="link" href="https://docs.smith.langchain.com/prompt_engineering/how_to_guides#prompt-hub" target="_blank" rel="noopener"
>文档地址&lt;/a>）&lt;/p>
&lt;p>代码里直接拉取调用：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-9.png"
width="3454"
height="2230"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-9_hu14466145963846159392.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-9_hu17314811126818976642.png 1024w"
loading="lazy"
alt="调用代码"
class="gallery-image"
data-flex-grow="154"
data-flex-basis="371px"
>&lt;/p>
&lt;p>创建模板，右边自动出现 inputs：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-10.png"
width="5104"
height="2662"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-10_hu15835326501385185382.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-10_hu6906222228396075153.png 1024w"
loading="lazy"
alt="创建方式"
class="gallery-image"
data-flex-grow="191"
data-flex-basis="460px"
>&lt;/p>
&lt;p>修改有 commit 记录：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-11.png"
width="3454"
height="2230"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-11_hu5092816811983418757.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-11_hu13980254746554264517.png 1024w"
loading="lazy"
alt="prompt commit"
class="gallery-image"
data-flex-grow="154"
data-flex-basis="371px"
>&lt;/p>
&lt;h2 id="数据集与评估">数据集与评估
&lt;/h2>&lt;p>评估是 LangSmith 对我来说价值最大的部分：&lt;strong>凭感觉调提示，和拿数据说话，是两回事。&lt;/strong>&lt;/p>
&lt;h3 id="创建数据集">创建数据集
&lt;/h3>&lt;p>三种方式：&lt;/p>
&lt;ul>
&lt;li>UI 里手动创建，添加输入和期望输出（Ground Truth）&lt;/li>
&lt;li>从生产 trace 里筛选样本，「Add to Dataset」存进数据集——基于真实用户交互做评估，这是最推荐的路子&lt;/li>
&lt;li>SDK 代码创建（见下）&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-12.png"
width="1912"
height="954"
srcset="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-12_hu3027362747169187572.png 480w, https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-12_hu9836541955965867834.png 1024w"
loading="lazy"
alt="手动上传数据集"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="481px"
>&lt;/p>
&lt;h3 id="sdk-创建数据集并运行评估">SDK 创建数据集并运行评估
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langsmith&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Client&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">evaluate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Client&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dataset_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;My Translation Evaluations&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create_dataset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dataset_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">description&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Dataset for evaluating translations.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create_example&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inputs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;input_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;English&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;output_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;French&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Hello&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">outputs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;expected_translation&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Bonjour&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dataset_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">dataset&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">except&lt;/span> &lt;span class="ne">Exception&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 大概率是数据集已存在，取现成的&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">list_datasets&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dataset_name_contains&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">dataset_name&lt;/span>&lt;span class="p">)[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 被评估的目标：包装成「接收 inputs、返回 dict」的函数（chain 即上文的翻译链）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">predict&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">inputs&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">translation&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;input_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">inputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;input_language&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;output_language&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">inputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;output_language&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">inputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">translation&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 自定义评估器：精确匹配&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">exact_match&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">example&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">expected&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">example&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">outputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;expected_translation&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">outputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">expected&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;key&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;exact_match&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;score&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">score&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">evaluate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">predict&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">dataset_name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">evaluators&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">exact_match&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">experiment_prefix&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;translation-eval&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>修订说明：原文这里原本是两行省略号占位（没有可运行代码），上面是按 langsmith SDK 的 &lt;code>evaluate()&lt;/code> 补全的最小示例，具体参数以&lt;a class="link" href="https://docs.smith.langchain.com/" target="_blank" rel="noopener"
>官方文档&lt;/a>为准。&lt;/p>
&lt;/blockquote>
&lt;p>评估器除了自定义函数，常用的几类：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>字符串评估器&lt;/strong>：精确匹配、正则、包含性判断&lt;/li>
&lt;li>&lt;strong>LLM-as-Judge&lt;/strong>：用另一个模型按标准打分（相关性、质量、有害性）&lt;/li>
&lt;li>&lt;strong>轨迹评估器&lt;/strong>：评 Agent 的完整执行轨迹（工具用得对不对），而不只是最终答案&lt;/li>
&lt;li>&lt;strong>比较评估器&lt;/strong>：两个版本在同一输入上并排比&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>⚠️ 版本注记：旧文档里的 &lt;code>RunCollector&lt;/code> / &lt;code>QAEvalChain&lt;/code>（langchain.evaluation 体系）已被 LangSmith 平台和 SDK 的 &lt;code>evaluate()&lt;/code> 取代，这里留个名字备查。&lt;/p>
&lt;/blockquote>
&lt;h3 id="我的建议">我的建议
&lt;/h3>&lt;ol>
&lt;li>先在 LangSmith UI 里熟悉评估流程，再上 SDK&lt;/li>
&lt;li>应用跑起来、有 trace 上报是评估的前提——评估的对象就是这些运行记录&lt;/li>
&lt;li>数据集从真实 trace 里攒，比凭空编造更接近真实分布&lt;/li>
&lt;/ol>
&lt;h2 id="监控与协作">监控与协作
&lt;/h2>&lt;p>应用部署后，LangSmith 可以持续监控：仪表盘看延迟、错误率、token 消耗、用户反馈；指标超阈值可设警报；用户点赞/点踩能关联到对应 trace。团队协作方面，组织内共享 trace、数据集和评估结果。&lt;/p>
&lt;h2 id="总结">总结
&lt;/h2>&lt;ul>
&lt;li>接入成本几乎为零：环境变量一设，LangChain 应用自动上报&lt;/li>
&lt;li>调试靠 trace，迭代靠 Playground + Prompt Hub，质量靠数据集 + 评估&lt;/li>
&lt;li>观测分工：给用户看的中间过程用 astream_events，记录和分析交给 LangSmith&lt;/li>
&lt;li>官方文档：&lt;a class="link" href="https://docs.smith.langchain.com/" target="_blank" rel="noopener"
>docs.smith.langchain.com&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>