第8篇:Agent工具系统 —— 从Function Calling到自定义Tool
第8篇Agent工具系统 —— 从Function Calling到自定义Tool工具Tool是Agent与外部世界交互的接口。没有工具的Agent只是一个“对话模型”有了工具的Agent才能真正“行动”——搜索信息、查询数据库、发送邮件、执行代码。本文系统讲解工具调用的完整链路从JSON Schema的工具定义、模型推理返回tool_calls、到应用程序执行函数并回传结果的多轮闭环。以博查搜索API为例展示如何为Agent集成自定义搜索工具并实现“模拟搜索”作为降级方案。一、为什么工具是Agent的“手脚”大语言模型本质上是“大脑”——它擅长推理、规划、生成文本但无法主动获取外部信息或执行具体操作。当用户问“今天北京天气怎么样”时模型要么基于过时的训练数据编造答案要么诚实地说“我不知道”。这就是模型的“能力边界”。工具系统的作用就是让模型能够调用外部函数从而突破这一边界。工具调用的核心流程是一个三方的协作闭环参与方角色示例用户提出需求“今天北京天气怎么样”模型LLM理解意图决定调用哪个工具生成参数输出{name: get_weather, arguments: {location: 北京}}应用程序执行工具函数将结果返回给模型调用天气API得到“晴22°C”再交给模型生成最终回复这个闭环的核心是模型不直接执行代码它只输出一个“调用请求”tool_calls真正的执行由应用程序完成。为什么这样设计原因很简单模型无法安全地执行任意代码会产生巨大的安全风险也无法直接访问外部系统没有网络权限、数据库连接等。让模型“提议”让应用程序“执行”是最安全、最可控的分工方式。二、Function Calling的完整数据链路2.1 工具的定义JSON Schema规范为了让模型理解“有哪些工具可用”我们需要用JSON Schema描述每个工具的名称、描述和参数结构。以“获取天气”工具为例{ type: function, function: { name: get_weather, description: 获取指定城市的当前天气信息返回温度、天气状况和空气质量, parameters: { type: object, properties: { location: { type: string, description: 城市名称例如北京、上海、广州 }, unit: { type: string, enum: [celsius, fahrenheit], description: 温度单位默认为celsius } }, required: [location] } } }这个JSON会作为请求的一部分发送给模型告诉模型“当用户询问天气时你可以调用这个函数需要提供location参数unit是可选的。”2.2 模型的输出tool_calls当模型判断需要调用工具时它的响应不是直接输出文本而是输出一个结构化的tool_calls对象{ choices: [{ message: { role: assistant, content: null, // 模型没有直接回复文本而是选择调用工具 tool_calls: [{ id: call_abc123, type: function, function: { name: get_weather, arguments: {\location\: \北京\, \unit\: \celsius\} } }] } }] }关键字段tool_calls一个数组表示模型希望调用的工具一个请求可以调用多个工具function.name要调用的函数名function.argumentsJSON字符串包含调用参数id本次调用的唯一标识用于后续关联工具结果2.3 应用程序执行工具应用程序收到tool_calls后根据name路由到对应的函数def execute_tool_call(tool_call): if tool_call.function.name get_weather: args json.loads(tool_call.function.arguments) location args.get(location) unit args.get(unit, celsius) return get_weather(location, unit) # 其他工具... raise ValueError(fUnknown tool: {tool_call.function.name})2.4 工具结果的回传执行完成后应用程序需要将结果以tool角色的消息追加到对话历史中messages.append({ role: tool, tool_call_id: call_abc123, # 必须与原始调用的id一致 content: 北京当前天气晴温度22°C空气质量良好 })然后再次调用模型将包含工具结果的完整历史发过去让模型基于工具返回的数据生成最终的自然语言回复。2.5 完整的多轮闭环一次完整的工具调用涉及两次API请求第一次请求带tools定义 用户消息 → 模型 → 返回tool_calls无文本内容 第二次请求不带tools或仍带但模型不再调用 用户消息 模型之前的tool_calls 工具执行结果 → 模型 → 返回最终文本如果需要多个工具或工具结果不满足需求这个循环可能持续多次模型可能连续调用多个工具。三、工具定义的最佳实践工具定义的优劣直接影响模型的调用准确率。以下是最佳实践3.1 name命名的三原则原则说明好例子坏例子动词优先用动词描述操作get_weatherweather清晰无歧义一个工具只做一件事search_webdo_search_and_summarize拆成两个工具与实现对应名称与函数名一致send_email→def send_email()名称与实现不匹配3.2 description的撰写策略描述是模型判断“何时调用”的核心依据。好的描述应该包含description: 获取指定城市的实时天气信息。 使用场景 - 用户询问当前天气状况 - 用户询问温度、降雨概率、空气质量 - 用户提到“出门是否需要带伞”等与天气相关的问题 不适用场景 - 查询历史天气数据 - 天气预报仅返回当前时刻数据 参数说明 - location: 城市名称需用中文 - unit: 温度单位默认摄氏度 关键原则描述要明确告诉模型什么时候该用、什么时候不该用。这能有效减少模型误用工具的概率。3.3 参数设计的规范parameters: { type: object, properties: { query: { type: string, description: 搜索关键词应使用用户原话中的关键词 }, max_results: { type: integer, description: 返回结果数量默认为5用户明确要求更多时调整, default: 5, minimum: 1, maximum: 20 } }, required: [query] }设计要点提供默认值减少模型必须提供参数的负担限定取值范围用enum、minimum/maximum约束参数避免非法输入明确描述参数语义让模型知道如何从用户问题中提取参数四、自定义Tool的实现CrewAI在CrewAI中自定义工具通过继承BaseTool实现。4.1 博查搜索工具的实现博查搜索是国内可用的免费搜索API适合个人开发者使用。import os import requests from crewai.tools import BaseTool from pydantic import Field from typing import Optional class BochaSearchTool(BaseTool): name: str 博查搜索 description: str ( 搜索互联网信息返回相关网页的标题、摘要和链接。 当用户需要查询实时信息、新闻、最新动态时使用此工具。 ) api_key: Optional[str] Field(defaultNone, description博查API密钥) def __init__(self, **kwargs): super().__init__(**kwargs) self.api_key os.getenv(BOCHA_API_KEY) if not self.api_key: raise ValueError(请在.env中设置BOCHA_API_KEY) def _run(self, query: str) - str: 执行搜索并返回格式化的结果 url https://open.bochaai.com/v1/web-search headers { Content-Type: application/json, Authorization: fBearer {self.api_key} } payload {query: query, count: 5} try: resp requests.post(url, headersheaders, jsonpayload, timeout10) resp.raise_for_status() data resp.json() pages data.get(data, {}).get(webPages, {}).get(value, []) if not pages: return f未找到 {query} 的相关结果。 result f关于 {query} 的搜索结果\n\n for i, p in enumerate(pages, 1): title p.get(name, 无标题) snippet p.get(snippet, 无摘要) link p.get(url, #) result f{i}. {title}\n {snippet}\n 来源: {link}\n\n return result except requests.exceptions.RequestException as e: return f搜索失败: {str(e)}4.2 在Agent中绑定工具from crewai import Agent search_tool BochaSearchTool() researcher Agent( role高级研究员, goal针对指定主题搜索并收集最新资料, backstory你是资深研究员擅长使用搜索引擎快速定位关键信息, tools[search_tool], # 研究员拥有搜索能力 llmllm, verboseTrue )4.3 模拟搜索作为降级方案当API不可用或测试阶段使用模拟搜索可以保证流程不被中断class MockSearchTool(BaseTool): name: str 模拟搜索 description: str 模拟搜索返回示例数据用于测试 def _run(self, query: str) - str: return f关于 {query} 的模拟搜索结果 1. AI Agent发展趋势 2025年多智能体系统在金融、医疗领域的应用快速增长... 来源: https://example.com/ai-agent-trends 2. 大模型工具调用技术解析 Function Calling正在成为Agent与外部世界交互的标准接口... 来源: https://example.com/tool-calling 此为模拟数据请配置BOCHA_API_KEY获取真实结果 def create_search_tool(): 根据环境变量决定使用真实搜索还是模拟 if os.getenv(BOCHA_API_KEY): return BochaSearchTool() else: print(⚠️ 未配置BOCHA_API_KEY使用模拟搜索) return MockSearchTool()五、工具调用的错误处理5.1 工具执行失败的场景失败场景原因处理方式API密钥无效未配置或配置错误返回友好错误信息引导用户检查配置网络超时API服务不可达重试或返回超时提示参数解析错误模型生成的参数格式不正确捕获JSON解析异常返回错误信息工具不存在模型调用了未注册的工具记录日志返回“工具不可用”5.2 错误处理代码模板async def execute_tool_call(tool_call) - str: try: if tool_call.function.name get_weather: args json.loads(tool_call.function.arguments) return get_weather(**args) else: return f错误未知工具 {tool_call.function.name} except json.JSONDecodeError: return f错误参数解析失败请检查工具定义 except KeyError as e: return f错误缺少必需参数 {e} except Exception as e: return f错误工具执行失败 - {str(e)}六、Agentic Loop的完整实现def agentic_loop(user_message: str, tools: list, max_iterations: int 5): 完整的Agentic Loop实现 messages [{role: user, content: user_message}] for _ in range(max_iterations): # 第一次调用可能返回tool_calls response client.chat.completions.create( modeldeepseek-v4-flash, messagesmessages, toolstools, tool_choiceauto ) message response.choices[0].message # 没有工具调用 → 直接返回 if not message.tool_calls: return message.content # 有工具调用 → 执行并追加结果 messages.append(message.model_dump()) for tool_call in message.tool_calls: result execute_tool_call(tool_call) messages.append({ role: tool, tool_call_id: tool_call.id, content: result }) # 继续循环再次调用模型 return 达到最大迭代次数未完成七、深度思考工具调用的关键理解7.1 模型不会“执行”工具只会“请求”工具这是理解Function Calling最重要的概念。整个链条中模型只做了两件事判断“当前是否需要调用工具”生成结构化的tool_callsJSON模型没有执行任何代码没有访问任何外部系统。所有的执行都发生在应用程序侧。这意味着安全可控你可以对工具调用进行审计、限流、权限校验灵活扩展可以添加任何类型的工具API调用、数据库查询、本地命令只要应用程序能执行7.2 tool_choice的三个选项选项行为适用场景auto模型自主决定是否调用工具通用场景推荐none强制模型不调用工具简单对话不需要工具{type: function, function: {name: xxx}}强制调用指定工具确定性场景如“必须查天气”7.3 多个tool_calls的处理模型一次可以返回多个tool_calls多个工具并行调用。应用程序需要并发执行所有工具asyncio.gather按顺序将结果追加到messages再次调用模型获取最终回复思考与动手建议用curl命令调用DeepSeek API手动构造一个包含工具定义和用户消息的请求观察返回的tool_calls结构。为你的Agent添加两个工具一个搜索工具和一个计算器工具观察模型如何根据用户问题选择合适的工具。尝试构造一个“工具调用失败”的场景观察Agent是否能够理解错误信息并给出合理的反馈。

相关新闻

AI芯片软硬件协同设计:从架构选型到流片Bring-up的工程实践

AI芯片软硬件协同设计:从架构选型到流片Bring-up的工程实践

1. AI芯片软硬件协同设计的核心逻辑1.1 为什么软硬件必须一起设计做AI芯片这行的人都有一个共识:芯片设计不再是单纯的硬件活儿。十年前做芯片,硬件团队把RTL写好、时序收敛、流片回来,软件团队再慢慢适配驱动和框架,这种串行模式…

2026/10/10 2:16:53 阅读更多 →
30 分钟白板演练:用刷榜笔记的思路手写一份短链系统设计

30 分钟白板演练:用刷榜笔记的思路手写一份短链系统设计

30 分钟白板演练:用刷榜笔记的思路手写一份短链系统设计 【免费下载链接】system-design-notes Notes of the book System Desgin Interview - An Insiders Guide 项目地址: https://gitcode.com/GitHub_Trending/sy/system-design-notes 系统设计面试里&…

2026/10/10 2:16:53 阅读更多 →
30 分钟白嫖部署:vLLM 一键跑起 Yandex 开源 80B,MTP 加速白拿 1.2–1.8×

30 分钟白嫖部署:vLLM 一键跑起 Yandex 开源 80B,MTP 加速白拿 1.2–1.8×

30 分钟白嫖部署:vLLM 一键跑起 Yandex 开源 80B,MTP 加速白拿 1.2–1.8 【免费下载链接】AliceAI-Foundation-80B-A3B-Base 项目地址: https://ai.gitcode.com/hf_mirrors/yandex/AliceAI-Foundation-80B-A3B-Base 当 Yandex 把 AliceAI-Founda…

2026/10/10 2:16:53 阅读更多 →

最新新闻

Spring AI 实战:从配置到对话,ChatClient 链式调用与上下文管理

Spring AI 实战:从配置到对话,ChatClient 链式调用与上下文管理

1. 从配置文件到对话窗口:Spring AI 到底简化了什么第一次接触 Spring AI 的时候,我脑子里其实带着一个很具体的疑问:过去在 Java 项目里接一个大模型对话能力,光是 HTTP 客户端封装、请求体拼装、响应解析、异常重试这些杂活&…

2026/10/10 5:17:30 阅读更多 →
如何安全管理 OpenFlux 共享密钥:传输、存储与轮换实战指南

如何安全管理 OpenFlux 共享密钥:传输、存储与轮换实战指南

如何安全管理 OpenFlux 共享密钥:传输、存储与轮换实战指南 OpenFlux 是一款网络栈研究工具,通过可插拔的传输层构建 TCP 隧道。当启用传输加密时,客户端与出口节点共用的**共享密钥(shared secret)**就是整条隧道的安…

2026/10/10 5:17:30 阅读更多 →
Ant Design Blazor Affix 滚动容器实战:用 TargetSelector 将固钉绑定到指定滚动元素

Ant Design Blazor Affix 滚动容器实战:用 TargetSelector 将固钉绑定到指定滚动元素

前端UI组件设计系统 【免费下载链接】ant-design-blazor 基于 Ant Design 与 Blazor 的前端组件库。让开发者解放生产力,实现更大价值。 项目地址: https://gitcode.com/ant-design-blazor/ant-design-blazor 点击查看 免费下载 本篇指南围绕 Ant Desig…

2026/10/10 5:17:30 阅读更多 →
x64dbg 调试器插件开发指南:深入解析 DbgScriptBpToggle 脚本断点切换 API 及其完整调用链

x64dbg 调试器插件开发指南:深入解析 DbgScriptBpToggle 脚本断点切换 API 及其完整调用链

逆向工程调试器开发工具应用安全 【免费下载链接】x64dbg An open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis. 项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg 点击查看 免费下载 导读 DbgScriptBpT…

2026/10/10 5:17:30 阅读更多 →
LogicStack-LeetCode 题解:813. 最大平均值和的分组——「序列 DP + 前缀和」求连续段平均值之和最大值

LogicStack-LeetCode 题解:813. 最大平均值和的分组——「序列 DP + 前缀和」求连续段平均值之和最大值

教程文档 【免费下载链接】LogicStack-LeetCode 公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码 项目地址: https://gitcode.com/gh_mirrors/lo/LogicStack-LeetCode 点击查看 免费下载 导读 本篇以「宫水三叶的刷题日记」系列仓库(LogicSta…

2026/10/10 5:17:30 阅读更多 →
GPS天线设计 GNSS天线设计建议

GPS天线设计 GNSS天线设计建议

GPS天线设计 GNSS天线设计建议 天线作为导航定位设备中最重要的接收器件,它起到的作用就像是人的“耳朵”;是将卫星发送下来的电磁波能量变换成电子器件可解析的电流。因此天线的性能好坏将直接关系到GPS整机的产品性能。目前GNSS系统开放民用定位系统主要是美国GPS…

2026/10/10 5:16:30 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →