在同一进程里用 pi
RPC 靠子进程 + 协议。如果你本来就在 Node.js / TypeScript 里,更直接的是用 SDK——它让你在同一个进程里,用类型安全的方式直接拿到 agent 的能力。
npm install @earendil-works/pi-coding-agent
SDK 已在主包里,不需要额外安装。
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"],
});
内置工具名:read、bash、edit、write、grep、find、ls;默认内置是 read、bash、edit、write。这对 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 真正用起来:
- 在你的 Node 项目里
npm install @earendil-works/pi-coding-agent - 写一个脚本:
createAgentSession只开read/grep等只读工具,发一条处理 markdown 的 prompt - 用
subscribe打印 assistant 的文本增量,验证它完成一次问答
怎么判断做对了?——脚本能起会话、
prompt()返回、流式文本正常打印,且模型没有对文件做写操作(只读生效)。
卡住了怎么办? 会话起不来?先确认装好了 SDK、ModelRuntime.create() 没抛错。没有认证?参照模块 01 配好环境变量或 auth.json。
常见坑:替换会话后忘了重新订阅
SDK 有个容易忽略的细节:事件订阅挂在特定的 AgentSession 上。 一旦你用 newSession() / switchSession() / fork() 替换了当前会话,旧的 session 引用就失效了,事件不会再到达你原来的订阅。
正确做法是替换后重新取 runtime.session,重新 subscribe()。很多人写了一次 subscribe 就以为一直有效,结果切会话后事件「凭空消失」。记住:订阅跟着会话走,不是跟着进程走。
小结
- SDK 让你在 Node 进程里类型安全地用 pi 的能力
createAgentSession()+subscribe()+prompt()是最小闭环ResourceLoader定制扩展/技能/系统提示词- 只读模式限定工具,适合处理文档这类低风险任务
- 换会话后要重新订阅事件
下一节,进入模块 05:安全与生产交付。