resolved_subject回指消解落地(仅user_id半,issue #137) #145

Merged
Yushu merged 1 commit from feat/137-resolved-subject-anaphora into main 2026-08-17 05:26:28 +00:00
Member

Closes #137

做了什么

Delta 压缩那一次 LLM 调用顺带产出 resolved_subject(ADR-0016),本片只落地 user_id 半

  • _EXTRACT_MEMORIES_TOOL:facts/events 候选各自新增独立的 resolved_subject 字段(["string", "null"],二元产物,不是置信度);facts 侧 scope_id 类型同步放宽为 ["string", "null"],description 补上"仅结构化已知才填、代词推断的改填 resolved_subject"的对称引导。
  • _build_prompt 的 instruction 改写(不是追加):facts/events 两个子句都重写成"先试着通过上下文消解代词/模糊指代,锁定了就写进 resolved_subject;实在锁定不了才填 null,不要瞎猜"的两段式,消除了旧文案"不要瞎猜"与新增"请消解"之间的直接冲突。
  • 解析层:新增共用辅助函数 _resolve_subject。facts 侧是 fallback-only 语义——scope_id 已合法时不读 resolved_subject(结构化判断优先,不做仲裁);scope_id 缺失时才尝试 resolved_subject,仍失败则维持既有整条候选丢弃。events 侧是并集语义——participants 的既有 null 过滤逻辑完全不动,resolved_subject 去重后追加进最终 participant_ids
  • events.py::EventRecord 的 docstring 未做任何文字改动——它此前描述的"回指消解成功、或结构化已知的参与者"是一句指向不存在机制的话,本片落地后这句话本身就变成了准确描述。

明确不在本片范围(留痕,非无声消失)

  • event_id 半:ADR-0016 原文写的是"具体 user_id/event_id 或 null",但 _build_prompt 目前没有喂"已知事件"上下文,EventCandidate/EventRecord 也没有关联既有事件的字段/合并语义——体量远大于 user_id 半,留给后续 ticket。已在 ADR-0016 更新节留两条具体面包屑。
  • relations:结构上与 facts/events 同样有主体判定需求,但核实后确认 ADR-0016 全文从未提到它(RelationshipEdgeCandidate 早于本 ADR 存在,issue #36/ADR-0008),判定是背景节枚举写入目标时的遗漏、不是有意排除;且 relations 有两个主体位,直接照搬 facts 单字段 fallback 不成立,需要独立设计。已在 issue #137 评论里留痕。

已知局限

events 侧 resolved_subject 是标量,一次候选只能消解一个模糊指代;同一事件里如果有多个不同的模糊指代分别指向不同的人,只有一个会被捕获,其余仍随 null 一起略过(fail-closed,漏报不误报)。已记入代码注释与 ADR-0016 更新节。

验证

  • 新增测试覆盖 tests/test_delta_compressor.pyTestResolvedSubjectFactsFallback ×3、TestEventExtraction 追加 ×3)与 tests/test_delta_compression_cycle.pyTestResolvedSubjectFlowsThroughToStorage ×2),均按 AC 要求构造"模型给出 resolved_subject 且与既有 participants 不一致"这类只有新逻辑才走得到的防空转用例。
  • 既有三条失败处理(事件参与者部分填 / 画像主体解析失败整条丢弃 / Knowledge 不特殊处理)保持原样,写成回归断言未新建。
  • 变异测试 4/4:禁用 _resolve_subject、跳过 facts fallback、facts 仲裁反转(覆盖而非 fallback)、events 合并逻辑禁用——均被对应测试捕获。
  • 全量测试 1872 passed,ruff 通过,ty 与既有基线一致(delta.py 及两个改动测试文件零诊断)。
  • 两轴 review(Standards + Spec,sonnet,含对抗式 Verify):2 条 LOW 发现均 CONFIRMED 并已修复——instruction 文本换行处空格不一致;test_resolved_subject_merges_into_participant_ids 等 2 处断言用 set() 弱于姊妹用例的精确 list 相等(已用 mutation 实测证实 set() 断言确实无法捕获顺序回归)。

文档同步

docs 分支(本地提交,未推送):ADR-0016 追加更新节、design.md/CONTEXT.md 标注"仅 user_id 半落地"。

Closes #137 ## 做了什么 Delta 压缩那一次 LLM 调用顺带产出 `resolved_subject`(ADR-0016),本片只落地 **user_id 半**: - `_EXTRACT_MEMORIES_TOOL`:facts/events 候选各自新增独立的 `resolved_subject` 字段(`["string", "null"]`,二元产物,不是置信度);facts 侧 `scope_id` 类型同步放宽为 `["string", "null"]`,description 补上"仅结构化已知才填、代词推断的改填 resolved_subject"的对称引导。 - `_build_prompt` 的 instruction **改写**(不是追加):facts/events 两个子句都重写成"先试着通过上下文消解代词/模糊指代,锁定了就写进 resolved_subject;实在锁定不了才填 null,不要瞎猜"的两段式,消除了旧文案"不要瞎猜"与新增"请消解"之间的直接冲突。 - 解析层:新增共用辅助函数 `_resolve_subject`。facts 侧是 fallback-only 语义——`scope_id` 已合法时不读 `resolved_subject`(结构化判断优先,不做仲裁);`scope_id` 缺失时才尝试 `resolved_subject`,仍失败则维持既有整条候选丢弃。events 侧是并集语义——`participants` 的既有 null 过滤逻辑完全不动,`resolved_subject` 去重后追加进最终 `participant_ids`。 - `events.py::EventRecord` 的 docstring **未做任何文字改动**——它此前描述的"回指消解成功、或结构化已知的参与者"是一句指向不存在机制的话,本片落地后这句话本身就变成了准确描述。 ## 明确不在本片范围(留痕,非无声消失) - **event_id 半**:ADR-0016 原文写的是"具体 user_id/event_id 或 null",但 `_build_prompt` 目前没有喂"已知事件"上下文,`EventCandidate`/`EventRecord` 也没有关联既有事件的字段/合并语义——体量远大于 user_id 半,留给后续 ticket。已在 ADR-0016 更新节留两条具体面包屑。 - **relations**:结构上与 facts/events 同样有主体判定需求,但核实后确认 ADR-0016 全文从未提到它(`RelationshipEdgeCandidate` 早于本 ADR 存在,issue #36/ADR-0008),判定是背景节枚举写入目标时的遗漏、不是有意排除;且 relations 有两个主体位,直接照搬 facts 单字段 fallback 不成立,需要独立设计。已在 issue #137 评论里留痕。 ## 已知局限 events 侧 `resolved_subject` 是标量,一次候选只能消解一个模糊指代;同一事件里如果有多个不同的模糊指代分别指向不同的人,只有一个会被捕获,其余仍随 null 一起略过(fail-closed,漏报不误报)。已记入代码注释与 ADR-0016 更新节。 ## 验证 - 新增测试覆盖 `tests/test_delta_compressor.py`(`TestResolvedSubjectFactsFallback` ×3、`TestEventExtraction` 追加 ×3)与 `tests/test_delta_compression_cycle.py`(`TestResolvedSubjectFlowsThroughToStorage` ×2),均按 AC 要求构造"模型给出 resolved_subject 且与既有 participants 不一致"这类只有新逻辑才走得到的防空转用例。 - 既有三条失败处理(事件参与者部分填 / 画像主体解析失败整条丢弃 / Knowledge 不特殊处理)保持原样,写成回归断言未新建。 - 变异测试 4/4:禁用 `_resolve_subject`、跳过 facts fallback、facts 仲裁反转(覆盖而非 fallback)、events 合并逻辑禁用——均被对应测试捕获。 - 全量测试 1872 passed,ruff 通过,ty 与既有基线一致(delta.py 及两个改动测试文件零诊断)。 - 两轴 review(Standards + Spec,sonnet,含对抗式 Verify):2 条 LOW 发现均 CONFIRMED 并已修复——instruction 文本换行处空格不一致;`test_resolved_subject_merges_into_participant_ids` 等 2 处断言用 `set()` 弱于姊妹用例的精确 list 相等(已用 mutation 实测证实 set() 断言确实无法捕获顺序回归)。 ## 文档同步 `docs` 分支(本地提交,未推送):ADR-0016 追加更新节、`design.md`/`CONTEXT.md` 标注"仅 user_id 半落地"。
_EXTRACT_MEMORIES_TOOL的facts/events候选各加resolved_subject出口(二元
string|null,非置信度),_build_prompt instruction改写为"先试消解代词,
不确定才填null"两段式,facts侧scope_id放宽为可null并作为fallback目标,
events侧resolved_subject去重并入participant_ids(不替换既有null过滤逻辑)。

event_id半、relations均明确排除在本片范围外,留痕见ADR-0016更新节与issue
comment。
Yushu merged commit b10b963a23 into main 2026-08-17 05:26:28 +00:00
Yushu deleted branch feat/137-resolved-subject-anaphora 2026-08-17 05:26:29 +00:00
Sign in to join this conversation.
No description provided.