恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

LangChain工具调用实战:从Schema设计到Agent落地

  • 首页
  • 资讯中心
  • /
  • LangChain工具调用实战:从Schema设计到Agent落地

相关资讯

PSO-BP神经网络权重优化原理与Matlab实现详解 2026/10/19 9:38:48
JavaWeb购书系统:Servlet+JSP+JDBC全流程实战 2026/10/19 9:38:48
DeskcommCRM深度解析:通讯型CRM的核心功能与落地实操 2026/10/19 9:38:47

最新资讯

C# OPC DA服务器与客户端测试Demo实战解析
Token级重要性采样与近端KL正则化在RLVR中的应用
恶劣天气道路目标检测:YOLO标注数据使用与避坑指南
MySQL核心原理与实战:SQL执行顺序、索引、事务与备份全解析
基于龙蜥Anolis制作OpenClaw Docker镜像的实践与排坑指南
无人机信息化项目管理实践:从架次级记录到全流程数据管控

今日推荐

OpenCowork 实测:支持本地文件、飞书机器人的 Windows AI 助手(只需配置 Token)
GPT-6 Sol 与 Luna 怎么选?Chat、Work、Codex 版本差异与 TaoToken 接入实测
openclaw.net 集成 ElBruno.MempalaceNet 记忆系统:把 MCP 配置改到 TaoToken 的完整实践

本周热门

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

LangChain工具调用实战:从Schema设计到Agent落地

发布时间:2026/10/19 9:38:48
LangChain工具调用实战:从Schema设计到Agent落地 前阵子和几个做 LLM 应用的朋友聊天发现大家提到 LangChain 时关注点还停留在“怎么把提示词写得更好”“怎么接向量库做 RAG”但一旦业务真正往前推进比如让模型去查一下天气、拉一下订单数据、发一封邮件、调一个内部接口所有人都会撞到同一个核心环节——Tool Calling工具调用。这篇博文从一个已经在落地的查询助手项目出发完整拆解我在 LangChain 里用工具调用的思路和实操从工具定义的 schema 设计、bind_tools 如何绑定模型、执行循环怎么写到 ToolMessage 回传的细节再到我实际踩过的几个坑和排查思路。无论你是刚接触 LangChain 的开发者还是已经能跑通简单 demo、正在往真实业务里迁移的人这篇文章里应该都有能直接拿走用的内容。我记得最开始接触工具调用时直接去翻官方文档结果被一堆抽象概念挡住了——又是 Tool 又是 ToolCall 又是 bind_tools看完了还是不知道代码到底怎么组织。后来硬着头皮把一个完整流程跑通了回头再看文档才算真的理解那些设计为什么存在。这篇博文就是按我理解的顺序来写的先讲清楚工具调用到底解决了什么问题再一步步给出代码级实现最后是我自己在项目中反复踩坑总结的排查经验。1. 工具调用的思路拆解大模型不该“假装”调用工具1.1 没有 Tool Calling 之前我们是怎么做的在 LangChain 把 Tool Calling 做成标准能力之前最土的办法是在提示词里写一大段说明“当你需要查询天气时请输出WEATHER[城市名]这样的格式然后我会解析你的输出。”听起来没问题实际操作起来全是泪。模型可能输出查询天气北京可能输出WEATHER[北京]也可能输出我来查一下北京的天气[北京]。你写正则去匹配一个模型版本一个脾气换一个模型又要重新调规则而且模型一旦在文本里“角色扮演”过头还会出现它其实没调用任何工具、却一本正经告诉你结论的情况。这个问题其实就是“模型在文本层面模拟工具调用而系统在规则层面解析文本”两边没有一个标准化的接口。这种做法的本质是把结构化的信息塞进非结构化的自然语言里然后靠脆弱的字符串解析把它捞出来。只要输出格式稍微漂移一点整个链路就断掉。所以后来业界才慢慢收敛出一个共识与其让模型“假装”调用工具不如在模型能力里直接定义一种结构化的调用请求格式让模型在输出环节就明确表达“我要调用哪个工具、传什么参数”。1.2 Tool Calling 把“调用意图”变成结构化数据工具调用的核心变化是模型不再输出一段文字来暗示它想干嘛而是直接输出一个结构化的调用请求通常包括三块信息工具名name参数args一个 JSON 对象调用 IDid用于关联请求和结果你可以把模型理解成一个决策者它不负责执行具体动作只负责“决定”该调哪个工具、参数是什么。真正执行的是你自己的代码执行完拿到结果再作为一条新消息喂回给模型让它基于结果继续生成回答。这个“决策—执行—回传—再决策”的循环就是 Agent 应用最基本的工作形态。这个设计的妙处在于语言模型的核心能力理解意图、生成符合格式的内容和外部世界的能力实时数据、业务系统、物理设备被清晰地切开了。模型不需要真的会查数据库它只需要知道“有一个查订单信息的工具参数是订单号”然后生成一个结构正确的调用请求剩下的数据库查询逻辑由你自己的代码来完成。这样一来每增加一个外部能力就只是多注册一个工具模型本身不用重新训练系统扩展性一下子打开了。1.3 LangChain 在这条链路里的坐标LangChain 里这个能力被抽象成了三层结构BaseTool/tool用来定义工具LangChain 会从函数签名和 docstring 自动生成描述信息和 JSON Schema。bind_tools()把工具列表绑定到模型上告诉模型“你现在有这些工具可以用”。ToolMessage把工具执行结果以消息形式回传给模型让模型看到结果后继续输出。理解这三个东西整条链路就通了。你定义一个或多个工具把它们 bind 到模型模型在生成时如果判断需要调用工具就返回一个AIMessage里面带有tool_calls字段你检查到这个字段拿参数去执行对应函数然后把结果包成ToolMessage追加到消息列表里把整个消息列表再次发给模型模型要么继续调工具要么终于给出最终答案。我画过很多次这个循环最后发现真正容易出错的地方全在细节上schema 没写对导致模型传参传歪了tool_call_id 对不上导致消息关联错乱工具执行结果没回传导致模型“失忆”。这些细节我会在后面专门展开。2. 工具定义schema 不写对后面全是坑2.1 从函数签名到 JSON Schema 的自动推导在 LangChain 里定义工具最简单的姿势是直接用tool装饰器。from langchain_core.tools import tool tool def get_weather(city: str, unit: str celsius) - str: 查询指定城市的实时天气。 Args: city: 城市名称中文比如“上海” unit: 温度单位可选 celsius 或 fahrenheit默认 celsius # 这里在生产环境会去请求真实的天气服务 return f{city} 当前 24°C天气晴{unit}就这么几行LangChain 会做几件事从函数签名拿到参数名和类型从类型注解生成 JSON Schema从文档字符串提炼工具和参数的描述信息。这个get_weather变量本身就是一个BaseTool实例可以直接传给bind_tools()。看起来黑魔法很爽但这里有个关键认知模型不理解你的函数代码它只理解那个从你的代码生成的 JSON Schema。你写的一切“为了让人看懂”的东西最终都得翻译成模型能看懂的自然语言描述。所以工具描述、参数描述的质量直接决定模型能不能正确使用工具。我见过太多人随便写一句“查询天气”结果模型传入各种千奇百怪的参数值或者干脆把这个工具和另一个工具搞混。2.2 JSON Schema 的四要素一个标准工具的 JSON Schema 长这样直接看 LangChain 生成结构会更清楚{ type: function, function: { name: get_weather, description: 查询指定城市的实时天气。, parameters: { type: object, properties: { city: { type: string, description: 城市名称中文比如“上海” }, unit: { type: string, enum: [celsius, fahrenheit], description: 温度单位可选 celsius 或 fahrenheit默认 celsius } }, required: [city] } } }四个要素缺一不可type: object参数本身必须是一个 JSON 对象这是约定俗成的标准。properties每个参数的类型、枚举约束、描述。required哪些参数是必填的选填参数别硬塞进 required。description工具和参数的自然语言描述。这里最容易犯的错是给unit设置了默认值就把unit从 required 里拿掉了——这没问题。但如果你在函数里写了if unit celsius而在 schema 里没有用enum约束模型就可能传C、摄氏、cel之类的值进来。如果业务上只接受两种输入一定要在 schema 层面写死enum而不是在函数里做兼容。2.3 什么时候需要手写 BaseTooltool装饰器解决了 90% 的场景但它也有做不到的地方工具需要依赖外部 API 的 client比如数据库连接、HTTP session且这个依赖不能放进函数参数。参数校验逻辑复杂比如多个参数之间有约束关系start 必须小于 end。需要在调用工具前后做埋点、鉴权、限流。工具执行结果需要按自定义格式返回而不是简单的字符串。这种时候可以用BaseTool子类from langchain_core.tools import BaseTool from pydantic import BaseModel, Field class SearchInput(BaseModel): query: str Field(description搜索关键词) limit: int Field(default10, description返回结果数量最大 50) class SearchTool(BaseTool): name: str search_docs description: str 在内部文档库中搜索相关资料返回标题和摘要列表。 args_schema: type[BaseModel] SearchInput def _run(self, query: str, limit: int 10) - str: # 真实的搜索逻辑 return 搜索结果用 Pydantic 模型定义args_schema的好处是校验能力更强你可以在 Field 里写ge1, le50这样的约束。LangChain 会把这些约束自动映射到 JSON Schema 的minimum、maximum字段给模型更明确的指引。2.4 工具数量多起来之后的命名规范我见过一个项目把 30 多个工具全部堆在一个文件里工具名有叫search的、有叫query_user_info的、有叫get_user_profile_by_id的。模型每次要从几十个工具里选很容易混淆。后来我们订了一条规范工具命名用“动词业务对象限定条件”的结构比如get_user_profile、list_user_orders、cancel_user_order描述里必须写明适合什么场景、不适合什么场景。这条极其重要——模型选错工具很多时候不是模型笨而是工具描述和名字本身有歧义。你多花十分钟把描述写清楚线上就能少踩很多次的坑。3. 绑定模型、执行调用与结果回传3.1 bind_tools 与模型侧的返回结构工具定义好了接下来要做的是把它绑到模型上。以 OpenAI 系模型为例from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage llm ChatOpenAI(modelgpt-4o, temperature0) llm_with_tools llm.bind_tools([get_weather]) resp llm_with_tools.invoke([HumanMessage(content上海今天适合出门吗)]) print(resp)注意看resp是一个AIMessage它有两个关键字段content可能是空字符串因为模型决定不直接回答而是去调工具。tool_calls一个列表里面每一个元素包含name、args、id、type。tool_calls里可能有多个调用说明模型认为一次需要调多个工具比如既查天气又查空气质量。LangChain 的AIMessage.tool_calls已经帮你解析好了不需要手动处理 OpenAI 原始返回里的function_call结构。bind_tools做的事本质上是把工具列表转换成模型厂商要求的 function 格式并在请求体里加上tools字段。你在 LangChain 里面对的是统一的接口底层不同厂商的格式差异被框架屏蔽了。这也是 LangChain 的价值之一你换模型供应商业务代码几乎不用动。3.2 tool_choice 参数的控制力bind_tools还接受一个tool_choice参数控制模型“何时”以及“如何”选工具取值行为适用场景不传 /auto模型自行判断是否调用工具大多数通用场景none禁止调用任何工具只做普通对话时required强制必须调用某个工具由模型选测试工具链路是否通具体工具名字符串强制调用指定工具某些路由场景比如用户消息进来先强制用意图识别工具我实际用到最多的其实是前两种。required我一般只在调试时用用来确认某个工具本身能不能被正确解析、执行环境有没有问题。你想强制模型调用某一个明确工具直接传工具名即可llm_with_tools llm.bind_tools([get_weather, search_docs], tool_choiceget_weather)设置tool_choicenone有时候能解决一些“模型过度调用工具”的问题。比如用户只是闲聊一句“你好”模型却非要去查一下天气这时候把tool_choice临时调成none反而更稳。更灵活的做法是写一个分类器先判断意图再决定这次请求走不带工具的链路还是带工具的链路。3.3 执行工具后如何构造 ToolMessage如果检测到resp.tool_calls不为空就需要执行工具然后把结果回传。核心代码如下from langchain_core.messages import AIMessage, ToolMessage def run_tool_call(tool_call): tool_name tool_call[name] tool_args tool_call[args] tool all_tools_map[tool_name] result tool.invoke(tool_args) return result # 假设 resp 是一个带 tool_calls 的 AIMessage all_tools_map {tool.name: tool for tool in [get_weather]} tool_messages [] for tool_call in resp.tool_calls: result run_tool_call(tool_call) tool_messages.append( ToolMessage( contentresult, tool_call_idtool_call[id], nametool_call[name] ) )这里有几个细节极易翻车tool_call_id必须和AIMessage.tool_calls里的id一一对应。模型要靠这个 ID 把结果和之前的请求关联起来。如果你自己捏一个随便的 ID很多模型会直接懵掉表现为“答非所问”或者“幻觉”。content格式要和后续模型的理解能力匹配。如果工具返回的是 JSON 字符串尽量序列化得干净一点如果你返回一个 Python 对象LangChain 会尝试转字符串但可能不是模型最好消化的形态。多个工具并行调用时最好保持 tool_call 的顺序和对应 ToolMessage 的顺序一致减少不必要的混乱源。3.4 完整循环直到模型不再调用工具整个工具调用流程是一个循环不是一次性调用。最简单的实现如下from langchain_core.messages import SystemMessage, HumanMessage def run_agent(user_input: str, max_iterations: int 5): messages [ SystemMessage(content你是一个助手可以调用工具获取信息并回答用户问题。), HumanMessage(contentuser_input), ] for _ in range(max_iterations): response llm_with_tools.invoke(messages) messages.append(response) if not response.tool_calls: return response.content for tool_call in response.tool_calls: tool all_tools_map[tool_call[name]] result tool.invoke(tool_call[args]) messages.append( ToolMessage(contentresult, tool_call_idtool_call[id]) ) return 达到最大迭代次数停止执行。这一步看起来简单实际生产里你还要考虑工具执行异常的情况。比如工具内部报错你是把异常信息直接作为 content 回传还是包装一层友好的错误提示我的习惯是用一个 try-except 包裹tool.invoke捕获异常后返回格式化的错误字符串比如工具执行失败连接超时。把原始堆栈回传给模型没有意义模型拿着堆栈也修不了 bug反而可能一本正经地“解释错误原因”误导用户。4. 实操过程与核心环节实现做一个带记忆的查询助手4.1 先搭一个最小闭环一块空地直接上复杂 Agent 架构容易翻车我建议先跑通最小闭环。下面这个例子你可以直接复制去跑只需要把 OpenAI 的 API Key 配上。import json from langchain_openai import ChatOpenAI from langchain_core.tools import tool from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage tool def get_current_weather(city: str) - str: 查询指定城市的实时天气返回温度和天气状况。 Args: city: 城市名称中文比如“北京” # 这里用模拟数据替代真实天气 API data { 北京: {temp: 26, condition: 晴}, 上海: {temp: 29, condition: 多云}, 广州: {temp: 33, condition: 小雨}, } info data.get(city, {temp: 25, condition: 未知}) return json.dumps({city: city, **info}, ensure_asciiFalse) llm ChatOpenAI(modelgpt-4o, temperature0) llm_with_tools llm.bind_tools([get_current_weather]) messages [ SystemMessage(content你是一个天气助手。当用户询问天气时必须调用 get_current_weather 工具获取信息再基于结果回答。), HumanMessage(content北京今天多少度), ] for _ in range(3): resp llm_with_tools.invoke(messages) messages.append(resp) if not resp.tool_calls: break for tc in resp.tool_calls: tool_result get_current_weather.invoke(tc[args]) messages.append(ToolMessage(contenttool_result, tool_call_idtc[id])) print(resp.content)跑通这个闭环后你会发现一个现象模型在第一轮返回AIMessage(content, tool_calls[...])执行完工具回传后第二轮才开始生成真正面向用户的回答。这就是上面说的“决策—执行—再决策”循环。4.2 引入多轮对话与历史记忆实际业务里用户不会只问一句而是会追问“那明天呢”“上海呢”。这时候如果消息列表里只有当前这一轮的 HumanMessage模型就失去了上下文。最简单的做法是把历史消息一起放进 messages 列表from langchain_core.messages import AIMessage history [] # 存储历史消息 def chat_step(user_input: str): global history current_messages [ SystemMessage(content你是助手。请根据对话历史回答用户问题必要时调用工具。) ] history [HumanMessage(contentuser_input)] for _ in range(5): resp llm_with_tools.invoke(current_messages) current_messages.append(resp) if not resp.tool_calls: break for tc in resp.tool_calls: tool_result get_current_weather.invoke(tc[args]) current_messages.append( ToolMessage(contenttool_result, tool_call_idtc[id]) ) history.extend([HumanMessage(contentuser_input), resp]) return resp.content注意一个细节history里存的是最终那一轮模型回答而不含中间过程里的 ToolMessage。如果你要把所有过程消息都存进去也能工作但会把历史撑得很大。我一般只保留对用户可见的那条最终AIMessage中间的推理过程通过langchain_core.messages的RemoveMessage或者自己拼消息列表时选择性过滤掉。历史存久了会超过模型的上下文窗口后续需要做消息裁剪、摘要压缩。朗Chain 里的trim_messages可以按 token 数从前往后裁剪或者用摘要模型把早期对话压缩成一段概述。这里面的细节不少但工具调用的核心循环不受影响属于接入长期记忆时的优化话题。4.3 多工具注册与并行调用当业务扩到多个工具时维护 all_tools_map 就有点蠢了。LangChain 推荐直接用列表bind_tools 接受列表或者 Runnable 列表。查找工具时可以这样from langchain_core.tools import tool tool def search_docs(query: str) - str: 在知识库中搜索文档返回相关片段。 tool def get_user_info(user_id: str) - str: 查询用户信息参数为 user_id。 tools [get_current_weather, search_docs, get_user_info] llm_with_tools llm.bind_tools(tools) # 执行工具时建立一个 name - tool 的映射 tool_map {t.name: t for t in tools}对于并行工具调用模型的tool_calls可能同时包含两个调用请求。上面的循环已经天然支持了——for 循环会依次执行每个工具然后每个都生成一个 ToolMessage。不过要注意LangChain 里AIMessage.tool_calls的并行只是模型层面的并行请求真正执行时如果你的代码用的是同步invoke还是串行的。如果工具调用很慢比如每个要 3 秒一次并发两个就是 6 秒可以用asyncio.gather或者run_in_executor做真正的并发。这一步的优化收益在真实业务里非常明显。5. 常见问题与排查技巧实录5.1 模型就是不调用工具怎么办这是最让新手崩溃的问题。提示词里明明写了“需要时调用工具”工具描述也写了模型却自顾自地回答。我总结过几个常见原因和排查顺序现象可能原因排查方向模型从不调用工具模型版本本身不支持 function calling确认使用的模型是否支持工具调用比如某些轻量模型并不支持模型从不调用工具temperature 过高导致输出随机把 temperature 调到 0 或 0.1模型从不调用工具工具描述和用户问题关联太弱重写描述明确写出“当用户问天气时调用此工具”模型偶尔不调用总的 messages 里工具被系统提示词“压住”了检查 SystemMessage 里有没有自相矛盾的指令模型不调用但直接生成答案工具本身能算出的信息和模型知识重合这是模型“自信”了可以在描述里强调“必须调用工具获取实时信息”另外还有一类隐蔽问题工具描述里用了“如果/可能/有时”这类含糊词模型就容易判断“这次可以不调”。如果你想让它积极调用描述里用明确的触发条件比如“当用户询问任何城市天气时调用此工具”。5.2 工具参数总传错参数传错分几种情况。最常见的是类型错误比如 schema 要求 integer 的 limit模型传进来一个字符串 10。这时候 Pydantic 的校验会帮你兜底转换如果配置了 coerce 的话但业务逻辑里最好还是显式做一次防御。第二种是枚举值不对。模型传unitC而不是celsius。这基本是 schema 里没写 enum 导致的把允许值写死到 enum 里能解决大部分问题。第三种是缺参。模型漏传了必填参数报错信息直接抛给模型。我建议工具内部自己做 catch返回一个“参数不完整缺少参数 city”的标准化错误字符串模型看到这个提示后往往会自动修正参数再次调用。如果你直接抛异常导致链路中断体验就很差。之前我还遇到过一种情况工具函数的参数名过于缩写比如q、l、u模型根本猜不出含义。给参数起一个语义明确的名字比描述里反复解释有用得多。5.3 循环不退出或者明显空转工具调用链路卡死在循环里可能的表现是模型反复调用同一个工具但参数几乎一样或者模型调工具、看到结果、又调同一个工具。加最大迭代次数只是兜底方案更关键的是搞清楚为什么空转。工具返回内容和用户的真实需求错位。比如用户在问“上海的天气”工具返回的却是一大段 JSON 里包含“上海”两字但模型没找到温度字段于是又调一次。解决办法是让工具返回结构化但精炼的结果最好一行人能看懂。模型在“尝试”不同的参数组合但你的工具描述没有给它足够信息它只能靠猜。这时候需要优化工具描述明确参数的含义和可选范围。多次调用确实有必要比如先查用户 ID 再查订单这是正常的链式调用。需要区分“合理多步”和“空转”。当迭代次数接近上限时打印完整的 messages 列表看每一步内容就能很快定位。我习惯在循环里加一个print或者日志输出每一步的动作[STEP 1] call get_current_weather args{city: 北京}、[STEP 2] tool result: ...。调试阶段这种原始日志比什么调试器都好用。5.4 ToolMessage 关联错乱如果构建 ToolMessage 时 tool_call_id 不匹配模型会认为“我调了工具但没收到结果”于是可能重复调用或者直接乱答。我排查这个问题的经验是打印全部消息人工检查每个 ToolMessage 的 tool_call_id 是否在之前的 AIMessage.tool_calls 里出现过。还有一个细节ToolMessage 的name字段需要填工具名。某些模型 API 对 name 有格式要求LangChain 会帮你做兼容但如果你手动构造最好把 name 一并传上。碰到“消息包含不受支持的字段”之类的报错时检查一下有没有把 ToolMessage 传错位置或者漏填参数。6. 工具调用背后的工程化设计6.1 权限边界要放在工具层工具调用给了模型调用外部世界的能力但同时也把权限边界问题推到了你面前。模型本身没有“便宜”和“危险”的概念如果你把一个delete_order工具直接 bind 进去模型在用户说“删掉刚才那单”的时候就会去调而不会先问一句“你确定吗”。我的做法是分三层控制对外暴露的工具有一套名字和服务范围内部真实操作另一个名字用户交互层再确认一次。危险操作删除、转账、发送消息一律在工具内部做二次校验参数里的用户 ID 是否和会话 ID 匹配、是否在可操作白名单内、是否超过频率限制。工具函数里记录完整调用日志包括传入参数和调用时间出了问题能回溯。这些都是工程层面的门禁不要指望模型“理解”权限。它只是一个参考真正把关的是你写的代码。6.2 工具数量膨胀后的命名与描述管理工具一旦超过十个描述管理就要规范化。我习惯建立一个工具清单表包含工具名、业务域、触发条件、参数说明、返回示例、所属服务。每次新增工具先更新这个表格再写代码。这样做的好处是当你需要对照检查“为什么模型总选错工具”时把这个表格和模型实际调用记录对照看往往一眼就能发现描述歧义。为了让模型更好区分相似工具我还会在描述里加上“反例”。比如有一个search_docs和一个search_web我分别写search_docs在内部知识库中检索文档。当用户提到“文档”“手册”“内部资料”时使用。不要用于公开网络信息查询。search_web在公开互联网上搜索。当用户询问实时新闻、外部网页时使用。不要用于内部文档检索。模型对“什么时候用”“什么时候不用”的描述非常敏感这两句话能明显降低选错概率。6.3 从“单轮调用”扩展到“多轮 Agent”工具调用本身不复杂复杂的是在它之上长出 Agent 行为。一个真正能解决业务问题的 Agent通常需要结合多轮对话记忆上文说了怎么存历史。多工具链式调用先调用户查询工具拿到 ID再调订单查询工具最后调知识库补充条款解释。条件判断根据工具返回内容决定下一步调哪个工具或者直接结束。中断与确认危险操作先问用户用户确认后再执行。这些都是在工具调用循环外围叠加状态机和策略。理解了核心循环后面这些都算是工程量的堆积而不是概念上的跳跃。我建议不要一上来就上各种框架先用原生的消息列表手写一个最小 Agent把循环里的每一步都打印出来看清楚。等你对状态流转有了手感再去考虑用框架的 Agent 抽象不然遇到问题根本不知道是框架的问题还是自己的问题。7. 我在实际项目里的几点体会真正把这套东西跑进生产环境和写 demo 是两回事。我第一次写工具调用 demo 只用了半天但上线前调试了一个多星期核心问题全出在我以为“模型会像我一样理解我的工具”。有一次我写了一个查订单的工具参数是order_id描述写的是“订单号”。我以为够了结果模型面对用户说“帮我看看上周买的那双鞋到哪了”它选择直接调用工具把order_id传成了“上周买的那双鞋”。这当然查不到。后来我把描述改成“订单号通常由纯数字组成如果用户没有提供则不要调用此工具而是先询问用户订单号”情况才好转。这件事给我的教训是工具描述要面向“使用者”写这个使用者是没有常识的模型它只会按你描述里的字面含义去理解。另一个体会是日志的重要性。工具调用天然是多步交互某一步出错用户看到的只是最终那句“抱歉我无法完成”。如果没有每一步的工具调用日志你根本无从排查。我现在所有工具都加了统一日志装饰器记录工具名、入参、出参、耗时线上定位问题基本靠这个。最后如果你正准备在自己的项目里引入 LangChain 工具调用我的建议是从最小的闭环开始先让一个工具跑通再逐步叠加。不要一上来就搭一个复杂的 Agent 框架因为工具调用这个机制本身足够底层把它吃透了上面再盖多少层概念都不怕。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号