跳到正文
曾亮懿 ZengLiangYi Blog

从 RAG 到知识图谱:让个人知识检索保留关系

区分关键词检索、向量搜索、RAG 与笔记关系图,结合 ChatCrystal 的候选召回和一跳扩展实现,说明关系能补充什么、不能证明什么。

曾亮懿

曾亮懿

阅读约 6 分钟
目录 展开 收起

“记得讨论过,但搜不到”是整理 AI 对话时很常见的问题。找到了相似内容之后,又会出现另一个问题:这条笔记是解决方案、前置条件,还是后来被推翻的结论?

向量搜索和笔记关系图分别处理这两个问题的一部分。它们可以配合使用,但不能简单排成一条“关键词被向量淘汰、RAG 又被图谱淘汰”的升级路线。本文结合 ChatCrystal 提交 7642768 的实现,说明两者怎样连接,以及结果有哪些边界。

先分清检索与生成

方式 适合回答的问题 需要留意的边界
字面匹配 哪条记录包含这个错误码、符号或路径? 表达不同可能漏掉结果
全文检索 哪些文档匹配这些词,如何排序? 分词、语言和排序配置影响结果
向量搜索 哪些内容与这个问题在表示空间中接近? 相似不等于正确,也不等于因果
关系图 已记录哪些笔记之间的联系? 关系可能缺失、过期或被错误提取
RAG 怎样利用检索到的材料生成回答? 还需要上下文组织、生成和证据核对

RAG 原论文 讨论的是把检索到的外部信息与语言生成结合。只返回一组向量搜索结果,还没有完成生成这一步;图中的关系也可以成为生成时使用的材料。

关键词搜索依然适合精确定位 ECONNREFUSED 或某个函数名。全文检索也不能被统一描述成只会机械匹配:具体能力取决于分词、同义词和排序配置。尤其不能假设任意默认分词器都能把中文按预期切开。

语义相关不代表同一个原因

搜索“内存泄漏排查”时,“OOM”“对象未释放”“大批量文件读取”都可能成为相关线索,但它们不是同义词。OOM 也可能来自合理工作集超过内存预算,不能因为笔记接近就断言存在泄漏。

向量检索的价值,是在不知道原文措辞时给出候选材料。随后仍需要读正文,确认运行环境、触发条件、版本和验证方法是否适用于当前问题。

ChatCrystal 将笔记中的选定字段拼成索引文本,分块后生成向量,查询时再计算问题的向量。所用 Vectra LocalIndex 的向量路径按候选相似度评分,不应把它写成已经建立 HNSW 或 LSH 索引;具体选型见本地向量数据库选型。

应用还会对直接结果做字面匹配加权、低分过滤和按笔记去重。符合字面匹配条件的结果可能被提高分数,因此返回的 score 不能一律当作原始向量相似度。

这里的图谱以笔记为节点

ChatCrystal 的关系图主要连接笔记,不是自动抽取整个代码库中所有函数、模块和调用关系。理解这一点,才能判断它适合回答什么问题。

当前关系类型包括 CAUSED_BY、LEADS_TO、RESOLVED_BY、SIMILAR_TO、CONTRADICTS、DEPENDS_ON、EXTENDS 和 REFERENCES。其中有些关系带方向。例如下面只是一个构造的说明:

笔记 A:缓存失效时出现重复请求
笔记 B:用共享 Promise 合并并发请求

A --RESOLVED_BY--> B

反向浏览时,可以从 B 找回它解决的问题,但不能把边反过来解释成 B 被 A 解决。SIMILAR_TO 表达的相似,也不能替代 CAUSED_BY 所要求的原因证据。

自动发现的关系是候选结论

关系发现实现 先通过语义搜索寻找候选;没有可用候选时,回退到最近的笔记。它把候选信息与新笔记内容交给语言模型,限制关系类型,并过滤目标 ID 与置信度。

因此,它没有把新笔记与知识库中的每条笔记逐一比较。候选召回遗漏了某条记录,后续模型也就没有机会判断这条联系。当前实现还截取摘要以控制提示长度,重要条件可能没有进入关系判断的上下文。

模型输出的置信度是筛选信号,不是经过校准的事实概率。一条 CAUSED_BY 边即使分数较高,也需要回到笔记及原始材料核对。图谱增加了可见的联系,同时也增加了要维护的假设。

当前搜索扩展是一跳

语义搜索实现 先得到直接命中的笔记;开启关系扩展时,再查这些笔记相邻的关系,加入符合条件的关联笔记,并对分数折减。

这个循环遍历的是直接结果,不会把新加入的邻居继续无限展开。因此这里应理解为直接召回加一跳关系扩展,不能写成任意多跳推理引擎。关联结果带有 viaRelation 标记,但仅凭这个标记还不足以恢复完整的有向关系;需要时应读取关系记录。扩展结果的 chunkText 为空,正文也需要另行读取。

入口同样影响行为:HTTP 搜索接口 通过 expand=true 开启扩展,而 MCP 的 search_knowledge 仅提供 query 和 limit,当前调用不会开启关系扩展。recall_for_task 则通过 options.include_relations 控制这一行为,默认开启。

问题
↓ 生成查询向量
直接命中的笔记
↓ 可选的一跳关系扩展
直接结果与关联笔记
↓ 调用方读取、筛选
组织上下文并生成回答

最后一步属于使用这些结果的客户端或 Agent 工作流。检索服务返回材料,不意味着每次搜索都会额外调用语言模型生成最终答案。

怎样判断关系扩展是否值得

先构造一小组你知道答案的任务。对同一任务分别查看直接检索与开启关系扩展后的结果,记录是否找到了必要前提、是否引入无关笔记,以及阅读额外材料需要多少时间。

至少包含三种情况:关系确实有帮助、关系过期、两条相似笔记实际给出了相反结论。评价目标应是当前任务能否据此作出更好的判断,而不是图上多了多少条边。

如果需要在此基础上生成答案,还应检查答案是否保留来源、是否把推测写成事实,以及缺少证据时能否说明不足。检索质量与生成质量要分别观察,避免把一次流畅回答当成整个系统正确的证明。

对个人知识库而言,关键词帮助精确定位,向量帮助发现候选,关系帮助检查上下文。怎样把这些材料交给 Agent 并写回经过验证的经验,见 Agent 知识库设计;模型调用的数据流见本地优先与隐私边界。

分享:

相关文章

笔记关系与图视图:从候选发现到可验证扩展

按 ChatCrystal 当前关系 API、候选筛选与图投影说明自动发现、手动关系和标签图的实际边界。

曾亮懿 曾亮懿
阅读约 3 分钟
#ChatCrystal #知识图谱 #关系

语义搜索与关系上下文:发现隐藏依赖但保留验证

用构造的重构场景组合直接检索和一跳关系扩展,解释分数、候选和来源字段,不把关联线索写成依赖证明。

曾亮懿 曾亮懿
阅读约 3 分钟
#ChatCrystal #语义搜索 #知识图谱

语义搜索流程:请求、候选去重与关系扩展

沿 ChatCrystal 实现检查查询向量、候选升级、词项加分和关系扩展,用实际 API 区分笔记列表筛选与语义检索。

曾亮懿 曾亮懿
阅读约 3 分钟
#ChatCrystal #语义搜索 #RAG

从文章到代码

到我的 GitHub 看看 ChatCrystal、开发者工具和日常学习记录,欢迎交流实现细节。