合并算法¶
富化管道现在采用字段级合并架构:
src/pipeline/merge_resolver.py中的MergeResolver负责结构化字段决策。src/pipeline/adjudicator.py中的SpecialNotesAdjudicator负责Special Notes语义整合。
分阶段富化流程先通过 MergeResolver.resolve() 完成结构化字段决策,再通过 SpecialNotesAdjudicator 完成语义化备注综合。
字段级权威矩阵¶
合并优先级由 src/pipeline/field_authority.yaml 配置驱动,而非全局固定策略顺序。
矩阵支持:
global:全局字段来源排序template_overrides:按模板覆盖字段排序field_aliases:字段别名与拼写修正
示例:
Width可按模板优先dimension_card或main_table_seedMaterial可优先text_rule而非表格值Special Notes不走常规抢占,改由 adjudicator 处理
候选值收集¶
MergeResolver 会为每个 row_id + field 构建 FieldCandidate:
- 主表种子候选(
main_table_seed)
主表中带__row_id__的原值始终作为候选。 - 专业策略候选
所有策略输出(GeminiEnrichedRowResult)均参与。
每个候选包含:
strategyvaluefield_sourcerow_conf- 可选
field_claim_conf stage_indexreasoning
胜出规则¶
对每个非备注字段,候选按以下顺序评分:
- 权威矩阵中的来源排名(
rankings(template, field)) - 枚举合法性惩罚(合法优先)
- 更高
field_claim_conf - 更高
row_conf - 更小
stage_index(同分时早阶段优先)
这样可以在字段级获得确定性结果,避免“单策略全行统治”。
Source Type 计算字段¶
Source Type 在矩阵中配置为计算字段([computed]),不参与常规候选排名抢占。
在非备注字段胜出后,resolver 按胜出字段的 field_sources 计算 Source Type:
- 只要存在
FieldSource.IMAGE_CONTEXT->Image - 否则若存在
FieldSource.MAIN_TABLE/FieldSource.AUXILIARY_TABLE/FieldSource.TEXT_CONTEXT->Table - 否则 -> 空字符串
枚举优先合法值¶
若同一枚举字段同时存在合法与不合法候选,resolver 会优先选择合法候选。最终 Other: ... 强制兜底仍在富化末尾的枚举校验阶段执行。
Special Notes 语义裁决¶
Special Notes 采用语义裁决,而不是子串去重。在基准测试中,纯词面去重会合并或丢失有效条款,导致备注召回下降。这符合 Cartex 的 guardrail 设计:开放文本字段采用软约束与语义综合,硬约束仅用于可边界验证的字段(如枚举;详见pipeline 运行技术规格)。
流程:
- resolver 从
main_table_seed和所有策略收集 note observations。 SpecialNotesAdjudicator按row_id聚合。- 单 observation 行直接输出一个 bullet。
- 多 observation 行按批次调用 FAST 模型综合(
GeminiSpecialNotesSynthesisResult)。 - 模型失败时回退到确定性去重 bullet 逻辑。
最终 EnrichedRow.data["Special Notes"] 为按换行分隔的 bullet 文本。
置信度、来源与推理¶
对每个结果行:
confidence:取所有胜出非备注字段的最小row_conf(若无更低值则为 1.0)field_sources:来自胜出候选的来源元数据reasoning:胜出候选推理去重拼接,并附加 adjudicator 合成说明
行恢复¶
权威 row_id 集合先由 Enricher 在调用 resolver 前准备:
- 对纯索引回退 ID(
{table_id}_row_{n})的表,不纳入权威集合,避免误恢复
resolver 再基于该集合执行“主行不丢失”恢复:
- 若某权威
row_id无输出,则补回空EnrichedRow(confidence=0.0)
合并流程图¶
flowchart TB
subgraph candidates["候选收集"]
MAIN["main_table_seed 候选"]
S1["所有已完成阶段的专业输出"]
MAIN --> CAND["按 row_id + field 聚合候选池"]
S1 --> CAND
end
subgraph resolve["MergeResolver"]
CAND --> PICK["逐字段评分<br/>(排名、合法性、置信度、阶段)"]
PICK --> ROWS["list[EnrichedRow](未最终写入备注)"]
end
subgraph notes["SpecialNotesAdjudicator"]
CAND --> OBS["按 row_id 聚合 note observations"]
OBS --> LLM["批量语义综合<br/>(FAST model)"]
LLM --> APPLY["bullet 输出 + 失败回退"]
end
ROWS --> APPLY --> FINAL["最终 EnrichedRow 列表"]