news 2026/7/15 5:41:24

手搓生产级 AI Agent 系统(2):工具系统深度实战——从参数校验到沙箱安全

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手搓生产级 AI Agent 系统(2):工具系统深度实战——从参数校验到沙箱安全

前言:Agent 翻车,90% 死在工具系统上

上一篇我们搭好了 Agent 的骨架(ReAct 循环 + 记忆架构 + 反思机制)。但真正让 Agent 从"能说"变成"能做"的,是工具系统。

我在三个真实项目中追踪了 Agent 的故障原因分布:

故障类型占比根因
工具调用死循环31%工具返回格式不一致,Agent 反复重试
参数错误28%Schema 定义模糊,LLM 传了错误参数
工具不存在12%工具注册名和 LLM 理解的功能对不上
危险操作9%没有沙箱,Agent 直接执行了 rm -rf
其他20%API 超时、网络错误等

71% 的故障可以通过改进工具系统设计来避免。这篇文章把工具系统的每一个环节拆开,给出一套生产级的实现方案。


一、工具系统的设计目标

一个靠谱的工具系统需要满足六条要求:

目标说明做不到的后果
通用注册任何函数一行代码就能注册为工具每加一个工具都要改 Agent 代码
自动校验参数类型、必填、枚举值自动检查LLM 传了错误参数,执行失败
精准描述工具描述直接影响 LLM 调用准确率工具误调用率高达 30%
安全隔离危险操作拦截 + 用户确认机制Agent 执行破坏性命令
失败恢复超时重试、错误提示、降级策略一个工具失败,整个任务挂掉
可观测每次调用的输入/输出/耗时/费用出问题不知道是哪一步

二、工具注册框架:一个装饰器搞定

2.1 为什么不用 LangChain 的 Tool 类?

LangChain 定义一个工具需要继承BaseTool,重写_run_arun,然后注册到ToolRegistry。一个发邮件的工具要写 40 行样板代码。

我们用一个装饰器,一行@register_tool搞定:

importinspectimportjsonimporttimeimportsignalimportrefromtypingimportCallable,Any,Optional,get_type_hintsfromdataclassesimportdataclass,fieldfromfunctoolsimportwraps# ====== 全局工具注册表 ======TOOL_REGISTRY:dict[str,'ToolDefinition']={}@dataclassclassToolParameter:"""工具参数定义——给 LLM 看的描述直接决定调用准确率"""name:strtype:str# string / number / boolean / objectdescription:str# 这个描述是给 LLM 看的,写清楚传什么required:bool=Truedefault:Any=Noneenum:Optional[list]=None# 限定可选值,大幅降低参数错误率@dataclassclassToolDefinition:"""工具的完整定义"""name:strdescription:str# 包含"做什么"和"不要做什么"parameters:list[ToolParameter]func:Callable category:str="general"# 工具分类,方便组织require_confirmation:bool=False# 危险操作需用户确认timeout:int=30# 超时秒数max_retries:int=1# 失败重试次数risk_level:str="safe"# safe / dangerous / destructivedefregister_tool(name:str,description:str,parameters:list[dict]=None,category:str="general",require_confirmation:bool=False,timeout:int=30,max_retries:int=1,risk_level:str="safe",):"""装饰器:将任意 Python 函数注册为 Agent 工具。 用法: @register_tool( , description="发送邮件。参数 to 为收件人邮箱...", parameters=[ {"name": "to", "type": "string", "description": "收件人邮箱地址", "required": True}, {"name": "subject", "type": "string", "description": "邮件主题", "required": True}, {"name": "body", "type": "string", "description": "邮件正文(支持 Markdown)", "required": True}, ], category="communication", require_confirmation=True, risk_level="dangerous", ) def send_email(to: str, subject: str, body: str) -> str: ... """ifparametersisNone:parameters=[]defdecorator(func:Callable)->Callable:# 从函数签名自动推断参数类型(如果没在 parameters 里显式声明)hints=get_type_hints(func)ifhasattr(func,'__annotations__')else{}param_list=[]forpinparameters:param_type=p.get("type","string")# 如果类型没指定,从 type hints 推断if"type"notinporp["type"]=="string":py_type=hints.get(p["name"])ifpy_type==int:param_type="number"elifpy_type==bool:param_type="boolean"elifpy_typein(list,dict):param_type="object"param_list.append(ToolParameter(name=p["name"],type=param_type,description=p.get("description",""),required=p.get("required",True),default=p.get("default"),enum=p.get("enum"),))tool_def=ToolDefinition(name=name,description=description,parameters=param_list,func=func,category=category,require_confirmation=require_confirmation,timeout=timeout,max_retries=max_retries,risk_level=risk_level,)TOOL_REGISTRY[name]=tool_defreturnfuncreturndecoratordefget_tools_for_llm(category:str=None)->str:"""生成给 LLM 看的工具描述。格式直接影响调用准确率。"""tools=TOOL_REGISTRY.values()ifcategory:tools=[tfortintoolsift.category==category]lines=[
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/15 5:40:33

游戏行业三大趋势:小游戏内容升级、AI重塑管线、出海精耕细作

1. 报告核心价值与行业背景解读看到这个标题,很多同行第一反应可能是“又是一份行业报告”。但这份《2026中国游戏产业趋势及潜力分析报告》的含金量,远不止于一份PDF文件。它更像是一张由数据、案例和前瞻性判断共同绘制的“藏宝图”,为身处…

作者头像 李华
网站建设 2026/7/15 5:40:28

一张图,秒懂TCP/IP协议栈核心原理!(建议收藏)

1. TCP/IP协议栈全景图:从应用层到网络接口层想象一下你正在网购,点击"下单"按钮的瞬间,数据就开始了一场奇妙的旅行。这个旅程的路线图就是TCP/IP协议栈,它像一座四层大楼:应用层(顶楼&#xff…

作者头像 李华
网站建设 2026/7/15 5:37:35

C++访问者模式实战:双分派机制与AST处理器实现

1. 项目概述:为什么我们需要访问者模式?干了这么多年C,我发现很多同行在设计模式里,对访问者模式(Visitor)的态度很微妙:要么觉得它太复杂、用不上,要么就是面试前突击背一下“双分派…

作者头像 李华
网站建设 2026/7/15 5:37:19

C++ cin/cout性能瓶颈深度解析与优化实战指南

1. 项目概述:为什么C的cin/cout会成为性能瓶颈?如果你是从C语言转过来学C的,或者刚开始接触C的输入输出,大概率会觉得cin和cout用起来真方便。不用记%d、%s这些格式符,类型安全,还能自动处理空格和换行&…

作者头像 李华
网站建设 2026/7/15 5:34:42

C++ XML解析实战:RapidXML原地解析与内存管理详解

1. 项目概述:为什么选择RapidXML来处理C中的XML?在C项目里处理XML,这事儿听起来简单,但真做起来,你会发现选择比努力更重要。我见过太多项目,一开始图省事用了重量级的XML库,结果项目后期被内存…

作者头像 李华
网站建设 2026/7/15 5:34:40

C++高性能系统开发:从数据导向设计到内存与并发优化实战

1. 项目概述:为什么C依然是高性能系统的基石如果你在开发一个需要处理每秒百万级请求的交易系统,或者一个需要实时渲染海量三角元的游戏引擎,又或者是一个需要从PB级数据流中实时提取特征的AI推理框架,你大概率不会选择Python或Ja…

作者头像 李华