在第一章我们定义了“外挂 RAG”的目标。在动手之前,我们必须先像外科医生一样了解我们要“动手术”的对象——Claude Code (CC)。
CC 不同于传统的“一问一答”式 Chatbot。它是一个自主智能体(Autonomous Agent)。当你按下回车时,它并非直接生成答案,而是在后台运行一个包含“观察-思考-决策-行动”的复杂循环。它拥有文件系统的读写权限,能执行终端命令,甚至能管理自己的记忆。
本章将深入解剖 CC 的认知循环(Cognitive Loop),揭示它决定“何时检索”的底层逻辑。我们将详细对比“工具化(Pull)”与“预加载(Push)”两种集成架构,并制定一套让 CC 既能看懂、又能引用的数据交互契约。这是构建“不致幻 RAG”的理论基石。
要让外挂 RAG 只有在“需要”的时候介入,而不是像垃圾广告一样时刻干扰 CC,我们需要理解它的工作流:ReAct (Reasoning + Acting)。
CC 的一次交互并非线性,而是一个递归的圆环。请看下图:
User Input: "修复 auth 模块里的内存泄漏问题"
|
v
+-----------------------------------------------------------------------+
| Claude Code Engine (宿主) |
| |
| +------------------+ +------------------+ +-----------+ |
| | 1. Short-term | ----> | 2. Thought (CoT) | ----> | 3. Action | |
| | Memory (Ctx) | | (思考/规划) | | (调用工具)| |
| +------------------+ +------------------+ +-----------+ |
| ^ | | |
| | v | |
| +------------------+ +------------------+ v |
| | 6. Context Update| <---- | 5. Observation | <---- [ RAG 介入 ] |
| | (追加上下文) | | (接收工具输出) | | 检索/返回 | |
| +------------------+ +------------------+ +-----------+ |
| | |
| +-----> [若任务未完成] -> 回到 Step 2 |
| +-----> [若任务完成] -> 4. Final Answer (输出给用户) |
+-----------------------------------------------------------------------+
ls 缓存)。auth 模块的代码在哪里,我需要先找代码。”或者“我不懂这个库的 API,我需要查文档。”ToolCall: search_knowledge_base(query="memory leak patterns in auth module")。理解 CC 的上下文(Context Window)结构,决定了你的 RAG 数据该“插”在哪里才不会被忽略。我们可以把 CC 的上下文想象成一个千层饼:
[ 顶层:最久远 / 权重最低 ]
+---------------------------------------------------------+
| SYSTEM PROMPT (指令层) |
| 定义 Persona、禁止事项、工具定义 (Tool Definitions) |
+---------------------------------------------------------+
| PROJECT CONTEXT (项目层) |
| 自动读取的 README、目录树结构、用户固定的文件 |
+---------------------------------------------------------+
| CONVERSATION HISTORY (对话历史) |
| User: ... / Assistant: ... (多轮对话) |
+---------------------------------------------------------+
| ... (滑动窗口,旧的会被丢弃) ... |
+---------------------------------------------------------+
| DYNAMIC TOOL OUTPUTS (动态层) <--- [ RAG 最佳栖息地 ] |
| 最近几次工具调用的结果 (检索到的文档片段) |
+---------------------------------------------------------+
| USER LAST QUERY (当前指令) |
+---------------------------------------------------------+
[ 底层:最新鲜 / 权重最高 (Attention 聚焦区) ]
💡 Rule of Thumb (经验法则): Recency Bias (近因效应):LLM 对放在最底部(最新)的信息关注度最高。因此,不要试图把 RAG 检索到的几万字塞进
SYSTEM PROMPT。最好的策略是让 RAG 的结果作为“最近的工具输出”出现在底部,这样 CC 能够立即利用这些信息进行回答。
我们不仅要能挂上去,还要挂得优雅。目前主要有三种架构模式:
这是最符合 Agent 理念的模式。RAG 被封装为一个工具 (Tool)。
search_docs(query: str, filters: list)。在用户发问之前,强制把相关文档“拍”在 CC 脸上。
检索脚本 -> 文本 -> 拼接到 User Query -> 发送给 CC。# 伪代码:先搜相关文档,再把结果喂给 CC
rag-cli retrieve "S3 upload logic" | claude "请基于以上信息重构 upload.py"
这是新手最容易踩坑的地方。不要只返回纯文本!CC 是一个代码 Agent,它喜欢结构化数据。
Claude 系列模型对 XML 标签(<tag>...</tag>)有经过微调的极高敏感度。相比 JSON,XML 在容纳长文本(如代码块)时消耗的转义 Token 更少,且更不容易出错。
当 RAG 检索到数据后,返回给 CC 的 stdout 应该长这样:
<search_results>
<meta>
<query>auth token expiration</query>
<count>2</count>
</meta>
<result id="1" score="0.92">
<file_path>docs/architecture/auth_flow.md</file_path>
<source_type>documentation</source_type>
<content>
The access token is valid for 1 hour. Refresh tokens are valid for 30 days.
When expiration occurs, the client must call /api/v1/refresh.
</content>
</result>
<result id="2" score="0.85">
<file_path>src/config.py</file_path>
<line_numbers>45-50</line_numbers>
<source_type>code</source_type>
<content>
ACCESS_TOKEN_TTL = 3600
REFRESH_TOKEN_TTL = 3600 * 24 * 30
</content>
</result>
</search_results>
为什么包含这些字段?
file_path:CC 看到这个路径,如果觉得片段不够,它会主动发起 read_file 命令去读取全文。这是“外挂”与“原生能力”的桥梁。line_numbers:让 CC 在修改代码时能精确定位。source_type:帮助 CC 判断这是“理论(文档)”还是“实现(代码)”。Thought -> Tool Call -> Observation -> Answer 循环工作。RAG 是插入在 Tool Call 和 Observation 之间的外挂显卡。search_docs 工具。请分析可能得原因(至少两点)。