news 2026/9/11 21:49:41

Kilo 核心工具架构解析:Tool 表示、Location 注册与结算机制深度指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kilo 核心工具架构解析:Tool 表示、Location 注册与结算机制深度指南

Kilo 核心工具架构解析:Tool 表示、Location 注册与结算机制深度指南

【免费下载链接】kilocodeKilo is the all-in-one agentic engineering platform. Build, ship, and iterate faster with the most popular open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/ki/kilocode

导读

本文以 packages/core/src/tool/AGENTS.md 为核心脉络,系统讲解 Kilo(开源编码 Agent 平台)Core 包中工具子系统的整体架构:一种不透明的规范 Tool 表示、进程级与 Location 级两层注册、生效查找(effective lookup)与结算(settlement)机制。读完本文,你将理解内置工具与应用工具如何共用同一类型、Location 注册如何覆盖应用注册、权限过滤与执行授权如何分离,以及工具输出的通用模型侧收口(bounding)与受管存储(managed output)边界落在哪里。


一、总体架构:一个本地工具表示 + 进程/Location 双层注册

packages/core/src/tool/目录拥有 Core 中唯一的本地工具表示,以及完整的注册、生效与结算链路。该目录下的核心文件构成四个层次:

文件职责
tool.ts定义不透明的规范Tool.make(...)值(description / input / output / execute / toModelOutput)
application-tools.ts存储进程作用域(process-scoped)的应用注册
tools.ts暴露仅含注册能力的Tools.Service视图,供 Location 生产者使用
registry.ts只存放规范工具,叠加 Location 注册、派生定义、调用工具并施加通用输出收口

从源码结构看,这套设计最重要的约束是单一执行入口类型:文档明确要求不要新增第二种可执行条目类型、registry 自有执行器、授权回调、输出路径回调或遗留归一化路径(legacy normalization path)。也就是说,所有可执行工具——无论应用工具还是内置工具——都必须以同一种Tool.make值存在。

对应到具体内置实现,builtins.ts 只组合随发布提供的 Location 作用域内置工具变换,其deps列出的正是basheditapply_patchwriteglobgrepquestionreadskilltodowritewebfetchwebsearch等工具节点。文件头部注释同时指出:动态 MCP 与插件工具后续走独立的 scoped 规范注册,而不是进入这份静态内置列表。


二、构造(Construction):不透明 Tool 值与其运行时细节

2.1Tool.make的配置面

Tool.make接收一个Config,其核心字段在 tool.ts 中定义:

  • description:模型可见的工具描述;
  • input/output:均基于 Effect Schema 的编解码器,构成工具输入/输出的唯一事实来源;
  • structured/toStructuredOutput(可选):用于派生结构化输出;
  • execute:真正的执行函数,签名是(input, context) => Effect<Output, ToolFailure>
  • toModelOutput(可选):把已验证的输出投影成模型可读的Content数组(文本或内联文件)。

而工具值是不透明的:调用方拿到的只是一个Object.freeze({})Definition对象,其 codec、执行器、定义派生逻辑与目录权限声明全部被隐藏在WeakMap<AnyTool, Runtime>中(tool.ts),外部无法直接触碰。Tool.definition(name, tool)Tool.settle(tool, call, context)Tool.permission(tool, name)Tool.withPermission(tool, permission)是仅有的几个公开操作入口。

2.2 名字校验与权限装饰

Tool.validateName用正则/^[A-Za-z][A-Za-z0-9_-]{0,63}$/校验工具名,失败时抛出Tool.RegistrationErrorTool.withPermission通过复制 runtime 并覆写permission字段来装饰工具(tool.ts),装饰后的值依然是同一个不透明类型。

2.3 规范调用上下文的权限源

Location 作用域的内置工具层在构造期间获取PermissionV2.Service及其所需的其他 Location 服务,执行器捕获这些服务。权限源(permission source)总是从规范的调用上下文构造:

const source = { type: "tool" as const, messageID: context.assistantMessageID, callID: context.toolCallID, }

这一代码模式在 bash.ts、edit.ts、write.ts、apply-patch.ts 中完全一致——Context结构(sessionIDagentassistantMessageIDtoolCallID)在 tool.ts 中统一定义。

2.4 错误翻译纪律

文档强调:叶子工具自行负责解析、权限与副作用顺序,并且只把预期的类型化错误翻译成ToolFailure,不要使用catchCause——因为中断(interruption)和缺陷(defect)必须原样存活。这正是 Effect 生态中"错误是值、缺陷是缺陷"的边界体现:工具内部可以用Effect.mapError把业务错误包装成ToolFailure(如 bash.ts 的Unable to execute command),但绝不能把中断/缺陷误吞为普通工具失败。


三、注册(Registration):两层作用域与覆盖规则

3.1 两条注册通路

  • 内置工具通过Tools.Service.register({ [name]: tool })注册,例如 bash.ts、edit.ts;
  • 应用工具通过ApplicationTools.Service.register(...)注册,对外公开为opencode.tools.register(...)

ApplicationTools.Service是**进程级(process-scoped)**的,由所有 Location 共享;而ToolRegistry.Service是 **Location 级(Location-scoped)**的。文档明确禁止把 registry 做成进程全局,也禁止为每个 Location 单独构造应用工具服务——保证应用工具注册一次、处处可见,而 Location 内注册天然隔离。

3.2 覆盖(shadowing)语义

两层注册的生效规则(来自 registry.ts 的实现):

  1. 最新活跃的同层注册胜出:Location 本地注册以Map<string, Array<Registration>>栈式存储,查找时取entries.at(-1)
  2. 关闭任一注册只移除该注册,并暴露下一个活跃注册:注册通过Effect.addFinalizer登记清理逻辑(registry.ts),Scope 关闭时只移除当前 token 对应的条目;
  3. Location 注册优先于应用注册settleWith中先查local栈,查不到才回落到applications.entries()
  4. 调用在结算开始时捕获生效工具materialize把应用注册与当前 Location 注册合并成一张不可变快照,后续settle都基于这张快照,避免注册中途变更导致执行不一致。

此外还有一个"陈旧调用"保护:settleWith支持传入advertised(广告过的注册身份),如果实际生效的注册身份与广告不一致,直接返回Stale tool call: <name>错误(registry.ts),防止模型基于旧定义发起调用。

3.3 注册期的并发与原子性

ToolRegistry.register内部包裹在Effect.uninterruptible中(registry.ts),确保一批工具的注册/清理是原子的。注册前还会用Effect.forEach批量执行validateName,任一名字非法则整批失败。


四、权限(Permissions):定义过滤 ≠ 执行授权

这是本架构最容易误解的一点,值得单独展开:

  • registry 不依赖PermissionV2.Service,也不做执行授权。从 registry.ts 的Service声明可见,ToolRegistry的依赖只有ApplicationToolsToolOutputStore
  • 有一个仅供内置工具使用的内部操作,会给工具附加一个 permission action,目的纯粹是为了保留整工具定义过滤(whole-tool definition filtering)能力,它不是公开Tool.make的一部分;
  • 大多数工具默认以其注册名为 action;而editwriteapply_patch三个工具统一声明共享的editaction——在 edit.ts、write.ts、apply-patch.ts 中均可看到Tool.withPermission(tool, "edit")的用法。

定义过滤是目录可见性(catalog visibility),不是执行授权。调用如果走到了结算,仍会执行捕获到的叶子策略(leaf policy)。在实现上,materializewhollyDisabled(action, permissions)判断:只有命中resource === "*" && effect === "deny"的通配规则时,才把工具从definitions中剔除(registry.ts);而真正的授权发生在叶子工具执行时的permission.assert(...)调用——例如 bash.ts 对命令本身、edit.ts 对编辑资源。

此外,叶子工具的解析顺序统一为:先用LocationMutation.resolve解析路径 → 若目标在外部目录则先断言external_directory权限 → 再断言自身 action(如edit)。bash.ts、edit.ts、write.ts、apply-patch.ts 都是这一模式。


五、输出(Output):settle是唯一的执行与收口边界

5.1 结算(settlement)流程

文档明确指出:ToolRegistry.Materialization.settle唯一的执行与通用模型输出收口(generic model-output bounding)边界,并且拥有受管保留路径(managed retention paths)。

结合 registry.ts 与 tool.ts 的实现,settle的完整链路是:

  1. 按调用名查找注册(Location 栈优先,回落应用注册);
  2. Tool.settleSchema.decodeUnknownEffect(config.input)解码调用输入,失败映射为Invalid tool input: ...ToolFailure
  3. 执行config.execute(input, context)
  4. Schema.encodeEffect(config.output)编码输出,失败映射为Tool returned an invalid value for its output schema: ...
  5. 若配置了structured+toStructuredOutput,再编码结构化输出;
  6. 调用toModelOutput生成Content数组——文本部分直接透传,file类型部分编码为data:<mime>;base64,<data>的 data URI(tool.ts);
  7. registry 捕获LLM.ToolFailure并转成错误结果,然后把输出交给ToolOutputStore.bound做通用收口(registry.ts)。

5.2 输出收口与受管存储

ToolOutputStore(tool-output-store.ts)定义了三个关键常量:

  • MAX_LINES = 2_000
  • MAX_BYTES = 50 * 1024
  • RETENTION = Duration.days(7)

bound会对超限输出做"首尾采样"式截断(preview保留头部一半、尾部一半行数),超过阈值的部分落盘到MANAGED_DIRECTORY = "tool-output"目录,返回的outputPaths指向受管文件。结算结果Settlement因此包含result、可选output与可选outputPaths三部分。

值得注意的是,editapply_patch两个工具还做了工具侧的自压缩:当序列化输出超过ToolOutputStore.MAX_BYTES时,compact会把files数组裁剪为仅保留additions/deletions/file/status字段、丢弃巨大的 diff 文本(edit.ts、apply-patch.ts),从而在不隐藏正常大小 diff 预览的前提下,保持持久化/SSE 工具记录的体积有界。

5.3 生产者捕获限制是另一回事

文档特别澄清:生产者(producer)的捕获限制与模型输出收口是分离的。以 Bash 为例:

  • 它自己维护AppProcess.maxOutputBytes(在 bash.ts 中即MAX_CAPTURE_BYTES = 1024 * 1024);
  • 它会在输出里如实报告 stdout/stderr 捕获丢失(输出被截断时追加[output capture truncated at the in-memory safety limit]说明);
  • 但它不做模型输出截断,也不返回受管outputPath——那是settle边界的事。

也就是说,Bash 的超时(默认DEFAULT_TIMEOUT_MS = 2 * 60 * 1_000,上限MAX_TIMEOUT_MS = 10 * 60 * 1_000)与内存捕获上限属于进程层约束,而模型看到的最终输出形状由 registry 的settle+ToolOutputStore.bound统一决定。edit/apply_patchcompactToolOutputStorepreview分别位于工具内与结算边界,两层共同保证输出记录可控。


六、当前缺口(Current Gaps):已知的演进方向

文档如实记录了三个尚未完成的设计点,对于理解代码现状至关重要:

  1. 插件启动(plugin boot)尚未重构为通过Tools.Service注册规范工具——文档明确要求不要作为叶子迁移的一部分顺带重设计它;
  2. MCP 与未来的 Session 级注册仍缺少显式的规范注册设计——builtins.ts的注释也印证了"动态 MCP 与插件工具稍后使用独立 scoped 规范注册"的规划;
  3. 公开 Session 结果形状目前暴露了受管的outputPaths——完整的存储封装需要未来的不透明受管输出引用设计(opaque managed-output reference)才能实现。

这三个缺口都指向同一个方向:当前"进程级应用注册 + Location 级内置注册 + registry 结算"的骨架已经稳定,而插件、MCP 与 Session 级扩展点正在向同一套规范工具模型收敛。


七、小结:架构设计的关键约束一览

关注点结论源码位置
工具表示唯一不透明的Tool.make值,应用/内置共用tool.ts
注册层级应用进程级、Location 级,Location 优先、最新胜出、可关闭回退application-tools.ts、registry.ts
公开注册 API内置走Tools.Service.register,应用走opencode.tools.registertools.ts、application-tools.ts
权限registry 不授权;定义过滤只影响目录可见性;edit/write/apply_patch共享editactionregistry.ts、edit.ts
输出边界settle是唯一执行与模型输出收口点;ToolOutputStore管理受管输出路径registry.ts、tool-output-store.ts
生产者限制如 Bash 的捕获上限仅报告丢失,不截断模型输出、不返回 outputPathbash.ts
已知缺口插件 boot、MCP/Session 级注册、Session 结果形状封装AGENTS.md

这套架构的核心价值在于:用"一个规范工具类型 + 两层作用域注册 + 唯一结算边界"把工具系统的可扩展性与可治理性统一起来——应用开发者只需opencode.tools.register即可接入,内置叶子共享同一执行与权限纪律,而输出形状则由settle统一收口,为插件、MCP 等后续扩展点保留了清晰的挂载位置。

【免费下载链接】kilocodeKilo is the all-in-one agentic engineering platform. Build, ship, and iterate faster with the most popular open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/ki/kilocode

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 21:48:35

Fischer算法原理与MATLAB实现:OFDMA自适应资源分配指南

简介&#xff1a;这份MATLAB程序包面向通信工程专业学生、研究人员及无线系统开发人员&#xff0c;针对OFDMA系统中的自适应资源分配问题&#xff0c;提供基于Fischer算法的完整实现方案。程序可根据信道状态信息动态完成子载波与功率分配&#xff0c;并在系统吞吐量和用户公平…

作者头像 李华
网站建设 2026/9/11 21:47:18

基于Spring Boot的库存管理系统:事务、并发扣减与防超卖实战

简介&#xff1a;一套基于Spring Boot的库存管理系统毕业设计资源包&#xff0c;面向计算机相关专业学生与开发者&#xff0c;旨在解决毕业设计或课程设计中从需求分析到前后端落地的完整实现问题。系统分为管理员与员工双角色&#xff0c;管理员端包含个人中心、管理员管理、基…

作者头像 李华
网站建设 2026/9/11 21:46:26

OpenCV+PyQt5实战:构建一套完整的人脸识别门禁系统

简介&#xff1a;面向需快速搭建人脸识别门禁系统的开发者&#xff0c;这是一份基于OpenCV与PyQt5的Python完整项目源码&#xff0c;解决特定人脸识别开门、管理员登录、人脸录入与训练等常见需求&#xff0c;适合计算机视觉入门及中级学习者。压缩包共2个文件&#xff0c;核心…

作者头像 李华
网站建设 2026/9/11 21:44:09

SRS 推流实战:使用 OBS 通过 RTMP 推送 HEVC(H.265)直播流

SRS 推流实战&#xff1a;使用 OBS 通过 RTMP 推送 HEVC&#xff08;H.265&#xff09;直播流 【免费下载链接】srs SRS is a simple, high-performance, AI-driven real-time media server supporting RTMP, WebRTC, HLS, HTTP-FLV, HTTP-TS, SRT, MPEG-DASH, and GB28181, wi…

作者头像 李华