本文档深入分析 agent.ts、agent-loop.ts、openai-completions.ts 三个核心文件中异步迭代器 Stream 的完整时序,以及所有 Event 的生产消费流程。
概览:三层 Stream 架构
css
┌─────────────────────────────────────────────────────────────────────────────┐
│ Stream 三层架构 │
│ │
│ Layer 3: Agent._runLoop() │
│ consumes EventStream<AgentEvent, AgentMessage[]> │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ for await (const event of stream) { │ │
│ │ // 更新内部状态,转发给 subscribers │ │
│ │ } │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ▲ consumes │
│ │ │
│ Layer 2: agentLoop() / runLoop() │
│ produces EventStream<AgentEvent, AgentMessage[]> │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ stream.push({ type: "agent_start" }) │ │
│ │ stream.push({ type: "turn_start" }) │ │
│ │ await streamAssistantResponse() ← 调用 Layer 1 │ │
│ │ await executeToolCalls() │ │
│ │ stream.push({ type: "agent_end", messages }) │ │
│ │ stream.end(newMessages) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ▲ consumes │
│ │ │
│ Layer 1: streamOpenAICompletions() │
│ produces AssistantMessageEventStream │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ stream.push({ type: "start", partial }) │ │
│ │ for await (const chunk of openaiStream) { │ │
│ │ stream.push({ type: "text_delta", ... }) │ │
│ │ stream.push({ type: "thinking_delta", ... }) │ │
│ │ stream.push({ type: "toolcall_delta", ... }) │ │
│ │ } │ │
│ │ stream.push({ type: "done", message }) │ │
│ │ stream.end() │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ▲ consumes │
│ │ │
│ Layer 0: OpenAI SDK │
│ produces AsyncIterable<ChatCompletionChunk> │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ for await (const chunk of openaiStream) { ... } │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
Layer 1: OpenAI Completions Provider
1.1 AssistantMessageEventStream 创建
typescript
// packages/ai/src/providers/openai-completions.ts:60-65
export const streamOpenAICompletions: StreamFunction<...> = (model, context, options) => {
const stream = new AssistantMessageEventStream(); // 创建空流
// 异步立即返回,内部 IIFE 填充事件
(async () => {
// ... 事件生产逻辑
})();
return stream; // 调用者立即拿到 stream 引用
};
关键设计 :Provider 函数同步返回 AssistantMessageEventStream,实际的事件推送在异步 IIFE 中进行。调用者通过 for await 消费时,会自动等待事件到达。
1.2 OpenAI SDK 层迭代
typescript
// packages/ai/src/providers/openai-completions.ts:94
const openaiStream = await client.chat.completions.create(params, { signal: options?.signal });
openaiStream 是 OpenAI SDK 返回的 AsyncIterable<ChatCompletionChunk>,代表 HTTP SSE 连接。
typescript
// packages/ai/src/providers/openai-completions.ts:129-266
for await (const chunk of openaiStream) {
// chunk 结构:
// {
// id: "chatcmpl-xxx",
// choices: [{
// delta: { content?: string, tool_calls?: [...] },
// finish_reason: "stop" | "tool_calls" | null
// }],
// usage: { prompt_tokens, completion_tokens, ... }
// }
}
1.3 事件类型与推送时序
css
┌─────────────────────────────────────────────────────────────────────────────┐
│ OpenAI Completions Event 推送时序 │
│ │
│ HTTP SSE 连接建立 │
│ │ │
│ ▼ │
│ stream.push({ type: "start", partial: output }) ← 初始化空的 partial │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ for await (chunk of openaiStream) │ │
│ │ │ │
│ │ chunk.choices[0].delta.content 有内容? │ │
│ │ ├─ 是 → 新文本块或追加到当前块 │ │
│ │ │ ├─ 新块: push({ type: "text_start", contentIndex, partial })│ │
│ │ │ └─ 追加: push({ type: "text_delta", contentIndex, delta, partial })│
│ │ │ │ │
│ │ chunk.choices[0].delta.reasoning_content 有内容? │ │
│ │ ├─ 是 → 新思维块或追加到当前块 │ │
│ │ │ ├─ 新块: push({ type: "thinking_start", ... }) │ │
│ │ │ └─ 追加: push({ type: "thinking_delta", ... }) │ │
│ │ │ │ │
│ │ chunk.choices[0].delta.tool_calls 有内容? │ │
│ │ ├─ 是 → 新工具调用块或追加到当前块 │ │
│ │ │ ├─ 新块: push({ type: "toolcall_start", ... }) │ │
│ │ │ └─ 追加: push({ type: "toolcall_delta", ... }) │ │
│ │ │ │ │
│ │ chunk.usage 存在? │ │
│ │ └─ 是 → output.usage = parseChunkUsage(chunk.usage, model) │ │
│ │ │ │
│ │ chunk.choices[0].finish_reason 存在? │ │
│ │ └─ 是 → output.stopReason = mapStopReason(finish_reason) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ finishCurrentBlock(currentBlock) ← 发送 *_end 事件 │
│ │ │
│ ▼ │
│ stream.push({ type: "done", reason, message: output }) │
│ stream.end() │
│ │
│ ───────────────────────────────────────────────────────────────────────── │
│ 异常路径: │
│ │ │
│ ▼ │
│ stream.push({ type: "error", reason, error: output }) │
│ stream.end() │
└─────────────────────────────────────────────────────────────────────────────┘
1.4 完整事件类型定义
typescript
// packages/ai/src/types.ts - AssistantMessageEvent 联合类型
type AssistantMessageEvent =
| { type: "start"; partial: AssistantMessage }
| { type: "text_start"; contentIndex: number; partial: AssistantMessage }
| { type: "text_delta"; contentIndex: number; delta: string; partial: AssistantMessage }
| { type: "text_end"; contentIndex: number; content: string; partial: AssistantMessage }
| { type: "thinking_start"; contentIndex: number; partial: AssistantMessage }
| { type: "thinking_delta"; contentIndex: number; delta: string; partial: AssistantMessage }
| { type: "thinking_end"; contentIndex: number; content: string; partial: AssistantMessage }
| { type: "toolcall_start"; contentIndex: number; partial: AssistantMessage }
| { type: "toolcall_delta"; contentIndex: number; delta: string; partial: AssistantMessage }
| { type: "toolcall_end"; contentIndex: number; toolCall: ToolCall; partial: AssistantMessage }
| { type: "done"; reason: StopReason; message: AssistantMessage }
| { type: "error"; reason: StopReason; error: AssistantMessage }
1.5 partial 对象的演进
typescript
// 初始 partial (在 "start" 事件时创建)
const output: AssistantMessage = {
role: "assistant",
content: [], // 空,后续事件会追加
api: model.api,
provider: model.provider,
model: model.id,
usage: { input: 0, output: 0, ... },
stopReason: "stop",
timestamp: Date.now(),
};
// 每个 delta 事件时,output.content 被原地修改
output.content.push({ type: "text", text: "" }); // text_start
output.content[0].text += delta; // text_delta
// ...以此类推
// 最终 message (在 "done" 事件时)
// output 已被完全填充
关键点 :partial 是同一个对象的引用,每次 push 时传递的是同一对象的快照引用。消费者看到的 partial 内容取决于消费时机。
Layer 2: Agent Loop
2.1 EventStream 创建
typescript
// packages/agent/src/agent-loop.ts:94-99
function createAgentStream(): EventStream<AgentEvent, AgentMessage[]> {
return new EventStream<AgentEvent, AgentMessage[]>(
(event: AgentEvent) => event.type === "agent_end", // 终止条件
(event: AgentEvent) => (event.type === "agent_end" ? event.messages : []), // 结果提取
);
}
2.2 agentLoop 入口
typescript
// packages/agent/src/agent-loop.ts:28-55
export function agentLoop(prompts, context, config, signal, streamFn) {
const stream = createAgentStream();
(async () => {
const newMessages: AgentMessage[] = [...prompts];
const currentContext: AgentContext = {
...context,
messages: [...context.messages, ...prompts], // 追加用户消息
};
// 发送会话启动事件
stream.push({ type: "agent_start" });
stream.push({ type: "turn_start" });
// 为每个用户消息发送 message_start/end
for (const prompt of prompts) {
stream.push({ type: "message_start", message: prompt });
stream.push({ type: "message_end", message: prompt });
}
// 进入主循环
await runLoop(currentContext, newMessages, config, signal, stream, streamFn);
})();
return stream; // 立即返回,异步填充
}
2.3 runLoop 双层循环
typescript
// packages/agent/src/agent-loop.ts:104-198
async function runLoop(currentContext, newMessages, config, signal, stream, streamFn) {
let firstTurn = true;
let pendingMessages: AgentMessage[] = (await config.getSteeringMessages?.()) || [];
// ========== 外层循环:处理 follow-up 消息 ==========
while (true) {
let hasMoreToolCalls = true;
let steeringAfterTools: AgentMessage[] | null = null;
// ========== 内层循环:处理工具调用 + steering 消息 ==========
while (hasMoreToolCalls || pendingMessages.length > 0) {
if (!firstTurn) {
stream.push({ type: "turn_start" });
} else {
firstTurn = false;
}
// [1] 注入 pending 消息(steering 或 follow-up)
if (pendingMessages.length > 0) {
for (const message of pendingMessages) {
stream.push({ type: "message_start", message });
stream.push({ type: "message_end", message });
currentContext.messages.push(message);
newMessages.push(message);
}
pendingMessages = [];
}
// [2] 调用 LLM(关键!消费 Layer 1 的 Stream)
const message = await streamAssistantResponse(currentContext, config, signal, stream, streamFn);
newMessages.push(message);
// [3] 检查错误/中止
if (message.stopReason === "error" || message.stopReason === "aborted") {
stream.push({ type: "turn_end", message, toolResults: [] });
stream.push({ type: "agent_end", messages: newMessages });
stream.end(newMessages);
return; // 提前退出
}
// [4] 检查工具调用
const toolCalls = message.content.filter((c) => c.type === "toolCall");
hasMoreToolCalls = toolCalls.length > 0;
// [5] 执行工具调用
const toolResults: ToolResultMessage[] = [];
if (hasMoreToolCalls) {
const toolExecution = await executeToolCalls(
currentContext.tools, message, signal, stream, config.getSteeringMessages
);
toolResults.push(...toolExecution.toolResults);
steeringAfterTools = toolExecution.steeringMessages ?? null;
for (const result of toolResults) {
currentContext.messages.push(result);
newMessages.push(result);
}
}
stream.push({ type: "turn_end", message, toolResults });
// [6] 检查 steering 消息(用户中断)
if (steeringAfterTools && steeringAfterTools.length > 0) {
pendingMessages = steeringAfterTools;
steeringAfterTools = null;
} else {
pendingMessages = (await config.getSteeringMessages?.()) || [];
}
}
// [7] 检查 follow-up 消息(Agent 完成后注入)
const followUpMessages = (await config.getFollowUpMessages?.()) || [];
if (followUpMessages.length > 0) {
pendingMessages = followUpMessages;
continue; // 继续外层循环
}
// 无更多消息,退出
break;
}
stream.push({ type: "agent_end", messages: newMessages });
stream.end(newMessages);
}
2.4 streamAssistantResponse:跨层消费
这是 Layer 1 → Layer 2 的桥梁:
typescript
// packages/agent/src/agent-loop.ts:204-289
async function streamAssistantResponse(context, config, signal, stream, streamFn) {
// [1] 可选:上下文变换(剪枝/注入外部知识)
let messages = context.messages;
if (config.transformContext) {
messages = await config.transformContext(messages, signal);
}
// [2] 转换为 LLM 兼容格式
const llmMessages = await config.convertToLlm(messages);
// [3] 构建 LLM Context
const llmContext: Context = {
systemPrompt: context.systemPrompt,
messages: llmMessages,
tools: context.tools,
};
// [4] 获取 stream 函数(默认 streamSimple)
const streamFunction = streamFn || streamSimple;
// [5] 调用 Layer 1,获取 AssistantMessageEventStream
const response = await streamFunction(config.model, llmContext, {
...config,
apiKey: resolvedApiKey,
signal,
});
let partialMessage: AssistantMessage | null = null;
let addedPartial = false;
// [6] ========== 消费 Layer 1 的 Stream ==========
for await (const event of response) {
switch (event.type) {
case "start":
partialMessage = event.partial;
context.messages.push(partialMessage); // 立即加入上下文!
addedPartial = true;
stream.push({ type: "message_start", message: { ...partialMessage } });
break;
case "text_start":
case "text_delta":
case "text_end":
case "thinking_start":
case "thinking_delta":
case "thinking_end":
case "toolcall_start":
case "toolcall_delta":
case "toolcall_end":
if (partialMessage) {
partialMessage = event.partial;
// 原地更新上下文中的 partial message
context.messages[context.messages.length - 1] = partialMessage;
stream.push({
type: "message_update",
assistantMessageEvent: event,
message: { ...partialMessage },
});
}
break;
case "done":
case "error": {
const finalMessage = await response.result();
if (addedPartial) {
context.messages[context.messages.length - 1] = finalMessage;
} else {
context.messages.push(finalMessage);
}
if (!addedPartial) {
stream.push({ type: "message_start", message: { ...finalMessage } });
}
stream.push({ type: "message_end", message: finalMessage });
return finalMessage;
}
}
}
return await response.result();
}
关键设计 :partial message 在 start 事件时就立即加入 context.messages,后续 delta 事件直接原地更新。这确保了:
- 如果发生中断,上下文中已有部分响应
- 工具调用参数在流式过程中就可被其他代码访问
2.5 executeToolCalls:工具执行与中断检测
typescript
// packages/agent/src/agent-loop.ts:294-378
async function executeToolCalls(tools, assistantMessage, signal, stream, getSteeringMessages) {
const toolCalls = assistantMessage.content.filter((c) => c.type === "toolCall");
const results: ToolResultMessage[] = [];
let steeringMessages: AgentMessage[] | undefined;
for (let index = 0; index < toolCalls.length; index++) {
const toolCall = toolCalls[index];
const tool = tools?.find((t) => t.name === toolCall.name);
// 发送工具执行开始事件
stream.push({
type: "tool_execution_start",
toolCallId: toolCall.id,
toolName: toolCall.name,
args: toolCall.arguments,
});
let result: AgentToolResult<any>;
let isError = false;
try {
if (!tool) throw new Error(`Tool ${toolCall.name} not found`);
// 参数验证
const validatedArgs = validateToolArguments(tool, toolCall);
// 执行工具,支持流式进度更新
result = await tool.execute(toolCall.id, validatedArgs, signal, (partialResult) => {
stream.push({
type: "tool_execution_update",
toolCallId: toolCall.id,
toolName: toolCall.name,
args: toolCall.arguments,
partialResult,
});
});
} catch (e) {
result = { content: [{ type: "text", text: e.message }], details: {} };
isError = true;
}
// 发送工具执行结束事件
stream.push({
type: "tool_execution_end",
toolCallId: toolCall.id,
toolName: toolCall.name,
result,
isError,
});
// 构建 ToolResultMessage
const toolResultMessage: ToolResultMessage = {
role: "toolResult",
toolCallId: toolCall.id,
toolName: toolCall.name,
content: result.content,
details: result.details,
isError,
timestamp: Date.now(),
};
results.push(toolResultMessage);
stream.push({ type: "message_start", message: toolResultMessage });
stream.push({ type: "message_end", message: toolResultMessage });
// ========== 关键:检查 steering 消息 ==========
if (getSteeringMessages) {
const steering = await getSteeringMessages();
if (steering.length > 0) {
steeringMessages = steering;
// 跳过剩余工具调用!
const remainingCalls = toolCalls.slice(index + 1);
for (const skipped of remainingCalls) {
results.push(skipToolCall(skipped, stream)); // 发送 skip 事件
}
break; // 跳出工具执行循环
}
}
}
return { toolResults: results, steeringMessages };
}
Layer 3: Agent Class
3.1 _runLoop:最终消费
typescript
// packages/agent/src/agent.ts:413-560
private async _runLoop(messages?: AgentMessage[], options?) {
// ...
try {
// 获取 Layer 2 的 Stream
const stream = messages
? agentLoop(messages, context, config, this.abortController.signal, this.streamFn)
: agentLoopContinue(context, config, this.abortController.signal, this.streamFn);
// ========== 消费 Layer 2 的 Stream ==========
for await (const event of stream) {
// 更新内部状态
switch (event.type) {
case "message_start":
partial = event.message;
this._state.streamMessage = event.message;
break;
case "message_update":
partial = event.message;
this._state.streamMessage = event.message;
break;
case "message_end":
partial = null;
this._state.streamMessage = null;
this.appendMessage(event.message); // 持久化到历史
break;
case "tool_execution_start": {
const s = new Set(this._state.pendingToolCalls);
s.add(event.toolCallId);
this._state.pendingToolCalls = s;
break;
}
case "tool_execution_end": {
const s = new Set(this._state.pendingToolCalls);
s.delete(event.toolCallId);
this._state.pendingToolCalls = s;
break;
}
case "turn_end":
if (event.message.role === "assistant" && (event.message as any).errorMessage) {
this._state.error = (event.message as any).errorMessage;
}
break;
case "agent_end":
this._state.isStreaming = false;
this._state.streamMessage = null;
break;
}
// 转发给所有订阅者
this.emit(event);
}
// 处理残留的 partial message
if (partial && partial.role === "assistant" && partial.content.length > 0) {
// ...
}
} catch (err: any) {
// 错误处理:构造错误消息
const errorMsg: AgentMessage = { /* ... */ };
this.appendMessage(errorMsg);
this._state.error = err?.message;
this.emit({ type: "agent_end", messages: [errorMsg] });
} finally {
this._state.isStreaming = false;
this._state.streamMessage = null;
this._state.pendingToolCalls = new Set<string>();
this.abortController = undefined;
this.resolveRunningPrompt?.();
}
}
3.2 subscribe:事件订阅
typescript
// packages/agent/src/agent.ts:210-213
subscribe(fn: (e: AgentEvent) => void): () => void {
this.listeners.add(fn);
return () => this.listeners.delete(fn); // 返回取消订阅函数
}
// packages/agent/src/agent.ts:563-566
private emit(e: AgentEvent) {
for (const listener of this.listeners) {
listener(e);
}
}
完整时序图
yaml
┌─────────────────────────────────────────────────────────────────────────────┐
│ 完整 Event 时序图(单次 LLM 调用 + 工具执行) │
│ │
│ 时间轴 ──────────────────────────────────────────────────────────────────► │
│ │
│ Layer 2 Layer 1 Layer 0 │
│ agentLoop() streamOpenAICompletions() OpenAI SDK │
│ │ │ │ │
│ │ push: agent_start │ │ │
│ │ push: turn_start │ │ │
│ │ push: message_start(user) │ │ │
│ │ push: message_end(user) │ │ │
│ │ │ │ │
│ │ ───streamAssistantResponse───► │ │ │
│ │ │ create: AssistantMsgStream │ │
│ │ │ ───await client.chat.create─►│ │
│ │ │ │ │
│ │ │ ◄─── SSE stream established ─┤ │
│ │ │ │ │
│ │ │ push: start │ │
│ │ ◄─── push: message_start ──────┤ │ │
│ │ │ │ │
│ │ │ ◄─── chunk: delta.content ───┤ │
│ │ │ push: text_start │ │
│ │ ◄─── push: message_update ─────┤ push: text_delta │ │
│ │ │ │ │
│ │ │ ◄─── chunk: delta.content ───┤ │
│ │ ◄─── push: message_update ─────┤ push: text_delta │ │
│ │ │ │ │
│ │ │ ◄─── chunk: tool_calls ──────┤ │
│ │ │ push: toolcall_start │ │
│ │ ◄─── push: message_update ─────┤ push: toolcall_delta │ │
│ │ │ │ │
│ │ │ ◄─── chunk: finish_reason ───┤ │
│ │ │ push: done │ │
│ │ ◄─── push: message_end ────────┤ │ │
│ │ │ stream.end() │ │
│ │ ◄─── return finalMessage ──────┤ │ │
│ │ │ │ │
│ │ push: turn_end │ │ │
│ │ │ │ │
│ │ ───executeToolCalls──────────► │ │ │
│ │ │ │ │
│ │ push: tool_execution_start │ │ │
│ │ push: tool_execution_update │ (流式进度) │ │
│ │ push: tool_execution_end │ │ │
│ │ push: message_start(toolResult)│ │ │
│ │ push: message_end(toolResult) │ │ │
│ │ │ │ │
│ │ ───streamAssistantResponse───► │ (第二轮 LLM 调用) │ │
│ │ ... │ ... │ │
│ │ │ │ │
│ │ push: turn_end │ │ │
│ │ push: agent_end │ │ │
│ │ stream.end() │ │ │
│ │ │ │ │
└─────────────────────────────────────────────────────────────────────────────┘
AgentEvent 完整类型
typescript
// packages/agent/src/types.ts
type AgentEvent =
// 会话级别
| { type: "agent_start" }
| { type: "agent_end"; messages: AgentMessage[] }
// Turn 级别(一次 LLM 调用)
| { type: "turn_start" }
| { type: "turn_end"; message: AssistantMessage; toolResults: ToolResultMessage[] }
// 消息级别
| { type: "message_start"; message: AgentMessage }
| { type: "message_update"; assistantMessageEvent: AssistantMessageEvent; message: AgentMessage }
| { type: "message_end"; message: AgentMessage }
// 工具执行级别
| { type: "tool_execution_start"; toolCallId: string; toolName: string; args: Record<string, any> }
| { type: "tool_execution_update"; toolCallId: string; toolName: string; args: Record<string, any>; partialResult: any }
| { type: "tool_execution_end"; toolCallId: string; toolName: string; result: AgentToolResult<any>; isError: boolean }
EventStream 内部机制
队列与等待者数组
typescript
// packages/ai/src/utils/event-stream.ts
export class EventStream<T, R = T> implements AsyncIterable<T> {
private queue: T[] = []; // 待消费事件
private waiting: ((value: IteratorResult<T>) => void)[] = []; // 等待 Promise 的 resolve 函数
private done = false;
private finalResultPromise: Promise<R>;
private resolveFinalResult!: (result: R) => void;
private isComplete: (event: T) => boolean;
private extractResult: (event: T) => R;
push 流程
typescript
push(event: T): void {
if (this.done) return; // 已终止,忽略
// 有等待者:直接唤醒
if (this.waiting.length > 0) {
const resolve = this.waiting.shift()!;
resolve({ value: event, done: false });
} else {
// 无等待者:存入队列
this.queue.push(event);
}
// 检查终止条件
if (this.isComplete(event)) {
this.done = true;
this.resolveFinalResult(this.extractResult(event));
}
}
AsyncIterator 实现
typescript
[Symbol.asyncIterator](): AsyncIterator<T> {
return {
next: async (): Promise<IteratorResult<T>> => {
// 情况一:队列有事件
if (this.queue.length > 0) {
return { value: this.queue.shift()!, done: false };
}
// 情况二:已终止
if (this.done) {
return { value: undefined as any, done: true };
}
// 情况三:等待事件
return new Promise((resolve) => {
this.waiting.push(resolve); // 注册 resolve,等待 push 调用
});
},
};
}
背压感知示意
scss
┌─────────────────────────────────────────────────────────────────────────────┐
│ 背压感知机制 │
│ │
│ 场景 A:生产者快于消费者 │
│ │
│ push(e1) → queue: [e1] │
│ push(e2) → queue: [e1, e2] │
│ push(e3) → queue: [e1, e2, e3] │
│ ... │
│ consumer.next() → yield e1, queue: [e2, e3, ...] │
│ │
│ 场景 B:消费者快于生产者 │
│ │
│ consumer.next() → waiting: [resolve1] │
│ push(e1) → 调用 resolve1({ value: e1, done: false }) │
│ consumer.next() → waiting: [resolve2] │
│ push(e2) → 调用 resolve2({ value: e2, done: false }) │
│ │
│ 场景 C:生产者完成 │
│ │
│ push(done_event) → done = true, resolveFinalResult(result) │
│ consumer.next() → { value: undefined, done: true } │
└─────────────────────────────────────────────────────────────────────────────┘
关键设计总结
1. 同步返回 + 异步填充
所有 Stream 创建函数都是同步返回 Stream 引用,实际事件在异步 IIFE 中推送:
typescript
const stream = new AssistantMessageEventStream();
(async () => { /* 推送事件 */ })();
return stream; // 立即返回
这允许调用者在事件开始推送前就持有 Stream 引用,通过 for await 进入等待状态。
2. Partial Message 原地更新
streamAssistantResponse 中:
typescript
case "start":
context.messages.push(event.partial); // 加入上下文
break;
case "text_delta":
partialMessage = event.partial;
context.messages[context.messages.length - 1] = partialMessage; // 原地更新
break;
这确保了即使发生中断,上下文中也保留了部分响应。
3. Steering 中断机制
typescript
// executeToolCalls 中
if (getSteeringMessages) {
const steering = await getSteeringMessages();
if (steering.length > 0) {
// 跳过剩余工具
for (const skipped of remainingCalls) {
results.push(skipToolCall(skipped, stream));
}
break;
}
}
用户可以在工具执行期间注入"方向盘消息",立即中断当前执行流程。
4. 双层循环结构
markdown
外层循环:处理 follow-up 消息(Agent 完成后继续)
└─ 内层循环:处理工具调用 + steering 消息
├─ 调用 LLM
├─ 执行工具
└─ 检查中断
这允许 Agent 在"完成"后接收新任务,实现长链 Agent 场景。
5. 事件转译层
| Layer 1 Event | Layer 2 Event |
|---|---|
start |
message_start |
*_delta |
message_update |
done / error |
message_end |
Layer 2 将底层的事件细节抽象为更高级的"消息生命周期"事件,简化了上层消费逻辑。