1. 从一次报价翻车说起LangChain Tools 到底解决什么问题先说结论LangChain Tools 是把「模型不擅长的确定性操作」外包给真实函数的那层机制它让模型从「凭概率编答案」变成「先申请调用、拿到真结果再作答」。如果你正在做 Agent、需要多模型协作、又不想为每个模型单独维护一套 Key 和调用逻辑那这篇就是给你写的。我最早接触 LangChain Tools 是因为一个报价小助手。需求很简单给一个不含税金额和税率算出含税总价。我当时想都没想直接让模型算。结果它一本正经地报了个数字我拿去对账差了一大截。连续三次数字都对不上。后来才反应过来算术这件事模型本来就不擅长它在「编」而不是在「算」。把这段逻辑抽成一个真正的 Python 函数再用tool声明成工具绑给模型之后模型遇到报价会先申请调用这个工具拿到准确结果再组织语言回答。算错率直接掉下来。这就是 LangChain Tools 的核心价值模型负责判断「该调哪个、传什么参」函数负责「精确执行」。一个 LangChain 工具本质上就是带三样附加信息的可调用对象name工具名、description功能描述、args_schema参数说明。模型靠 name 和 description 判断该不该用、用哪个靠 args_schema 才知道参数怎么填。这三样填得越清楚工具调用的准确率越高。那为什么还要扯上 TaoToken因为当你把工具链跑通之后下一步一定是「换模型试试」。不同模型对工具调用的支持程度、参数填充风格都不一样你需要一个统一的入口来切换模型而不是每换一个模型就改一遍 base_url 和 key。TaoToken 在这里扮演的就是这个统一入口的角色一个 Key 打通多家模型的调用链。这篇会给你三样东西可复制的 Tool 定义模板、统一 Key 的接入配置、以及用最小示例验证工具调用链是否跑通的完整步骤。适合已经会写 Python、想认真把 Agent 工具链落地的人。2. 前置准备用 TaoToken 统一 Key 接入多模型调用链在写工具之前先把模型接入这层理顺。很多人卡在第一步想试不同模型就得去不同平台注册、拿不同的 Key、记不同的 base_url代码里到处是 if-else。TaoToken 的思路是把这些收敛成一个入口。TaoToken 的 API 地址是https://taotoken.net/api兼容 OpenAI 的接口格式。这意味着你原来用langchain-openai写的代码只需要改base_url和api_key两个地方就能把请求打到 TaoToken再由它路由到你指定的模型。对 LangChain 来说ChatOpenAI这个类照样能用因为它走的就是 OpenAI 兼容协议。先装依赖。LangChain 现在拆得很细工具相关的核心在langchain-core模型接入在langchain-openaipip install langchain-core langchain-openai python-dotenv pydantic然后配置环境变量。我习惯用.env文件管理避免 Key 硬编码进代码# .env TAOTOKEN_API_KEY你的TaoToken密钥 TAOTOKEN_BASE_URLhttps://taotoken.net/apiKey 在 TaoToken 控制台的 API Keys 页面创建地址是https://taotoken.net/console/api-keys。创建后复制出来填进.env就行。接下来是模型初始化。关键点在于base_url要指向 TaoToken 的 API 地址api_key用 TaoToken 的 Keymodel填你想用的模型 IDimport os from dotenv import load_dotenv from langchain_openai import ChatOpenAI load_dotenv() llm ChatOpenAI( modelgpt-4o-mini, # 换成你想用的模型 ID temperature0.1, # 工具调用场景建议低温减少乱填参数 api_keyos.getenv(TAOTOKEN_API_KEY), base_urlos.getenv(TAOTOKEN_BASE_URL), )这里有个细节值得说temperature在工具调用场景建议调低0 到 0.2 之间比较稳。因为工具调用要求模型精确输出工具名和参数温度太高它容易「发挥」把税率填成 13 而不是 0.13 这种事就来了。如果你用的是 Claude 系列模型LangChain 里有对应的ChatAnthropic但通过 TaoToken 接入时因为 TaoToken 兼容 OpenAI 协议你依然可以用ChatOpenAI这个类只是model填 Claude 的模型 ID。这样你的代码不用为不同厂商的模型写两套初始化逻辑切换模型只改一个字符串。这一步做完你就有了一个「统一入口的模型对象」。后面所有的工具绑定、调用都基于这个对象。想换模型改model参数就行工具代码一行不用动。这就是统一 Key 的价值把模型接入的复杂度收敛到一处让工具链本身保持干净。顺便提一句如果你打算长期跑 Agent、频繁调用可以了解下 TaoToken 的 Coding Plan它在高频调用场景下比按量计费更划算具体在https://taotoken.net/coding-plan能看到。3. 可复制配置tool 与 StructuredTool 两种工具定义模板这一节是全文的技术核心给你两套能直接抄的工具定义模板以及把它们绑给模型的完整配置。先看tool装饰器这条路。它的特点是函数式你在一个普通 Python 函数上加装饰器函数签名和 docstring 会自动变成工具的 schemafrom langchain_core.tools import tool def calc_total(base_price: float, tax_rate: float) - dict: 真正干活的业务函数确定性的算术逻辑。 total base_price * (1 tax_rate) return {total: round(total, 2), currency: CNY} tool def price_calculator(base_price: float, tax_rate: float) - str: 根据不含税金额和税率计算含税总价。 用于报价、订单金额计算等需要精确数字的场景。 result calc_total(base_price, tax_rate) return f含税总价为 {result[total]} {result[currency]}注意tool装饰的函数它的 docstring 会直接变成工具的description。模型就是靠这段描述判断「什么时候该用这个工具」。所以 docstring 别写废话要写清楚「这个工具干什么、什么场景用」。再看StructuredTool这条路。它是声明式的参数 schema 用 pydantic 单独定义可控性更强适合参数多、需要严格校验的场景from langchain_core.tools import StructuredTool from pydantic import BaseModel, Field class OrderArgs(BaseModel): base_price: float Field(description不含税的商品金额单位元) tax_rate: float Field( description税率小数形式如0.13表示百分之十三 ) order_tool StructuredTool.from_function( funccalc_total, nameorder_total, description计算一笔订单的含税总价适合报价与对账场景, args_schemaOrderArgs, )这里最关键的是Field(description...)。这段描述会被注入到 JSON Schema 里模型据此理解每个参数的含义。我见过太多工具调用不准根因都是参数描述含糊。比如税率这个参数如果你只写「税率」模型可能填 13你写「小数形式如0.13表示百分之十三」它就知道该填 0.13。给每个参数一条准话正确率立刻不一样。两种方式怎么选小工具、参数简单用tool省事参数多、要严格校验、要复用 schema用StructuredTool。两条路都得会。工具定义好之后要绑给模型。这一步用bind_toolstools [price_calculator, order_tool] llm_with_tools llm.bind_tools(tools)bind_tools做的事情是把工具的 name、description、args_schema 打包成 JSON Schema拼进每一次 API 请求的工具列表里。模型只是「看见了工具」真正执行工具背后的函数还是你的活。这一点很多人误解以为 bind 完模型就会自己调其实不是。如果你用的是 Claude Code 这类工具配置方式略有不同需要在 settings 里指定 Base URL、Key 和 Model ID 三件套。以 Claude Code 的配置为例大致是这样{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: 你的TaoToken密钥, ANTHROPIC_MODEL: claude-sonnet-4-20250514 } }这三件套——Base URL、Key、Model ID——是任何模型接入的通用要素记住这个结构换任何工具都能套。Claude Code 的详细接入文档在https://taotoken.net/doc能看到。4. 验证请求跑通一次完整的工具调用链配置写完了得验证它真的能跑。这一节给你一段最小可运行代码走通「模型申请调用 → 你执行函数 → 回填结果 → 模型作答」的完整链路。import os from dotenv import load_dotenv from langchain_core.tools import tool from langchain_core.messages import ToolMessage from langchain_openai import ChatOpenAI load_dotenv() def calc_total(base_price: float, tax_rate: float) - dict: total base_price * (1 tax_rate) return {total: round(total, 2), currency: CNY} tool def price_calculator(base_price: float, tax_rate: float) - str: 根据不含税金额和税率计算含税总价。 result calc_total(base_price, tax_rate) return f含税总价为 {result[total]} {result[currency]} llm ChatOpenAI( modelgpt-4o-mini, temperature0.1, api_keyos.getenv(TAOTOKEN_API_KEY), base_urlos.getenv(TAOTOKEN_BASE_URL), ) tools [price_calculator] llm_with_tools llm.bind_tools(tools) user_ask 帮我算一下不含税1000元、税率13%的含税总价大概是多少钱 # 第一轮模型只返回工具调用请求 ai_msg llm_with_tools.invoke(user_ask) print(模型是否申请调用工具, ai_msg.tool_calls) if ai_msg.tool_calls: name ai_msg.tool_calls[0][name] args ai_msg.tool_calls[0][args] print(被调用的工具, name) print(模型给的入参, args) # 按 name 从工具箱里查对应的函数别写死 tool_map {t.name: t for t in tools} chosen tool_map[name] raw_result chosen.invoke(args) tool_message ToolMessage( contentstr(raw_result), tool_call_idai_msg.tool_calls[0][id], ) # 第二轮把工具结果回填得到最终回答 final llm_with_tools.invoke([user_ask, ai_msg, tool_message]) print(最终回答, final.content) else: print(模型决定直接回答, ai_msg.content)跑起来之后你会看到第一轮ai_msg.tool_calls里有一个工具调用请求包含工具名price_calculator和入参{base_price: 1000, tax_rate: 0.13}。注意模型把「13%」正确识别成了0.13这就是参数描述写清楚的效果。然后代码手工执行chosen.invoke(args)拿到真实计算结果包装成ToolMessage回填。第二轮 invoke 之后模型基于真实结果给出自然语言回答比如「含税总价约为 1130 元」。这里有个关键点tool_call_id必须和第一轮请求里的 id 对上。模型靠这个 id 把「工具结果」和「工具请求」对应起来。如果对不上模型会困惑可能报错或者忽略结果。验证成功的标志有三个第一ai_msg.tool_calls非空说明模型感知到了工具第二入参正确说明 schema 描述到位第三最终回答引用了工具算出的数字而不是凭空编。三个都满足说明你的工具调用链跑通了。如果你想在网页上直接对比不同模型的工具调用表现可以用 TaoToken 的模型对话功能地址是https://taotoken.net/model-chat切换模型看同一个 prompt 下工具调用的差异很直观。5. 常见报错排查401、local proxy failed、reading choices 怎么解工具链跑不通八成是下面几个错。我按实际遇到的频率排一下。401 Unauthorized。这个最常见基本是 Key 的问题。检查三处.env里的TAOTOKEN_API_KEY有没有填对、有没有多余空格、load_dotenv()有没有在读取环境变量之前调用。还有一种情况是 Key 复制时漏了字符建议重新去https://taotoken.net/console/api-keys复制一遍。如果 Key 没问题检查base_url是不是写成了https://taotoken.net/api少个/api或者多个斜杠都会导致鉴权失败。local proxy failed / connection error。这类报错通常是网络层的问题不是代码逻辑。先确认base_url拼写正确再确认你的运行环境能正常访问外网。如果你在公司内网可能有防火墙拦截需要走公司允许的出口。注意这里说的是正常的网络连通性排查不涉及任何特殊网络工具。Error reading choices / response parsing error。这个错通常出现在模型返回的格式和 LangChain 预期的不一致时。常见原因有两个一是模型本身不支持工具调用tool calling你 bind_tools 之后它返回的是普通文本而不是 tool_calls 结构二是temperature太高模型输出格式飘了。解决办法换一个明确支持工具调用的模型把 temperature 降到 0.1 以下。如果你不确定某个模型支不支持工具调用可以在 TaoToken 的文档里查模型能力表。OAuth / authentication 相关报错。如果你用的是 Claude Code 这类工具报 OAuth 错误通常是配置里的认证字段没对上。Claude Code 走的是ANTHROPIC_API_KEY和ANTHROPIC_BASE_URL这套环境变量别和 OpenAI 的OPENAI_API_KEY混用。检查你的 settings 文件里字段名是否正确Base URL、Key、Model ID 三件套是否齐全。工具被调用但取错函数。这个不是报错是逻辑 bug但很隐蔽。我踩过一次把两个工具都 bind 给模型模型调用的是order_total我代码里却硬匹配price_calculator结果找不到工具直接崩。修法是建一个 name 到对象的映射表按 name 查别写死tool_map {t.name: t for t in tools} chosen tool_map[name]这样任何工具都能被正确找到不会再出现「工具在但取不对人」的事故。模型不调用工具直接回答。这种情况说明模型没意识到该用工具。排查方向工具的description是不是写得太模糊模型判断不出适用场景或者用户提问的方式让模型觉得可以直接答。解决办法是把 description 写具体明确「什么场景用这个工具」必要时在系统提示里引导模型优先使用工具。排错的核心思路是先确认 Key 和 base_url 这层接入没问题再确认模型支持工具调用最后检查工具定义和调用逻辑。一层层往下排别一上来就怀疑代码。6. 把工具链用起来从单工具到多模型协作工具链跑通之后真正的价值在于复用和扩展。先说复用。把一组工具做成一个列表或字典绑定给不同的模型对象就能在不同 Agent 里秒复用。比如你有一个报价工具、一个查库存工具、一个算运费工具打包成一个tools列表今天绑给 GPT 系列明天绑给 Claude 系列工具代码一行不用改。这就是统一 Key 加统一工具定义带来的好处模型可换工具不动。再说多模型协作。有些场景下不同模型各有所长。比如一个模型擅长理解复杂指令、决定调哪个工具另一个模型擅长生成最终的自然语言回复。你可以用 TaoToken 的统一入口在同一套代码里初始化两个模型对象让它们各司其职。工具绑定和调用逻辑是共享的只是模型对象不同。错误处理这块也得补上。工具背后的函数会受各种扰动记得用 try/except 包起来失败时返回一段中文说明而不是把异常抛出去tool def price_calculator(base_price: float, tax_rate: float) - str: 根据不含税金额和税率计算含税总价。 try: if base_price 0: return 金额必须大于0请重新确认。 result calc_total(base_price, tax_rate) return f含税总价为 {result[total]} {result[currency]} except Exception as e: return f计算失败{e}请检查输入参数。这样模型拿到的是「一段可读的说明」它会理解并换个方式重试而不是整个流程崩掉。工具要提供线索而不是制造中断。最后是安全。工具背后是真实副作用下单、删数据这类写操作务必做权限校验和参数白名单。别让模型自由调用任意函数。我给写操作工具都加了二次确认多一层安全网。走到这里你已经有了一个能感知并调用外部能力的工具型 Agent 雏形。剩下的就是根据你的业务把更多工具接进来把多模型协作的循环串起来。工具定义模板、统一 Key 配置、验证步骤这三样你都有了接下来就是动手改造成你自己的场景。
阅读完成 · 觉得有帮助?