【手搓 Agent 第1关】建立最小 Agent(中):工具调用

发布时间:2026/7/24 19:03:19

【手搓 Agent 第1关】建立最小 Agent(中):工具调用 普通大模型对话只能依托固有知识应答无法适配真实场景的复杂需求而工具调用是区分普通对话模型与智能 Agent 的核心关键。承接上篇 LLM 基础能力本篇聚焦 Agent「手脚能力」搭建手把手讲解自定义工具函数、标准化工具描述文档、解析模型工具调用指令、执行本地任务并回传结果的完整流程让模型突破固有能力限制自主对接外部场景、完成实操任务。一、安装手脚完成目标定义工具函数、解析 tool call/function call。学习指南写个本地函数比如写一个极简的计算器函数def add(a, b)或者一个假天气查询def get_weather(location)。撰写“工具说明书”SchemaLLM 看不到你的本地代码它只能看懂 JSON 格式的说明书。去阅读官方文档中的 Function Calling 部分学习如何用 JSON Schema 向 LLM 描述你的函数名、描述description以及参数类型。捕获“动手的意图”把你的 Schema 传给大模型。当模型觉得需要调用工具时它的返回值会发生变化——它不再返回普通的文本回答而是返回一个tool_calls对象。你需要写代码来判断这次返回的是普通聊天还是一个工具调用请求验收标准当你问“北京天气如何”模型不直接回答而是返回一个结构化的指令告诉你“我想调用get_weather函数参数是location: 北京”。1. 定义工具全局列表增加一个工具描述位置写在chat_history后generate_response前# 工具描述 Schematools[{type:function,function:{name:get_weather,description:查询指定城市的当日天气,parameters:{type:object,required:[location],properties:{location:{type:string,description:城市名称如北京、合肥}}}}},]定义工具函数defget_weather(location:str)-str: 通过调用 wttr.in API 查询真实的天气信息。 # API端点请求JSON格式的数据urlfhttps://wttr.in/{location}?formatj1try:# 请求天气数据responserequests.get(url)# 检查请求是否成功为200response.raise_for_status()# 解析JSON响应dataresponse.json()# 提取当前天气状况current_conditiondata[current_condition][0]weather_desccurrent_condition[weatherDesc][0][value]temp_ccurrent_condition[temp_C]# 格式化成自然语言返回returnf{location}当前天气{weather_desc}温度{temp_c}°CexceptExceptionase:print(fError fetching weather data for{location}:{e})return抱歉生成响应时发生错误。2. 修改 AI 生成回复函数第一轮请求大模型判断是否需要工具chat_history.append({role:user,content:prompt})responseclient.chat.completions.create(modelModel_ID,# response_format{type: json_object},messageschat_history,toolstools,tool_choiceauto)msgresponse.choices[0].message变量解释tools全局数组写给 AI 看的函数说明书AI 不知道你本地代码只能读这份 JSON 规则msgOpenAI 内置对象二选一无工具需求msg.content存文字msg.tool_calls为空需调用工具msg.tool_calls存调用指令msg.content是空判断 AI 是否发起工具调用if msg.tool_callsifmsg.tool_calls:print(AI 调用了工具:,msg.tool_calls[0].function.name)# 把模型的工具调用指令存入对话历史chat_history.append(msg)# 定义可用工具映射available_tools{get_weather:get_weather}关键说明chat_history.append(msg)不能省略OpenAI 规范后续二次请求 AI 时AI 需要看到自己刚刚下发了工具调用指令上下文才完整否则会逻辑错乱。available_tools是工具映射字典函数名字符串 → 本地真实函数作用为让AI只传函数名文本get_weather通过字典找到def get_weather()执行循环处理所有要调用的工具支持一次性调用多个函数# 遍历所有要调用的工具fortoolinmsg.tool_calls:tool_idtool.idfunc_nametool.function.name func_argsjson.loads(tool.function.arguments)# 打印调用详情可视化print(f调用工具{func_name}, 参数{func_args})# 执行本地工具动态分发调用tool_funcavailable_tools.get(func_name)iftool_func:tool_resulttool_func(**func_args)else:tool_result不存在该工具print(f工具返回结果{tool_result})# 存入工具执行结果chat_history.append({role:tool,tool_call_id:tool_id,name:func_name,content:tool_result})重点难懂语法拆解json.loads(tool.function.arguments)AI 传的参数是一段 JSON 文本字符串Python 无法直接读取必须转成字典{location:合肥}→{location: 合肥}tool_func(**func_args)**字典解包语法func_args {location: 合肥}等价于get_weather(location合肥)第二轮请求大模型结合工具结果生成最终回答# 工具执行完二次请求大模型生成最终回答print(\n正在结合工具结果生成最终回复...)second_respclient.chat.completions.create(modelModel_ID,messageschat_history,)final_contentsecond_resp.choices[0].message.content.strip()chat_history.append({role:assistant,content:final_content})print( 工具流程结束 \n)returnfinal_content作用第一轮 AI 只下发调用指令不会生成答案这一轮把查到的天气数据全部传给 AI让 AI 按照 system_prompt 规则整理输出 JSON。else 分支无需调用工具普通闲聊else:# 无工具调用直接返回普通文本print(❌ 模型无需调用工具直接文字回答)contentmsg.content.strip()chat_history.append({role:assistant,content:content})returncontent3. 示例输出注此处为了让 AI 能够输出自然语言把强制 AI 输出 JSON 格式的system prompt和相关代码删去。AI对话程序输入 quit 结束对话 你:你好我叫阿言。 ❌ 模型无需调用工具直接文字回答 AI: 你好阿言很高兴认识你 我是你的 AI 助手有什么我可以帮助你的吗 你:帮我查询合肥明日的天气 AI 调用了工具: get_weather 调用工具get_weather, 参数{location: 合肥} 工具返回结果合肥当前天气Partly Cloudy 温度29°C 正在结合工具结果生成最终回复... 工具流程结束 AI: 你好阿言我已经查询了这合肥的天气信息 **合肥** - 天气状况局部多云 (Partly Cloudy) - 温度29°C ⚠️ **温馨提示** 以上为当前实时天气数据。如果你需要了解**明日具体天气预报**包括详细温度范围、降水概率、风力风向等建议你查看专业天气应用或网站比如中国气象局官网、墨迹天气或手机自带天气功能这样能获得更精准的预报信息。 需要我帮你做其他事情吗 你:quit 结束对话。二、本篇总结 下期预告本篇我们实现了单次工具调用的完整流程让模型具备了调用外部工具的能力但目前仍无法自主多轮推理、迭代完成复杂任务且缺乏工程防护机制。下一篇 Stage1-下我们将搭建完整的 Agent 自主循环逻辑同时新增步数限制、超时控制、分层错误处理三大安全护栏最终落地一套稳定、可复用、适配线上场景的最小完整 Agent。

相关新闻