Agent工具系统设计哲学:让AI拥有“双手“的艺术
工具系统设计哲学让AI拥有双手的艺术工具是AI Agent的手脚。没有工具LLM只是一个嘴炮——能说会道但啥也干不了。本文基于三个开源项目的实践结合OpenAI Tool Use规范探讨工具系统设计的核心哲学。前言从ChatGPT的Function Calling到Claude的Tool Use再到各种Agent框架的工具系统——工具让AI从聊天机器人进化成了数字员工。但工具设计是一门艺术。设计得好AI如虎添翼设计得差AI寸步难行。工具设计的三个哲学哲学1工具是手不是脑工具应该做脏活累活不要让工具做决策。反例tool(description分析代码质量并决定是否重构)defanalyze_and_refactor(code:str)-str:# 工具里做了太多决策qualityassess_quality(code)ifquality0.7:returnrefactor(code)returncode正例tool(description分析代码质量返回质量分数)defanalyze_quality(code:str)-float:returnassess_quality(code)tool(description重构代码)defrefactor_code(code:str)-str:returndo_refactor(code)原则工具提供能力Agent做决策。让AI来决定是否重构而不是让工具自己决定。哲学2粒度适中不粗不细工具太粗 → AI无法精确控制工具太细 → AI调用太多次效率低下太粗的例子tool(description处理整个项目)defprocess_project(project_path:str,action:str)-str:# 一个工具包打天下AI不知道怎么用pass太细的例子tool(description读取文件的第一行)defread_first_line(path:str)-str:...tool(description读取文件的第二行)defread_second_line(path:str)-str:...# 太细了AI要调用几百次适中的粒度tool(description读取文件内容支持指定行范围)defread_file(path:str,start_line:intNone,end_line:intNone)-str:读取文件可选指定行范围pass哲学3描述即文档工具的description是AI理解工具的唯一途径。好的描述 好的工具使用。糟糕的描述tool(description处理文件)defprocess_file(path:str)-str:...好的描述tool(description读取文件内容并返回。支持UTF-8和GBK编码。如果文件不存在返回错误信息。)defread_file(path:str)-str: 读取指定路径的文件内容。 参数 path: 文件的相对或绝对路径 返回 文件内容的字符串或错误信息 示例 read_file(src/main.py) → 返回main.py的内容 pass三个项目的工具系统对比项目1CodeLite的装饰器模式CodeLite用Python装饰器实现工具注册优雅且自动# tools.py_registry{}deftool(nameNone,description,dangerousFalse):defdecorator(func):tool_namenameorfunc.__name__# 从函数签名自动生成JSON Schemaschemagenerate_schema_from_signature(func)_registry[tool_name]{name:tool_name,description:description,parameters:schema,handler:func,dangerous:dangerous}returnfuncreturndecorator# 使用tool(description读取文件内容)defread_file(path:str)-str:withopen(path)asf:returnf.read()优点代码即文档自动Schema生成开发效率高缺点Python特有其他语言难以复用项目2Education-Agent的类注册模式Education-Agent用类来组织工具更结构化# tools/base.pyclassBaseTool:name:strdescription:strparameters:dictdefexecute(self,**kwargs)-str:raiseNotImplementedError# tools/search.pyclassSearchKnowledgeTool(BaseTool):namesearch_knowledgedescription搜索知识库返回相关文档片段parameters{query:{type:string,description:搜索查询},top_k:{type:integer,description:返回结果数量,default:5}}defexecute(self,query:str,top_k:int5)-str:# 实现搜索逻辑resultsself.vector_store.search(query,top_ktop_k)returnformat_results(results)# 注册tool_registryToolRegistry()tool_registry.register(SearchKnowledgeTool())tool_registry.register(WebSearchTool())优点结构清晰易于测试和扩展缺点代码量较多不够简洁项目3DeepScope的Schema驱动模式DeepScope用JSON Schema定义工具更接近OpenAI规范# tools/definitions.pyTOOL_DEFINITIONS[{type:function,function:{name:web_search,description:搜索互联网获取最新信息,parameters:{type:object,properties:{query:{type:string,description:搜索关键词},num_results:{type:integer,description:返回结果数量,default:5}},required:[query]}}}]# tools/executor.pyTOOL_HANDLERS{web_search:handle_web_search,web_fetch:handle_web_fetch,analyze_content:handle_analyze_content,}defexecute_tool(name:str,args:dict)-str:handlerTOOL_HANDLERS.get(name)ifnothandler:returnfUnknown tool:{name}returnhandler(**args)优点与OpenAI规范完全一致易于跨语言复用缺点Schema和实现分离维护成本高工具依赖管理问题工具之间有依赖比如推送到GitHub依赖于git add和git commit# 错误的方式让AI自己按顺序调用# AI可能会忘记先commit再push# 正确的方式用工具组合tool(description提交并推送到GitHub)defgit_push(message:str)-str:自动add、commit、pushrun_command(git add .)run_command(fgit commit -m {message})run_command(git push)return推送成功工具组合模式classToolChain:工具链按顺序执行多个工具def__init__(self):self.steps[]defadd_step(self,tool_name:str,args:dict):self.steps.append((tool_name,args))defexecute(self)-list[str]:results[]fortool_name,argsinself.steps:resultexecute_tool(tool_name,args)results.append(result)# 如果某步失败停止执行ifresult.startswith(Error):breakreturnresults# 预定义的工具链git_push_chainToolChain()git_push_chain.add_step(run_command,{command:git add .})git_push_chain.add_step(run_command,{command:git commit -m {message}})git_push_chain.add_step(run_command,{command:git push})工具错误处理错误分类classToolError(Exception):工具错误基类passclassToolNotFoundError(ToolError):工具不存在passclassToolPermissionError(ToolError):权限不足passclassToolTimeoutError(ToolError):执行超时passclassToolInputError(ToolError):输入参数错误pass错误恢复策略defexecute_with_retry(tool_name:str,args:dict,max_retries:int3)-str:带重试的工具执行forattemptinrange(max_retries):try:resultexecute_tool(tool_name,args)returnresultexceptToolTimeoutError:ifattemptmax_retries-1:time.sleep(2**attempt)# 指数退避continuereturnError: 工具执行超时exceptToolInputErrorase:returnfError: 输入参数错误 -{e}exceptToolErrorase:returnfError:{e}returnError: 超过最大重试次数工具安全设计三级权限模型classPermission(Enum):READread# 只读操作WRITEwrite# 写操作EXECUTEexecute# 执行命令TOOL_PERMISSIONS{read_file:Permission.READ,list_dir:Permission.READ,write_file:Permission.WRITE,edit_file:Permission.WRITE,run_command:Permission.EXECUTE,git_push:Permission.EXECUTE,}defcheck_permission(tool_name:str,required:Permission)-bool:检查工具权限tool_permTOOL_PERMISSIONS.get(tool_name,Permission.READ)# 权限等级READ WRITE EXECUTElevels{Permission.READ:0,Permission.WRITE:1,Permission.EXECUTE:2}returnlevels[tool_perm]levels[required]沙箱执行defexecute_in_sandbox(tool_name:str,args:dict,sandbox_dir:str)-str:在沙箱中执行工具# 限制文件操作范围iftool_namein[read_file,write_file,edit_file]:pathargs.get(path,)ifnotpath.startswith(sandbox_dir):returnError: 文件路径超出沙箱范围# 限制命令执行iftool_namerun_command:cmdargs.get(command,)dangerous_commands[rm -rf,format,sudo]fordcindangerous_commands:ifdcincmd:returnfError: 禁止执行危险命令:{dc}returnexecute_tool(tool_name,args)工具调用优化批量调用defbatch_execute(tool_calls:list[dict])-list[str]:批量执行工具调用# 检查是否有依赖关系independent,dependentseparate_dependencies(tool_calls)# 独立的工具并行执行results{}withThreadPoolExecutor(max_workers5)asexecutor:futures{executor.submit(execute_tool,tc[name],tc[args]):tc[id]fortcinindependent}forfutureinas_completed(futures):tc_idfutures[future]results[tc_id]future.result()# 有依赖的工具顺序执行fortcindependent:results[tc[id]]execute_tool(tc[name],tc[args])returnresults工具结果缓存fromfunctoolsimportlru_cachelru_cache(maxsize100)defcached_read_file(path:str)-str:缓存文件读取结果withopen(path)asf:returnf.read()defexecute_with_cache(tool_name:str,args:dict)-str:带缓存的工具执行# 只读工具可以缓存iftool_namein[read_file,list_dir,search_code]:cache_keyf{tool_name}:{json.dumps(args,sort_keysTrue)}ifcache_keyin_cache:return_cache[cache_key]resultexecute_tool(tool_name,args)_cache[cache_key]resultreturnresult# 写操作清除相关缓存iftool_namein[write_file,edit_file]:clear_cache_for_file(args.get(path))returnexecute_tool(tool_name,args)实战设计一个完整的工具系统需求分析一个AI编程助手需要这些工具类别工具用途文件操作read_file, write_file, edit_file, apply_patch读写编辑文件代码搜索search_code, find_files搜索代码命令执行run_command运行Shell命令Git操作git_status, git_diff, git_log, git_pushGit操作Web工具web_search, web_fetch搜索和抓取网页Agent工具spawn_subagent, spawn_parallel子Agent完整实现# tools/registry.pyclassToolRegistry:def__init__(self):self._tools{}self._categories{}defregister(self,name,handler,description,parametersNone,dangerousFalse,categorygeneral):注册工具self._tools[name]{name:name,handler:handler,description:description,parameters:parametersor{},dangerous:dangerous,category:category}ifcategorynotinself._categories:self._categories[category][]self._categories[category].append(name)defexecute(self,name,args):执行工具toolself._tools.get(name)ifnottool:returnfError: Unknown tool {name}try:returntool[handler](**args)exceptExceptionase:returnfError executing{name}:{e}defget_definitions(self):获取所有工具定义OpenAI格式return[{type:function,function:{name:t[name],description:t[description],parameters:t[parameters]}}fortinself._tools.values()]deflist_by_category(self,category):列出某类别的工具returnself._categories.get(category,[])# 全局注册表registryToolRegistry()# 注册工具registry.register(read_file,handlerread_file,description读取文件内容,parameters{type:object,properties:{path:{type:string,description:文件路径}},required:[path]},categoryfile)总结工具系统设计的核心原则工具是手不是脑— 提供能力不做决策粒度适中— 不粗不细恰到好处描述即文档— 好的描述 好的工具使用安全第一— 权限控制沙箱执行错误恢复— 自动重试智能修复性能优化— 批量调用结果缓存下一篇预告《Agent安全层设计如何防止AI误删你的数据库》— 我们会深入探讨Agent系统的安全设计包括命令分级、权限控制、审计日志等。参考资料OpenAI Tool Use文档Anthropic Tool Use文档OWASP AI安全指南本文基于三个开源项目的实践代码已开源。tags: tools, agent-design, python, architecture, securityseries: ai-agent-development
