在上一章,我们像法医一样解剖了 CC(Claude Code)的数据载荷。现在,我们要利用这些数据,为 CC 打造一个“第二大脑”。
很多开发者在尝试外挂 RAG 时,容易陷入“脚本陷阱”:写一个几百行的 Python 脚本,混杂着文件读取、OpenAI API 调用和简单的余弦相似度计算。这种“大泥球”架构在 Demo 阶段能跑通,但一旦接入 CC 进行高频交互,就会遇到三个致命问题:
本章学习目标:
在深入模块之前,必须先决定你的 RAG 以什么形态存在。这直接决定了架构的复杂度。
每次 CC 调用工具时,启动一个新进程(如 python search.py)。
启动一个本地 HTTP/RPC 服务(如 localhost:8000),CC 的工具只是一个轻量级 curl 包装。
Rule of Thumb (经验法则): 如果你要做本地 Embedding(如使用 HuggingFace 模型),必须采用形态 B(驻留服务)。否则 CC 每次思考都要等你几秒钟,你会疯的。
一个健壮的 RAG 系统就像一个漏斗,数据层层筛选,最终滴出精华。
[ Disk / Network ]
|
v
+-------------+
| 1. Ingest | <-- "脏"数据入口 (ETL)
+-------------+
| (Clean Text + Metadata)
v
+-------------+
| 2. Index | <-- 向量化与存储 (The Map)
+-------------+
| (Vector DB)
v
+-------------+ Query
| 3. Retrieve| <-- 粗筛 (Recall Top-100)
+-------------+
| (Candidates)
v
+-------------+
| 4. Rerank | <-- 精排 (Precision Top-10)
+-------------+
| (Ranked Chunks)
v
+-------------+
| 5. Serve | <-- 包装与预算 (Format & Cut)
+-------------+
|
v
[ Claude Code ]
不仅仅是读文件。这一层决定了“垃圾进,垃圾出”。
.gitignore。package-lock.json、构建产物、二进制文件。file_path: src/main.rsline_range: 10-50last_modified: 1719283200单一的向量检索(Dense Retrieval)在代码场景往往表现不佳(例如搜索具体的变量名 MAX_RETRY_COUNT)。
Score = 0.7 * Dense + 0.3 * BM25。Retrieve 层为了速度(召回 100 个),牺牲了精度。Rerank 层使用更强的模型(Cross-Encoder)对这 100 个进行精细打。
这是直接面对 CC 的门户。
</tool_output> 闭合标签,防止注入攻击。这是你和 CC 签订的协议。任何一方违反,系统就会崩溃。
这是你在 CC 的工具定义(Tool Definition)中需要配置的 JSON Schema。
{
"name": "search_codebase",
"description": "Search the codebase for snippets relevant to a query. Use this when you need to understand how functions are defined or used.",
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The natural language query or specific code symbol to search for."
},
"project_root": {
"type": "string",
"description": "Absolute path to the root of the project being worked on."
},
"file_pattern": {
"type": "string",
"description": "Optional glob pattern to restrict search (e.g., 'src/**/*.py')."
},
"max_results": {
"type": "integer",
"default": 10,
"description": "Maximum number of code chunks to return."
}
},
"required": ["query", "project_root"]
}
}
RAG 服务返回给 CC 的内容。建议包含结构化元数据和人类可读文本两部分。
JSON 载荷示例:
{
"status": "success",
"meta": {
"total_found": 42,
"returned": 3,
"search_time_ms": 150
},
"results": [
{
"file": "src/auth/login.py",
"lines": [15, 30],
"score": 0.92,
"content": "def login(user, password):\n # implementation..."
},
// ... more items
],
"formatted_output": "<results>\n<item file='src/auth/login.py'>\n..." // 预组装好的Prompt片段
}
Rule of Thumb (经验法则): 虽然 CC 能读 JSON,但提供一段预格式化好的
formatted_output往往效果更好。因为你可以控制换行、缩进和 XML 标签,确保 LLM 能够以一种“视觉上”清晰的方式阅读代码块。
在 Serve 层,如何把结果塞进有限的窗口?这里介绍 “贪婪填充算法” (Greedy Packing)。
算法逻辑:
MAX_TOKENS = 4000。if (current_tokens + chunk_tokens) <= MAX_TOKENS:
current_tokens += chunk_tokenselse:
理解这个时序图对于性能优化至关重要。
User CC (Agent) RAG Tool (Client) RAG Server (Daemon)
| | | |
|--(Query)-->| | |
| |--(Think)--------->| |
| | | |
| |--(Tool Call)----->| |
| | "search(q, n)" |--(HTTP POST)------->|
| | | | [1. Embed Query]
| | | | [2. Vector Search]
| | | | [3. Rerank]
| | | | [4. Pack Context]
| | |<--(JSON Resp)-------|
| |<--(StdOut)--------| |
| | | |
| |--(Read Context)-->| |
| |--(Gen Answer)---->| |
|<-(Reply)-- | | |
性能瓶颈点:
file_path 和 lines,这是 CC 能够“引用”而不是“瞎编”的基础。.md 文档时,数据是如何流经 Ingest 和 Index 层,最终到达 Vector DB 的?Response Structure,增加一个字段来表示文件的“最后修改时间”。utils.py。如何设计 Index 结构和 Retrieve 接口,确保在 Project A 提问时不会搜到 Project B 的代码?rag --help,都要等 3 秒钟 import 库。.gitignorenode_modules 或 venv 里的几万个库文件都索引了。gitignore 解析器(如 python 的 pathspec 库)。/Users/tom/projects/my-app/src/main.py。src/main.py)。这样 CC 可以直接用这个路径去读文件或做 patch。" 或 \ 或换行符,直接拼接进 JSON 导致格式错误。json.dumps),严禁手动拼接字符串生成 JSON。| < 上一章:CC 数据完整拆解 | 下一章:数据摄取 > |