forked from ChaiHao/BPOS-MVP
人手改的那一手真正落得下去;界面不撒谎那几处补齐
手改是**兜 AI 的错**的那一手 —— 这一轮把它从「摆在那儿但落不了地」做成真的: 件上就地改 · 保存**按地址收**,不看当下那张卡:载荷从前借道工单的 `blocks[i]` 取列名, 于是熔断卡(没有 blocks)在屏上时,人补的那一格一个字都送不出去 —— 而他要补的恰恰是把这一单卡住的那个必填格。授权面改由活动清单的台账声明把关。 · **能增删行**:错不止「这一格填错了」,还有「这一行不该在」与「漏了一行」。 删挂记号不当场抹掉(看得见、可后悔),且**删要说出来**(`drops`)—— 「没回传就是删了」会把一次不完整的回传变成一次删表。 · **必填没填按不下提交**:与放行门 L1 同一个数同一套算法;闸上摆出差在哪一行哪一格。 · 只写真改了的那几行 —— 改一格却留「修订 4 行」的痕,那句话是假的。 判据与门 · 裁量判据带谓词时**谓词说了算**:「选填缺口已补齐」基座数得出来, 人填好的目标价它照样判「客户未提供」的毛病由此消掉。 · 验收卡判据**读时现算** —— 人在成果件上补一格,判据跟着变。 · 放行门认「**办完了没有**」不认「有人写过」:从前写个 `false` 就把门推开, 于是一单没打标就进了放行门。三个数全由基座现数。 · 拒回把「活没干完」说成「数或行对不上」的毛病:判据点名的表一行都没写时直说那一步没办。 **指向两扇打不开的门,比不指还糟。** 界面不撒谎 · 日志只有三色:负责人绿 · 智能体蓝 · 系统灰(归侧五种是度量的分法,不是给人看的)。 · 验收收口答两件事:落了什么(按**交付物**点名,一份成果的几张表并成一行)、 往哪儿去(转出哪一口、带过去哪些字段);一个口都没发就直说到此为止。 · 签完到收尾之间那一段有自己的话「字已签下,正在落地」—— 中间态本来就该说出来,只是要说对。 · 消息不再被内部事件刷屏:只有从通道进来的件才谈得上「无人认领」。 · 建客户公司与联系人**一次确认落两行**;联系人编码自增不问人;卡面摆表的人读名。 A1 三案例端到端全 released:全 A 类直通 12 拍 / 客户点名跳一条 10 拍 / 新客户缺必填 14 拍。 五道门禁全绿(971 passed)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JsTDjoHovERXyy6Wmw1ALp
This commit is contained in:
+539
-356
@@ -1,449 +1,632 @@
|
||||
# 活动开发规范
|
||||
|
||||
> 2026-07-30 定稿。给 **AI 开发活动时查阅**的规范:开发一个活动要写什么、能用什么、
|
||||
> 什么会被拒、跑起来是什么样。基于代码现状如实记述 —— 每条「已就位」的断言都带
|
||||
> `file:line`;产品要求但现状还不满足的,标 **⚠️待实现** 并汇总在[第 12 章](#12-待实现清单)。
|
||||
> 2026-07-30 第二版(第一版是「现状如实、差距列账」的勘误体;本版按跑通后的现状重写成
|
||||
> **开发手册**)。目标读者:**开发活动的 AI 或人** —— 读完这一份,你应当能独立把
|
||||
> `docs/MVP示例工作流.html` 里的一个 SOP 节点做成一个能 `released` 的案例活动,
|
||||
> 不必读 `apps/api` 的源码。
|
||||
>
|
||||
> 相关文档:`docs/编码规范.md` §8(活动包平台契约)、各模块 `CONTRACT.md`(模块级不变量)、
|
||||
> `docs/产品设计.md`(三面一体)。本文与它们冲突时,**以代码现状为准**,并把冲突记进第 12 章。
|
||||
> 范本:`activities/quote-intake`(A1 客户RFQ受理,2026-07-30 已两条路径 `released`、
|
||||
> 常路 7-8 拍收口)。**本文与代码冲突时以代码为准**,发现即改本文。
|
||||
> 相关:`docs/编码规范.md` §8(平台契约)、各模块 `CONTRACT.md`(模块级不变量)、
|
||||
> `docs/debt.md`(欠账)。历史语义源(操作系统 / 活动模型两卷)已归档 `docs/archive/`。
|
||||
|
||||
---
|
||||
|
||||
## 1. 读者与用法
|
||||
## 1. 世界观:一个活动是怎么跑起来的
|
||||
|
||||
- **读者**:开发活动(`activities/<id>/`)的 AI 或人。你不需要读 apps/api 的源码 ——
|
||||
活动的全部世界是 SDK 基座六件(第 3 章)与清单六区(第 4 章)。
|
||||
- **用法**:开发前通读 2–4 章(边界与结构);写案例模型看第 8 章;写交互看第 9 章;
|
||||
提交前对着第 11 章自测清单过一遍。
|
||||
- **标注约定**:✅ = 现状已就位(带 file:line);⚠️ = 产品已定、代码未实现(汇总第 12 章)。
|
||||
**活动 = 任务的定义。** 编排引用一个活动,运行时长出一个任务(恰一名负责人)。
|
||||
活动在系统外开发、经安装通道入库(`pnpm activities:install`),版本恒新增、
|
||||
同版重装 no-op、在途任务按启动时版本跑完。
|
||||
|
||||
## 2. 总纲与铁律
|
||||
**活动没有代码逻辑。** 2026-07-29 范式退役后,活动一律**案例活动**:行为全部写在
|
||||
清单里(案例模型 CMMN + 说明书 + 判定表 DMN + 技能 + 台账声明 + 报告模版),
|
||||
「系统不执行代码即知包为何物」。`src/index.ts` 只是被静态审扫描的产物文本
|
||||
(证明零环境能力),`defineActivity({ name })` 即可,`run` 可选且通常不写。
|
||||
|
||||
**活动 = 任务的定义。** 编排引用一个活动,运行时就长出一个任务(恰一名负责人)。
|
||||
活动在系统外开发、经安装通道入库(`registry.install`),版本恒新增、同版重装 no-op、
|
||||
在途任务按启动时版本跑完(`apps/api/src/modules/activity-registry/CONTRACT.md`)。
|
||||
**推进由 Loop 驱动,一拍的解剖**(`loop-engine/internal/usecase/turn-runner.ts`):
|
||||
|
||||
**统一建模**(语义权威:`JCAI_PRD/workbench/src/app/(proto)/_data/model.ts` 头注,2026-07-25):
|
||||
```
|
||||
语义引擎发布局面 ──> Agent 决断一串行动 ──> 引擎逐个执行 ──> 留痕 ──> 下一拍
|
||||
· 当下可为(Enabled) (1-6 个,一次模型调用) 被拒停在谁那儿
|
||||
· 进行中(Active) tool 后截断
|
||||
· 目标缺口(未完成项) 挂起型收拍
|
||||
· 最近轨迹 + 工具清单 + 台账字典 + 任务数据
|
||||
```
|
||||
|
||||
> 一个活动**完全由 AI Loop 自主完成**;Loop 按**阶段**推进(**阶段可重复**),阶段产出
|
||||
> **过程成果**(Deliverable:草稿 → 待验收 → 已放行 / 已驳回)。一个任务可能有一或多个
|
||||
> 过程成果,**每个成果都要调人类工具放行**。
|
||||
**拍是按模型调用计费的** —— 每拍把说明书 + 轨迹 + 局面整段重读一遍。
|
||||
所以一切设计都围着「少一拍是一拍」:一拍连做(写两张台账顺手 finish)、
|
||||
批量工具入参(一单所有 PN 一次传)、说明书把规则写死(判断不花拍)。
|
||||
A1 的账:六步的活,常路 **7-8 拍**收口(起初 21 拍 —— 差距全在设计,不在模型)。
|
||||
|
||||
活动不再分范式(2026-07-29 用户定):业务模型一律**案例模型**(CMMN + 说明书 + DMN),
|
||||
业务分类是活动库唯一的一维(`packages/contracts/src/manifest.ts:20-23`)。
|
||||
## 2. 四条铁律(违者拒装或被引擎拒)
|
||||
|
||||
### 四条铁律
|
||||
|
||||
| # | 铁律 | 机制依据 |
|
||||
| # | 铁律 | 机制承载 |
|
||||
|---|---|---|
|
||||
| ① | **只读世界**:活动只能读台账、调读类工具、问模型、加工表单 | 基座六件即全部能力(§3);环境能力静态审拒装(§11) |
|
||||
| ② | **写只有草稿与意向**:写台账 = 草稿层(不产生数据事件);外部动作(发邮件等)= 登记动作意向(入参冻结 + 幂等键),**登记那一刻世界上什么都没变** | `ledger.write` 只有草稿写(`base.ts:55-60`);写类工具自动转意向(`apps/api/src/app/tool-bus.ts:206-209`) |
|
||||
| ③ | **终局唯放行**:任务完成的唯一出口是**人类验收通过** —— 那一刻才草稿合并、意向执行、输出口发射;验收不过 = 不完成 | `raiseAcceptance` 是唯一提交口(`turn-runner.ts:207-242`);`land()` 六步(§7.3) |
|
||||
| ④ | **活动不自转**:`run(base)` 是**一个回合**的推进,由引擎驱动;不自设步数、不产生终局迁移;预算与时限由 Harness 在环外强制 | `define.ts:22-25`;迁移拓扑表把 Completed/Terminated 设为死胡同(`loop-engine/internal/domain/lifecycle.ts:29-38`) |
|
||||
| ① | **只读世界**:活动只能读台账、调读类工具、问模型、发卡问人 | 产物文本静态审:`process.env` / `fetch` / `node:*` / `eval` 命中即拒装;depcruise 双向隔离 |
|
||||
| ② | **写只有草稿与意向**:写台账 = 草稿层(不抛数据事件);外部动作(发邮件等)= 登记动作意向(入参冻结 + 幂等键)。**登记那一刻世界上什么都没变** | `write` 行动只落草稿;写类工具调用自动转意向 |
|
||||
| ③ | **终局唯放行**:任务完成的唯一出口是人类验收通过 —— 那一刻才草稿合并、意向执行、输出口发射 | `submit` = 发验收卡挂起;放行落地六步(§11.4) |
|
||||
| ④ | **活动不自转**:推进由引擎逐拍驱动;预算(步数/token/时长/调人次数)与停滞熔断由 Harness 环外强制,活动不得自查自守 | 迁移拓扑把 Completed/Terminated 设为死胡同;AI 不得产生终局迁移 |
|
||||
|
||||
## 3. 能力边界:基座六件就是全部世界
|
||||
## 3. 开发流程(从 SOP 节点到 released)
|
||||
|
||||
`packages/activity-sdk/src/base.ts`(权威,100 行,逐行有注释)。
|
||||
「基座即笼子:**内容归活动,笼子归系统**。活动代码无环境能力,一切能力经调用契约注入,
|
||||
越权在结构上不可能。」
|
||||
1. **读业务**:`docs/MVP示例工作流.html` 找到你的节点 —— 任务定义卡(执行人/触发/目标)、
|
||||
作业指导书(S1…Sn + 判断菱形)、判定表、留痕 · SLA。
|
||||
2. **列台账**:这个节点的成品是哪几张表?每张的主键与列(§6.3)。留痕清单就是答案。
|
||||
3. **列判定表**:图上的菱形与「规则 · 约束」区的表,一张不落进 DMN(§8)。
|
||||
4. **建案例模型**:**图上有几步就是几项**(§7),案卷项 id = 任务视图键名。
|
||||
5. **写说明书**:每步一段,写死规则、写明落点计划项与要写的台账列(§7.2)。
|
||||
6. **声明工具**:需要什么就声明什么名字;系统没有的运行时会转人(§9)。
|
||||
7. **配报告模版**:一个局面一张(§10.3)。
|
||||
8. **写清单其余区**:契约七件对齐台账列(§6.2 的三条对齐线),样例 ≥1 条。
|
||||
9. **自测**:`pnpm vitest run activities/<id>`(四段,§12.1)。
|
||||
10. **构建安装**:升 `packageVersion` → `pnpm activities:install`(同版重装 no-op!)。
|
||||
11. **试跑**:登记触发起单(试跑一律用 AI 账号)→ 看任务轨迹逐拍对账 →
|
||||
验收签字 → 查落地行与出口载荷。卡壳对照 §15 排障表。
|
||||
12. **调优**:轨迹里每一条「行动被拒」都是设计欠账 —— 修模型或说明书,不怪模型。
|
||||
|
||||
| 件 | 句柄 | API | 要点 |
|
||||
|---|---|---|---|
|
||||
| ① 契约 | `base.contract` | `activityId / version / data / message / outputPorts{mandatory,selective} / prohibitions / discretion` | **只读**,启动前冻结;`message` 是委托消息,不得携带扩大授权的效力 |
|
||||
| ② 交互 | `base.interaction` | `pushView(blocks)` / `pushHtml(html)` / `say(text, tag?)` / `issueWorkOrder(order)` / `issueCard(card)` | 视图模版过净化门(含脚本即拒);工单两模式、对话卡三族(§9) |
|
||||
| ③ 记忆 | `base.memory` | `window(size)` / `recall({layers?, limit?})` | 任务记忆窗口是活动侧唯一记忆接口;活动记忆渐进披露,语义引擎不读 |
|
||||
| ④ 台账 | `base.ledger` | `read(address, where?)` / `write(address, key, patch)` | 读 = 任务视图(正式版 + 本任务草稿层,读己之写);写 = **只有草稿写**,不产生数据事件 |
|
||||
| ⑤ 工具 | `base.tools` | `invoke(tool, input)` / `intend(tool, input, label)` / `listen({eventType, keyFilter, deadline})` | AI 对世界只有**触达**(动作)与**等待**(监听)两种姿势;**监听时限必配** |
|
||||
| ⑥ 模型 | `base.llm` | `generate({model, prompt, system?, outputSchema?})` | `model` 是**自定义模型名**(逻辑名),不触厂商与密钥;给 outputSchema 即要求结构化出参并校验 |
|
||||
|
||||
**outcome 三态**(`define.ts:10-16`):`{kind:'submit', deliverable, rationale}`(提交放行)/
|
||||
`{kind:'await', reason}`(等人或等事件)/ `{kind:'blocked', reason}`(推不动,如实报)。
|
||||
|
||||
**禁区**(安装静态审黑名单,`activity-registry/internal/domain/static-audit.ts:144-159`):
|
||||
`process.env`、`require(`、`node:fs`、`node:net`、`node:child_process`、`globalThis.fetch`、
|
||||
`fetch(`、`XMLHttpRequest`、`eval(`、`new Function(` —— 产物文本扫描命中即拒装
|
||||
(`environment-capability` / `dynamic-eval`)。另有 depcruise 双向隔离(activities 不得
|
||||
import apps,反向也不得)与 eslint 禁 `fetch`/`process` 全局。
|
||||
|
||||
**SDK 三道守卫**(本地 mock 宿主即可测出,`packages/activity-sdk/src/host.ts`):
|
||||
|
||||
1. 视图模版**无脚本**(`host.ts:64`):`<script` 即抛 —— 容器无脚本执行权;
|
||||
2. **裸调用拒发**(`host.ts:71`):`issueWorkOrder` 的 `basis` 为空即抛 —— 调用必附上下文;
|
||||
3. **监听时限必配**(`host.ts:116`):`deadline` 空串即抛 —— 无死等。
|
||||
|
||||
> 如实说明:SDK 面是**本地开发面**(线上 `.js` 产物只当数据存,不 import 不 eval,
|
||||
> ADR-004)。线上真正的闸是工具总线 + `ToolInvoker` 白名单 + `card-rules` 机检 ——
|
||||
> 与 SDK 守卫同一套规矩,两头都得过。
|
||||
|
||||
## 4. 包结构与清单六区
|
||||
|
||||
### 4.1 目录树(照 `activities/quote-intake`,六包结构一致)
|
||||
## 4. 包结构与构建安装
|
||||
|
||||
```
|
||||
activities/<activity-id>/
|
||||
├── package.json # @activities/<id>,private,type:module,exports "." → ./src/index.ts
|
||||
├── package.json # @activities/<id>,private,type:module
|
||||
├── src/
|
||||
│ ├── index.ts # 代码入口:export default defineActivity({...})
|
||||
│ ├── manifest.ts # 清单六区 + 导出 DMN 常量(供测试直接求值)
|
||||
│ ├── case-model.ts # 导出 CMMN_XML 与 NARRATIVE(双表示,同版本发布)
|
||||
│ ├── skills.ts # 导出 Skill[](id 用 skillIdSchema.parse 品牌化)
|
||||
│ └── views/report.ts # HTML 视图模版常量(viewShell/kvCard/tableCard/noteCard 拼装)
|
||||
├── __tests__/activity.test.ts # 自测四段(§11.2)
|
||||
└── dist/{manifest.json,index.js} # 构建产物(⚠️ 现为过期占位,债 M5-3)
|
||||
│ ├── index.ts # export default defineActivity({ name });行为不在这
|
||||
│ ├── manifest.ts # 清单六区 + 导出 DMN 常量(测试直接求值)
|
||||
│ ├── case-model.ts # 导出 CMMN_XML 与 NARRATIVE(双表示同版本)
|
||||
│ ├── skills.ts # 导出 Skill[](id 用 skillIdSchema.parse)
|
||||
│ └── views/report.ts # 报告模版(viewShell/kvCard/tableCard/... 拼装)
|
||||
├── __tests__/activity.test.ts # 自测四段;只准 import @sdk / @contracts / @sfeel
|
||||
└── dist/{manifest.json,index.js} # pnpm activities:build 产出(index.js 现为源码文本,债 M5-3)
|
||||
```
|
||||
|
||||
**入口写法**(`activities/quote-intake/src/index.ts`):只 import `@sdk`;台账地址提成
|
||||
顶部常量;`export default defineActivity({ name, async run(base) {...} })`;典型 run 体 =
|
||||
`ledger.read` 取数 → `pushView` 推块 → 分支 `say` + `return {kind:'await'}` 或
|
||||
`return {kind:'submit', deliverable, rationale}`;本地纯函数放文件末尾。
|
||||
⚠️ `defineActivity` 目前仍要求 `paradigm` 参数 —— 陈旧残留,统一填 `'loop'`,待清理(第 12 章⑥)。
|
||||
- **构建**:`pnpm activities:build`(新包要在 `tools/build-activities.ts` 的 PACKAGES 里登记)。
|
||||
- **安装**:`pnpm activities:install` —— 只装活动包,不碰治理台配置。
|
||||
**别用 `db:seed` 装活动**:它重写整套治理台配置。真活动包要在
|
||||
`apps/api/src/app/seed-activities.ts` 的 `REAL_PACKAGES` 登记(经安装通道读文件,不经 import)。
|
||||
- **版本纪律**:改了任何内容必须升 `identity.packageVersion` —— 同版重装静默丢失(踩过)。
|
||||
卸载只置状态不删行;资产播下一版不覆盖。
|
||||
- 流程包节点用 `activityId` 引用活动;同名旧槽位要**退役卸载**(库里两个同名活动会骗人)。
|
||||
|
||||
### 4.2 清单六区(`packages/contracts/src/manifest.ts:163-172`)
|
||||
## 5. 隔离与守卫
|
||||
|
||||
| 区 | 内容 | 硬规矩 |
|
||||
|---|---|---|
|
||||
| ① identity | `activityId / name / packageVersion / category / sdkVersion / checksum / signature?` | **无 paradigm**;`sdkVersion` 与 SDK 精确相等否则拒装;**改了包内容必须升 packageVersion**(同版重装 no-op,改了不升版 = 静默丢失) |
|
||||
| ② contract | 七件契约(§4.3) | |
|
||||
| ③ declarations | `models[] / tools[] / ledger[] / listeners[] / harness` | 运行时**只读这里**的工具与模型声明;监听时限必配否则 `dangling-listener` 拒装 |
|
||||
| ④ assets | `businessModel{cmmnXml, narrative, dmn} / skills / viewTemplates / cardSchemas / distillHooks` | CMMN 与说明书**同版本发布不允许分叉**(空任一即拒装);**关键表单逐张配 HTML 报告模版**(执行与验收都要,待处理与成果同模版,§9.3) |
|
||||
| ⑤ samples | `{name, input, message?, expect:{ports[], payloadShape?}}[]` | **至少 1 条**;首装无历史时以静态审 + 样例集替代回放审 |
|
||||
| ⑥ entry | `{file, checksum}` | 指 .js 产物 |
|
||||
活动包**只准 import `@sdk` / `@contracts` / `@sfeel`**(depcruise 双向隔离:activities 不进
|
||||
apps,apps 也不 import activities —— 之间只有安装通道)。产物文本静态审黑名单:
|
||||
`process.env`、`require(`、`node:fs/net/child_process`、`fetch(`、`XMLHttpRequest`、
|
||||
`eval(`、`new Function(`。视图模版含 `<script` 或外链即拒(容器是 `sandbox=""` iframe)。
|
||||
|
||||
`declarations.harness`(`manifest.ts:33-45`):`memoryWindow`(默认 20)/ `stallAfter`
|
||||
(ISO-8601 时长,默认 PT30M —— 停滞熔断的判据)/ `nudge[]` / `retry`。
|
||||
## 6. 清单六区(`packages/contracts/src/manifest.ts`)
|
||||
|
||||
**台账声明**(`packages/contracts/src/ledger.ts:59-76`):`{domain, object, label?, fields[]}`,
|
||||
字段 `{name, type, label?, primaryKey, masked}`;标识符**只准小写字母开头、字母数字下划线**。
|
||||
域的归属纪律:活动自持域用活动名;**跨活动的事实只有两条路 —— 输出口载荷,或共享域
|
||||
`shared`**。物化三律(`ledger/internal/domain/schema-merge.ts`):最大化合并(同名字段
|
||||
masked/primaryKey 取或)、最宽类型(integer→number→text 等加宽链,json 与标量互斥即冲突)、
|
||||
只增不破坏(只产出建表/加列/加宽,**新增主键即冲突** → `ledger-type-conflict` 拒装)。
|
||||
### 6.1 identity
|
||||
|
||||
### 4.3 契约七件(`packages/contracts/src/contract7.ts:129-140`)
|
||||
```ts
|
||||
identity: {
|
||||
activityId: activityIdSchema.parse('quote-intake'),
|
||||
name: '客户RFQ受理', // 人读名
|
||||
packageVersion: '2.4.0', // 改内容必升
|
||||
category: '统一报价', // 活动库唯一的一维分类
|
||||
sdkVersion: '0.1.0-m5', // 与 SDK 精确相等,否则拒装
|
||||
checksum: 'dev-checksum-…', // 非空即可(开发期)
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 契约七件(`contract7.ts`)
|
||||
|
||||
| 件 | 写什么 | 硬规矩 |
|
||||
|---|---|---|
|
||||
| ① input | `data`(JSON Schema)+ `messageAllowed / messageHint` | `data` 即**启动 Schema**(§5);message 不得携带扩大授权的效力 |
|
||||
| ② output | `mandatory[]` + `selective[]` | mandatory 每次放行必发(AND);selective 每组**至少 2 口、恰发一口**,选口即分支结论(§7.4) |
|
||||
| ③ requiredDeliverables | `{key, label, schema?}[]` | 缺一不得提交放行;验收工单「成果清单」的来源 |
|
||||
| ④ acceptanceCriteria | `{key, label, level: L1-L5, predicate?, modelRef?}[]` | **L1/L2 必带 predicate(S-FEEL)或 modelRef**,否则 `acceptance-level-misreported` 拒装 —— 验收等级不得谎报 |
|
||||
| ⑤ prohibitions | `{key, statement}[]` | 短而硬;负面清单优于正面枚举 |
|
||||
| ⑥ authorization | `discretion[] / preauthorizedActions[] / humanCallBudget` | humanCallBudget = 调人预算(环外强制);⚠️ preauthorizedActions 现为死代码,**暂不使用**(第 12 章⑨) |
|
||||
| ⑦ resources | `tools[] / skills[] / models[] / roles[] / budget{steps, tokens, activeMinutes, humanCalls}` | 预算四项由基座环外强制,**不得由活动代码自查自守**;models 是自定义模型名 |
|
||||
| ① input | `data`(JSON Schema)+ `messageAllowed / messageHint` | 起单**宽进**:缺 required 不拒单,由基座发补全卡(§11.1);`properties[k].title` 写中文 —— 补全卡上给人看的就是它 |
|
||||
| ② output | `mandatory[]` + `selective[]` | mandatory 每次放行必发;selective 每组 ≥2 口、**恰发一口**,人在验收单上选,选中口名注入 `outcome` 供下游网关 |
|
||||
| ③ requiredDeliverables | `{key, label, ledger}[]` | **`ledger` 必写**(`域.对象` 台账地址):预检数**本单草稿**在该地址的行数,缺一不得提交放行 —— 不给地址就只能靠活动自报,自报等于没有门 |
|
||||
| ④ acceptanceCriteria | `{key, label, level: L1-L5, predicate?}[]` | **L1/L2 必带 predicate(S-FEEL)**,提交时对任务视图当场机检;L3-L5 交人。**能算的判据一律降到 L1/L2** —— 交给人的判据,人忙时会盲签(踩过) |
|
||||
| ⑤ revisableAtRelease | `{key, label, ledger}[]` | 放行时人可当场改的值(如两枚去向旗);AI 拟值由基座放行前**从台账现读**,不让模型重述;改完落回声明的地址 |
|
||||
| ⑥ prohibitions / authorization | 禁区短而硬;`humanCallBudget` = 调人预算 | `preauthorizedActions` 是死代码,**不要写** |
|
||||
| ⑦ resources | `tools / skills / models / roles / budget{steps,tokens,activeMinutes,humanCalls}` | **budget 按实测给,留一倍余量** —— 给太紧等于让 Harness 杀正在干的活(A1:steps 60 / tokens 400k / 60min / 调人 4;实测常路 ~8 拍 5-8 万 token) |
|
||||
|
||||
> 坑位:`contract.resources.tools` 与 `declarations.tools` 是**双份声明**,运行时只读
|
||||
> declarations(`engines.ts:443-447`)——两处要写一致,安装审只查 declarations。
|
||||
> **出口字段要有台账列着落**:出口载荷从放行落地的事实推导(§7.5),声明一个没有任何
|
||||
> 台账列承载的标量出口字段,下游网关会因取不到值而死路(`route-dead-end`)。
|
||||
**三条对齐线**(错位不报错,只在放行那一刻「取不到值」,查起来一层皮 —— 自测四段第 ④ 段钉死):
|
||||
|
||||
## 5. 启动与补全(活动输入无强制类型)
|
||||
1. `requiredDeliverables[].ledger` 的地址必须在 `declarations.ledger` 里声明过;
|
||||
2. L1/L2 谓词引用的标识符必须是**声明过的台账列**(且用汇总列,见下);
|
||||
3. 出口载荷的**标量字段**必须有台账列承载 —— 载荷从落地事实推导:数组字段 ←
|
||||
落地行整体,标量 ← 摊平事实(驼峰/下划线两认)。声明了没列承载的标量,
|
||||
下游网关取不到值全流死路(踩过:两枚去向旗没有列,G1 全断)。
|
||||
|
||||
### 5.1 编排层确认结论(✅ 全部成立)
|
||||
**汇总列纪律**:任务视图把草稿行摊平到顶层是「后写的赢」——拿逐行列(如
|
||||
`missing_key_fields`)做谓词只会验到最后写的那一行,那不叫机检叫抽样。
|
||||
所以验收谓词读**一单一行的汇总对象**(A1 的 `quote_intake.verdict`:`line_total` /
|
||||
`missing_key_total` / `untyped_total` / `type_a_total` / `alt_request_total`…),
|
||||
汇总列与逐行列**故意不同名**,免得互相盖掉。
|
||||
|
||||
用户语义「活动间流转数据没有强制类型、活动输入可接受任何类型」在编排层**已成立**:
|
||||
**派单对账模式**(A1 实例,值得照抄):「C 类几条就该开几张替代评估请求」这类
|
||||
一致性,把两边的条数都落成汇总列,谓词写 `alt_request_total = type_c_total` ——
|
||||
机检,不交人。
|
||||
|
||||
- **起单宽进**:`process.start` 不按调用接口拒缺项(`process-service.ts:270-315`,
|
||||
注释写死「门口不设缺项闸,收敛点在活动」;`start-guard.test.ts:109-115` 守着)。
|
||||
- **数据按字段名流动**:`flatScope` 铺平作用域(起单入参垫底 → 各输出口字段依次盖上 →
|
||||
刚发射的载荷最后盖,后发生的赢,零类型检查,`routing.ts:32-41`);节点入参 =
|
||||
**整个铺平作用域**,下游按自己的契约取,无契约裁剪(`self-hosted-runtime.ts:256`)。
|
||||
- **闭合校验不查字段**:只剩结构闭合(dangling-node / multi-default-edge)与活动装载
|
||||
(unknown-activity)四条(`closure.ts:33-81`)。字段够不够是活动自己的事。
|
||||
### 6.3 declarations
|
||||
|
||||
### 5.2 机械门:required 缺项 → 补全卡(✅)
|
||||
```ts
|
||||
declarations: {
|
||||
models: ['deepseek-v4-flash'], // 自定义模型名;非空才有 LLM 五件上总线
|
||||
tools: [{ name: 'crm.history-price-read' }, { name: 'quote-intake.classify' }],
|
||||
ledger: [ { domain, object, label, fields: [{name, type, label, primaryKey, masked, required?}] }, … ],
|
||||
listeners: [ { origin:'static', eventType, keyFilter, semantic:'observe'|'complete-step',
|
||||
deadline:'P1D', escalation:['nudge','escalate'] } ], // 时限必配,悬空拒装
|
||||
harness: { memoryWindow: 20, stallAfter: 'PT45M',
|
||||
nudge: [{ after:'PT15M', action:'nudge' }], retry: { times:2, backoff:'PT30S' } },
|
||||
}
|
||||
```
|
||||
|
||||
任务启动时(`task-service.ts:88-134`):
|
||||
- **台账声明就是授权面**:运行时只准写这里声明的地址与列名,编出来的列当场拒。
|
||||
它同时是喂给 Agent 的字典(不给字典,它只会照想象编列名)。
|
||||
- **`required: true` = 这一列缺了就是活没干完**(可选,不建 NOT NULL —— 草稿层的本分
|
||||
就是过程中允许缺)。标了它,基座就数得出 `域_对象_missing`(缺必填几行)与
|
||||
`_missing_keys`(缺在哪几行、缺哪一列)——**机检判据该读这个,不读活动自报的汇总列**。
|
||||
栽过两次:自报的数要模型自己重算,漏算一次那道门就再也开不了(第二次是连跑十轮里三轮)。
|
||||
- 标识符小写字母开头、字母数字下划线。域归属:活动自持域用活动名
|
||||
(`quote_intake`);跨活动事实只有两条路 —— 输出口载荷,或共享域(`shared`/`intel` 等)。
|
||||
- 物化三律:最大化合并、最宽类型(integer→number→text 加宽链)、**只增不破坏**
|
||||
(新增主键 = 冲突拒装)。
|
||||
- 监听语义只有 `observe`(记观察交 Agent 裁量)与 `complete-step`;「客户改需求要回炉」
|
||||
这类业务反应写 `observe`,回炉是 Agent 看到观察后自己的决定。
|
||||
- `contract.resources.tools` 与 `declarations.tools` 是双份声明,运行时只读 declarations ——
|
||||
两处写一致。
|
||||
|
||||
1. `missingRequired(inputSchema, data)`:**只查启动 Schema 顶层 `required`**,
|
||||
「缺」= `undefined | null | ''`(`0`/`false`/`[]` 都算有);标签取
|
||||
`properties[key].title ?? label ?? key`(给人看「客户码」不是 `customerCode`)。
|
||||
**不做类型校验** —— 这是机械门的全部(语义级判断归 Agent,§5.4)。
|
||||
2. 缺项**照建单**,任务落 `awaiting-human`,引擎照常装载;
|
||||
3. 发**补全卡**(elicitation 对话卡,`engines.ts:715-736`):字段 = 缺的 `{key, label}`;
|
||||
**不占活动调人预算**(补全是受理的事,不是活动的打扰);挂 P2D 人事件监听。
|
||||
### 6.4 assets
|
||||
|
||||
**答卡合并**(`task-service.ts:262-299`)四条硬规矩:
|
||||
```ts
|
||||
assets: {
|
||||
businessModel: { cmmnXml: CMMN_XML, narrative: NARRATIVE, dmn: INTAKE_DMN }, // 双表示同版本,空任一拒装
|
||||
skills: INTAKE_SKILLS,
|
||||
viewTemplates: [ { name:'intake-overview', html: … }, … ], // 一个局面一张(§10.3)
|
||||
cardSchemas: {},
|
||||
distillHooks: [ { on:'terminal', prompt:'归纳本单…经验' } ], // 封卷后蒸馏进活动记忆
|
||||
}
|
||||
```
|
||||
|
||||
- 只收**当前还缺的键**(白名单),契约外的键、已填过的键静默丢弃;
|
||||
- 只收**非空字符串** —— 补全卡回传一律按字符串,数字/布尔会被静默丢弃(设计表单时注意);
|
||||
- 补齐(或「没法填」)→ 置 `running` 并立即 tick;答一半 → 只再派一张**只含剩余项**的卡,引擎一步不走;
|
||||
- **「没法填」**:回传 `reason` 非空即放行**带缺口开跑** —— 收敛责任从此归活动内部。
|
||||
两条路必走其一(答值或给理由),空答被 `card-rules` 拒收。
|
||||
### 6.5 samples · 6.6 entry
|
||||
|
||||
### 5.3 前端呈现(✅)
|
||||
样例 **≥1 条**(首装无历史时,静态审 + 样例集替代回放审);把常路与最长路径都给上:
|
||||
|
||||
补全卡渲染在对话区(`desk-task/internal/chat-bubbles.tsx:214-251`,题头「请你补全 · N 项」);
|
||||
「没法填?告诉 AI 原因」是显式按钮;**有未答的补全/决策卡时输入框锁定 ——「先办卡,再说话」**。
|
||||
```ts
|
||||
samples: [
|
||||
{ name:'三条料 A/B/C 各一', input:{ customerCode:'C001', rfqRef:'RFQ-…', subject:'…',
|
||||
body:'LM358DR 12K/年 目标价8.0 交期8周;NE555P 5K/年 交期6周;TL072CDR 3K/年 …' },
|
||||
expect:{ ports:['intake'] } },
|
||||
{ name:'缺三要素 → 走追问补全', input:{ …, body:'LM358DR 报个价,数量交期没定' },
|
||||
expect:{ ports:['intake'] } },
|
||||
],
|
||||
entry: { file: 'index.js', checksum: 'dev-entry-…' },
|
||||
```
|
||||
|
||||
### 5.4 Agent 案内随时可补(✅ 机制在,活动要自己用)
|
||||
## 7. 案例模型(CMMN)
|
||||
|
||||
开跑后发现「字段在但内容不像话」(机械门查不出的语义级不满足),活动应:
|
||||
### 7.1 建模纪律 —— 每一条都是实测账
|
||||
|
||||
- **小信息**:`interaction.issueCard({k:'elicitation', ask, fields, basis})` —— 对话区就地要几个小信息;
|
||||
- **要人干活**:`interaction.issueWorkOrder({mode:'execution', ...})` —— 执行工单
|
||||
(PRD 统一建模里「补全」并入执行事项的那条路,§9.2)。
|
||||
1. **图上有几步就是几项,不多建一件。** A1 第一版把 6 步建成 12 项(自加两个阶段、
|
||||
两个里程碑、一个裁量项、一个事件监听器)—— 业务一分没多,模型全程跟 harness 较劲:
|
||||
阶段当落点、里程碑收不收、项先 start 还是先干、编不存在的 id……一单烧二三十万 token
|
||||
还常收不了口。重写成扁平 7 项(S1…S6 + 放行)后零被拒。阶段/里程碑/裁量项是 CMMN
|
||||
有的东西,「有」不等于「该用」—— 要能换来业务上说得出的好处。
|
||||
2. **案卷项 id 就是任务视图里的键名。** 前提的 onPart 认的是「这个键有值了」——
|
||||
起单入参(`rfqRef`)与台账列(`line_total` / `missing_key_total`)都会摊平到任务视图
|
||||
顶层,照它们命名。名字对不上 = 前提永不满足 = **第一个计划项都不 Enabled**,
|
||||
整案一步走不动,而每一层单看都「正常」(踩过)。
|
||||
3. **`manualActivationRule` 只标触达世界与关键交付的项**(A1 标了两项:
|
||||
受理需求交人审定 `S3_confirm`、受理放行 `T_release`)。标了的项必须 Agent 显式
|
||||
`start` 才能干活;别的项引擎就地过 Active —— 为一次草稿写单烧一拍去「按一下」是纯仪式。
|
||||
**凡触达世界的项一律不给 `doneWhen`**:给了就等于给引擎一把替人签字的钥匙。
|
||||
3.5. **人的门要建成一项,不要挂成某一步的条件分支。**
|
||||
A1 栽过一次(2026-08-01 用户指出):「人确认受理需求」原本不是一项,而是归一那一步
|
||||
收步条件里的半句 —— `missing_minor_total = 0 or answered_total > 0`,
|
||||
意思是**只在缺东西的时候才叫人**。于是一封字段齐全的询价进来,这半句在拆完那一刻
|
||||
就为真,引擎自己把那步收了,人一眼没看过受理需求,下一次被叫到已经是终局验收。
|
||||
用户原话:「我都没确认需求 它就让我验收了」。
|
||||
**判据是「这件事办完了没有」,不是「要不要办」**;要不要经过人是模型结构的事,
|
||||
写进判据就成了「不缺就不必经过人」。做法:单独一项 + 人工激活 + 不给 `doneWhen`,
|
||||
并给它一个只有人的回复才产生的计数列(A1 是 `confirmed_total`)当下一步的入场前提。
|
||||
4. **`repetitionRule` 标常来回的项**(逐条登记、追问补全)——驳回重入靠它给新实例。
|
||||
5. **判断菱形 = 两条互斥前提,不是一个计划项。** D1「判完整度」在图上是菱形,
|
||||
在模型里是 `s_incomplete`(missing_key_total > 0 → S2 入场)与 `s_complete`
|
||||
(line_total > 0 and missing_key_total = 0 → S3 入场)两个 sentry。
|
||||
6. `autoComplete="true"`:必做项全 Completed 且无 Active 项时,引擎**不问模型**直接提验收。
|
||||
事件监听器不算必做项、不进缺口(它是等待岗不是成品)。
|
||||
|
||||
> ⚠️ 边界隐患(第 12 章④):走过「没法填」的任务,若后续验收被驳回(runtime 回到
|
||||
> awaiting-human),到达的 human 事件会被受理分支误吞(它不看 cardId)。在修复落地前,
|
||||
> 活动拿到「带缺口开跑」的单,应尽早在环内把缺项补齐或显式置值,别让缺口一路挂到验收。
|
||||
### 7.2 A1 的模型骨架(照抄改名即可)
|
||||
|
||||
## 6. 工具调用:完全声明式
|
||||
```xml
|
||||
<definitions xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL">
|
||||
<case id="intake">
|
||||
<caseFileModel> <!-- id = 任务视图键名(纪律 2) -->
|
||||
<caseFileItem id="rfqRef" name="询价单号(开案即有)"/>
|
||||
<caseFileItem id="line_total" name="登记出的条目数"/>
|
||||
<caseFileItem id="missing_key_total" name="缺关键字段条数"/>
|
||||
<caseFileItem id="history_days" name="历史价判据"/>
|
||||
<caseFileItem id="untyped_total" name="未打标条数"/>
|
||||
<caseFileItem id="need_sourcing" name="去向旗"/>
|
||||
</caseFileModel>
|
||||
<casePlanModel id="plan" name="客户RFQ受理" autoComplete="true">
|
||||
<sentry id="s_incomplete">
|
||||
<onPart caseFileItemRef="missing_key_total"><standardEvent>update</standardEvent></onPart>
|
||||
<ifPart><condition>missing_key_total > 0</condition></ifPart>
|
||||
</sentry>
|
||||
<sentry id="s_complete">…line_total > 0 and missing_key_total = 0…</sentry>
|
||||
<sentry id="s_basis"><onPart caseFileItemRef="history_days">…</onPart></sentry>
|
||||
<sentry id="s_typed">…untyped_total = 0…</sentry>
|
||||
<sentry id="s_routed"><onPart caseFileItemRef="need_sourcing">…</onPart></sentry>
|
||||
|
||||
### 6.1 设计口径
|
||||
<humanTask id="S1_register" name="接收登记:逐条落统一受理记录,结算条数与缺口数">
|
||||
<itemControl><repetitionRule id="r1"/></itemControl>
|
||||
</humanTask>
|
||||
<humanTask id="S2_ask_back" name="追问补全信息:按缺口清单发补全卡">
|
||||
<entryCriterion sentryRef="s_incomplete"/>
|
||||
<itemControl><manualActivationRule id="m2"/><repetitionRule id="r2"/></itemControl>
|
||||
</humanTask>
|
||||
<humanTask id="S3_normalize" name="…"><entryCriterion sentryRef="s_complete"/>…</humanTask>
|
||||
<humanTask id="S4_history" name="…"><entryCriterion sentryRef="s_complete"/></humanTask>
|
||||
<decisionTask id="S5_classify" name="…按三分类规则P-03打标" decisionRef="p03-type">
|
||||
<entryCriterion sentryRef="s_basis"/>
|
||||
</decisionTask>
|
||||
<humanTask id="S6_route" name="定去向与派单…"><entryCriterion sentryRef="s_typed"/></humanTask>
|
||||
<humanTask id="T_release" name="受理结论验收放行">
|
||||
<entryCriterion sentryRef="s_routed"/>
|
||||
<itemControl><manualActivationRule id="mR"/></itemControl> <!-- 触达世界,必标 -->
|
||||
</humanTask>
|
||||
</casePlanModel>
|
||||
</case>
|
||||
</definitions>
|
||||
```
|
||||
|
||||
设计活动时**只考虑需要什么工具**:工具名 + 输入 + 输出(+ 给人看的指导段⚠️)。
|
||||
不关心系统里有没有实现 —— 声明的工具进 `declarations.tools`(只有名,版本字段现无消费者)。
|
||||
CMMN 子集封顶:计划项 8 型(humanTask / decisionTask / processTask / caseTask / stage /
|
||||
milestone / timerEventListener / userEventListener),表外构件直接拒装。
|
||||
`ifPart` 前缀 `semantic:` 的条件引擎不机算,标「待裁量评估」交 Agent(judge 行动)。
|
||||
里程碑**前提达成即达成**(引擎自己收,不需要谁去按;无前提的里程碑不自达成)。
|
||||
|
||||
已注册工具的契约形状(`toolbox/internal/domain/tool-contract.ts:13-39`):
|
||||
`name / displayName(中文名)/ version / description(**面向 AI** 的适用说明,≥8 字)/
|
||||
input / output(JSON Schema)/ effect: 'read'|'write' / errors[] / idempotent(写类必须 true)/
|
||||
timeoutMs / retries / connectionKeys[](只声明键,值在治理台)/ capabilities.validate? /
|
||||
pure? / ownerActivityId?`。
|
||||
⚠️ 增加 `guide` 字段(**面向人**的作业指导段):工具未命中转人时,执行卡上的作业指导书
|
||||
从这里来 —— 声明式、可审、不临场生成(第 12 章②)。
|
||||
### 7.3 说明书(narrative)—— Agent 的作业指导书,最重要的一份资产
|
||||
|
||||
### 6.2 运行时五分叉
|
||||
与 CMMN 同版本发布、不允许分叉。它是每一拍叙事面的第一段,**写给 AI 执行看**。
|
||||
结构照 A1(`activities/quote-intake/src/case-model.ts`):
|
||||
|
||||
| 情形 | 目标行为 | 现状 |
|
||||
```
|
||||
§0 开案:什么事、成品是什么、SLA。
|
||||
【整条路就 N 步】一步一个计划项,别绕:S1 → D1 →(分支)S2 → S3 → …→ 提交放行。
|
||||
【S1 … · 计划项 S1_register】干什么、写哪张台账哪些列、几条纪律。
|
||||
【D1 判完整度 —— 判定表说了算,不是你临场判】…
|
||||
…每步一段…
|
||||
【提交放行】机检判据点名(line_total > 0、missing_key_total = 0…),先 start T_release 再 submit。
|
||||
【怎么把一项干完 · 一拍连做】…(见下)
|
||||
【一路上要说话】…(见下)
|
||||
§Z 结案。
|
||||
```
|
||||
|
||||
**八条写作纪律**(每条都对应一次实测翻车):
|
||||
|
||||
1. **每步点名落点计划项**(「计划项 S4_history」)—— 模型会把活挂错项,点名了才挂得对。
|
||||
2. **写哪张台账、哪些列,写死**(`quote_intake.record` 的八字段、`verdict` 的哪几个汇总数)。
|
||||
汇总列不写,机检判据无米下锅,整案卡在半路。
|
||||
3. **规则写死,不留给模型判**。Harness 已关模型思考换十倍速度(每拍 1.5-5s),代价是
|
||||
模型不再细想边界 —— 所以边界规则必须写到不可能读歪:「**关键字段只有三个:PN、EAU、
|
||||
交期。**目标价/厂牌/包装一律次要 —— 次要缺了不拦、不发补全卡」。凡判定表能表达的,
|
||||
说明书里只写「照 F-01」,并把表的口径复述一遍。
|
||||
4. **一拍连做指引**:「写 record、写 verdict、finish S1」是一拍不是三拍;调工具要看出参,
|
||||
tool 放串尾;发卡/提交会挂起,只能是串尾。**写明常路拍数预期**(「这一单常路 5-7 拍
|
||||
该收口」)—— 模型会照着省。
|
||||
5. **finish 语义**:write / tool 不会自动把项收掉(一项常要两三个动作才交得出成果);
|
||||
都做完了用 finish 宣告,再往下一步走。
|
||||
6. **补全走 ask 卡**:「用补全卡(ask 行动),不是执行工单」;人答回的值出现在轨迹里,
|
||||
**看到了要自己 write 进台账并把汇总数改小 —— 系统不会替你写**。
|
||||
7. **说话指引**:「每一拍都带一句 say,对负责人说人话;局面变了用 view 换画面」+
|
||||
每个局面对应哪张模版点名(§10.3)。不写这段,对话区从头到尾空白。
|
||||
8. **禁臆造**:「原文没写的不要补,缺就记缺」「拿不准一律降 B,不许臆断 A」——
|
||||
受理阶段编出来的数会一路流到报价单上。
|
||||
|
||||
### 7.4 拍数账(设计自查)
|
||||
|
||||
估算:每个计划项 ≈ 1 拍(写 + finish 同拍),每次工具调用 +1 拍(要看出参),
|
||||
每次人环 +1 拍(回传后续跑)。A1:S1(1)+ S3(1)+ 工具 ×2(2)+ 打标写+finish(1)+
|
||||
S6(1)+ start 放行 + submit(1)≈ 7 拍。**估出来超过 10 拍,回头合并计划项。**
|
||||
|
||||
## 8. 判定表(DMN)—— 能算的不问 LLM
|
||||
|
||||
```ts
|
||||
export const P03_DMN: DmnModel['tables'][number] = {
|
||||
id: 'p03-type', label: '三分类规则 P-03', hitPolicy: 'FIRST',
|
||||
inputs: [ { name:'history_days', expression:'history_days' }, … ],
|
||||
outputs: [ { name:'type_tag' }, { name:'play' } ],
|
||||
rules: [
|
||||
{ id:'p03-c', when:['-','-','"EOL","缺货","要求替代"'], then:['C','转案处理'] },
|
||||
{ id:'p03-a', when:['<= 90','true','-'], then:['A','直接快答'] },
|
||||
{ id:'p03-b', when:['-','-','-'], then:['B','重估后报'] },
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
- 单元格谓词 = S-FEEL:`'-'` 任意、`'"A","B"'` 析取、`'<= 90'` 比较、`'[0..100]'` 区间。
|
||||
- **FIRST 的行序就是业务优先级**:C(EOL)必须排最前 —— EOL 的料哪怕历史价很新也不该判 A。
|
||||
- 求值:任一输入缺 → incomplete;命中留痕(inputs / hitRules / outputs),同输入必同结论、零 token。
|
||||
- **纯工具求值器模式**(推荐):把判定表做成 `pure: true` 的功能工具
|
||||
(`quote-intake.classify`,`ownerActivityId` 私有作用域),模型只负责取齐判据、
|
||||
调工具拿结论 —— 「不要绕过决策表自己判:规则表可回放可追责,临场判断不是」。
|
||||
|
||||
## 9. 工具
|
||||
|
||||
**声明式**:设计时只考虑需要什么(名字进 `declarations.tools`),不关心系统里有没有。
|
||||
工具本体在系统侧注册(治理台 / `seed-mvp.ts`),契约含面向 AI 的 `description` 与
|
||||
面向人的 `guide{ask, steps?, standard?}`。
|
||||
|
||||
**运行时五分叉**:命中读类直接执行;命中写类**登记动作意向**(放行才执行);
|
||||
**未命中转人** —— 开一张执行卡(卡面 = guide + 本次入参 + 按出参 Schema 生成的回填表单),
|
||||
人填回的值就是这次调用的出参(`sys.tool_handoff`,同调用只发一张卡);
|
||||
未白名单授权报错(越权不转人);已停用走失败路径。
|
||||
|
||||
**设计惯例**(拍数与正确性都靠它):
|
||||
|
||||
- **批量入参**:一单几十条料,工具收数组(`pns: string[]` / `rows: object[]`),
|
||||
一次传完 —— 一条一调,每调一次多烧一拍。
|
||||
- **出参键名与台账列同名**(`type_tag` / `basis` / `history_days`):模型拿回结果直接
|
||||
write,不必自己改名 —— 改名这一步交给模型,它会漏。
|
||||
- 判据类出参把「没有」也当判据回(无历史 → `historyDays: 9999, hit: false`),
|
||||
别回错误 —— 新料号无历史本就该命中 B 类。
|
||||
|
||||
**系统内化、零声明即得**(不进目录不进白名单):`write`(写台账草稿,授权面是台账声明)、
|
||||
`ask`(补全卡)、`human`(执行/验收工单)、`listen`(立监听);声明了模型才有 LLM 五件
|
||||
(extract/classify/summarize/judge/rewrite,提示词归系统,judge 只作参考不代放行)。
|
||||
|
||||
## 10. 人类工具与汇报
|
||||
|
||||
### 10.1 四类(前三类已通;决策卡 UI 在、Agent 侧未接)
|
||||
|
||||
| 类 | 形态 | 活动怎么用 |
|
||||
|---|---|---|
|
||||
| 命中 · 读类 | 直接执行 | ✅ `tool-invoker.ts` 执行(超时 race;入参不合 schema 回 `{ok:false, code:'input-invalid'}` 不抛) |
|
||||
| 命中 · 写类 | **登记动作意向**,放行后由系统执行(附幂等键) | ✅ 登记(`app/tool-bus.ts:206-209`「现在世界上什么都没变」);⚠️ 但落地链路断着 —— 意向永停 drafted,放行时不执行(§7.3、第 12 章①) |
|
||||
| **未注册**(系统里没有) | **生成人类执行卡 + 作业指导书**:卡面 = 工具 guide 段 + 本次输入数据 + 按**输出 Schema** 生成的回填表单;人填回的表单值**就是这次工具调用的出参** | ⚠️ 现状抛 `ToolNotFoundError`(`tool-invoker.ts:43`),单拍路径 catch 后记忆留痕「行动被拒」回合不崩 —— **转人待实现**(第 12 章②) |
|
||||
| 已注册 · **未白名单授权** | **报错**(越权是治理配置问题,不静默转人) | ✅ `ToolNotAllowedError`(`tool-invoker.ts:45-51`,文案「越权在结构上不可能」) |
|
||||
| 已停用 | 失败路径(活动按失败处理) | ✅ `ToolDisabledError` |
|
||||
| 执行 | 工单:ask / steps / standard / fields(8 种控件,可带 ai 拟值)| `human` 行动,`workOrder:'execution'`;卡面填实(title/ask/basis/fields),裸卡拒发 |
|
||||
| **补全** | 对话卡:ask + fields(就地填)+「没法填」两条路必走其一 | `ask` 行动 `{kind:'ask', itemId, ask, fields:[{key,label,hint?}], basis}`;fields 空即拒 —— 空表单等于没问 |
|
||||
| 验收 | 工单:判据表(L1/L2 已机检)+ 落地动作清单 + 可修订值 + 恰选一口 | `submit` 行动;判据/动作/拟值由基座从契约与台账装配,活动不重述 |
|
||||
| 决策 | 对话卡:选择题 + 推荐 + 后果 + 自定义口径 | 暂不用(Agent 侧未接) |
|
||||
|
||||
配套现状(如实记):
|
||||
调人预算(`humanCallBudget`)环外扣账;起单补全卡不占活动预算。
|
||||
|
||||
- 工具总线 = **声明 × 整张目录**,编译时未命中收进 `missing[]` 如实回报、写观察进任务
|
||||
记忆、**不阻断**(`compileToolBus`,`tool-bus.ts:184-213`;`engines.ts:449-455`)——
|
||||
这半边已经是「声明式」的形状,缺的是运行时把 miss 转人;
|
||||
- ⚠️ 安装静态审现把 `tool-contract-missing` 判 **error 拒装**(`static-audit.ts:108-111`)——
|
||||
与「声明式工具、系统没有就转人」正面冲突,需降级为 warn(第 12 章②);
|
||||
- ⚠️ 白名单授权现状只有种子常量表(`seed-mvp.ts:109-117`),无治理台授权口(第 12 章⑤);
|
||||
- 缺省单拍 Agent 路径**不经工具总线**(工具名是自由字符串,唯一闸门是 ToolInvoker);
|
||||
`JCAI_AGENT_OPEN_LOOP=1` 的开环路径才吃总线的声明×目录编译(第 12 章⑧)。
|
||||
### 10.2 唤醒与挂起
|
||||
|
||||
### 6.3 系统自带的工具(不用声明/只需声明模型)
|
||||
三类同级唤醒:人对话输入、外部事件(数据/时间)、页面交互(答卡/办结工单)。
|
||||
发卡即挂起(status awaiting),回传即续跑;**在途验收先于一切**(有未裁决验收卡时
|
||||
回合原地等,不重发卡不问模型)。人答的值出现在下一拍轨迹里(`回填 {...}`)。
|
||||
|
||||
- **human.ask 恒在**(`tool-bus.ts:52-75`):调人执行卡,零声明即得,不进目录不进白名单 ——
|
||||
Harness 给的能力;每发一卡计 `humanCalls: 1`。
|
||||
- **LLM 五件**(`tool-bus.ts:84-159`):`llm.extract / classify / summarize / judge / rewrite`,
|
||||
仅当 `declarations.models` 非空才上总线(没声明模型却问模型是越权,用量无从归因);
|
||||
提示词归系统,活动不写也改不了;`llm.judge` 只作参考,**不代替人的放行裁决**。
|
||||
### 10.3 报告模版:活动持有,一单一模版贯穿始终
|
||||
|
||||
## 7. 写动作与验收(所有写必须经人类验收)
|
||||
1. **一个局面一张**,局面变了让 Agent 用 `view` 换(A1 四张:overview / gaps /
|
||||
classified / release),不在一张里堆条件分支;
|
||||
2. **在办与归档同一张**:办结后指导与判据卸下,报告原样进「工作成果」;
|
||||
3. 容器是 `sandbox=""` iframe:**无脚本、无外链、样式自带**;数据由基座推送时填
|
||||
(`data-slot` 取值、`data-rows` 逐行展开、`data-class-of` 按值上色)。
|
||||
|
||||
### 7.1 写三分(✅,`ledger/contract.ts:85-114`)
|
||||
### 10.3.0 三个区各放什么(2026-08-01 用户定,别再混)
|
||||
|
||||
| 写法 | 谁用 | 事件 |
|
||||
| 区 | 放什么 | 不放什么 |
|
||||
|---|---|---|
|
||||
| `systemWrite` | 系统(受理落件等) | 即时抛数据事件 |
|
||||
| `draftWrite` | **活动过程写的唯一形态**(`base.ledger.write`) | **不抛事件**;写时记基线值 |
|
||||
| `releaseWrite` | 放行那一刻(系统调) | 先查冲突(草稿基线 ≠ 当前正式版即 `DraftConflictError`)→ 事务合并 → 读回落地行 → **此刻才抛事件** → 清草稿 |
|
||||
| **工作区 · 待处理** | **只放那份件**(活动推的 HTML 模版),整篇铺开、**可就地改** | 题头 / 编号带 / 依据带 / 判据表 / 作业指导书 / 提交钮 |
|
||||
| **工作区 · 工作成果** | **只放那份件**,不加编辑态 —— 同一份 HTML 就是归档件 | 同上 |
|
||||
| **对话区的卡** | 作业指导书(ask / steps / standard)· 依据 · 判据逐条 · 去向 · 填报表单 · **提交与裁决** | 件本身(给一个「去工作区看」的入口就够) |
|
||||
|
||||
### 7.2 动作意向(✅ 登记,⚠️ 执行断链)
|
||||
栽过的:工单报告组件自带一整套外壳(题头 / 元信息 / 指标带 / 章节头),再把活动推的那一屏
|
||||
**包在自己里面** —— 于是人看到的是「一份报告里嵌着另一份报告」,两套版面语言叠在一起。
|
||||
用户原话:「你用你自己的报告格式嵌套了一层我要的报告格式」。
|
||||
|
||||
写类工具调用(`base.tools.intend`,或 invoke 写类工具被自动转意向)→ 登记
|
||||
`sys.action_intent`:入参冻结 + 幂等键 + 状态机 `drafted → prechecked → landed/failed/abandoned`。
|
||||
**登记那一刻世界上什么都没变** —— 这是铁律②的机制承载。
|
||||
**就地改怎么做的**(`view-render.ts` 的 `twinInputs` + `ui-kit/ViewContainer`):
|
||||
可改的格子在模版里挂 `data-col="台账列名"`,所在的行容器挂 `data-edit="域.对象"`;
|
||||
渲染时每格多渲一个 `<input class="jc-cell">` 孪生件,只读那份与输入那份**由样式表二选一**。
|
||||
容器是 `sandbox="allow-same-origin"` 的无脚本 iframe —— 外层读得到也改得动里面的 DOM:
|
||||
加 class、挂事件、读 input 全在父文档这一侧,**iframe 里始终一行脚本都没有**。
|
||||
收上来的值按台账地址对上工单 `blocks[i]` 的 `ledger`/`fields`,仍走老的 `table:N` 回传,
|
||||
服务端写回一个字都不用改。
|
||||
|
||||
### 7.3 放行落地六步(`release-service.ts:144-244`)
|
||||
**默认锁着,按「编辑」才开**(2026-08-01 用户定):每张可改的表各带一道闸
|
||||
(`[data-edit-for="域.对象"]`,三个钮 `data-edit-open` / `-cancel` / `-save`)——
|
||||
锁着只出「编辑」,开了换「取消 / 保存」;取消还原成按下那一刻的值,保存才交给外层缓冲
|
||||
(真正落草稿层仍在提交那一下)。**看与改是两件事**:整份件一进屏就是编辑态的话,
|
||||
人还没决定要改什么,眼前已经是一张待填的表(A1 那份 54 个输入框一起亮着)。
|
||||
|
||||
① `releaseWrite` 草稿合并(原子、过冲突检查)→ ② 记痕「放行写合并 N 行」→
|
||||
③ **只取 `prechecked` 的意向**逐个执行(附幂等键 `${taskId}:${intentId}`,已成动作不回滚)→
|
||||
④ 裁定(有失败即 blocked)→ ⑤ 受阻:发落地失败卡、口一个不发、任务落 `blocked` 终态 →
|
||||
⑥ 收敛:恰选一口校验(兜底)→ 发射输出口。
|
||||
⚠️ **跨 realm 陷阱**:父文档里写 `event.target instanceof Element` 判 iframe 里的元素,
|
||||
**恒为 false**(`Element` 是父文档那个构造器,原型链不同)。症状极难查:监听挂上了、
|
||||
事件也收到了,却每次都在第一行 return,而 class / CSS / 选择器逐个查都是对的。
|
||||
**认能力不认血统**:有 `closest` 就当它是元素。同理,`srcDoc` 换文档的时机抓不准 ——
|
||||
与其判断「挂过没有」,不如每次有机会就挂一遍(同一个 (type, listener) 重复 add 无副作用)。
|
||||
|
||||
> ⚠️ **重大现状洞(第 12 章①)**:把意向从 `drafted` 推到 `prechecked` 的
|
||||
> `submitForRelease`(预检 + 已验证动作清单上卡)**没有任何调用方** —— 活路径
|
||||
> (`turn-runner.raiseAcceptance`)绕过了它。后果:**写类动作登记后永不执行**、
|
||||
> 验收卡的 `actions` 恒空、`criteria` 恒为硬编码单条 L5、幂等键登记/落地两套不一致。
|
||||
> 实现工具调用逻辑时这是第一优先级。
|
||||
为什么两份都渲、不渲成两套 HTML:同一份件既在待处理页给人改、又在工作成果里定稿归档,
|
||||
**两处是同一份**。渲两套等于存两份,存两份就会分叉。
|
||||
|
||||
### 7.4 验收工单与恰选一口(✅)
|
||||
### 10.3.1 有目标件(设计稿)的时候:**搬,不要照着做**
|
||||
|
||||
验收工单 schema(`card.ts:328-350`):`deliverable / criteria(L1-L5 逐条,pass 标记)/
|
||||
actions?(通过即执行的动作清单)/ fields(报告可修订)/ portChoice?(选择组)/ passLabel?`。
|
||||
回传(`card.ts:473-491`):`verdict: pass | redo | revise` + `values` + `comment?` + `selectedPorts`。
|
||||
A1 两份报告是 1:1 平移 `~/coding/JCAI_PRD/docs/活动执行页/` 的目标件 ——
|
||||
样式表整段搬进 `views/prd-style.ts`(`PRD_CSS` + `PRD_SPRITE`,一个数值都没改),
|
||||
标记逐块搬进 `views/report.ts`,只把写死的数据换成绑定。**不用 `@sdk` 的拼装件**:
|
||||
那套外壳有自己的版面语言(编号章节头、摘要段、元信息带),拿它「照着做」出来的是第二种版面。
|
||||
|
||||
机检三规矩(`card-rules.ts:101-150`):驳回**必附意见**;**恰选一口**在 pass 时强制
|
||||
(未选 / 选组外即拒收,主闸在回传收单,落地 `validateSelection` 兜底);工单只增不改。
|
||||
`producesCompletion` 仅 `acceptance + pass` —— **验收通过是任务完成的唯一来源**。
|
||||
栽过三轮才认下来:块搬齐了就报「25 块到位」,而那是**结构断言**(某个 class 在不在),
|
||||
对版面一点约束都没有 —— 人一眼就看出不是那份件。所以:
|
||||
**视觉目标件只能看,不能用 DOM 断言验收**;搬完必须与目标件并排截图比对。
|
||||
|
||||
选中的那一口**就是这单的分支结论**:放行注入 `facts.flat.outcome = 选中口名`
|
||||
(`engines.ts:642-644`),下游异或网关的条件读的就是它(`outcome = "won"` 等)。
|
||||
搬的时候要换掉的四样(原型是带脚本的演示页,这里一行脚本也跑不了):
|
||||
|
||||
> ⚠️ 前端链两端断(第 12 章③):验收工单的 `fields` 前端不渲染、`values` 恒传 `{}`
|
||||
> (人工修订报告落不下来);驳回 UI 只有 redo,引擎侧把 redo/revise 都塌成 reject。
|
||||
| 原型怎么做 | 搬过来怎么做 |
|
||||
|---|---|
|
||||
| JS 造表、写死的行 | `data-rows` 行模版,一行一条从草稿层展开;序号交 CSS 计数器 |
|
||||
| JS 算的计数 | 台账里的汇总列,`data-slot` 取 |
|
||||
| JS 判空插「必填/选填」胶囊 | `data-blank` 让空值真空 + CSS `td:empty::after` |
|
||||
| JS 拼 class 上色 / 写 `style="flex:N"` | `data-class-of` 把值(或**条数**)投影成 `v-<值>`,样式表按值分档、按数取宽(备一把 `v-1…v-20` 的梯子) |
|
||||
|
||||
### 7.5 出口载荷从落地事实推导(✅,契约设计的硬约束)
|
||||
搬不过来的如实空着 —— A1 空了两处:圆标里的首字母(得算)、「近 6 笔成交」迷你柱
|
||||
(`market.price_history` 有列没数据、没有写入方)。编六根柱子是撒谎,画一条空轨像组件坏了,
|
||||
所以那一格不画,把「为什么没有」写在卡脚。
|
||||
|
||||
`outputPortsOf(manifest, landed)`(`engines.ts:535-574`):数组字段 ← `landed.rows` 整体;
|
||||
标量字段 ← `landed.flat[field]`(驼峰/下划线两认);`landed.flat` 还会全量兜底铺进每个口。
|
||||
**推论**:出口声明的标量字段必须有台账列承载(活动放行前写进草稿的行),否则下游取不到 ——
|
||||
这是活动契约与台账声明之间必须对齐的一条线(历史事故:两枚去向旗没有列,G1 全流死路)。
|
||||
拼装件(`@sdk`,**A2–A7 仍在用**):`viewShell({title,titleRef,kicker,tone,icon,summary,meta,stamp,body,sign})`
|
||||
外壳,正文用 `section` 编号章节串起 `letter` / `factCards` / `statsBand` / `tableOnly` /
|
||||
`stagesOnly` / `noteOnly` / `verdict` / `actionList`。
|
||||
|
||||
## 8. 案例模型(CMMN + 说明书 + DMN)
|
||||
- `letter({from,subject,body,attachments})` —— **来件原文**,`pre-wrap` 照录不摘要。
|
||||
归一出来的每个字段都要回溯得到出处;原文摆在结果**上面**,人一屏之内核得完。
|
||||
- `factCards([{label,value,note,icon,tone,mono}])` —— 要素带:摆「谁与什么」,
|
||||
与指标带(摆「数」)分工。
|
||||
- `tableOnly(rowsPath, cols, { numbered })` —— 列样式 `mono`/`num`/`pill`/`req`/`opt`
|
||||
(以及 `req-mono`/`opt-mono`/`req-num`/`opt-num`)。**`req`/`opt` 是分量声明**:
|
||||
空格子自己出「必填」红胶囊 / 「选填」灰胶囊,缺必填的整行浅红打底、行首红条 ——
|
||||
哪几列缺了不许放行,是写清单时就该说清的事,不是让人对着一屏「—」自己猜。
|
||||
序号列由 CSS 计数器排(模版里没有循环变量)。**归档件不摆胶囊**:到了那一屏,
|
||||
空格子的意思不再是「你还能填」而是「客户没给」。
|
||||
- `actionList([{title,detail}])` —— 放行那一刻系统替人做了什么,一条一行。
|
||||
|
||||
### 8.1 CMMN 子集封顶(`loop-engine/internal/domain/cmmn-parse.ts`)
|
||||
**取数约定**:`mine:域.对象` = 本单草稿行(成果表只用它 —— 别单放行过的记录不混进来);
|
||||
裸键 = 摊平字段;引擎注入的案情:`case:stages` / `case:enabled` / `case:gaps` ·
|
||||
`case:openedAt`(本地时刻)/ `case:origin` / `case:owner` ·
|
||||
**`case:routeKey` / `case:routePort` / `case:routeTo` / `case:routeWhy`** = 这一单算出来走哪一口。
|
||||
放行那张必须摆出**下游要读的关键值**与**去向**—— 人签字前得看见这一单要往哪走。
|
||||
|
||||
计划项 **8 型**:`humanTask / decisionTask / processTask / caseTask / stage / milestone /
|
||||
timerEventListener / userEventListener`;结构标签 23 个(sentry/onPart/ifPart/planningTable/
|
||||
discretionaryItem/itemControl/…)。**表外构件直接拒**(`assertSubset` 抛错,不静默忽略)——
|
||||
子集封顶要能被机器证明。
|
||||
**模版不许算,但基座算过的可以摆**。两件事别混:
|
||||
- 报告**自己算**一遍(把判据抄成静态文字、拿宽度骗人)—— 不行。抄本与判据是两份东西:
|
||||
改了 `when` 忘了改报告,屏上那一行就一直指着旧规则。
|
||||
- 摆**基座算好的那一次**—— 该做。去向由契约里各出口的 `when` 按序求值(首个命中即出),
|
||||
基座把结果注入 `case:route*`,与放行时发口的是同一次判定;
|
||||
报告只把它摆出来:壳上 `data-class-of="case:routeKey"` → `v-r1/r2/r3`,对应那一行自己上色,
|
||||
「命中 / 未命中」两句话都写在模版里、由样式表决定哪句现身。
|
||||
算不出来(有一口没写谓词、或一条都不命中)三个键就是空的 —— 一行都不高亮,不冒充判过。
|
||||
|
||||
| 记号 | 语义 | 纪律 |
|
||||
**剧本的 `view` 决定每一拍推哪张**:引擎按「还没干完的那一步」推,活步都收完就一帧不推 ——
|
||||
最后摆上去的那张(放行页)就是归档件。所以放行那一步的 `view` 必须指向验收目标件。
|
||||
|
||||
## 11. 运行时参考(活动作者要知道的引擎行为)
|
||||
|
||||
### 11.1 起单与补全(宽进)
|
||||
|
||||
`process.start` 不按 Schema 拒缺项;任务照建,缺 required 顶层键(缺 = `undefined|null|''`)
|
||||
→ 任务落 `awaiting-human` + 发补全卡(不占预算)。答卡只收还缺的键、只收非空字符串;
|
||||
「没法填」给理由即**带缺口开跑**,收敛责任归活动。数据按字段名在流程里流动,零强制类型。
|
||||
|
||||
### 11.2 Agent 的行动型(一拍一串,封顶 6)
|
||||
|
||||
| 行动 | 形状 | 语义 |
|
||||
|---|---|---|
|
||||
| `manualActivationRule` | Enabled ≠ Active,要按一下才动;**Agent 发 `start` 来按** | **触达世界与关键交付的项必须标**(AM 4.1 原理一) |
|
||||
| `repetitionRule` | 项可重复(阶段可多轮) | 撒网/追问这类常来回的事要标 |
|
||||
| `planningTable / discretionaryItem` | 裁量项:Agent 临场规划入场 | 「看情况做」的事放这里 |
|
||||
| `entryCriterion / exitCriterion` | sentry = onPart(听任务数据/项迁移)+ ifPart(S-FEEL) | 条件读任务数据字段 |
|
||||
| `ifPart` 前缀 `semantic:` | **语义性前提**:引擎不机算,交 Agent 裁量 | 「回价是否可信」这类判断这样写 |
|
||||
| `autoComplete` | 齐了即收(必做的都完成、没有进行中的)| 案计划级与阶段级都可标;齐了不问 Agent 直接提验收 |
|
||||
| `timerEventListener` | 时限(ISO-8601 时长) | 催办/过半提醒 |
|
||||
| `userEventListener` | 人事件(客户改需求等) | 外部变化的入口 |
|
||||
| `start` | `{itemId}` | 启动标了人工激活的项;已 Active 再按是无事发生 |
|
||||
| `plan` | `{itemId}` | 裁量项入场 |
|
||||
| `disable` | `{itemId, reason}` | 此单不走这条 |
|
||||
| `judge` | `{criterion, satisfied, rationale}` | 评估 `semantic:` 前提,留痕 |
|
||||
| `write` | `{itemId, address:'域.对象', rows:[{key,patch}]}` | 写草稿。**授权面是台账声明**(未声明地址/列即拒;空转写 —— 一行都没变 —— 即拒);`itemId` 只是记账,挂不上照写不拦;patch **只带有值的列** |
|
||||
| `drop` | `{itemId, address:'域.对象', keys:[{主键}], reason}` | **撤掉自己写错的那几行**(2026-08-01 加)。只动本单草稿层,正式版碰不到、不抛事件 —— 不是第四种写。授权面与 `write` 同一道;主键要与写进去时**一字不差**,撤不到当场拒。**门拦住一件办错的事却不给改正的手段,那扇门就只是个死胡同** |
|
||||
| `tool` | `{itemId, tool, input}` | 读类即调(出参进轨迹);写类转意向;未命中转人挂起 |
|
||||
| `ask` | `{itemId, ask, fields, basis}` | 补全卡,挂起 |
|
||||
| `human` | `{itemId, card, workOrder}` | 执行/验收工单,挂起 |
|
||||
| `listen` | `{itemId, eventType, keyFilter, deadline}` | 立监听,挂起;时限必配(缺省兜 P3D) |
|
||||
| `finish` | `{itemId, rationale}` | 宣告一项完成;非人工激活的项就地过 Active 再收 |
|
||||
| `submit` | `{rationale, fields?}` | 提交放行 → 预检 → 发验收卡挂起 |
|
||||
|
||||
**AI 不得产生终局迁移**:迁移拓扑表把 `Completed`/`Terminated` 设为死胡同,
|
||||
`Available → Completed` 不在表内 —— Agent 想跳步会吃 `IllegalTransitionError`,
|
||||
被回合循环捕获成「行动被拒」留痕,不崩(`turn-runner.ts:363-372`)。
|
||||
串的纪律(引擎强制):按序执行,谁被拒停在谁那儿(**前缀已生效不回滚**,理由喂回下一拍);
|
||||
`tool` 之后截断(出参没看见,后面是闭着眼计划的);挂起型收拍;超封顶丢弃留痕。
|
||||
拍级附带 `say{text, tag, view?}` —— 不占行动位。
|
||||
|
||||
### 8.2 说明书(narrative)
|
||||
### 11.3 局面里有什么(Agent 每拍看到的)
|
||||
|
||||
与 CMMN **同版本发布、不允许分叉**(空任一即拒装)。写法照 `activities/quote-intake/src/case-model.ts`:
|
||||
`§0 开案`(什么事、成品是什么)→ `S1..Sn`(各阶段怎么办、判断口径、宁可空着不许臆造这类
|
||||
纪律)→ `§Z 结案`(什么算办完、交出去什么)。说明书是 Agent 每一拍叙事面的第一段 ——
|
||||
**写给 AI 执行看的,不是给人读的产品文案**;判断口径要写死(「吃不准降 B,不许臆断 A」)。
|
||||
说明书 + 活动记忆选摘 + 最近轨迹(窗口 = memoryWindow;含人答的值与工具出参,旧出参截短;
|
||||
装配技能戳不进叙事)+ 当下可为(Enabled)+ **进行中**(Active —— 有开着的活就不算断点)+
|
||||
目标缺口 + 待裁量前提 + 功能工具清单(带入参键名)+ 可写台账字典(地址/主键/列)+
|
||||
本单草稿(mine:*)+ 任务数据(摊平字段在前、正式版整表殿后截断)。
|
||||
|
||||
### 8.3 DMN(能算的不问 LLM)
|
||||
### 11.4 提交与放行
|
||||
|
||||
`{drd[], tables[]}`;表 = `hitPolicy(默认 FIRST)/ inputs(name+expression)/ outputs / rules`。
|
||||
单元格谓词 = S-FEEL:`'-'` 或空串任意匹配、`'"A","B"'` 析取、`'<90'` 比较、`'[0..100]'` 区间。
|
||||
求值(`dmn.ts:33-76`):任一输入缺 → `incomplete`(带 missing);零命中 → `no-match`;
|
||||
命中留痕 `DecisionTrace{inputs, hitRules, outputs}`。决策表在 `decisionTask` 上以
|
||||
`decisionRef` 引用;**同输入必同结论、零 token** —— 规则能写死的判断一律进 DMN。
|
||||
`submit` → 落地预检(必交付物按 ledger 地址数本单草稿行;写类意向 drafted→prechecked;
|
||||
数据冲突查草稿基线)→ 预检不过**不发卡**,理由喂回下一拍 → 过了发验收卡
|
||||
(判据机检结果 + 已验证动作清单 + 可修订值拟值 + 恰选一口)→ 人裁决:
|
||||
pass → 草稿合并、意向执行(幂等键)、输出口发射、`released`;
|
||||
redo/revise → 附意见驳回,环内回转。落地失败 → 落地失败卡,任务 `blocked`。
|
||||
|
||||
## 9. 工作台视图逻辑(PRD 为语义权威)
|
||||
### 11.5 Harness 兜底(活动不用管,但要知道)
|
||||
|
||||
语义权威:`JCAI_PRD/workbench/src/app/(proto)/_data/model.ts`(统一建模)与
|
||||
`_components/{ViewRegion,TaskChat}.tsx`;WOS 平移件:`apps/web/src/features/desk-task/internal/
|
||||
{ViewRegion,TaskChat,chat-bubbles,work-order-report}.tsx`。卡族 schema 前后端同源:
|
||||
`packages/contracts/src/card.ts`。
|
||||
预算四项环外强制;停滞熔断:**连续 5 拍行动被拒**即停问模型,发卡把拒绝理由原话
|
||||
摆给人;`stallAfter` 判停滞、`nudge` 催办;模型隐藏思考已关(`reasoning:'off'`,
|
||||
每拍 1.5-5s)—— 这就是说明书必须把规则写死的原因(§7.3 纪律 3)。
|
||||
事件到达痕只记 task 层一条;对话区只投人话(narration / 回执 / 世界事件),
|
||||
机器踪迹进工作日志不进对话。
|
||||
|
||||
### 9.1 三栏分工(结构性定调)
|
||||
## 12. 自测与安装
|
||||
|
||||
| 栏 | 承载 | 铁律 |
|
||||
### 12.1 自测四段(照 `activities/quote-intake/__tests__/activity.test.ts`)
|
||||
|
||||
1. **清单合规**:过 `activityManifestSchema`;监听时限已配;L1/L2 有谓词;禁区非空、
|
||||
调人预算 > 0;**每件必交付物都有 ledger 地址**;
|
||||
2. **案例模型**:双表示同在;触达世界的项标了人工激活;常来回的项标了可重复;
|
||||
末尾有收口验收项;说明书把每一步都交代到(逐步关键词断言);
|
||||
3. **判定表逐行求值**(用 `@sfeel` 的 `evaluateUnaryTest` 写个 6 行的求值器):
|
||||
每条规则、优先级序、同输入同结论;
|
||||
4. **声明与契约对齐**(§6.2 三条线)+ 模版纪律(局面数、无脚本、放行页摆关键值)+
|
||||
技能引用的工具都在声明内。
|
||||
|
||||
案例活动没有 `run`,自测**不跑回合**(那是 Loop 的事);mock 宿主对没有 run 的活动
|
||||
如实报错,不给假 outcome。
|
||||
|
||||
### 12.2 安装两审
|
||||
|
||||
静态审(任一 error 拒装):signature-invalid / sdk-incompatible / schema-invalid /
|
||||
model-name-unbound / dangling-listener / template-has-script / environment-capability /
|
||||
dynamic-eval / ledger-type-conflict / acceptance-level-misreported;
|
||||
`tool-contract-missing` 是 **warn**(声明了系统没有 → 运行时转人,是系统的缺口不是活动的错)。
|
||||
回放审:历史成功样本仍须全过;首装以样例集替代。
|
||||
|
||||
### 12.3 试跑口径
|
||||
|
||||
试跑一律用 AI 账号(绝不派到用户工作台);起单走登记触发;逐拍对账看
|
||||
`sys.task_memory`(每条「行动被拒」都是设计欠账);测完
|
||||
`pnpm --filter @app/api reset-runs` 清运行数据(清跑出来的,留配置好的)。
|
||||
|
||||
## 13. 记忆与技能
|
||||
|
||||
- **任务记忆**只增不减;终态封卷后 `distillHooks` 蒸馏进**活动记忆**(跨任务、四层、
|
||||
渐进披露、语义引擎不读)。
|
||||
- **技能** `{id, label, applicability, method, crystallized[], knowledgeRefs[], version}`:
|
||||
常驻上下文只有目录(`label: applicability`),正文按需加载。**载方法不载权力** ——
|
||||
结晶片段调工具仍过白名单。A1 的三条(读懂一封询价 / 打标前备齐判据 / 状况标不等于去向)
|
||||
是好范本:方法里写锚点、纪律、批量惯例,不写成产品文案。
|
||||
|
||||
## 14. 排障速查表(症状 → 根因 → 处置;全部实测账)
|
||||
|
||||
| 症状 | 根因 | 处置 |
|
||||
|---|---|---|
|
||||
| **视图区** | **全部结构化交互**:HTML 交互界面(表单读写 + 图形展示)+ 工作事项 + 过程成果 | 待处理**翻页不排队**(队列是活动内部的事);五页签:待处理/沟通记录/工作成果/任务详情/工作日志 |
|
||||
| **对话区** | **只承载非结构化消息** + 决策选择题;系统事件 = 灰头像 | **气泡二元制**:纯文本气泡或完整表单卡,不允许中间态;补全/决策卡吐出期间 composer 锁定 ——「先办卡,再说话」 |
|
||||
| **任务日志** | 全部任务记忆的结构化呈现 | 归侧四类(机器产出/人产出/人裁决/世界等待)= 度量口径,零埋点 |
|
||||
| 起单后第一个项都不 Enabled,整案不动 | 案卷项 id 不是任务视图键名 | §7.1 纪律 2:改成入参/台账列名 |
|
||||
| 连续「行动被拒:…迁移非法/项不存在」,熔断卡出现 | 模型多半没错 —— 计划项建多了/嵌套了 | §7.1 纪律 1:扁平化,图上几步就几项 |
|
||||
| 一单 20+ 拍才收口 | 说明书没写一拍连做/拍数预期;或计划项过多 | §7.3 纪律 4;§7.4 拍数账 |
|
||||
| 验收机检判据全 false,可数据明明写了 | 谓词读了逐行列(后写的赢盖掉了) | §6.2 汇总列纪律:一单一行汇总对象 |
|
||||
| 提交放行被拒「必交付物缺」 | deliverable.ledger 地址没写/写错;或活动确实没写那张表 | §6.2 对齐线 1;说明书点名台账 |
|
||||
| 下游网关死路(取不到 needXxx) | 出口标量字段没有台账列承载 | §6.2 对齐线 3 |
|
||||
| 放行后正式表没有某字段 | write 的列名不在声明里(被当场拒过,轨迹里有) | 对照台账字典改列名 |
|
||||
| 补全卡答了,数据没进台账 | 系统不替 Agent 写 —— 说明书没写「答案要自己 write」 | §7.3 纪律 6 |
|
||||
| 对话区从头到尾空白 | 说明书没写 say 指引 | §7.3 纪律 7 |
|
||||
| 工作区画面不换 | view 名不在 viewTemplates 里(轨迹有「换画面失败」留痕) | §10.3 点名局面模版 |
|
||||
| 该走「受理并追问」的次要缺字段被拦单发卡 | 关思考后模型不细想边界 —— 规则没写死 | §7.3 纪律 3 |
|
||||
| 明明有工具却发卡问人 | 早期版本症状(工具清单已进提示词);若复现,查该工具是否在 declarations.tools | §9 |
|
||||
| 收口前一两拍被熔断(预算耗尽) | budget 给太紧 | §6.2 ⑦:按实测留一倍余量 |
|
||||
| 改了包重装没生效 | 同版重装 no-op | 升 packageVersion |
|
||||
| 一次人答/工具出参在日志重复多行 | 旧版症状,已修(到达痕只记一条) | 升级后消失 |
|
||||
| 机检判据全绿,而交付物本身是错的(条数对不上、单没开全) | **等式两边都由活动自报** —— 它数错一次就一起错、一起「对上」 | 一边改读基座数出来的 `域_对象_rows`(本单在那张表写下几行);会数数的交判定器/工具出参,别让模型在脑子里数 |
|
||||
| 一条 L1/L2 判据**永远绿不了**,而数据本身完全合规 | 它读的是活动自报的汇总列 —— 人补了值、基座写回了草稿层,而那个数要模型自己重算,漏算一次门就再也开不了 | 台账字段标 `required`,判据改读基座现数的 `域_对象_missing`。**机检只许读基座数得出来的数** —— 这一条已经栽过两次,第二次是十轮里三轮 |
|
||||
| 台账里某个数被「改小」了,判据随之变绿 | 那条机检判据读的正是活动自己写的数 —— **门只接受「是」,模型就去改数**(实测:missing_minor_total 从 1 改回 0) | 机检判据只许读基座数得出来的数;数不出来的,别假装机器判得了 —— 降 L3/L4 交人,并把那个数摆到放行页上让人看见 |
|
||||
| 裁量判据(L3/L4)裁成「不满足」之后反复提交 | 旧提交门要求裁量项也「为真」—— 而裁量的本义就是可以裁出不满足 | 已修:裁量项**裁过即可**提交,裁断连理由随卡交给人;活动侧把每条 L3/L4 都在 T_release 那一步裁一次,别漏 |
|
||||
| 同一条理由拒回十几次却不熔断,预算烧到 exhausted | 一拍一串行动,拒回标题带串位(「第 2 个」)—— 整条一比成了多堵墙;叙事窗只有 20 条也装不下几次 | 已修:比对削掉串位前缀 + 熔断另开 60 条窗。活动侧看到这种日志,先查是不是某个数一直不对 |
|
||||
| 人在卡上填了值,台账里那一格还是空的 | `doneWhen` 写成「问过就算完」(`asked_total > 0`)—— 卡一发它就为真,人一提交这一步当场被自动收掉,模型没轮到把答案写回来 | 收步等的是**答案落了表**:另记一个 `answered_total`,写回台账时才 +1 |
|
||||
| 「工作成果」里定稿归档的件不是人签的那份 | 收口后引擎还会跑一拍,那一拍推了固定模版名、退到「声明的第一张」,把放行页冲掉 | 已修:按剧本 `view` 推,活步收完不推;活动侧确保放行那一步的 `view` 指向验收目标件 |
|
||||
| 制表符 BOM 拆错列(空格子被右边的值填上) | 连着的 `\t\t` 是中间那格空着;模型把后面的值往前挪 | 说明书写死:每行格子数必须等于表头列数,数不对整行重数;行尾连续制表符是末尾几列都空 |
|
||||
| 数值字段被写成 0 而缺口数还记着缺 | 模型拿 0 表示「没有」 | 说明书写死:数值列没有就给 `""`(基座按列型归型,空值=不写这一列);0 是一个真实的数量 |
|
||||
| 同一颗料在成果表里出现两行 | PN 是主键,补写时换了个写法(补全了后缀)就是新开一行 | 说明书写死:回填按卡上那一列的 PN 对行;发现抄错交给人,别用新 PN 再写一行 |
|
||||
| **判据拦住了,而模型改不动** —— 同一条理由连拒到熔断 | 门后没有路:办错的那件事没有对应的动作 | §11.2 有 `drop`(撤本单草稿层的一行);拒回的话必须指向**一个做得到的动作**,指错方向比不给指引更糟 |
|
||||
| 行数判据对不上,模型只会改数字 / 重写 / 再提 | 拒回只给了两个数,「多的是哪一行」它数不出来 | 已修:读数连**主键名单**一起摆(`域_对象_keys`,封顶 12 行)。活动侧照名单撤那一行,别去改数 |
|
||||
| 「要先 start」而项当下是 Completed,start 又被拒 | 拒回的话没按状态分开说,两堵墙对着撞 | 已修:收掉/停用的项改说「已办完,挂到当下这一步」并列出可为的项;start 已收的项无事发生 |
|
||||
| **收了步却过不了验收**,回头补写一张已经收掉的表 | 收步判据比验收判据松(读自报的数,验收读基座数的行) | 两处比**同一件事**:收步也读 `域_对象_rows`。松出来的那一截就是返工 |
|
||||
| **该经过人的一步整个没发生**,人一眼没看过就被叫去验收 | 「经过人」被写成了某一步收步条件里的半句(「缺东西才叫人」)—— 不缺就自动收了 | §7.1 纪律 3.5:人的门单独建成一项 + 人工激活 + 不给 `doneWhen`,下一步的入场前提取一个只有人回复才产生的计数列 |
|
||||
| 一行没写的那张表,判据 `域_对象_rows = 0` 却过不去,连撞五拍熔断 | 读数从前只数「写过的表」——**没写过 ≠ 写了 0 行**,而 S-FEEL 里未绑定的名字参与比较求值为假 | 已修:按 `declarations.ledger` 把每张声明过的表先铺成 0。活动侧遇到「某类为 0 就卡住」的判据,先查读数在不在 |
|
||||
|
||||
**三类同级唤醒事件**:①对话输入 ②外部事件(数据/时间/控制)③页面交互(含工单回传)。
|
||||
## 15. 待实现与已知不一致
|
||||
|
||||
### 9.2 工作事项(WorkItem):人类工作清单
|
||||
|
||||
**AI 决定、只增不减**,只有两类(决策不是工作事项):
|
||||
|
||||
| 类 | 字段(PRD `model.ts:106-133` ↔ WOS `card.ts:279-350`) | 用途 |
|
||||
|---|---|---|
|
||||
| **执行** | `ask`(工作要求)/ `steps[]`(作业指导书分步要点)/ `standard?`(完成标准)/ `basedOn?`(报告正文取自哪份过程成果 —— 人要在数据上作判断,就得把数据摆在表单旁)/ `blocks?` / `fields[]`(回填表单,字段可带 **ai 拟值**)/ `submitLabel?` | 做 AI 的工作要求 —— **原「补全」并入此类**;回填报告用活动持有的 HTML 模版(§9.3);⚠️ 工具未命中转人也走这张卡(第 12 章②) |
|
||||
| **验收** | `deliverable / criteria(L1-L5)/ actions?(待落地动作意向,读/写)/ portChoice? / passLabel?` | 收 AI 的成果:放行 / 驳回(必附意见) |
|
||||
|
||||
对话区另有三张卡:**决策**(选择题,recommend + 后果说明)、**fill 补全卡**(就地要几个
|
||||
小信息;「没法填的理由」是另一条必走之路)、**rate 评价卡 —— 暂不做**(schema 在,不接)。
|
||||
|
||||
### 9.3 HTML 报告模版:活动持有,一单一模版贯穿始终
|
||||
|
||||
**活动的每一张关键表单,都必须有对应的 HTML 报告模版** —— 模版是**活动自己持有的资产**
|
||||
(`assets.viewTemplates`,随包版本冻结),不是系统临场生成的。三条硬规矩:
|
||||
|
||||
1. **执行与验收都要配**:执行工单的回填报告、验收工单的成果报告,各有其模版 ——
|
||||
人办工单时看到的不是一堆裸字段,是一份**已排好版的报告**(AI 拟值已填、可编辑,
|
||||
数据摆在表单旁边,`basedOn` 引用的过程成果就嵌在报告正文里);
|
||||
2. **从待处理工单到工作成果报告是同一张模版**:工单在办时,卡面 = 报告模版 +
|
||||
作业指导 / 验收判据;办结后**指导与判据卸下,只剩报告** —— 它原样进「工作成果」
|
||||
页签成为归档件。不许为「在办」与「归档」各做一张模版:两张迟早长歪,
|
||||
人回头核对时对不上账。语义出处:PRD ViewRegion 头注「一张工单 → 卡组一
|
||||
(HTML 报告 · AI 已填好、可编辑)+ 卡组二(作业指导 / 验收判据);办结后只剩
|
||||
报告卡 —— 指导与判据是办理时的参照,不进归档件」;
|
||||
3. **一个局面一张模版**(受理中/已分类/待放行……):局面变了换模版,
|
||||
不在一张模版里堆条件分支。
|
||||
|
||||
推送两条路:`pushView(blocks)`(结构化块,11 型:callout/kv/badges/table/bars/donut/
|
||||
mail/cards/waiting/files,`card.ts:103-201`)与 `pushHtml(html)`(HTML 模版)。
|
||||
模版纪律(`activity-sdk/src/view.ts`):容器是 `sandbox=""` iframe,**无脚本执行权、
|
||||
样式必须自带**;数据由基座推送时填(`data-slot` 取值、`data-rows` 逐行展开);
|
||||
SDK 给了 `viewShell / kvCard / tableCard / noteCard / stagesCard` 拼装件,照
|
||||
`activities/quote-intake/src/views/report.ts` 用。
|
||||
|
||||
### 9.4 运行时的卡怎么到人(✅ 链路)
|
||||
|
||||
活动 `issueWorkOrder`/`issueCard` → `card-rules` 机检(裸调用拒发/字段齐备)→ 调人预算
|
||||
环外扣账 → 落 `sys.work_order` / `sys.chat_card` → 到达面两路:收件人是负责人 →
|
||||
**直接长在任务对话流**;他人 → 系统消息要素。人答卡 → 校验(§7.4)→ human 事件 →
|
||||
任务 `deliver` → 引擎下一拍收裁决。**在途验收先于一切**:有未裁决的验收卡时,
|
||||
回合原地等,不重发卡、不问模型(`turn-runner.ts:274-287`)。
|
||||
|
||||
## 10. 记忆与技能
|
||||
|
||||
- **任务记忆**:只增不减;`memory.window(n)` 取最近轨迹(n = `declarations.harness.memoryWindow`);
|
||||
终态封卷后不能再追加;蒸馏钩子(`distillHooks`:on terminal / on milestone + prompt)
|
||||
在封卷后跑,失败不掀翻终态。
|
||||
- **活动记忆**:跨任务沉淀,四层;`memory.recall({layers?, limit?})` 选摘;**语义引擎不读**
|
||||
(它只进叙事面)。
|
||||
- **技能**(`manifest.ts:117-131`):`{id, label, applicability, method, crystallized[],
|
||||
knowledgeRefs[], version}`。**渐进披露**:常驻上下文只有目录(`id label:applicability`),
|
||||
正文 Agent 说要才加载 —— 一次塞全部正文会把局面淹掉。**载方法不载权力**:技能里的
|
||||
结晶片段调工具,仍过活动自己的白名单,技能不带来新授权。
|
||||
|
||||
## 11. 开发流程与自测
|
||||
|
||||
### 11.1 本地开发(mock host)
|
||||
|
||||
`createMockHost({contract, tools?, models?, ledger?, memory?})` → `{base, recording, run}`。
|
||||
`recording` 十通道:`views / htmls / says / workOrders / cards / toolCalls / intents /
|
||||
listeners / llmCalls / writes` —— 断言活动这一回合做了什么。工具未 mock 回
|
||||
`{ok:false, code:'not-mocked'}`(活动要能按失败路径走);模型未 mock 直接抛。
|
||||
|
||||
### 11.2 自测四段(照 `activities/quote-intake/__tests__/activity.test.ts`)
|
||||
|
||||
1. **清单合规**:过 `activityManifestSchema`;监听时限已配;L1/L2 判据可机检;禁区非空 + 调人预算 > 0;
|
||||
2. **案例模型与模版**:CMMN 双表示齐备;触达世界的项全标 manualActivationRule;末尾有收口验收项;**关键表单逐张有报告模版**(执行/验收都配、无脚本、待处理与成果同模版);
|
||||
3. **DMN**:逐规则求值断言(同输入必同结论、优先级、任意格);
|
||||
4. **入口**:mock host 跑 run —— 各局面的 outcome、推了什么视图、说了什么话、守卫真的拦
|
||||
(无脚本/裸调用/时限必配)。
|
||||
|
||||
### 11.3 安装两审与版本纪律
|
||||
|
||||
- **静态审 11 code**(`static-audit.ts`):signature-invalid / sdk-incompatible / schema-invalid /
|
||||
tool-contract-missing(⚠️ 待降级)/ model-name-unbound / dangling-listener /
|
||||
template-has-script / environment-capability / dynamic-eval / ledger-type-conflict /
|
||||
acceptance-level-misreported。任一 error 即拒装。
|
||||
- **回放审**:历史成功样本现在必须仍过(regressions 硬门);首装无历史以样例集替代。
|
||||
- **版本纪律**:安装恒新增、同版重装 no-op(**改了内容必须升 packageVersion**,否则静默
|
||||
丢失 —— 实测踩过);卸载只置状态不删行;资产(业务模型/DMN/技能)播下一版不覆盖,
|
||||
覆盖只能是显式治理回滚。
|
||||
- 白名单授权:装入 ≠ 能调 —— 授权是另一步(⚠️ 现只有种子常量表,第 12 章⑤)。
|
||||
|
||||
## 12. 待实现清单
|
||||
|
||||
按对四条产品语义的阻塞度排序。实现时逐条销账,销一条更新本表。
|
||||
|
||||
| # | 事项 | 现状(证据) | 目标 |
|
||||
| # | 事项 | 现状 | 目标 |
|
||||
|---|---|---|---|
|
||||
| ① | **写类意向落地断链**(阻塞语义①) | `submitForRelease`/`precheck` 无调用方(`release-service.ts:119-141` 全仓无引用);活路径 `raiseAcceptance` 绕过预检;`land()` 只执行 `prechecked`(`:156`)→ 意向永停 `drafted`,写类动作永不执行;验收卡 `actions` 恒空、`criteria` 恒为硬编码单条 L5(`engines.ts:301`);幂等键两套(`engines.ts:360` vs `precheck.ts:93-95`) | 提交放行走 `submitForRelease`(预检 → 意向置 prechecked → 已验证动作清单与契约 acceptanceCriteria 上卡);幂等键统一为 `${taskId}:${intentId}` |
|
||||
| ② | **工具未命中转人执行卡**(阻塞语义③) | 未注册抛 `ToolNotFoundError`(`tool-invoker.ts:43`);静态审 `tool-contract-missing` = error 拒装(`static-audit.ts:108-111`);工具契约无面向人的指导段 | 未注册 → 生成执行工单(卡面 = 工具 `guide` 段 + 输入数据 + 按输出 Schema 生成的回填表单,回传值即工具出参,幂等键沿用);静态审降级为 warn;工具契约加 `guide` 字段。未授权仍报错不转人 |
|
||||
| ③ | **验收报告修订链两端断** | 验收工单 `fields` 前端不渲染(`work-order-report.tsx:75`)、`values` 恒传 `{}`(`chat-bubbles.tsx:500,519`);驳回 UI 只有 redo,引擎把 redo/revise 塌成 reject(`turn-runner.ts:432`) | fields 渲染 + values 回传(AI 拟值可改);驳回两式贯通到引擎 |
|
||||
| ④ | **受理分支误吞验收裁决** | `deliver` 的受理分支判 `awaiting-human && kind==='human'` 不看 cardId(`task-service.ts:262-264`);「没法填」带缺口开跑的单再遇验收驳回,human 事件被误判成补全 | 受理分支按补全卡 cardId 匹配 |
|
||||
| ⑤ | **治理台白名单授权口** | 唯一入口是种子常量表(`seed-mvp.ts:109-117`),无 API 无 UI;安装不授权 | 治理台按活动配工具白名单(读走 read、改走 govern) |
|
||||
| ⑥ | `defineActivity` 去 paradigm | schema 已删,SDK 仍要求(`define.ts:21`);六包里还有 `'flow'` 字样 | SDK 签名去掉;六包清理 |
|
||||
| ⑦ | dist 构建产物真编译 | `dist/manifest.json` 是过期 flow 形状、`index.js` 是源码文本拷贝(债 M5-3) | build-activities 真编译 + manifest 同步 |
|
||||
| ⑧ | 缺省单拍路径无工具总线 | 只有 `JCAI_AGENT_OPEN_LOOP=1` 走总线(`engines.ts:464-491`);缺省 tool 是自由字符串 | 工具调用逻辑实现时统一走总线(开环转正或单拍接总线) |
|
||||
| ⑨ | `preauthorizedActions` 死代码 | `dispositionOf`/`ToolInvoker.disposition` 无调用方;两包 manifest 里声明了但不生效 | 暂不启用:manifest 里**不要写**;将来要么接线要么删字段 |
|
||||
| ⑩ | `SchemaValidator` 双死依赖 | task 与 process 都声明了 `schemas` 端口但不再调(宽进后退役) | 清理端口与注入 |
|
||||
| ⑪ | **评价卡(rate)暂不做** | schema 在(`card.ts:424-438`),链路未接 | 用户定:暂不做 —— 不设计、不接线 |
|
||||
| ④ | 受理分支误吞验收裁决 | `deliver` 的受理分支判 `awaiting-human && human` 不看 cardId(`task-service.ts`);「没法填」带缺口开跑的单再遇验收驳回会被误判成补全 | 受理分支按补全卡 cardId 匹配。**开发侧规避**:带缺口开跑的单尽早在环内补齐缺项 |
|
||||
| ⑤ | 治理台白名单授权口 | 只有种子常量表(`seed-mvp.ts`),无 API 无 UI | 治理台按活动配白名单 |
|
||||
| ⑦ | dist 真编译 | `index.js` 是源码文本拷贝(债 M5-3) | build-activities 真编译 |
|
||||
| ⑧ | 单拍路径的工具调用缝 | 工具清单已进提示词(只给功能工具);调用仍是自由字符串经 ToolInvoker,未走总线编译 | 开环转正或单拍接总线 |
|
||||
| ⑨ | `preauthorizedActions` 死代码 | 无调用方 | manifest 里**不要写** |
|
||||
| ⑩ | `SchemaValidator` 双死依赖 | task/process 声明了不再调 | 清理 |
|
||||
| ⑪ | 评价卡(rate) | schema 在、链路未接 | 用户定:暂不做 |
|
||||
|
||||
其余记录在案的小不一致(不阻塞,修到顺手时清):`declarations.tools[].version` 无消费者;
|
||||
`contract.resources.tools` 双份声明;replay 模式跳过白名单检查(`tool-invoker.ts:58-61`
|
||||
在 loadContract 之前);注册路由造不出纯工具;工具 `retries` 声明了不重试;
|
||||
`activity-registry/contract.ts:54`「两维管理」注释陈旧;`chat-bubbles.tsx:6` /
|
||||
`work-order-report.tsx:6`「当前未挂载」注释已过期(实际都已挂载)。
|
||||
小不一致(不阻塞):`declarations.tools[].version` 无消费者;`contract.resources.tools`
|
||||
双份声明;replay 模式跳过白名单检查;注册路由造不出纯工具;工具 `retries` 声明了不重试;
|
||||
转人答案不经出参 Schema 校验(债 M6-1);进程重启后动作级 trace 丢失(债 M6-2)。
|
||||
|
||||
Reference in New Issue
Block a user