智能体工程:Pi SDK
在上一篇文章中,我用 Pi 的 CLI 加上一个扩展文件和一个系统提示词文件构建了一个新闻阅读器。在 .pi/ 目录里放两个文件,用 pi -p 运行。相比 Claude Code 版本,它已经显得很精简,但它仍然依赖 Pi 的运行时来发现和加载扩展。
Pi 还有一个编程式 SDK,可以让你把智能体嵌入到自己的脚本中,就像 Agent SDK 让我把 Claude Code 版本折叠成单个 Python 文件一样。
1、内联工具、内联提示词
工具代码与 CLI 版本相同:web_fetch 用 turndown 抓取页面,save_news_item 把 JSON 写入 data/。区别在于,它们不再放在 .pi/extensions/news-tools.ts 里,而是用 defineTool() 内联定义。下面是一个例子:
import {
createAgentSession, DefaultResourceLoader, defineTool,
getAgentDir, SessionManager,
} from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";
import TurndownService from "turndown";
const webFetchTool = defineTool({
name: "web_fetch",
label: "Fetch URL",
description: `Fetch a web page and return content as markdown. Allowed domains: ${ALLOWED_DOMAINS.join(", ")}`,
parameters: Type.Object({
url: Type.String({ description: "URL to fetch" }),
}),
async execute(_id, params) {
const hostname = new URL(params.url).hostname;
if (!ALLOWED_DOMAINS.includes(hostname)) {
throw new Error(`Domain not allowed: ${hostname}`);
}
const resp = await fetch(params.url);
const html = await resp.text();
return { content: [{ type: "text", text: turndown.turndown(html) }], details: {} };
},
});
// save_news_item defined the same way (full source on GitHub)
SDK 的编程模型是基于事件的。你创建一个会话,发送提示词,然后在智能体工作的过程中订阅事件。会话设置如下:
// Replace Pi's default system prompt and suppress global skill discovery
const resourceLoader = new DefaultResourceLoader({
cwd: process.cwd(),
agentDir: getAgentDir(),
systemPromptOverride: () => SYSTEM_PROMPT,
appendSystemPromptOverride: () => [],
skillsOverride: () => ({ skills: [], diagnostics: [] }),
});
await resourceLoader.reload();
// Create a session with only our two custom tools
const { session } = await createAgentSession({
resourceLoader,
customTools: [webFetchTool, saveNewsItemTool],
tools: ["web_fetch", "save_news_item"],
sessionManager: SessionManager.inMemory(),
});
// Stream agent output to stdout
session.subscribe((event) => {
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
process.stdout.write(event.assistantMessageEvent.delta);
}
});
// Run the agent
await session.prompt("Go");
session.dispose();
结果是一个完全自包含的设置:没有文件系统发现,也没有会话持久化。认证默认使用 ~/.pi/agent/auth.json(pi /login 存储的任何内容),但你可以传入自定义的 authStorage 来实现完全独立的设置。
session.subscribe() 是你观察智能体所做一切的方式。会话会为文本输出、工具调用、工具结果、思考和压缩(compaction)发出事件。这里我只关心把文本流式输出到 stdout。SDK 示例展示了更多模式。
用 npx tsx scrape.ts 运行它。
2、比较两个 SDK
两个 SDK 最终形状相似:
| Claude Agent SDK | Pi SDK | |
|---|---|---|
| 语言 | Python | TypeScript |
| 入口点 | query() 异步生成器 | createAgentSession() + session.prompt() |
| 工具定义 | @tool 装饰器 + Pydantic | defineTool() + TypeBox |
| 系统提示词 | prompt 参数 | systemPromptOverride 回调 |
| 工具限制 | allowed_tools 列表 | tools 白名单 |
| 会话 | 默认无状态 | SessionManager.inMemory() |
| 子智能体 | AgentDefinition 对象 | 不可用 |
| 输出 | 消息的异步迭代器 | 事件订阅 |
Claude Agent SDK 有更多内置结构。Pi 的 SDK 给你一个带工具和提示词的会话,其余部分由你自己构建。
3、什么时候用 SDK 而不是 CLI
CLI 适合一次性自动化:运行一条命令、拿到结果、退出。当你想把 Pi 嵌入到更大的应用中,或者在一个会话里链式发起多个提示词而无需重新初始化时,SDK 才更有意义。
对于这个新闻阅读器来说,CLI 版本更简单。SDK 版本则可以作为参考:当你需要把 Pi 当作库而不是命令来使用时。
原文链接:Agent engineering: Pi SDK
汇智网翻译整理,转载请标明出处