公司动态
【Day 12】加个工具就要改代码重启?该上注册中心了
摘要每加一个工具要改函数、写Schema、改Agent列表还得重启。本文构建ToolRegistry注册中心支持装饰器注册、JSON配置加载和运行时热加载增删工具不改Agent代码。核心约80行。Day10里我们用装饰器注册工具已经比手写Schema列表干净了。但还是有个问题加新工具得改Python代码生产环境改完还要重新部署。想象一下你的Agent已经上线跑着了产品经理说加个天气查询工具。你得打开代码仓库、写函数、加Schema、跑测试、提交PR、等CI、重新部署——就为了加一个工具。这篇把工具从代码变成配置让加工具像往菜单板上写菜名一样简单。问题到底出在哪先看工具和Agent耦合的典型写法# 工具定义、Schema、Agent逻辑全挤在一个文件里defcalculate(expression):returneval(expression)defget_time():returndatetime.now().isoformat()SCHEMAS[{type:function,function:{name:calculate,description:数学计算,parameters:{type:object,properties:{expression:{type:string}}}}},{type:function,function:{name:get_time,description:获取时间,parameters:{type:object,properties:{}}}},]FUNCS{calculate:calculate,get_time:get_time}# Agent里硬编码工具列表responseclient.chat.completions.create(modelglm-4.7-flash,messagesmessages,toolsSCHEMAS)加一个search_web工具你要写函数、加Schema到SCHEMAS、加映射到FUNCS——三处。忘了哪一处要么LLM看不到这个工具要么看到了调不到。核心矛盾是工具是数据却被当成代码来管。注册中心一个字典解决问题思路很简单搞一个统一的注册中心所有工具往里注册Agent从这里取。谁也不直接持有工具列表。registry.register 装饰器ToolRegistryJSON配置文件加载hot_load 热加载to_schemas() 导出给LLMexecute() 执行工具Agentimportjson,osclassToolRegistry:def__init__(self):self._tools{}# name - {desc, params, func, category, version}defregister(self,name,desc,paramsNone,categorygeneral,version1.0.0):装饰器注册一个工具函数defwrapper(func):self._tools[name]{desc:desc,params:paramsor{type:object,properties:{}},func:func,category:category,version:version,}print(f✅ 注册:{name}v{version})returnfuncreturnwrapperdefunregister(self,name):注销工具运行时立即生效ifnameinself._tools:delself._tools[name]print(f️ 注销:{name})defget(self,name):returnself._tools.get(name)defexecute(self,name,**kwargs):toolself._tools.get(name)ifnottool:raiseKeyError(f工具未注册:{name})returntool[func](**kwargs)defto_schemas(self,categoryNone):导出为Function Calling格式Agent直接用tools[tfortinself._tools.values()ifnotcategoryort[category]category]return[{type:function,function:{name:n,description:t[desc],parameters:t[params]}}forn,tinself._tools.items()ifnotcategoryort[category]category]注册工具变成了一行装饰器registryToolRegistry()registry.register(calculate,数学计算,{type:object,properties:{expression:{type:string}},required:[expression]},categorymath)defcalculate(expression):returneval(expression)registry.register(get_time,获取当前时间,categorysystem)defget_time():fromdatetimeimportdatetimereturndatetime.now().strftime(%Y-%m-%d %H:%M:%S)Agent这边完全不关心有哪些工具每次从注册中心现取classPluginAgent:def__init__(self,registry,llm):self.registryregistry self.llmllmdefchat(self,message):messages[{role:user,content:message}]for_inrange(5):# 每轮重新取schema热加载的工具立即可用respself.llm.client.chat.completions.create(modelglm-4.7-flash,messagesmessages,toolsself.registry.to_schemas())msgresp.choices[0].messageifnotmsg.tool_calls:returnmsg.content messages.append(msg)fortcinmsg.tool_calls:resultself.registry.execute(tc.function.name,**json.loads(tc.function.arguments))messages.append({role:tool,tool_call_id:tc.id,content:str(result)})return轮次超限关键细节to_schemas()放在循环里面调用而不是初始化时取一次。这样运行时新加的工具下一轮LLM就能看到。从JSON配置加载装饰器注册还是得写Python代码。如果想让非开发人员也能加工具或者在不同环境加载不同工具集可以把工具描述放到JSON配置里{tools:[{name:text_count,description:统计文本字数和行数,category:text,version:1.0.0,parameters:{type:object,properties:{text:{type:string,description:要统计的文本}},required:[text]}}]}配置文件只存描述信息名称、参数Schema、分类不含执行函数——函数还得用Python写。但工具的增删、描述的修改、参数的调整全在JSON里改不用动Agent代码defload_from_json(self,path):从JSON配置文件加载工具定义函数后续绑定withopen(path,r,encodingutf-8)asf:configjson.load(f)fortinconfig.get(tools,[]):self._tools[t[name]]{desc:t[description],params:t.get(parameters,{type:object,properties:{}}),func:None,# 占位用bind_func绑定category:t.get(category,general),version:t.get(version,1.0.0),}print(f 加载配置:{t[name]})defbind_func(self,name):装饰器为配置加载的工具绑定函数defwrapper(func):ifnameinself._tools:self._tools[name][func]funcreturnfuncreturnwrapperregistry.load_from_json(tools_config.json)registry.bind_func(text_count)deftext_count(text):linestext.split(\n)return{chars:len(text),lines:len(lines)}配置和代码分离后运维可以通过改配置文件控制哪些工具启用不用碰Python代码。运行时热加载最爽的是这个Agent跑着跑着突然需要新工具直接加不用重启agentPluginAgent(registry,llm)# 正常对话中...agent.chat(现在几点)# 产品说要加天气工具你直接在运行时注册registry.register(get_weather,查询指定城市天气,{type:object,properties:{city:{type:string}},required:[city]},categoryexternal)defget_weather(city):importurllib.request# 调用免费天气API...returnf{city}今天晴25°C# 下一轮对话LLM立刻就能用这个工具agent.chat(北京天气怎么样)# 自动调get_weather因为Agent的每轮循环都调to_schemas()现取工具列表新注册的工具下一轮就出现在LLM的视野里。同理unregister()注销的工具也会立即消失。注册中心核心功能实测装饰器注册→导出Schema→执行→分类过滤→注销→JSON加载→热加载三种注册方式对比方式写代码改配置重启适合场景装饰器注册是否否热加载内置工具、开发阶段JSON加载写函数是否外部工具、不同环境切换热加载是否不需要运行时动态扩展完整版包含YAML配置支持、工具版本升级检测同名高版本自动替换、健康检查启动时遍历测试所有工具、权限控制read/write分级放在CSDN下载区。GLM-4.7-Flash永久免费200K上下文可在智谱AI开放平台自行注册使用。下一篇Day13讲分工型多Agent当一个Agent搞不定时怎么让多个Agent各干各的。本文由「梅雅达编程笔记」原创首发CSDN。AI Agent实战系列共18篇从Function Calling到多Agent协作全程用免费模型GLM-4.7-Flash点个关注不迷路✨CSDN博客https://blog.csdn.net/2601_96428997/