|
| 1 | +--- |
| 2 | +description: |
| 3 | +globs: src/main/presenter/llmProviderPresenter/index.ts,src/main/presenter/llmProviderPresenter/baseProvider.ts |
| 4 | +alwaysApply: false |
| 5 | +--- |
| 6 | +# LLM Agent Loop 和 Provider 架构 |
| 7 | + |
| 8 | +本文档概述了处理 LLM 流式补全的架构,特别关注涉及工具调用的 Agent 循环。 |
| 9 | + |
| 10 | +## 核心原则 |
| 11 | + |
| 12 | +1. **关注点分离 (Separation of Concerns):** |
| 13 | + * `src/main/presenter/llmProviderPresenter/index.ts`: 管理整体 Agent 循环、对话历史、通过 `McpPresenter` 执行工具、并通过 `eventBus` 与前端通信。 |
| 14 | + * `src/main/presenter/llmProviderPresenter/providers/*.ts`: 每个 Provider 文件负责与特定的 LLM API 交互,处理特定于 Provider 的请求/响应格式,转换工具定义,管理原生与非原生工具调用机制(Prompt 包装),并将输出流标准化为通用事件格式。 |
| 15 | + |
| 16 | +2. **标准化流事件 (Standardized Stream Events):** Provider 实现 (`coreStream` 方法) 使用标准化接口 `yield` 事件,以将主循环与 Provider 的具体细节解耦。 |
| 17 | + |
| 18 | +3. **Provider 中的单次流式传输 (Single-Pass Streaming in Providers):** 每个 Provider 中的核心流式方法 (`coreStream`) 应为对话的每一轮执行*单次*流式 API 请求。它不应包含多轮工具调用的循环逻辑。 |
| 19 | + |
| 20 | +## 架构细节 |
| 21 | + |
| 22 | +### `llmProviderPresenter/index.ts` (`startStreamCompletion`) |
| 23 | + |
| 24 | +* **Agent 循环:** 包含主要的 `while` 循环,管理对话流程,包括在需要时进行多轮 LLM 调用和工具使用。 |
| 25 | +* **状态管理:** 在循环迭代中维护 `conversationMessages` 历史记录。 |
| 26 | +* **Provider 交互:** 在每个循环迭代中调用 `provider.coreStream()` 方法。 |
| 27 | +* **事件处理:** |
| 28 | + * 监听由 `coreStream` `yield` 的标准化事件。 |
| 29 | + * 缓冲文本内容 (`currentContent`)。 |
| 30 | + * 处理 `tool_call_start/chunk/end` 事件: |
| 31 | + * 收集完整的工具调用详细信息(id, name, arguments)。 |
| 32 | + * 调用 `presenter.mcpPresenter.callTool` 执行工具。 |
| 33 | + * 向前端发送带有工具调用状态(`tool_call: 'start' | 'end' | 'error'`)的 `STREAM_EVENTS.RESPONSE` 事件。 |
| 34 | + * 将工具结果格式化为适合*下一次* LLM 调用的消息(例如,角色为 'tool' 或附加到用户消息)。 |
| 35 | + * 设置 `needContinueConversation = true`。 |
| 36 | + * 处理 `reasoning` 事件并通过 `STREAM_EVENTS.RESPONSE` 发送它们。 |
| 37 | + * 处理 `text` 事件并通过 `STREAM_EVENTS.RESPONSE` 发送它们。 |
| 38 | + * 处理 `image_data` 事件并通过 `STREAM_EVENTS.RESPONSE` 发送它们。 |
| 39 | + * 处理 `usage` 事件并聚合它们。 |
| 40 | + * 处理 `stop` 事件: |
| 41 | + * 如果 `stop_reason: 'tool_use'`,则添加缓冲的助手消息并准备下一次循环迭代。 |
| 42 | + * 否则,添加最终的助手消息并跳出循环。 |
| 43 | +* **循环控制:** 使用 `needContinueConversation` 和 `toolCallCount`(与 `MAX_TOOL_CALLS` 比较)来管理循环。 |
| 44 | +* **前端通信:** 通过 `eventBus` 发送标准化的 `STREAM_EVENTS`(`RESPONSE`, `END`, `ERROR`)。 |
| 45 | + |
| 46 | +### Provider 实现 (`src/main/presenter/llmProviderPresenter/providers/*.ts`) |
| 47 | + |
| 48 | +* **`coreStream(messages, modelId, temperature, maxTokens)` 方法:** |
| 49 | + * **输入:** 接收当前对话消息(根据 Provider 的需求格式化,可能包含上一轮的工具结果)和生成参数。 |
| 50 | + * **工具处理:** |
| 51 | + * **原生支持:** 如果 Provider/模型支持原生函数调用,则将 MCP 工具转换为 Provider 的格式 (`convertToProviderTools`) 并包含在 API 请求中。 |
| 52 | + * **无原生支持:** 如果不支持原生 FC,则在进行 API 调用之前使用 Prompt 包装 (`prepareFunctionCallPrompt`) 准备消息。 |
| 53 | + * **API 调用:** 向 LLM Provider 发出*单次*流式 API 调用。 |
| 54 | + * **流处理:** 迭代 Provider 的原生流数据块。 |
| 55 | + * **事件标准化:** 解析特定于 Provider 的数据块,并 `yield` 符合 **标准化流事件接口** 的事件。 |
| 56 | + * 解析文本、思考(`<think>` 或原生)、工具调用(原生或 `<function_call>`)、使用情况、错误、停止原因和图像数据。 |
| 57 | + * **输出:** 异步 `yield` `StandardizedStreamEvent` 对象。 |
| 58 | +* **辅助方法:** 包含特定于 Provider 的辅助函数,如 `formatMessages`, `convertToProviderTools`, `parseFunctionCalls`, `prepareFunctionCallPrompt` 等。 |
| 59 | + |
| 60 | +### 标准化流事件接口 (`LLMCoreStreamEvent`) |
| 61 | + |
| 62 | +```typescript |
| 63 | +// 建议定义在例如 src/main/presenter/llmProviderPresenter/streamEvents.ts |
| 64 | +interface LLMCoreStreamEvent { |
| 65 | + type: 'text' | 'reasoning' | 'tool_call_start' | 'tool_call_chunk' | 'tool_call_end' | 'error' | 'usage' | 'stop' | 'image_data'; |
| 66 | + content?: string; // 用于 type 'text' |
| 67 | + reasoning_content?: string; // 用于 type 'reasoning' |
| 68 | + tool_call_id?: string; // 用于 tool_call_* 类型 |
| 69 | + tool_call_name?: string; // 用于 tool_call_start |
| 70 | + tool_call_arguments_chunk?: string; // 用于 tool_call_chunk (流式参数) |
| 71 | + tool_call_arguments_complete?: string; // 用于 tool_call_end (可选,如果一次性可用) |
| 72 | + error_message?: string; // 用于 type 'error' |
| 73 | + usage?: { // 用于 type 'usage' |
| 74 | + prompt_tokens: number; |
| 75 | + completion_tokens: number; |
| 76 | + total_tokens: number; |
| 77 | + }; |
| 78 | + stop_reason?: 'tool_use' | 'max_tokens' | 'stop_sequence' | 'error' | 'complete'; // 用于 type 'stop' |
| 79 | + image_data?: { // 用于 type 'image_data' |
| 80 | + data: string; // Base64 编码的图像数据 |
| 81 | + mimeType: string; |
| 82 | + }; |
| 83 | +} |
| 84 | +``` |
| 85 | + |
| 86 | +## 优点 |
| 87 | + |
| 88 | +* **减少代码重复:** Agent 循环逻辑集中在 `index.ts`。 |
| 89 | +* **提高可维护性:** Provider 实现更简单,专注于 API 交互和事件标准化。 |
| 90 | +* **更容易添加新 Provider:** 添加新 Provider 只需根据标准化接口实现 `coreStream` 方法,无需复制复杂的 Agent 循环。 |
| 91 | +* **行为一致性:** 确保工具处理、思考内容解析和事件发送在所有 Provider 之间保持一致。 |
0 commit comments