跳到主要内容

两个最妙的机制:记忆清零 + CodeRAG 参考检索

这章是全项目的精华。第一个机制解决「写几十个文件对话会爆」,第二个解决「怎么让模型参考已有代码而不把整份代码塞进上下文」。

3.1 write_file 触发的「干净起点」记忆

它要解决的小问题

实现一个有几十个文件的项目,如果把每轮的对话、每个工具的完整输出都累积在上下文里,几轮之后就会撑爆 token 上限、变慢、变贵。

思路/直觉:写完一个文件 = 一个干净的检查点

DeepCode 的核心洞察:每写完一个文件,前面那一堆「读这读那、想来想去」的过程就没用了——重要的只剩「这个文件实现了什么」。所以每次 write_file 成功后,下一轮直接把对话历史清空,只重建一个最小上下文:

一轮里若发生了 write_file:


标记 should_clear_memory_next = True (record_tool_result, :1579-:1581)


下一轮把 messages 重建成「干净起点」,只含两条 user 消息:
① 复现计划 + 已实现文件清单 + 剩余文件清单
② 最新代码摘要(知识库)+ 下一步指引


几十轮下来,上下文长度保持「常数级」,不随文件数线性增长

图示:对话历史怎么被压缩

清空前(累积了一堆): 清空后(干净起点,固定结构):
┌──────────────────────────┐ ┌──────────────────────────┐
│ system prompt │ │ user: 计划 + 已实现清单 │
│ user: 实现吧 │ │ + 剩余文件清单 │
│ assistant: 我先读... │ ──► ├──────────────────────────┤
│ user: [read_file 全文] │ │ user: 最新代码摘要(知识库)│
│ assistant: 再读... │ │ + 下一步指引 │
│ user: [search 结果] │ └──────────────────────────┘
│ assistant: write_file │ (历史全丢,只靠落盘摘要续命)
│ user: [write 成功] │
│ ... 越积越长 ... │
└──────────────────────────┘

原理演示

# 示意,非源码:清零的触发与重建(对应 should_trigger_memory_optimization :2086 与 create_concise_messages :1616)
# 触发条件极简:只看「上一轮是否 write 过」
def should_trigger_memory_optimization(...):
return self.should_clear_memory_next # write_file 后置 True

# 重建:丢掉所有历史,只放两条 user 消息
def create_concise_messages(system_prompt, messages, files_implemented):
plan_msg = f"复现计划:{self.initial_plan}\n已实现:{implemented_list}\n剩余:{unimplemented_list}"
kb_msg = f"最新代码知识库:{self._read_code_knowledge_base()}\n下一步:{self.current_next_steps}"
return [plan_msg, kb_msg] # 干净起点

真实实现

  • 触发标志在 write_file 时置位:record_tool_result(workflows/agents/memory_agent_concise.py:1579-:1581)设 last_write_file_detected = True; should_clear_memory_next = True
  • 重建逻辑:create_concise_messages(:1616)只产出两条 user 消息——计划+文件清单(:1674-:1694)、知识库+指引(:1705-:1724)。
  • 应用并打印压缩比:apply_memory_optimization(:2108),并把标志复位 should_clear_memory_next = False(:2134)。

关键细节:历史去哪了?——存进摘要文件

清空对话不等于丢信息。每写一个文件,记忆 agent 会调 LLM 给这个文件生成结构化摘要(核心职责、公开接口、依赖、实现要点),append 到 implement_code_summary.md:create_code_implementation_summary(:1045)。重建上下文时,_read_code_knowledge_base(:1763)把这份摘要读回来当「知识库」。

这就是「靠落盘文件传递状态」在实现阶段的体现:对话历史是易失的、被清空的;摘要文件是持久的、累积的。注意摘要还会把 Next Steps 单独抽出来只存内存不写文件(:1086-:1089),避免污染知识库。

3.2 read_file → read_code_mem 拦截

它要解决的小问题

模型实现新文件时,常想「先读一下我之前写的那个文件」。但把整份源码读回上下文又很贵——尤其那个文件已经有摘要了。

思路:能读摘要就别读全文

实现 agent 拦截所有 read_file 调用,先查这个文件有没有摘要;有就返回摘要(read_code_mem),没有才真读文件:

# 示意,非源码:read_file 拦截(对应 code_implementation_agent.py:202-:221)
if tool_name == "read_file" and self.memory_agent is not None:
# 不直接读文件,先走「记忆优化」:查摘要
return await self._handle_read_file_with_memory_optimization(tool_call)
# 内部:调 read_code_mem MCP 工具查 implement_code_summary.md 里有没有这个文件的段落
# 有 → 返回摘要(标记 optimization="redirected_to_read_code_mem")
# 无 → 回落到真正的 read_file

真实实现

  • 拦截入口:CodeImplementationAgent.execute_tool_calls(code_implementation_agent.py:202)→ _handle_read_file_with_memory_optimization(:280)。
  • 摘要查询是个真正的 MCP 工具:read_code_mem(tools/code_implementation_server.py:847),它打开 implement_code_summary.md,用 _extract_file_section_from_summary(:981)按路径匹配抽出对应文件的摘要段。

这条拦截和 3.1 的摘要文件是配套的:write_file 时生成摘要 → 后续 read_file 命中摘要 → 避免重读全文 → 上下文继续保持精简。

3.3 CodeRAG:从下载的仓库里检索参考代码

它要解决的小问题

复现论文时,网上常有相关的开源实现。怎么让模型「参考」它们,又不把整个仓库塞进上下文?

思路:先离线建索引,实现时按需检索

这是 Phase 7/8(完整模式)做的事:下载相关 GitHub 仓库 → 用 LLM 离线分析每个文件,产出「这个仓库文件和目标计划里哪个文件相关、相关类型、置信度、有什么可借鉴之处」的关系索引(JSON)。

核心数据结构(tools/code_indexer.py:31):

# 示意,非源码:索引里一条「仓库文件 ↔ 目标文件」关系(对应 FileRelationship :31)
@dataclass
class FileRelationship:
repo_file_path: str # 参考仓库里的文件
target_file_path: str # 对应计划里要实现的文件
relationship_type: str # direct_match / partial_match / reference / utility
confidence_score: float # 0~1
helpful_aspects: List[str] # 可借鉴的点
usage_suggestions: str # 怎么用

真实实现

  • 建索引:CodeIndexer.process_repository(tools/code_indexer.py:871)→ build_all_indexes(:1094),先 pre_filter_files(:522)用文件树粗筛,再 analyze_file_content(:658)逐文件 LLM 分析,带缓存(_get_cache_key_manage_cache_size)。
  • 编排入口:orchestrate_codebase_intelligence_agent(agent_orchestration_engine.py:1396)→ run_codebase_indexing,产出落到 indexes/*.json
  • 实现阶段检索:实现 agent 连了 code-reference-indexer MCP 服务器,用 search_code_references 工具(config/mcp_tool_definitions.py:244)从索引里按需查参考——「统一工具:一次调用完成目录设置+索引加载+搜索」。

关键细节

  • 没下到仓库就优雅跳过。 code_base/ 为空时直接返回 skipped,流水线继续(:1438-:1462),不让缺参考阻断实现。
  • 快速模式整段跳过 CodeRAG。 见第 01 章的 enable_indexing 开关。

4. 巧妙之处(可借鉴)

  • 「写完即清零」把上下文压到常数级。 触发条件极简(只看上轮是否 write 过),重建结构固定(永远两条 user 消息)。这是处理超长编码会话最干净的招之一。
  • 摘要文件 = 跨清零的持久记忆。 易失的对话历史 vs 持久的 implement_code_summary.md,职责分明。
  • read_file 透明拦截成 read_code_mem。 模型无感知地从「读全文」被优化成「读摘要」,不需要改模型行为。
  • CodeRAG 离线建索引 + 按需检索。 把「参考几十个仓库」从「塞进上下文」变成「先离线消化成关系索引,实现时检索片段」。

5. 边界与局限

  • 激进清零会丢中间推理。 如果某个文件的实现依赖前几轮试错得出的微妙结论,而它没进摘要,清零后就丢了。摘要质量是上限。
  • 摘要匹配靠路径。 read_code_mem 按文件路径匹配摘要段(_paths_match,code_implementation_server.py:1050),路径表述不一致可能漏命中。
  • CodeRAG 索引靠 LLM 离线判断相关性。 confidence_score 是模型估的,不是静态分析得来的硬关系;且建索引本身要烧不少 LLM 调用。
  • 本章针对 ConciseMemoryAgent(单文档)。 项目还有 memory_agent_concise_index.py / memory_agent_concise_multi.py 两个变体(索引模式、多文档),机制类似但细节不同,本章未逐行覆盖。

6. 横向对比

  • 对比「滑动窗口」式记忆(只保留最近 N 条):DeepCode 不是「保留最近」,而是**「保留计划 + 摘要,丢掉过程」**——结构化压缩而非时间窗截断。prompts/code_prompts.py:815SLIDING_WINDOW_SYSTEM_PROMPT 是它早期/备选的滑窗思路,而 ConciseMemoryAgent 走的是更激进的「清零 + 摘要」路线。
  • 对比把整个代码库塞进上下文的「长上下文」流派:DeepCode 用摘要 + 检索,把成本从「随项目规模线性增长」压成「近似常数」。这正是 README 强调的「同样的模型,靠脚手架取胜」的技术落点。

7. 代码地图

主题文件符号
简洁记忆 agentworkflows/agents/memory_agent_concise.pyConciseMemoryAgent
清零触发标志同上record_tool_result(置 should_clear_memory_next)
是否该清零同上should_trigger_memory_optimization
重建干净上下文同上create_concise_messages
应用优化(打印压缩比)同上apply_memory_optimization
生成文件摘要同上create_code_implementation_summary
读知识库同上_read_code_knowledge_base
read_file 拦截workflows/agents/code_implementation_agent.pyexecute_tool_calls / _handle_read_file_with_memory_optimization
read_code_mem 工具tools/code_implementation_server.pyread_code_mem / _extract_file_section_from_summary
CodeRAG 索引器tools/code_indexer.pyCodeIndexer / FileRelationship / process_repository / build_all_indexes
参考检索工具定义config/mcp_tool_definitions.pysearch_code_references(get_code_implementation_tools)
记忆变体workflows/agents/memory_agent_concise_index.py / memory_agent_concise_multi.py