跳转至

14 · 路线 C:SDK 驱动自己的代码研究助手

14.1 为什么这个场景适合 SDK

代码研究需要文件工具、资源、会话、压缩与恢复。SDK 已经实现这些边界,你可以把产品层放在它上面,避免重复开发。

本例功能:读取一个指定源码目录,查入口和调用链,流式输出中文证据,保存会话。它关闭自动 extension / skill / prompt / theme 与项目 context 注入,明确选择只读内建工具。

这是工具选择层的限制,不是 OS 沙箱;read 仍可读取账户允许的其他路径。如果给不可信用户使用,应把 worker 放隔离环境。

14.2 配置目录与工作目录分开

PI_LAB_CWD → 被研究仓库
examples/.agent → 这个应用自己的模型/凭据配置
examples/.sessions → 持久会话记录

它不会自动借用个人 ~/.pi/agent/auth.json。Anthropic 的环境变量可用;若使用 models.json,应放入这个应用的 .agent。示例 .gitignore 不提交这些私密和历史目录。

这减少隐式依赖:你的应用和你每天使用的 Pi 配方可以不同。真正多用户时每个用户或服务账户需要自己的存储和权利边界。

14.3 资源加载的控制

const settingsManager = SettingsManager.inMemory({
  defaultProvider: "anthropic", defaultModel: "claude-sonnet-4-6",
}, { projectTrusted: false });

const resourceLoader = new DefaultResourceLoader({
  cwd, agentDir, settingsManager,
  noExtensions: true,
  noSkills: true,
  noPromptTemplates: true,
  noThemes: true,
  noContextFiles: true,
  systemPromptOverride: () => undefined,
  agentsFilesOverride: () => ({ agentsFiles: [] }),
  appendSystemPromptOverride: () => ["先读证据,给路径;不执行代码,不修改文件。"],
});
await resourceLoader.reload();

三个来源要分别处理:noContextFiles 关闭 AGENTS 等 context 文件,systemPromptOverride 覆盖发现到的 SYSTEM.md(返回 undefined 让 SDK 使用默认基础 prompt),appendSystemPromptOverride 只提供产品自己的追加规则。只清空 AGENTS 与 APPEND 仍可能导入项目 SYSTEM;SettingsManager.inMemory 本来默认项目受信,故本例显式 projectTrusted:false。

实际配置在 src/research-resources.ts,SDK 程序与隔离测试使用同一函数。测试真实创建带 AGENTS / SYSTEM / APPEND 与坏 extension 的目标目录,确认 loader 未导入,并断言发给 Faux Provider 的 messages 不含哨兵,产品研究指令仍存在。这个检查验证指令来源,不是文件系统沙箱。

noExtensions 关闭默认发现,但显式 additionalExtensionPaths 和 inline factories 仍可能加载;所以你的产品要控制整个 loader options 来源,不让用户任意传路径。

更彻底的做法是自己实现 ResourceLoader,所有 getters 都返回你管理的内容。官方 12-full-control.ts 提供配方。

14.4 创建和运行

const { session } = await createAgentSession({
  cwd, agentDir, settingsManager, resourceLoader,
  tools: ["read", "grep", "find", "ls"],
  sessionManager: SessionManager.create(cwd, sessionDir),
});

工具名为 allowlist。当前 SDK createAgentSession 不是旧文档里的 tools: [readTool, bashTool]。

真实运行:

cd /home/debian/codex/learn/pi/examples
export ANTHROPIC_API_KEY='YOUR_API_KEY'
PI_LAB_CWD=../pi-1.0 npm run sdk -- "说明 Agent.prompt 到工具 execute 的调用链"

这是由你提供真实 key 后的实验入口;本次验收没有使用你的未知凭据。无 key 的行为是可预期前置错误,而不是装作得到真实回答。

14.5 恢复已有会话

把创建 manager 换成 SessionManager.open(file);或采用 SDK 官方 sessions / runtime 示例。调用前检查文件是否属于当前用户,不接受任意跨目录 file。

不要将前端传来的 messages 数组塞给 session.agent.state.messages 作为正式恢复。它不改变 authoritative persisted context,而且前端可能伪造 system 或 toolResult。

14.6 流式输出和清理

subscribe 在 prompt 前安装,text_delta 追加界面。完成后检查最终状态。定时器到期向 session.abort 发出取消请求,finally dispose;合作取消不保证忽略 signal 的工作立刻停止,硬 deadline 需外部进程监督。

订阅回调若启动异步写数据库,应自己管理写入完成;SDK public listener 不承诺 await 任意返回 Promise。用宿主任务持久化和正式 boundary 处理必须一致的业务提交。

14.7 给 SDK 加自己的工具

有两个入口:options.customTools,或 inline extension 的 registerTool。后者还可以注册 hooks / commands,通常适合完整工作流。

extensionFactories: [
  (pi) => {
    pi.registerTool({
      name: "lookup_ticket",
      label: "查询工单",
      description: "按工单编号查询已授权项目的状态",
      parameters: Type.Object({ ticket: Type.String() }),
      async execute(_id, { ticket }, signal) {
        signal?.throwIfAborted();
        // 在这里调用你自己的、已认证的业务服务。
        return { content: [{ type: "text", text: "这里必须替换为真实查询结果" }], details: { ticket } };
      },
    });
  },
]

这是说明注册结构的模板,不是一个已经完成的真实工单集成。完整运行实验使用实际内存笔记工具;接真实业务时补齐客户端、异常、授权与测试。

14.8 新增自己的知识上下文

通过 loader 注入自己的 skills/context 或工具检索。关键选择:哪些常驻,哪些按需;哪些可信指令,哪些是不可信资料。

上下文窗口有限,因此不要每轮注入所有文档。skill description 负责「知道有这个能力」,工具检索负责「需要时获取证据」,持久业务状态负责「授权和进度不丢失」。

14.9 长驻服务的会话所有权

为每个 session 建 worker 或 queue。普通请求只在 idle 时 prompt;工作中必须显式 steer / followUp。用户取消应有已认证的所有权校验,不能凭知道 session ID 就 abort 别人的任务。

flowchart LR B[浏览器] --> API[你的 authenticated API] API --> Q[session queue / ownership] Q --> S[SDK Session worker] S --> P[模型服务] S --> T[已授权工具] S --> E[事件推送] E --> B

本例只是 CLI SDK 程序。第 15 章提供产品化设计;课程网站不执行这个后台流程。

完整代码:实验源码。官方参考:SDK、资源示例。