oh-my-pi Coding Agent SDK 编程接入指南:基于 createAgentSession 的会话编排、工具与事件系统
【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi
导读
本文面向希望以编程方式(而非命令行)驱动 oh-my-pi Coding Agent 的开发者,系统讲解@oh-my-pi/pi-coding-agent包的核心入口createAgentSession():从最简会话、模型与思考等级选择、系统提示词定制,到技能(Skills)、工具(Tools)、扩展(Hooks/Extensions)、上下文文件(AGENTS.md)、提示词模板(Prompt Templates)的发现与替换,再到 API Key/OAuth 凭据解析、设置覆盖、会话持久化与事件流订阅。文中的全部示例均来自仓库packages/coding-agent/examples/sdk/目录,可直接运行验证;读完本文你将掌握用十余行代码启动一个完整的 Coding Agent 会话、并对它的每一步行为进行精细控制的实战能力。
一、SDK 是什么:将 Coding Agent 变成可编程组件
oh-my-pi 的 Coding Agent(即omp-coding-agent)既可以作为交互式终端程序使用,也可以通过 examples/sdk/README.md 中描述的方式,以 SDK 形式嵌入任意 Node.js/TypeScript 应用。核心入口是一个异步工厂函数createAgentSession(),它负责完成整套“装配”工作:
- 凭据存储(
AuthStorage)与模型注册表(ModelRegistry)的发现与解析; - 从当前工作目录与
~/.omp/agent配置目录发现技能、扩展、工具、AGENTS.md 上下文文件与提示词模板; - 组装系统提示词(
buildSystemPrompt); - 创建会话管理器(
SessionManager)实现持久化; - 返回一个可订阅事件、可执行
prompt()的会话对象。
一次典型的 SDK 调用只需要一个不带任何参数的createAgentSession()——它会自动完成全部默认装配(见 01-minimal.ts):
import { createAgentSession } from "@oh-my-pi/pi-coding-agent"; const { session } = await createAgentSession(); session.subscribe(event => { if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") { process.stdout.write(event.assistantMessageEvent.delta); } }); await session.prompt("What files are in the current directory?"); session.state.messages.forEach(msg => { console.log(msg); });默认情况下,SDK 会从cwd与~/.omp/agent自动发现技能、扩展、工具与上下文文件;模型则按设置选择,或取第一个可用模型。这是理解整个 SDK 的最小模型:一个会话 = 模型 + 凭据 + 工具 + 提示上下文 + 持久化,而createAgentSession()的每个可选参数都可以覆盖其中任意一环。
二、示例清单与运行方式
examples/sdk/目录下提供了一组渐进式的可运行示例,每个示例对应一个独立的配置主题(README 表格中的示例编号与文件名一一对应,其中部分示例已按最新 API 演进为新的命名,例如06-extensions.ts对应 Hooks、08-prompt-templates.ts对应文件型 Slash 命令):
| 文件 | 主题 |
|---|---|
01-minimal.ts | 全默认的最简用法 |
02-custom-model.ts | 选择模型与思考等级 |
03-custom-prompt.ts | 替换或修改系统提示词 |
04-skills.ts | 发现、过滤或替换技能 |
05-tools.ts | 内置工具与自定义工具 |
06-hooks.ts/06-extensions.ts | 日志记录、拦截阻断、结果改写 |
07-context-files.ts | AGENTS.md 上下文文件 |
08-slash-commands.ts/08-prompt-templates.ts | 文件型斜杠命令(提示词模板) |
09-api-keys-and-oauth.ts | API Key 解析与 OAuth 配置 |
10-settings.ts | 覆盖压缩(compaction)、重试、终端设置 |
11-sessions.ts | 内存、持久化、继续、列出会话 |
12-full-control.ts | 完全接管,禁用一切发现 |
12-redis-sessions.ts/13-sql-sessions.ts | Redis / SQL 会话后端 |
运行任一示例只需在仓库根目录执行(示例入口从packages/coding-agent包内加载依赖):
cd packages/coding-agent npx tsx examples/sdk/01-minimal.ts将01-minimal.ts替换为其他示例文件名即可逐个验证;由于使用tsx直接执行 TypeScript,无需预先编译。
三、Options:createAgentSession 全量配置速查
README 给出了createAgentSession()的完整选项表,这是 SDK 的“控制面板”,值得逐项理解(带默认值的项意味着你不传参也能工作):
| 选项 | 默认值 | 说明 |
|---|---|---|
authStorage | discoverAuthStorage() | 凭据存储(API Key 保存位置) |
modelRegistry | discoverModels(authStorage) | 模型注册表 |
cwd | process.cwd() | 工作目录 |
agentDir | ~/.omp/agent | 配置目录 |
model | 来自设置 / 第一个可用模型 | 使用的模型 |
thinkingLevel | 来自设置 /"off" | off、low、medium、high |
systemPrompt | 自动发现组装 | 字符串,或(default) => modified函数 |
toolNames | 全部内置工具 | 过滤要包含的工具 |
customTools | 自动发现 | 替换自动发现的自定义工具 |
additionalCustomToolPaths | [] | 与自动发现结果合并 |
hooks | 自动发现 | 替换自动发现的扩展 |
additionalHookPaths | [] | 与自动发现结果合并 |
skills | 自动发现 | 用于提示词的技能 |
contextFiles | 自动发现 | AGENTS.md 文件 |
slashCommands | 自动发现 | 文件型命令 |
sessionManager | SessionManager.create(cwd) | 持久化策略 |
settingsManager | 来自 agentDir | 设置覆盖 |
注意表中“替换”与“合并”两类语义的区别:customTools、hooks、skills、contextFiles、slashCommands传参后即取代自动发现的结果(传空数组可完全禁用对应能力);而additionalCustomToolPaths、additionalHookPaths则是把额外路径追加到自动发现结果之上。
四、模型与凭据:AuthStorage + ModelRegistry 的装配
4.1 从默认发现到显式装配
discoverAuthStorage()默认读取~/.omp/agent/agent.db中的凭据;discoverModels(authStorage)在此基础上加载内置模型,并合并~/.omp/agent/models.json中的自定义模型。显式装配可以让应用拥有完全独立的凭据与模型空间(见 09-api-keys-and-oauth.ts):
const authStorage = await discoverAuthStorage(); const modelRegistry = await discoverModels(authStorage); // 自定义存储位置:完全脱离 ~/.omp/agent const customAuthStorage = await AuthStorage.create("/tmp/my-app/agent.db"); const customModelRegistry = await ModelRegistry.create(customAuthStorage, "/tmp/my-app/models.json"); // 不传 models.json:仅内置模型 const simpleRegistry = await ModelRegistry.create(authStorage);4.2 运行时 API Key 覆盖
AuthStorage.setRuntimeApiKey(provider, key)可以注入临时凭据,它只驻留内存、不落盘,适合在运行时从环境变量或密钥管理服务取 Key 的场景:
authStorage.setRuntimeApiKey("anthropic", "sk-my-temp-key");discoverAuthStorage()的完整调用链(包含 OAuth 配置解析)可从 09-api-keys-and-oauth.ts 与 Quick Reference 片段 中的AuthStorage.create/setRuntimeApiKey用法印证。
4.3 选择模型与思考等级
模型选择有三种途径(见 02-custom-model.ts):
import { ThinkingLevel } from "@oh-my-pi/pi-agent-core"; import { getModel } from "@oh-my-pi/pi-ai"; // 方式一:按 provider/id 直接取内置模型 const opus = getModel("anthropic", "claude-opus-4-5"); // 方式二:通过注册表查找(含 models.json 中的自定义模型) const customModel = modelRegistry.find("my-provider", "my-model"); // 方式三:取当前拥有有效 API Key 的可用模型 const available = modelRegistry.getAvailable();选定模型后,通过thinkingLevel控制推理深度,取值依次为off、low、medium、high(对应枚举ThinkingLevel.Off/Low/Medium/High):
const { session } = await createAgentSession({ model: available[0], thinkingLevel: ThinkingLevel.Medium, authStorage, modelRegistry, });getAvailable()会结合AuthStorage中已有的 Key 过滤出真正可用的模型,避免启动后才发现凭据缺失。
五、系统提示词:替换与函数式改写
systemPrompt选项支持两种形态(见 03-custom-prompt.ts)。
完全替换——传入字符串数组,覆盖自动发现组装的默认提示词:
const { session } = await createAgentSession({ systemPrompt: [ `You are a helpful assistant that speaks like a pirate. Always end responses with "Arrr!"`, ], sessionManager: SessionManager.inMemory(), });函数式改写——接收默认提示词,返回修改后的版本,适合在保留内置指令体系(工具说明、技能、上下文文件)的基础上追加约束:
const { session } = await createAgentSession({ systemPrompt: defaultPrompt => [ ...defaultPrompt, `## Additional Instructions - Always be concise - Use bullet points when listing things`, ], sessionManager: SessionManager.inMemory(), });第二种方式与 README Quick Reference 中的(defaultPrompt) => defaultPrompt + "\n\nBe concise."一致,是生产环境最常用的姿势:默认提示词由buildSystemPrompt(结合技能、上下文文件等)生成,改写函数相当于在既有体系上做增量。
六、技能(Skills):发现、过滤、内联定义
技能是注入系统提示词的专业指令片段。SDK 通过discoverSkills()从cwd/.omp/skills、~/.omp/agent/skills等位置发现技能,并支持三种使用方式(见 04-skills.ts):
import { createAgentSession, discoverSkills, SessionManager, type Skill } from "@oh-my-pi/pi-coding-agent"; // 1. 发现全部技能 const { skills: allSkills } = await discoverSkills(); // 2. 按名称过滤 const filteredSkills = allSkills.filter(s => s.name.includes("browser") || s.name.includes("search")); // 3. 内联定义自定义技能 const customSkill: Skill = { name: "my-skill", description: "Custom project instructions", filePath: "/virtual/SKILL.md", baseDir: "/virtual", source: "custom", }; await createAgentSession({ skills: [...filteredSkills, customSkill], sessionManager: SessionManager.inMemory(), });skills: []可完全禁用技能;discoverSkills(cwd, undefined, { ignoredSkills: ["browser-tools"], includeSkills: ["brave-*"] })支持按 glob 模式过滤——ignoredSkills排除、includeSkills白名单(空表示全部),两者可配合设置项实现“发现但按项目裁剪”。
七、工具(Tools):内置工具、白名单与自定义工具
工具是 Agent 执行操作的通道。README Quick Reference 展示了只读工具白名单的用法:
const { session } = await createAgentSession({ toolNames: ["read", "search", "find"], // 只开放三个只读工具 authStorage, modelRegistry, });toolNames接受内置工具名列表(如read、search、bash等),用于收紧 Agent 的能力边界;createTools()则负责把工具会话中的工具清单实例化为可执行对象,BUILTIN_TOOLS与HIDDEN_TOOLS常量定义了内置工具集合与隐藏工具集合。
7.1 自定义工具与完全接管
customTools选项替换自动发现的工具,可搭配toolNames只开放需要的工具:
const { session } = await createAgentSession({ toolNames: ["read", "bash"], customTools: [{ tool: myTool }], // 替换发现结果,只保留自定义工具 });若需保留自动发现并追加额外工具路径,使用additionalCustomToolPaths: ["/extra/tools"]。
八、扩展(Hooks/Extensions):日志、拦截与安全护栏
Hooks 用于拦截 Agent 生命周期事件,实现日志记录、阻断执行或改写结果。注意 API 演进:Hooks 在新版 API 中更名为 Extensions,示例 06-hooks.ts 对此有明确注释,工厂函数类型为ExtensionFactory,回调注册在api.on(...)上。
import { createAgentSession, type ExtensionFactory, SessionManager } from "@oh-my-pi/pi-coding-agent"; // 日志扩展 const loggingHook: ExtensionFactory = api => { api.on("agent_start", async () => { console.log("[Hook] Agent starting"); }); api.on("tool_call", async event => { console.log(`[Hook] Tool: ${event.toolName}`); return undefined; // 不阻断 }); api.on("agent_end", async event => { console.log(`[Hook] Done, ${event.messages.length} messages`); }); }; // 安全拦截扩展:返回 { block: true, reason } 阻断工具调用 const safetyHook: ExtensionFactory = api => { api.on("tool_call", async event => { if (event.toolName === "bash") { const cmd = (event.input as { command?: string }).command ?? ""; if (cmd.includes("rm -rf")) { return { block: true, reason: "Dangerous command blocked" }; } } return undefined; }); }; const { session } = await createAgentSession({ extensions: [loggingHook, safetyHook], sessionManager: SessionManager.inMemory(), });关键点:
tool_call回调返回undefined表示放行;返回{ block: true, reason }则阻断该次调用,reason会反馈给模型;extensions: []禁用全部扩展;- 与发现结果合并使用:
const discovered = await discoverExtensions();后传[...discovered.extensions.map(e => e.factory), myHook]; - 追加路径而不替换发现结果:
additionalExtensionPaths: ["/extra/extensions"]。
README 的 Options 表中hooks/additionalHookPaths即对应上述扩展语义。
九、上下文文件(AGENTS.md)与提示词模板
9.1 AGENTS.md 上下文文件
discoverContextFiles()会从cwd向上逐级发现 AGENTS.md,将其内容并入系统提示词(见 07-context-files.ts):
const discovered = discoverContextFiles(); for (const file of discovered) { console.log(` - ${file.path} (${file.content.length} chars)`); } await createAgentSession({ contextFiles: [ ...discovered, { path: "/virtual/AGENTS.md", content: `# Project Guidelines ## Code Style - Use TypeScript strict mode - No any types - Prefer const over let`, }, ], sessionManager: SessionManager.inMemory(), });contextFiles: []可关闭上下文文件注入。
9.2 文件型斜杠命令 = 提示词模板
文件型斜杠命令(/commandname触发)在新 API 中更名为Prompt Templates,通过discoverPromptTemplates()从cwd/.pi/prompts/与~/.pi/agent/prompts/发现(见 08-slash-commands.ts):
const discovered = await discoverPromptTemplates(); for (const cmd of discovered) { console.log(` /${cmd.name}: ${cmd.description}`); } const deployCommand: PromptTemplate = { name: "deploy", description: "Deploy the application", source: "(custom)", content: `# Deploy Instructions 1. Build: npm run build 2. Test: npm test 3. Deploy: npm run deploy`, }; await createAgentSession({ promptTemplates: [...discovered, deployCommand], sessionManager: SessionManager.inMemory(), });promptTemplates: []禁用全部模板;- 需要注意:传统文件型 Markdown 命令走
promptTemplates,而TypeScript 编写的命令则通过discoverCustomTSCommands()加载,两者并存于createAgentSession的自动装配中。
十、会话(Session)管理:内存、持久化、继续与列表
SessionManager控制会话的存储策略(见 11-sessions.ts):
// 内存会话:不落盘,适合临时/测试 const { session: inMemory } = await createAgentSession({ sessionManager: SessionManager.inMemory(), }); // 新建持久化会话:按 cwd 编码目录存放 const { session: newSession } = await createAgentSession({ sessionManager: SessionManager.create(process.cwd()), }); console.log("New session file:", newSession.sessionFile); // 继续最近一次会话(无则新建) const { session: continued, modelFallbackMessage } = await createAgentSession({ sessionManager: await SessionManager.continueRecent(process.cwd()), }); // 列出并打开指定会话 const sessions = await SessionManager.list(process.cwd()); const { session: opened } = await createAgentSession({ sessionManager: await SessionManager.open(sessions[0].path), });SessionManager.create(cwd, customDir)支持自定义会话目录(第二参数,省略时按 cwd 编码);list(cwd, customDir)与continueRecent(cwd, customDir)同样接受该参数。仓库中还提供了12-redis-sessions.ts与13-sql-sessions.ts两个示例,展示把会话后端替换为 Redis / SQL 的扩展方式——说明SessionManager是接口化的,可对接自定义持久化实现。
十一、事件系统:订阅 Agent 的每一步
session.subscribe()提供流式事件,README 给出了完整的事件类型骨架:
session.subscribe((event) => { switch (event.type) { case "message_update": if (event.assistantMessageEvent.type === "text_delta") { process.stdout.write(event.assistantMessageEvent.delta); } break; case "tool_execution_start": console.log(`Tool: ${event.toolName}`); break; case "tool_execution_end": console.log(`Result: ${event.result}`); break; case "agent_end": console.log("Done"); break; } });message_update:模型消息增量更新,配合assistantMessageEvent.type === "text_delta"可逐 token 输出流式文本(这是所有示例实现“打字机”效果的统一手法);tool_execution_start/tool_execution_end:工具调用生命周期,可用于进度展示或审计;agent_end:一轮 Agent 循环结束。
事件的订阅时机在session.prompt()之前完成注册即可捕获全过程。
十二、AST 编辑预览工作流:xd:// 虚拟设备
README 还特别说明了ast_edit工具的新行为:它现在总是返回预览(preview)而非直接落盘。要最终确认修改,需要用write工具向对应的虚拟设备写入纯文本:
xd://resolve→ 应用挂起的预览;body 为原因文本;xd://reject→ 丢弃挂起的预览;body 为原因文本。
createAgentSession()/createTools()会在存在可延迟工具(如ast_edit)时自动包含write,因此虚拟设备始终可达:
const tools = await createTools(toolSession, ["ast_edit"]); // write 被自动包含 const writeTool = tools.find(t => t.name === "write")!; await writeTool.execute("call-1", { path: "xd://resolve", content: "Preview matches expected replacements", });这意味着通过 SDK 接入时,Agent 对代码的修改天然处于“先预览、后裁决”的受控流程中,宿主应用可以在这两步之间插入人工审核或自动化校验。
十三、完全接管模式:Full Control
当需要彻底摆脱自动发现、把 Agent 行为完全握在自己手中时,README Quick Reference 展示了 Full Control 的完整形态:
const customAuth = await AuthStorage.create("/my/app/agent.db"); customAuth.setRuntimeApiKey("anthropic", Bun.env.MY_KEY!); const customRegistry = new ModelRegistry(customAuth); const { session } = await createAgentSession({ model, authStorage: customAuth, // 自定义凭据 modelRegistry: customRegistry, // 自定义模型注册表 systemPrompt: ["You are helpful."], // 完全替换提示词 toolNames: ["read", "bash"], // 工具白名单 customTools: [{ tool: myTool }], // 替换发现的工具 hooks: [{ factory: myHook }], // 替换发现的扩展 skills: [], // 禁用技能 contextFiles: [], // 禁用上下文文件 slashCommands: [], // 禁用文件命令 sessionManager: SessionManager.inMemory(), });对应示例12-full-control.ts即以此为蓝本。该模式下所有“自动发现”的输入源都被显式指定或清空,应用对会话的每一个组成部分都拥有完全决定权——适合构建需要严格管控的嵌入式 Agent 场景(如受限的执行环境、白名单工具集、固定提示词模板)。
十四、小结:从示例到生产接入
回顾整个 SDK 的使用脉络,可以归纳出三层控制粒度:
- 零配置起步:
createAgentSession()全默认调用即可获得功能完整的 Agent(01-minimal.ts); - 逐项覆盖:通过 Options 表中的 16 个选项按需替换模型、提示词、工具、技能、扩展、上下文、会话与设置(
02~11系列示例); - 完全接管:禁用一切发现,显式装配全部组件(
12-full-control.ts)。
无论哪种粒度,session.subscribe()事件流与session.prompt()调用接口保持一致,这保证了上层代码可以在配置演进过程中保持稳定。对于有更进一步需求的场景,仓库中的12-redis-sessions.ts、13-sql-sessions.ts还展示了会话存储可插拔的扩展方向;所有示例均可直接通过npx tsx examples/sdk/<file>.ts在packages/coding-agent目录下运行验证。
【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考