没有工具的 Agent = 只有嘴的程序员
想象一个只会说不会做的程序员:
PM:帮我把这个 Bug 修了程序员:好的,你需要打开 src/utils.py,找到第 42 行,把 return None 改成 return [],然后保存...PM:你为什么不自己改?程序员:我没有手。
这就是没有工具的 LLM。它能分析问题、给出方案,但无法执行任何操作。
工具系统就是给 Agent 装上"手脚"。
Function Calling:LLM 与工具的桥梁
LLM 本身不能调用函数,但它可以输出一个结构化的请求,告诉外部系统"我想调用这个函数"。
LLM 输出:{"name": "read_file","arguments": {"file_path": "/src/main.py"}}外部系统:1. 解析这个请求2. 执行 read_file("/src/main.py")3. 把结果返回给 LLM
这就是 OpenAI 提出的 Function Calling 机制,现在已成为行业标准。
工具 Schema 定义
每个工具需要定义一个 JSON Schema,告诉 LLM 这个工具能做什么、需要什么参数:
{"name": "edit_file","description": "Edit a file by replacing a specific text section","parameters": {"type": "object","properties": {"file_path": {"type": "string","description": "Absolute path to the file"},"old_str": {"type": "string","description": "The text to search for and replace"},"new_str": {"type": "string","description": "The text to replace with"}},"required": ["file_path", "old_str", "new_str"]}}
Schema 设计的黄金法则:
1.description 要精确:LLM 靠描述决定何时调用工具
2.参数要有类型和描述:减少 LLM 传错参数的概率
3.required 要合理:必填参数越少,调用成功率越高
4.不要太多工具:10-15 个工具是甜蜜点,太多 LLM 会选错
工具分类与设计
一个完整的 AI Coding Agent 需要哪些工具?
文件操作类(核心)
工具 | 功能 | 风险等级 |
read_file | 读取文件内容 | 🟢 安全 |
write_file | 创建/覆盖文件 | 🟡 中等 |
edit_file | 精确替换文件内容 | 🟡 中等 |
delete_file | 删除文件 | 🔴 危险 |
设计要点:
# read_file:支持 offset + limit,避免一次读取超大文件def read_file(file_path: str, offset: int = 1, limit: int = 2000) -> str:"""读取文件指定行范围"""with open(file_path, 'r') as f:lines = f.readlines()return ''.join(lines[offset-1:offset-1+limit])# edit_file:基于搜索替换,比"覆盖整个文件"更安全def edit_file(file_path: str, old_str: str, new_str: str) -> str:"""精确替换文件中的文本片段"""content = Path(file_path).read_text()if old_str not in content:return "Error: old_str not found in file"new_content = content.replace(old_str, new_str, 1) # 只替换第一个匹配Path(file_path).write_text(new_content)return "File edited successfully"
为什么用 edit_file 而不是 write_file?
write_file 的风险:Agent 需要输出整个文件内容 → 容易丢失代码 → 一次失误全毁edit_file 的优势:Agent 只需输出要修改的部分 → 改动最小化 → 出错范围可控
代码搜索类
工具 | 功能 | 使用场景 |
grep | 正则搜索文件内容 | 查找函数定义、变量引用 |
glob | 按文件名模式搜索 | 查找特定类型的文件 |
list_directory | 列出目录内容 | 了解项目结构 |
命令执行类
工具 | 功能 | 风险等级 |
run_command | 执行终端命令 | 🔴 危险 |
这是最强大也最危险的工具。必须加安全限制。
工具路由器:统一调度中心
所有工具通过路由器统一管理:
Agent 请求调用工具↓ToolRouter↓┌────┼────┐↓ ↓ ↓文件工具 搜索工具 命令工具
路由器的核心职责:
class ToolRouter:def register(self, tool: BaseTool) -> None:"""注册工具"""def get_tool(self, name: str) -> BaseTool | None:"""按名称查找工具"""def get_all_schemas(self) -> list[dict]:"""获取所有工具的 Schema(给 LLM 用)"""async def execute(self, name: str, arguments: dict) -> ToolResult:"""执行工具"""
为什么需要路由器?
5.统一接口:所有工具通过同一个入口调用
6.Schema 聚合:一次性获取所有工具描述,传给 LLM
7.安全拦截:在执行前做权限检查
8.结果标准化:所有工具返回统一的 ToolResult
安全机制:给手脚戴上手套
Agent 能执行命令、修改文件,如果被恶意引导后果严重。
1. 危险操作确认
class BaseTool:def requires_confirmation(self) -> bool:"""是否需要用户确认"""return False # 默认不需要class DeleteFileTool(BaseTool):def requires_confirmation(self) -> bool:return True # 删除文件需要确认class RunCommandTool(BaseTool):def requires_confirmation(self) -> bool:return True # 执行命令需要确认
执行流程:
Agent 请求 delete_file("/important/config.yaml")↓requires_confirmation? → Yes↓弹出确认框:Agent 想要删除 config.yaml,是否允许?↓用户确认 → 执行用户拒绝 → 返回 "User declined this operation"
2. 命令黑名单
BLOCKED_COMMANDS = ["rm -rf /", # 删除整个系统"format C:", # 格式化磁盘"shutdown", # 关机"curl | bash", # 远程执行脚本"chmod 777", # 危险权限]
3. 路径限制
def validate_path(file_path: str, allowed_dirs: list[str]) -> bool:"""确保文件操作只在允许的目录内"""abs_path = os.path.abspath(file_path)return any(abs_path.startswith(d) for d in allowed_dirs)
工具设计的实战经验
经验 1:工具粒度要适中
太粗:code_editor(action="modify", file="a.py", content="...")→ LLM 不知道有哪些 action,容易用错太细:insert_line(), delete_line(), replace_line(), append_line()→ 工具太多,LLM 选择困难适中:edit_file(old_str, new_str)→ 一个工具覆盖所有修改场景,参数语义清晰
经验 2:工具结果要精简
# 不好:返回整个文件内容def grep(pattern, path):# 返回所有匹配行 + 前后 10 行上下文# 一个 grep 可能返回 5000 行# 好:限制返回行数def grep(pattern, path, max_results=50):# 最多返回 50 行匹配结果
原因:工具结果会进入对话历史,太长会快速消耗上下文窗口。
经验 3:错误信息要有指导性
# 不好return "Error: file not found"# 好return "Error: file '/src/main.py' not found. Use list_directory('/src') to see available files."
LLM 看到有指导性的错误信息,会自动采取正确的下一步操作。
经验 4:工具描述要教 LLM 何时使用
# 不好description = "Read a file"# 好description = """Read file content. Use this when you need to:- Understand existing code before modifying it- Check current file state- Find specific code patternsAlways read a file BEFORE editing it."""
工具系统的未来
当前的工具系统是"被动"的——LLM 请求什么就执行什么。未来的方向:
9.智能工具推荐:根据任务上下文,主动推荐合适的工具
10.工具组合:自动编排多个工具完成复杂操作(如"重构"= read + grep + edit × N)
11.工具学习:Agent 根据执行结果,学习哪些工具组合更有效
12.MCP 协议:Model Context Protocol,标准化的工具接入协议
夜雨聆风