喜欢对技术刨根问底,却总是被打退,晕,再上,屡败屡战

注定要与程序打交道,毫无疑问我喜欢编程,而且适合

导航

kimi-code 深度掌握系列文章-上下文管理:压缩与记忆(8)

Posted on 2026-08-09 10:32  Carmack  阅读(54)  评论(0)    收藏  举报

1. 上下文管理的挑战

上下文(Context)是 Agent 的「工作记忆」——它决定了模型在每个 turn 中能「看到」多少信息,从而影响推理质量和决策能力。然而,上下文管理并非简单地「把所有历史消息塞给模型」,而是一道在容量、成本、准确性之间持续权衡的优化问题。

1.1 上下文窗口的物理限制

尽管主流 LLM 的上下文窗口已经扩展到了 128K、200K 甚至 1M token,但窗口的本质仍然是固定的有限资源。对于 kimi-code 这种可以执行长时间任务的 Agent 来说,一个 session 可能包含数百个 turn,累积的文本量远超单次可容纳的范围。当上下文窗口被占满时,模型将无法接收新的输入——这意味着 Agent 会「失忆」,丧失对对话前因后果的理解能力。

1.2 Token 成本的经济账

上下文长度不仅影响能力,还直接影响运行成本。当前主流 LLM 的定价模型通常将输入 token 和输出 token 分开计费,而输入 token 的数量与上下文长度成正比。一个有 50 轮对话的 session,如果每轮平均 2000 token,仅在输入端的消耗就达到 10 万 token——而这还没算工具调用返回的大量文本数据。

场景 平均 token / turn 50 turn 累计输入 token 估算输入成本(参考)
简单问答 ~800 ~40,000 可忽略
代码分析(含文件内容) ~3,000 ~150,000 中等
多文件重构(含工具返回) ~8,000 ~400,000 较高
长篇调试(含大量日志) ~15,000+ ~750,000+

​设计目标:​上下文管理不是「越少越好」,而是在 token 预算信息完整性 之间找到最优平衡——保留关键信息,裁剪冗余噪音。

1.3 注意力衰减

LLM 的注意力机制并非均匀分布——研究与实践表明,模型对上下文靠前和靠后的内容关注度更高,而对中间部分容易产生注意力衰减(又称"迷失在中间"效应)。这意味着即使上下文窗口足够大,将越积越多的历史全部堆进去,远端的关键信息也可能被模型忽略。

上下文管理需要解决的三个核心问题是:

  1. ​容量约束:​Token 总数不能超过模型上限
  2. ​成本约束:​在保证质量的前提下,尽可能减少 token 消耗
  3. ​信息密度约束:​上下文中每一段内容都应该「物有所值」,对推理有实质贡献

1.4 Prompt Cache 的价值

多数 LLM 提供商支持 Prompt Cache——对上下文中不变的前缀部分进行缓存,后续请求只需为增量部分付费。这意味着将稳定内容放在前端、变化内容放在后端可以显著降低成本。这是接下来要讨论的上下文字段排序和 compaction 策略的重要约束。

2. ContextMemory 设计

ContextMemory 是 kimi-code 上下文系统的核心数据结构,承担着「对话历史的存储器」角色。它不是简单的消息数组,而是一个具备容量感知、优先级排序和压缩能力的智能缓存。

2.1 数据结构

ContextMemory 的核心是一个按时间顺序排列的消息列表,每条消息包含角色标记、内容体、以及用于压缩决策的元数据:

// ContextMemory 的核心数据模型(简化伪代码)
interface ContextMemory {
  // 按时间顺序排列的消息列表
  messages: Message[];

  // 当前累计 token 数(缓存值,避免每次重新计数)
  tokenCount: number;

  // 元信息
  metadata: {
    sessionId: string;
    totalTurns: number;
    lastCompactionAt: number;
    compactionCount: number;
  };
}

interface Message {
  id: string;
  role: 'system' | 'user' | 'assistant' | 'tool';
  content: string | ToolResult[];
  timestamp: number;

  // —— 压缩策略用到的关键字段 ——
  // 优先级:数值越高,越不容易被压缩裁剪
  priority: MessagePriority;

  // 是否可以被压缩(某些特殊消息需要强制保留)
  compressible: boolean;

  // 消息的「保质期」——过期后可安全压缩
  ttl?: number;

  // 工具调用相关
  toolCallId?: string;
  toolName?: string;
}

enum MessagePriority {
  Critical = 100,   // 系统关键信息,永远不压缩
  High     = 75,    // 用户指令、工具调用的关键结果
  Normal   = 50,    // 一般对话内容
  Low      = 25,    // 中间推理步骤、冗余信息
  Trivial  = 0,     // 可随时丢弃的确认消息
}

2.2 Token 计数机制

精确的 token 计数是压缩决策的前提。kimi-code 使用 tiktoken 作为 tokenizer(与多数 OpenAI 模型使用的分词器一致),对消息内容进行实时计数。由于 token 计数本身也有开销,系统采用以下优化策略:

  • ​增量计数:​新增消息时只计算该消息的 token 数并累加到总额,而非每次重新计算全集
  • ​缓存命中:​已被计数过的消息(内容未变化时)直接使用缓存值
  • ​估算兜底:​对于不支持 tokenizer 的模型或超长内容,使用字符数/4 的粗略估算作为 fallback
// Token 计数的简化实现
class TokenCounter {
  private encoder: Tiktoken;
  private cache: Map<string, number>;

  count(text: string): number {
    // 缓存命中
    if (this.cache.has(text)) return this.cache.get(text)!;

    // 精确计数
    const tokens = this.encoder.encode(text).length;
    this.cache.set(text, tokens);
    return tokens;
  }

  // 计算整条消息的 token 开销(包含角色标记的 overhead)
  countMessage(msg: Message): number {
    const overhead = 4; // 每条消息的格式开销(role 标记等)
    return overhead + this.count(this.extractContent(msg));
  }
}

2.3 消息的角色与组织

ContextMemory 中的消息严格遵循 LLM 的对话格式,支持四种角色:

角色 说明 优先级 压缩策略
system 系统提示词、注入的策略规则 Critical 始终保留(使用 cache 锚定)
user 用户的原始输入 High 保留最新 N 条,更早的合并为摘要
assistant 模型生成的推理和工具调用 Normal 保留工具调用结果,裁剪纯文本推理
tool 工具执行返回的结果 按内容定 保留关键结果,裁剪冗余输出

2.4 消息的优先级与保留策略

MessagePriority 不是静态赋值的,而是在消息创建时根据其内容和上下文动态确定的:

  • ​用户的明确指令:​优先级 High——这是任务的核心定义,不能丢失
  • ​工具调用的关键返回值:​优先级 High——包含执行结果和后续决策依据
  • ​模型的中间推理链:​优先级 Normal 到 Low——有价值但可被摘要替代
  • ​纯确认类消息:​优先级 Trivial——如"已成功创建文件"的简单回执
  • ​系统注入的策略内容:​优先级 Critical——如 Goal 状态、Plan 进度,不可压缩

​设计洞察:​优先级系统是压缩策略的「交通灯」。当 token 预算紧张时,系统从最低优先级的消息开始裁剪,逐步释放空间,而非一刀切地丢弃前 N 条。这种细粒度的裁剪方式最大程度保留了对话的核心脉络。

3. 上下文注入系统

在构建发送给 LLM 的最终上下文时,对话历史并非唯一成分。kimi-code 的 InjectionManager 负责在每个 turn 开始前,将来自多个子系统运行时信息动态注入到上下文中,确保模型始终掌握最新的执行状态和环境上下文。

3.1 InjectionManager 的职责

InjectionManager 是上下文组装管线的最后一环:

┌──────────────────────────────────────────────────┐ │ 上下文组装管线 │ │ │ │ ContextMemory ──┐ │ │ ├──▶ 上下文合并 ──▶ 注入排序 ──▶ 去重 ──▶ LLM │ 注入源 1 ────────┤ │ │ 注入源 2 ────────┤ │ │ 注入源 3 ────────┘ │ │ │ │ ▲ │ │ │ InjectionManager 调度 │ │ ┌─────────┴──────────────────────────┐ │ │ │ • 插件提醒 (Plugin Hints) │ │ │ │ • 技能提示 (Skill Prompts) │ │ │ │ • 配置文件规则 (Settings Rules) │ │ │ │ • Goal 状态摘要 (Active Goals) │ │ │ │ • Plan 进度 (Current Plan State) │ │ │ │ • Swarm 协调状态 (Swarm Status) │ │ │ └────────────────────────────────────┘ │ └──────────────────────────────────────────────────┘

3.2 注入来源详解

注入来源 内容 触发时机 优先级
插件提醒 活跃插件注入的上下文提示,如"请使用 git worktree 进行隔离开发" 插件注册时 Normal
技能提示 当前激活 skill 提供的领域知识或约束规则 skill 匹配触发时 Normal
配置规则 从 project/user settings 读取的常驻规则,如 CODEBUDDY.md 文件内容 session 启动时 High
Goal 状态 当前 active goal 的描述、进度、里程碑状态 每个 turn 开始前 High
Plan 状态 当前执行计划的步骤列表与完成进度 每个 turn 开始前(Plan Mode 启用时) High
Swarm 状态 并行子 Agent 的任务分配、完成状态、需要汇总的结果 子 Agent 返回结果时 High

3.3 注入时机与去重

所有注入操作在每个 turn 的「上下文组装阶段」统一执行——即 LLM 调用发起之前。InjectionManager 维护一个内容指纹集合,对注入项进行去重:如果两个注入源产生了相同或高度相似的内容,只保留优先级更高的那一份。

// InjectionManager 的注入与去重逻辑(简化)
class InjectionManager {
  private sources: Map<string, InjectionSource>;
  private fingerprints: Set<string>; // 已注入内容的指纹集合

  collectInjections(): InjectItem[] {
    const items: InjectItem[] = [];

    for (const [name, source] of this.sources) {
      const content = source.generate(this.context);
      if (!content) continue;

      const fp = this.hash(content);
      if (this.fingerprints.has(fp)) {
        // 重复内容 → 跳过
        continue;
      }
      this.fingerprints.add(fp);

      items.push({
        source: name,
        content,
        priority: source.priority,
      });
    }

    // 按优先级降序排列
    return items.sort((a, b) => b.priority - a.priority);
  }

  private hash(content: string): string {
    // 使用简单的规范化哈希检测重复
    return this.normalize(content);
  }
}

​为什么不提前注入?​延迟到每个 turn 组装时才收集注入内容,可以确保信息是最新的——例如 Goal 的状态可能在上一轮工具执行后发生了变化,提前注入会携带过时信息。

4. Compaction 压缩机制

压缩(Compaction)是 kimi-code 上下文管理中最重要的子系统。当对话历史积累到一定规模后,系统必须对其进行有损压缩——即用更少的 token 换取尽可能多的信息保留。Kimi-code 实现了两级压缩策略:微压缩和完全压缩,两者在不同阈值触发,形成渐进式的信息降级机制。

4.1 完全压缩(Full Compaction)

完全压缩是最深度的一级压缩,当 token 数接近模型上下文窗口上限时触发。其核心思想是用模型自身的能力对历史对话进行总结,将数千 token 的对话历史浓缩为一段几百 token 的结构化摘要。

触发条件

// 完全压缩的触发判断(简化)
function shouldFullCompact(memory: ContextMemory): boolean {
  const usage = memory.tokenCount / memory.contextWindowSize;
  return usage > 0.85; // 使用率达到 85% 时触发
  // 或者连续微压缩后 token 仍在增长,触发升级
}

压缩算法

完全压缩并非简单地截断历史,而是走一个精心设计的流程:

1

选择压缩范围

从最早的消息开始,向最新的消息方向扫描,标记出可以被压缩的消息段(压缩窗口),同时跳过不可压缩的消息(如正在执行中的工具调用)。

2

提取关键信息

对压缩窗口内的消息进行分类提取:用户的核心意图、Assistant 做出的关键决策、工具调用的名称和结果摘要、用户提供的修正意见。

3

生成摘要

调用 LLM 将提取的关键信息整合为一段结构化的摘要文本。摘要包含:已完成任务的描述、进行中的任务状态、关键决策记录、未解决的问题列表。

4

替换消息

用生成的摘要替换压缩窗口内的所有消息(删除旧消息,在对应位置插入摘要消息),更新 token 计数。

// 完全压缩的简化伪代码
async function fullCompaction(memory: ContextMemory): Promise<void> {
  // 1. 确定压缩窗口
  const compactionWindow = selectCompressionWindow(memory.messages);

  // 2. 分离不可压缩的消息
  const { compressible, protected } = splitByCompressible(compactionWindow);

  // 3. 构建总结请求
  const summaryPrompt = buildSummaryPrompt(compressible, {
    format: 'structured',
    maxTokens: memory.contextWindowSize * 0.15, // 摘要占预算的 15%
  });

  // 4. 调用模型生成摘要
  const summary = await llm.complete(summaryPrompt);

  // 5. 用摘要替换压缩窗口
  memory.messages = [
    ...memory.messages.slice(0, compactionWindow.start),
    createSummaryMessage(summary),
    ...protected, // 受保护的消息紧接摘要之后
    ...memory.messages.slice(compactionWindow.end),
  ];

  // 6. 更新元数据
  memory.metadata.lastCompactionAt = Date.now();
  memory.metadata.compactionCount++;
  memory.tokenCount = recount(memory.messages);
}

保留与丢弃规则

保留 丢弃 / 压缩
用户最近 N 条原始消息 早期纯文本推理过程
重要的工具调用结果(如文件修改结果、测试输出) 已完成工具调用的中间参数细节
关键决策点(如"我将使用 React 而非 Vue") 重复的确认和纠错消息
任务目标定义和约束 已经被后续操作覆盖的早期内容
当前正在执行中的工具调用链 已解决且不再相关的错误诊断

摘要的格式与注入

生成的摘要以 system 消息的形式注入到上下文的前部(紧跟系统提示词之后),使用标准化的格式:

// 压缩摘要在上下文中的注入格式
[压缩摘要 — 用于替代早期对话]
- 已完成的任务: [摘要]
- 进行中的任务: [摘要]
- 关键决策: [摘要]
- 未解决的问题: [摘要]
- 重要文件: [变更记录]

4.2 微压缩(Micro Compaction)

微压缩是轻量级的中间预处理,在 token 数达到中间阈值时触发。与完全压缩不同,微压缩不对内容进行语义级总结,而是通过内容层面的裁剪和合并来释放空间。

触发条件

function shouldMicroCompact(memory: ContextMemory): boolean {
  const usage = memory.tokenCount / memory.contextWindowSize;
  return usage > 0.50; // 达到 50% 时开始微压缩
  // 注意:微压缩和完全压缩的阈值可以级联 ——
  // 微压缩后如果使用率仍然 > 85%,自动升级到完全压缩
}

微压缩的具体策略

微压缩执行以下操作,每一项都是低风险的优化:

  1. ​工具结果截断:​对于过长的工具返回内容(如 5000+ 字符的终端输出),保留前 N 行和后 N 行,中间用 [...truncated...] 标记替换。这适用于日志输出、编译结果等包含大量重复模式的内容。
  2. ​合并相似消息:​将连续的 short assistant messages(如"好的""继续执行""让我检查一下")合并为一条描述性消息,减少 message overhead。
  3. ​移除空白与格式冗余:​对代码块中的多余空行进行压缩,对工具输出的纯 whitespace 行进行裁剪。
  4. ​最低优先级消息裁剪:​如果以上操作仍不够,从标记为 Trivial 优先级的消息开始移除。
// 微压缩的简化伪代码
async function microCompaction(memory: ContextMemory): Promise<boolean> {
  let savings = 0;

  for (const msg of memory.messages) {
    // 跳过不可压缩的消息
    if (!msg.compressible) continue;

    // 策略1: 截断过长的工具输出
    if (msg.role === 'tool' && msg.content.length > 5000) {
      const before = tokenCounter.count(msg.content);
      msg.content = truncateMiddle(msg.content, 2000);
      const after = tokenCounter.count(msg.content);
      savings += (before - after);
    }

    // 策略2: 移除 Trivial 优先级消息
    if (msg.priority === MessagePriority.Trivial) {
      const before = tokenCounter.countMessage(msg);
      memory.messages = memory.messages.filter(m => m.id !== msg.id);
      savings += before;
    }
  }

  memory.tokenCount -= savings;
  return true;
}

4.3 微压缩 vs 完全压缩

维度 微压缩 完全压缩
触发阈值 50% 上下文使用率 85% 上下文使用率
压缩方式 内容裁剪、合并 语义总结
信息损失 低(主要去冗余) 中(细节被概括化)
LLM 调用 不需要 需要 1 次额外调用
Token 节省 10-30% 50-80%(对压缩段)
是否需要升级 如果使用率仍 > 85%,升级为完全压缩 压缩后 token 大幅下降,无需再升级

​为什么需要两级?​单独使用完全压缩会频繁调用 LLM 生成摘要,增加延迟和成本。微压缩作为第一道防线,用几乎零成本的裁剪操作应对大部分轻微的超额。当微压缩不再有效时,才升级到完全压缩——这是一种渐进式降级策略。

4.4 压缩的边界条件

不是所有内容都可以压缩。以下「安全边界」确保了压缩不会破坏 Agent 的正确性:

  • ​不可压缩的消息:​正在执行中的工具调用链——如果压缩掉了 tool_call 的上下文,模型将无法处理对应的 tool_result
  • ​未完成的 turn:​如果当前 turn 还有 pending 的工具调用等待结果,该 turn 的 assistant 消息不能压缩
  • ​上下文一致性:​压缩后的摘要必须保留原有的因果链——压缩到一半的任务必须在摘要中标明"进行中"而非"已完成"

与 Goal Mode 的交互

当 Agent 处于 Goal Mode 时,active goal 相关的上下文受到特殊保护。Goal 的描述、里程碑状态、进度信息被标记为 compressible = false,优先级为 Critical。这是因为 Goal 定义了任务的根本目标——如果压缩导致目标丢失,Agent 的行为将失去方向。

// Goal 相关消息的保护逻辑
function protectGoalContext(messages: Message[], activeGoal: Goal): void {
  for (const msg of messages) {
    // 任何引用了 active goal 的消息都被保护
    if (msg.content?.includes(activeGoal.id)) {
      msg.compressible = false;
      msg.priority = MessagePriority.Critical;
    }
  }
}

5. Prompt Cache 优化

Prompt Cache(也称为 Context Caching)是现代 LLM API 的一个关键性能特性。它的工作方式类似于 HTTP 缓存:API 提供方对请求上下文的前缀部分进行哈希缓存,后续请求如果前缀相同,该部分不会重复计算,用户也只需为增量 token 付费。

5.1 利用 Cache 的上下文字段排序

kimi-code 在组装最终上下文时,有意识地将内容按「稳定性」从高到低排列:

上下文内容的稳定性排序(从高到低 / 从前到后): [最稳定 — 享受完整 Cache] [稳定 — 周期 Cache] [不稳定 — 不 Cache] 系统提示词 (System Prompt) → 注入内容 (Injections) → 对话历史 (Messages) ┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐ │ • 基础 profile │ │ • 配置规则 │ │ • user/assistant/tool │ │ • 工具描述 │ │ • Goal 状态 │ │ 消息按时间排列 │ │ • 角色定义 │ │ • Plan 进度 │ │ • 最新消息在最末尾 │ │ • 行为约束 │ │ • Skill 提示 │ │ │ └──────────────────────┘ └──────────────────────┘ └──────────────────────┘

这种排序策略带来两个直接好处:

  1. ​系统提示词完全缓存:​基础 profile 和工具描述在一个 session 内很少变化,这部分 token 只计费一次
  2. ​注入内容部分缓存:​配置规则稳定不变;Goal 和 Plan 在同一 turn 内不变,跨 turn 才变化,因此至少在一个 turn 内的多次 LLM 调用间可以命中缓存
  3. ​对话历史不缓存:​新增的用户消息和助手回复位于末尾,不影响前缀缓存的命中

5.2 增量更新策略

当需要修改缓存友好的前缀内容时(如动态添加/移除工具描述),kimi-code 遵循以下策略:

  • ​尽量在其所在段落的末尾追加:​而非在中部插入。前缀每增加一个 token 都会使缓存失效,而末尾追加则只影响新增部分
  • ​批量更新:​如果一个 turn 内多个注入源产生变化,收集所有变更后一次性应用到上下文中,而非每个变更触发一次重建
  • ​缓存失效监控:​记录每次缓存 hit/miss 的统计,用于评估优化效果

6. 系统提示词管理

系统提示词(System Prompt)是上下文中的「不变量」——它定义了 Agent 的身份、能力边界和行为准则。kimi-code 的系统提示词采用分层架构,由多个模块按优先级叠加组合而成。

6.1 分层结构

系统提示词的分层结构(从上到下 / 从底层到高层): ┌───────────────────────────────────────────────────────┐ │ Layer 4: 注入内容 (Injections) │ │ Goal 状态 / Plan 进度 / Swarm 协调信息 │ ├───────────────────────────────────────────────────────┤ │ Layer 3: 对话历史 (Conversation History) │ │ 用户消息 / Assistant 回复 / 工具调用结果 / 压缩摘要 │ ├───────────────────────────────────────────────────────┤ │ Layer 2: 工具描述 (Tool Descriptions) │ │ 动态生成:当前环境中可用的工具列表及其参数 schema │ ├───────────────────────────────────────────────────────┤ │ Layer 1: 基础 Profile (Base Profile) │ │ 身份定义 / 行为准则 / 输出格式 / 安全约束 │ └───────────────────────────────────────────────────────┘

6.2 Profile 的加载与合并

基础 profile 来自多个来源,按优先级合并:

// Profile 的合并优先级(从低到高,后者覆盖前者)
// 1. 内置系统 profile(框架硬编码的角色定义)
// 2. 工作空间 profile(MEMORY.md / CODEBUDDY.md)
// 3. 用户级 profile(~/.workbuddy/profile.md)
// 4. Session 级运行时注入(CLI 参数 / API 选项)

function buildBaseProfile(session: Session): string {
  const profiles = [
    BUILTIN_SYSTEM_PROFILE,
    loadWorkspaceProfile(session.cwd),   // MEMORY.md
    loadUserProfile(),                    // ~/.workbuddy/profile.md
    session.runtimeProfile,               // 运行时参数
  ];

  // 从底层向高层叠加
  return profiles.filter(Boolean).join('\n\n---\n\n');
}

6.3 工具描述的动态生成

工具描述(Tool Descriptions)是系统提示词中最具动态性的部分。当 Agent 使用 select_tools 机制时,每次 LLM 调用前都会重新生成工具描述列表——只包含当前 turn 可能需要的那部分工具。这不仅减少了 token 消耗,也帮助模型聚焦于相关工具,减少「迷路」概率。

// 工具描述动态生成的简化流程
function generateToolDescriptions(
  allTools: Tool[],
  context: TurnContext
): string {
  // 1. 过滤:只包含当前可用的工具
  const availableTools = allTools.filter(t => t.isAvailable(context));

  // 2. 排序:把最近使用过的工具放在前面(利用 cache 友好性)
  const sorted = sortByRecency(availableTools, context.recentToolCalls);

  // 3. 生成描述:每个工具包含名称、参数 schema、简要说明
  const descriptions = sorted.map(tool => ({
    name: tool.name,
    description: tool.description,
    parameters: JSON.stringify(tool.parameters),
  }));

  // 4. 格式化输出
  return formatToolList(descriptions);
}

​select_tools 对上下文的影响:​当工具集变化时,系统提示词的工具描述部分也会变化。这会导致 Prompt Cache 该部分失效。因此 select_tools 的筛选逻辑应该尽量稳定——如果过滤条件在 session 中变化不频繁,缓存命中率会更高。

7. 记忆系统

上下文管理解决的是「当前 session 内的记忆」问题,但 Agent 的智能还需要跨 session 的持久记忆。kimi-code 的记忆系统由三个层次构成,从短暂到持久逐级增强。

7.1 Session 级别记忆

Session 记忆即 ContextMemory 中保存的当前对话历史。它的特征是全量、实时、但生命周期有限——session 结束即销毁(如果不保存 transcript)。这是 Agent 的「工作记忆」,用于当前任务的理解和推理。

7.2 用户 Profile 的持久化

用户 profile 是跨 session 的第一层持久化记忆。它保存在用户目录下(~/.workbuddy/profile.md),内容由用户手动维护或 Agent 在用户允许下更新。Profile 包含:

  • 用户偏好的代码风格和命名规范
  • 常用的工具链和项目结构偏好
  • 技术栈偏好(语言、框架、数据库等)
  • 自定义的行为规则和约束

Profile 在 Agent 启动时被加载并合并到系统提示词中,确保即使在新 session 中,Agent 也「了解」用户的偏好。

7.3 工作空间记忆(MEMORY.md)

MEMORY.md(也叫 CODEBUDDY.md)是保存在项目根目录下的项目级持久记忆文件。与用户 profile 不同,它是项目相关的、团队共享的。Agent 在 session 中可以将关键的项目理解写入这个文件,下次打开同一项目时自动加载。

// 工作空间记忆的读写流程
class WorkspaceMemory {
  private filePath: string; // e.g., d:/code/my-project/CODEBUDDY.md

  async load(): Promise<string | null> {
    try {
      return await fs.readFile(this.filePath, 'utf-8');
    } catch {
      return null; // 文件不存在,首次启动
    }
  }

  async update(content: string): Promise<void> {
    // 由 Agent 在完成任务后写入(需用户许可)
    await fs.writeFile(this.filePath, content, 'utf-8');
  }
}

7.4 三种记忆的对比

记忆类型 存储位置 生命周期 共享范围 典型用途
Session 记忆 内存 (ContextMemory) 单次 session 当前对话 理解当前任务的上下文、跟踪执行进度
用户 Profile 磁盘 (~/.workbuddy/) 持久 该用户的所有项目 代码风格偏好、技术栈选择、行为约束
工作空间记忆 磁盘 (项目根目录 CODEBUDDY.md) 持久 该项目团队成员 项目架构决策、关键约定、依赖说明

​设计哲学:​kimi-code 的记忆系统遵循「最近记忆在内存,关键记忆落磁盘」的原则。Session 记忆提供实时推理所需的上下文;Profile 和 MEMORY.md 则确保 Agent 在跨 session 时不会从零开始——它至少了解用户是谁、项目是什么、之前达成了哪些共识。

总结

kimi-code 的上下文管理系统是一套从容量约束到记忆持久化的完整解决方案。它以 ContextMemory 为核心,通过消息优先级机制和两级压缩策略实现上下文的动态管理,通过 InjectionManager 在关键节点注入运行时状态,通过 Prompt Cache 友好的字段排序降低运行成本,并通过分层的系统提示词和三级记忆系统确保 Agent 既不会遗忘当前任务,也不会在跨 session 时丢失长期知识。

理解上下文管理,是理解 Agent 如何「思考」和「记忆」的根本。它不是辅助子系统,而是 Agent 的认知基础设施——压缩算法决定了 Agent 能走多远,注入系统决定了 Agent 知道什么,记忆系统则决定了 Agent 是否能持续成长。

​关键洞察:​上下文管理的本质是信息经济学——用有限的 token 预算实现信息的最大效用。优先级、压缩、缓存、注入,每一个机制都在回答同一个问题:「在给定的容量约束下,当前最值得让模型看到的信息是什么?」