【2】kilo 消息流转过程

Kilo 消息流转过程

本文档描述 Kilo 从用户发送消息到 AI 返回结果的完整流程


一、核心流程概览

复制代码
用户输入 → 消息预处理 → AI 推理循环 → 结果返回 → UI 更新

核心阶段:

  1. 用户输入处理:接收用户消息,处理 @mentions 和斜杠命令
  2. AI 推理循环 :构建提示词,调用 LLM,处理流式响应,在循环内部执行工具调用
  3. 上下文管理:监控 token 使用,自动压缩上下文
  4. 结果返回:更新 UI,持久化消息历史

二、完整流程的调用链路图

为了更清晰地展示各阶段之间的调用关系,这里提供一个完整的新建任务的调用链路图:

复制代码
用户输入
  ↓
Webview UI (vscode.postMessage)
  ↓
webviewMessageHandler (case "newTask")
  ↓
provider.createTask(text, images)
  ↓
new Task() → startTask(text, images)
  └─ initiateTaskLoop(userContent)
      ↓
recursivelyMakeClineRequests()  ← [递归入口]
  ├─ 状态检查、错误限制检查
  ├─ 构建系统提示词
  ├─ ensureContextFitsInWindow()
  │   ├─ 计算 token 数
  │   └─ [如果需要] condenseMessages()
  │       ├─ 调用 LLM 生成摘要
  │       └─ 更新 apiConversationHistory
  ├─ 选择 Provider
  ├─ 添加缓存控制
  └─ api.createMessage()
      ↓
处理流式响应(不同的类型,不同的处理)
  ├─ 文本 chunk (type: "text")
  │   ├─ 累积文本,增量解析为内容块
  │   └─ 调用 presentAssistantMessage(处理消息块) 
  │       ├─ 显示文本块
  │       ├─ 执行工具块
  │       │   ├─ 重复检测
  │       │   ├─ 自动批准检查
  │       │   ├─ 执行工具逻辑
  │       │   │   ├─ read_file / write_to_file / execute_command 等
  │       │   │   └─ attempt_completion(完成任务工具)
  │       │   │       ├─ 展示完成结果:say("completion_result")
  │       │   │       ├─ 等待用户响应:ask("completion_result")
  │       │   │       │   └─ 【阻塞点】pWaitFor(() => askResponse !== undefined)
  │       │   │       ├─ 用户在界面发送消息
  │       │   │       ├─ WebView → webviewMessageHandler
  │       │   │       ├─ handleWebviewAskResponse() 设置 askResponse
  │       │   │       ├─ ask() 返回用户反馈
  │       │   │       └─ 将反馈添加到 userMessageContent
  │       │   └─ 收集结果到 userMessageContent
  │       └─ 设置 userMessageContentReady = true(!触发Task继续循环)
  │
  ├─ Token 使用 chunk (type: "usage")
  │   ├─ 记录 inputTokens / outputTokens
  │   ├─ 记录 cacheWriteTokens / cacheReadTokens
  │   ├─ 记录 reasoningTokens(如有)
  │   └─ 累加到 totalCost
  │
  ├─ 推理 chunk (type: "reasoning")
  │   ├─ 累积推理文本
  │   └─ 显示推理过程
  │
  ├─ 工具调用 chunk (type: "tool_call_partial")
  │   ├─ 通过 NativeToolCallParser 处理
  │   └─ 累积工具调用参数
  │
  ├─ 错误 chunk (type: "error")
  │   └─ 记录并显示错误信息
  │
  └─ 其他 chunk 类型
      ├─ thinking_complete:思考完成标记
      ├─ grounding:搜索来源信息
      └─ tool_call_start/delta/end:工具调用事件
      ↓
等待工具执行完成(通过 userMessageContentReady = true 触发循环执行)
  await pWaitFor(() => userMessageContentReady)
      ↓
判断是否继续循环
  ├─ [如果有工具结果] 
  │   ├─ 将结果压入栈
  │   └─ continue(继续循环)← [返回递归入口]
  │
  └─ [如果没有工具结果] 结束循环
      ├─ 保存最终状态
      ├─ 更新 UI
      └─ 等待下一次输入

数据流转关键点

  1. 用户消息 → webviewMessageHandlerprovider.createTask()startTask() → 保存到 apiConversationHistory + clineMessages
  2. AI 响应(流式)→ 增量解析 → presentAssistantMessage() → 工具执行
  3. 工具结果 → userMessageContent → 压入栈 → apiConversationHistory(作为用户消息)
  4. 上下文压缩 → 更新 apiConversationHistory,在 clineMessages 中添加提示
  5. 用户响应 AI 询问 → webviewMessageHandler (case "askResponse") → Task.handleWebviewAskResponse() → 继续循环

三、核心流程时序图

工具系统 AI Provider Task 实例 Webview UI 用户 工具系统 AI Provider Task 实例 Webview UI 用户 阶段 1: 用户输入处理 阶段 2: AI 推理循环 alt [上下文过长] alt [需要用户批准] alt [包含工具调用] loop [处理流式响应] 继续下一轮循环 循环结束 alt [有工具结果] [无工具结果] loop [持续对话直到完成] 阶段 3: 结果返回 输入消息 1 发送消息 2 保存到双轨历史 3 构建系统提示词 4 生成摘要 5 返回摘要 6 压缩上下文 7 发起流式请求 8 Stream Chunk 9 解析内容块 10 实时显示文本 11 执行工具 12 请求批准 13 批准/拒绝 14 返回工具结果 15 将结果压入栈 16 持久化消息历史 17 更新 UI 18 显示完整响应 19


四、详细流程说明

本节将详细描述消息从用户输入到 AI 返回的完整流转过程,重点展示各阶段之间的调用关系和数据流转


4.1 AI 推理循环:核心对话引擎

核心方法:recursivelyMakeClineRequests()

这是整个系统的心脏,实现了递归式的 AI 对话循环。它会持续运行,直到 AI 完成任务或遇到需要用户干预的情况。

阶段 4.1.1:循环前的准备工作

步骤 1:状态检查

  • 检查任务是否被中止(this.abort)或暂停(this.paused
  • 如果是 → 直接返回,结束循环

步骤 2:错误限制检查

  • 检查 consecutiveMistakeCount(连续错误次数)
  • 如果超过限制(默认 3 次)→ 请求用户反馈,暂停循环

步骤 3:构建系统提示词

  • 调用 getSystemPrompt() 构建完整的系统提示词
  • 包含内容:
    • 角色定义和基础指令
    • 所有可用工具的描述和使用指南
    • 自定义指令和规则
    • 系统信息(OS、Shell、工作目录、当前时间等)

步骤 4:上下文长度检查与压缩

  • 调用 ensureContextFitsInWindow() 检查当前对话的 token 数
  • 如果超过阈值 (默认 80%):
    1. 调用 condenseMessages() 触发上下文压缩
    2. 压缩过程会调用 LLM 生成摘要(这是一个嵌套的 API 调用
    3. 压缩完成后,更新 apiConversationHistory
    4. clineMessages 中添加一条 "上下文已压缩" 的提示消息

数据流转

复制代码
apiConversationHistory → ensureContextFitsInWindow() 
→ [如果需要] condenseMessages() → LLM API(生成摘要)
→ 新的 apiConversationHistory
阶段 4.1.2:发起 API 请求

步骤 1:选择和初始化 Provider

  • 根据配置选择 AI Provider(Anthropic、OpenAI、Gemini 等)
  • 创建对应的 ApiHandler 实例

步骤 2:消息格式转换

  • 如果目标 Provider 需要特殊格式(如 OpenAI),调用 convertToOpenAiMessages() 进行转换
  • Anthropic 使用原生格式,无需转换

步骤 3:添加缓存控制(Anthropic 特有)

  • 为系统提示词添加 cache_control: { type: "ephemeral" } 标记
  • 为最近的用户消息添加缓存标记
  • 这样可以在后续请求中复用缓存,降低成本

步骤 4:创建流式请求

  • 调用 api.createMessage() 发起流式请求
  • 传入参数:
    • systemPrompt:系统提示词
    • messages:对话历史(apiConversationHistory
    • tools:可用工具列表

数据流转

复制代码
systemPrompt + apiConversationHistory + tools 
→ ApiHandler.createMessage() 
→ 流式 AsyncGenerator
阶段 4.1.3:处理流式响应(包含工具执行)

核心循环 :遍历 AsyncGenerator,实时处理每个 chunk

typescript 复制代码
for await (const chunk of stream) {
  switch (chunk.type) {
    case "text":
      // 累积文本
      assistantMessage += chunk.text
      
      // 🔑 关键:增量解析为内容块(文本块 + 工具调用块)
      this.assistantMessageContent = 
        this.assistantMessageParser.processChunk(chunk.text)
      
      // 🔑 关键:立即展示和执行工具(在循环内部)
      presentAssistantMessage(this)
      break
      
    case "usage":
      // 记录 token 使用
      break
  }
}

Chunk 类型处理

  1. 文本 chunktype: "text"):

    • 累积到 assistantMessage 变量
    • 增量解析 :调用 assistantMessageParser.processChunk() 解析为内容块
    • 立即执行 :调用 presentAssistantMessage() 展示文本和执行工具
    • 数据流转chunk.text → 解析为内容块 → presentAssistantMessage() → 工具执行
  2. Token 使用 chunktype: "usage"):

    • 记录输入/输出 token 数
    • 记录缓存读写情况(Anthropic)
    • 计算成本并累加到 this.totalCost
    • 数据流转chunk.usagethis.totalCost + this.totalInputTokens + this.totalOutputTokens
  3. 推理 chunktype: "reasoning"):

    • 累积推理文本到专门的变量
    • 显示推理过程(DeepSeek R1 等支持推理的模型)
    • 可能包含 signature 字段用于验证(Anthropic extended thinking)
  4. 工具调用 chunktype: "tool_call_partial"):

    • 通过 NativeToolCallParser 处理和缓冲
    • 累积工具调用的 id、name、arguments
    • 最终组装成完整的工具调用块供 presentAssistantMessage() 执行
  5. 错误 chunktype: "error"):

    • 记录错误信息
    • 显示给用户并中断当前流程
  6. 其他 chunk 类型

    • thinking_complete:标记思考完成,包含验证签名
    • grounding:搜索引擎返回的来源信息
    • tool_call_start/delta/end:工具调用的生命周期事件

关键理解

  • 工具调用的解析和执行都在流式响应循环内部完成
  • presentAssistantMessage() 在每个 text chunk 到达时被调用
  • 工具执行是同步阻塞的,执行完成后才继续处理下一个 chunk
阶段 4.1.4:presentAssistantMessage() 的工具执行逻辑

核心方法presentAssistantMessage(cline: Task)

这个方法在流式响应循环内部被调用,负责:

  1. 遍历已解析的内容块(assistantMessageContent
  2. 显示文本块
  3. 执行工具调用块
  4. 收集工具结果到 userMessageContent

执行流程

typescript 复制代码
export async function presentAssistantMessage(cline: Task) {
  // 遍历所有内容块
  for (let i = cline.currentStreamingContentIndex; 
       i < cline.assistantMessageContent.length; 
       i++) {
    
    const block = cline.assistantMessageContent[i]
    
    if (block.type === "text") {
      // 显示文本块
      await cline.say("text", block.content)
    }
    
    if (block.type === "tool_use") {
      // 🔑 在这里执行工具!
      
      // 1. 安全检查

      // 重复检测
      const repetitionCheck = cline.toolRepetitionDetector.check(block)
      if (!repetitionCheck.allowExecution) {
        // 检测到重复,跳过执行
        break
      }
      
      // 2. 自动批准检查
      const needsApproval = await checkAutoApproval(block)
      if (needsApproval) {
        const { response } = await cline.ask("tool", ...)
        if (response !== "yesButtonClicked") {
          // 用户拒绝,设置标志并跳过
          cline.didRejectTool = true
          break
        }
      }
      
      // 3. 执行工具
      switch (block.name) {
        case "read_file":
          result = await readFileTool(cline, block, ...)
          break
        case "write_to_file":
          result = await writeToFileTool(cline, block, ...)
          break
        case "execute_command":
          result = await executeCommandTool(cline, block, ...)
          break
        // ... 其他工具
      }
      
      // 4. 收集工具结果
      cline.userMessageContent.push({
        type: "tool_result",
        tool_use_id: block.id,
        content: result
      })
      
      // 标记已使用工具(每次只能执行一个工具)
      cline.didAlreadyUseTool = true
    }
  }
  
  // 5. 所有块处理完成后设置就绪标志
  if (所有块都完成) {
    cline.userMessageContentReady = true
  }
}
阶段 4.1.5:等待工具执行完成并判断是否继续循环

步骤 1:等待工具执行完成

typescript 复制代码
// 流式响应处理完毕后,等待工具执行完成
await pWaitFor(() => this.userMessageContentReady)

步骤 2:判断是否继续循环

typescript 复制代码
// 检查是否有工具结果需要返回给 AI
if (this.userMessageContent.length > 0) {
  // 将工具结果压入栈,触发下一轮循环
  stack.push({
    userContent: [...this.userMessageContent],
    includeFileDetails: false
  })
  
  // 继续下一轮循环
  continue
}

// 没有工具结果,循环结束

关键理解

  • 工具执行在流式响应循环内部 通过 presentAssistantMessage() 完成

  • 工具结果被收集到 userMessageContent

  • 工具结果作为新的"用户消息"压入栈,触发下一轮 AI 推理

  • 这样形成了一个完整的递归循环:

    AI 推理 → 流式响应 → 解析工具调用 → 执行工具 → 收集结果
    → 压入栈 → AI 推理 → ... → attempt_completion(最终答案)...


4.2 结果返回与 UI 更新:循环的终点

当 AI 推理循环结束时(无工具调用或任务完成),系统进入最终阶段。

阶段 4.2.1:保存最终状态

持久化内容

  1. 消息历史

    • api_conversation_history.json:API 层历史
    • ui_messages.json:UI 层历史
  2. 任务元数据

    • 任务 ID、创建时间、完成时间
    • Token 使用统计(输入/输出/缓存)
    • 总成本
    • 任务摘要(如果有)
  3. 上下文追踪

    • 已访问的文件列表
    • 已执行的命令列表
    • 浏览器状态(如果使用了浏览器工具)

数据流转

复制代码
apiConversationHistory + clineMessages + taskMetadata 
→ 本地文件系统(JSON 文件)
阶段 4.2.2:更新 UI

更新内容

  1. 消息列表

    • 显示完整的对话历史(基于 clineMessages
    • 包括用户消息、AI 响应、工具执行记录等
  2. Token 统计

    • 输入 token 数
    • 输出 token 数
    • 缓存读取/写入 token 数(Anthropic)
    • 总成本
  3. 任务状态

    • 任务是否完成
    • 是否有错误
    • 是否需要用户反馈
  4. 工具执行记录

    • 显示已执行的工具列表
    • 每个工具的执行状态(成功/失败)
    • 工具执行结果摘要

数据流转

复制代码
clineMessages + taskMetadata 
→ Webview UI(通过 postMessage)
→ 用户界面更新
阶段 4.2.3:等待下一次输入

状态

  • Task 实例保持活跃(不销毁)
  • 等待用户发送新消息
  • 用户可以:
    • 继续对话(发送新消息)
    • 结束任务(关闭对话)
    • 查看历史记录

控制流转移

  • 如果用户发送新消息 → 返回阶段 2.1(用户输入处理)
  • 如果用户结束任务 → 销毁 Task 实例,清理资源

五、双轨消息系统详解

5.1 双轨制概述

Kilo 维护两套独立但同步的消息历史系统:

  1. api_conversation_history.json - API 层历史(发送给 LLM 的实际对话)
  2. ui_messages.json - UI 层历史(在界面显示的所有消息)

存储位置:

复制代码
{globalStoragePath}/tasks/{taskId}/
├── api_conversation_history.json
└── ui_messages.json

5.2 为什么需要双轨制?

关注点分离
  • API 层:专注于与 LLM 的技术交互,确保 API 调用的正确性和上下文管理
  • UI 层:专注于用户体验,展示丰富的交互信息和状态
不同的数据需求
  • API 层:需要简洁、标准化的消息格式(Anthropic MessageParam)
  • UI 层:需要丰富的元数据、状态信息、进度指示等(ClineMessage)
性能优化
  • API 层:可以进行上下文压缩以减少 token 使用
  • UI 层:保持完整历史,用于调试和审计

5.3 api_conversation_history.json 详解

数据结构
typescript 复制代码
type ApiMessage = Anthropic.MessageParam & { 
  ts?: number         // 时间戳
  isSummary?: boolean // 是否为压缩摘要
}
实际示例

基于 Button 组件创建场景,api_conversation_history.json 的内容:

json 复制代码
[
  {
    "role": "user",
    "content": [
      {
        "type": "text",
        "text": "帮我创建一个 Button 组件,要支持不同的尺寸和颜色"
      }
    ],
    "ts": 1736654400000
  },
  {
    "role": "assistant",
    "content": [
      {
        "type": "text",
        "text": "好的,让我先查看一下项目结构,了解现有的组件规范。"
      },
      {
        "type": "tool_use",
        "id": "toolu_01A09q90qw90lq917835lq9",
        "name": "read_file",
        "input": {
          "path": "src/components"
        }
      }
    ],
    "ts": 1736654401000
  },
  {
    "role": "user",
    "content": [
      {
        "type": "text",
        "text": "[read_file for 'src/components'] Result:"
      },
      {
        "type": "text",
        "text": "<files>\n<file>\n<path>src/components</path>\n<content>\nCard.tsx\nInput.tsx\nModal.tsx\n</content>\n</file>\n</files>"
      }
    ],
    "ts": 1736654402000
  },
  {
    "role": "assistant",
    "content": [
      {
        "type": "text",
        "text": "我看到了现有的组件。让我查看一下其中一个组件的实现,了解代码风格。"
      },
      {
        "type": "tool_use",
        "id": "toolu_01B19r91rw91mr928946mr0",
        "name": "read_file",
        "input": {
          "path": "src/components/Card.tsx"
        }
      }
    ],
    "ts": 1736654403000
  },
  {
    "role": "user",
    "content": [
      {
        "type": "text",
        "text": "[read_file for 'src/components/Card.tsx'] Result:"
      },
      {
        "type": "text",
        "text": "<files>\n<file>\n<path>src/components/Card.tsx</path>\n<content lines=\"1-25\">\nimport React from 'react'\nimport { cn } from '@/lib/utils'\n\ninterface CardProps {\n  className?: string\n  children: React.ReactNode\n}\n\nexport const Card: React.FC<CardProps> = ({ className, children }) => {\n  return (\n    <div className={cn('rounded-lg border bg-card p-4', className)}>\n      {children}\n    </div>\n  )\n}\n</content>\n</file>\n</files>"
      }
    ],
    "ts": 1736654404000
  },
  {
    "role": "assistant",
    "content": [
      {
        "type": "text",
        "text": "好的,我了解了代码风格。现在让我创建 Button 组件。"
      },
      {
        "type": "tool_use",
        "id": "toolu_01C29s02sw02ns039057ns1",
        "name": "write_to_file",
        "input": {
          "path": "src/components/Button.tsx",
          "content": "import React from 'react'\nimport { cn } from '@/lib/utils'\n\ninterface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n  variant?: 'primary' | 'secondary' | 'outline'\n  size?: 'sm' | 'md' | 'lg'\n  children: React.ReactNode\n}\n\nexport const Button: React.FC<ButtonProps> = ({\n  variant = 'primary',\n  size = 'md',\n  className,\n  children,\n  ...props\n}) => {\n  const variantStyles = {\n    primary: 'bg-blue-600 text-white hover:bg-blue-700',\n    secondary: 'bg-gray-600 text-white hover:bg-gray-700',\n    outline: 'border border-gray-300 hover:bg-gray-100'\n  }\n\n  const sizeStyles = {\n    sm: 'px-3 py-1.5 text-sm',\n    md: 'px-4 py-2 text-base',\n    lg: 'px-6 py-3 text-lg'\n  }\n\n  return (\n    <button\n      className={cn(\n        'rounded-lg font-medium transition-colors',\n        variantStyles[variant],\n        sizeStyles[size],\n        className\n      )}\n      {...props}\n    >\n      {children}\n    </button>\n  )\n}"
        }
      }
    ],
    "ts": 1736654405000
  },
  {
    "role": "user",
    "content": [
      {
        "type": "text",
        "text": "[write_to_file for 'src/components/Button.tsx'] Result:"
      },
      {
        "type": "text",
        "text": "Successfully created src/components/Button.tsx with 42 lines of code."
      }
    ],
    "ts": 1736654406000
  },
  {
    "role": "assistant",
    "content": [
      {
        "type": "text",
        "text": "我已经成功创建了 Button 组件!这个组件支持:\n\n1. **三种样式变体:**\n   - `primary`:蓝色主按钮\n   - `secondary`:灰色次要按钮\n   - `outline`:边框按钮\n\n2. **三种尺寸:**\n   - `sm`:小尺寸\n   - `md`:中等尺寸(默认)\n   - `lg`:大尺寸\n\n3. **完全的类型安全:** 继承了所有原生 button 的属性\n\n使用示例:\n```tsx\n<Button variant=\"primary\" size=\"lg\">\n  点击我\n</Button>\n```\n\n还需要我做其他调整吗?"
      }
    ],
    "ts": 1736654407000
  }
]
特点总结
  • 标准格式:严格遵循 Anthropic API 的 MessageParam 格式
  • 角色明确 :只有 userassistant 两种角色
  • 工具表示 :工具调用作为 assistant 消息的 content 块,工具结果作为 user 消息
  • 时间戳 :每条消息都有 ts 字段记录时间
  • 压缩支持 :支持 isSummary 标记压缩后的摘要消息

5.4 ui_messages.json 详解

数据结构
typescript 复制代码
type ClineMessage = {
  ts: number                          // 时间戳
  type: "ask" | "say"                 // 消息类型
  ask?: ClineAsk                      // 询问类型
  say?: ClineSay                      // 陈述类型
  text?: string                       // 文本内容
  images?: string[]                   // 图片数组
  partial?: boolean                   // 是否部分消息
  reasoning?: string                  // 推理内容
  conversationHistoryIndex?: number   // 对话历史索引
  checkpoint?: Record<string, unknown> // 检查点
  progressStatus?: ToolProgressStatus // 进度状态
  contextCondense?: ContextCondense   // 上下文压缩信息
  isProtected?: boolean               // 是否受保护
  apiProtocol?: "openai" | "anthropic" // API 协议
  isAnswered?: boolean                // 是否已回答
  metadata?: {                        // 元数据
    gpt5?: Gpt5Metadata
    kiloCode?: KiloCodeMetaData
  }
}
实际示例

基于同一场景,ui_messages.json 的内容:

json 复制代码
[
  {
    "ts": 1736654400000,
    "type": "say",
    "say": "text",
    "text": "帮我创建一个 Button 组件,要支持不同的尺寸和颜色",
    "conversationHistoryIndex": 0
  },
  {
    "ts": 1736654401000,
    "type": "say",
    "say": "api_req_started",
    "text": "正在思考..."
  },
  {
    "ts": 1736654401500,
    "type": "say",
    "say": "text",
    "text": "好的,让我先查看一下项目结构,了解现有的组件规范。",
    "partial": false
  },
  {
    "ts": 1736654401600,
    "type": "ask",
    "ask": "tool",
    "text": "{\"tool\":\"readFile\",\"path\":\"src/components\",\"content\":\"/Users/user/project/src/components\"}"
  },
  {
    "ts": 1736654402000,
    "type": "say",
    "say": "text",
    "text": "[read_file for 'src/components'] Result:",
    "conversationHistoryIndex": 2
  },
  {
    "ts": 1736654402100,
    "type": "say",
    "say": "command_output",
    "text": "<files>\n<file>\n<path>src/components</path>\n<content>\nCard.tsx\nInput.tsx\nModal.tsx\n</content>\n</file>\n</files>"
  },
  {
    "ts": 1736654403000,
    "type": "say",
    "say": "text",
    "text": "我看到了现有的组件。让我查看一下其中一个组件的实现,了解代码风格。",
    "partial": false
  },
  {
    "ts": 1736654403100,
    "type": "ask",
    "ask": "tool",
    "text": "{\"tool\":\"readFile\",\"path\":\"src/components/Card.tsx\",\"content\":\"/Users/user/project/src/components/Card.tsx\"}"
  },
  {
    "ts": 1736654404000,
    "type": "say",
    "say": "text",
    "text": "[read_file for 'src/components/Card.tsx'] Result:",
    "conversationHistoryIndex": 4
  },
  {
    "ts": 1736654404100,
    "type": "say",
    "say": "command_output",
    "text": "<files>\n<file>\n<path>src/components/Card.tsx</path>\n<content lines=\"1-25\">\nimport React from 'react'\nimport { cn } from '@/lib/utils'\n\ninterface CardProps {\n  className?: string\n  children: React.ReactNode\n}\n\nexport const Card: React.FC<CardProps> = ({ className, children }) => {\n  return (\n    <div className={cn('rounded-lg border bg-card p-4', className)}>\n      {children}\n    </div>\n  )\n}\n</content>\n</file>\n</files>"
  },
  {
    "ts": 1736654405000,
    "type": "say",
    "say": "text",
    "text": "好的,我了解了代码风格。现在让我创建 Button 组件。",
    "partial": false
  },
  {
    "ts": 1736654405100,
    "type": "ask",
    "ask": "tool",
    "text": "{\"tool\":\"newFileCreated\",\"path\":\"src/components/Button.tsx\",\"diff\":\"+ import React from 'react'\\n+ import { cn } from '@/lib/utils'\\n+ \\n+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\\n+   variant?: 'primary' | 'secondary' | 'outline'\\n+   size?: 'sm' | 'md' | 'lg'\\n+   children: React.ReactNode\\n+ }\\n+ \\n+ export const Button: React.FC<ButtonProps> = ({\\n+   variant = 'primary',\\n+   size = 'md',\\n+   className,\\n+   children,\\n+   ...props\\n+ }) => {\\n+   const variantStyles = {\\n+     primary: 'bg-blue-600 text-white hover:bg-blue-700',\\n+     secondary: 'bg-gray-600 text-white hover:bg-gray-700',\\n+     outline: 'border border-gray-300 hover:bg-gray-100'\\n+   }\\n+ \\n+   const sizeStyles = {\\n+     sm: 'px-3 py-1.5 text-sm',\\n+     md: 'px-4 py-2 text-base',\\n+     lg: 'px-6 py-3 text-lg'\\n+   }\\n+ \\n+   return (\\n+     <button\\n+       className={cn(\\n+         'rounded-lg font-medium transition-colors',\\n+         variantStyles[variant],\\n+         sizeStyles[size],\\n+         className\\n+       )}\\n+       {...props}\\n+     >\\n+       {children}\\n+     </button>\\n+   )\\n+ }\"}"
  },
  {
    "ts": 1736654406000,
    "type": "say",
    "say": "text",
    "text": "[write_to_file for 'src/components/Button.tsx'] Result:",
    "conversationHistoryIndex": 6
  },
  {
    "ts": 1736654406100,
    "type": "say",
    "say": "command_output",
    "text": "Successfully created src/components/Button.tsx with 42 lines of code."
  },
  {
    "ts": 1736654407000,
    "type": "say",
    "say": "text",
    "text": "我已经成功创建了 Button 组件!这个组件支持:\n\n1. **三种样式变体:**\n   - `primary`:蓝色主按钮\n   - `secondary`:灰色次要按钮\n   - `outline`:边框按钮\n\n2. **三种尺寸:**\n   - `sm`:小尺寸\n   - `md`:中等尺寸(默认)\n   - `lg`:大尺寸\n\n3. **完全的类型安全:** 继承了所有原生 button 的属性\n\n使用示例:\n```tsx\n<Button variant=\"primary\" size=\"lg\">\n  点击我\n</Button>\n```\n\n还需要我做其他调整吗?",
    "partial": false
  },
  {
    "ts": 1736654407100,
    "type": "say",
    "say": "api_req_finished",
    "text": "API 请求完成"
  }
]
特点总结
  • 丰富类型 :包含 api_req_startedtexttoolcommand_outputapi_req_finished 等多种消息类型
  • 交互记录 :记录了所有用户交互(ask 类型)和系统响应(say 类型)
  • 工具详情:工具调用包含完整的参数和 diff 预览
  • 状态追踪:记录 API 请求状态、工具执行进度等
  • 元数据丰富:包含检查点、进度状态、上下文压缩信息等

5.5 两套历史的对比

特性 api_conversation_history.json ui_messages.json
用途 发送给 LLM 的对话上下文 在 UI 中显示的完整历史
格式 Anthropic MessageParam ClineMessage
角色 user / assistant ask / say
工具调用 作为 assistant 消息的 content 块 作为独立的 ask 消息
工具结果 作为 user 消息的 content 块 作为独立的 say 消息
压缩 支持,删除早期消息并替换为摘要 不压缩,保留完整历史
元数据 最小化(ts, isSummary) 丰富(进度、状态、检查点等)
消息数量 较少(压缩后) 较多(完整记录)
文件大小 较小 较大

5.6 双轨同步机制

用户消息同步
typescript 复制代码
// 在 Task.ts 中
async handleWebviewAskResponse(askResponse: ClineAskResponse, text?: string, images?: string[]) {
  // 1. 添加到 UI 历史
  await this.say("user_feedback", text, images)
  
  // 2. 添加到 API 历史
  await this.addToApiConversationHistory({
    role: "user",
    content: [{ type: "text", text }]
  })
}
AI 响应同步
typescript 复制代码
// 在 recursivelyMakeClineRequests 中
for await (const chunk of stream) {
  if (chunk.type === "text") {
    // 1. 实时更新 UI(通过 presentAssistantMessage)
    await presentAssistantMessage(this)
    
    // 2. 累积完整响应
    assistantMessage += chunk.text
  }
}

// 3. 添加到 API 历史
await this.addToApiConversationHistory({
  role: "assistant",
  content: [{ type: "text", text: assistantMessage }]
})
工具执行同步
typescript 复制代码
// 在 presentAssistantMessage.ts 中
const pushToolResult = (content: ToolResponse) => {
  // 1. 添加到 userMessageContent(稍后会添加到 API 历史)
  cline.userMessageContent.push({ type: "text", text: `${toolDescription()} Result:` })
  cline.userMessageContent.push({ type: "text", text: content })
  
  // 2. 同时更新 UI 历史
  await cline.say("command_output", content)
}

5.7 上下文压缩的双轨处理

背景场景

用户与 AI 进行了长时间对话,实现了一个完整的用户管理系统,包括登录、注册、权限管理等功能。对话历史已经积累了 53 条消息,token 使用率达到 84%,触发了自动压缩。

压缩前

api_conversation_history.json(53 条消息):

json 复制代码
[
  // 前 50 条原始消息(涵盖登录、注册、权限管理等功能的实现)
  { "role": "user", "content": "帮我创建一个用户登录功能", "ts": 1704700000000 },
  { "role": "assistant", "content": "好的,我会帮你创建用户登录功能...", "ts": 1704700001000 },
  { "role": "user", "content": "使用 JWT 进行身份验证", "ts": 1704700002000 },
  { "role": "assistant", "content": "明白了,我会使用 JWT...", "ts": 1704700003000 },
  // ... 中间 45 条消息(消息 5-49)涵盖了:
  //     - 实现 JWT 认证逻辑
  //     - 创建用户注册接口
  //     - 实现权限管理系统
  //     - 添加用户角色功能
  { "role": "user", "content": "添加用户注册功能", "ts": 1704700049000 },
  
  // 最后 3 条消息(保持最新的对话上下文)
  { "role": "user", "content": "现在添加密码重置功能", "ts": 1704700050000 },
  { "role": "assistant", "content": "好的,让我添加密码重置功能...", "ts": 1704700051000 },
  { "role": "user", "content": "发送重置邮件时使用 SendGrid", "ts": 1704700052000 }
]

Token 统计

  • 总消息数:53 条
  • Token 使用:45,000 / 50,000 (90%)
  • 触发压缩阈值:80%

ui_messages.json(约 150 条消息,包含所有交互细节):

  • 用户消息:20 条
  • API 请求状态:40 条(started/finished)
  • AI 文本响应:30 条
  • 工具调用请求:25 条(包含参数和 diff)
  • 工具执行结果:25 条
  • 其他状态消息:10 条
压缩后

api_conversation_history.json(54 条消息 - 保留所有原始消息 + 新增摘要):

typescript 复制代码
[
	// 前 50 条原始消息(保持不变)
	{ role: "user", content: "帮我创建一个用户登录功能", ts: 1704700000000 },
	{ role: "assistant", content: "好的,我会帮你创建用户登录功能...", ts: 1704700001000 },
	{ role: "user", content: "使用 JWT 进行身份验证", ts: 1704700002000 },
	{ role: "assistant", content: "明白了,我会使用 JWT...", ts: 1704700003000 },
	// ... 中间 45 条消息(消息 5-49)
	{ role: "user", content: "添加用户注册功能", ts: 1704700049000 },

	// 新增的摘要消息
	{
		role: "assistant",
		content: "## 对话摘要\n\n### 1. Previous Conversation\n...",
		ts: 1704700050000,
		isSummary: true  // 标记为摘要
	},

	// 保留的最后 3 条消息(保持不变)
	{ role: "user", content: "现在添加密码重置功能", ts: 1704700050000 },
	{ role: "assistant", content: "好的,让我添加密码重置功能...", ts: 1704700051000 },
	{ role: "user", content: "发送重置邮件时使用 SendGrid", ts: 1704700052000 },
]

传递给 LLM 的消息 (通过 getMessagesSinceLastSummary() 筛选后只有 5 条):

typescript 复制代码
[
	{ role: "user", content: "Please continue from the following summary:" },  // 提示消息
	{ role: "assistant", content: "## 对话摘要\n...", isSummary: true },      // 摘要消息
	{ role: "user", content: "现在添加密码重置功能" },                          // 最近消息 1
	{ role: "assistant", content: "好的,让我添加密码重置功能..." },            // 最近消息 2
	{ role: "user", content: "发送重置邮件时使用 SendGrid" },                  // 最近消息 3
]

ui_messages.json(新增压缩提示):

json 复制代码
[
  // ... 原有的约 xxx 条消息都保留 ...
  
  // 新增压缩提示消息
  {
    "ts": 1704700053000,
    "type": "say",
    "say": "condense_context",
    "text": "上下文已压缩:从 45,000 tokens 减少到 12,000 tokens",
    "contextCondense": {
      "summary": "用户请求创建用户管理系统,包括登录、注册、权限管理等功能...",
      "cost": 0.15,
      "newContextTokens": 12000,
      "prevContextTokens": 45000
    }
  }
]
压缩效果对比
维度 压缩前 压缩后 说明
本地存储(apiConversationHistory)
消息数量 53 条 54 条 ↑ 增加 1 条摘要消息
存储完整性 ✅ 完整 ✅ 完整 所有原始消息都保留
传递给 LLM
消息数量 53 条 5 条 ↓ 减少 48 条(90.6%)
Token 使用 45,000 12,000 ↓ 减少 33,000(73.3%)
上下文使用率 90% 24% ↓ 降低 66%
语义信息
关键信息 ✅ 完整 ✅ 完整 通过摘要保留
技术细节 ✅ 完整 ✅ 完整 摘要包含代码和文件
任务上下文 ✅ 完整 ✅ 完整 摘要包含下一步计划
关键理解

存储层(apiConversationHistory)

  • 所有原始消息都保留(53 条)
  • 新增 1 条摘要消息 (标记 isSummary: true
  • 总消息数:54 条(53 + 1)
  • 完整的对话历史,便于回溯和调试

传输层(传递给 LLM)

  • 只传递必要的上下文(摘要 + 最近 3 条消息)
  • 大幅减少 token 使用(从 53 条减少到 5 条)
  • 通过 getMessagesSinceLastSummary() 智能筛选
  • 保持对话连续性和语义完整性

双层架构的优势

  1. 存储层

    • 保留所有原始消息和摘要消息
    • 完整的对话轨迹,便于回溯和调试
    • 消息数量会随着压缩而增加(每次压缩 +1)
  2. 传输层

    • 只传递必要的上下文(摘要 + 最近消息)
    • 大幅减少 token 使用和成本
    • 通过智能筛选保持对话质量

为什么不删除消息?

  1. 完整性:保留完整对话历史,便于用户回顾
  2. 可追溯:出现问题时可以查看完整上下文
  3. 灵活性:可以根据需要调整传输策略
  4. 用户体验:UI 显示完整对话,用户不会感到信息丢失
  5. 调试友好:开发者可以查看完整的消息流转过程

5.8 双轨系统的核心价值

  • API 历史:优化 token 使用,支持上下文压缩,确保高效的 LLM 交互
  • UI 历史:保留完整记录,提供丰富的用户体验和调试能力
  • 同步机制:确保两套历史始终保持一致,支持消息编辑、任务恢复等功能
  • 灵活扩展:两套系统可以独立演进,互不影响

六、关键特性说明

6.1 递归式对话

特点

  • 使用栈结构管理请求队列
  • 支持工具执行后自动继续对话
  • AI 可以多次调用工具直到完成任务

优势

  • 无需用户干预,AI 自主完成复杂任务
  • 支持多步骤推理和决策
  • 自然的对话流程

6.2 流式响应

特点

  • 使用 AsyncGenerator 实现流式处理
  • 实时显示 AI 生成的文本
  • 支持中断和取消

优势

  • 降低用户等待时间
  • 提升交互体验
  • 及时发现问题

6.3 智能保护机制

连续错误检测

  • 记录连续错误次数
  • 超过限制时请求用户反馈
  • 防止无限重试

工具重复检测

  • 检测连续相同的工具调用
  • 防止 AI 陷入死循环
  • 提示用户尝试不同方法

自动批准限制

  • 基于请求次数限制
  • 基于成本限制
  • 防止过度消耗

6.4 上下文管理

自动压缩

  • 监控 token 使用率
  • 自动触发压缩
  • 保持对话连续性

双轨制历史

  • API 层:优化的对话历史
  • UI 层:完整的交互记录
  • 分离关注点

6.5 多 Provider 支持

统一接口

  • ApiHandler 接口抽象
  • 支持 Anthropic、OpenAI、Gemini 等
  • 自动格式转换

Provider 特性

  • Anthropic:Prompt Caching
  • OpenAI:o1 推理模式
  • 统一的流式响应处理

七、总结

Kilo 核心消息流转过程体现了以下设计理念:

  1. 用户体验优先:流式响应、实时反馈、智能保护
  2. 自主性:递归式对话,AI 自主完成复杂任务
  3. 可靠性:错误检测、重复检测、自动批准限制
  4. 可扩展性:多 Provider 支持、工具系统、MCP 协议
  5. 性能优化:上下文压缩、Worker Pool、懒加载

整个流程从用户输入到 AI 返回结果,形成了一个完整的闭环,支持持续的对话和工具执行,最终帮助用户完成各种编程任务。

相关推荐
Devlive 开源社区2 小时前
技术日报|微软数据科学课程登顶日增651星,AI编程GUI工具AionUi与React视频制作工具霸榜前三
react.js·microsoft·ai编程
大势智慧2 小时前
大势智慧与土耳其合作发展中心、蕾奥规划签署土耳其智慧城市项目战略合作协议
人工智能·ai·智慧城市·三维建模·实景三维·发展趋势·创新
小学生波波2 小时前
HarmonyOS6 - 鸿蒙AI卡证识别实战案例
ai·harmonyos·鸿蒙ai·卡证识别
啊阿狸不会拉杆2 小时前
《机器学习》 第 9 章 - 深度强化学习
人工智能·算法·机器学习·计算机视觉·ai·ml
studymary3 小时前
04-Agent代理模式深度解析
ai编程
铮铭3 小时前
AI编程能力的边界
ai编程
运维栈记3 小时前
Claude Code 与 MCP 服务器使用指南
运维·ai
CoderJia程序员甲3 小时前
GitHub 热榜项目 - 日榜(2026-01-23)
人工智能·ai·大模型·github·ai教程
DS随心转小程序3 小时前
ChatGPT和Gemini转pdf
人工智能·ai·chatgpt·pdf·豆包·deepseek·ds随心转