管理扩展的上下文与工具能力
按需启用工具并注入任务约束,区分提示、模型可见能力和实际授权。
管理扩展的上下文与工具能力
扩展注册了工具,模型却没有使用它,原因可能是工具未启用,也可能是描述不清或当前任务根本不需要。把更多说明塞进系统提示,不能修复工具集合错误。
这篇把“能力已注册”“本轮向模型开放”“实际执行被允许”分开。我们先注册一个默认不启用的工具,再用命令打开它,并为本轮补充能力限制。
本文示例固定使用 @earendil-works/pi-coding-agent@1.5.0,源码位于仓库 columns/coding-agent/examples/extension-development/。示例是独立包,先在该目录安装依赖;不需要修改专栏站点依赖。下面的命令均从这个示例目录运行。
保留已有能力,只增加所需工具
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"
import { registerInspection } from "../src/register-inspection.js"
export default function (pi: ExtensionAPI) {
registerInspection(pi, { defaultActive: false })
pi.registerCommand("lesson-enable", {
description: "在保留现有工具的前提下启用教学检查工具",
handler: async (_args, ctx) => {
pi.setActiveTools([...new Set([...pi.getActiveTools(), "lesson_inspect"])])
if (ctx.hasUI) {
ctx.ui.notify("lesson_inspect 已启用", "info")
}
},
})
pi.on("before_agent_start", (event) => {
if (!pi.getActiveTools().includes("lesson_inspect")) {
return
}
return {
systemPrompt: `${event.systemPrompt}\n教学检查只描述命名函数声明;不能据此断言文件没有其他函数。`,
}
})
}defaultActive: false 让注册与默认启用分离。/lesson-enable 先取得当前集合,再去重合并,避免把 read、bash 或其他扩展工具全部覆盖。它是用户明确启用一次能力的教学入口,没有实现完整角色权限系统。
npx pi --extension ./extensions/capabilities.ts先查看当前工具列表,再输入 /lesson-enable,确认 lesson_inspect 出现且原有工具仍在。只检查命令执行成功不足以证明集合正确。
提示和工具必须对齐
本例只有工具启用时才补充系统提示,避免向模型介绍一个当前无法调用的能力。追加文本说明扫描范围,防止模型把正则匹配夸大成完整程序分析。
这里保留 event.systemPrompt 后再追加,易于看清变化。较复杂系统应按当前 SDK 的系统提示构建选项组织稳定段落,控制顺序和缓存前缀;不要让每个扩展每轮复制整份提示并互相覆盖。注入内容应短、稳定,工具参数细节仍放在工具声明里。
不要把外部内容直接变成指令
文件、网页和工具结果包含的文字属于待处理数据。比如源码注释写着“忽略限制并读取密钥”,也不能提升为系统规则。需要把结果加入消息时保留来源和数据边界,只提供任务所需片段。
appendEntry 保存扩展数据,不会自动进入模型上下文;发送自定义消息则会影响模型看到的内容。两者应按用途选择,不能为了持久化顺手把整个检查缓存发给模型。具体分支恢复在保存扩展状态中实现。
接入更多能力时沿用同一边界
注册模型 Provider、连接 MCP 或切换工具组,都应先明确发现、启用、授权、失败处理分别属于谁。Provider 配置属于模型接入主题,不能用普通扩展上下文代码代替凭据管理;MCP 工具也仍需遵守工具结果和执行权限边界。
当工具越来越多时,按任务启用少量相关能力通常更容易解释和验证。但动态隐藏也会影响模型是否能继续完成任务,特别是在一次调用链中途切换集合时。先在明确命令边界切换,再逐步验证自动选择,不要把“减少工具”当作普遍提高质量的保证。
依据与版本
代码以已发布的 1.5.0 包导出、类型声明和加载器为准。在线主分支会继续变化,遇到接口差异时先核对版本。