# @averyyy/pi-agent-core 中文文档

规范地址：https://npmcn.com/package/%40averyyy/pi-agent-core/

版本：1\.5\.0-piclient\.4
npm 元数据更新时间：2026-11-05T01:10:23.639Z
中文译文更新时间：2026-11-07T07:59:18.324Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40averyyy/pi-agent-core
原文对照：https://npmcn.com/package/%40averyyy/pi-agent-core/?lang=original
项目主页：https://github.com/Averyyy/pi-client#readme
源码仓库：https://github.com/Averyyy/pi-client

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：4 MB，474 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=22\.19\.0
- 运行依赖：8 个
- 首次发布：2026-07-02T00:26:06.965Z
- 最新版本发布：2026-11-05T01:10:23.300Z
- 近 30 天下载量：7821（2026-10-07 至 2026-11-05，npm 官方下载统计）
- 已发布版本数：95（近 12 个月 95 个）

## 中文简介

具有运输抽象、状态管理和附件支持的通用剂

## 安装

```sh
npm install @averyyy/pi-agent-core
```

也可以使用：`pnpm add @averyyy/pi-agent-core`、`yarn add @averyyy/pi-agent-core`、`bun add @averyyy/pi-agent-core`

## README

# @ earendil- works/ pi- agent- core

具有工具执行和事件流的状态代理 。 建造在 `@earendil-works/pi-ai`。 。 。 .

## 安装

```bash
npm install @earendil-works/pi-agent-core
```

## 快速启动

```typescript
import { Agent } from "@earendil-works/pi-agent-core";
import { createModels } from "@earendil-works/pi-ai";
import { anthropicProvider } from "@earendil-works/pi-ai/providers/anthropic";

const models = createModels();
models.setProvider(anthropicProvider());
const model = models.getModel("anthropic", "claude-sonnet-4-6");
if (!model) throw new Error("Model not found");

const agent = new Agent({
  initialState: {
    systemPrompt: "You are a helpful assistant.",
    model,
  },
  streamFn: models.streamSimple.bind(models),
});

agent.subscribe((event) => {
  if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
    // Stream just the new text chunk
    process.stdout.write(event.assistantMessageEvent.delta);
  }
});

await agent.prompt("Hello!");
```

## 核心概念

### 代理Message vs LLM 信件

代理与 `AgentMessage`,灵活类型,可以包括:

-   标准 LLM 消息 (`user`, (中文(简体) ), `assistant`, (中文(简体) ), `toolResult`(中文(简体) )
-   通过声明合并自定义应用特定消息类型

法学硕士只理解 `user`, (中文(简体) ), `assistant`,以及 `toolResult`编辑 `convertToLlm` 函数通过在每次LLM调用前过滤和转换消息来弥补这一空白.

### 信件流动

```
AgentMessage[] → transformContext() → AgentMessage[] → convertToLlm() → Message[] → LLM
                    (optional)                           (required)
```

1.  **转换语句**: Prune 旧消息, 注入外部上下文
2.  **转换为Llm**: 过滤出仅UI 信件, 将自定义类型转换为 LLM 格式

## 事件流程

代理为 UI 更新发布事件 。 了解事件序列有助于构建响应界面.

### 提示( ) 事件顺序

当你打电话 `prompt("Hello")`数字 :

```
prompt("Hello")
├─ agent_start
├─ turn_start
├─ message_start   { message: userMessage }      // Your prompt
├─ message_end     { message: userMessage }
├─ message_start   { message: assistantMessage } // LLM starts responding
├─ message_update  { message: partial... }       // Streaming chunks
├─ message_update  { message: partial... }
├─ message_end     { message: assistantMessage } // Complete response
├─ turn_end        { message, toolResults: [] }
└─ agent_end       { messages: [...] }
```

### 用工具调用

如果助手调用工具,循环继续:

```
prompt("Read config.json")
├─ agent_start
├─ turn_start
├─ message_start/end  { userMessage }
├─ message_start      { assistantMessage with toolCall }
├─ message_update...
├─ message_end        { assistantMessage }
├─ tool_execution_start  { toolCallId, toolName, args }
├─ tool_execution_update { partialResult }           // If tool streams
├─ tool_execution_end    { toolCallId, result }
├─ message_start/end  { toolResultMessage }
├─ turn_end           { message, toolResults: [toolResult] }
│
├─ turn_start                                        // Next turn
├─ message_start      { assistantMessage }           // LLM responds to tool result
├─ message_update...
├─ message_end
├─ turn_end
└─ agent_end
```

工具执行模式可配置 :

-   `parallel` (默认):飞行前工具按顺序调用,同时执行允许的工具,放出 `tool_execution_end` 在每个工具定稿后立即发送工具Result信息和 `turn_end.toolResults` 以助理源排序
-   `sequential`: 逐一执行工具调用, 匹配历史行为

在平行模式下,工具补全事件遵循工具补全顺序,但持续存在的工具Result消息仍然遵循助理源命令.

该模式可通过 `toolExecution` 在代理配置中,或每个工具通过 `executionMode` 打开 `AgentTool`。如果任何工具在批次中调用,则以工具为目标 `executionMode: "sequential"`,整个批次不分全局设置顺序执行.

这个 `beforeToolCall` 钩子在后面运行 `tool_execution_start` 并验证论证。 它可以阻止执行和附加 `terminate: true` 到被封锁的结果。 这个 `afterToolCall` 工具执行完成后及执行前的勾钩运行 `tool_execution_end` 发布最后的工具结果信息事件.

工具被封锁 `beforeToolCall` 成果,以及 `afterToolCall` 覆盖可以返回 `terminate: true` 提示应该跳过自动后续 LLM 呼叫。 当每个最终完成的工具都出现这批货时,循环才提前停止 `terminate: true`混合批次继续正常进行.

当你使用 `Agent` 班级,助理 `message_end` 在工具开始飞行前,加工被视为障碍。 这意味着 `beforeToolCall` 查看已包含请求工具调用助理消息的代理状态 .

### 请求准备和最后敲定

`prepareRequest` 运行在每一个对话提供者请求之前,包括第一个。 使用它来在等待输入输出后安装空洞持续上下文 :

```typescript
agent.prepareRequest = async ({ context }) => ({
  context: { ...context, messages: await session.loadModelContext() },
});
```

`prepareRequest` 不投票队列。 运行时排队等待下一次普通的引导民意调查.

`finishTurn` 在助理和所有工具结果定稿后运行,但在此之前 `turn_end`。它运行到正常、错误和中止的响应:

```typescript
agent.finishTurn = async ({ message }) => {
  if (message.stopReason === "error" || message.stopReason === "aborted") return;
  if (shouldEndRun(message)) return { action: "end" };
  return needsAnotherResponse(message) ? { action: "continue" } : undefined;
};
```

正在返回 `undefined` 保留正常调度. `{ action: "end" }` 紧接着停止 `turn_end`,在投票指导或后续排队或准备其他请求之前。 在正常的反应中, `{ action: "continue" }` 确保下一个提供者请求。 如果工具结果,引导,或后续已经导致该请求,它们满足了决定,没有提出额外请求;否则循环只提出一个上下文请求. 出错和被中止的反应仍然是硬出错,因此他们的决定被忽略了. `finishTurn` 在下一个请求后再次运行, 所以返回 `{ action: "continue" }` 无条件创造出无尽的循环.

从被清除者中迁移 `shouldStopAfterTurn`ưμ㼯a `{ action: "end" }`。守护错误和中止的响应,以保留旧钩子的常态反应唯一引用,特别是当上游具有副作用或假设成功响应时:

```typescript
finishTurn: async (turn, signal) => {
  if (turn.message.stopReason === "error" || turn.message.stopReason === "aborted") return;
  return (await shouldStop(turn, signal)) ? { action: "end" } : undefined;
},
```

每个提供者都遵循这个生命周期:

```text
selected input events
→ prepareRequest
→ provider response
→ tool results
→ finishTurn
→ turn_end
→ existing continuation scheduling or agent_end
```

### 继续()并排队输入

`continue()` 保留其现有的队列行为。 空的和只使用系统的记录簿拒绝而不消耗队列 。 非辅助尾巴继续从现有上下文进行:引导在启动时会接受民意调查,而后续输入则会等反应自然停止后再进行.

```typescript
agent.followUp({ role: "user", content: "After the retry", timestamp: Date.now() });
await agent.continue(); // The first request retries the existing user/toolResult tail.
```

助理尾巴不能直接发送,所以 `continue()` 返回到一个排队的引导批,然后一个排队的后继批。 队列模式仍然控制所选批次是否包含一个消息或所有消息:

```typescript
agent.steer({ role: "user", content: "Continue from here", timestamp: Date.now() });
await agent.continue(); // Uses the queued message only because the tail is assistant.
```

### 事件类型

| 活动 | 说明 |
| --- | --- |
| `agent_start` | 代理开始处理 |
| `agent_end` | 跑步的最后一场比赛 等待参加这项活动的订户仍数到解决 |
| `turn_start` | 新回合开始( 一个 LLM 调用 + 工具执行) |
| `turn_end` | 以助理消息和工具结果转换完成 |
| `message_start` | 任何消息开始( 用户、 助手、 工具Result) |
| `message_update` | **仅限助理.** 包括 `assistantMessageEvent` 带三角洲 |
| `message_end` | 信件完成 |
| `tool_execution_start` | 工具开始 |
| `tool_execution_update` | 工具流进展 |
| `tool_execution_end` | 工具完成 |

`Agent.subscribe()` 等待听众登记. `agent_end` 意味着不再出现循环事件,但 `await agent.waitForIdle()` 财务报告和审定财务报表 `await agent.prompt(...)` 在等待之后才和解 `agent_end` 听众结束.

## 代理选项

```typescript
const agent = new Agent({
  // Initial state. systemPrompt and tools become the leading system message
  // unless messages already starts with one.
  initialState: {
    systemPrompt: string,
    model: Model<any>,
    thinkingLevel: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max",
    tools: AgentTool<any>[],
    messages: AgentMessage[],
  },

  // Convert AgentMessage[] to LLM Message[] (required for custom message types)
  convertToLlm: (messages) => messages.filter(...),

  // Transform context before convertToLlm (for pruning, compaction)
  transformContext: async (messages, signal) => pruneOldMessages(messages),

  // Steering mode: "one-at-a-time" (default) or "all"
  steeringMode: "one-at-a-time",

  // Follow-up mode: "one-at-a-time" (default) or "all"
  followUpMode: "one-at-a-time",

  // Required stream function. Receives a TranscriptContext: the prompt and tools
  // are in the transcript's system messages, not on the context.
  streamFn: models.streamSimple.bind(models),

  // Session ID for provider caching
  sessionId: "session-123",

  // How requests identify the app to providers (pi-ai ClientIdentity), merged over
  // DEFAULT_AGENT_IDENTITY ({ attribution: PI_ATTRIBUTION }: provider attribution headers name pi).
  // Pass your app's attribution, or null to send none.
  identity: { attribution: { name: "my-app", url: "https://my-app.example" } },

  // Dynamic API key resolution (for expiring OAuth tokens)
  getApiKey: async (provider) => refreshToken(),

  // Tool execution mode: "parallel" (default) or "sequential"
  toolExecution: "parallel",

  // Preflight each tool call after args are validated. Can block execution.
  beforeToolCall: async ({ toolCall, args, context }) => {
    if (toolCall.name === "bash") {
      return { block: true, reason: "bash is disabled", terminate: true };
    }
  },

  // Postprocess each tool result before final tool events are emitted.
  afterToolCall: async ({ toolCall, result, isError, context }) => {
    if (toolCall.name === "notify_done" && !isError) {
      return { terminate: true };
    }
    if (!isError) {
      return { details: { ...result.details, audited: true } };
    }
  },

  // Rebuild finalized context immediately before every provider request.
  prepareRequest: async ({ context }, signal) => {
    return { context: { ...context, messages: await loadCanonicalMessages(signal) } };
  },

  // Finalize a completed turn before turn_end is emitted.
  // `continue` ensures one next request; existing tool/queue scheduling can satisfy it.
  // `end` ends this run after turn_end without polling queues.
  finishTurn: async ({ message, toolResults }, signal) => {
    return shouldContinue(message, toolResults) ? { action: "continue" } : undefined;
  },

  // Custom thinking budgets for token-based providers
  thinkingBudgets: {
    minimal: 128,
    low: 512,
    medium: 1024,
    high: 2048,
  },
});
```

## 代理国

```typescript
interface AgentState {
  model: Model<any>;
  thinkingLevel: ThinkingLevel;
  tools: AgentTool<any>[];
  messages: AgentMessage[];
  readonly isStreaming: boolean;
  readonly streamingMessage?: AgentMessage;
  readonly pendingToolCalls: ReadonlySet<string>;
  readonly errorMessage?: string;
}
```

访问状态通过 `agent.state`。 。 。 .

指派 `agent.state.tools = [...]` 或者说 `agent.state.messages = [...]` 在存储前复制顶级数组。 变换返回的数组会变换当前代理状态 .

记录稿拥有系统即时和工具声明:主要系统信息是即时,后期系统信息补丁(参见: `SystemMessage` 说得没错. `agent.state.systemPrompt` 只读并重放记录. `agent.state.tools` 是可执行的加载;在每次请求之前,循环 diff 将其与记录稿所声明的工具相比,如果它们有差异,则在系统消息中宣布更改(当存在时合并为待处理的系统消息)。 皮爱的 `getCurrentSystemMessage(messages)` 返回任何信件阵列的重放头,包括已声明的工具,包括带有自定义信件角色的代理记录.

要更改提示性中间对话,请将系统消息附加到 `content` (添加指示)或 `sections` (代用名称):

```typescript
await agent.prompt([
  { role: "system", content: "", sections: { skills: "<skills>...</skills>" }, timestamp: Date.now() },
  { role: "user", content: "Continue", timestamp: Date.now() },
]);
```

在流出期间, `agent.state.streamingMessage` 包含当前部分助理消息.

`agent.state.isStreaming` 遗 物 `true` 直到运行完全平息,包括等待 `agent_end` 订户.

## 方法

### 提示

```typescript
// Text prompt
await agent.prompt("Hello");

// With images
await agent.prompt("What's in this image?", [
  { type: "image", data: base64Data, mimeType: "image/jpeg" }
]);

// AgentMessage directly
await agent.prompt({ role: "user", content: "Hello", timestamp: Date.now() });

// Continue existing non-assistant input; an assistant tail may use queued input as fallback
await agent.continue();
```

### 州管理层

```typescript
agent.state.model = getModel("openai", "gpt-4o");
agent.state.thinkingLevel = "medium";
agent.state.tools = [myTool];
agent.toolExecution = "sequential";
agent.beforeToolCall = async ({ toolCall }) => undefined;
agent.afterToolCall = async ({ toolCall, result }) => undefined;
agent.prepareRequest = async ({ context }) => ({
  context: { ...context, messages: await loadCanonicalMessages() },
});
agent.finishTurn = async () => undefined;
agent.state.messages = newMessages; // top-level array is copied
agent.state.messages.push(message);
const nextQueuedMessages = agent.peekQueuedMessages(); // respects queue modes; does not consume
agent.reset();
```

### 会议和思考预算

```typescript
agent.sessionId = "session-123";

agent.thinkingBudgets = {
  minimal: 128,
  low: 512,
  medium: 1024,
  high: 2048,
};
```

### 控制权

```typescript
agent.abort();           // Cancel current operation
await agent.waitForIdle(); // Wait for completion
```

### 活动

```typescript
const unsubscribe = agent.subscribe(async (event, signal) => {
  if (event.type === "agent_end") {
    // Final barrier work for the run
    await flushSessionState(signal);
  }
});
unsubscribe();
```

## 指导和后续行动

引导消息允许您在工具运行时中断代理 。 后续消息允许您排队工作, 否则代理将停止 .

```typescript
agent.steeringMode = "one-at-a-time";
agent.followUpMode = "one-at-a-time";

// While agent is running tools
agent.steer({
  role: "user",
  content: "Stop! Do this instead.",
  timestamp: Date.now(),
});

// After the agent finishes its current work
agent.followUp({
  role: "user",
  content: "Also summarize the result.",
  timestamp: Date.now(),
});

const steeringMode = agent.steeringMode;
const followUpMode = agent.followUpMode;

agent.clearSteeringQueue();
agent.clearFollowUpQueue();
agent.clearAllQueues();
```

使用清除SteeringQueee, 清除后继Queee, 或清除所有已排队的消息 .

当旋转完成后检测到方向信息时:

1.  当前助理消息中的所有工具呼叫都已完成
2.  输入引导消息
3.  LLM 在下一个回合响应

只有在没有更多的工具呼叫和引导消息时,才会检查后续消息. 如果有排队,则进行注射并再转一圈.

## 自定义信件类型

扩展 `AgentMessage` 通过声明合并:

```typescript
declare module "@earendil-works/pi-agent-core" {
  interface CustomAgentMessages {
    notification: { role: "notification"; text: string; timestamp: number };
  }
}

// Now valid
const msg: AgentMessage = { role: "notification", text: "Info", timestamp: Date.now() };
```

处理自定义类型 `convertToLlm`数字 :

```typescript
const agent = new Agent({
  streamFn: models.streamSimple.bind(models),
  convertToLlm: (messages) => messages.flatMap(m => {
    if (m.role === "notification") return []; // Filter out
    return [m];
  }),
});
```

## 工具

使用 `AgentTool`。 。 。 . `parameters` 是 JSON Schema 对象; 以 `@earendil-works/pi-schema`建设者,它也类型 `params` (其他计划) `unknown`, (中文(简体) ), `AgentTool<any>` 给 `any`:

```typescript
import { obj, str } from "@earendil-works/pi-schema";

const readFileParameters = obj({
  path: str({ description: "File path" }),
});

const readFileTool: AgentTool<typeof readFileParameters> = {
  name: "read_file",
  label: "Read File",  // For UI display
  description: "Read a file's contents",
  parameters: readFileParameters,
  // Override execution mode for this tool (optional).
  // "sequential" forces the entire batch to run one at a time.
  // "parallel" allows concurrent execution with other tool calls.
  // If omitted, the global toolExecution config applies.
  executionMode: "sequential",
  execute: async (toolCallId, params, signal, onUpdate) => {
    const content = await fs.readFile(params.path, "utf-8");

    // Optional: stream progress
    onUpdate?.({ content: [{ type: "text", text: "Reading..." }], details: {} });

    // Optional: add `terminate: true` here to skip the automatic follow-up LLM call
    // when every finalized tool result in the batch does the same.
    return {
      content: [{ type: "text", text: content }],
      details: { path: params.path, size: content.length },
    };
  },
};

agent.state.tools = [readFileTool];
```

### 处理错误

**丢出错误** 当工具失败时。 不返回错误消息为内容 .

```typescript
execute: async (toolCallId, params, signal, onUpdate) => {
  if (!fs.existsSync(params.path)) {
    throw new Error(`File not found: ${params.path}`);
  }
  // Return content only on success
  return { content: [{ type: "text", text: "..." }] };
}
```

丢出的错误被代理抓住,并作为工具错误报告到 LLM `isError: true`。 。 。 .

返回时 `terminate: true` 从 `execute()`,一个被屏蔽 `beforeToolCall`,或 `afterToolCall` 提示代理在当前工具批次后应当停止。 这只有在每个最终完成的工具都导致批量终止时才会生效. 提示是只运行时间; 释放 `toolResult` 记录信息仍然是标准的LLM工具结果.

### MCP 和代码

`@earendil-works/pi-mcp` 连接到 MCP 服务器和 `@earendil-works/pi-codemode` 运行调用工具的模型笔迹JavaScript. [实例/mcp 编码模式](https://npmcn.com/package/%40averyyy/pi-agent-core/examples/mcp-codemode) 将两者相包围为 `AgentTool`s: 每个多边CP工具一个工具,和一个 `codemode` 用于脚本调用代理工具的工具 `runToolCall()`这样 `beforeToolCall` 财务报告和审定财务报表 `afterToolCall` 这些电话也适用.

## 代理用户

对于通过后端代理的浏览器应用:

```typescript
import { Agent, streamProxy } from "@earendil-works/pi-agent-core";

const agent = new Agent({
  streamFn: (model, context, options) =>
    streamProxy(model, context, {
      ...options,
      authToken: "...",
      proxyUrl: "https://your-server.com",
    }),
});
```

## 低级 API

对于没有代理类的直接控制:

```typescript
import { agentLoop, agentLoopContinue } from "@earendil-works/pi-agent-core";

const context: AgentContext = {
  messages: [{ role: "system", content: "You are helpful.", timestamp: Date.now() }],
  tools: [],
};

const config: AgentLoopConfig = {
  model: getModel("openai", "gpt-4o"),
  convertToLlm: (msgs) => msgs.filter(m => ["user", "assistant", "toolResult"].includes(m.role)),
  toolExecution: "parallel",  // overridden by per-tool executionMode if set
  beforeToolCall: async ({ toolCall, args, context }) => undefined,
  afterToolCall: async ({ toolCall, result, isError, context }) => undefined,
};

const userMessage = { role: "user", content: "Hello", timestamp: Date.now() };

const streamFn = models.streamSimple.bind(models);
for await (const event of agentLoop([userMessage], context, config, undefined, streamFn)) {
  console.log(event.type);
}

// Continue from existing context
for await (const event of agentLoopContinue(context, config, undefined, streamFn)) {
  console.log(event.type);
}
```

这些低层溪流是可观测的. 它们保存事件顺序, 但是它们不会等待您的 Async 事件处理在以后的制作阶段继续之前解决 。 如果您需要信息处理在工具飞行前起到屏障作用,请使用 `Agent` 类而不是生 `agentLoop()` 或者说 `agentLoopContinue()`。 。 。 .

## 许可证

麻省理工学院

## 常见问题

### @averyyy/pi-agent-core 是什么？

具有运输抽象、状态管理和附件支持的通用剂

### 如何安装 @averyyy/pi-agent-core？

在项目目录运行 npm install @averyyy/pi-agent-core，或使用 pnpm add / yarn add / bun add。

### @averyyy/pi-agent-core 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @averyyy/pi-agent-core 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @averyyy/pi-agent-core 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22\.19\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @averyyy/pi-agent-core 可以商用吗？

@averyyy/pi-agent-core 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @averyyy/pi-agent-core 的下载量有多少？

根据 npm 官方下载统计，@averyyy/pi-agent-core 在 2026/10/07 – 2026/11/05 这 30 天内被下载 7,821 次，日均约 261 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @averyyy/pi-agent-core 还在维护吗？

最新版本 1\.5\.0-piclient\.4 发布于 2026/11/05（2 天前）；至今共发布 95 个版本，其中过去 12 个月发布了 95 个；首次发布于 2026/07/02。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

