第 04 模块 · 3 节

SDK 嵌入

《Pi 生产级工程》04 程序化使用 · 本节时长 34 分钟

在同一进程里用 pi

RPC 靠子进程 + 协议。如果你本来就在 Node.js / TypeScript 里,更直接的是用 SDK——它让你在同一个进程里,用类型安全的方式直接拿到 agent 的能力。

npm install @earendil-works/pi-coding-agent

SDK 已在主包里,不需要额外安装。

贯穿项目这是 md-tools「程序化」的最强形态:md-tools 的分发对象,不一定得是终端用户——它可以是一个 Node 应用,直接把 markdown 处理逻辑 import 进去。**SDK 让你能 createAgentSession 起一个只读会话,调用 session.prompt() 处理文档,全程类型安全。** 这一节学会基本会话,md-tools 就能被嵌进任何 Node 工具链。

极简示例:起一个会话并提问

最少的代码就能让 agent 跑起来:

import { createAgentSession, ModelRuntime, SessionManager } from "@earendil-works/pi-coding-agent";

const modelRuntime = await ModelRuntime.create();
const { session } = await createAgentSession({
  sessionManager: SessionManager.inMemory(),
  modelRuntime,
});

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?");

核心思路:createAgentSession() 造一个会话,subscribe() 听事件,prompt() 发提示词。


核心概念:AgentSession

AgentSession 管理 agent 的生命周期、消息历史、模型状态、压缩和事件流。几个常用方法:

// 发提示词并等完成
await session.prompt("What files are here?");

// 流式期间:必须指定怎么排队
await session.prompt("Stop and do this instead", { streamingBehavior: "steer" });
await session.prompt("After you're done, also check X", { streamingBehavior: "followUp" });

// 订阅事件(返回退订函数)
const unsubscribe = session.subscribe((event) => { /* ... */ });

// 模型控制
await session.setModel(model);
session.setThinkingLevel("medium");

// 压缩
await session.compact();

流式期间调用 prompt() 而不指定 streamingBehavior,会抛错。要排队就用 steer() / followUp()


用资源加载器定制会话

SDK 用 ResourceLoader 提供扩展、技能、提示词模板、主题和上下文文件。不传就用 DefaultResourceLoader 做标准发现。想改系统提示词:

import { createAgentSession, DefaultResourceLoader } from "@earendil-works/pi-coding-agent";

const loader = new DefaultResourceLoader({
  systemPromptOverride: () => "You are a helpful assistant.",
});
await loader.reload();

const { session } = await createAgentSession({ resourceLoader: loader });

只读模式:生产环境的安全姿势

想让它「只读」,别让它改文件?限定工具集:

import { createAgentSession } from "@earendil-works/pi-coding-agent";

// 只读模式
const { session } = await createAgentSession({
  tools: ["read", "grep", "find", "ls"],
});

// 只挑特定工具
const { session } = await createAgentSession({
  tools: ["read", "bash", "grep"],
});

内置工具名:readbasheditwritegrepfindls;默认内置是 readbasheditwrite。这对 md-tools 很关键——处理文档时你可以给模型一把「只读」剪刀,而不是全权限。


自定义工具:defineTool

想给 agent 加你自己的工具?用 defineTool

import { Type } from "typebox";
import { createAgentSession, defineTool } from "@earendil-works/pi-coding-agent";

const myTool = defineTool({
  name: "my_tool",
  label: "My Tool",
  description: "Does something useful",
  parameters: Type.Object({
    input: Type.String({ description: "Input value" }),
  }),
  execute: async (_toolCallId, params) => ({
    content: [{ type: "text", text: `Result: ${params.input}` }],
    details: {},
  }),
});

const { session } = await createAgentSession({
  customTools: [myTool],
});

加了自定义工具后,记得在 tools 里把它点名启用,比如 tools: ["read", "bash", "my_tool"]


SDK vs RPC:怎么选

两种嵌入方式各有场景:

SDK RPC
要类型安全 否(走协议)
同一进程 否(子进程)
直接拿 agent 状态 需查命令
程序化定制工具/扩展 受限
跨语言
进程隔离

一句话:你是 Node 就优先 SDK;要跨语言、要隔离,就用 RPC。


落地练习:起一个只读 SDK 会话

把 SDK 真正用起来:

  1. 在你的 Node 项目里 npm install @earendil-works/pi-coding-agent
  2. 写一个脚本:createAgentSession 只开 read/grep 等只读工具,发一条处理 markdown 的 prompt
  3. subscribe 打印 assistant 的文本增量,验证它完成一次问答

怎么判断做对了?——脚本能起会话、prompt() 返回、流式文本正常打印,且模型没有对文件做写操作(只读生效)。

卡住了怎么办? 会话起不来?先确认装好了 SDK、ModelRuntime.create() 没抛错。没有认证?参照模块 01 配好环境变量或 auth.json。


常见坑:替换会话后忘了重新订阅

SDK 有个容易忽略的细节:事件订阅挂在特定的 AgentSession 上。 一旦你用 newSession() / switchSession() / fork() 替换了当前会话,旧的 session 引用就失效了,事件不会再到达你原来的订阅。

正确做法是替换后重新取 runtime.session,重新 subscribe()。很多人写了一次 subscribe 就以为一直有效,结果切会话后事件「凭空消失」。记住:订阅跟着会话走,不是跟着进程走。


小结

  1. SDK 让你在 Node 进程里类型安全地用 pi 的能力
  2. createAgentSession() + subscribe() + prompt() 是最小闭环
  3. ResourceLoader 定制扩展/技能/系统提示词
  4. 只读模式限定工具,适合处理文档这类低风险任务
  5. 换会话后要重新订阅事件

下一节,进入模块 05:安全与生产交付。