feat(ai): issue verified application preview decisions

This commit is contained in:
caoxiaozhu
2026-07-14 14:37:53 +08:00
parent a662cfe6c3
commit 5b24630710
32 changed files with 1976 additions and 217 deletions

View File

@@ -209,6 +209,7 @@
- `expense_cases`:统一费用事件和当前阶段。
- `expense_case_links`:连接申请、报销、票据、审批、付款、凭证和外部对象。
- `business_events`append-only 业务事件账本。
- `ai_application_preview_decisions`:申请落单前由服务端重新规范化并短期签发的预览建议;不创建空 Expense Case不保存建议明文动作成功后一次消费。
- `ai_decisions`:结构化 AI 建议与版本证据。
- `ai_decision_feedback`:接受、修改、拒绝和忽略。
- `workflow_outcomes`:退回、补件、审批、付款、审计和最终结果。
@@ -250,6 +251,8 @@
-`ReimbursementRequest` 进入只读兼容和迁移状态,停止新增第二套业务编排。
- 现有 `risk_flags_json` 保持读取兼容,新审批、付款、归档和关系事件写入结构化表。
- API 新字段优先追加,不在同一阶段破坏现有前端契约。
- 从未成功签发 `decision_id` 的旧申请预览,或签发接口失败且服务端没有当前会话有效决策时,保存/提交继续走现有 `client_observed` 路径;当前会话一旦存在有效服务端决策,省略 `decision_id` 必须拒绝,不能主动降级绕过核验。兼容路径不得在用户已经编辑后把最终值反向登记成“原始建议”。
- 新预览使用 30 分钟有效的服务端 `decision_id`。保存草稿或提交成功后一次消费;保存草稿会尽力签发基于已保存事实的新 `decision_id`,供后续提交继续核验,但续签属于业务提交后的派生能力,续签失败不能把已成功保存返回为失败。相同动作请求 ID、动作和最终指纹允许安全重放其他重放拒绝。
- 迁移桥接期集中维护 migration-owned 表集合;所有 legacy bootstrap 只能创建集合之外的表。标准启动在 `alembic upgrade head` 前只读核对 revision 与自有表集合发现漂移时拒绝启动不自动猜测、stamp 或修复。
- 所有表结构通过 Alembic 迁移,不继续在请求路径执行 DDL。
@@ -268,6 +271,8 @@
- 已初始化系统的 Bootstrap 状态只返回脱敏信息Bootstrap 写入、系统设置、模型连通性、缓存、审计与系统日志等敏感管理面由平台管理员权限保护Vite 本地 Setup 桥在初始化完成后锁定重新配置入口。
- P0 数据契约即引入最小 `tenant_id`、数据库约束、行级过滤、向量库命名空间和对象存储前缀隔离;删除传播、数据导出和私有部署加固可在商业化阶段继续完善。
- Expense Case 用户态查询只返回流程摘要Case 基础状态、关系类型、事件类型、操作人、发生时间及白名单业务载荷;不返回关联资源 ID、幂等键、correlation、causation、聚合标识或 Outbox 投递状态。有权查看入口单据的本人、当前审批人、财务和管理员可查看整 Case 的安全摘要,无权限与跨租户查询继续以 404 隐藏资源存在性。
- 申请预览签发与消费同时绑定 `tenant_id`、actor、Bearer 登录会话和会话 ID动作入口按这些服务端事实锁行校验不能只凭随机 UUID 授权。浏览器提供的模型来源、`finalValue`、用户和角色声明均不作为可信事实。
- 预览与学习账本中的字段值使用独立、可版本化的费用申请密钥计算 HMAC-SHA256 指纹,密钥目录和文件权限分别为 `0700``0600`;核验旧决策必须使用其签发版本,缺失版本直接拒绝,不静默生成替代密钥。字段指纹同时编码“字段是否存在”,避免新增或删除字段被误判为采纳;训练资格仍保持关闭,直至审批、付款或人工核验闭环完成。
- 自动化权限按动作、金额、场景、风险和有效期授予,不使用全局“允许 Agent 自动执行”开关。
- 收款账户变更、资金支付、制度发布、高风险驳回和敏感主数据变更执行双人或更高等级复核。
@@ -278,6 +283,7 @@
- Qdrant/few-shot 不可用:使用 stable Prompt 和基础规则,不阻塞主流程。
- 外部支付/ERP/税务连接器不可用:事件进入 retryable 状态,保留人工处理入口和幂等键。
- 记忆冲突或可信度不足:只展示建议,不自动应用。
- 预览签发超时或不可用:仍展示本地预览并允许编辑、保存和提交,内部标记为降级;此时只记录 `client_observed` 行为证据,不升级为服务端核验或训练样本。
- Savings 证据不足:保留机会状态,不进入已实现节省。
## 算法与公式
@@ -515,7 +521,10 @@ docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv \
- 2026-07-14克隆库联调将持久开发库以只读 `pg_dump` 流式恢复到一次性 tmpfs PostgreSQL 17原 40 张表、4 张费用单、105 名员工、248 条预算和 62 个 Agent 资产完整保留。升级到 `20260713_0002` 后首次 dry-run 识别 4 张旧单apply 创建 4 组 Case/Link/Event重复 apply 创建 0 条;隔离后端完成登录、`/auth/me`、旧单时间线 200 和登出,内部回填字段未出现在 API。临时数据库和隔离进程已清理持久库复查仍为原数据签名且没有 migration-owned 表。
- 2026-07-14安全复核与质量验证交叉审查后补齐超长租户幂等键稳定哈希、Link/Case 租户一致性、孤立 Case 冲突、URL 路由参数覆盖防护和部分批次失败进度摘要;前端将快照语义明确为“纳入时状态/节点”。容器内 65 项后端定向测试、11 项前端测试、Ruff 和 Vite 生产构建通过。全库代码体积门禁仍被本次未修改的 `RiskRuleGenerationService` 807 行存量问题阻断,未混入当前功能提交。
- 2026-07-14AI 行为闭环首个切片):新增 `AIDecision``AIDecisionFeedback``WorkflowOutcome` 三类独立事实,把申请预填建议、用户确认或显式字段纠正、草稿保存或申请提交结果通过稳定 `decision_id` 关联。只有已认证的申请预览快速入口显式传入服务端 `CurrentUserContext` 时才写账本;通用 User Agent、模板预览和单据详情编辑路径均不写入避免伪造身份和非 AI 操作污染样本。
- 2026-07-14字段纠正与隐私边界申请表编辑器在当前会话中维护首次建议值与最终显式编辑值多次修改保留最初建议改回原值会移除差异日期调整同时记录 `time` 与派生 `days`。服务端最终值始终从解析后的申请 facts 重建,不信任客户端 `finalValue`;持久化层只保存字段名和 SHA-256 值指纹不复制事由、地点、人员、金额等原始值原始对话、Prompt、模型全文、附件和支付敏感信息也不进入学习账本。
- 2026-07-14可信度边界浏览器编辑轨迹统一标记为 `verification_status=client_observed``trust_level=behavioral_analytics_only``training_eligible=false`,只能用于受限产品行为统计和后续人工核验,不能直接作为模型训练、审计结论、风险自动放行或企业记忆激活依据。后续需增加服务端预览登记并返回不可伪造的 `decision_id`,再实现拒绝/忽略、审批/退回/付款结果追踪、记忆候选与下次建议变化
- 2026-07-14字段纠正与隐私边界申请表编辑器在当前会话中维护首次建议值与最终显式编辑值多次修改保留最初建议改回原值会移除差异日期调整同时记录 `time` 与派生 `days`。服务端最终值始终从解析后的申请 facts 重建,不信任客户端 `finalValue`;持久化层只保存字段名和带密钥版本的 HMAC-SHA256 值指纹不复制事由、地点、人员、金额等原始值原始对话、Prompt、模型全文、附件和支付敏感信息也不进入学习账本。
- 2026-07-14可信度边界浏览器编辑轨迹标记为 `verification_status=client_observed``trust_level=behavioral_analytics_only``training_eligible=false`,只能用于受限产品统计;服务端签发预览消费后提升为 `server_verified`,仅证明“服务端签发快照与最终业务事实的差异已核验,不证明浏览器声称的模型来源,也仍不直接训练模型、自动放行风险或激活企业记忆
- 2026-07-14事务与一致性学习三表与申请、预算及 Business Event 使用同一 Session在最终 commit 前 `flush`,失败整体回滚;提交场景通过提交服务的事务内回调关联真实 `application_submitted` 事件。Case、Business Event 和 Decision 使用包含 `tenant_id + expense_case_id` 的复合外键,既拒绝跨租户,也拒绝同租户跨 Case 串联。前端估算使用消息级请求版本和输入指纹,只合并估算派生字段,旧响应不会覆盖后续编辑。
- 2026-07-140003 迁移与验证):新增 `20260714_0003_ai_learning_loop.py`三张学习账本表纳入集中迁移所有权AgentRun 与旧 ExpenseClaim 保持带索引软引用以兼容空库迁移顺序。一次性 tmpfs PostgreSQL 17 的升级、重复升级、跨租户/同租户跨 Case 外键拒绝、降级和再次升级 4 项通过;容器内学习账本、入口边界和迁移所有权定向 29 项,日期联动及异步估算关键场景 5 项通过。持久开发库只读复查仍为 `40|4|105|248|62`7 张 migration-owned 表数量为 0。
- 2026-07-14服务端预览决策新增认证的 `POST /api/v1/reimbursements/application-previews`。服务端从原始申请文本和当前登录身份重新解析字段、重跑规则测算,再签发 30 分钟有效的 `decision_id`浏览器返回的模型来源和建议值不被直接认证。预览绑定租户、actor、登录会话和 conversation字段只保存独立版本密钥生成的 HMAC 指纹;字段存在性也纳入指纹,新增、删除和改值都能由服务端识别。
- 2026-07-14消费与续签快速保存/提交显式携带动作类型、`decision_id` 和稳定请求 ID。服务端锁行校验后在 Claim、Expense Case、Business Event、AIDecision、Feedback、Outcome 的同一事务中一次消费;当前会话已有有效决策时禁止省略 ID 降级。保存草稿提交成功后再尽力签发基于服务端事实的新 `decision_id`,续签失败只返回无新 ID不反转已成功业务动作相同请求安全重放不会重复建单或重复写学习账本。
- 2026-07-140004 迁移与验证):新增 `20260714_0004_ai_application_preview_decisions.py`,预览决策表和正式 Decision 复合租户外键纳入集中迁移所有权。一次性隔离 PostgreSQL 17 完整 upgrade、重复 upgrade、约束/索引、downgrade 和再次 upgrade 4 项通过;容器内新增决策安全用例 9 项、旧快速保存/提交 4 项、申请学习账本 9 项、迁移与所有权 26 项通过且条件型 PostgreSQL 用例 1 项跳过3 组前端定向测试及 Vite 生产构建通过。持久开发库只读确认 8 张 migration-owned 表数量仍为 0。