在 Finch 里,Agent 能调用的一切——读文件、发搜索、改主题、装 Mini Tool、连 MCP server——都叫「工具」(Tool)。这些工具不是单一来源:了解它们分别从哪来、怎么命名、怎么被授权,能帮你判断「这个能力该不该自己写一个 Mini Tool」,以及 Mini Tool 里的工具要怎么和内置工具配合。
工具的四层来源
| 层级 | 来源 | 特点 |
|---|---|---|
| Agent 运行时内置 | Finch 依赖的 Agent 核心(pi 二进制) | 文件读写、搜索、终端等基础原语,随 Agent 核心分发,不经过 Finch 的工具注册表 |
| 工具集 | Finch 源码 | Finch 自己实现的「一个工具多个 action」的复合工具,覆盖记忆、Skill、会话、App 控制、自动化等产品能力 |
| Mini Tool 贡献的工具 | 已安装并启用的 Mini Tool,通过 ctx.tools.register() 注册 | 见 Mini Tools 开发指南 |
| MCP 贡献的工具 | 官方 MCP 桥接 Mini Tool 动态接入的外部 MCP server | 工具名固定格式 mcp__<server>__<tool> |
四层工具在模型看来没有本质区别——都是一次函数调用;区别主要体现在命名规则和权限网关上,见下文。
Agent 运行时内置工具
这些工具由 Finch 依赖的 Agent 核心提供,Finch 只持有它们的权限策略(是否需要用户确认、超时时间),不重新实现执行逻辑。
| 工具 | 用途 |
|---|---|
Read | 读文件(支持按行 offset/limit 读取,也能读图片、PDF) |
Write | 创建/覆盖文本文件 |
Edit | 对已读取过的文件做精确文本替换 |
Grep | 正则内容搜索(支持 glob 过滤、上下文行、多种输出模式) |
Glob | 按文件名模式匹配 |
Bash | 执行 shell 命令;危险命令(rm、git reset --hard 等)与只读命令有专门的判定逻辑 |
WebSearch | 网页搜索 |
WebFetch | 抓取单个网页并转换为文本/Markdown |
AskUserQuestion | 结构化多选问答卡片,Finch 用它替代纯文本提问 |
TodoWrite | 维护当前任务的待办清单,供长任务过程中展示进度 |
Skill | Composer 里插入 /skill 引用时,由此工具承载 Skill 的调用 |
这一层是「基础设施」层,Mini Tool 不需要也不应该重新实现文件读写、搜索这类能力——直接假设它们已经存在。
工具集
Finch 自己的产品能力没有拆成几十个零散工具,而是收敛成 7 个「调度器工具」,每个工具用 action 参数复用同一个工具定义,模型省略 action 时会拿到该工具的完整用法说明(等价于工具自带的 --help)。
| 工具 | 用途 | 常见 action |
|---|---|---|
Memory | 中长期记忆管理,落盘到 MEMORY.md / 每日日志 / Space 记忆 | remember / replace / forget / search |
Skills | 发现并调用 SKILL.md 技能包 | list / invoke |
Session | 当前会话内的操作 | search(历史会话)/ extendDir(信任新目录)/ attach(把文件推进输入框)/ rename |
AppCall | App 级控制 + 用户画像写入 | info / setAppearance / listSpaces / listModels / createSpace / checkUpdate / saveProfile 等 |
ToolSearch | 发现并激活当前会话尚未加载的动态工具(详见下文) | 传 query/source/limit |
MiniTool | 管理已安装的 Mini Tool(通过 @finchtoys/minitools CLI) | list / add / update / remove / enable / disable / reload |
Schedule | 管理定时自动化任务 | list / create / update / enable / disable / delete / run / history |
这套「调度器」设计是 Finch 自己的架构选择,不是 Mini Tool API 的强制要求——Mini Tool 既可以照这个思路把一组相关能力收进一个带 action 的工具,也可以按需注册多个独立工具,取决于你希望模型怎么发现和调用它们。
动态工具与 ToolSearch
不是所有工具都会在新会话一开始就塞进模型的工具表——工具定义本身要消耗上下文,工具越多,模型选择的准确率越低。Finch 用 exposure 区分两种暴露策略:
startup(默认):新会话启动时就注入,例如上面列出的内置工具和调度器工具。dynamic:不进初始工具表,需要先被激活才能调用。典型场景是 MCP server 暴露的工具、按需发现的大量 Mini Tool 工具。
模型需要用不常用的工具时,先调用 ToolSearch 按自然语言描述检索,Finch 把匹配到的工具「激活」进当前会话,模型才能在下一步真正调用它们。MCP 贡献的工具遵循固定命名 mcp__<server>__<tool>(例如 mcp__tavily__tavily_search),且永远不会预加载——必须先经过 ToolSearch source="mcp" 激活。
Mini Tool 贡献工具的命名
Mini Tool 通过 ctx.tools.register({ name, ... }) 注册的工具,模型看到的最终名字是 <mini-tool-id>_<name>(例如 id 为 myextension 的 Mini Tool 注册了 search_docs,模型看到的是 myextension_search_docs)。
如果一个 Mini Tool 代表另一个 Mini Tool 的贡献注册工具(比如官方 MCP 桥接为其它 Mini Tool 贡献的 MCP server 注册工具),可以在工具定义里设置 owner 字段,把工具的来源、权限归属、UI 计数都记到贡献方而不是实际注册方名下——这是官方 MCP 桥接自己用的机制,普通 Mini Tool 一般不需要。
权限与执行策略
每次工具调用在执行前都要过一层策略判断,一共三种结果:
| 策略 | 行为 | 适用对象 |
|---|---|---|
完全免审批(autoAllow) | 直接执行,不弹权限卡 | AskUserQuestion/TodoWrite/Skill,以及 Finch 调度器工具里除 MiniTool 外的全部(Memory/Skills/Session/AppCall/ToolSearch/Schedule) |
快速通道(acceptCallsAutoAllow) | 仅在用户把权限模式切到「Accept Calls」时自动放行,默认模式仍需确认 | Read/Write/Edit/Grep/Glob/WebSearch/WebFetch,以及只读的 Bash 命令 |
| 逐次确认(权限卡) | 每次调用都展示权限卡,用户手动批准 | 危险 Bash 命令、所有 Mini Tool 贡献的工具、MiniTool 调度器工具本身 |
关键结论:Mini Tool 贡献的工具永远不会自动放行,无论用户处于哪种权限模式——每次调用都要经过权限卡。这是有意的设计:内置工具和 Finch 自身的调度器工具经过审计,Mini Tool 代码来自第三方,不能获得同等信任级别。写 Mini Tool 工具时,把这一点考虑进交互设计:如果某个操作会被频繁调用,尽量把它拆成一次性配置(存进 ctx.storage/ctx.settings)而不是每轮都触发一次新的工具调用。
Bash 单独判定「只读 / 危险」:只读命令(如 ls、git status)走快速通道,rm、git reset --hard 一类命令始终要求确认,与权限模式无关。
给 Mini Tool 开发者的启示
- 先看这张清单,再决定要不要写新工具:文件读写、搜索、网页抓取、待办清单这些能力已经存在,不要在 Mini Tool 里重新造轮子。
- 工具名不会和内置工具冲突:模型侧名字自动加了
<mini-tool-id>_前缀,但工具描述仍然要写清楚「和 Read/Grep 这类通用工具比,我的工具专门解决什么」,否则模型可能优先选熟悉的内置工具。 description决定被调用的概率:内置工具和调度器工具的description都在系统提示词里长期可见、经过反复打磨;你的 Mini Tool 工具只有安装且启用后才会出现,描述要足够具体地说明触发场景,才能在候选工具里被模型选中。- 不要假设自己会被无审批调用:设计交互时默认「每次调用都会先给用户看一张权限卡」,把关键信息(会做什么、影响范围)放进
title/description,而不是指望用户去看代码。
参考资料
- 工具注册与执行接口:见 Mini Tools 开发指南 中的
ctx.tools - 完整类型定义:npm 包
@finchtoys/minitool-api