DEV Community

lijesom9-create
lijesom9-create

Posted on

工具系统设计哲学:三个AI项目的工具架构对比

工具系统设计哲学:让AI拥有"双手"的艺术

工具是AI Agent的手脚。没有工具,LLM只是一个"嘴炮"——能说会道,但啥也干不了。本文基于三个开源项目的实践,结合OpenAI Tool Use规范,探讨工具系统设计的核心哲学。

前言

2024年,AI领域最大的进步是什么?

不是模型更大了,不是推理更快了,而是AI学会使用工具了

从ChatGPT的Function Calling,到Claude的Tool Use,再到各种Agent框架的工具系统——工具让AI从"聊天机器人"进化成了"数字员工"。

但工具设计是一门艺术。设计得好,AI如虎添翼;设计得差,AI寸步难行。

工具设计的三个哲学

哲学1:工具是手,不是脑

工具应该做"脏活累活",不要让工具做决策。

反例

@tool(description="分析代码质量并决定是否重构")
def analyze_and_refactor(code: str) -> str:
    # 工具里做了太多决策
    quality = assess_quality(code)
    if quality < 0.7:
        return refactor(code)
    return code
Enter fullscreen mode Exit fullscreen mode

正例

@tool(description="分析代码质量,返回质量分数")
def analyze_quality(code: str) -> float:
    return assess_quality(code)

@tool(description="重构代码")
def refactor_code(code: str) -> str:
    return do_refactor(code)
Enter fullscreen mode Exit fullscreen mode

原则:工具提供能力,Agent做决策。让AI来决定"是否重构",而不是让工具自己决定。

哲学2:粒度适中,不粗不细

工具太粗 → AI无法精确控制
工具太细 → AI调用太多次,效率低下

太粗的例子

@tool(description="处理整个项目")
def process_project(project_path: str, action: str) -> str:
    # 一个工具包打天下,AI不知道怎么用
    pass
Enter fullscreen mode Exit fullscreen mode

太细的例子

@tool(description="读取文件的第一行")
def read_first_line(path: str) -> str: ...

@tool(description="读取文件的第二行")
def read_second_line(path: str) -> str: ...

# 太细了,AI要调用几百次
Enter fullscreen mode Exit fullscreen mode

适中的粒度

@tool(description="读取文件内容,支持指定行范围")
def read_file(path: str, start_line: int = None, end_line: int = None) -> str:
    """读取文件,可选指定行范围"""
    pass
Enter fullscreen mode Exit fullscreen mode

哲学3:描述即文档

工具的description是AI理解工具的唯一途径。好的描述 = 好的工具使用。

糟糕的描述

@tool(description="处理文件")
def process_file(path: str) -> str: ...
Enter fullscreen mode Exit fullscreen mode

好的描述

@tool(description="读取文件内容并返回。支持UTF-8和GBK编码。如果文件不存在,返回错误信息。")
def read_file(path: str) -> str:
    """
    读取指定路径的文件内容。

    参数:
        path: 文件的相对或绝对路径

    返回:
        文件内容的字符串,或错误信息

    示例:
        read_file("src/main.py") → 返回main.py的内容
    """
    pass
Enter fullscreen mode Exit fullscreen mode

三个项目的工具系统对比

项目1:CodeLite的装饰器模式

CodeLite用Python装饰器实现工具注册,优雅且自动:

# tools.py
_registry = {}

def tool(name=None, description="", dangerous=False):
    def decorator(func):
        tool_name = name or func.__name__

        # 从函数签名自动生成JSON Schema
        schema = generate_schema_from_signature(func)

        _registry[tool_name] = {
            "name": tool_name,
            "description": description,
            "parameters": schema,
            "handler": func,
            "dangerous": dangerous
        }
        return func
    return decorator

# 使用
@tool(description="读取文件内容")
def read_file(path: str) -> str:
    with open(path) as f:
        return f.read()
Enter fullscreen mode Exit fullscreen mode

优点:代码即文档,自动Schema生成,开发效率高
缺点:Python特有,其他语言难以复用

项目2:Education-Agent的类注册模式

Education-Agent用类来组织工具,更结构化:

# tools/base.py
class BaseTool:
    name: str
    description: str
    parameters: dict

    def execute(self, **kwargs) -> str:
        raise NotImplementedError

# tools/search.py
class SearchKnowledgeTool(BaseTool):
    name = "search_knowledge"
    description = "搜索知识库,返回相关文档片段"
    parameters = {
        "query": {"type": "string", "description": "搜索查询"},
        "top_k": {"type": "integer", "description": "返回结果数量", "default": 5}
    }

    def execute(self, query: str, top_k: int = 5) -> str:
        # 实现搜索逻辑
        results = self.vector_store.search(query, top_k=top_k)
        return format_results(results)

# 注册
tool_registry = ToolRegistry()
tool_registry.register(SearchKnowledgeTool())
tool_registry.register(WebSearchTool())
Enter fullscreen mode Exit fullscreen mode

优点:结构清晰,易于测试和扩展
缺点:代码量较多,不够简洁

项目3:DeepScope的Schema驱动模式

DeepScope用JSON Schema定义工具,更接近OpenAI规范:

# tools/definitions.py
TOOL_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.py
TOOL_HANDLERS = {
    "web_search": handle_web_search,
    "web_fetch": handle_web_fetch,
    "analyze_content": handle_analyze_content,
}

def execute_tool(name: str, args: dict) -> str:
    handler = TOOL_HANDLERS.get(name)
    if not handler:
        return f"Unknown tool: {name}"
    return handler(**args)
Enter fullscreen mode Exit fullscreen mode

优点:与OpenAI规范完全一致,易于跨语言复用
缺点:Schema和实现分离,维护成本高

工具依赖管理

问题:工具之间有依赖

比如"推送到GitHub"依赖于"git add"和"git commit":

# 错误的方式:让AI自己按顺序调用
# AI可能会忘记先commit再push

# 正确的方式:用工具组合
@tool(description="提交并推送到GitHub")
def git_push(message: str) -> str:
    """自动add、commit、push"""
    run_command("git add .")
    run_command(f'git commit -m "{message}"')
    run_command("git push")
    return "推送成功"
Enter fullscreen mode Exit fullscreen mode

工具组合模式

class ToolChain:
    """工具链:按顺序执行多个工具"""
    def __init__(self):
        self.steps = []

    def add_step(self, tool_name: str, args: dict):
        self.steps.append((tool_name, args))

    def execute(self) -> list[str]:
        results = []
        for tool_name, args in self.steps:
            result = execute_tool(tool_name, args)
            results.append(result)

            # 如果某步失败,停止执行
            if result.startswith("Error"):
                break

        return results

# 预定义的工具链
git_push_chain = ToolChain()
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"})
Enter fullscreen mode Exit fullscreen mode

工具错误处理

错误分类

class ToolError(Exception):
    """工具错误基类"""
    pass

class ToolNotFoundError(ToolError):
    """工具不存在"""
    pass

class ToolPermissionError(ToolError):
    """权限不足"""
    pass

class ToolTimeoutError(ToolError):
    """执行超时"""
    pass

class ToolInputError(ToolError):
    """输入参数错误"""
    pass
Enter fullscreen mode Exit fullscreen mode

错误恢复策略

def execute_with_retry(tool_name: str, args: dict, max_retries: int = 3) -> str:
    """带重试的工具执行"""
    for attempt in range(max_retries):
        try:
            result = execute_tool(tool_name, args)
            return result
        except ToolTimeoutError:
            if attempt < max_retries - 1:
                time.sleep(2 ** attempt)  # 指数退避
                continue
            return "Error: 工具执行超时"
        except ToolInputError as e:
            return f"Error: 输入参数错误 - {e}"
        except ToolError as e:
            return f"Error: {e}"

    return "Error: 超过最大重试次数"
Enter fullscreen mode Exit fullscreen mode

工具安全设计

三级权限模型

class Permission(Enum):
    READ = "read"      # 只读操作
    WRITE = "write"    # 写操作
    EXECUTE = "execute" # 执行命令

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,
}

def check_permission(tool_name: str, required: Permission) -> bool:
    """检查工具权限"""
    tool_perm = TOOL_PERMISSIONS.get(tool_name, Permission.READ)

    # 权限等级:READ < WRITE < EXECUTE
    levels = {Permission.READ: 0, Permission.WRITE: 1, Permission.EXECUTE: 2}
    return levels[tool_perm] <= levels[required]
Enter fullscreen mode Exit fullscreen mode

沙箱执行

def execute_in_sandbox(tool_name: str, args: dict, sandbox_dir: str) -> str:
    """在沙箱中执行工具"""
    # 限制文件操作范围
    if tool_name in ["read_file", "write_file", "edit_file"]:
        path = args.get("path", "")
        if not path.startswith(sandbox_dir):
            return "Error: 文件路径超出沙箱范围"

    # 限制命令执行
    if tool_name == "run_command":
        cmd = args.get("command", "")
        dangerous_commands = ["rm -rf", "format", "sudo"]
        for dc in dangerous_commands:
            if dc in cmd:
                return f"Error: 禁止执行危险命令: {dc}"

    return execute_tool(tool_name, args)
Enter fullscreen mode Exit fullscreen mode

工具调用优化

批量调用

def batch_execute(tool_calls: list[dict]) -> list[str]:
    """批量执行工具调用"""
    # 检查是否有依赖关系
    independent, dependent = separate_dependencies(tool_calls)

    # 独立的工具并行执行
    results = {}
    with ThreadPoolExecutor(max_workers=5) as executor:
        futures = {
            executor.submit(execute_tool, tc["name"], tc["args"]): tc["id"]
            for tc in independent
        }
        for future in as_completed(futures):
            tc_id = futures[future]
            results[tc_id] = future.result()

    # 有依赖的工具顺序执行
    for tc in dependent:
        results[tc["id"]] = execute_tool(tc["name"], tc["args"])

    return results
Enter fullscreen mode Exit fullscreen mode

工具结果缓存

from functools import lru_cache

@lru_cache(maxsize=100)
def cached_read_file(path: str) -> str:
    """缓存文件读取结果"""
    with open(path) as f:
        return f.read()

def execute_with_cache(tool_name: str, args: dict) -> str:
    """带缓存的工具执行"""
    # 只读工具可以缓存
    if tool_name in ["read_file", "list_dir", "search_code"]:
        cache_key = f"{tool_name}:{json.dumps(args, sort_keys=True)}"
        if cache_key in _cache:
            return _cache[cache_key]

        result = execute_tool(tool_name, args)
        _cache[cache_key] = result
        return result

    # 写操作清除相关缓存
    if tool_name in ["write_file", "edit_file"]:
        clear_cache_for_file(args.get("path"))

    return execute_tool(tool_name, args)
Enter fullscreen mode Exit fullscreen mode

实战:设计一个完整的工具系统

需求分析

一个AI编程助手需要这些工具:

类别 工具 用途
文件操作 read_file, write_file, edit_file, apply_patch 读写编辑文件
代码搜索 search_code, find_files 搜索代码
命令执行 run_command 运行Shell命令
Git操作 git_status, git_diff, git_log, git_push Git操作
Web工具 web_search, web_fetch 搜索和抓取网页
Agent工具 spawn_subagent, spawn_parallel 子Agent

完整实现

# tools/registry.py
class ToolRegistry:
    def __init__(self):
        self._tools = {}
        self._categories = {}

    def register(self, name, handler, description="", 
                 parameters=None, dangerous=False, category="general"):
        """注册工具"""
        self._tools[name] = {
            "name": name,
            "handler": handler,
            "description": description,
            "parameters": parameters or {},
            "dangerous": dangerous,
            "category": category
        }

        if category not in self._categories:
            self._categories[category] = []
        self._categories[category].append(name)

    def execute(self, name, args):
        """执行工具"""
        tool = self._tools.get(name)
        if not tool:
            return f"Error: Unknown tool '{name}'"

        try:
            return tool["handler"](**args)
        except Exception as e:
            return f"Error executing {name}: {e}"

    def get_definitions(self):
        """获取所有工具定义(OpenAI格式)"""
        return [
            {
                "type": "function",
                "function": {
                    "name": t["name"],
                    "description": t["description"],
                    "parameters": t["parameters"]
                }
            }
            for t in self._tools.values()
        ]

    def list_by_category(self, category):
        """列出某类别的工具"""
        return self._categories.get(category, [])


# 全局注册表
registry = ToolRegistry()

# 注册工具
registry.register(
    "read_file",
    handler=read_file,
    description="读取文件内容",
    parameters={
        "type": "object",
        "properties": {
            "path": {"type": "string", "description": "文件路径"}
        },
        "required": ["path"]
    },
    category="file"
)
Enter fullscreen mode Exit fullscreen mode

总结

工具系统设计的核心原则:

  1. 工具是手,不是脑 — 提供能力,不做决策
  2. 粒度适中 — 不粗不细,恰到好处
  3. 描述即文档 — 好的描述 = 好的工具使用
  4. 安全第一 — 权限控制,沙箱执行
  5. 错误恢复 — 自动重试,智能修复
  6. 性能优化 — 批量调用,结果缓存

下一篇预告

《Agent安全层设计:如何防止AI误删你的数据库》— 我们会深入探讨Agent系统的安全设计,包括命令分级、权限控制、审计日志等。

参考资料


本文基于三个开源项目的实践,代码已开源。

tags: tools, agent-design, python, architecture, security
series: ai-agent-development

Top comments (0)