从零搭建私人 RAG 实战:让 Claude Code 按需查询知识库

本文是「从零搭建私人 RAG 实战」专栏的第五篇。上一篇《从零搭建私人 RAG 实战:用户问题向量化与语义检索》完成了用户问题的向量化,并从 BADR、FADR 向量表中检索相关知识片段;本篇继续介绍 Claude Code 如何通过本地规则、Skill 和 MCP 调用 CorpRAG,实现知识库的按需查询。

在完成文档切分、向量化和 MCP Server 开发后,我们已经具备了一个可以被外部调用的知识库服务。

不过,服务能够运行,并不代表 AI 客户端能够稳定、准确地使用它。

从"服务可用"到"客户端按需调用",中间还需要解决几个问题:

  • Claude Code 如何发现并连接 MCP Server?
  • 用户提出问题后,客户端如何判断该查询哪个知识库?
  • 如何通过文档类型缩小检索范围?
  • MCP 返回的知识片段如何参与大模型的最终回答?
  • 如何避免把完整的内部文档长期放入模型上下文?

本文以 Claude Code 调用 CorpRAG 为例,介绍"客户端配置---规则路由---工具发现---向量检索---答案生成"的完整链路。

本文只讨论客户端接入方式和通用调用机制。示例不包含任何真实的内部决策、业务规则或知识库正文。


一、完整调用链

整个流程可以概括为:

text 复制代码
用户
  │
  │  在 Claude Code 中提出问题
  ▼
Claude Code
  │
  │  根据 CLAUDE.local.md 判断是否需要查询知识库
  │  命中决策场景 → 加载 Skill
  ▼
Skill(如 decision-guide)
  │
  │  识别决策类型(FADR/BADR/混合)
  │  将宽泛问题改写为具体查询
  │  调用 MCP 工具
  ▼
MCP 工具调用
  │
  │  mcp__corprag__fr_query({ question, docType })
  ▼
CorpRAG MCP Server
  │
  │  参数校验 + 知识库路由
  ▼
向量检索
  │
  │  问题向量化 → 查询对应向量表 → 返回相关片段
  ▼
MCP Server
  │
  │  格式化片段、来源和文档类型
  ▼
Claude Code
  │
  │  将用户问题、检索结果和当前上下文交给大模型组合
  ▼
最终回答

这里涉及两种不同模型的职责:

  1. Embedding 模型负责把问题转换为向量,用于查找语义相近的知识片段;
  2. Claude 大模型负责理解用户意图、选择工具、阅读检索结果并组织最终答案。

MCP Server 不直接替代大模型回答问题,它提供的是与当前问题相关的知识上下文。


二、Claude Code 如何连接 MCP Server

项目通过根目录下的 .mcp.json 声明 MCP 服务:

json 复制代码
{
  "mcpServers": {
    "corprag": {
      "command": "node",
      "args": ["<CorpRAG 项目路径>/dist/stdio.js"],
      "cwd": "<CorpRAG 项目路径>"
    }
  }
}

1. 服务名称

corprag 是 Claude Code 识别该 MCP Server 时使用的名称。

服务端注册的工具(如 fr_query)在 Claude Code 运行时中显示为:

text 复制代码
mcp__corprag__fr_query

名称可以拆成三部分:

text 复制代码
mcp__<服务名称>__<工具名称>

因此,规则文档中描述的逻辑名称 corprag.fr_query 与 Claude Code 运行时展示的完整名称 mcp__corprag__fr_query 指向的是同一个工具。

2. STDIO 传输

Claude Code 根据配置启动一个子进程,执行编译后的 MCP Server 入口文件。入口文件中通过 StdioServerTransport 建立通信:

ts 复制代码
const server = createServer();
const transport = new StdioServerTransport();
await server.connect(transport);

在这种模式下:

  • Claude Code 是 MCP Client;
  • CorpRAG 是 MCP Server;
  • 双方通过标准输入和标准输出传递 MCP 协议消息;
  • 服务端运行日志应写入标准错误,避免干扰协议消息。

STDIO 适合本地知识库场景,不需要额外监听网络端口,也不必将知识库服务暴露到公网。

3. 工作目录

cwd 指定 MCP Server 启动时的工作目录。当服务需要读取环境变量、本地数据或相对路径时,正确的工作目录很重要。显式配置 cwd 可以减少不同启动环境带来的路径差异。

修改 .mcp.json 后,需要让 Claude Code 重新加载 MCP 配置,再检查当前会话是否已发现 corprag 服务及其工具。


三、工具注册机制

CorpRAG 不会为每个系统手工注册工具,而是通过配置驱动的方式动态注册。

ts 复制代码
for (const system of SYSTEMS) {
    const toolName = `${system}_query`;
    server.registerTool(toolName, { /* ... */ });
}

SYSTEMS 配置定义了当前项目需要接入的知识库系统。例如,配置中只包含 fr 时,注册的工具为 fr_query。每个工具都接受相同的参数结构:

json 复制代码
{
  "question": "需要查询的具体问题",
  "docType": "FADR"
}
参数 说明
question 要进行语义检索的具体问题
docType 文档类型,用于限定检索范围

docType 在服务端通过 Schema 约束为 FADRBADR

  • FADR:技术架构与实现决策
  • BADR:业务架构、业务规则与合规决策

底层允许省略 docType 以同时检索两类文档,但客户端规则通常要求每次调用都显式传入。这样做有三个好处:

  1. 缩小检索范围;
  2. 减少无关片段干扰;
  3. 让调用过程更容易观察和排查。

四、从工具参数到向量检索

当 Claude Code 发起如下调用时:

json 复制代码
{
  "question": "当前技术方案需要满足哪些约束?",
  "docType": "FADR"
}

MCP Server 内部经历以下步骤。

第一步:确定系统

调用的是 fr_query,目标系统为 fr。系统路由避免了在所有数据中进行无边界检索。

第二步:确定文档类型

docType 决定查询哪一类文档。例如系统为 fr、文档类型为 FADR 时,对应的向量表为 fr_fadr_kb

第三步:将问题转换为向量

使用与建库时相同的 Embedding 模型将问题转换为向量。如果建库和查询使用了不同模型,它们产生的向量不在同一个语义空间中,检索质量无法保证。

第四步:执行相似度检索

ts 复制代码
store.similaritySearch(question, topK)

topK 表示返回多少个相关片段。向量检索不是简单的关键词匹配,即使用户的表达与文档原文不同,只要语义相近,也可能召回相关内容。

第五步:格式化结果

每个检索结果保留以下字段返回给客户端:

  • content:知识片段正文
  • source:片段来源
  • docType:文档类型

来源和文档类型可以帮助模型区分检索证据,也方便排查错误召回。


五、为什么只配置 MCP 还不够

完成 .mcp.json 后,Claude Code 可以发现 MCP 工具,但不一定会在正确的时间使用正确的工具。

例如,用户提出一个宽泛的问题:

text 复制代码
请为当前需求提供实现建议。

如果缺少调用规则,AI 可能出现以下情况:

  • 不查询知识库,直接依赖通用经验回答;
  • 选择了错误的工具(如用 rebuild_kb 代替查询);
  • 调用工具时没有传 docType
  • 问题同时涉及两类文档,却只查询了一类;
  • 已经开始修改代码,之后才查询项目约束;
  • 因工具显示名称不同(mcp__corprag__fr_query vs corprag.fr_query),错误判断 MCP 工具不存在。

因此,连接 MCP Server 之后,还需要给 AI 定义一套稳定的调用规则。


六、通过 CLAUDE.local.md 定义调用入口

CLAUDE.local.md 适合保存只在本地生效、不希望提交到仓库的规则。

在 frodo 的实际项目中,CLAUDE.local.md 没有直接内联所有查询规则,而是采用了一层间接委托:

markdown 复制代码
# frodo 本地决策入口(按需查询)

## 强制路由

收到任务后,若命中以下决策场景,必须在调用其他 Agent / Skill 前,
先加载并执行 decision-guide:

- 技术决策(FADR):前端架构、组件设计、状态管理、性能优化、构建配置等
- 业务决策(BADR):业务规则、交易流程、合规约束、权限控制、渠道差异等
- 混合决策:同时包含技术实现与业务目标

执行入口:.claude/skills/decision-guide/SKILL.md

这种设计的好处是:

  • CLAUDE.local.md 只负责"触发判断",不承载具体查询逻辑;
  • 具体的识别规则、查询参数、冲突处理全部封装在 Skill 中;
  • 多个项目可以复用同一套 Skill,只需在本地规则中配置触发条件。

规则层应该覆盖的内容

一套完整的本地调用规则至少应该覆盖以下五部分:

1. 触发条件

明确什么情况下需要查询知识库,什么情况下不需要。简单的 Bug 修复、样式微调、文案修改等可以直接按常规流程执行,无需查询。

2. 系统路由

判断问题属于哪个系统,选择对应工具。如果无法判断,应先询问用户,而不是随意查询某个知识库。

3. 文档类型路由

规则需要说明什么情况下选择 FADR,什么情况下选择 BADR。具体触发关键词由项目内部定义。

4. 调用顺序

建议固定为:

text 复制代码
理解用户问题
  → 判断是否需要查询知识库
  → 是 → 加载 Skill
  → Skill 识别类型并改写问题
  → 调用 MCP 工具
  → 整理返回结果
  → 再执行后续任务

"先查询约束,再执行任务"是关键。如果 AI 已经根据通用经验形成了完整方案,知识库就只能变成事后补充,而不能真正约束执行过程。

5. 降级边界

允许降级,但必须先确认工具确实无法提供结果。仅在以下情况允许降级:

  1. 已实际调用工具,但 MCP Server 返回错误;
  2. 调用成功,但没有检索到相关片段;
  3. 已检查当前会话工具列表,确认对应工具不存在。

不能仅仅因为规则中写的是 corprag.fr_query 而运行时显示的是 mcp__corprag__fr_query,就判断工具不可用。


七、通过 Skills 封装可复用查询流程

CLAUDE.local.md 解决的是"什么情况下触发",而 Skill 解决的是"触发后怎么做"。

实际项目中,decision-guide Skill 封装了完整的查询流程:

text 复制代码
接收任务
  ↓
识别是否属于决策场景
  ├─ 否 → 按常规路由继续
  └─ 是
      ↓
识别 FADR / BADR / 混合类型
      ↓
将问题改写为具体、单一的决策问题
      ↓
按类型调用 corprag.fr_query
      ├─ FADR → docType: "FADR"
      ├─ BADR → docType: "BADR"
      └─ 混合 → 拆分并分别查询
      ↓
提取适用决策、约束和禁止项
      ↓
处理冲突并形成执行建议
      ↓
路由到对应 Agent/Skill 或输出结论

这几种配置承担不同职责:

方式 主要职责
.mcp.json 声明 MCP Server 在哪里、如何启动
CLAUDE.local.md 定义何时触发知识库查询
Skills 将多步骤查询流程封装为可复用能力

三层架构可以概括为:

text 复制代码
连接层:.mcp.json
触发层:CLAUDE.local.md
流程层:Skills

如果希望 Claude 在日常对话中自动判断是否查询,CLAUDE.local.md 更直接;如果希望将一套固定步骤作为能力复用,Skills 更合适。二者可以同时使用。


八、两类文档同时相关时如何处理

某些问题可能同时涉及 FADR(技术决策)和 BADR(业务决策)。

此时不建议省略 docType 进行一次混合查询,而应该先把问题拆分。例如,用户提出:

text 复制代码
请结合项目已有约束,给出当前需求的实现方案。

客户端可以分别构造两个查询:

json 复制代码
{
  "question": "当前需求涉及哪些技术架构约束?",
  "docType": "FADR"
}

以及:

json 复制代码
{
  "question": "当前需求涉及哪些业务规则和合规约束?",
  "docType": "BADR"
}

两次调用完成后,再由大模型合并结果。合并时按优先级处理:先满足业务目标和合规要求,再在技术架构允许范围内选择实现方案。

这种方式比一次混合检索更容易控制:

  • 每次查询的目标明确;
  • 两类结果不会互相挤占 topK
  • 可以分别判断某类文档是否有结果;
  • 便于记录调用日志和分析召回质量。

九、一次完整调用是怎样发生的

假设用户在 Claude Code 中提出一个涉及技术决策的问题:

text 复制代码
请查询 fr 系统的已有约束,并给出当前需求的处理建议。

1. Claude Code 读取 CLAUDE.local.md

命中技术决策触发场景,加载 decision-guide Skill。

2. Skill 识别决策类型

判断问题属于 FADR(技术架构与实现决策)。

3. Skill 改写问题并调用 MCP

将宽泛问题改写为具体查询:

json 复制代码
{
  "question": "当前需求涉及哪些技术架构约束?",
  "docType": "FADR"
}

运行时对应的工具名为 mcp__corprag__fr_query

4. CorpRAG 执行向量检索

text 复制代码
fr_query
  → fr_fadr_kb
  → Embedding
  → similaritySearch
  → Top K 片段

5. MCP Server 返回结构化文本

将片段正文、来源和类型格式化后返回给 Claude Code。

6. Claude 生成最终回答

Claude 应完成以下工作:

  1. 根据检索结果回答当前问题;
  2. 不把无关片段强行加入答案;
  3. 区分知识库内容与模型推断;
  4. 检索结果不足时明确说明,而不是补写不存在的内部规则。

这就是完整的 RAG 闭环:

text 复制代码
检索负责提供依据,大模型负责结合问题组织答案。

十、为什么不在会话启动时加载全部文档

一种简单方案是在 Claude Code 启动时直接导入全部知识文档。这种方式在文档很少时可以工作,但随着内容增长,会出现几个问题。

1. 占用上下文窗口

大量与当前任务无关的内容长期驻留在上下文中,挤占代码和问题分析空间。

2. 增加注意力干扰

模型同时面对大量规则,不一定比只提供少量相关片段效果更好。

3. 增加 Token 消耗

如果每轮对话都携带完整文档,会产生不必要的输入成本。

4. 增加信息暴露范围

一次性加载全部内容,会让与当前任务无关的内部信息也进入会话上下文。按系统、按类型、按问题检索,可以遵循最小必要原则。

5. 不利于知识更新

文档更新后,旧会话中已经加载的内容可能过期;按需检索则可以访问当前索引。

更合理的方式是:

text 复制代码
内部文档
  → 建立向量索引
  → 按系统和类型查询
  → 只返回相关片段
  → 临时注入当前上下文

CLAUDE.local.md 只保存触发规则,Skills 封装查询流程,真实知识正文始终在向量库中按需检索。


十一、常见问题与排查方法

1. Claude Code 中看不到 corprag 工具

依次检查:

  • .mcp.json 是否位于正确的项目目录;
  • Node.js 命令是否可执行;
  • MCP Server 入口文件是否已经构建;
  • cwd 是否正确;
  • 服务端是否把普通日志错误地写入标准输出;
  • 修改配置后是否重新加载了 Claude Code 会话。

2. MCP Server 可以启动,但知识库不存在

MCP Server 启动成功只说明客户端与服务端已经建立连接。向量表仍然需要提前创建。例如,要查询 fr_fadr_kb,必须先完成该表对应文档的加载、切分和向量化。

3. 工具可以调用,但召回结果不准确

可以从以下方面排查:

  • question 是否足够具体;
  • 工具是否选择正确(如用 fr_query 还是 rebuild_kb);
  • docType 是否选择正确;
  • 建库与查询是否使用相同的 Embedding 模型;
  • 文档切分粒度是否合理;
  • topK 是否合适;
  • 知识库本身是否包含相关内容。

4. Claude 选择了错误的系统或文档类型

这通常不是 MCP 连接问题,而是客户端规则不够明确。需要在规则或 Skill 中补充:

  • 触发条件(什么情况下需要查询);
  • 系统识别方式;
  • 文档类型映射;
  • 混合问题拆分方式;
  • 调用顺序;
  • 无法判断时是否先询问用户;
  • 查询失败后的降级条件。

5. 检索结果包含不应对外展示的信息

RAG 检索和答案输出是两个不同的权限环节。系统不仅要控制"能不能查",还要控制"查到后能不能展示"。不要把"工具调用成功"视为"内容可以公开"的依据。


十二、总结

AI 客户端调用 MCP 服务,不只是增加一份 .mcp.json 配置。一条稳定的知识调用链需要同时具备:

  1. 服务连接:Claude Code 启动并连接 CorpRAG;
  2. 工具发现 :客户端识别 fr_query 等工具;
  3. 触发判断CLAUDE.local.md 决定何时需要查询知识库;
  4. 流程封装:Skill 将识别、改写、查询、处理封装为可复用流程;
  5. 类型路由 :通过 docType 限定知识范围;
  6. 向量检索:将问题转换为向量并返回相关片段;
  7. 答案组合:Claude 将检索结果与当前问题组合成最终回答;
  8. 安全治理:控制查询权限、输出范围、日志内容和公开信息。

MCP 解决的是客户端与工具之间的连接问题,RAG 解决的是知识检索问题,而 CLAUDE.local.md 与 Skills 解决的是 AI 在什么条件下、按照什么流程使用这些能力的问题。

相关推荐
程序员黑豆1 小时前
鸿蒙应用开发:Scroll 组件从入门到实战
前端·华为·harmonyos
llwszx1 小时前
【Java/Go后端手撸原生Agent(第七篇):Token预算管理 + 滑动窗口上下文裁剪】
java·后端·python·agent开发·上下文工程·上下文裁剪·滑动窗口裁剪
AskHarries2 小时前
CDN 要不要上
后端
a1117762 小时前
三色软糖坠落玻璃池 THreeJS kimi
前端·人工智能·threejs
wu8587734572 小时前
从 Prompt 到 Loop:拆解 AI 工程化四范式的演进逻辑与落地边界
人工智能·ai·prompt·aigc·ai编程
大龄码农有梦想3 小时前
Codex、Claude Code 等 AI 编程工具对软件工程的启发
人工智能·软件工程·agent·ai编程·ai agent·智能体·智能体平台
凤山老林3 小时前
SpringBoot 3 启用 spring.factories 后如何升级替换原有的 spring.factories ?
spring boot·后端·spring
吃饱了得干活3 小时前
JVM垃圾回收:从新生代到ZGC,从理论到调优
java·jvm·后端
Conan在掘金3 小时前
鸿蒙报错速查:arkts-no-func-expressions 禁用 function 表达式,用了就炸,根因 + 真解法
后端