【Python智能体开发实战:RAG、工具调用与多智能体协作】用Python定义第一个Agent工具:把库存查询函数变成明确的接口契约
用Python定义第一个Agent工具把库存查询函数变成明确的接口契约具体问题与完成目标你正在为一个内部工具编写 Agent 能力。假设场景是这样的运营团队希望 AI 助手能回答“商品 A 还有多少库存”这类问题。你手里已经有一个 Python 函数query_stock(sku)它在本地运行良好。但当你把它注册给一个 LLM Agent 时问题出现了。模型要么不用这个工具要么在调用时传入了你根本没有定义的参数比如product_name、warehouse_id要么把返回值当成了自然语言来“理解”而不是“引用”。根本原因不在于模型而在于你的函数缺少一份明确的接口契约——它告诉模型叫什么名字、用来做什么、需要什么参数、每个参数是什么含义、返回什么、什么情况下不应该调用。完成本文后你将能够用 Python 标准库typing.Protocol定义一个 Agent 工具的最小接口契约实现一个可被 LLM 正确调用的库存查询工具用三个可复现的测试场景验证工具契约是否生效。前置条件与适用环境读者假定掌握 Python 基础语法了解函数定义和类型注解的基本写法。适用环境Python 3.9typing.Protocol在 3.8 引入3.9 起稳定用于生产代码仅依赖标准库。本文示例在 Python 3.11 下完成静态语法检查未连接任何外部模型服务。案例数据虚构的库存数据包含 4 个 SKU覆盖正常、边界和失败三种情况。文件清单文件用途stock_tool.py库存工具的核心实现包含契约定义和查询函数test_stock_tool.py验证脚本覆盖三个验收场景必要原理为什么需要“接口契约”LLM Agent 的工具调用机制决定了模型不读你的源码只读你提供的工具描述。OpenAI 的函数调用文档明确指出函数名称、参数描述和指令应该“清晰且详细”目的是让模型理解“何时使用以及何时不使用”每个函数。换句话说你的 Python 函数本身对模型是不可见的。模型看到的是你提供的 JSON Schema函数名、描述、参数列表及其类型和说明。如果你只给出一个光秃秃的query_stock模型只能靠函数名猜测——然后猜错。typing.Protocol在本文中的作用是让你用 Python 类型系统从源头定义这份契约。它不是你直接递给模型的 JSON Schema而是你生成那份 Schema 的“单一事实来源”。当你用 Protocol 写清楚参数和返回类型后可以用代码提取这些信息生成模型能读懂的描述。这与 OpenAI 的“实习生测试”原则一致如果你把函数定义给一个实习生他能否仅凭这些信息正确调用如果不能缺失的信息就应该补进契约里。完整实现第一步定义协议契约# stock_tool.py库存查询 Agent 工具的接口契约与实现。fromtypingimportProtocol,runtime_checkablefromdataclassesimportdataclassdataclass(frozenTrue)classStockResult:库存查询结果。sku:strquantity:intunit:strwarehouse:strfound:boolruntime_checkableclassStockQueryTool(Protocol):库存查询工具的接口契约。 用途根据 SKU 查询当前可用库存数量。 何时使用当用户询问某个具体商品SKU的库存数量时调用。 何时不使用当用户询问商品价格、订单状态或模糊名称时不调用本工具。 defquery_stock(self,sku:str)-StockResult:查询指定 SKU 的库存。 Args: sku: 商品唯一标识格式为 XXX-数字例如 A-1001。 Returns: StockResult: 包含 sku、quantity、unit、warehouse、found 字段。 foundFalse 表示该 SKU 不存在于库存系统中。 Raises: ValueError: 当 sku 格式不合法不匹配 XXX-NNNN 模式时抛出。 ...关键设计决策runtime_checkable允许用isinstance()在运行时检查某个对象是否满足这个契约。这在我们后续验证实现是否正确时有用。契约文本本身包含“何时使用”和“何时不使用”OpenAI 的指南建议在系统提示或函数描述中明确说明使用条件。我把这部分直接写进了 Protocol 的文档字符串里因为它就是模型需要知道的全部信息。StockResult用 dataclass 而不是裸 dict模型需要理解返回值结构。一个明确定义的数据类比{qty: 100}更不容易被误解。而且 dataclass 的字段名和类型可以自动生成返回值的描述。第二步实现工具# stock_tool.py续# 虚构的库存数据库_STOCK_DB:dict[str,tuple[int,str,str]]{A-1001:(50,件,华东仓),B-2002:(0,件,华南仓),C-3003:(120,箱,华北仓),# D-4004 故意不添加用于测试“不存在”的情况}importre _SKU_PATTERNre.compile(r^[A-Z]-\d{4}$)classInventoryTool:StockQueryTool 契约的具体实现。defquery_stock(self,sku:str)-StockResult:# 参数校验格式不合法时明确拒绝而不是返回模糊结果ifnot_SKU_PATTERN.match(sku):raiseValueError(fSKU 格式不合法:{sku!r}应为 A-1001 格式大写字母-四位数字)entry_STOCK_DB.get(sku)ifentryisNone:returnStockResult(skusku,quantity0,unit,warehouse,foundFalse)quantity,unit,warehouseentryreturnStockResult(skusku,quantityquantity,unitunit,warehousewarehouse,foundTrue)defget_tool_contract()-dict:生成给 LLM 看的工具描述模拟 OpenAI function 格式。return{type:function,name:query_stock,description:(根据 SKU 查询当前可用库存数量。当用户询问某个具体商品SKU的库存时使用。当用户询问价格、订单或模糊名称时不要使用。),parameters:{type:object,properties:{sku:{type:string,description:(商品唯一标识格式为 XXX-数字例如 A-1001。大小写敏感。),}},required:[sku],},}要点说明格式校验前置如果 SKU 格式不对直接抛ValueError而不是返回foundFalse。这给了模型一个清晰的信号它不是“没找到”而是“用错了工具”。OpenAI 的文档建议用枚举和结构化类型来防止无效调用。虽然这里没法用枚举但正则校验达到了类似效果。“不存在”和“错误”区分开foundFalse表示 SKU 格式合法但系统里没有这个商品ValueError表示 SKU 格式本身就不合法。这两种情况对 Agent 的后续行为有不同含义。get_tool_contract()函数这是“契约”的对外呈现。它把 Protocol 中的信息翻译成模型能读的 JSON Schema。在真实 Agent 中这个函数就是你的工具注册逻辑。第三步验证脚本# test_stock_tool.py验证库存工具契约是否生效。fromstock_toolimportInventoryTool,StockQueryTooldeftest_contract_satisfied():静态契约检查实现类是否满足 Protocol。toolInventoryTool()assertisinstance(tool,StockQueryTool),(InventoryTool 未满足 StockQueryTool 契约)print(PASS: 契约满足检查通过)deftest_normal_query():正常场景查询存在的 SKU。toolInventoryTool()resulttool.query_stock(A-1001)assertresult.foundisTrueassertresult.quantity50assertresult.unit件assertresult.warehouse华东仓print(fPASS: 正常查询 A-1001 →{result.quantity}{result.unit})deftest_boundary_zero_stock():边界场景库存为 0 但 SKU 存在。toolInventoryTool()resulttool.query_stock(B-2002)assertresult.foundisTrueassertresult.quantity0print(PASS: 边界查询 B-2002 → 库存 0foundTrue)deftest_not_found():边界场景SKU 格式合法但不存在。toolInventoryTool()resulttool.query_stock(Z-9999)assertresult.foundisFalseassertresult.quantity0print(PASS: 不存在 SKU Z-9999 → foundFalse)deftest_invalid_format_failure():失败场景SKU 格式不合法应抛 ValueError。toolInventoryTool()try:tool.query_stock(invalid-sku)raiseAssertionError(应该抛出 ValueError 但没有)exceptValueErrorase:assert格式不合法instr(e)print(fPASS: 非法格式被拒绝 →{e})if__name____main__:test_contract_satisfied()test_normal_query()test_boundary_zero_stock()test_not_found()test_invalid_format_failure()print(\n全部验证通过。)运行方式与中间结果在隔离目录中执行# 将上述两个文件放在同一目录下python test_stock_tool.py预期输出基于给定数据可复现PASS: 契约满足检查通过 PASS: 正常查询 A-1001 → 50件 PASS: 边界查询 B-2002 → 库存 0foundTrue PASS: 不存在 SKU Z-9999 → foundFalse PASS: 非法格式被拒绝 → SKU 格式不合法: invalid-sku应为 A-1001 格式大写字母-四位数字 全部验证通过。可操作的验收与测试测试目的输入/操作预期结果判定方法契约实现正确性isinstance(InventoryTool(), StockQueryTool)True断言通过正常库存查询query_stock(A-1001)quantity50, foundTrue检查返回字段值零库存边界query_stock(B-2002)quantity0, foundTrue区分“存在但为零”与“不存在”SKU 不存在query_stock(Z-9999)foundFalse, quantity0found 标志为 False格式非法失败query_stock(invalid-sku)抛出 ValueError捕获异常并检查消息验收的核心原则模型不需要“理解”库存逻辑它只需要能正确调用。如果你的契约能让一个不了解业务的人或模型正确调用验收就通过了。常见故障的定位方法问题 1模型调用了工具但参数名不对如传了product_id而不是sku定位检查get_tool_contract()返回的 JSON Schema 中parameters.properties的键名是否与 Protocol 方法签名一致。模型只会按你给的 Schema 传参。如果 Schema 写的是sku模型传product_id那是模型没有遵循 Schema——此时需要在 description 中更明确地说明参数名称或者考虑是否需要别名机制。问题 2模型应该调用工具时没有调用定位在真实 Agent 中这是系统提示词的问题。OpenAI 建议“通常明确告诉模型该做什么”。你需要用自然语言告诉模型“当用户询问库存时调用query_stock传入 SKU 参数”。工具契约本身只定义了“怎么调用”没有定义“什么时候调用”——后者是系统提示词的职责。问题 3返回值被模型“解释”而不是“引用”这是 Agent 工作流的常见问题不是契约问题。模型看到{quantity: 50}后可能说“大约有五十件左右”而不是“精确为 50 件”。解决方式是在系统提示中要求“直接引用工具返回的数值不要估算”。适用边界本文聚焦的是工具接口契约不是完整的 Agent 工作流。未覆盖的部分包括真实 LLM 集成get_tool_contract()返回的字典格式模拟了 OpenAI 的 function calling 格式但本文没有连接任何真实 API。将契约接入真实模型时需要按你使用的 SDK如openai、langchain等的文档调整格式。多工具场景当你有 20 个以上工具时契约管理会变得复杂。OpenAI 建议初始暴露的工具数量“少于 20 个”。如果工具很多需要考虑工具筛选或语义检索机制这超出了本文范围。持久化与并发示例中的_STOCK_DB是内存字典不涉及数据库连接、并发写入或事务。生产环境中的库存查询需要考虑这些。本文的技术依据来自 Pythontyping官方文档中关于Protocol和runtime_checkable的说明以及 OpenAI 官方函数调用指南中关于函数定义最佳实践的描述。验证状态已完成Python 3.11 环境下对stock_tool.py和test_stock_tool.py的语法检查python -m py_compile。五个测试场景的静态逻辑复核契约检查、正常查询、零库存边界、不存在 SKU、非法格式失败。Protocol 签名与实现类方法签名的一致性核对。未执行实际运行test_stock_tool.py本文撰写时未在本地环境执行预期输出基于代码逻辑推导。连接真实 LLM 服务验证模型是否能正确理解契约并调用工具。多工具并发注册或工具数量超过 20 个时的行为验证。参考资料Python Documentation,typing — Support for type hints, https://docs.python.org/zh-cn/3.9/library/typing.html 核验日期2026-10-03OpenAI Developers,Function calling, https://developers.openai.com/api/docs/guides/function-calling 核验日期2026-10-03OpenAI Developers,Using tools, https://developers.openai.com/api/docs/guides/tools 核验日期2026-10-03

相关新闻

从零搭建AI工程体系:数据、特征、训练、服务全链路实战

从零搭建AI工程体系:数据、特征、训练、服务全链路实战

1. 从零搭建AI工程体系,为什么我劝你别急着调包"ai-engineering-from-scratch"这个标题,第一次看到的时候我愣了一下。市面上讲AI的教程铺天盖地,但绝大多数都是教你import torch然后跑个预训练模型,或者调个API接口就完…

2026/10/4 8:13:36 阅读更多 →
MRAM工业存储实战:MR25H40CDF与PIC18LF45K80方案解析

MRAM工业存储实战:MR25H40CDF与PIC18LF45K80方案解析

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

2026/10/4 8:13:36 阅读更多 →
大模型+AI 3D建模:智慧农业数字孪生大屏实战

大模型+AI 3D建模:智慧农业数字孪生大屏实战

1. 项目缘起与整体设计思路1.1 为什么想到用大模型加3D建模来做农业大屏去年底我接手了一个智慧农业园区的数字化项目,甲方最初的诉求很朴素:把园区里的传感器数据、气象站数据、灌溉设备状态集中到一个大屏上展示。我一开始想的是常规方案,用…

2026/10/4 8:13:36 阅读更多 →

最新新闻

插件机制深度拆解:从IAR到MusicFree,详解加载失败排查实战

插件机制深度拆解:从IAR到MusicFree,详解加载失败排查实战

刚看到plugins这个关键词冲上热搜的时候,我第一反应是:这个词太宽泛了,宽泛到几乎没法聊。但点进去看完那些关联搜索词,我反而觉得这个话题有得写,而且很值得写。既有iar plugins 是干什么的这种偏基础的疑问&#xff…

2026/10/4 8:44:54 阅读更多 →
金融机构接连入驻WorkBuddy,争的不是多一个Skill,是下一个高频入口

金融机构接连入驻WorkBuddy,争的不是多一个Skill,是下一个高频入口

自腾讯9月初发布WorkBuddy金融版,面向金融机构推出AI智能工作台后,券商陆续入驻WorkBuddy,角力下一个流量入口。继腾讯发布WorkBuddy金融版后,广发证券、东方财富、兴业证券、中信建投相继入驻WorkBuddy。四家机构分别从对外投研专…

2026/10/4 8:44:54 阅读更多 →
Skill Scanner数据流污点分析揭秘:AST+CFG如何捕获跨文件数据外泄攻击链

Skill Scanner数据流污点分析揭秘:AST+CFG如何捕获跨文件数据外泄攻击链

Skill Scanner数据流污点分析揭秘:ASTCFG如何捕获跨文件数据外泄攻击链 【免费下载链接】skill-scanner Security Scanner for Agent Skills 项目地址: https://gitcode.com/gh_mirrors/sk/skill-scanner Skill Scanner 是一款面向 Agent Skills 的开源安全扫…

2026/10/4 8:44:54 阅读更多 →
大材小用烧冤枉钱?用Token Optimizer route命令为任务匹配最合适的模型

大材小用烧冤枉钱?用Token Optimizer route命令为任务匹配最合适的模型

大材小用烧冤枉钱?用Token Optimizer route命令为任务匹配最合适的模型 【免费下载链接】token-optimizer Find the ghost tokens. Fix them. Survive compaction. Avoid context quality decay. 项目地址: https://gitcode.com/gh_mirrors/toke/token-optimizer…

2026/10/4 8:44:54 阅读更多 →
OpenShell:整合PowerShell与WSL的Windows终端增效实战

OpenShell:整合PowerShell与WSL的Windows终端增效实战

说实话,我一开始看到“OpenShell”这个名字,以为又是一个 Windows 终端的换肤工具。毕竟这年头,给终端加个背景图、调个透明度,就能自称“生产力神器”的项目太多了。但真正装完、配置好、用了两周之后,我想说&#xf…

2026/10/4 8:44:54 阅读更多 →
Magenta实操指南:用神经网络生成MIDI旋律的原理与训练全流程

Magenta实操指南:用神经网络生成MIDI旋律的原理与训练全流程

我在整理自己的 MIDI 素材库时,经常会冒出同一个念头:如果神经网络能接住我写到一半的旋律,顺着音乐情绪往下生成几小节,那该多省事。真正让我确认这件事靠谱的,是谷歌 Magenta 项目。Magenta 是谷歌研究团队主导的开放…

2026/10/4 8:43:53 阅读更多 →

日新闻

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/4 1:00:58 阅读更多 →
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/4 1:00:58 阅读更多 →
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/4 1:00:58 阅读更多 →

周新闻

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/4 1:00:58 阅读更多 →
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/4 1:00:58 阅读更多 →
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/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/2 10:36:31 阅读更多 →
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/3 9:42:35 阅读更多 →
黑夜航拍船只数据集训练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/3 9:42:36 阅读更多 →