6.5 KiB
AI 合同平台 V2 Phase 3 display cache 完整上下文一致性收口
背景
本记录承接 Phase 3 UI 展示字段派生和旧 display cache 治理工作。
上一轮 smoke 中出现过一个容易误判的现象:
/api/v1/runtime/schema-check中display_cache_consistency.mismatch_count = 0。- 但合同详情页渲染期间,Contract API 日志仍出现
Contract display cache mismatch; derived value will be used。
排查后确认这不是单纯日志噪音,而是治理检查口径与真实读路径口径不一致。
根因
合同详情页、合同列表、workspace 和搜索读路径使用完整 ContractActionContext 派生展示字段。该上下文会纳入:
- 最新审查任务状态。
- 结构化风险项和条款修复状态。
- 审批实例和待办步骤。
- 履约任务。
action_hint。
旧的 display_cache_governance 只按 contracts.status 与旧缓存列做对比,没有加载上述上下文。因此会出现“schema-check 全绿,但详情页按完整上下文派生时仍 warning”的假一致。
本次修正
本次将 display cache 治理口径升级为完整上下文口径:
display_cache_governance.iter_display_cache_rows(...)通过 SQL 读取最新审查、结构化风险、审批、履约和action_hint。display_cache_mismatch_for_row(...)调用同一个derive_contract_display(...),并传入ContractActionContext。schema_check在缺少上下文表或contracts.action_hint时跳过 display cache assessment,避免旧库结构下误报连接异常。contract-apiDocker healthcheck 改为轻量/api/v1/health。重型/api/v1/runtime/schema-check继续作为 smoke/CI 质量门,而不再作为 5 秒容器健康探针。
这修正了前一份 V2-phase3-search-action-context-audit-2026-06-30.md 中“display_cache_governance 保持轻量、不加载 workflow context”的阶段性判断。新的 smoke 证据表明:cleanup prestate 要判断旧缓存列是否真的安全,必须使用与用户可见读路径一致的完整上下文。
Smoke 验证
远端环境:
- Host:
192.168.3.90 - Contract API:
http://192.168.3.90:18100 - Contract Web:
http://192.168.3.90:13000 - Database:
yuqei_contract - Alembic actual/head:
20260701_0024
修正治理口径后,首次复跑:
{
"display_cache_consistency": "warning",
"checked_rows": 12017,
"mismatch_count": 12006,
"field_counts": {
"next_action": 12006,
"next_actions": 12006
}
}
抽样合同:
contract-0ba3bd5b04b6- 修复前旧缓存:
根据审查结果修改或提交审批 - 完整上下文派生:
处理审查风险并应用推荐条款
这说明旧缓存主要不是状态标签问题,而是 next_action/next_actions 尚未按最新审查风险和条款推荐上下文刷新。
Backfill
已在远端 smoke 执行可回滚 backfill:
{
"command": "backfill",
"mode": "apply",
"updated_rows": 12006,
"snapshot": "/data/contract-files/display-cache-context-snapshot-20260701.json"
}
回滚 snapshot:
- 容器路径:
/data/contract-files/display-cache-context-snapshot-20260701.json - 宿主卷路径:
/var/lib/docker/volumes/yuqei-contract-v2-smoke_contract-files/_data/display-cache-context-snapshot-20260701.json - 文件大小:约
5.0M
如需回滚 smoke 数据:
cd /opt/apps/ai-contract-platform-v2-smoke/yuqei-infra/compose
docker compose --env-file .env.smoke -p yuqei-contract-v2-smoke run --rm contract-api \
python -m yuqei_contract_api.display_cache_governance_cli rollback \
--apply \
--snapshot /data/contract-files/display-cache-context-snapshot-20260701.json
Backfill 后结果
复跑 /api/v1/runtime/schema-check:
{
"status": "ok",
"display_cache_consistency": {
"status": "ok",
"checked_rows": 12017,
"mismatch_count": 0
},
"display_cache_cleanup_prestate": {
"status": "ok",
"legacy_columns_retained": true,
"display_cache_consistent": true
}
}
复跑治理 CLI:
{
"command": "assess",
"checked_rows": 12017,
"mismatch_count": 0,
"field_counts": {},
"samples": []
}
服务状态:
contract-api:healthycontract-review-worker:healthycontract-web:healthy
页面抽样:
/contracts/contract-0ba3bd5b04b6返回200。- 页面仍展示“可应用条款”推荐卡片。
- 本次请求后
Contract display cache mismatch日志计数为0。
本地验证
python -m py_compile `
yuqei-contract-platform\services\contract-api\src\yuqei_contract_api\display_cache_governance.py `
yuqei-contract-platform\services\contract-api\src\yuqei_contract_api\schema_check.py
python -m pytest yuqei-contract-platform\services\contract-api\tests\test_display_cache_governance.py -q
python -m pytest yuqei-contract-platform\services\contract-api\tests\test_health.py -q
结果:
test_display_cache_governance.py:5 passedtest_health.py:10 passed
新增回归覆盖:
test_assess_display_cache_consistency_uses_review_action_context
该测试构造“合同状态默认缓存看似一致,但最新审查有未修复风险”的场景,确认治理检查会按完整上下文发现 next_action/next_actions mismatch。
当前结论
当前 smoke 已重新固化为:
- 旧 display cache 列仍保留。
- 旧列值已按完整业务上下文与 domain/serializer 派生结果一致。
display_cache_cleanup_prestate可作为后续停写/删列前的保护门。- 不建议立即删列。
下一步建议
下一片仍应走保守路径:
- 先继续停写旧
contracts.next_action / contracts.next_actions的生产写路径。 - 保留 API 响应字段由
derive_contract_display(...)输出。 - 重建搜索索引或增量同步时继续使用完整
ContractActionContext。 - 连续 smoke 多轮保持
display_cache_consistency=ok后,再评审是否进入 cleanup migration。
不要把 status_label/status_tone 与 next_action/next_actions 一次性删掉。前者是纯展示缓存,后者仍承担动作上下文兼容面,必须分片处理。
后续停写进展
2026-07-01 后续小片已开始停写旧 contracts.next_action / contracts.next_actions 生产写路径。
新的口径是:
status_label/status_tone继续作为兼容缓存保持一致。next_action/next_actions旧列仍保留,但不再要求跟随完整上下文派生值写回。- API 响应和搜索展示继续由完整
ContractActionContext派生。
详细记录见:
project-docs/V2-phase3-stop-legacy-next-action-writes-2026-07-01.md