目录

统一注册工具函数、统一调用执行的框架,就像你现在做的那样 —— 类似于一个「工具工厂」。

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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
机制作用
@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"
1
2
3
4
5

等价于:

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(...)>
}
1
2
3
4
5
6
7
8
9

当你执行:

for name, fn in cls.registry.items():
1
namefn
“get_weather”<function get_weather>
“get_time”<function get_time>

Python 的 inspect.signature(fn) 是标准库中的函数,用来获取函数的参数信息

def get_weather(location: str):
    pass

sig = inspect.signature(get_weather)
print(sig)  # (location: str)
1
2
3
4
5
代码作用
cls.registry.items()获取注册的所有函数名和函数本体
inspect.signature(fn)获取函数的参数定义
构建 parameters 字典转成 OpenAI tools 能识别的格式
上次更新: 9/13/2025, 4:21:56 PM
Contributors: jacinli