ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

从零搭建AgentChat:构建能调用工具的智能对话系统

从零搭建AgentChat:构建能调用工具的智能对话系统 1. 项目概述与核心价值1.1 AgentChat 到底是什么先直接说结论AgentChat 不是一个玩具项目它本质上是一个“能干活”的对话系统。传统聊天机器人只能一问一答你说一句它接一句而 AgentChat 里跑的是一个智能体循环——模型不仅能理解你的话还能自己决定调用哪些工具、按什么顺序调用、拿到结果后再继续推理直到完成你交付的任务。很多人会把 AgentChat 和普通 Chatbot 搞混。举个最直白的例子普通 Chatbot 就像前台客服你问“几点了”它回答“现在下午三点”AgentChat 更像一个实习生你告诉它“帮我整理一下这个文件夹里的报告提取关键结论列成清单发我邮箱”它会拆解任务、遍历文件、调用解析工具、汇总结果、调用邮件接口——整个过程不需要你一步步指挥。我最早接触这个概念是在做内部自动化工具时团队需要一个能连接数据库、文件系统、API 服务的中枢节点而不是再写一堆死板的 if-else 调度脚本。后来大模型能力上来了这个中枢节点自然就演变成了 AgentChat。现在你可能也发现了市面上各种 AI 助手、Copilot、自动化工作流底层核心架构基本都是这么一套东西。所以从零搭一个简单的 AgentChat不是为了写 demo 炫技而是在理解现在所有 AI 应用的地基。1.2 这个项目能解决什么问题从实际应用角度来说搭建 AgentChat 的首要价值是打破模型“只能聊天、不能做事”的局限。底层大模型无论多聪明它的知识截止到训练时间它也无法直接读取你本地的数据库、调用你公司的内部接口。AgentChat 就是那个“接线员”——把模型和外部世界连接起来。其次它帮你把复杂任务自动化。比如我举个例子工作场景里经常要查各种数据写周报。传统方式是手动打开后台、拉数据、导表格、整理成文字。有了 AgentChat 之后你可以让它“查一下本周各渠道的转化率、对比上周涨跌、挑出异常项、生成一份周报草稿”。系统会自动完成调用数据查询工具 → 获取原始数据 → 代码解释器做统计分析 → 模型生成报告。这个过程只需要自然语言描述一遍。再者对于开发者来说AgentChat 是一个极佳的框架学习样本。你理解了它的循环机制之后不管以后是接触 LangChain、AutoGen 还是自己手写编排逻辑核心都是同一套思想模型推理 工具执行 结果反馈 再推理。这个思维模型一旦建立了看任何 AI 应用框架都会很通透。1.3 哪些人适合参考这篇文章如果你是有一定 Python 基础、想上手 AI Agent 开发的开发者这篇内容完全为你定制。不需要你提前精通大模型原理你只要会写基本的 Python 代码、懂函数定义、能调用 API 就可以。我会把 AgentChat 的每个模块拆开讲清楚包括设计思路、代码组织方式、工具注册机制、循环终止条件这些关键点。如果你是产品经理、技术爱好者或者刚入行的数据工程师这篇文章同样有价值。你不用亲手敲代码但读完你能理解 AgentChat 的系统边界在哪里、为什么智能体会“卡住”、为什么工具设计这么重要、为什么有人会说“Agent 的上限取决于工具的质量”。这些认知能帮你在跟开发协作时沟通得更顺畅也能让你对市面上各种 AI 产品的底层运作有更准确的判断。2. 整体设计与思路拆解2.1 核心概念从“对话”到“Agent 循环”要理解 AgentChat必须先理解一个核心概念——Agent 循环Agent Loop。它跟传统对话系统的本质区别在于控制流的归属。传统对话系统里控制流由开发者预设好。用户说“查天气”代码里写死了匹配“查天气”关键词就去调天气 API然后返回结果。系统能干什么完全取决于开发者写了多少条分支逻辑模型只是做关键词识别和话术拼接。AgentChat 里控制流交给模型自己决定。这就好比你把一个笼统的目标交给了一个有判断力的执行者它能自己规划步骤、选择工具、评估结果做不了的时候还能向你提问。具体的循环流程是这样的接收用户输入通常是自然语言描述的目标把输入和可用的工具清单一起发给大模型模型分析任务决定是否需要调用工具如果需要输出一个结构化的调用指令包含工具名和参数系统解析这个指令找到对应的工具函数执行它拿到结果把工具返回的结果回传给模型模型根据结果继续推理要么再调用下一个工具要么输出最终答案结束循环这套循环正是 AgentChat 的灵魂。设计它的时候有三件事是必须想清楚的怎么让模型知道有哪些工具可用、模型输出怎么解析成可执行指令、什么时候结束循环避免死循环。2.2 技术选型为什么这么定具体到实现层面我的选型逻辑很直接找个能力足够强的对话模型、写一段干净的工具注册代码、用最简单的 JSON 协议做模型与工具之间的消息交换。模型选型上我建议优先选支持 Function Calling / Tool Use 的模型比如 OpenAI 的 gpt-4o 系列、Claude 的 tool use 能力、或者国产的 Qwen、GLM 也都支持类似功能。这些模型在训练阶段就针对“输出结构化工具调用指令”做过专门优化输出格式稳定、参数填充准确比自己写 Prompt 硬塞要可靠得多。我做过对比测试用支持 Function Calling 的模型搭 Agent 比用通用模型 强 Prompt 约束的方式工具调用准确率能高很多关键是几乎不会出现输出格式错乱的问题。工具注册机制上我设计了一个非常轻量的装饰器方案。开发者只需要写一个普通的 Python 函数然后在上面加一个register_tool装饰器填入函数描述、参数说明系统自动把这个函数的信息加入工具清单。这个方案的优势在于新工具的接入成本几乎为零团队里任何一个人都能在五分钟内新增一个 Agent 能力。消息协议上我选了 JSON。这不是什么新潮设计但它是当前生态里兼容性最好的格式。OpenAI 的 function call 输出是 JSONAnthropic 的 tool_use 是 JSON各大模型厂商基本都对齐了 JSON Schema 描述工具。我们自己也用 JSON 做内部协议这样不仅跟各模型兼容将来接不同的模型后端时也不用改核心逻辑。2.3 从零搭建的模块划分把 AgentChat 拆开看核心模块大致是这几个模型接入层负责跟大模型 API 打交道把对话历史、工具定义发给模型拿到模型的回复。这是 Agent 的“大脑”入口。工具管理层维护一个工具注册表记录每个工具的名字、描述、参数 Schema、对应的 Python 函数。同时负责根据模型的调用指令找到函数并执行。消息组装器负责把对话历史、工具调用结果、系统提示词按模型要求的格式组装好。每次循环都要重新组装一次因为对话上下文在增长。Agent 执行引擎整个循环的调度中心。它有状态管理功能记录当前是第几轮、已经调用过哪些工具、累计消费了多少 token还要设定最大迭代次数防止死循环。用户交互层命令行交互界面或者后续扩展成 Web 界面负责接收用户输入、流式显示模型回复、把工具调用的过程可视化展示。模块划分清晰之后AgentChat 的可扩展性就体现出来了——你想加一个新工具只碰工具管理层你想换一个更便宜的模型只碰模型接入层你想让 Agent 在 Web 端跑只重写用户交互层。每一层之间的耦合度都很低这是我认为值得借鉴的设计思路。3. 环境准备与核心依赖3.1 Python 环境和依赖清单动手之前先把环境准备好。我的推荐版本组合如下这些都是经过验证的稳定搭配# Python 3.10 以上版本 python --version # 安装核心依赖 pip install openai # 如果你选 Anthropic 作为模型后端 pip install anthropic # 或者用 Qwen 的 SDK pip install dashscope这个项目其实对第三方框架的依赖非常少我不建议大家一开始就上手 LangChain 之类的全家桶。原因在后面细说。3.2 为什么不用现成的 Agent 框架你可能好奇市面上有 LangChain、AutoGen、CrewAI 这么多现成框架为什么还要从零搭我自己用过这些框架坦白说它们很强大但也存在问题抽象层级太高出了问题很难排查。你调用一个高层 API底层做了很多事情一旦中间某个环节出错你得一层层扒源码才能定位。对于学习阶段来说这种黑盒体验非常糟糕。从零搭建的 AgentChat 代码量其实很小核心引擎部分大概两百行左右。你能看到每一步在做什么消息怎么组装、工具怎么被调用、结果怎么回传。这种透明度带来的掌控感是框架给不了的。更重要的是当你理解了底层原理之后再回头用 LangChain 这类框架你会清晰地知道它在每一层替你做了什么。这时候框架反而是助力而不是黑盒。所以我一直建议先用原始方式搭一遍再考虑框架。这跟学编程先学数据结构再学框架是一个道理。4. 核心模块的代码逻辑解析4.1 工具注册机制的设计与实现工具注册是 AgentChat 最基础的能力。我把每个工具看成一个独立函数并用一个全局注册表统一管理。这里用 Python 装饰器实现非常简洁# tool_registry.py from typing import Callable, Dict, Any import inspect import json TOOL_REGISTRY: Dict[str, Dict[str, Any]] {} def register_tool(name: str, description: str, parameters: dict): 工具注册装饰器。 name: 工具的唯一名称 description: 工具功能描述这是给模型看的越详细越好 parameters: JSON Schema 格式的参数定义 def decorator(func: Callable) - Callable: TOOL_REGISTRY[name] { name: name, description: description, parameters: parameters, func: func, } return func return decorator def get_tool_schemas() - list: 返回所有工具的 JSON Schema 描述用于发给模型 schemas [] for tool in TOOL_REGISTRY.values(): schemas.append({ type: function, function: { name: tool[name], description: tool[description], parameters: tool[parameters], }, }) return schemas def execute_tool(tool_name: str, arguments: dict) - Any: 根据工具名和参数执行对应的 Python 函数 if tool_name not in TOOL_REGISTRY: raise ValueError(fUnknown tool: {tool_name}) tool TOOL_REGISTRY[tool_name] return tool[func](**arguments)这段代码的核心价值在于把“工具是什么”和“怎么执行”彻底分开。工具的定义name、description、parameters是给模型看的目的是让模型理解“有什么工具、什么时候用、传什么参数”而工具的执行func是给 Python 解释器看的目的是真正干活。装饰器方案还有一个额外的好处工具列表是自动收集的。你新增一个工具时不用去改任何注册中心的代码get_tool_schemas()会自动把新工具纳入清单。这在框架设计里叫“开闭原则”——对扩展开放对修改关闭。4.2 具体工具的编写示例光有注册机制还不够得有几个实际工具演示。这里写三个最常见的获取当前时间、做数学计算、访问某个模拟的数据接口。# tools.py import datetime import json from tool_registry import register_tool register_tool( nameget_current_time, description获取当前日期和时间返回格式为 YYYY-MM-DD HH:MM:SS, parameters{ type: object, properties: {}, } ) def get_current_time(): return datetime.datetime.now().strftime(%Y-%m-%d %H:%M:%S) register_tool( namecalculator, description执行数学计算支持加减乘除、幂运算等输入一个字符串表达式, parameters{ type: object, properties: { expression: { type: string, description: 合法的数学表达式例如 3 * (4 5), } }, required: [expression], } ) def calculator(expression: str): # 用 eval 需要非常小心这里是演示生产环境请用 ast 模块做安全校验 return eval(expression) register_tool( namequery_user_info, description根据用户 ID 查询用户基本信息返回姓名、邮箱、注册日期, parameters{ type: object, properties: { user_id: { type: string, description: 用户 ID例如 U12345, } }, required: [user_id], } ) def query_user_info(user_id: str): # 模拟数据实际项目里这里可以连数据库 mock_db { U12345: {name: 张三, email: zhangsanexample.com, registered: 2023-06-15}, U67890: {name: 李四, email: lisiexample.com, registered: 2024-01-08}, } return mock_db.get(user_id, {error: user not found})注意一下工具描述的重要性。我在代码里对每一项 description 都写得比较精确这不是随意写的。模型是通过 description 来理解工具用途的如果你的描述太模糊模型就可能在不合适的场景下调用工具。比如calculator的描述里我明确写了“输入一个字符串表达式”这样模型就会知道参数格式。我一开始写的时候 description 只写了“计算器”结果模型经常把参数以 JSON 对象形式传过来解析时各种报错。描述写的越清楚后面的麻烦越少。4.3 模型接入层构建消息并调用 Function Calling模型接入层是 AgentChat 跟大模型通信的桥梁。这里以 OpenAI 兼容接口为例# model_client.py import json from openai import OpenAI from tool_registry import get_tool_schemas client OpenAI( api_keyyour-api-key, base_urlyour-base-url, # 如果使用兼容接口的第三方服务 ) SYSTEM_PROMPT 你是一个智能助手可以通过调用工具来帮助用户完成任务。 当用户的需求需要工具协助时请调用合适的工具。如果无法通过工具完成 请如实告知用户。请注意工具执行结果返回后请基于结果继续回答用户。 def chat_with_tools(messages: list): 发送对话消息和工具定义给模型返回模型回复 response client.chat.completions.create( modelgpt-4o, # 换成你实际使用的模型 messagesmessages, toolsget_tool_schemas(), tool_choiceauto, # 让模型自己决定是否调用工具 ) return response.choices[0].message这里的关键参数是toolsget_tool_schemas()和tool_choiceauto。前者把当前所有可用工具告诉模型后者让模型自行判断是否需要调用工具、调用哪个。如果模型认为不需要调用工具它就直接返回普通文本回复如果需要调用它的回复里会带tool_calls字段里面是结构化的工具名和参数 JSON。还有SYSTEM_PROMPT也很重要。虽然 Function Calling 已经很智能但一个简洁清晰的系统提示词能让模型的工具使用更规范。我在实践里总结的经验是不要写太长的角色设定把核心规则交代清楚即可——“需要时调用工具、调用后根据结果继续回答”。4.4 主循环引擎组装、调用、回传、迭代现在到了整个 AgentChat 的心脏——主循环引擎。这段代码负责把模型、工具、消息历史串起来# agent.py import json from model_client import chat_with_tools, SYSTEM_PROMPT from tool_registry import execute_tool class AgentChat: def __init__(self, max_iterations: int 5): self.messages [{role: system, content: SYSTEM_PROMPT}] self.max_iterations max_iterations def run(self, user_input: str): # 把用户输入加入消息历史 self.messages.append({role: user, content: user_input}) for i in range(self.max_iterations): print(f\n--- 第 {i 1} 轮迭代 ---) # 1. 调用模型 response_message chat_with_tools(self.messages) print(f模型回复: {response_message}) # 2. 把模型的回复加入历史这是必须的 self.messages.append(response_message) # 3. 判断模型是否要调用工具 if not response_message.tool_calls: print(模型判定无需调用工具返回最终答案。) return response_message.content # 4. 逐个处理工具调用 for tool_call in response_message.tool_calls: tool_name tool_call.function.name tool_args json.loads(tool_call.function.arguments or {}) print(f调用工具: {tool_name}, 参数: {tool_args}) # 5. 执行工具并获取结果 result execute_tool(tool_name, tool_args) # 6. 把工具结果以 tool 角色消息加入历史 self.messages.append({ role: tool, tool_call_id: tool_call.id, content: json.dumps(result, ensure_asciiFalse), }) # 7. 进入下一轮迭代让模型基于工具结果继续推理 # 8. 超过最大迭代次数强制终止 print(f达到最大迭代次数 {self.max_iterations}自动终止。) return 任务未能完成已达到最大迭代限制。这段主循环里每一步都有着不可替代的作用缺一步整个循环就转不起来。我拆开解释几个关键细节第一模型的 assistant 回复必须原样加入消息历史。这不仅仅是记录对话更重要的是模型下一轮推理时能看到自己上一轮的选择和判断。如果你不加入模型会丢失上下文就会陷入“失忆”状态。第二工具执行结果也必须以 tool 角色的消息加入历史而且tool_call_id必须和模型的工具调用 ID 对应。这是 Function Calling 协议的要求——模型要看得到“我调用了某个工具、工具返回了什么”才能继续判断下一步动作。ID 对不上或者角色搞错模型就不知道这段结果跟哪个调用关联。第三循环的终止条件有两个一是模型不再输出 tool_calls直接给出最终答案二是达到我们设置的最大迭代次数上限。第二个条件极其重要我刚写 AgentChat 的时候没加这个限制结果有一次模型死循环调了几十次工具token 消耗非常惊人。加了限制之后最坏情况也就是任务失败但不会失控。4.5 命令行交互界面最后加一个简单的命令行入口让 AgentChat 能真正跑起来# main.py from agent import AgentChat def main(): agent AgentChat(max_iterations5) print(AgentChat 已启动输入你的需求输入 /quit 退出。) while True: user_input input(\n你: ) if user_input.strip().lower() in (/quit, /exit, quit, exit): print(再见) break final_answer agent.run(user_input) print(f\nAgent: {final_answer}) if __name__ __main__: main()这个交互层暂时只有最基本的功能但它体现了 AgentChat 的一个特性对话历史在累积。agent.run()内部把每轮的用户输入和 agent 回复都保存在self.messages里所以 Agent 是有记忆的。你可以先问“查询 U12345 的用户信息”再继续问“这个用户的注册日期是什么”它可以基于上文回答。这种记忆能力是实现多轮任务的基础。5. 实操运行与效果验证5.1 启动项目的完整流程代码写完以后实际的启动流程非常直接。把项目文件按下面的结构放好agentchat/ ├── main.py # 入口文件 ├── agent.py # Agent 主循环 ├── model_client.py # 模型接入层 ├── tool_registry.py # 工具注册机制 └── tools.py # 具体工具定义然后运行python main.py看到AgentChat 已启动的提示就说明系统已经就绪了。这个过程中如果报错绝大多数情况是依赖没装全或者 API Key 配置有问题。建议先把model_client.py里的 API 配置项检查一遍。5.2 三个典型运行场景演示我实际运行了一下挑三个典型场景分享。第一个场景是单工具调用你: 现在几点了 Agent: 2025-01-15 14:32:08运行过程里你能看到模型判断出需要调用get_current_time工具执行后拿到时间再组织语言回复。这个场景虽然简单但它完整走了一遍“模型决策 → 工具执行 → 结果返回 → 模型回答”的链路。第二个场景是多工具协作。比如说你: 帮我查一下 U67890 这个用户的情况然后算一下他注册了多少天。这个过程模型会先调用query_user_info拿到注册日期然后调用calculator算出时间差最后汇总两个工具的结果给你一个完整的回答。这种场景最能体现 Agent 的价值——多个工具串起来完成一个复合任务你不需要分两次提问。第三个场景是不需要调用工具的普通对话你: 你好介绍一下你自己。 Agent: 我是一个智能助手可以通过调用工具来帮助你查询信息、执行计算等任务。这种情况下模型直接返回文本不会强行调用工具。这得益于tool_choiceauto的设置模型有判断力去决定“这个需求不需要工具”。5.3 运行过程中观察到的效果与边界运行一段时间之后我对 AgentChat 的实际能力和边界都有了更直观的认识。能力的部分不用多说它确实能做普通聊天机器人做不到的事。我需要提醒的是边界尤其是模型的判断能力依赖模型本身的智商。模型如果不够强它可能在“该调用工具”的时候不调用或者在“不该调用”的时候瞎调用。我就遇到过 gpt-3.5 时代的模型它面对“今天是几号”这种明确需要工具的问题居然靠训练数据里的记忆瞎猜了一个日期。换更强的新模型之后这种情况就很少出现。所以如果你想认真用 AgentChat 做事情建议直接用当前最强的模型虽然贵一点但稳定性和准确率对得起差价。另一个观察是工具数量对模型选择的影响。我一开始只注册了两个工具模型选择非常准确后来一口气注册了十几个工具模型偶尔会出现找错工具的情况。这说明工具描述要写得足够差异化尤其是功能相近的工具描述里的区分度至关重要。6. 常见问题与排查技巧实录6.1 模型不调用工具或调用格式错误这是新手搭建 AgentChat 时遇到频率最高的一类问题。模型就是不调用工具明明工具列表已经传过去了它却直接生成文本答案。或者调用了但返回的 JSON 参数格式乱七八糟函数执行直接报错。排查思路要从三个层面走。第一确认模型版本是否支持 Function Calling很多模型 API 需要显式启用这个能力旧版本模型根本不支持结构化工具调用第二确认工具描述是否清晰把 description 写得更具体些明确说清楚“什么时候应该用这个工具”这一点非常管用第三检查消息格式是否符合 API 要求如果消息里混入了不符合格式的历史记录模型可能直接放弃工具调用。我自己的一个排查经验是把发给模型的原始请求打印出来人工看一遍。很多问题一眼就能发现——要么是函数描述参数不完整要么是历史消息角色混乱。把请求内容转成可读的 JSON 打印出来调试是定位这类问题的最高效手段。6.2 工具调用结果丢失或上下文错乱另一个常见问题是工具执行成功了结果也拿到了但模型下一轮推理时似乎“忘了”这个结果回答得驴唇不对马嘴。这个问题九成出在消息历史的组装上。工具调用的结果必须以role: tool的角色进入消息历史并携带正确的tool_call_id。如果你的代码里忘了这一步或者把工具结果错误地以role: user发回去模型就无法正确关联“第一次调用 → 对应结果”的对应关系推理自然乱套。也有一种情况是tool_call_id对不上。模型的 tool_calls 里每个调用都有唯一的 id你得把执行结果关联到正确的那个 id 上。如果同一个回复里调用了多个工具每个结果都要用自己的 id 单独回传不能混淆。这个细节在检查代码时经常被忽略但一旦错了整个上下文就全乱了。6.3 死循环与 Token 爆炸问题AgentChat 跑着跑着停不下来一轮又一轮地调用工具Token 消耗蹭蹭往上涨。这是最让人头疼的问题也是我一开始差点放弃 AgentChat 的点。解决方案主要有两板斧。第一板斧是设置max_iterations上限从根上掐断死循环的可能。我的默认值是 5简单任务三轮内能解决复杂任务五轮已经足够。第二板斧是观察循环轨迹。在每一轮迭代打印模型回复和工具调用信息你能直接看到它在哪个环节反复横跳。比如我遇到过模型反复调用同一个工具、参数还一模一样的情况那就是典型的“陷入循环陷阱”需要调整工具描述或者增加系统提示词的引导。还有一个进阶技巧是引入 Token 预算。在 Agent 引擎里累计每轮消耗的 token 数超过预算就强制停止。这个机制在做钱包保护时特别有用毕竟模型调用的费用是实打实的。6.4 工具执行报错处理与提示词优化工具函数本身出错是不可避免的尤其是跟外部系统交互的时候。比如你查数据库库连不上调 API接口报 500。这个问题的处理方案很简单不要把错误直接抛出去中断整个 Agent 循环而是把错误信息当作工具结果返回给模型。def execute_tool(tool_name: str, arguments: dict) - Any: if tool_name not in TOOL_REGISTRY: return {error: fUnknown tool: {tool_name}} try: tool TOOL_REGISTRY[tool_name] return tool[func](**arguments) except Exception as e: return {error: str(e)}这样做的意义在于模型看到错误信息后可以自己判断是换个参数重试、还是换一种方式解决问题、或者告诉用户任务无法完成。这比直接把异常抛出来让整个程序崩溃要健壮得多。AgentChat 的精髓就在这里——它允许“出错”但要求“出错后可恢复”。6.5 常见问题速查表现象可能原因排查方案模型从不调用工具模型不支持 Function Calling / 工具描述不清晰更换支持工具调用的新模型重写工具 description明确使用场景工具参数解析报错模型返回的 JSON 格式不规范 / 参数 Schema 定义错误在解析处加 try-except 并打印原始参数检查 JSON Schema 的 required 字段模型忽略工具执行结果tool 角色消息未正确加入历史 / tool_call_id 不匹配确保每条 tool 消息都带对应 id检查消息顺序是否错乱Agent 陷入死循环模型反复调用同一工具 / 任务目标模糊设置 max_iterations在提示词中要求“如果工具结果多次相同则停止”Token 消耗过高迭代次数太多 / 工具返回结果太长限制 max_iterations工具结果做截断处理工具函数报错导致程序崩溃未捕获工具内部异常在 execute_tool 中统一捕获异常把错误信息作为结果返回7. 进阶扩展建议7.1 增加流式输出提升体验现在这个版本的 AgentChat 回答是“一次性打印”的交互体验上比较生硬。你可以改成流式输出——模型一个字一个字地往外蹦工具调用过程也能实时展示。这对于面向用户的场景是必要的因为人脑处理信息需要时间流式输出能让用户感觉系统“在思考”而不是卡住了。具体的实现方式是把模型调用的streamTrue打开然后对返回的流式 chunk 做逐段处理。工具调用的部分逻辑会复杂一些因为流式输出的 tool_calls 可能被拆成多个片段需要手动拼接。这部分建议单独写一个流式消息组装器。7.2 接入更多工具类型当前的工具都是纯 Python 函数你可以扩展出更丰富的能力。比如访问数据库MySQL、PostgreSQL、MongoDB调用外部 HTTP API直接在工具函数里发请求操作文件系统读文件、写文件、遍历目录执行代码在沙箱环境里跑一段 Python 脚本调用向量数据库做知识库检索这是 RAG 场景的基础每加一种工具都要遵循同一个步骤写函数 → 加装饰器 → 定义参数 Schema。AgentChat 的扩展模式非常统一这也是它易用的原因。7.3 从命令行到真实产品如果要把 AgentChat 做成一个真正可用的产品还需要补上这些能力持久化存储对话历史存到数据库重启不丢。多用户支持每个用户有独立的会话隔离。权限管理哪些工具对哪些用户开放防止越权调用。可观测性记录每一次工具调用的耗时、参数、结果方便排查。异步化用 FastAPI 包一层支持 Web 端长连接。这一步是从“个人玩具”走向“可用的服务”的分水岭。架构上主循环不需要变主要是在外层增加管理和调度能力。8. 实操总结与个人经验分享做了这么多轮实验我最大的体会有两个。第一个是Agent 的上限由工具决定而不是由模型决定。模型再聪明如果你的工具只覆盖了一小部分场景Agent 能做的事情就相当有限。反过来工具设计得丰富、描述得清晰Agent 的可用性会成倍提升。所以我后续开发 AgentChat大部分精力都花在“如何把更多能力接入系统”和“如何让工具描述更准确”上而不是反复调模型参数。第二个体会是AgentChat 调试的关键在于让过程可见。每一轮迭代模型说了什么、调了哪个工具、工具返回了啥这些信息一定要打印出来或者记录下来。Agent 的“黑盒感”比普通程序强得多如果过程不透明出了问题只能猜。加上日志之后绝大多数问题都能通过观察运行轨迹找到原因。最后再分享一个小技巧给 Agent 写系统提示词的时候可以加一句“如果你认为任务无法通过现有工具完成请直接告知用户不要强行调用工具”。这句话能省掉很多不必要的 token 消耗也能避免模型为了“迎合用户”而去做一些不合理的操作。从零开始搭一个 AgentChat 并不难但让它变得可靠、可控、可扩展需要你在实际使用中反复打磨。
返回列表