Commit 783b9dbfe99dd33fdbe1ec186857a99311e51a5e
1 parent
203353cf
docs: §21 rearch3 current state (write-path recentralization, MySQL event ledger…
… + token-budget projection, skills-in-DB, security audit fixes)
Showing
1 changed file
with
78 additions
and
2 deletions
docs/agent-architecture.md
| ... | ... | @@ -3,8 +3,8 @@ |
| 3 | 3 | > 状态:设计已收敛(2026-07-21)。全 Java / LangChain4j,单 ReAct agent + 通用工具 + 人在环写入。 |
| 4 | 4 | > 关联:知识图谱底座见 `sql/README.md`;现状代码接线见 memory `xlyai-rag-milvus-wiring`。 |
| 5 | 5 | > |
| 6 | -> **阅读指引**:§1–§14 是**设计**(决策与取舍,基本稳定);§15/§16 是**历史实现快照**(按日期,不再更新); | |
| 7 | -> **当前实现状态看 §17,未完成清单看 §18**。分支 `kg-edge-flow`(尚未合并 master)。 | |
| 6 | +> **阅读指引**:§1–§14 是**设计**(决策与取舍,基本稳定);§15–§20 是**历史实现快照**(按日期,不再更新); | |
| 7 | +> **当前实现状态看 §21(rearch3:写路径收权 + MySQL 记忆底座 + 技能入库)**。分支 `agent-main`。 | |
| 8 | 8 | |
| 9 | 9 | ## 1. 背景与目标 |
| 10 | 10 | |
| ... | ... | @@ -466,3 +466,79 @@ ai_op_queue( |
| 466 | 466 | - 单测 29 个:投影正确性(当前轮保真/旧轮压缩/预算收拢/摘要区/internal 隐显/流程卡钉摘)、 |
| 467 | 467 | 并发 append 不丢事件、尺寸正则、skill 加载、以及 §19.5 的安全不变量回归。 |
| 468 | 468 | - 验收数字见 `bench/bench_p3_acceptance.out`。 |
| 469 | + | |
| 470 | +## 21. 当前实现状态(2026-07-29,rearch3——写路径收权 + 记忆持久化 + 技能入库) | |
| 471 | + | |
| 472 | +> **本节是现状**;§20 的编排(skill-ReAct)保留,其记忆存储、写路径、技能存储被本节取代。 | |
| 473 | +> 施工依据:memory `xlyai-suggestions-backlog`(#1-#7 + 施工顺序 A∥B→C→D)。 | |
| 474 | + | |
| 475 | +### 21.1 写路径:模型全只读,AI 侧唯一的写 = ai_op_queue | |
| 476 | + | |
| 477 | +- **模型可用的 7 个工具全部只读/只渲染**:useSkill / findForms / readFormData / lookupRecord / | |
| 478 | + collectForm(弹新增表单)/ **previewChange**(写操作预览卡)/ askUser。`proposeWrite` 退役。 | |
| 479 | +- **previewChange(action, entity, record, fieldChinese?, newValue?)**(action:update/invalid/ | |
| 480 | + cancelInvalid/examine/cancelExamine/delete;create 走 collectForm):定位记录 → 校验 | |
| 481 | + (字段字典/FK 名称→id/类型强转/系统列拒改/**状态合法性**)→ 渲染预览卡(update=整表当前值+ | |
| 482 | + 改动高亮、可在卡上继续修正;状态类=单据摘要+状态变化高亮,只读)→ 解析产物以 **previewId** | |
| 483 | + 暂存 Redis(30 分钟 TTL,服务端绑定 userId/convId,客户端只见 id)。按钮文案 = ERP 同名 | |
| 484 | + (保存/审核/反审核/作废/取消作废/删除)。 | |
| 485 | +- **确定性保存端点**(不经 LLM):`POST /api/agent/preview/{id}/save`(fields=技术名→值, | |
| 486 | + 空值=放弃该字段的改动)与 `POST /api/agent/form/submit`(create)。流程:归属校验 → 权限重查 → | |
| 487 | + **重读记录重校验**(值/状态被他人改过 → 拒绝,"所见即所写")→ **原子 claim previewId**(防并发/ | |
| 488 | + 重复提交)→ 写 `ai_op_queue` 一行 `sStatus='confirmed'`(多字段=多行、单事务)→ 落 `queued` 事件。 | |
| 489 | + **xlyAi 到此为止**:执行/审计/权属全部移交 ERP 侧(执行器读本表;现状=报价自动执行、其余进待办)。 | |
| 490 | +- 退役:OpController(confirm/cancel/pending)、AuditService、ErpClient 全部写方法 + execStaging。 | |
| 491 | + ErpClient 只剩 whoami / readForm / getUuid / checkBusinessData(dry-run 接口位)。 | |
| 492 | +- **状态合法性硬检查**(预览+保存两次,防 TOCTOU;依据已核实的存储过程行为):审核要求未审核、 | |
| 493 | + 销审要求已审核、作废要求未作废、复原要求已作废;表无对应状态列则跳过。已审核单物理删除仅警告。 | |
| 494 | +- **ai_op_queue 列名兼容**:新增 `sMakePerson`(ERP 惯例)与 `sUserId` 双写(ERP 执行器仍读 | |
| 495 | + sUserId,切换后可删);迁移见 `sql/migrate_rearch3.sql`。 | |
| 496 | +- 护栏:反编造重试与 `maxSequentialToolsInvocations(8)` 原样保留;WRITE_CLAIM 正则补 | |
| 497 | + 保存/录入并用负向前瞻排除「已审核状态」类状态陈述;"出过卡"标志改为 change_preview/form_collect。 | |
| 498 | + | |
| 499 | +### 21.2 记忆:MySQL `ai_chat_event` 权威账本 + token 记账投影 | |
| 500 | + | |
| 501 | +- 账本 Redis→**MySQL `ai_chat_event`**(每事件一行、append-only 永不删;iId PK、iSeq 会话内序号 | |
| 502 | + UNIQUE 防撞、iTurn 轮次——两者纯服务端溯源列,不进投影/API;sMakePerson/sBrandsId/sSubsidiaryId | |
| 503 | + 按 ERP 惯例)。Redis `chat:ledger:{conv}` 降为**热缓存**(30 天 TTL);缓存失效冷读回源 | |
| 504 | + (有界窗口)并回填;**缓存键已失效时新事件不单独重建残缺列表**。删除会话 = 清缓存 + 落 | |
| 505 | + `deleted` 标记(冷读止于标记,历史永久可溯源但不复活)。 | |
| 506 | +- `tool_call` 事件存**中立格式** `calls=[{id,name,args}]`(脱 LangChain4j 序列化耦合),旧载荷兼容读。 | |
| 507 | +- 投影层 **token 记账**取代 6000 字符:本地保守估算(CJK≈1字/t、其余 3 字符/t,故意高估); | |
| 508 | + 预算基数 = `llm.context-length`(镜像 Ollama OLLAMA_CONTEXT_LENGTH=16384,OpenAI 兼容通道 | |
| 509 | + 无法逐请求传 num_ctx)− 输出预留 − 工具 schema 定额;四段让位顺序 ⑤→④、①②与当前轮永不让位; | |
| 510 | + **轮内让位**(当前轮超限:最早工具结果降 120 字摘要、保最近 2 个全文、单条 2400t 硬顶、配对不破坏); | |
| 511 | + **发送前自检**(总量超预算再收缩,绝不让 Ollama 静默截头);④ 摘要区按 token 预算装载; | |
| 512 | + LLM 窗口截断的孤儿 tool_result 丢弃。校准回路:`prompt_eval_count` vs 估算+定额,超出即告警 | |
| 513 | + (TracingChatModelListener)。 | |
| 514 | +- 流程卡新语义:previewChange 预览行("尚未写入");`queued` 后技能线摘下,待办行**只读**展示 | |
| 515 | + `ai_op_queue.sStatus` 进度(confirmed/executing/executed/failed),直到下一个流程事件替换。 | |
| 516 | + | |
| 517 | +### 21.3 技能:ai_skill 表存在开关 | |
| 518 | + | |
| 519 | +- 新表 `ai_skill`(sId/sName UNIQUE/sBrief/sBody/bEnabled/sUpdatePerson/tCreateDate/tUpdateDate; | |
| 520 | + 旧草稿表 DROP 重建)。**切换开关 = 表是否存在**:存在→只读 DB(空表=无技能场景,绝不回读包); | |
| 521 | + 不存在→war 包 `skills/*.md`;DB 故障→降级无技能+告警(绝不静默换回出厂版)。零技能优雅降级 | |
| 522 | + (索引空渲染、useSkill 答无可用、裸工具正常聊)。 | |
| 523 | +- 开发工作流文件为主:改 `skills/*.md` → `uv run --no-project sql/gen_ai_skill_sql.py > | |
| 524 | + sql/ai_skill_seed.sql`(幂等 ON DUPLICATE KEY,按 sName 重置为文件版、bEnabled 保留, | |
| 525 | + 超 600t 拒绝)→ 注入 DB。技能索引本就每次动态拼接,无需新机制。 | |
| 526 | + | |
| 527 | +### 21.4 安全(红线全保留 + 本轮审计修复) | |
| 528 | + | |
| 529 | +- 保留:whoami 内省 fail-closed / 权限白名单进每请求工具 / token 不进 prompt·账本·预览草稿 / | |
| 530 | + {userId}: 会话隔离 / safeId URL 防注入 / canRelogin 不静默提权 / FK 解析强制租户谓词。 | |
| 531 | +- 新端点复刻同套检查:save 前归属+权限+重校验三层;previewId 122 位随机+格式校验+一次性。 | |
| 532 | +- 本轮审计修复:readFormData 鉴权改用**表单目录反查的权威 moduleId**(堵"无权 formId+有权 | |
| 533 | + moduleId"混搭);`/form/options` 增加"FK 表须被用户有权表单引用"检查(堵无表单权限翻名录)。 | |
| 534 | +- 已知残余(交付说明):ai_skill 表内容=system prompt 注入面(需收紧写权限);dry-run fail-open | |
| 535 | + (依赖 ERP 执行时真校验);ai_chat_event 永久保留需定保留策略;?token= URL 通道与 CORS 全开 | |
| 536 | + 为本地/壳层现状。 | |
| 537 | + | |
| 538 | +### 21.5 待 ERP 侧(D 阶段对接) | |
| 539 | + | |
| 540 | +- `/business/checkBusinessData` dry-run 端点在 ERP 仓库**未提交**(他人负责);xlyAi 已留 | |
| 541 | + `erp.dry-run.enabled` 开关 + `ErpClient.checkBusinessData`,完工后开启即接入(update 载荷 | |
| 542 | + 与入队同口径;__tables__ 多行分组形状需与 ERP 对一次)。 | |
| 543 | +- doExamine/updatebInvalid 的 ERP 侧校验补做中;xlyAi 预览暂以直读 bCheck/bInvalid 实现硬检查。 | |
| 544 | +- ai_op_queue `sUserId` 列:ERP 执行器切到 sMakePerson 后删除。 | ... | ... |