RAG 管道实战

适用版本:ChengOS v0.1.0+ | 最后核对:2026-08-12 | 来源:crates/cheng-nodes/src/nodes/builtin/rag/constants.rscrates/cheng-nodes/src/nodes/builtin/rag/nodescrates/cheng-nodes/src/nodes/builtin/rag/servicescrates/cheng-vector

ChengOS 里的 RAG 就是五个节点、两条管线。索引管线在文档变化时运行;检索管线在每次提问时运行。二者在运行时除了共享读写的那个知识库之外,别无关联。

索引                                        检索
 源文本                                      用户查询
     │                                         │
 rag/chunker      ──chunks_artifact──►     rag/retriever  ──documents_artifact──►
     │                                         │                    rag/formatter
 rag/document_indexer                          │                          │
     │                                         │                       ai/llm
     ▼                                         ▼
  知识库(Qdrant 集合) ◄────────────────────────┘

配置节点

其中三个 RAG 节点接受的是配置对象而非零散字段,这些对象由专门的节点产出:ai/qdrant_config(向量数据库在哪)、ai/embedding_config(用哪个向量嵌入模型,以及可选的重排模型),以及需要模型的步骤所用的 ai/llm 配置。

把配置作为一个对象连线、而不是摊成二十个输入,正是为了让同一个配置节点同时服务索引器和检索器——这很重要,因为索引时使用的向量嵌入模型必须与检索时使用的一致。共用一个配置节点就让这种不匹配不可能发生。

分块

rag/chunker 把文本切成父块子块。四种模式:

模式 切分依据
structural(默认) Markdown 标题
fixed 固定字符窗口
semantic 段落语义边界
hybrid 先结构、再在小节内做语义切分

尺寸以字符数而非 token 数度量,这是刻意的设计决策:分块器本质上是字符串切分器而不是分词器,字符数可预测、可测试、与语言无关。token 数则不然——一个中日韩字符大约 1–2 个 token,而一个英文单词大约 1 个。

常量 取值
默认父块大小 384 字符
默认子块大小 384 字符
向量嵌入块上限(硬限制) 512 字符
父块重叠比例 0.1
子块重叠比例 0.2
最小 / 最大块大小 50 / 10000 字符

512 字符的天花板是运行时施加的安全钳制:多数向量嵌入模型上限为 512 token,而 512 个字符在任何语言下都留有余量。超过它的子块会被钳制,而不是被拒绝。

输出是产物,不是数组。 分块器返回 chunks_artifact 加上有界的预览(parent_previewchild_previewpreview_truncated),而不是把完整块列表内联返回。一份大文档会产生成千上万个块;让它们穿过节点输出会撑爆执行记录,以及它们途经的任何 LLM 上下文。内联的 parent_chunks / child_chunks 字段仍在,但除非你打开 legacy_inline_chunks 否则为空——把它们视为已废弃。对称地,输入侧也优先接受 content_artifact / content_artifact_uri,而非内联的 content

索引

rag/document_indexer 把向量写入知识库。两种模式:

模式 写入的向量
standard(默认) child_vec
enhanced parent_vec + child_vec + context_vec

不同维度的具名向量共存于同一个 Qdrant 点中:

向量 维度 作用
parent_vec 384 小而快——用于预筛
child_vec 1024 大而准——主要匹配依据
context_vec 256 轻量的上下文信号

enhanced 模式的存在是为了喂养 ultimate 检索模式;如果你不打算用 ultimate 检索,standard 更便宜、索引更快。

打开 generate_context: true(enhanced 模式)时,会由大语言模型为每个块写一小段上下文描述——即 Contextual Retrieval 技术——context_vec 嵌入的正是它。这需要 llm_config,且每个块要花一次模型调用,因此默认关闭。

索引器接受的是 kb_id 而不是集合名:集合从数据库解析并按工作区校验,因此工作流无法靠猜名字把数据索引进别的工作区的集合。优先从分块器连 chunks_artifact 过来;直接给 content 会让索引器用默认参数在内部自行分块。

幂等性:传入 version,重新索引一份未变化的文档会返回 skipped: true,而不是重写全部向量。这正是把索引工作流挂上定时任务也安全的原因。

检索

rag/retriever 有三种模式,它们之间的差别就是整个系统的成本/质量旋钮。

模式 管线 默认条数 P50 目标
economy(默认) BM25 + child_vec 5 100 毫秒
power parent_vec 预筛 → child_vec + BM25 → RRF 融合 10 120 毫秒
ultimate HyDE + Contextual Retrieval + 重排 20 150 毫秒

economy 同时跑一次关键词检索和一次向量检索并返回并集——便宜,而且在查询与语料用词相近时已经够用。

power 先用廉价的 parent_vec 收窄候选集(预筛上限 50),再在幸存者上跑 child_vec 和 BM25,最后用倒数排名融合(RRF)把两个排名列表融合:

RRF 分数 = Σ  1 / (k + rank_i)        k = 60

RRF 融合的是排名而不是分数,所以它能把 BM25 列表和余弦相似度列表合到一起,而不必去归一化两种本就不可比的分值尺度。

ultimate 额外引入 HyDE(让模型先写一个假想答案,去嵌入而不是问题本身,因为它的措辞更贴近语料)、使用索引期写好的上下文描述,并对头部候选做重排(重排 top-k 为 10)。它需要 llm_config、一个以 enhanced 方式索引过的知识库,以及嵌入配置里的重排模型——支持的重排模型包括 Cohere rerank-multilingual-v3.0、Jina jina-reranker-v2-base-multilingual,以及 Qwen3 rerank 的 0.6b / 4b / 8b。

还有两个输入很关键:

  • workspace_scope——ambient 跟随运行时工作区;pinned 把这个检索器锁定到某个配置好的工作区。当一个共享工作流必须始终从同一份参考语料作答、与谁来运行无关时,就固定它。
  • document_filter——一组文档 id,把检索范围限制在其中。

query 既接受纯字符串也接受对话上下文,所以检索器可以直接接在对话输入节点后面,中间不需要转换器。

每条结果都带有 scoretextdocument_idchunk_index、存在时的 context 描述,以及 retrieval_method——真正命中它的是哪种方法。调优时最该看的就是最后这个字段:它告诉你干活的到底是 BM25 还是向量检索。

与分块器一样,检索器返回 documents_artifact 和有界预览。

组装提示词

rag/formatter 把查询和检索到的文档变成提示词。模板有:qa(标准问答)、summary(文档摘要),或 custom 配合占位符 {query}{documents}{count}。用 include_scoresinclude_sources 决定模型是否看得到相关性分数和文档 id——希望答案给出引用时就包含来源,不希望模型絮叨检索过程时就去掉。

优先从检索器连 documents_artifact;内联的 documents 输入只是兼容路径。

管理知识库

rag/kb_manager 负责杂务:list(默认)、infodeletedelete 会删掉集合——它是 RAG 这组节点里唯一的破坏性节点,所以别把它放进面向智能体的工作流。

一条可用的管线

索引:
  io/read_file → rag/chunker → rag/document_indexer
                    ▲                  ▲
       ai/embedding_config ────────────┘
       ai/qdrant_config    ────────────┘
       workspace/knowledge_base(select)→ kb_id

问答:
  chat/input → rag/retriever → rag/formatter → ai/llm → chat/output

先从 economy + standard 起步。召回成为瓶颈时再上 power;只有当你实测出重排值回它的延迟与成本时,才切到 ultimate + enhanced

下一步

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容