unit.Registry.Execute 进入,
强制经过 来源证明 / 确认凭证 / 幂等 / 事务 / 审计 五道硬校验。本图以「教师请假」为示例走完全流程。
// 一个单元 = 一个原子业务事务 type Unit interface { Name() string Describe() Descriptor // 给 AI 的说明书 + 硬约束声明 Execute(ctx, req) (*ExecuteResult, error) } type TxAwareUnit interface { Unit ExecuteTx(ctx, tx pgx.Tx, req) (*ExecuteResult, error) // Atomic=true 的单元必须实现,否则注册即报错 }
Atomic=true 却未实现 ExecuteTx → Register() 直接报错;单元名重复同样拒绝。| 字段 | 作用(示例) |
|---|---|
InputSchema | 参数校验依据;关键 ID 字段内嵌 x-source 标记(来源证明) |
RequiresUserConfirm | 资金/权属承诺点=true(建报销单、入库、付款登记…) |
Idempotent | AI 超时重试安全:同键命中直接返回首次结果 |
Atomic | true = 单数据库事务(业务表 + 引擎表同事务提交) |
Domain | 业务域(请假/报销/…),编排候选集过滤依据 |
ExecuteRequest 携带:TenantID / ActorType(user·ai·system) / TraceID / IdempotencyKey / Args / PriorResults / ConfirmationToken —— PriorResults(前序单元真实输出)与 ConfirmationToken(用户确认凭证)就是第 3 / 3.5 步校验的对象。unitName 从 79 个已注册单元中查找。ErrUnitNotFound,编排层据此修正候选集InputSchema 校验必填字段与类型(string/number/integer/boolean/array)。ErrSchemaValidation("缺少必填参数: reason")→ 对话侧广播 form_request 补表单x-source: prior_unit_result 的字段(request_id / employee_id / …),取值必须精确等于 PriorResults 中对应单元输出的对应路径值。ErrSourceProof("request_id 未命中 create_leave_request 的真实输出,请先调用该单元")RequiresUserConfirm=true 的单元执行到此步:必须携带 ConfirmationToken;服务端按 租户+用户+单元+参数哈希 校验,成功后原子消费(issued→consumed)。ErrConfirmRequired → 挂起 needs_confirm 等确认卡;✗ 无效/过期/改参 → ErrConfirmRejectedtenant_id AND user_id)。Idempotent=true 的单元必须携带 IdempotencyKey;查 unit_audit_logs 唯一索引 (tenant_id, unit_name, idempotency_key),命中历史成功记录 → 直接返回首次结果快照,不再执行。ErrIdempotencyKeyMissunit.Tx(ctx) 取连接(不暴露连接池,结构上杜绝"忘记用事务连接")。unit_audit_logs(脱敏 args / 结果快照)+ 返回 ExecuteResult{Data, Event},Data 并入 PriorResults 供下一步引用(含 wf instance_id)。失败:回滚 + 落 error 审计,返回带明确原因的错误(供 AI 重新规划)。submit_leave_request 示例执行序列:
tx := db.Begin() txCtx := unit.WithTx(ctx, tx) // 事务注入 context u.ExecuteTx(txCtx, tx, req) { // 单元内只经 unit.Tx(ctx) ① CAS: 业务表 draft→pending (UPDATE ... WHERE id=$1 AND status='draft' AND tenant_id=$2 AND user_id=$3) ② Engine.StartTx 同一事务建 wf 实例 + 自动推进 ai_auto 节点 } writeAuditTx(同事务) // 审计与业务同生共死 tx.Commit() / 失败 → Rollback
lookup_org / query_receipt_stats 等只读或单写单元:
u.Execute(ctx, req) // 直接执行 writeAudit(...) // 成功后独立写审计 // 出错 → 写 error 审计后返回错误
{ "employee_id": "<当前登录用户ID·系统注入>",
"leave_type": "sick",
"start_at": "2026-09-09T08:30:00",
"end_at": "2026-09-10T17:30:00",
"reason": "感冒发烧,需要休息两天" }
employee_id(注入) / leave_type enum / 日期 format 全通过prior.lookup_employee.employee_id(精确命中)plan:v3:1 首次执行oa_teacher_leaves(status=draft){ "request_id": "<真实UUID>", "status": "draft" }
{ "request_id": "<= create_leave_request 输出 ✓>" }
ErrSourceProofplan:v3:2;7 ✓ 单事务内:业务 draft→pending(CAS 防并发)+ Engine.StartTx 启动审批实例,8 ✓ 审计{ "instance_id": "<wf实例ID>", "status": "pending" }
instance_id 后查 wf 待办:全部 ai_auto 已自动通过 → 继续下一步;存在 manual 校长待办 → 挂起 waiting_approval,审批通过事件到达后自动续跑(引擎层图 ⑥ 有完整走查)。| 攻击 / 误操作 | 拦截点 | 系统行为 |
|---|---|---|
AI 编造 request_id="12345" 提交请假 | 第 3 步 来源证明 | ErrSourceProof:值未命中 create_leave_request 的真实输出,疑似编造 ID → 编排层据错误重规划(先调 create) |
AI 直接调 create_expense_request(资金承诺点)无凭证 | 第 3.5 步 确认凭证 | ErrConfirmRequired → 对话挂起 needs_confirm,前端渲染确认卡,用户点击后 POST /oa/confirm/issue 签发一次性 15 分钟凭证再续跑 —— 绝不静默执行/跳过 |
| AI 从历史会话抄来一个 token 但改了金额 | 第 3.5 步 args 哈希 | 凭证绑定 CanonicalHash(args)(键排序规范化 JSON 的 SHA-256),改任一处 → ErrConfirmRejected |
| AI 调用超时 → 自动重试同一单元 | 第 6 步 幂等 | 同 IdempotencyKey 命中 unit_audit_logs 唯一索引 → 直接返回首次成功结果,不会重复建单 |
unit_audit_logs(trace_id / actor_type / unit_name / 脱敏 args / result),既是"来源证明拦截次数"等指标的取数源,也可回放 AI 的每一步决策与动作。