idea_world_labDEV JOURNAL
2026年6月28日星期日

Retriever 检索 替代 方案 拆解 文档

编写日期:2026年 6月 28日

此目录是为了更易阅读而重新划分的文档集合,来源于 Retriever 检索 替代 ChatGPT 原文 备注

原文备注保持原样。此 README 汇总了共同上下文、当前方式、模型候选、选择指南。子文档仅包含各替代方案的文档。

综合比较表

这些表格并非最终选择,而是 6 月 28 日用于在同一页面判断需要比较的检索替代方案的观察表。标准是 “把代码 chunk 原样放入 Retriever 时,会出现哪些 JSONL 候选,以及这些候选能否在 Qwen 验证阶段直接被认可为依据”。

一目了然的结论

替代方案 位置 预期角色 可直接使用程度 核心优势 核心风险 当前判断
A. PostgreSQL full-text 维护 当前 /api/retrieve 基准线 检查现有实现的检索能达到什么程度 已经集成且日志可直接查看 当长代码 chunk 成为查询时,相关文档可能被遗漏 作为基准线保留,但不确定为最终检索方式
B. 仅 BM25 首次检索候选 查找直接包含代码/API 字符串的文档 KinematicBody2Dmove_and_slideAnimatedSprite2D 等直接字符串表现强 当出现 2D/3D 等宽泛词汇时会产生 false positive 作为首个 PoC 最适合观察
C. 仅 embedding 基于语义的 recall 扩展 即使字符串不完全相同也能找到相关说明文档 捕获句子意义、教程说明、相似使用上下文 单独使用在版本/API 精确度判断上风险大 作为辅助 recall 使用,而非单独使用
D. BM25 + embedding 现实的中间方案 同时收集字符串依据和语义依据 兼具 BM25 的准确性和 embedding 的 recall 需要分数融合、去重、上位候选标准 接近实际使用的最小方案
E. Qwen query profile 检索辅助实验 将代码 chunk 的检索意图转为 JSON 人类可读的 query profile 易于阅读 在检索前 LLM 可能生成不存在的 API/意图 作为实验/辅助,而非首要检索核心
F. BM25 + embedding + reranker + validator 最终候选结构 从候选生成、重新排序到直接依据验证全链路 质量和可观察性最佳 成本、延迟、步骤多 作为最终目标候选

PoC 仿真结果比较

替代方案 输入 chunk 检索候选产生方式 相关 JSONL 上线可能性 不相关 JSONL 混入可能性 Qwen 验证前的预期状态 Qwen 验证后的预期状态
A chunkText 全部 使用 plainto_tsquery('simple', chunkText) 生成长 query 低或不稳定 可能低,但若为 0 则无候选可观察 可能没有候选或仅返回部分 在直接依据验证前可能被遗漏
B chunkText 全部 按 token 的 BM25 分数对文档排序 可能出现 Input.is_action_pressedAnimatedSprite2Dclamp 等文档 若缺少直接字符串则被淘汰
C chunkText 全部 code/document embedding 相似度 “movement”、 “2D”、 “player” 等说明文档会广泛出现 若 JSONL 中无直接字符串则被淘汰
D chunkText 全部 合并 BM25 候选和 embedding 候选 同时看到字符串候选和语义候选 仅保留有直接依据的候选
E chunkText 全部由 Qwen 解析 使用 Qwen 生成的检索 profile 进行检索 若 profile 匹配则为中等 可能混入 profile 中未出现的 API 需要验证 profile 本身是否与实际代码匹配
F chunkText 全部 BM25 + embedding 候选经 reranker 重新排序 即使候选很多,上位也会被整理 Qwen 以 “是/否” 直接留下有依据的结果

演示判定中显现的差异

观察情形 最初的简单提示 改进后的直接依据提示 对检索结构的意义
Godot 3 代码 + 相关 JSONL 可能得到 “是” 必须得到 “是” 若相关依据与实际字符串/API 对应则通过
Godot 3 代码 + 无关 JSONL 由于 Godot 词汇宽泛可能得到 “是” 应该得到 “否” 即使检索候选出现,缺少直接依据验证仍有风险
KinematicBody2D 代码 + 3D Spatial 映射 可能因 “Godot migration” 这种宽泛意义被视为相关 正确答案应为 “否” 仅 embedding 或宽松的 LLM 判断会产生 false positive
yield(...) 代码 + await 原型 若有直接模式则为 “是” “是” label_prototypes 可作为捕捉整体使用模式变化的依据
AnimatedSprite2D.play() 代码 + 普通 animation 文档 语义上可能看似相关 若 JSONL 中无直接字符串/模式则为 “否” docs_chunks 在最终判定时仍需直接依据确认

候选返回情况比较

替代方案 上位容易出现的候选 易被遗漏的候选 易被错误混入的候选 目视检查要点
A 在同一行中拥有大量 query token 的文档 当相关 token 分散在多篇文档时 若 query 过于严格几乎没有结果 为什么为 0,哪些 token 导致匹配失败
B 直接包含 API 名称、函数名、节点名的行 表述被改写的说明文档 其他系统中出现相同词汇的文档 match_terms 与实际 source chunk 字符串是否一致
C 语义相近的教程/说明文档 短小的 exact API 映射 Godot 版本或 2D/3D 不同的文档 除了 cosine 分数,是否有直接字符串依据
D exact 候选 + semantic 候选 在分数融合中被压制的候选 两种检索都仅弱匹配的候选 候选是来源于 BM25 还是 embedding
E 与 Qwen 选出的意图匹配的候选 Qwen profile 未包含的实际代码线索 Qwen 想象的 API/意图相关候选 profile 各字段是否真实存在于 chunk 中
F reranker 认为与 chunk 匹配的候选 reranker 未识别的 Godot 特殊案例 reranker 分数高但 JSONL 无直接依据的候选 rerank 分数与 validator 判定不一致的点

False Positive / False Negative 比较

方案 false positive 示例 false negative 示例 减少方法
A 虽然少见,但 query 过宽时会混入一般 movement 文档 相关文档分散在多行时为 0 条 改变 query 生成方式或添加 BM25 候选
B 因为 movementplayer2d 导致混入其他 movement 文档 API 名称仅在文档中以说明形式出现时会遗漏 停用词/弱 token 分离,直接对 API token 加权
C 意义相近的 3D/其他版本文档混入 短的 API rename 行未被捕获 与 BM25 并行使用并附加 validator
D BM25 与 embedding 两侧弱匹配的候选混入 fusion 标准错误时一侧候选被压制 保留 union source 标记和 direct evidence 日志
E Qwen 未包含的 migration_intent 生成导致候选被污染 Qwen 实际代码线索在 profile 中缺失 在搜索前不信任 profile,仅作辅助使用
F reranker 过度评价合理的解释 候选生成阶段根本未获取到文档 充分获取候选数量并将 Qwen 验证放在最后

各表适用度

方案 docs_chunks api_mapping label_prototypes 备注
A 长说明文档中不稳定 exact mapping 行太短可能会漏掉 pattern 行太短可能会漏掉 确认当前方式局限性的基准线
B 对 API/heading/token 搜索友好 对 source/target API 的 exact 搜索友好 对 source/target pattern 的搜索友好 三表都适合作为首次搜索候选观察
C 对说明型文档 recall 好 对像 API rename 这种短行弱 使用模式意义相似时可作辅助 docs_chunks 辅助最自然
D 同时获取说明/字符串候选 同时获取 exact mapping 与语义候选 大幅获取整体使用模式候选 三表统一结构最通用
E query profile 良好时可搜索说明文档 出现幻觉 API 时风险大 可生成 prototype 意图但可能被污染 更像观察/辅助实验而非核心搜索
F 对最终说明依据的精炼有帮助 对 migration 依据的精炼有帮助 对使用方式变更依据的精炼有帮助 三表在同一流水线中处理最方便

必须通过日志查看的内容

方案 搜索前日志 搜索中日志 搜索后日志 失败原因确认点
A 原始 chunkText、生成的 tsquery search_tsv @@ query 命中数、ts_rank_cd 返回行、score query 是否过长,哪些 token 在行中不存在
B 原始 chunkText、token 列表 词频、匹配的 term、BM25 分数 各表 top‑k 强 token 与弱 token 是否被区分
C 原始 chunkText、embedding 模型 向量距离/余弦分数 语义 top‑k 是否为仅凭意义匹配、无实际字符串的候选
D BM25 top‑k、embedding top‑k union、duplicate 合并、score 融合 带 source 标记的候选列表 只来自单一搜索的候选为何存活
E 发送给 Qwen 的原始 chunk Qwen query profile JSON 基于 profile 的搜索结果 profile 字段是否真实存在于 source 中
F 全部 BM25/embedding 候选 rerank 分数、rerank 顺序 Qwen “是/否”、reject reason 通过 reranker 的候选为何在直接依据验证中被淘汰

实现复杂度与运营成本

方案 实现量 DB 变更 外部模型费用 latency 调试难度 失败后修复难易度
A 几乎无 低。仅是结构性性能上限可能存在
B 中等 需选择 BM25 搜索引擎/索引 无或低 高。可调 token/权重
C 中等 需 embedding 列/索引 中。需调模型/维度/阈值
D 中等~高 BM25 与 vector 均需 高。可分离哪条搜索路径失败
E 中等 profile 存储为可选 低。LLM profile 错误难复现
F 需 BM25 + vector + rerank 日志 高。有分阶段日志时易定位问题

Qwen 放置位置比较

位置 优点 风险 当前更合适的使用方式
搜索前 query 生成 可在无需硬编码的情况下构造搜索意图 可能制造不存在的线索污染搜索 仅用于实验
搜索候选后直接依据验证 可过滤搜索结果是否与实际代码匹配 调用成本高 最重要的环节
最终答案生成 可生成代码说明/迁移响应 依据错误则答案也错 放在已验证的 JSONL 候选之后
整体判断整合 可生成对人友好的结论 可能隐藏中间失败 在日志足够累积后再审查

选择优先级

优先级 选项 理由 本阶段需确认的问题
1 以 A 为基准线测量 必须了解当前实现在哪儿失效 现行方式实际返回多少条
2 将 B 作为独立 PoC 接入 代码/API 字符串搜索最具解释性 KinematicBody2Dyieldmove_and_slide 能否找到正确行
3 用 C 作为辅助 recall 对比 可扩大 docs_chunks 说明型候选 语义候选在直接依据验证中被淘汰的比例
4 用 D 并行生成候选 现实的最小结构 合并 BM25 与 embedding 候选后质量是否提升
5 用 F 添加 reranker/validator 最终质量确认 Qwen 直接依据判断无关的 JSONL 是否能被稳妥剔除
保留 E 搜索前 LLM 污染风险大 profile 是否仅基于实际代码字符串生成

PoC 检查清单

检查项目 需要查看的界面/日志 成功标准
将相同的 chunkText 放入多个方案 Web 调试器的 chunk 输入区域 输入在各方案之间保持不变
各表搜索按钮 docs_chunksapi_mappinglabel_prototypes 按钮 同一 chunk 在各表的候选结果出现差异
raw 候选查看 返回的 JSONL payload 能看到与 source chunk 中实际字符串对应的字段
Qwen 验证确认 prompt + chunk + jsonl 调试区域 相关候选显示 “是”,无关候选显示 “否”
reject reason 确认 validator 结果日志 仅匹配到 Godotmigration2D 等宽泛词汇的候选被剔除
false positive 收集 被剔除的候选列表 可用于后续 query/token/validator 规则的改进
各方案比较保存 观察日志或结果 JSON 同一输入的 A-F 差异得以保留

方案文档

当前前提

当前基准如下。

1. zip 中包含 markdown 原始文件、模式、调试器、设计文档。
2. 本地已经有 markdown → JSONL → DB 的转换版本。
3. 现在可以看到的是转换前的 markdown 和 DB 模式/搜索流程。
4. PoC 需要将 zip 中的 markdown 像 JSONL chunk 那样拆分,并模拟可能进入本地 DB 的搜索目标。
5. 所需的不是代码框架,而是追踪当 chunk 进入时,各种搜索方式如何生成分数以及为何成功/失败。

JSONL/DB 基准

文档基准产出如下。

work/godot_rag/jsonl/docs_chunks.jsonl
work/godot_rag/jsonl/api_mapping.jsonl
work/godot_rag/jsonl/label_prototypes.jsonl
work/godot_rag/jsonl/ingest_report.jsonl

数据库不是将整行 JSONL 放入 payload jsonb,而是将 JSONL 的字段展开为表的列来理解。

docs_chunks

chunk_id
doc_version
source_url
source_file
source_sha256
doc_type
symbol
section_path
heading
content
code_blocks
language_tags
godot_version_tags
api_symbols
token_count
metadata
embedding
search_tsv

api_mapping

mapping_id
source_api
target_api
change_type
godot_from
godot_to
confidence
evidence_chunk_ids
match_terms
notes
negative_patterns

label_prototypes

prototype_id
label
task_type
input_pattern
expected_finding
recommended_action
evidence_mapping_ids
evidence_chunk_ids
severity
validator_rules
embedding
search_tsv

基准块

在替代方案比较中作为基准的块是以下的 Godot 代码。

func _process(delta):
	var velocity = Vector2.ZERO
	if Input.is_action_pressed(&"move_right"):
		velocity.x += 1
	if Input.is_action_pressed(&"move_left"):
		velocity.x -= 1

	if velocity.length() > 0:
		velocity = velocity.normalized() * speed
		$AnimatedSprite2D.play()
	else:
		$AnimatedSprite2D.stop()

	position += velocity * delta
	position = position.clamp(Vector2.ZERO, screen_size)

强令牌:

animatedsprite2d
is_action_pressed
move_left
move_right
normalized
clamp
screen_size
vector2
zero

弱令牌:

func
var
if
else
velocity
position
delta
x
0
1

搜索输入保持为原始的 chunkText。在搜索之前必需提取 Godot API signal 并将其转换为单独的 query 的做法,并不是基本设计的核心。如果有需要,只在以后作为辅助评分进行审查。

当前 /api/retrieve 方式

当前项目的 /api/retrieve 并非 BM25。

流程接近如下。

chunkText
  -> plainto_tsquery('simple', chunkText)
  -> search_tsv @@ query
  -> ts_rank_cd(...)

PostgreSQL的 ts_rank_cd 是全文检索排名函数。它不同于像 BM25 那样将词频、逆文档频率、字段长度归一化组合在一起的搜索引擎式 BM25 本身,需要另作区分。

plainto_tsquery(raw_chunk) 会从输入的 chunk 中提取标记并生成 query。基准 chunk 可以像下面这样成为一个很长的 query。

func & process & delta & var & velocity & vector2 & zero
& input & is_action_pressed & move_right & move_left
& length & normalized & speed & animatedsprite2d
& play & stop & position & clamp & screen_size

这种方式可能在一行中要求太多的标记。

如果官方文档的块被划分成如下所示,就会出现问题。

chunk A:
  Input.is_action_pressed
  velocity.normalized
  AnimatedSprite2D.play/stop

chunk B:
  AnimatedSprite2D 说明
  position.clamp
  screen_size

两者都相关,但可能并非所有令牌同时出现在同一行中。

在原始备注中,认为严格原始 AND 性质的搜索可能会像下面这样失败。

strict raw AND hits: 0

当前方式可以作为 PoC 的基准线,但作为最终搜索策略维持下来较为薄弱。下一阶段需要将当前方式与 BM25 分离后进行比较。

模型候选

voyage-code-3

被提及为最符合当前项目的 embedding 候选。

原因:

query 不是自然语言问题,而是 GDScript 代码块。  
搜索目标 JSONL 中也混杂了 code_blocks、API 名称、文档说明。

优点

  • 对代码检索能力强。
  • 适合处理长块(chunk)。
  • 适用于 code → docs 的搜索。

缺点

  • 依赖外部 API。
  • 成本较高。
  • 不是专门针对 Godot 的模型。

基于内存的推荐

voyage-code-3 1024 float

OpenAI text-embedding-3-large

通用高质量 embedding 候选。

优点:

  • 在通用语义检索方面表现强劲。
  • 对文档说明搜索稳定可靠。
  • 易于与 OpenAI 生态系统集成。

缺点:

  • 并非专用于代码检索。
  • 基础维度较大,可能导致存储/索引成本上升。
  • 对于 GDScript 块 → JSONL 的搜索,可能不如 voyage-code-3 直接。

基于内存的推荐度:

第二优先级

Gemini Embedding

通用/多语言语义检索候选。

优点:

  • 适用于文档说明型检索。
  • 有维度缩减的选项。
  • 在多语言文档搜索方面可能具有优势。

缺点:

  • 不是专用于代码检索的选项。
  • 更侧重语义相似性,而非 API 代码的准确性。
  • 在判断 Godot 迁移是否精确时,单独使用存在风险。

Jina embeddings v4

在复杂文档、跨模态、视觉丰富的文档检索方面具有优势的候选。

优点:

  • 文档搜索范围广泛。
  • 对跨模态/复合文档表现出色。
  • 也可以考虑代码适配器系列。

缺点:

  • 当前项目以 markdown/代码为中心。
  • 图像/表格检索不是核心需求。
  • 在现阶段可能是过度的选择。

基于备注的推荐度:

目前优先级较低

rerank-2.5

reranker 候选人被提及。

角色:

BM25和 embedding 带来的候选项会与原始块再次比较。  
根据相关度得分对候选项重新排序。

在此项目中预期的效果:

  • 降低 2D movement 和 3D movement 的误报率。
  • 更好地反映 AnimatedSprite2DVector2screen_sizeposition.clamp 等直接上下文。
  • 将 BM25 和 embedding 各自得到的候选项整理为最终候选。

注意:

  • reranker 不是 validator。
  • 直接依据的验证需要由 Qwen direct‑evidence validator 重新进行。

选项摘要

方案 流程 优点 风险
A 保持 PostgreSQL 全文检索 已实现 在长 chunk 中可能为 0 条,非 BM25
B 仅使用 BM25 透明,对字符串/API 强 3D movement 等误报
C 仅使用 embedding 可进行语义检索 API/版本准确性较弱
D BM25 + embedding 现实的平衡 需要对分数组合进行调优
E Qwen 查询配置文件 减少硬编码,可概括意图 幻觉、成本、检索前污染
F BM25 + embedding + reranker + validator 质量至上 成本、延迟、步骤多

当前临时判断

原始备忘的结论接近于以下内容。

输入仅保留一个 chunkText。

第一次搜索使用 BM25。
  原因:在代码/API 字符串搜索中透明且强大。

第二次搜索使用 voyage-code-3 嵌入。
  原因:因为查询是代码块,适合代码检索模型。

第三次排序使用 rerank-2.5。
  原因:在 BM25 和嵌入得到的相似候选中,将真正的 chunk 对应的 JSONL 提升到上方。

最后验证使用 Qwen direct-evidence validator。
  原因:如果 JSONL 中没有与 chunk 直接匹配的字符串/模式依据,就必须舍弃。

一行摘要:

raw chunk 原样输入  
  -> BM25 + code embedding 并行候选生成  
  -> reranker 重新排序  
  -> Qwen 直接依据验证

接下来实际比较的内容

  • 当前 PostgreSQL 全文检索方式在基准 chunk 中返回多少条记录
  • BM25 是否真的把 first_2d_game 排到前面
  • 在仅使用 BM25 时,3D movement 的误报会混入多少
  • 仅使用 embedding 时会额外检索到哪些说明性文档
  • 在 BM25 + embedding 联合时,如何合并重复项和分数
  • reranker 是否能正确重新排序 2D/3D 候选项
  • Qwen direct-evidence 验证器是否会把无关的 JSONL 实际上标记为 아니오 并丢弃