编排集中,策略分开
Pipeline 负责步骤顺序与错误语义;parser 和 chunker 处理文档结构;存储适配层封装读取、清理与写入。新增类型不必复制整条构建链。
一次查询能找到哪些证据,取决于索引保留了什么。本文沿着文档变更与搜索请求两条链路,展示文本、幻灯片、会议纪要和表格如何进入索引,以及查询如何筛选与融合候选。育苗工作簿示例可以切换切分粒度、窗口尺寸和生成预算。
上游知识源持有权威正文与权限。构建服务异步把变更转成索引;搜索服务读取索引并返回候选。索引是可以重建的派生数据,不承担文档编辑,也不等同于最终答案生成。
Pipeline 负责步骤顺序与错误语义;parser 和 chunker 处理文档结构;存储适配层封装读取、清理与写入。新增类型不必复制整条构建链。
单文档任务状态持有本次配置快照、解析结果、指纹和文档锁。共享的流水线与依赖不保存单篇文档的中间状态。
build 消费事件,serve 对外搜索,migrate 管理 schema,DLQ worker 处理延迟重试。Schema 迁移独立于服务启动。
同一个变更 topic 可以被多种 builder 消费。入口先排除无关类型,元信息只按需查询一次;实际删索引、更新权限和重建,统一受文档锁保护。下面切换事件,观察走过的阶段。
类型早筛
删除 / force / 早判
确定类型计划
完整过滤
collection + docID
自动续期
Hit / MetaOnly
Rebuild / Deferred
下载 / 二判 / 限流
解析 / 读旧片段
复用 / 计算向量
清旧 / 写新 / 打标
元信息生成只读文档处理计划;锁内下载正文并解析、切分。从目标索引读取旧 chunk,复用未变化片段的向量,只计算缺失向量。
示意条件:事件类型明确、缓存与依赖可读;正文下载次数描述普通文本 / 网格主路径。PPT 自行获取文件,重试会开启新的处理尝试。
消息里的类型用于早筛;权威元信息决定 parser、chunker、正文来源、缓存后缀和 chunk 上限。两者不一致时按权威类型过滤并记录差异,避免每个阶段重新解释类型。
可预期解析失败走 ChunkDrop:保留旧索引、不打 done。合法空文档走 0 chunk 重建:清旧并打 done。依赖错误上抛给消费者重试,耗尽后再进入 DLQ。
0 是合法类型,不能当成“字段缺失”。缺类型、null 或 -1 走兼容路径并计数:DELETE 仍清理,更新查询权威元信息后分流。已知非本类型的 DELETE 则在缓存、查询和取锁之前结束。on_event(event):
if event.has_type and event.type != handled_type:
return SKIP
if event.is_delete:
with document_lock(event.document_id):
clear_available_indexes()
remove_completion_marker()
return DONE
if event.only_content_changed and early_fingerprint_matches(event):
return SKIP
metadata = load_metadata_once(event.document_id)
plan = choose_document_plan(metadata, config_snapshot)
with document_lock(event.document_id):
execute_plan(plan, metadata)仅展示早筛、删除和计划选择的控制关系;正文二判、依赖错误与重试由后续阶段处理。
Chunk 太小会丢掉条件、主体和指代,太大又会稀释检索信号、占满上下文。相比表格的“找到整份工作簿”,正文片段更可能直接作为下游回答的证据,因此要同时考虑语义边界、事实覆盖、预算、定位和版本一致性。下面按已核对的实现机制展开,而不是把理想中的 RAG 能力一并算作已具备。
“已具备”描述实现机制,不代表已经证明所有场景的质量;“未覆盖”保留架构边界,不把待建设能力包装成现有功能。
| 技术难题 | 已具备的机制 | 仍未覆盖 / 需要验证 |
|---|---|---|
| 语义边界 vs 粒度 | 文本标题与句子边界、小片合并;PPT 按页再细分;纪要按原子单元和局部锚点。 | 没有基于句向量的主题变点切分,也没有按查询动态重切。一个完整句子可能仍依赖前文事实。 |
| 脱离原文后的上下文 | 标题路径、页标题、纪要 section / 发言者 / 有效时间写入 Content。 | 没有通用的逐片 LLM 上下文增强、指代消解或摘要层级树。上下文头过长也可能挤掉正文。 |
| Token、字符与存储限制 | 普通文本 rune + UTF-8 byte 保护;PPT 字节驱动细分;纪要出片真实 token / rune / byte 复核。 | 三条路径并非统一 token 预算。PPT 对长度参数的 byte 解释与普通文本不同,末端硬截断不等于无损切分。 |
| 原始结构与多模态信息 | 结构节点转码、内嵌表头处理、PPT Vision 转 Markdown;独立表格多粒度。 | 普通图片未获得这条视觉链能力;PPT 没有原图向量或图形坐标索引。长代码 / 内嵌表仍可能碎片化。 |
| 高召回与精确证据 | Dense + BM25、RRF、rerank 和范围 / 权限下推,提高候选的可检索性。 | 没有通用的“检索小片、回取父段”或按相邻片扩展证据链。重排无法恢复构建时已经丢失的条件句。 |
| 重复与 Top-K 被挤占 | 可配置 overlap、小片合并、chunk 级内容向量复用,多 query 合并去重。 | 片段 ID 去重不等于语义去重;相邻重复片段仍可能占据前几名。未见通用文档多样性 / MMR 选择机制。 |
| 局部更新与边界稳定 | 四类缓存指纹;内容 + pipeline 向量复用;纪要局部 anchor 减少无关重切。 | 文本插入可能移动后续边界,PPT 模型输出可能漂移;索引仍整篇覆盖,局部向量复用并非局部事务更新。 |
| 引用可信度与内容完整性 | 表格结构坐标、文本主题路径、纪要组时段,版本化解析与失败分支。 | 普通正文没有统一的字符区间引用协议;PPT 缺页 / 误读和输出截断需单独检查,合法完成不自动意味着完整证据。 |
# 育苗手册 ## 灌溉 保持土壤湿润,避免积水。 ↓ 育苗手册 > 灌溉 保持土壤湿润,避免积水。
文本链路直接索引代码与内嵌表的文本,不额外生成 LLM 摘要。小片合并也处理标题路径,减少语境丢失。
标题路径让“每天一次”这类短句带上灌溉语境;句子优先避免把条件和结论切成两半;先合并再重叠,减少被 overlap 掩盖的极短片段。保留原文本而不让 LLM 重写,降低构建成本和新增事实风险。
chunks = []
for section in walk_heading_sections(parse_markdown(input)):
text = render_nodes_as_text(section.nodes)
parts = split_near_sentence_end(text, max_runes=1024)
chunks.extend(with_heading_path(parts, section.path))
chunks = merge_small_chunks(chunks, min_runes=100)
chunks = append_previous_tail(chunks, ratio=0.10, cap_runes=200)
for chunk in chunks:
chunk.content = add_document_context(chunk)
enforce_final_rune_and_utf8_byte_limits(chunk)描述结构化主路径;缓冲区的早期小片合并、标题路径组合和截断规则略去。无法结构化切分时回退到清理后的纯文本固定窗口。
怎么验证:用条件句、跨段指代、短标题下的一句话、长代码、含空单元格的内嵌表做对照;检查事实是否保留、标题是否正确、前后片段重复是否挤占 Top-K。
表格沿三步构建:先从网格或 schema 恢复结构,再为工作簿和各 region 生成检索表示,最后按文档预算控制产出。Overview 属于工作簿;Summary、Column、Window 和 Row 属于 region。两种输入共用这套生成机制。
构建器接收的是上游转换后的结构化 JSON:电子表格是 sheet → 二维 cell,智能表格是 schema → 记录。不需要在每次 build 中重新还原整份 Excel 文件。前半段恢复网格语义,后半段才生成检索片段。
保留 value、displayValue、format、合并范围与 sheet 属性;智能表格保留列类型和选项。
电子表格跳过隐藏 sheet,展开 rowspan / colspan;智能表格把类型化值渲染成文本。
普通网格推断多个 region 和多层 header;已知 schema 的智能表格直接构造 region。
按配置选择传统切分或 FRTR 五个 pass,追加上下文和定位元信息,控制长度与数量。
展开后的合并区域共享同一 cell 指针,读取时可去掉重复锚点。非空位置先形成行带和列组,再按重合度、空隙、说明文本和标题锚点合并或拆分 region。表头按顶部文本行推断,每列建立 header_paths。
电子表格优先使用上游 displayValue。传统路径缺少显示值时由格式化器按需处理数值和样式,避免构造完整临时工作簿。FRTR 读取 displayValue,缺失则退回原始值字符串;两路并非完全相同。
智能表格的表头来自显式 schema,row 打包可去掉表头行;电子表格的启发式表头仍保留为数据行,防止猜错后永久漏掉真实内容。标签优先取 header path 最具体的一层,缺失时用列字母。
| 策略 | 传统路径 · 仍保留 | FRTR 路径 · 灰度选择 |
|---|---|---|
| 电子表格结构 | 展开合并格,查找 / 合并子表,扫描候选表头行并评分,展平多层列名。 | 根据占用分布识别 region,处理说明文字 / 标题边界,构造 header paths。 |
| 表达方式 | 逐行输出“列名: 值”,小表可合并;超长内容用固定窗口二次切分。 | overview / summary / column / window / row 同时表达不同查询视角。 |
| 智能表格空行 | 系统自动生成列不作为“有用户内容”的依据。 | 先排除系统列、公式与 rollup 作为有效行依据;整表只含派生内容时回退到传统的非系统列口径。 |
| 存储与回退 | 写主索引。 | FRTR 意图选新 chunker;repository 可用才写独立表格索引,否则可将新切分结果写回主索引。退出灰度时清理可用的旧表格侧。 |
智能表格过滤的是“这一行是否值得索引”,并非删除这些列的所有渲染值。多行单元格会被压成单行,避免把一个 cell 的后半段误读成另一条记录。锁定单元格由上游提供占位符,未锁内容仍参与构建。
adapt_table(table):
if table.has_column_schema:
grid = [table.column_names] + render_typed_records(table)
regions = [whole_table_region(grid)]
header_is_declared = true
else:
grid = expand_merged_cells(table.visible_cells)
regions = detect_regions_from_occupied_cells(grid)
header_is_declared = false
headers = build_header_paths(grid, regions)
return grid, headers, header_is_declared
pack_rows(grid, headers, header_is_declared):
rows = grid.data_rows if header_is_declared else grid.all_rows
return pack_until_content_budget(rows, headers)电子表格的启发式表头保留在数据中,以避免误判漏内容;智能表格的显式表头可以安全摘除。有效行与派生列过滤属于前置适配规则。
这里的表格切分借鉴了 From Rows to Reasoning(FRTR) 的多粒度思路,但这里的主要目标是工作簿级召回:先找到值得读取的表,再让下游获取完整数据分析。采样后的 chunk 是检索信号,不能当作完整数据集计算精确答案。
左侧 A1:F8 是育苗明细,右侧 I1:L4 是区域基准,各有表头、字段和数据,G / H 两列为空白分隔。按占用分布识别得到这两个 region,后续粒度均在各自区域内生成。
窄屏上可横向滚动工作簿,查看右侧的区域基准。
| A | B | C | D | E | F | G | H | I | J | K | L | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 品种 | 区域 | 灌溉量 ml | 光照 h | 成活率 % | 株高 cm | 区域 | 基准量 ml | 目标成活率 % | 说明 | ||
| 2 | 鸢尾 | 东区 | 120 | 6 | 92 | 18 | 东区 | 100 | 95 | 偏湿 | ||
| 3 | 罗勒 | 东区 | 80 | 7 | 96 | 16 | 西区 | 80 | 94 | 常规 | ||
| 4 | 薄荷 | 西区 | 100 | 5 | 95 | 14 | 南区 | 60 | 92 | 偏干 | ||
| 5 | 鼠尾草 | 西区 | 60 | 8 | 90 | 15 | ||||||
| 6 | 迷迭香 | 南区 | 70 | 9 | 94 | 20 | ||||||
| 7 | 百里香 | 南区 | 50 | 8 | 93 | 12 | ||||||
| 8 | 金盏菊 | 东区 | 90 | 6 | 97 | 17 |
工作簿概览遍历所有 sheet 和 region。多 region 的 sheet 列出各子表范围与列名,样例留给 region summary。
Overview 属于工作簿,不绑定单个单元格范围。此格式的“12 rows”是两个 region 高度 8 + 4 的汇总,不是 sheet 的物理行数或数据记录数。
Workbook: 育苗观察簿
This workbook contains 1 sheet(s).
Sheet "育苗记录与基准" (12 rows):
Sub-table 1 at A1:F8 (8 rows):
Columns: 品种, 区域, 灌溉量 ml, 光照 h, 成活率 %, 株高 cm
Sub-table 2 at I1:L4 (4 rows):
Columns: 区域, 基准量 ml, 目标成活率 %, 说明Region 是结构恢复得到的矩形候选区域;本例分别对应明细和基准子表。Summary 写入整个 region 的字段与样例,Window 则枚举区域内的局部矩形。区域识别使用布局启发式,仍可能把说明文字、空列或相邻表块判断错,需结合字段和数据检查。
窗口数 = max(0, ⌊(行数 − 高度) / 行步长⌋ + 1) × max(0, ⌊(列数 − 宽度) / 列步长⌋ + 1)。行数和列数包含表头;不足窗口尺寸则产出 0 条,不补齐、不生成尾部半窗。本例 4×4、步长 1×1 时,明细 15 个,基准 1 个。
尺寸决定一个窗口覆盖多少行列;步长决定相邻窗口的起点距离;单窗口正文采样限制一条文本的长度。大表动态计划可把行高 / 行步长改为 8、16、32、64,列宽维持 4、列步长 1;最终覆盖还受生成配额和正文采样影响。
先生成非空且未超额度的窗口,再按起点加减步长回填八方向 chunk 标识。若某窗口被跳过或预算耗尽,对应邻接就不存在。本例未触发采样和配额截断,展示的是完整枚举结果。
为什么用两列空白分隔:识别规则可能合并仅隔一列空白、且有内容的行重合度 ≥ 0.6 的列组。空白列数量是布局证据之一,不能仅凭看到一条空列就认定出现了新子表。
for region in detected_regions:
last_row = region.end_row - window_height + 1
last_col = region.end_col - window_width + 1
windows_by_start = {}
for row in range(region.start_row, last_row + 1, stride_row):
for col in range(region.start_col, last_col + 1, stride_col):
cells = rectangle(row, col, window_height, window_width)
if quota_exhausted():
stop_region_generation()
if cells_are_empty(cells):
continue
record = emit_window(cells, original_rectangle_anchor)
windows_by_start[(row, col)] = record
for start, record in windows_by_start:
record.adjacent_windows = existing_neighbors_at(
start + eight_direction_offsets(stride_row, stride_col)
)此处省略单窗口 head + tail 采样及格式化。空间邻接引用生成窗口的标识;序列 prev / next 属于另一条关系。配额耗尽时生成过程会停止,而不是靠末端的 continue 无限遍历。
五种粒度提供互补检索信号,也会增加向量数量。预算控制分为生成配额、单片内容长度和最终落地条数:先保住高层语义,再给列、窗口与行组分配额度。
此图演示五类全开、前四 pass 用满逻辑上限时的预算顺序,不预测实际 chunk 数。本演示的 caps 固定为 3 / 18 / 35 / 35;拖动总预算不会同比放大它们。实际按产出扣减,二次切分还会消耗额外的落地条数。
Column 按列数权重、Window 按估算窗口数分配额度。分配器使用 floor + 最大余数,处理每 region 的最低份额与总 cap;预算少于 region 数时只保留较高权重区域。Summary 超配额时优先较大 region,再恢复输入顺序。
Row pass 先处理 ≤50 行的小 region,按实际打包产出的逻辑记录扣减额度;余量按行数分给大 region。先累积连续行直到内容预算,再在超额时等距 stride 采样,避免小汇总表被大明细表挤掉。
Row / Column 做内容驱动打包;Column / Window 控制正文长度并对长数据做 head + tail 降采样。Window 采样保持整行宽度,避免列错位。最终仍有硬切兜底和 fragmentation 指标,不能把它当成常规压缩手段。
普通文本 / 网格的正文目标 1024 runes、含 breadcrumb 的内容预算 1280 runes;索引 content 字段上限 4096 bytes。PPT 的局部内容预算按 byte 解释。字符数不能替代 UTF-8 字节数。会议纪要还检查真实 token 数,避免 embedding 截断尾部。
output = []
max_chunks = 350
for kind, quota in [
(OVERVIEW, 3), (SUMMARY, 18),
(COLUMN, 35), (WINDOW, 35), (ROW, REMAINING)]:
remaining = max_chunks - len(output)
if remaining == 0:
break
limit = remaining if quota == REMAINING else min(quota, remaining)
records = generate(kind, contexts, limit)
for record in records:
fragments = encode_for_storage(record)
output.extend(fragments[:max_chunks - len(output)])
row_pass(regions, budget):
small_regions = [r for r in regions if r.row_count <= 50]
large_regions = [r for r in regions if r.row_count > 50]
small = take_within_budget(pack_all_rows(small_regions), budget)
budget -= count(small) # 扣打包记录数,不扣原始行数
if budget == 0:
return small
quotas = allocate_by_weight(large_regions.row_counts, budget)
large = pack_then_stride_sample(large_regions, quotas)
return small + largeREMAINING 表示剩余额度。此处强调逻辑记录与落地分片分别计数;真正的生成器还需处理 region 权重、空内容、单片长度与负值“不限”等条件。
原始节点 / 页图 → 转换文本 → 片段逐层比对。观察事实、单位、标题、页序、引用主体、空页和硬截断,区分 parser / Vision 损失与 splitter 损失。
用条件句、跨段指代、长代码、跨页图表和跨发言决策构造匿名问题。检查 Top-K 是否同时包含必要事实与限定条件,而不只是命中正确文档。
同条件对照粒度 / overlap,统计去重后证据量、候选文档多样性、上下文占用;局部编辑前后观察边界漂移、向量复用和重建耗时。
这些是建议的验收维度,不是新增实验结果。正文、PPT、纪要尚无可在本文展示的同条件量化对照;表格的历史工作簿召回实验也不能代替这些验证。
文档缓存用来判断是否需要重建,chunk 指纹用来减少 embedding 成本,文档锁用来约束并发修改。三者解决不同问题;缓存命中不是索引事务的替代品。
Hit四类一致 → 结束。入口早判仅适用于字段充分且全为 CONTENT 的事件;含 BASE / AUTH、force 或缓存不可读时仍向下处理。
MetaOnly正文、上下文、pipeline 一致 → 读取目标索引的旧 chunk,只套新元信息再整篇写回。无需正文下载与重新 embedding。
Rebuild / Deferred内容、上下文或切分版本变化 → 重建。正文 MD5 未给且尚未下载 → Deferred,下载后执行第二次完整判定。
向量复用键是 MD5(Content + PipelineHash),不包含权限 MetaHash;空旧向量不复用。force=rebuild 绕过文档缓存但可复用向量;force=reembed 额外跳过旧 chunk 读取。
锁键为 collection + docID;示例锁配置的 TTL 为 120 秒,自动续期,获取重试间隔 2 秒、最大等待 10 秒。下载正文、切分、删除、写入和 done 操作在锁内,避免同篇重建与删除互相覆盖。失锁 context 会取消后续操作,清理另外检查锁所有权;已发出的 Milvus 请求只能尽力取消,目标端没有 fencing token 保证。
空 Content、非法 UTF-8、空向量和不合规条数可以提前发现。先清再发现不可写,会用一次失败换掉整篇旧索引。写前预检把这些错误挡在删除之前。MetaOnly 的存量 Content 不套用新的严格内容规则,避免旧内容不合规让一次权限收紧永久无法写入。
这里采用 delete-by-doc + 分批 insert,不是原子替换。某批失败可能留下“旧的已删,新的只写一部分”。调用方在写入失败时删除旧 done 标记并返回错误,尝试通过重试收敛;删除 done 自身失败也只记日志,旧标记可能继续遮蔽修复。done 的 Redis 写失败只记录日志,不重试已完成的索引写入。缺标会增加未来重建成本,错误打标却可能永久跳过修复。
有 chunk 的重建要求目标侧清理成功;非目标侧清理失败只告警后继续,因此仍可能残留。0 chunk 清理和显式删除则要求所有可用、被选中清理侧成功。删除成功后清掉 done 而不重新打标,文档恢复时才有机会重新构建。
消费者关闭自动 mark,由滑动窗口跟踪已完成消息并推进提交,窗口满时形成背压。处理尝试失败后先按配置立即重试,再投递 DLQ;DLQ worker 用 Redis ZSET + HASH 延迟调度,并把事件投回原 topic。延迟记录按 docID 保存,同篇后续消息可覆盖前一条;超过最大次数只告警后结束。原消费者回调结束会 处理完成标记,DLQ 发送失败只记录日志,所以这套机制不能宣称端到端零丢失或 exactly-once。
表格 chunk 是语义代表,尾部受预算压缩仍可提供召回信号。纪要超限属于异常保护,静默截断逐字稿会丢掉后段且调用方难以察觉,因此使用 超限拒绝策略 并保留旧索引。合法空纪要清旧;异常空 payload 报错保留旧索引,二者不能合并。
with document_lock(document_id):
decision = compare(meta_hash, content_hash, context_hash, pipeline_hash)
if decision == HIT:
return SKIP
if decision == META_ONLY:
chunks = replace_metadata(read_existing_chunks(), new_metadata)
else:
chunks = parse_and_chunk(load_body(), plan)
for chunk in chunks:
key = hash(chunk.content + pipeline_version)
chunk.vector = reuse_nonempty_vector(key) or embed(chunk.content)
validate_chunks_before_deleting(chunks)
verify_lock_ownership()
clear_indexes_according_to_policy()
try:
insert_in_batches(chunks, batch_size=200)
except WriteError:
best_effort_remove_completion_marker()
raise
best_effort_mark_complete()没有把权限元信息加入向量复用键。清理与分批插入不是原子事务,失锁取消和 best-effort 标记也不提供零丢失保证;具体失败边界见本节说明。
Content 支持语义与关键词两路检索,顶层字段支持来源、权限与粒度筛选,extra_meta 保留回到原表的位置。构建和召回的配置需要对齐:有新索引但未开启相应召回,效果不会自动出现。
请求来源 ∩ 服务允许类型;目录、归档与权限组合成过滤表达式,在 Top-K 前下推。
同一个查询绑定文本与向量。各路并发检索,再用倒数排名融合(RRF)合并排名。
按接口组装 rerank / finalize / score_filter / Top-K。需要原文时走回查或由下游取完整数据。
主库与表格库的 dense 候选共用模型和 COSINE,按有效相关分排序,保留真实 cosine。Sparse 跨库及 dense ↔ sparse 使用排名融合,再进入同一后处理链。
Cosine、BM25、归一化 RRF 与 rerank 不是同一个标尺。Dense 跨库权重用于排序,不改写真实 cosine 阈值。Reranker fallback 时跳过不再可比的阈值;部分检索接口用 -1 哨兵或 ScoreType 表达降级。
MetaOnly 把权限变化写回 chunk;检索先下推索引权限。执行知识源批量权威鉴权的路径,在调用失败时返回空结果、未明确授权的文档不保留。请求范围只能收窄服务配置,未知来源枚举返回参数错误。
主路 BM25“能力不存在”且 dense 可用时可降到 dense;普通查询错误或超时继续上抛。独立 FRTR 侧路是增量能力,其错误可降为空侧路。不能把所有异常都归结成“返回部分结果”。
目录范围约束普通文档,显式混合来源时表达为“纪要 OR(普通文档 AND 目录)”,再与权限等约束相交。minutes-only 不查询表格侧路。多 query 每个拥有独立 RunState,最后轮询合并与去重。
架构的价值来自约束下的选择。下表把设计选择、原因和代价放在一起;实现层的收益与实验指标分开描述,避免用一个数字解释全部设计。
锁边界、缓存分支与多类型路由交织,隐式处理链难以确认执行顺序。
步骤可以单独观测和测试;类型只解释一次。新类型仍需定义空内容、失败和清理语义。
共享 topic 下无关 builder 会放大查询、锁竞争和重复删除;显式删除不需要再问正文是否存在。
明确的非本类型事件可做到零查询、零缓存、零锁。依赖类型不可漂移等生产方契约;缺类型保留兼容成本。
只有逐行文本难以覆盖主题、列分布和局部矩阵查询;大明细表容易淹没小汇总表。
概览与摘要不被 row 尾部预算挤掉;小 region 优先。Summary 全局 cap 仍可能排除部分小区域。
固定“几行一片”无法适配宽表 / 窄表;超长逻辑片段会触发二次切分,消耗额外预算。
减少碎片、提高单片信号密度,可复现。采样不保证保留任意关键行,精确统计必须读取原表。
新策略需要粒度、region 与 sheet 字段,也需要灰度验证和退回旧路径。
独立配置与召回模式支持演进。切分 / 写入 / 清理 / 召回要一起考虑,非目标清理失败仍可能残留。
权限变化不改变文本向量;路径、标题和切分配置变化却会影响真实 Content。
元信息更新无需重算 embedding;新 chunk 可复用旧向量。指纹覆盖不完整或版本未更新时,需要显式重建存量。
删除之后才发现空向量 / 超限会损失旧索引;失败时保留旧 done 又可能永远命中错误缓存。
提前拒绝可预知写入错误,并让失败重试。Milvus 分批插入没有原子替换,仍可能出现半写状态;重试链也存在有限次数与投递失败边界。
启动时 DDL 把结构变更混进滚动部署,放大运行期不确定性。
先 dry-run,再 apply,再启动构建与搜索。迁移流程添加缺失字段 / 索引,遗留字段告警,不自动删除。
| 检索模式 | wb_recall@5:基线 → FRTR | wb_recall@50:基线 → FRTR | 工作簿 MRR:基线 → FRTR |
|---|---|---|---|
| Hybrid | 0.67 → 0.93 | 0.89 → 1.00 | 0.60 → 0.82 |
| Dense | 0.56 → 0.93 | 0.59 → 0.98 | 0.54 → 0.91 |
| BM25 | 0.38 → 0.71 | 0.81 → 0.87 | 文档未给出 |
历史报告记录了 column / window 超长碎片、小 region 被挤压等修复和实验结果。不能把粒度首命中占比解释为严格消融因果,也不能用“减少 chunks”直接推出端到端成本或延迟下降同样比例。
该方案将总 cap 收紧至 350,并配合内容驱动 row 打包、小 region 按实际记录数扣减、两种表格输入共用五 pass。历史 0.93 不能直接归属于这套参数,仍需要同条件实验验证。