docs(rag): add MemoryBear RAG implementation docs v1.0
Some checks failed
Sync to Gitee / sync (push) Has been cancelled

Submit the formed RAG documentation set produced across Sprint-1/2/3
(WS-12 through WS-26) under docs/rag/. Includes:

- README.md / INDEX.md: landing + total index (responsibility matrix,
  review verdicts, dual-link to source issues)
- overview/: full-pipeline architecture (4 .mmd diagrams),
  11-stage boundary contracts, doc map, source-code inventory
- pipeline/: 5 deep-dives (Loader/Parser/Chunking, Embedding,
  VDB & retrieval, GraphRAG, Rerank/Prompt/LLM)
- graphrag/, end-to-end/: v1.0 formal versions with full source
  retained as reference
- evolution/: 11 architecture-refactor proposals,
  6-direction roadmap, capability map
- review/: S3-T1 / S3-T2 final reviews, S2-T7 final summary
- _indexes/: glossary (81 terms), source->doc reverse index, chart index
- _release/: v1.0-RC1 release manifest, versioning convention,
  ops & freshness plan
- _meta/README.md: placeholder noting WS-12 governance assets gap

Aggregate review score 92.6/100 (8/8 PASS, 31/31 source-code spot
checks hit). The legacy docs/ ignore in .gitignore is narrowed to
docs/* with an explicit allowlist for docs/rag/.

Refs: WS-26
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
Multica PM Agent
2026-05-09 10:51:48 +08:00
parent feae2f2e1e
commit 343a5eebe3
33 changed files with 8410 additions and 1 deletions

View File

@@ -0,0 +1,445 @@
---
# [S2-T5] 检索后处理与生成Reranking / Prompt 工程 / LLM 调用 / 后处理)实现详解
**author:** Python 开发工程师
**source-commit:** `feae2f2e` (Merge PR #1033 release/v0.3.2)
**reviewer:** 待 [S2-T7] 评审
**last-reviewed-at:** 2026-05-08
---
## 一句话定位
本文档覆盖 MemoryBear RAG 链路的后半段:从检索结果进入系统,到最终 LLM 生成答案并输出给用户的全过程包括重排序、Prompt 组装、多模型 LLM 调用、流式输出、工具调用及生成后处理。
## 设计目标与适用场景
- **设计目标**:在多知识库、多检索策略(关键词 / 向量 / 混合 / GraphRAG返回的原始结果上通过重排序提升相关性通过 Prompt 工程高效利用上下文,通过多提供商 LLM 封装实现高可用调用,最终输出带引用溯源、支持流式/非流式的答案。
- **适用场景**
- Agent 聊天(`app_chat_service.py` / `draft_run_service.py`
- Workflow 知识检索节点(`workflow/nodes/knowledge/node.py`
- 独立 chunk 检索 API`chunk_controller.py`
## 关键概念与术语表
| 术语 | 含义 |
|------|------|
| Rerank | 在初步召回后对 chunk 进行精细重排序 |
| RedBearRerank | 基于 LangChain `BaseDocumentCompressor` 的 rerank 封装 |
| Dealer | 底层检索调度器,负责混合搜索、内置 rerank、引用插入 |
| KnowledgeRetrievalNode | Workflow 引擎中的知识检索节点 |
| LangChainAgent | 基于 `create_agent` 的 ReAct Agent负责工具调用循环 |
| citation | 生成后处理阶段向答案文本中插入 `[ID:N]` 引用标记 |
| rank_feature | 基于 tag 特征和 PageRank 的辅助排序分 |
## 实现概览Mermaid 流程图)
```
检索结果输入
┌─────────────────┐
│ Rerank 层 │
│ A:内置混合 │
│ B:外部模型 │
│ C:RedBearRerank │
│ D:ES层封装 │
└────────┬────────┘
┌─────────────────────────┐
│ Prompt 工程与上下文组装 │
│ 系统 Prompt + 技能 Prompt │
│ 知识上下文拼接 │
│ Token 预算管理 │
└────────┬────────────────┘
┌─────────────────────────┐
│ LLM 调用层 (LangChainAgent)│
│ ReAct 工具调用循环 │
│ 流式/非流式 │
│ 多模态 + 深度思考 │
└────────┬────────────────┘
┌─────────────────────────┐
│ 生成后处理 │
│ 引用过滤 + 下载链接 │
│ 引用插入 (embedding 匹配) │
│ JSON 结构化校验 │
└─────────────────────────┘
```
---
## 1. Reranking 章节
### 1.1 是否使用显式 Rerank
**是**。MemoryBear 在多处实现了 rerank采用"多方案并存、按场景选择"策略。
### 1.2 Rerank 方案全景
#### 方案 A内置混合 RerankDealer.rerank
**源码**`api/app/core/rag/nlp/search.py:606-643`
核心融合公式:
```
score = tkweight * token_similarity + vtweight * vector_similarity + rank_feature
```
- `tkweight` 默认 0.3`vtweight` 默认 0.7
- `token_similarity`:基于 rag_tokenizer 分词后的 Jaccard 风格相似度
- `vector_similarity`query_vector 与 chunk 向量的余弦相似度
- `rank_feature`tag 特征 TF-IDF 余弦 + PageRank缩放 10 倍(`search.py:579-604`
- token 权重分配:`content_ltks + title_tks*2 + important_kwd*5 + question_tks*6`
#### 方案 B外部 Rerank 模型Dealer.rerank_by_model
**源码**`api/app/core/rag/nlp/search.py:645-666`
将向量相似度替换为外部 rerank 模型的 `similarity()` 输出,保留 token 相似度和 rank_feature。
#### 方案 CRedBearRerankLCEL 兼容封装)
**源码**`api/app/core/models/rerank.py:11-84`
- 继承 `langchain_core.documents.BaseDocumentCompressor`
- 支持 `XINFERENCE` / `GPUSTACK``JinaRerank`
- 支持 `DASHSCOPE``DashScopeRerank`
- 端点自动规范化:补齐 `/v1/rerank`
使用场景:
- Workflow `KnowledgeRetrievalNode.rerank()``node.py:108-155`
- `ElasticSearchVector.rerank()``elasticsearch_vector.py:560-607`
- `nlp/search.py:rerank()``search.py:284-343`
#### 方案 DElasticSearchVector 层 Rerank
ES Vector 初始化时注入 `reranker_config``rerank()` 中调用 `self.reranker.compress_documents()`
### 1.3 阈值与延迟
- **内置 rerank**:本地 numpy 计算,毫秒级延迟
- **外部 rerank**:网络调用,本地 Xinference <10ms远程 DashScope 100-500ms
- **相似度阈值**`similarity_threshold` 默认 0.2,低于此值的 chunk 被过滤(`search.py:674-768`
### 1.4 为什么没有统一使用 Cross-Encoder
- Cross-Encoder 需额外部署,对小型部署不友好
- 内置 `Dealer.rerank` 在多数场景已足够
- RedBearRerank 作为可选增强,仅在显式配置 `reranker_id` 时启用
---
## 2. Prompt 工程与上下文组装
### 2.1 Prompt 模板组织
**目录**`api/app/core/rag/prompts/`
| 模板文件 | 用途 |
|----------|------|
| `ask_summary.md` | 知识库问答主 Prompt |
| `citation_prompt.md` | 引用标注规范(`[ID:i]` 格式) |
| `citation_plus.md` | 引用回填 Agent Prompt |
| `question_prompt.md` | 文本生成问题 |
| `keyword_prompt.md` | 关键词提取 |
| `structured_output_prompt.md` | JSON Schema 约束 |
| `cross_languages_*.md` | 跨语言查询扩展 |
| `analyze_task_*.md` | 任务分析与工具选择 |
**加载机制**`api/app/core/rag/prompts/template.py:9-20`,启动时加载并缓存。
### 2.2 上下文组装流程
**Agent 层**`api/app/core/agent/langchain_agent.py:230-271`
```python
def _prepare_messages(self, message, history, context, files):
messages = []
for msg in history:
if msg["role"] == "user": messages.append(HumanMessage(...))
elif msg["role"] == "assistant": messages.append(AIMessage(...))
user_content = message
if context:
user_content = f"参考信息:\n{context}\n\n用户问题:\n{user_content}"
messages.append(HumanMessage(content=user_content))
return messages
```
### 2.3 知识检索工具中的 Chunk 拼接
**源码**`api/app/services/draft_run_service.py:227-255`
```python
retrieve_chunks_result = knowledge_retrieval(query, kb_config)
retrieval_knowledge = [i.page_content for i in retrieve_chunks_result]
context = '\n\n'.join(retrieval_knowledge)
return f"检索到以下相关信息:\n\n{context}"
```
- chunk 间用 `\n\n` 分隔
- 引用信息document_id、file_name、score由外部 `citations_collector` 收集,与上下文字符串分离
- 属于"隐式引用"策略LLM 看不到 `[ID:N]`,引用回填在生成后完成
### 2.4 Token 预算管理
**源码**`api/app/core/rag/prompts/generator.py:46-80`
策略:
1. 计算总 token未超限直接返回
2. 超限后保留 `system` + 最后一条消息,丢弃中间历史
3. 仍超限则按比例截断 system 或 user 内容
### 2.5 System / User 分层结构
```
system: {用户自定义 system_prompt} + {技能 Prompt} + {文档图片识别指令}
user: {历史消息...}
user: 参考信息:\n\n{chunks}\n\n用户问题\n{query}
```
System Prompt 组装见 `app_chat_service.py:77-96`:先变量替换,再追加 skill_prompts。
---
## 3. LLM 调用
### 3.1 支持的模型与切换机制
**核心封装**`api/app/core/rag/llm/chat_model.py:52-63`
`Base` 类基于 OpenAI 兼容 API子类覆盖
| 类名 | 提供商 |
|------|--------|
| `GptTurbo` | OpenAI |
| `XinferenceChat` | Xinference |
| `HuggingFaceChat` | HuggingFace |
| `ModelScopeChat` | ModelScope |
| `AzureChat` | Azure OpenAI |
| `BaiChuanChat` | 百川 |
| `LocalAIChat` | LocalAI |
| `VolcEngineChat` | 火山引擎 |
| `OpenAI_APIChat` | VLLM / OpenAI-API-Compatible |
| `GPUStackChat` | GPUStack |
**切换机制**`ModelApiKeyService.get_available_api_key()` 根据 `model_id` 从数据库读取 provider/api_key/base_url/model_name运行时动态实例化。
### 3.2 流式 vs 非流式
**非流式**`Base._chat()``chat_model.py:122-150`
- `stream=False`,返回 `(text, total_tokens)`
- QWQ 推理模型强制内部走流式聚合,过滤 `<think>` 标签
**流式**`Base._chat_streamly()``chat_model.py:152-185`
- `stream=True`yield `(delta, token_count)`
- 支持 `reasoning_content` 提取
- `finish_reason == "length"` 时自动追加截断提示(中英文自适应)
**Agent 流式**`LangChainAgent.chat_stream()`
- `agent.astream_events(version="v2")`
- 处理 `on_chat_model_stream` / `on_llm_stream`
- 支持多模态响应解析OpenAI + 通义千问格式)
### 3.3 超时、重试、降级
**源码**`chat_model.py:64-89, 192-215`
- 超时:`LLM_TIMEOUT_SECONDS`(默认 600s
- 重试:`LLM_MAX_RETRIES`(默认 5+ 随机抖动延迟
- 仅对 `RATE_LIMIT` / `SERVER_ERROR` 重试
- **降级**:无自动模型降级,失败返回 `"**ERROR**: ..."`
### 3.4 函数调用 / 工具使用
**源码**`chat_model.py:251-303, 335-436`
- 最多 `max_rounds`(默认 5轮工具调用循环
- 工具参数解析使用 `json_repair.loads()` 增强容错
- 流式工具调用:`chat_streamly_with_tools()`
**Agent 工具循环**`LangChainAgent`
- `create_agent(model, tools, system_prompt)`
- `max_iterations = 5 + len(tools) * 2`
- 单个工具最大连续调用:`max_tool_consecutive_calls = 3`
- `_wrap_tools_with_tracking()` 防循环
### 3.5 CV 模型与序列到文本模型
**CV 模型**`cv_model.py``QWenCV``AzureGptV4` — 用于图片/版面分析。
**序列到文本**`sequence2txt_model.py``QWenSeq2txt`(带时间戳 ASR`GPTSeq2txt`Whisper— 用于音视频预处理。
---
## 4. 生成后处理
### 4.1 引用回填Citation Insertion
**源码**`api/app/core/rag/nlp/search.py:489-577`
流程:
1. 将答案按句子切分(避开代码块 ```` ``` ````
2. 对每句话 embedding与 chunk embeddings 计算 hybrid similarity
3. 阈值从 0.63 开始动态衰减×0.8),最低 0.3
4. 每句最多引用 4 个 chunk句末插入 `[ID:N]`
### 4.2 引用过滤与下载链接
**源码**`api/app/services/draft_run_service.py:474-490`
- `features_config.citation.enabled` 开关控制
- `allow_download=True` 时附加 `download_url`
### 4.3 安全过滤
当前版本无显式敏感词过滤模块。安全依赖:
- LLM 提供商自带内容过滤
- `ERROR_CONTENT_FILTER` 错误码捕获
### 4.4 输出结构化JSON Schema
**源码**`api/app/core/agent/langchain_agent.py:85-92`
通过 system prompt 注入 `"\n请以JSON格式输出。"` 实现(非 `response_format` API因为 LangChain Agent 有工具时无法使用原生 API。
---
## 5. 端到端示例
### 场景Agent 聊天触发知识库检索
**Step 1** — 用户提问:`"MemoryBear 的 Rerank 策略是什么?"`
**Step 2** — System Prompt 组装:
```
你是一个专业的 AI 知识库助手,名为 Miss R。
任务:根据知识库中的信息回答用户问题。
要求:不要编造信息;使用 Markdown用用户提问的语言回答。
```
(来自 `ask_summary.md`
**Step 3** — LLM 判断调用 `knowledge_retrieval_tool`
工具内部:
```python
retrieve_chunks_result = knowledge_retrieval(query, kb_config)
context = '\n\n'.join([i.page_content for i in retrieve_chunks_result])
return f"检索到以下相关信息:\n\n{context}"
```
**Step 4** — 若配置 `reranker_id`,执行 RedBearRerank
```python
reranker = RedBearRerank(RedBearModelConfig(...))
reranked_docs = list(reranker.compress_documents(documents, query))
```
**Step 5** — Agent 组装消息并调用 LLM
```
system: 你是一个专业的 AI 知识库助手...
user: 参考信息:\n\nChunk 0...\n\nChunk 1...\n\n用户问题\nMemoryBear 的 Rerank 策略是什么?
```
**Step 6** — 输出后处理:
```python
filtered_citations = _filter_citations(features_config, citations_collector)
```
最终返回content + citations含 document_id、file_name、score、可选 download_url
---
## 6. 关键源码索引
| 功能 | 文件 | 类/函数 | 行号 |
|------|------|---------|------|
| Rerank 封装 | `api/app/core/models/rerank.py` | `RedBearRerank` | 11-84 |
| 内置混合 Rerank | `api/app/core/rag/nlp/search.py` | `Dealer.rerank` | 606-643 |
| 外部模型 Rerank | `api/app/core/rag/nlp/search.py` | `Dealer.rerank_by_model` | 645-666 |
| rank_feature | `api/app/core/rag/nlp/search.py` | `_rank_feature_scores` | 579-604 |
| 独立 rerank | `api/app/core/rag/nlp/search.py` | `rerank()` | 284-343 |
| 知识检索入口 | `api/app/core/rag/nlp/search.py` | `knowledge_retrieval()` | 36-147 |
| ES Vector rerank | `api/app/core/rag/vdb/elasticsearch/elasticsearch_vector.py` | `ElasticSearchVector.rerank` | 560-607 |
| Workflow 节点 rerank | `api/app/core/workflow/nodes/knowledge/node.py` | `KnowledgeRetrievalNode.rerank` | 108-155 |
| Workflow 执行 | `api/app/core/workflow/nodes/knowledge/node.py` | `KnowledgeRetrievalNode.execute` | 303-378 |
| LLM 基类 | `api/app/core/rag/llm/chat_model.py` | `Base` | 52-319 |
| 流式 LLM | `api/app/core/rag/llm/chat_model.py` | `_chat_streamly` | 152-185 |
| 工具调用 | `api/app/core/rag/llm/chat_model.py` | `chat_with_tools` | 251-303 |
| 流式工具调用 | `api/app/core/rag/llm/chat_model.py` | `chat_streamly_with_tools` | 335-436 |
| 错误分类 | `api/app/core/rag/llm/chat_model.py` | `_classify_error` | 69-89 |
| CV 模型 | `api/app/core/rag/llm/cv_model.py` | `QWenCV`, `AzureGptV4` | 1-497 |
| 音频转录 | `api/app/core/rag/llm/sequence2txt_model.py` | `QWenSeq2txt`, `GPTSeq2txt` | 1-215 |
| Prompt 加载 | `api/app/core/rag/prompts/template.py` | `load_prompt` | 9-20 |
| Prompt 生成器 | `api/app/core/rag/prompts/generator.py` | `message_fit_in` 等 | 1-744 |
| Agent 封装 | `api/app/core/agent/langchain_agent.py` | `LangChainAgent` | 26-641 |
| Agent 消息准备 | `api/app/core/agent/langchain_agent.py` | `_prepare_messages` | 230-271 |
| 知识检索工具 | `api/app/services/draft_run_service.py` | `create_knowledge_retrieval_tool` | 195-263 |
| 引用过滤 | `api/app/services/draft_run_service.py` | `_filter_citations` | 474-490 |
| 聊天服务 | `api/app/services/app_chat_service.py` | `agnet_chat` | 43-239 |
| 流式聊天 | `api/app/services/app_chat_service.py` | `agnet_chat_stream` | 340-550 |
| 引用插入 | `api/app/core/rag/nlp/search.py` | `Dealer.insert_citations` | 489-577 |
---
## 7. 配置项与可调参数
**环境变量**
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `LLM_TIMEOUT_SECONDS` | 600 | LLM 超时 |
| `LLM_MAX_RETRIES` | 5 | 最大重试 |
| `LLM_BASE_DELAY` | 2.0 | 重试基础延迟 |
**知识检索配置**
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `retrieve_type` | `participle` | participle/semantic/hybrid/graph |
| `similarity_threshold` | 0.2 | 关键词相似度阈值 |
| `vector_similarity_weight` | 0.3 | 向量权重 |
| `top_k` | 4 | 单次检索 chunk 数 |
| `reranker_id` | `None` | Rerank 模型 ID |
| `reranker_top_k` | 4 | Rerank 后最终返回数 |
**Agent 参数**
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `max_iterations` | `5 + len(tools) * 2` | Agent 最大迭代 |
| `max_tool_consecutive_calls` | 3 | 单工具最大连续调用 |
| `max_rounds` | 5 | LLM 工具调用最大轮数 |
| `temperature` | 0.7 | 生成温度 |
| `max_tokens` | 2000 | 最大生成 token |
| `json_output` | `False` | 强制 JSON 输出 |
| `deep_thinking` | `False` | 深度思考 |
---
## 8. 边界条件与已知限制
1. **外部 Rerank 延迟高**RedBearRerank 调用 Jina/DashScope API无本地缓存。
2. **Token 裁剪较粗糙**`message_fit_in` 丢弃中间历史,可能丢失上下文;按比例截断可能切断语义。
3. **引用回填非 LLM 原生**:基于 embedding 相似度匹配,表述不同可能漏引。
4. **JSON 输出兼容性差**:通过 system prompt 注入实现,可靠性低于原生 `response_format`
5. **无模型降级**LLM 失败返回错误文本,不自动切换备用模型。
6. **混合检索融合简单**:仅去重取并集,无 RRF 或加权分数融合。
7. **GraphRAG 结果前置**:始终 `insert(0, ...)`,优先级最高但无分数参与 rerank。
---
## 9. 优化建议与未来扩展点
1. **Rerank 缓存**:对高频 query 做 LRU 缓存,降低外部 API 成本。
2. **引用增强**:将 `citation_prompt.md` 注入 system prompt让 LLM 生成阶段就输出 `[ID:N]`
3. **Token 预算精细化**:引入 `tiktoken` 精确计数,实现滑动窗口历史管理。
4. **模型降级**:在 `Base.chat()` 中增加 fallback 模型链。
5. **混合检索 RRF**:在 ES 查询层面实现 Reciprocal Rank Fusion。
6. **流式引用**:在 `on_tool_end` 事件中实时 emit citation 元数据。
7. **输出校验中间件**:对 `json_output=True` 增加 JSON Schema 强制校验层。
---
以上为 [S2-T5] 初版全文,请评审。