数据截至 (上游 commit c4cb19d8fa20)
第 1 章 · 检索主线:图谱先发言,再去翻书
这一章讲编排:一次检索请求被拆成哪几步、谁调谁。两个主角:
RetrieveFlow(纯检索)和ChatFlow(检索 + 答题的完整对话)。
1.1 RetrieveFlow:检索的三步
RetrieveFlow.retrieve 是整个检索的入口,逻辑短得惊人——三步:
# 真实源码节选,retrieve_flow.py:66
def retrieve(self, user_question: str) -> List[NodeWithScore]:
if self.engine_config.refine_question_with_kg:
# 1. 先在知识图谱里检索与问题相关的子图
_, knowledge_graph_context = self.search_knowledge_graph(user_question)
# 2. 用图谱上下文 + 聊天历史,把用户问题改写得更精准
self._refine_user_question(user_question, knowledge_graph_context)
# 3. 用(可能被精化过的)问题去向量库捞 chunk
return self.search_relevant_chunks(user_question=user_question)
这就是 Graph RAG 的核心套路:让知识图谱在向量检索之前发言,用它改写问题。refine_question_with_kg 默认是 True(chat/config.py:83)。
一个真实存在的细节(诚实标注): 上面第 2 步
self._refine_user_question(...)的返回值没有被接住——retrieve里调用它但丢掉了返回的refined_question,第 3 步仍然用原始user_question。也就是说在RetrieveFlow.retrieve这条路径上,精化的副作用(图谱检索本身、LLM 调用)发生了,但精化后的问题并没真正用于 chunk 检索。真正用上 refined_question 的是ChatFlow(见 1.4),它自己重新编排了这几步。
1.2 第一步:知识图谱检索 + 转成上下文文本
search_knowledge_graph(retrieve_flow.py:81)做两件事:
- 建一个
KnowledgeGraphFusionRetriever,调retrieve_knowledge_graph(user_question)拿到结构化的实体/关系(算法细节在第 3 章)。 - 把结构化结果渲染成一段文本
knowledge_graph_context,供 LLM 阅读。
渲染分两种模式,取决于 using_intent_search(意图搜索,默认开):
| 模式 | 何时用 | 渲染什么 | 模板 |
|---|---|---|---|
| intent | using_intent_search=True | 按子问题组织的子图(to_subqueries_dict()) | llm.intent_graph_knowledge |
| normal | 否则 | 扁平的 entities + relationships 列表 | llm.normal_graph_knowledge |
# 真实源码节选,retrieve_flow.py:101 _get_knowledge_graph_context
if self.engine_config.knowledge_graph.using_intent_search:
kg_context_template = RichPromptTemplate(self.engine_config.llm.intent_graph_knowledge)
return kg_context_template.format(sub_queries=knowledge_graph.to_subqueries_dict())
else:
kg_context_template = RichPromptTemplate(self.engine_config.llm.normal_graph_knowledge)
return kg_context_template.format(
entities=knowledge_graph.entities,
relationships=knowledge_graph.relationships,
)
to_subqueries_dict()(retrievers/knowledge_graph/schema.py:137)把各个子图按它的 query(子问题)聚合成 {子问题: {entities, relationships}} 的字典——这样喂给 LLM 的上下文是按意图分组的,而不是一锅乱炖。
1.3 第二步:用图谱上下文精化问题
_refine_user_question(retrieve_flow.py:120)就是一次 LLM 调用:把图谱上下文 + 当前日期塞进 condense_question_prompt,让 fast LLM(便宜的小模型)吐出改写后的问题。
# 真实源码节选,retrieve_flow.py:126
refined_question = self._fast_llm.predict(
prompt_template,
graph_knowledges=knowledge_graph_context, # 图谱给的上下文
question=user_question,
current_date=datetime.now().strftime("%Y-%m-%d"), # 注入「今天」,处理时效性问题
)
return refined_question.strip().strip(".\"'!")
为什么注入 current_date? 像「最新版本」这种问题,LLM 不知道「最新」是哪天。把今天的日期喂进去,精化时就能落到具体时间。