上一篇文章讨论了 Agent。Agent 是围绕目标、上下文、工具和执行策略的一层任务调度机制。如果 Agent 只能调用 LLM它能做的事情其实很有限。它可以分析问题。它可以生成文本。它可以给出建议。但它不能直接读取项目文件。不能查询数据库。不能检索知识库。不能调用业务接口。不能执行特定动作。如果想让 Agent 具备行动能力就需要 Tool。不过Tool 不是随便写几个函数交给模型调用。在工程项目里Tool 必须是可描述、可校验、可控制、可追踪的能力接口。否则 Agent 的能力越强系统风险也越高。一、为什么 Agent 需要 ToolLLM 本身擅长理解和生成文本。但它并不天然知道外部系统的实时状态。例如当前项目有哪些文件数据库里有没有某条记录用户上传的文档内容是什么某个接口现在返回什么结果测试命令执行是否通过这些问题不能只靠模型自己猜。它需要通过工具获取外部信息。比如read_file读取文件内容。 search_code搜索代码。 query_database查询数据库。 search_docs检索知识库。 call_api调用业务接口。 run_tests运行测试命令。有了 ToolAgent 才能从“只会说”变成“可以查、可以看、可以执行”。但这也带来了新的问题工具能力越强越需要边界。所以 Tool 设计不能只考虑能不能调用。还要考虑能不能安全地调用。二、Tool 不是普通函数普通函数通常是给程序内部代码调用的。例如defget_user(user_id:str):returndb.query(User).filter(User.iduser_id).first()这个函数的调用者通常是开发者写好的代码。参数怎么传、什么时候调用、结果怎么处理都由代码控制。但 Tool 不一样。Tool 是暴露给 Agent 或 LLM 的能力。模型可能会根据自然语言任务决定是否调用它。所以 Tool 必须比普通函数多一些信息。至少要说明这个工具叫什么。它能做什么。什么时候应该使用。输入参数有哪些。输出结果是什么。有什么限制。是否需要权限。调用失败时怎么处理。也就是说Tool 不是简单函数。Tool 是可以被 AI 系统理解和调度的受控能力。三、一个 Tool 应该包含哪些字段一个工程化 Tool 可以包含这些字段name工具名称。 description工具说明。 input_schema输入参数结构。 output_schema输出结果结构。 permission权限级别。 timeout超时时间。 run执行逻辑。例如classTool:name:strdescription:strinput_schema:dictoutput_schema:dictpermission:strtimeout:intdefrun(self,arguments:dict)-dict:raiseNotImplementedError这些字段不是形式主义。它们分别解决不同问题。name让 Agent 知道调用哪个工具。description让 Agent 理解工具用途。input_schema用来校验参数。output_schema让结果结构更稳定。permission用来控制风险。timeout避免工具调用卡死。run才是真正的执行逻辑。如果 Tool 只有一个函数没有这些元信息Agent 就很难稳定、可靠地使用它。四、Tool 的描述要写清楚Tool 的描述非常重要。因为 Agent 选择工具时往往会参考工具描述。一个不清楚的描述会导致模型误用工具。例如name: search description: search something这个描述太模糊。它没有说明搜索哪里。没有说明适用场景。没有说明返回什么。更好的写法是name: search_code description: 在当前项目代码中搜索关键词适合查找函数名、类名、配置项或错误信息。该工具只读取代码内容不会修改文件。这个描述更清楚。它说明了搜索范围。说明了适用场景。说明了只读属性。Tool 描述写得越清楚Agent 越容易正确选择工具。五、输入参数必须有 schemaTool 的输入不能随便传。如果没有参数 schemaAgent 可能会生成不稳定的参数。例如search_code(main.py error)search_code({keyword:error})search_code({query:error,path:src})这些写法可能都能表达搜索意图但程序需要明确一种格式。可以使用 JSON Schema 描述输入。例如input_schema{type:object,properties:{query:{type:string,description:要搜索的关键词},path:{type:string,description:搜索目录默认为项目根目录}},required:[query]}这样 Agent 调用工具时就应该生成{query:DATABASE_URL,path:app}Tool 执行前可以先校验参数。参数不合法时应该返回明确错误而不是直接执行。六、输出结果也要结构化很多人只关注 Tool 输入却忽略输出。如果 Tool 输出是一段随意字符串Agent 后续处理会很不稳定。例如找到了几个文件好像有问题。这种输出对模型可能能读懂但对程序很难处理。更好的方式是返回结构化结果。例如{success:true,matches:[{file:app/config/settings.py,line:12,text:DATABASE_URL ...}]}结构化输出有几个好处。第一方便 Agent 继续分析。第二方便日志记录。第三方便测试。第四方便前端展示。第五方便后续接入 Workflow。所以 Tool 不只是执行动作还要提供稳定的结果结构。七、工具白名单很重要Agent 不应该能调用系统里的所有函数。它只能调用被明确注册的 Tool。这就是工具白名单。例如tools[ReadFileTool(),SearchCodeTool(),SearchDocsTool(),]agentAgent(llmllm,toolstools,)Agent 只能在这个列表里选择工具。没有注册的工具不能调用。这样做的好处是边界清楚。不同 Agent 可以有不同工具集。例如代码分析 Agent 可以读取文件和搜索代码。内容生成 Agent 可以检索资料但不能读本地文件。客服 Agent 可以查订单但不能修改订单。工具白名单是 Agent 权限控制的第一层。八、Tool 要有权限分级不同工具的风险不一样。读取文件和删除文件不是一个级别。查询订单和退款也不是一个级别。所以 Tool 应该有权限分级。例如read只读操作。 write写入操作。 external访问外部服务。 dangerous高风险操作。只读工具通常风险较低。写入工具需要更谨慎。高风险工具可能需要人工确认。例如classDeleteFileTool(Tool):namedelete_filepermissiondangerous当 Agent 试图调用高风险工具时系统可以拒绝执行或者要求用户确认。这不是在限制 Agent。这是让 Agent 在可控边界内运行。九、Tool 不能绕过业务接口Tool 可以封装业务能力。但不应该让模型直接操作底层数据库或危险 API。例如不建议设计成run_sql(sql:str)这个工具太危险。Agent 如果生成错误 SQL可能会造成数据风险。更好的方式是封装明确业务接口。例如get_order_status(order_id:str)create_support_ticket(user_id:str,content:str)这样工具能力更窄。输入输出更明确。权限也更容易控制。Tool 的设计原则是给 Agent 必要能力。不要把系统底层权限直接交给模型。能力越具体风险越可控。十、Tool 调用必须有日志Tool 调用日志非常重要。因为当 Agent 结果不符合预期时需要知道它调用了哪些工具。至少应该记录工具名称。调用参数摘要。调用时间。执行耗时。是否成功。错误信息。返回结果摘要。例如{tool:search_code,success:true,latency_ms:120,query:DATABASE_URL}注意不一定要把完整参数和完整结果都写入日志。因为里面可能包含用户隐私或敏感数据。生产环境要做脱敏。但没有日志是不行的。没有 Tool 调用日志就很难解释 Agent 为什么做出某个判断。十一、Tool 要有超时和错误处理Tool 调用可能失败。例如文件不存在。接口超时。数据库连接失败。参数不合法。外部服务不可用。所以 Tool 必须有错误处理。不要让底层异常直接暴露给 Agent。可以统一返回{success:false,error:{type:timeout,message:tool execution timeout}}这样 Agent 可以根据错误类型决定下一步。是重试是换工具是向用户说明失败还是停止任务同时Tool 要设置超时时间。否则一个工具卡住整个 Agent 都会卡住。十二、Tool 和 Workflow、Agent 的关系Tool 不应该直接控制整个任务流程。Tool 只负责完成一个具体动作。Workflow 负责编排固定流程。Agent 负责动态选择动作。例如Agent 决定需要搜索代码 ↓ 调用 search_code Tool ↓ Tool 返回搜索结果 ↓ Agent 根据结果决定下一步在 Workflow 里也可以使用 Tool。例如输入问题 ↓ 检索知识库 Tool ↓ 渲染 Prompt ↓ 调用 LLM ↓ 输出回答所以 Tool 是底层能力。Agent 和 Workflow 是能力编排者。不要把 Tool 写成一个大而全的流程模块。否则边界会变乱。十三、Tool 在 AI Scaffold 里的位置在 AI Scaffold 里Tool 可以作为独立模块。项目结构可以这样设计app/ ├── agents/ │ └── code_agent.py ├── workflows/ │ └── rag_workflow.py ├── llm/ │ └── factory.py ├── tools/ │ ├── __init__.py │ ├── base.py │ ├── registry.py │ ├── file_tools.py │ ├── search_tools.py │ └── business_tools.py └── config/ └── settings.py其中tools/base.py定义 Tool 基类。tools/registry.py管理工具注册。file_tools.py放文件相关工具。search_tools.py放检索相关工具。business_tools.py放业务接口封装。Agent 不直接扫描所有工具。而是从 registry 中拿到允许使用的工具集。例如tool_registry.get_tools_for_agent(code_agent)这样工具管理会更清楚。十四、总结Tool 是 Agent 获得行动能力的关键。但 Tool 不是普通函数也不是无限开放的系统权限。一个合理的 Tool 设计至少应该做到有明确名称。有清晰描述。有输入参数 schema。有结构化输出。有工具白名单。有权限分级。有超时控制。有错误处理。有调用日志。不绕过业务接口直接操作底层资源。和 Agent、Workflow 保持边界。对于 AI Scaffold 来说Tool 层是 Agent 和外部世界之间的能力接口。它既要让 Agent 能做事也要让 Agent 在可控范围内做事。如果说 Agent 是任务调度层那么 Tool 就是它可以使用的受控能力集合。下一篇文章可以继续讨论 RepositoryAI 应用里的数据访问层应该如何设计才能让业务数据、向量数据和模型调用结果有清晰的存储边界。