构建在 pi-ai 之上的有状态 Agent,提供工具执行和事件流。
安装
npm install @earendil-works/pi-agent-core快速开始
import { Agent } from "@earendil-works/pi-agent-core";
import { getModel } from "@earendil-works/pi-ai";
const agent = new Agent({
initialState: {
systemPrompt: "You are a helpful assistant.",
model: getModel("anthropic", "claude-sonnet-4-20250514"),
},
});
agent.subscribe((event) => {
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
process.stdout.write(event.assistantMessageEvent.delta);
}
});
await agent.prompt("Hello!");核心概念
AgentMessage vs LLM Message
Agent 内部使用 AgentMessage,包含:
- 标准 LLM 消息(
user、assistant、toolResult) - 通过声明合并扩展的自定义 app 消息类型
LLM 只理解标准类型,convertToLlm 函数在每次 LLM 调用前负责过滤和转换。
消息处理管线
AgentMessage[] → transformContext() → AgentMessage[] → convertToLlm() → Message[] → LLM
(可选,裁剪/注入) (必须,过滤自定义类型)
事件流
无工具调用
prompt("Hello")
├─ agent_start
├─ turn_start
├─ message_start { message: userMessage }
├─ message_end { message: userMessage }
├─ message_start { message: assistantMessage }
├─ message_update { assistantMessageEvent: { type: "text_delta", delta: "..." } }
...
├─ message_end { message: assistantMessage }
├─ turn_end { message, toolResults: [] }
└─ agent_end { messages: [...] }
带工具调用
prompt("Read config.json")
├─ agent_start
├─ turn_start
├─ message_start/end { userMessage }
├─ message_start/end { assistantMessage with toolCall }
├─ tool_execution_start { toolCallId, toolName, args }
├─ tool_execution_update { partialResult } // 工具支持流式时
├─ tool_execution_end { toolCallId, result }
├─ message_start/end { toolResultMessage }
├─ turn_end { message, toolResults: [toolResult] }
│
├─ turn_start // 下一轮:LLM 处理工具结果
├─ message_start/end { assistantMessage }
├─ turn_end
└─ agent_end
工具执行模式
| 模式 | 行为 |
|---|---|
parallel(默认) | 预检串行,执行并发;tool_execution_end 按完成顺序发出,toolResult 消息和 turn_end.toolResults 按 LLM 源顺序排列 |
sequential | 工具逐个执行,保留历史兼容行为 |
若批次中任一工具设置了 executionMode: "sequential",整批退化为串行执行。
Hooks
beforeToolCall:在tool_execution_start之后、参数验证完毕后运行,可阻止执行(返回{ block: true, reason: "..." })afterToolCall:在工具执行结束后、tool_execution_end事件发出前运行,可修改结果或返回{ terminate: true }
终止行为
工具可以返回 terminate: true 跳过后续 LLM 调用。仅当批次中所有工具结果都设置 terminate: true 时,循环才提前终止;混合批次照常继续。
continue()
从现有 context 恢复,不添加新消息(用于错误后重试):
await agent.continue();要求:context 最后一条消息必须是 user 或 toolResult(不能是 assistant)。
完整事件类型
| 事件 | 说明 |
|---|---|
agent_start | Agent 开始处理 |
agent_end | 最终事件,携带所有消息 |
turn_start | 新轮次开始(一次 LLM 调用 + 工具执行) |
turn_end | 轮次结束,携带 assistant 消息和 toolResults |
message_start | 任意消息开始(user/assistant/toolResult) |
message_update | 仅 assistant,携带 assistantMessageEvent(delta) |
message_end | 消息完成 |
tool_execution_start | 工具开始,携带 toolCallId、toolName、args |
tool_execution_update | 工具流式进度 |
tool_execution_end | 工具完成,携带 result、isError |
Agent Options
const agent = new Agent({
initialState: {
systemPrompt: string,
model: Model<any>,
thinkingLevel: "off" | "minimal" | "low" | "medium" | "high" | "xhigh",
tools: AgentTool<any>[],
messages: AgentMessage[],
},
convertToLlm: (messages) => messages.filter(...),
transformContext: async (messages, signal) => pruneOldMessages(messages),
steeringMode: "one-at-a-time" | "all",
followUpMode: "one-at-a-time" | "all",
streamFn: streamProxy, // 自定义流函数(代理后端)
sessionId: "session-123", // Provider 缓存用
getApiKey: async (provider) => refreshToken(), // 动态 API Key(OAuth token 刷新)
toolExecution: "parallel" | "sequential",
beforeToolCall: async ({ toolCall, args, context }) => { ... },
afterToolCall: async ({ toolCall, result, isError, context }) => { ... },
thinkingBudgets: { minimal: 128, low: 512, medium: 1024, high: 2048 },
});shouldStopAfterTurn(低层 agentLoop)
低层调用者可通过 shouldStopAfterTurn 在当前轮次结束后优雅停止:
const stream = agentLoop(prompts, context, {
shouldStopAfterTurn: async ({ message, toolResults, context, newMessages }) => {
return shouldCompactBeforeNextTurn(context.messages);
},
});在 turn_end 发出后运行;返回 true 时发出 agent_end 并退出,不启动下一轮 LLM 调用。
相关页面:pi-ai | pi-coding-agent