Complete the selective V2 checkpoint with its minimal AgentBus, object-storage, replay persistence, and validated-workbench shared dependency closure.
371 lines
15 KiB
Markdown
371 lines
15 KiB
Markdown
# M012 / QBD+LianTai Parser 输出冻结契约 v1
|
||
|
||
| 项 | 内容 |
|
||
|---|---|
|
||
| 状态 | **FROZEN — USER CONFIRMED;PARSER IMPLEMENTED;REAL-SAMPLE CORRECTIONS ACCEPTED** |
|
||
| 日期 | 2026-08-11 |
|
||
| 契约名称 | `fixed-channel-parser-fact-material-v1` |
|
||
| 适用渠道 | QBD、LianTai |
|
||
| 不在范围 | 其他 sender、Parsing Agent 完整输出 schema、Rate/Room、TaskCard、PMS/Opera |
|
||
| 变更规则 | 冻结后只能通过新 ADR 和新 contract version 修改 |
|
||
|
||
## 1. 给业务人员看的结论
|
||
|
||
Parser 每次处理一份工作簿,只交付:
|
||
|
||
1. 本次处理是否正常完成;
|
||
2. 程序已经确认的业务信息;
|
||
3. 程序看到了、应该继续处理、但无法确认的原文。
|
||
|
||
程序不再输出“缺失、未解决、歧义、冲突、原因代码”等分类。
|
||
|
||
## 2. 顶层输出
|
||
|
||
```json
|
||
{
|
||
"meta": {},
|
||
"status": "SUCCESS",
|
||
"facts": [],
|
||
"agent_materials": []
|
||
}
|
||
```
|
||
|
||
| 字段 | 必填 | 业务含义 |
|
||
|---|---:|---|
|
||
| `meta` | 是 | 来源文件、渠道 Profile 和版本等追踪信息 |
|
||
| `status` | 是 | `SUCCESS` 或 `FAILED` |
|
||
| `facts` | 是 | 已经形成唯一确定值的事实,允许空数组 |
|
||
| `agent_materials` | 是 | 需要继续处理的原始材料,允许空数组 |
|
||
|
||
业务结果只有 `facts` 和 `agent_materials` 两类。`meta/status` 只是运行外壳。
|
||
|
||
## 3. `meta` 冻结字段
|
||
|
||
```json
|
||
{
|
||
"parser_result_id": "pr_xxx",
|
||
"source_message_id": "msg_xxx",
|
||
"attachment_id": "att_xxx",
|
||
"attachment_content_hash": "sha256:...",
|
||
"file_name": "QBD月表.xlsx",
|
||
"profile_code": "QBD",
|
||
"contract_version": "fixed-channel-parser-fact-material-v1",
|
||
"parser_version": "1.0.0",
|
||
"profile_version": "1.0.0",
|
||
"rule_bundle_version": "1.0.0"
|
||
}
|
||
```
|
||
|
||
`profile_code` v1 只允许:
|
||
|
||
```text
|
||
QBD
|
||
LIANTAI
|
||
```
|
||
|
||
文件名只用于描述和审计,不参与渠道业务判断。
|
||
|
||
## 4. `facts[]` 冻结字段
|
||
|
||
```json
|
||
{
|
||
"fact_id": "fact_xxx",
|
||
"fact_key": "target_1/room_1/SOURCE_ROOM_NAME/CURRENT",
|
||
"target_ref": {
|
||
"target_id": "target_1",
|
||
"row_unit_id": "row_12",
|
||
"segment_id": "segment_1",
|
||
"room_item_id": "room_1"
|
||
},
|
||
"semantic_role": "SOURCE_ROOM_NAME",
|
||
"material_state": "CURRENT",
|
||
"value_type": "STRING",
|
||
"raw_value": "ONE-BEDROOM-SUITE TWN BF(Q10)",
|
||
"normalized_value": "ONE-BEDROOM-SUITE TWN BF(Q10)",
|
||
"source_locations": [],
|
||
"rule_ref": "shared-source-room-name-v1"
|
||
}
|
||
```
|
||
|
||
| 字段 | 必填 | 说明 |
|
||
|---|---:|---|
|
||
| `fact_id` | 是 | 本条事实的稳定唯一 ID |
|
||
| `fact_key` | 是 | 合并时识别同一事实槽位的稳定键;由 target、role、state 构成 |
|
||
| `target_ref` | 是 | 事实属于哪一业务行、住宿段和房型项;不适用的下级 ID 为 `null` |
|
||
| `semantic_role` | 是 | 本契约允许的业务事实类型 |
|
||
| `material_state` | 是 | `CURRENT`、`BEFORE` 或 `AFTER`;fact 不允许 `MIXED` |
|
||
| `value_type` | 是 | `STRING / INTEGER / DECIMAL / DATE / BOOLEAN / JSON` |
|
||
| `raw_value` | 否 | 来源中的原始值;确定性默认值允许为 `null` |
|
||
| `normalized_value` | 是 | Parser 依据冻结规则形成的唯一规范值 |
|
||
| `source_locations` | 是 | 一个或多个可复核来源位置,不允许空 |
|
||
| `rule_ref` | 是 | 形成该事实的确定性规则引用;不是失败原因代码 |
|
||
|
||
`facts` 内不存在独立的状态、缺失标志或 reason code。fact 存在本身就表示该值已确定。
|
||
|
||
## 5. 冻结的十类业务事实
|
||
|
||
| `semantic_role` | 给业务人员的含义 | v1 规则摘要 |
|
||
|---|---|---|
|
||
| `GROUP_CODE` | 团号/预订目标代码 | 统一承接 Tour Code、Group Code 等已确认来源别名 |
|
||
| `SOURCE_ACTION_DATE` | 来源动作指令日期 | 只表示表格指令写出的日期,不是处理时间或入住日期 |
|
||
| `SOURCE_ACTION_KIND` | 来源动作类型 | 规范值只允许 `NEW_BOOKING / UPDATE_BOOKING / CANCEL_BOOKING`;原始细分文字保存在 `raw_value` |
|
||
| `ARRIVAL_DATE` | 入住日期 | 必须由当前材料和 Profile 日期规则唯一确定 |
|
||
| `DEPARTURE_DATE` | 离店日期 | 必须由当前材料和 Profile 日期规则唯一确定 |
|
||
| `NIGHTS` | 晚数 | 入住、离店均确定后按日期差确定;不跨住宿段累加 |
|
||
| `SOURCE_ROOM_NAME` | 完整来源房型名称 | 保留 BF、Q10、FAM/PAX、床型、景观等来源含义 |
|
||
| `BREAKFAST_INCLUDED` | 来源明确声明的早餐条件 | 独立 BF=`true`;明确 RO/不含早=`false`;未声明时不生成该事实 |
|
||
| `ROOM_QUANTITY` | 房间数量 | 明写数量按原文;Profile 已冻结的“缺数量默认 1”可生成事实并保留 rule ref |
|
||
| `SOURCE_PRICE` | 来源价格 | 只有来源价格存在且能按冻结规则唯一规范化时生成 |
|
||
|
||
以下内容不属于 Parser v1 公共事实:
|
||
|
||
```text
|
||
GROUP_IN
|
||
SOURCE_RATE_CODE
|
||
ROOM_QUALIFIERS
|
||
BOOKING_TYPE
|
||
canonical Room / Rate Code
|
||
Allotment source/actual relationship
|
||
最终业务事件
|
||
Trace / 普通备注的最终业务处置
|
||
TaskCard / 部门 / PMS或Opera执行状态
|
||
```
|
||
|
||
## 6. 完整来源房型名称、BF 与 Q10
|
||
|
||
### 6.1 不拆 Q10
|
||
|
||
原文:
|
||
|
||
```text
|
||
ONE-BEDROOM-SUITE TWN BF(Q10)
|
||
```
|
||
|
||
正确输出:
|
||
|
||
```text
|
||
SOURCE_ROOM_NAME = ONE-BEDROOM-SUITE TWN BF(Q10)
|
||
BREAKFAST_INCLUDED = true
|
||
```
|
||
|
||
错误输出:
|
||
|
||
```text
|
||
SOURCE_ROOM_NAME = ONE-BEDROOM-SUITE TWN
|
||
ROOM_QUALIFIERS = Q10
|
||
```
|
||
|
||
### 6.2 允许的规范化
|
||
|
||
`raw_value` 保留准确来源文本。`normalized_value` 可以统一大小写、重复空格和无业务含义的连字符差异,
|
||
但不能删除或拆分 `BF`、`Q10`、ONE/TWO-BEDROOM、DBL/TWN/TRP、FAM/PAX、U、GARDEN/POOL/VIEW
|
||
等业务区分项。
|
||
|
||
若数量或价格与房型名称紧邻,只有在 Profile 规则能唯一证明它们分别属于数量/价格时才允许拆出;
|
||
拆出后仍不得破坏完整来源房型名称中的 BF/Q10 语义。
|
||
|
||
### 6.3 早餐
|
||
|
||
- 出现独立 `BF` 标记:`BREAKFAST_INCLUDED = true`。
|
||
- 出现明确 `RO` 或“不含早”:`BREAKFAST_INCLUDED = false`。
|
||
- 两者均未出现:不生成 `BREAKFAST_INCLUDED`,不得把未声明解释为不含早。
|
||
- `Q10` 与早餐判断无关。
|
||
- 输出早餐布尔值时,不能从 `SOURCE_ROOM_NAME` 中删除 `BF`。
|
||
- 公共字段结构仍为 v1;本次语义通过 `shared-breakfast-explicit-token-v2` 规则引用版本化,不新增字段。
|
||
|
||
## 7. `agent_materials[]` 冻结字段
|
||
|
||
```json
|
||
{
|
||
"material_id": "material_xxx",
|
||
"target_ref": {
|
||
"target_id": "target_1",
|
||
"row_unit_id": "row_12",
|
||
"segment_id": "segment_1",
|
||
"room_item_id": "room_1"
|
||
},
|
||
"expected_fact_keys": [
|
||
"target_1/room_1/SOURCE_PRICE/CURRENT"
|
||
],
|
||
"material_state": "CURRENT",
|
||
"material_kind": "WORKBOOK_TEXT_SPAN",
|
||
"raw_content": "【U-DBL1.2/1.3】",
|
||
"context": {
|
||
"header": "房型及价格",
|
||
"neighboring_cells": ["2间", "含早"]
|
||
},
|
||
"source_locations": []
|
||
}
|
||
```
|
||
|
||
| 字段 | 必填 | 说明 |
|
||
|---|---:|---|
|
||
| `material_id` | 是 | 待处理材料的稳定唯一 ID |
|
||
| `target_ref` | 否 | 能安全定位所属目标时填写;不能猜测 |
|
||
| `expected_fact_keys` | 是 | 已知待解决事实槽位时填写;整表兜底时允许空数组 |
|
||
| `material_state` | 是 | `CURRENT / BEFORE / AFTER / MIXED` |
|
||
| `material_kind` | 是 | `ATTACHMENT / SHEET / ROW / CELL / TEXT_SPAN` |
|
||
| `raw_content` | 是 | 原文、结构化单元格内容或受控附件引用 |
|
||
| `context` | 是 | Agent 理解该材料所需的最小表头、相邻值和格式上下文;允许空对象 |
|
||
| `source_locations` | 是 | 一个或多个准确来源位置,不允许空 |
|
||
|
||
`agent_materials` 不包含:
|
||
|
||
```text
|
||
MISSING
|
||
UNRESOLVED
|
||
AMBIGUOUS
|
||
CONFLICT
|
||
reason_code
|
||
```
|
||
|
||
每个 material 应尽量对应一个待决事实问题;可以带更宽的只读上下文,但不能成为无边界原文垃圾桶。
|
||
|
||
## 8. 共用来源位置
|
||
|
||
```json
|
||
{
|
||
"attachment_id": "att_xxx",
|
||
"sheet_name": "2026.08",
|
||
"row_number": 12,
|
||
"cell_ref": "F12",
|
||
"text_start": 0,
|
||
"text_end": 15,
|
||
"formatting_signature": "fill:#FFFF00",
|
||
"content_hash": "sha256:..."
|
||
}
|
||
```
|
||
|
||
不适用的细粒度位置字段可为 `null`,但 attachment ID、content hash 和能够达到的最精确位置必须存在。
|
||
不得在结果中嵌入附件 URL、Secret 或附件二进制/Base64。
|
||
|
||
## 9. 唯一判断规则
|
||
|
||
Parser 按事实槽位判断,不按整行或整格一刀切:
|
||
|
||
1. 唯一规范值成立:输出 fact。
|
||
2. 有原文但不能唯一确定:不输出该 fact;输出 agent material。
|
||
3. 来源中完全没有该内容:fact/material 都不输出。
|
||
4. 同槽位多个来源给出相同规范值:输出一个 fact,并保留全部来源位置。
|
||
5. 同槽位存在不同规范值:不选择任何一个作为 Parser fact;保留全部相关原文为 material。
|
||
|
||
例如 `【U-DBL1.2/1.3】2`:若房型和数量可以唯一确认,Parser 输出
|
||
`SOURCE_ROOM_NAME = U-DBL` 与 `ROOM_QUANTITY = 2`;价格原文进入 material,不生成 `SOURCE_PRICE`。
|
||
|
||
## 10. SUCCESS / FAILED
|
||
|
||
### SUCCESS
|
||
|
||
表示 Parser 正常结束且结果可信,不表示“所有字段齐全”。以下均合法:
|
||
|
||
```text
|
||
facts有值,agent_materials为空
|
||
facts有值,agent_materials有值
|
||
facts为空,agent_materials有值
|
||
facts为空,agent_materials为空(工作簿中没有本期业务行)
|
||
```
|
||
|
||
能安全读取但不支持的版式、多个候选业务表或无法唯一识别的业务区域,使用 `SUCCESS` 并把必要的
|
||
Sheet/Row/Attachment 材料交给 Agent,不改成歧义状态。
|
||
|
||
### FAILED
|
||
|
||
只用于结果无法被信任的技术失败,例如:
|
||
|
||
- 文件损坏、加密或无法安全读取;
|
||
- 文件大小/安全限制;
|
||
- Parser 未处理异常;
|
||
- 结果身份、来源定位或序列化契约无法成立。
|
||
|
||
`FAILED` 时 `facts` 必须为空,不发布部分事实。原始附件仍由外层 SourceMessage/Attachment 记录保留,
|
||
由外层流程进入失败复核或受控兜底;Parser 不伪造材料内容。
|
||
|
||
## 11. 合并结果边界
|
||
|
||
本契约同时冻结合并结果的两类顶层业务内容:
|
||
|
||
```json
|
||
{
|
||
"effective_facts": [],
|
||
"review_items": []
|
||
}
|
||
```
|
||
|
||
- Parser facts 不可被 Agent 覆盖。
|
||
- Agent 只能基于获准的 agent material 补充候选事实。
|
||
- 同一事实键同值可以合并来源;不同值不得静默覆盖,进入 review。
|
||
- 每个 agent material 最终必须被接受事实覆盖,或进入 review,不能静默消失。
|
||
- 必需事实完全缺失时,由合并后的完整性校验产生 review,不由 Parser 伪造材料。
|
||
|
||
Parsing Agent 自身的完整输入/输出字段和 review item 的展示分类不在本 Parser 契约中冻结,后续单独确认。
|
||
|
||
## 12. Core 与 Profile 边界
|
||
|
||
### 共享 Parser Core
|
||
|
||
- 输出结构、版本和运行状态;
|
||
- fact/material 稳定 ID、事实键和 target 结构;
|
||
- “唯一值才形成事实”的门禁;
|
||
- 来源位置与上下文封装;
|
||
- 结果 schema 校验。
|
||
|
||
Core 提供一个只供 Profile 使用的原始材料入口。Profile 交入业务行、整段原文、上下文和证据位置,Core
|
||
统一生成正式 `agent_materials`。备注类材料固定为 `CURRENT`,`expected_fact_keys=[]`,上下文固定包含
|
||
`material_purpose=USER_REMARK`、`source_column`,能取得表头时还包含 `source_header`。Core 必须验证材料
|
||
引用的业务行、来源消息、附件、Sheet、行号和单元格都与已登记证据一致;任一不一致时 Parser 整体
|
||
`FAILED`,不发布部分结果。
|
||
|
||
### QBD / LianTai Profile
|
||
|
||
- 工作簿、Sheet、表头和业务行识别;
|
||
- 渠道列位、底色、rich text 和模板噪音;
|
||
- 日期、来源动作、房型、数量和价格语法;
|
||
- Profile 确定性默认规则。
|
||
|
||
Profile 允许内部临时拆分 BF、Q10、价格 token 或 QBD `GROUP IN` 单元格,但公共输出必须重新符合本契约;
|
||
内部字段不得泄漏成第十一类公共事实。
|
||
|
||
QBD Profile 补充规则:
|
||
|
||
- `WAITING 等待确认 / อักษรดำ` 是可选备注列,按表头文字识别,不固定为 K 列;只在核心表头行及其紧邻
|
||
上一行寻找。没有该列不影响旧模板;同一表头带出现多个候选时不得猜测,走既有附件级兜底材料;
|
||
- WAITING 不参与业务行选中。只对已经选中的有色业务行读取非空单元格,每格整段原文形成一条备注材料;
|
||
空格不生成任何输出,`2300 THB` 等内容不得拆数字、解释为价格或生成 `SOURCE_PRICE`;
|
||
- 每个物理明细行先按完整“房型+数量”块判定状态。所有块均划线时,该行日期、晚数、房型、数量都属于
|
||
`BEFORE`;所有块均未划线且同一业务行已有修改前内容时都属于 `AFTER`;没有修改前内容时仍为
|
||
`CURRENT`。不同房型块状态不一致或单块内部混合时保持 `MIXED`,不强行生成整行日期事实;没有房型块时
|
||
沿用整行格式判断。
|
||
|
||
LianTai Profile 补充规则:
|
||
|
||
- G、I 列完整原文及 D 列完成确定性字段提取后的残余原文,均通过上述共享入口形成备注材料;
|
||
- G 列已经唯一形成的 `SOURCE_ACTION_DATE`、`SOURCE_ACTION_KIND` facts 继续保留,完整 G 原文可以同时
|
||
作为补充材料保留;旧内部 `classification_status`、reason code 等状态不得进入公共结果。
|
||
|
||
## 13. 迁移与验收门禁
|
||
|
||
本契约冻结时,现有代码仍使用旧 `FieldObservation`、多状态和 Recovery Patch 模型。2026-08-11 已完成
|
||
Parser-only 迁移切片,实施状态如下:
|
||
|
||
1. 已以本契约替换 QBD、LianTai 和共享入口的默认 `parse` 公共 DTO;
|
||
2. 目标输出已移除旧公共状态、reason code、`GROUP_IN`、`SOURCE_RATE_CODE`、`ROOM_QUALIFIERS`;
|
||
3. 完整来源房型名称与 BF/Q10 规则已进入双渠道共享契约测试;
|
||
4. 十类 facts、部分确定、完全缺失、材料冲突、SUCCESS/FAILED 已有契约测试;
|
||
5. 真实样本缺口已修正并回放通过:QBD 为 114 facts、4 条 WAITING materials,rows 118/121 的
|
||
BEFORE/AFTER 日期、晚数、房型和数量完整;LianTai AI 样板为 70 facts、6 materials,其中 G 列 5 条、
|
||
I 列 1 条。公共 DTO 与十类 fact 目录未改变;
|
||
6. Parsing Agent v1.0 已由独立 ADR-009 冻结;CP4 已接通默认关闭的 runtime/merge/fake链。旧
|
||
observation/Recovery只保留在显式 `parseLegacy` 兼容桥,尚未删除;
|
||
7. 本次没有启用 Provider、AgentBus Recovery、PMS、Opera 或生产功能。
|
||
|
||
## 14. 权威性
|
||
|
||
对 QBD/LianTai Parser 公共输出,本文件与 ADR-008 的优先级高于:
|
||
|
||
- ADR-001 的字段 Recovery 公共边界;
|
||
- ADR-007 的旧 observation/Field Recovery 兼容描述;
|
||
- `C03-v2` Draft 的多状态 contribution core;
|
||
- 旧 QBD/LianTai Parser CR 中的 observation status、reason code 和内部拆分字段。
|
||
|
||
历史文档继续保留用于说明当前代码和迁移来源,不得再作为新 Parser 输出的实现依据。
|