适用版本:ChengOS v0.1.0+ | 最后核对:2026-08-12 | 来源:
crates/cheng-nodes/src/nodes/builtin/rag/constants.rs、crates/cheng-nodes/src/nodes/builtin/rag/nodes、crates/cheng-nodes/src/nodes/builtin/rag/services、crates/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_preview、child_preview、preview_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 既接受纯字符串也接受对话上下文,所以检索器可以直接接在对话输入节点后面,中间不需要转换器。
每条结果都带有 score、text、document_id、chunk_index、存在时的 context 描述,以及 retrieval_method——真正命中它的是哪种方法。调优时最该看的就是最后这个字段:它告诉你干活的到底是 BM25 还是向量检索。
与分块器一样,检索器返回 documents_artifact 和有界预览。
组装提示词
rag/formatter 把查询和检索到的文档变成提示词。模板有:qa(标准问答)、summary(文档摘要),或 custom 配合占位符 {query}、{documents}、{count}。用 include_scores 和 include_sources 决定模型是否看得到相关性分数和文档 id——希望答案给出引用时就包含来源,不希望模型絮叨检索过程时就去掉。
优先从检索器连 documents_artifact;内联的 documents 输入只是兼容路径。
管理知识库
rag/kb_manager 负责杂务:list(默认)、info 和 delete。delete 会删掉集合——它是 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。
下一步
- 知识库与工作区上下文——创建目标集合。
- 存储、Redis 与向量配置——运行 Qdrant。
- 大语言模型与模型配置——向量嵌入模型与生成模型。

暂无评论内容