数据截至 (上游 commit c4cb19d8fa20)
AutoFlow — 架构与原理
30 秒导读: AutoFlow(线上叫 tidb.ai)是一个 Graph RAG 知识库:你喂给它文档,它一边切块存向量、一边用 LLM 抽出「实体—关系」知识图谱;查询时先用图谱把你那句口语化的问题精化成精准问题,再去向量库捞相关片段喂给大模型答题。底层全部存在 TiDB 里(向量、图、聊天记录共用一个库)。本文聚焦它的 检索(retrieval) 这一面。
1. 这是什么(零基础也能懂)
一句话定义: AutoFlow 是一个开源的「带知识图谱的 RAG 检索引擎」——RAG(Retrieval-Augmented Generation,检索增强生成:答题前先从你自己的资料里检索证据,再让大模型基于证据回答)。
它和普通 RAG 差在哪? 普通 RAG 只做一件事:把问题向量化 → 在向量库里找最像的几段文字 → 塞给大模型。AutoFlow 在前面多加了一层 知识图谱。
解决什么问题 / 给谁用: 假设你把一整套产品文档灌进去,用户问「TiDB 怎么扩容?会不会影响在线业务?」。这种问题口语、含糊、跨多个主题。
- 纯向量检索容易只命中字面相近的段落,抓不住「扩容」和「在线业务影响」之间的关联。
- AutoFlow 先去知识图谱里查:与「扩容」相关的实体、关系是什么?把这些上下文喂回去,把问题改写得更精准,再去捞 chunk。
所以它给的是:文档站、客 服机器人、Perplexity 式对话搜索这类需要「答得准、还能溯源」的场景。
它能做什么(检索侧功能):
- 多知识库(multi-KB)联合检索,结果融合去重。
- 把复杂问题分解成子问题(query decomposition),分别检索再合并。
- 知识图谱检索:从问题出发,在「实体—关系」图上做带权重的多跳扩散。
- 向量检索 chunk:TiDB 向量近邻搜索 + 过采样(oversampling)+ 可选 reranker 重排。
- 用图谱上下文精化(refine)用户问题后再检索。
用起来什么样: 检索能力直接暴露成 HTTP 接口,给一个 query 和配置就回结果——
# 示意,非源码:调用 chunk 检索接口
POST /api/v1/admin/retrieve/chunks
{
"query": "TiDB 怎么扩容?",
"retrieval_config": {
"knowledge_base_ids": [1],
"use_query_decompose": true, # 先把问题拆成子问题
"vector_search": { "top_k": 10, "oversampling_factor": 5 }
}
}
# 返回:命中的 chunks(带 score)+ 它们所属的文档
真实路由见 backend/app/api/routes/retrieve/routes.py:23(retrieve_chunks)与 :46(retrieve_knowledge_graph)。
一句话直觉/类比: 把它想成**「先查目录索引,再翻书」**。知识图谱是那本书的「概念地图 / 索引」,它帮你想清楚该往哪几页翻;向量库才是真正存着原文段落的书页。AutoFlow 的精髓就是 让索引先发言,改写你的问题,再去翻书页。
2. 顶层全景(它大概怎么转)
一次检索请求,数据这样流过(从上到下):
用户问题 user_question
│
▼
┌──────────────────────────────────────────────┐
│ RetrieveFlow.retrieve() │ ← 检索总编排
│ retrieve_flow.py │
└──────────────────────────────────────────────┘
│
│ ① 若 refine_question_with_kg 开启
▼
┌──────────────────────────────┐
│ 知识图谱检索 (第 3 章) │ 从问题 embedding 出发,
│ KnowledgeGraphFusionRetriever │ 在实体/关系图上带权多跳扩散,
│ → TiDBGraphStore │ 得到 entities + relationships
│ .retrieve_with_weight() │
└──────────────────────────────┘
│
│ ② 把图谱上下文喂给 fast LLM,改写问题
▼
refined_question (更精准的问题)
│
│ ③ 用精化后的问题做向量检索
▼
┌──────────────────────────────┐
│ chunk 向量检索 (第 2 章) │ (可选)先拆子问题 → 各 KB
│ ChunkFusionRetriever │ 并发向量近邻搜索 → 融合去重
│ → ChunkSimpleRetriever │ → reranker 重排 → top_k
│ → TiDBVectorStore.query() │
└──────────────────────────────┘
│
▼
List[NodeWithScore] (按相似度排序的文字片段)
│
▼
喂给大模型生成答案 (ChatFlow._generate_answer)
怎么读这张图: 竖着从上到下是一次检索的时间顺序。注意 ①②③ 是有先后的:图谱检索的产物(②)会反过来改写问题,影响③向量检索的输入。这就是 Graph RAG 的「图谱先发言」。
部件一句话职责:
| 部件 | 干什么 | 在哪个文件 |
|---|---|---|
RetrieveFlow | 检索总编排:KG → 精化问题 → chunk | backend/app/rag/chat/retrieve/retrieve_flow.py |
ChatFlow | 对话总编排:在检索两侧加上澄清、答题、落库 | backend/app/rag/chat/chat_flow.py |
MultiKBFusionRetriever | 多 KB 融合检索的抽象基类:子问题分解 + 并发 + 融合 | backend/app/rag/retrievers/multiple_knowledge_base.py |
ChunkFusionRetriever / ChunkSimpleRetriever | chunk 向量检索的融合层 / 单 KB 执行层 | backend/app/rag/retrievers/chunk/ |
KnowledgeGraphFusionRetriever / ...SimpleRetriever | 图谱检索的融合层 / 单 KB 执行层 | backend/app/rag/retrievers/knowledge_graph/ |
TiDBVectorStore | TiDB 上的向量近邻查询(带 oversampling) | backend/app/rag/indices/vector_search/vector_store/tidb_vector_store.py |
TiDBGraphStore | 图谱存储 + 带权重多跳检索 retrieve_with_weight | backend/app/rag/indices/knowledge_graph/graph_store/tidb_graph_store.py |
QueryDecomposer | 用 DSPy 把复杂问题拆成 ≤5 个子问题 | backend/app/rag/question_gen/query_decomposer.py |
主线走一遍(高层,不进代码):
- 入口
RetrieveFlow.retrieve(user_question)(retrieve_flow.py:66)。 - 若开启
refine_question_with_kg(默认开,chat/config.py:83):先search_knowledge_graph拿到图谱上下文,再_refine_user_question用 fast LLM 改写问题。 - 不管有没有精化,最后都走
search_relevant_chunks做向量检索(retrieve_flow.py:134),返回排好序的 chunk。
注意一个容易看漏的细节:
RetrieveFlow.search_relevant_chunks里把use_query_decompose=False写死了(retrieve_flow.py:140)——也就是说对话主路径默认不做子问题分解,分解能力主要由独立的/retrieve/chunksAPI 通过配置打开。
3. 阅读地图(往哪下钻)
建议顺序:
- 01-retrieve-flow.md — 先看主线怎么编排:KG 精化问题 + chunk 检索两步,以及
ChatFlow在外面套的完整对话循环。看完你就懂「图谱先发言」是怎么落地的。 - 02-chunk-retrieval.md — 向量检索这条线:多 KB 并发 + 子问题分解 + 融合去重(simple fusion)+ TiDB oversampling + reranker。
- 03-kg-retrieval.md — 最有技术含量的一章:
retrieve_with_weight的带权重、分距离段、多跳扩散算法,以及关系打分公式。 - 04-clever-and-boundaries.md — 巧妙之处、边界局限、与兄弟项目的横向对比、完整代码地图。
如果你只想抓精华:直接读第 3 章的 retrieve_with_weight(图谱检索的灵魂)+ 第 2 章的 _simple_fusion(融合去重)。