统一注册工具函数、统一调用执行的框架,就像你现在做的那样 —— 类似于一个「工具工厂」。
from typing import Dict, Callable, Any
import inspect
import json
# 工具工厂基类
class BaseTool:
registry: Dict[str, Callable] = {}
@classmethod
def register(cls, fn: Callable):
"""注册工具函数到工厂"""
cls.registry[fn.__name__] = fn
return fn
@classmethod
def get_tools(cls):
"""返回所有注册的工具定义(给 OpenAI)"""
tools = []
for name, fn in cls.registry.items():
sig = inspect.signature(fn)
parameters = {
"type": "object",
"properties": {},
"required": [],
}
for param in sig.parameters.values():
parameters["properties"][param.name] = {
"type": "string",
"description": f"{param.name} 参数"
}
parameters["required"].append(param.name)
tools.append({
"type": "function",
"function": {
"name": name,
"description": fn.__doc__ or "无描述",
"parameters": parameters
}
})
return tools
@classmethod
def call(cls, name: str, args: Dict[str, Any]) -> str:
"""根据名称调用工具函数"""
if name not in cls.registry:
raise ValueError(f"未注册的工具函数: {name}")
return cls.registry[name](**args)
# ==============================
# 子类工具集:自动注册
class MyTools(BaseTool):
@BaseTool.register
def get_weather(location: str) -> str:
"""获取城市天气信息"""
return f"☀️ 当前 {location} 晴天,温度 25°C"
@BaseTool.register
def get_time(city: str) -> str:
"""获取当前时间"""
return f"🕒 当前 {city} 时间为 12:00"
# ==============================
# 🧪 测试工厂调用
if __name__ == "__main__":
tools = BaseTool.get_tools()
print("注册的工具定义(可传给 OpenAI 的 tools):")
print(json.dumps(tools, indent=2, ensure_ascii=False))
print("\n✅ 工厂调用 get_weather:")
result = BaseTool.call("get_weather", {"location": "北京"})
print(result)
print("\n✅ 工厂调用 get_time:")
result = BaseTool.call("get_time", {"city": "上海"})
print(result)
机制 | 作用 |
---|---|
@BaseTool.register | 自动注册工具函数进工厂 |
BaseTool.call(name, args) | 根据名字自动找到函数并执行 |
BaseTool.get_tools() | 生成 OpenAI 所需的 tools 描述 |
🔁 @BaseTool.register 是什么?
它是一个类方法装饰器,装饰的作用是把函数注册到 BaseTool.registry 这个字典中,供后续统一调用。
这个 @classmethod 的作用是——将一个函数 fn 注册进当前类的 registry 字典中,键是函数名,值是函数本体。
class MyTools(BaseTool):
@BaseTool.register
def get_weather(location: str) -> str:
return f"{location} 今天晴,25°C"
等价于:
def get_weather(location: str) -> str:
return f"{location} 今天晴,25°C"
BaseTool.register(get_weather)
{
"get_weather": <function get_weather(...)>,
"get_time": <function get_time(...)>
}
当你执行:
for name, fn in cls.registry.items():
name | fn |
---|---|
“get_weather” | |
“get_time” |
Python 的 inspect.signature(fn) 是标准库中的函数,用来获取函数的参数信息。
def get_weather(location: str):
pass
sig = inspect.signature(get_weather)
print(sig) # (location: str)
代码 | 作用 |
---|---|
cls.registry.items() | 获取注册的所有函数名和函数本体 |
inspect.signature(fn) | 获取函数的参数定义 |
构建 parameters 字典 | 转成 OpenAI tools 能识别的格式 |