Archived
给「任务活动开发 agent」沉淀的两份操作手册,基准是用户验收过的 A1(quote-intake@42, 三条形状不同的案例 released、被拒 0)。 活动开发规范.md 第三版,按四条轴重组: · 解耦机制 —— 活动与系统之间只有安装通道;三份声明=三个授权面; 边界纪律「别替下游产出」(随令牌流向下游的只有验收过的成果); · Harness 契约 —— 台账三写一撤与字段四标记(required/expect/tally/auto)、 基座读数全表与机检纪律(三次违反史逐条记账)、一路的门与闸 (起单宽进/提交闸两端/提交门/放行门哨兵/熔断)、拒回话术三分、五类卡、 phases/三色日志/一轮一句、放行落地; · Loop —— 局面组成、行动十四型全表、串纪律、人工激活与 start (「话说的范围要盖住门拦的范围」)、doneWhen 自动收步; · 活动怎么写 —— 清单六区(ledgers/editableAtRelease/判据措辞/内部一致性不上卡)、 建模十纪律(哨兵 ifPart、阶段全覆盖补进)、剧本 steps 六字段与 doneWhen 三坑、 guide 写作十条(一单一卡、卡骨架照抄、纯文本)、三常备案例与验收口径。 排障速查表并入这一轮全部新账(14 行新增)。 HTML报告生成规范.md 初版(用户点名单独成册,并强调报告与表单的关系): · 一份 HTML 三个身份:件(看)/表单(改)/归档件(存)—— 同一份,分开必分叉; · 报告与卡的关系立成硬规矩:逐行数据归件、整单级归卡;编辑在件、提交在卡; 同一个数管三处(件上红带/卡上闸/放行门 L1 同源);保存链路与三条实测教训; · 绑定语言全表、不算不判(按值上色/按条数分宽/按数开关)、编辑态四件套 (闸/孪生件/增删行/缺项红带)、PRD 平移法、浏览器走查清单、陷阱表。 CLAUDE.md 文档权威地图指向两份新版。五道门禁全绿(975 passed)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JsTDjoHovERXyy6Wmw1ALp
485 lines
34 KiB
Markdown
485 lines
34 KiB
Markdown
# 活动开发规范(任务类活动)
|
|
|
|
> 2026-08-02 第三版。基准:`activities/quote-intake`(A1 询价受理,`@42`,**用户验收通过** ——
|
|
> 三条形状不同的案例端到端 `released`、行动被拒 0)。目标读者:**开发活动的 AI(任务活动
|
|
> 开发 agent)或人** —— 读完这一份,你应当能独立把一个 SOP 节点做成能 `released` 的
|
|
> 案例活动,不必读 `apps/api` 的源码。
|
|
>
|
|
> 本版按四条轴重组:**解耦机制**(§2)· **Harness 契约**(§3)· **Loop**(§4)·
|
|
> **活动怎么写**(§5)。报告模版单独成册:`docs/HTML报告生成规范.md`(与本文互为姊妹篇,
|
|
> 报告与表单的关系在那份里立成硬规矩)。
|
|
>
|
|
> **本文与代码冲突时以代码为准**,发现即改本文。相关:`docs/编码规范.md` §8、
|
|
> 各模块 `CONTRACT.md`、`docs/debt.md`(欠账)。历史语义源已归档 `docs/archive/`。
|
|
|
|
---
|
|
|
|
## 1. 世界观:一个活动是怎么跑起来的
|
|
|
|
**活动 = 任务的定义。** 编排引用一个活动,运行时长出一个任务(恰一名负责人)。
|
|
活动在系统外开发、经安装通道入库,版本恒新增、同版重装 no-op、在途任务按启动时版本跑完。
|
|
|
|
**活动没有代码逻辑。** 行为全部写在清单里:案例模型(CMMN)+ 逐步剧本(steps)+
|
|
说明书(narrative)+ 判定表(DMN)+ 技能 + 台账声明 + 报告模版。
|
|
「系统不执行代码即知包为何物」。`src/index.ts` 只是被静态审扫描的产物文本,
|
|
`defineActivity({ name })` 即可。
|
|
|
|
**推进由 Loop 逐拍驱动**(一拍 = 一次模型调用):
|
|
|
|
```
|
|
语义引擎发布局面 ──> Agent 决断一串行动 ──> 引擎逐个执行 ──> 留痕 ──> 下一拍
|
|
· 当下可为(Enabled,标「须你启动」) (1-6 个) 被拒停在谁那儿
|
|
· 进行中(Active) tool 后截断
|
|
· 当前这一步的剧本(guide 摊开,其余步各留一行) 挂起型收拍
|
|
· 目标缺口 + 轨迹 + 工具清单 + 台账字典 + 基座读数
|
|
```
|
|
|
|
**拍是按模型调用计费的** —— 一切设计围着「少一拍是一拍」:一拍连做、批量工具入参、
|
|
规则写死不留给模型判。A1 的账:六步的活,常路 **9-11 拍**收口(第一版 21 拍 ——
|
|
差距全在设计,不在模型)。
|
|
|
|
**人只在责任点与放行点出现**:确定交系统(判定表/基座读数),临场交 AI(编排、拆解、
|
|
落表),落地交人(审定、放行)。整套机制的反复主题只有一句:
|
|
**该由人亲手落下的,不许引擎或 AI 悄悄替它发生;能由数据算出来的,不许活动自报。**
|
|
|
|
### 1.1 四条铁律(违者拒装或被引擎拒)
|
|
|
|
| # | 铁律 | 机制承载 |
|
|
|---|---|---|
|
|
| ① | **只读世界**:活动只能读台账、调读类工具、问模型、发卡问人 | 产物文本静态审(`process.env`/`fetch`/`node:*`/`eval` 命中即拒装);depcruise 双向隔离 |
|
|
| ② | **写只有草稿与意向**:写台账 = 草稿层(不抛事件);外部动作 = 登记动作意向(入参冻结 + 幂等键)。登记那一刻世界上什么都没变 | `write` 只落草稿;写类工具自动转意向 |
|
|
| ③ | **终局唯放行**:任务完成的唯一出口是人验收通过 —— 那一刻才草稿合并、意向执行、输出口发射 | `submit` = 发验收卡挂起;放行落地见 §3.7 |
|
|
| ④ | **活动不自转**:推进由引擎逐拍驱动;预算与熔断由 Harness 环外强制 | 迁移拓扑把 Completed/Terminated 设为死胡同 |
|
|
|
|
---
|
|
|
|
## 2. 解耦机制:活动与系统之间只有一条通道
|
|
|
|
### 2.1 安装通道(唯一入口)
|
|
|
|
```
|
|
activities/<id>/src/ ──(pnpm activities:build)──> dist/{manifest.json,index.js}
|
|
──(pnpm activities:install)──> 库(经安装通道读文件,不经 import)
|
|
```
|
|
|
|
- 新包在 `tools/build-activities.ts` 的 PACKAGES 与
|
|
`apps/api/src/app/seed-activities.ts` 的 `REAL_PACKAGES` 两处登记。
|
|
- **别用 `db:seed` 装活动**:它重写整套治理台配置。
|
|
- **版本纪律**:改了任何内容必须升 `identity.packageVersion` —— 同版重装静默丢失(踩过)。
|
|
卸载只置状态不删行;**同名旧槽位要退役卸载**(库里两个同名活动会骗人)。
|
|
- 撤掉一张台账声明**不会 drop 物理表**(物化只增不破坏)—— 历史行还在库里,要清得单独迁移。
|
|
|
|
### 2.2 import 白名单与静态审
|
|
|
|
活动包**只准 import `@sdk` / `@contracts` / `@sfeel`**;depcruise 双向隔离
|
|
(activities 不进 apps,apps 不 import activities)。产物文本黑名单:
|
|
`process.env`、`require(`、`node:fs/net/child_process`、`fetch(`、`XMLHttpRequest`、
|
|
`eval(`、`new Function(`。视图模版含 `<script` 或外链即拒。
|
|
|
|
### 2.3 声明即授权(三个授权面)
|
|
|
|
| 声明 | 授权了什么 | 越界的下场 |
|
|
|---|---|---|
|
|
| `declarations.ledger` | 运行时**只准写**这里声明的地址与列名;**人从件上改回来的值也走同一道门**(§3.1) | 编出来的列当场拒;未声明地址的保存一格不落 |
|
|
| `declarations.tools` | Agent 可调的工具名单 | 未白名单授权报错(越权不转人) |
|
|
| `assets.viewTemplates` | 工作区能推的画面 | 模版名对不上,「换画面失败」留痕、屏上不动 |
|
|
|
|
静态审还核:**谓词里的名字必须是声明过的台账列或基座读数**(`域_对象_rows` /
|
|
`_missing` / `_gaps` / `_<列>_<值>` 这些基座读数在审的白名单里)。
|
|
|
|
### 2.4 边界纪律:别替下游产出
|
|
|
|
**随令牌流向下一个节点的,只有工作成果验收里的内容**(出口载荷 = 已落地的事实)。
|
|
下游活动拿到之后按自己的口径归一需要的数据。
|
|
|
|
实测账(2026-08-02,用户当场定):A1 从前逐条开「替代评估请求 / 询价请求」交给 A2/A3 ——
|
|
那是**替下游把它的输入先摆好**,而下游本来就要归一一遍:同一份事实两处各存一份,
|
|
改一处就分叉,而分叉之后没人看得出以哪份为准。两张表连同派单判据、报告明细表一并退休。
|
|
|
|
跨活动共享事实只有两条路:**输出口载荷**,或**共享域**(`shared`/`customer`/`market` 等
|
|
主数据域)。活动自持域用活动名(`quote_intake`)。
|
|
|
|
---
|
|
|
|
## 3. Harness 契约:基座替你做什么
|
|
|
|
这一章列的是**你能依赖、也必须依赖**的基座行为 —— 活动侧不要重做它们,
|
|
更不要绕开它们。
|
|
|
|
### 3.1 台账:三写一撤 + 归型 + 字段四标记
|
|
|
|
- **写三分**:`systemWrite`(世界已发生的事实,即时抛事件)/ `draftWrite`(任务草稿层,
|
|
读己之写,不抛事件)/ `releaseWrite`(放行合并,此刻抛事件)。三个方法不是一个带 flag 的方法。
|
|
- **`draftDrop`**:撤掉本单草稿层的一行 —— 不是第四种写(正式版碰不到、不抛事件)。
|
|
它存在的理由:门拦住一件办错的事却不给改正的手段,那扇门就只是个死胡同。
|
|
- **归型前移**:`draftWrite` 按声明列型归(空值 = 不写这一列、数值/布尔字符串转真类型、
|
|
归不了当场拒)。**人手改的值与 AI 写的值过同一道门**。
|
|
- **字段四标记**(`fieldDeclarationSchema`):
|
|
|
|
| 标记 | 语义 | 基座据此产出 |
|
|
|---|---|---|
|
|
| `required` | 这一列缺了就是活没干完(验收标准;不建 NOT NULL) | `域_对象_missing` / `_missing_keys`;执行卡提交闸;件上缺项红带 |
|
|
| `expect` | 缺了不拦单,但那一问不能省(客户该给而没给的) | `域_对象_gaps` / `_gaps_keys`;L4「选填缺口」谓词读它 |
|
|
| `tally: ['A','B','C']` | 分类列,给出取值域 | `域_对象_<列>_<值>` 按值现数(没出现的值也铺 0) |
|
|
| `auto` | 内部流水号,**不问人、不让模型编** | 写入那一刻按「同组其余主键值-两位序号」生成(`CU-9001-01`);录入卡上那一格不出现,另摆一句「由系统按顺序生成」 |
|
|
|
|
注意 `expect` 的边界:**我方写的注(如备注列)不算** —— 标了它 `_gaps` 恒大于 0,
|
|
那条判据永远绿不了,门就废了。
|
|
|
|
### 3.2 基座读数(机检的唯一合法数源)
|
|
|
|
任务视图顶层永远铺着这些数,**全部由基座现算,模型碰不着**:
|
|
|
|
| 键 | 含义 | 备注 |
|
|
|---|---|---|
|
|
| `域_对象_rows` | 本单在这张表写了几行 | 声明过的表**铺 0**(没写过 ≠ 写了 0 行 —— S-FEEL 未绑定名字参与比较为假,踩过) |
|
|
| `域_对象_keys` | 本单写的行的主键名单(JSON) | 封顶 12 行;拒回时摆出来,「多的是哪一行」一比便知 |
|
|
| `域_对象_missing` / `_missing_keys` | 缺必填几行 / 缺在哪几行哪一列 | 名单用中文列名 |
|
|
| `域_对象_gaps` / `_gaps_keys` | 缺选填几行 / 哪几行 | 同上 |
|
|
| `域_对象_<列>_<值>` | 该分类值现有几行 | 取值域声明给,数得全 |
|
|
|
|
**机检纪律(最重要的一条,已被违反三次)**:
|
|
**L1/L2 判据、收步判据、放行门哨兵,一律只许读基座数得出来的数。**
|
|
活动自报的汇总列(`line_total` 这类)只能出现在等式的**一边**,或只做提示。
|
|
|
|
三次违反史,每次都是同一个死法 ——「那个数要模型自己重算,漏算/改小一次,门就废了」:
|
|
① `missing_key_total` 自报 → 人补了值门不开,十轮里三轮熔断;
|
|
② `missing_minor_total` 自报 → 门只接受「是」,模型把 1 改回 0,台账从此写着假数;
|
|
③ `untyped_total` 自报 → 模型跳过打标写个数就想过门(判据现读
|
|
`_type_tag_a + _b + _c = _rows`,两边都是基座出的数)。
|
|
|
|
### 3.3 门与闸(从起单到放行,一路的拦截点)
|
|
|
|
| 门 | 在哪 | 规则 |
|
|
|---|---|---|
|
|
| **起单宽进** | `process.start` | 不按 Schema 拒缺项;缺 required 顶层键 → 发补全卡(不占预算)。「没法填」给理由即带缺口开跑 |
|
|
| **执行卡提交闸** | 卡面 `gate` + 服务端 | 卡上绑了台账表的执行单,必填缺着按不下提交(`{ready,why,blanks}`,与放行门 L1 **同一个数同一套算法**);**服务端在收下回传之前也把一遍**(前端灰按钮只是给人看的)。算不出来(卡没绑表)不给闸 —— 没有依据的时候不拦人 |
|
|
| **提交门**(`submit` 预检) | engines | 机检(L1/L2)**必须为真**;裁量(L3/L4)**裁过即可**(裁成「不满足」照样能提交,裁断连理由随卡交给人 —— 一条只接受「是」的判据是一句教模型说谎的提示);**L4 带谓词时谓词说了算**(谓词真 = 已达标,模型那句相反的理由一并丢掉;谓词假才轮到裁量);必交付物按 ledger 地址数本单草稿行 |
|
|
| **放行门入场哨兵** | CMMN sentry | **必须有 ifPart,且读基座数、认「办完了没有」** —— 只有 onPart 的哨兵等于「有人写过一笔就开门」,模型写个 `false` 就推开了(实测:跳过三步直达放行门) |
|
|
| **熔断** | Harness | 连续 5 拍行动被拒 → 停问模型,发熔断卡把拒绝理由摆给人(比对削掉串位前缀、另开 60 条窗) |
|
|
|
|
**拒回话术纪律**(拒回的话是模型下一拍的行动指南,指错方向比不给指引更糟):
|
|
- 机检差数,先分清**数不对**(改汇总列)还是**行不对**(照主键名单 drop/补写);
|
|
- 判据点名的表**这一单一行都没写**(`_rows` = 0)→ 直说「那一步还没办,回去办了再来」——
|
|
这时数与行都是对的,指「改数/改行」是指向两扇打不开的门(实测五拍撞死);
|
|
- 已收掉的项按状态分开说(「挂到当下这一步」+ 列出可为的项),别说「先 start」——
|
|
start 一个 Completed 项同样被拒,两堵墙对着撞。
|
|
|
|
### 3.4 人类工具:五类卡
|
|
|
|
| 类 | 行动 | 形态与硬规矩 |
|
|
|---|---|---|
|
|
| **执行** | `human` + `workOrder:'execution'` | 卡 = 查看工单钮(四字粗体,与验收卡同形)+ ask + **作业指导书**(`steps`+`standard`,与判据同一套版面、不画对勾)+ 依据 + 表单 + 提交闸。指导书写「哪几格必须有、哪几格值得追」,不写「怎么核表」。**`steps`/`standard` 是纯文本** —— 别写 `**` 这类记号,它们不会变粗体,只会原样摆在人眼前 |
|
|
| **验收** | `submit` | 判据表(读时现算 —— 人在件上补一格判据跟着变;**L5 不上卡**:那张卡本身就是那道关,列进去等于摆一条恒红的项)+ 落地动作 + 可修订值 + 出口(算得出摆 `routedPort` 告知,算不出退 `portChoice` 让人选)。**办结之后换成落地结果**(landing):按交付物点名落了几行、转出哪一口、带过去哪些字段;一个口都没发直说「到此为止」 |
|
|
| **补全** | `ask` | 对话卡就地填;`fields` 空即拒;能预填的带 `value` 预填 |
|
|
| **决策** | `decide` | 选择题(≥2 个选项,少了拒发);人点哪个进轨迹 |
|
|
| **录入** | `entry` | **人按下确认那一刻基座直接写库,AI 不经手** —— 建档这类主数据用它。可多段(`also`):建客户 = 公司档 + 联系人档,一张卡两段、确认一次落两行(值按 `段号.列名` 隔开)。卡面摆表的**人读名**(不摆 `customer.profile`);`auto` 列不进表单、另摆一句说明 |
|
|
|
|
通用规矩:
|
|
- **一单同一张卡只发一次**。人交回来之后不论还缺什么都不许再发 ——
|
|
必填有提交闸兜着(卡回来时必填一定齐);**人留空交回来 = 对方给不了,那也是一个答案**:
|
|
如实记账、缺口照实留着、把这一步收掉往下走。
|
|
- 发卡即挂起,回传即续跑;在途验收先于一切;人答的值出现在下一拍轨迹里,
|
|
**看到了要自己 write 落台账**(绑台账的表除外 —— 基座直写)。
|
|
- 调人预算(`humanCallBudget`)环外扣账。
|
|
|
|
### 3.5 视图渲染(概要,详见《HTML报告生成规范》)
|
|
|
|
- 模版**活动持有**,一单一模版贯穿始终(硬规矩);剧本每步的 `view` 定推哪张。
|
|
- 渲染 = 纯取值绑定(`data-slot`/`data-rows`/`data-blank`/`data-class-of`/`data-col`);
|
|
**模版不算不判**,基座算好的可以摆。
|
|
- 待处理那一屏**报告即表单**:可改格子渲孪生 `<input>`,默认锁定、按「编辑」才开,
|
|
能增删行;保存按地址落草稿层(授权面 = 台账声明),**基座随即重渲**(repush)——
|
|
类徽、分布条、去向这些算出来的东西跟着变。
|
|
- 报告与卡的分工、同源读数、保存链路 —— 全在姊妹篇 §2,那是这套里最容易做错的一章。
|
|
|
|
### 3.6 进度条、日志、对话区
|
|
|
|
- **phases 是给人看的刻度**(引擎一眼不看):`{name, items[]}[]`,
|
|
**除放行外每个计划项恰属一个阶段**(漏一项,人办那件事时进度条已经走过去了 ——
|
|
实测:S3 漏在名单外,人对着审定卡而条上写着「判定去向」)。收口那一格由基座统一加
|
|
(「放行收口」),总格数 = 声明数 + 1。
|
|
- **日志三色**:负责人绿 / 智能体蓝 / 系统灰;日志与卡面**不摆系统内英文名**
|
|
(状态名、表名、列名、口名一律换人读名)。
|
|
- **对话区一轮一句**:`say` 只在交出控制权那一拍(ask/human/decide/entry/listen/submit)
|
|
才摆到人面前,别的拍进工作日志。到了要找人的那一拍,say 写「这一轮办完了什么、
|
|
为什么现在需要他」。顺利收尾对话区**不再出一条**(落地结果那一屏已说全);
|
|
没顺利收尾的出一条人话(「这一单受阻待人」)。
|
|
|
|
### 3.7 放行落地(人按「通过」之后发生什么)
|
|
|
|
草稿合并进正式版(逐表计数留结构化痕)→ 动作意向执行(幂等键;失败发落地失败卡,
|
|
任务 `blocked`)→ 输出口发射:**载荷 = 已落地的事实**(`landedFacts`,放行后草稿层已清,
|
|
不从作用域猜)→ `released`。selective 出口按各口 `when` 谓词序求值,首个命中即出 ——
|
|
**去向是算出来的,不是人选的**;人签的字确认的是算出来的那一口。
|
|
|
|
---
|
|
|
|
## 4. Loop:一拍怎么跑(Agent 视角)
|
|
|
|
### 4.1 局面里有什么
|
|
|
|
当前这一步的剧本(guide 整段摊开,其余步各留一行)+ 说明书(整单的事)+
|
|
活动记忆选摘 + 最近轨迹(含人答的值与工具出参)+ 当下可为(标「须你启动」/「裁量项」)+
|
|
**进行中**(有开着的活就不算断点)+ 目标缺口 + 待裁量前提 + 工具清单(带入参键名)+
|
|
可写台账字典(地址/主键/列/列型)+ 本单草稿(`mine:*`)+ **基座读数** + 任务数据
|
|
(摊平字段在前、整表殿后截断 —— 但剧本 `reads` 声明的表**置前不截断**)。
|
|
|
|
### 4.2 行动十四型(一拍一串,封顶 6)
|
|
|
|
| 行动 | 形状 | 语义 |
|
|
|---|---|---|
|
|
| `start` | `{itemId}` | 启动标了人工激活的项;**已 Active / 已收掉再按 = 无事发生** |
|
|
| `plan` | `{itemId}` | 裁量项入场 |
|
|
| `disable` | `{itemId, reason}` | 此单不走这条 |
|
|
| `judge` | `{criterion, satisfied, rationale}` | 裁语义性前提 / L3-L4 判据;criterion 照判据名**整句原样抄** |
|
|
| `write` | `{itemId, address, rows:[{key,patch}]}` | 写草稿。授权面是台账声明;`itemId` 只是记账,挂不上照写不拦;patch 只带有值的列;空转写(一行没变)即拒 |
|
|
| `drop` | `{itemId, address, keys:[{主键}], reason}` | 撤本单草稿层的行;主键与写进去时一字不差 |
|
|
| `tool` | `{itemId, tool, input}` | 读类即调;写类转意向;未命中转人挂起 |
|
|
| `ask` | `{itemId, ask, fields:[{key,label,hint?,value?}], basis}` | 补全卡,挂起 |
|
|
| `entry` | `{itemId, ask, ledger, entryKey, fields, also?}` | 数据录入卡,挂起;确认那一下基座直写 |
|
|
| `decide` | `{itemId, ask, options(≥2), basis}` | 决策卡,挂起 |
|
|
| `human` | `{itemId, workOrder, card{title,ask,steps?,standard?,basis,fields?,blocks?}}` | 执行/验收工单,挂起。`blocks` 里绑 `ledger`+`fields` 的表**由基座铺行、由基座写回**,别自己抄行(回复有长度上限,铺长了截断整拍作废) |
|
|
| `listen` | `{itemId, eventType, keyFilter, deadline}` | 立监听,挂起;时限必配 |
|
|
| `finish` | `{itemId, rationale}` | 宣告一项完成;非人工激活的项就地过 Active 再收 |
|
|
| `submit` | `{rationale, fields?}` | 提交放行 → 预检 → 发验收卡挂起 |
|
|
|
|
**串的纪律**(引擎强制):按序执行,谁被拒停在谁那儿(前缀已生效不回滚,理由喂回下一拍);
|
|
`tool` 之后截断(出参没看见,后面是闭着眼计划的);挂起型收拍;超封顶丢弃留痕。
|
|
拍级附带 `say{text, tag, view?}`,不占行动位。
|
|
|
|
### 4.3 人工激活与 start(话与门同宽)
|
|
|
|
标了 `manualActivationRule` 的项,**动它之前必须先 `start`** —— 调工具、发卡
|
|
(ask/human/decide/entry)、宣告完成(finish)都算,一拍里 start 排在那个动作前面即可。
|
|
只有 `write` 例外(itemId 只是记账)。
|
|
|
|
实测账(2026-08-02):这条规矩从前在提示词里写成「先 start **再调工具**」——
|
|
只说了调工具,而门拦的是一整片;模型那一拍发的是 `human`,照字面读规矩管不着它,
|
|
于是漏了 start 白烧一拍。**话说的范围必须盖住门拦的范围,话窄一格,模型就正好漏在那一格上。**
|
|
写全之后三案例复跑全部被拒 0。
|
|
|
|
### 4.4 自动收步(doneWhen)
|
|
|
|
引擎每拍开头对任务视图求每一步的 `doneWhen`,**真了就自己收那一项,不问模型**
|
|
(与模型同拍撞车时:引擎收过了,模型这拍的 finish 当已完成,不抛非法迁移)。
|
|
人工激活的项**不给 doneWhen** —— 给了等于给引擎一把替人签字的钥匙。
|
|
|
|
---
|
|
|
|
## 5. 活动包怎么写
|
|
|
|
### 5.1 包结构
|
|
|
|
```
|
|
activities/<activity-id>/
|
|
├── package.json # @activities/<id>,private,type:module
|
|
├── src/
|
|
│ ├── index.ts # export default defineActivity({ name })
|
|
│ ├── manifest.ts # 清单六区 + 导出 DMN 常量
|
|
│ ├── case-model.ts # CMMN_XML + NARRATIVE + INTAKE_STEPS + INTAKE_PHASES
|
|
│ ├── skills.ts # Skill[]
|
|
│ └── views/{report.ts, prd-style.ts} # 报告模版(见姊妹篇)
|
|
├── __tests__/ # 自测;只准 import @sdk/@contracts/@sfeel
|
|
└── dist/ # pnpm activities:build 产出
|
|
```
|
|
|
|
### 5.2 清单六区要点(全量 schema 见 `packages/contracts/src/manifest.ts`)
|
|
|
|
**identity**:`activityId / name / packageVersion(改内容必升)/ category / sdkVersion(精确相等)/ checksum`。
|
|
|
|
**contract 七件**:
|
|
|
|
| 件 | 硬规矩 |
|
|
|---|---|
|
|
| ① input | 起单宽进;`properties[k].title` 写中文(补全卡上给人看的);**`inboundComm`** 声明哪几个入参字段合起来是那封来件 —— 基座起单那一刻归档进沟通记录 |
|
|
| ② output | mandatory 必发;selective 每组 ≥2 口、**每口写 `when` 谓词**(读台账列/基座读数)—— 放行时按序求值首个命中即出,算不出才退人选。载荷 = 已落地事实,别在出口另编载荷 |
|
|
| ③ requiredDeliverables | `ledger` 必写(预检数行);**一份成果由几张表承载就写 `ledgers`**(A1 的受理记录 = record + type_table + verdict 三张表、**一份**成果 —— 从前各占一条,人在成果面上看到三样东西);`editableAtRelease` 列出验收页上人可就地改的列(**AI 判的结论列也要在其列**,如 `type_tag` —— 人改它就是在推翻一次判定,那是他的职权) |
|
|
| ④ acceptanceCriteria | L1/L2 必带 predicate 且**只读基座读数**(§3.2);L4 可带谓词(谓词说了算,§3.3);**措辞写全**(「客户已确认:发件邮箱认出为老客户,或已为新客户建好档」——「(认出或已建档)」这种缩写人读不懂);**内部一致性不上卡**(打标依据对不对、两张表行数齐不齐 —— 该由收步判据看住,放行卡是给签字的人看的,一屏九条真要他过目的反而淹了);**判据得能指向一个可做的动作**(「去向合乎常理」这种让模型给自己没做的决定盖章的,删) |
|
|
| ⑤ revisableAtRelease | 放行时人可当场改的整单级值;拟值基座从台账现读,不让模型重述 |
|
|
| ⑥ prohibitions / authorization | 禁区短而硬;`humanCallBudget` 调人预算 |
|
|
| ⑦ resources | budget 按实测留一倍余量(A1:steps 60 / tokens 400k / 60min / 调人 4;实测常路 9-11 拍 8-13 万 token) |
|
|
|
|
**declarations**:台账(字段四标记,§3.1)、tools、models、listeners(时限必配)、harness。
|
|
|
|
**assets**:`businessModel{cmmnXml, narrative, dmn, steps, phases}` + skills + viewTemplates +
|
|
cardSchemas + distillHooks。**steps 与 phases 都在这**(见 §5.4、§3.6)。
|
|
|
|
**samples ≥1 条**(常路 + 最长路径都给上;input 键与 contract.input 对齐 ——
|
|
A1 是 `customerEmail / subject / body / attachments`)。
|
|
|
|
### 5.3 案例模型(CMMN)建模纪律 —— 每条都是实测账
|
|
|
|
1. **图上有几步就是几项,不多建一件。** A1 第一版 6 步建成 12 项(自加阶段/里程碑/
|
|
裁量项),模型全程跟 harness 较劲,一单二三十万 token 收不了口;扁平 7 项后零被拒。
|
|
2. **案卷项 id 就是任务视图键名**(入参与台账列摊平后的名字)。对不上 = 前提永不满足 =
|
|
第一个项都不 Enabled,而每一层单看都「正常」。
|
|
3. **`manualActivationRule` 只标触达世界与关键交付的项**,且**不给 doneWhen**。
|
|
A1 标两项:S3_confirm(要打断真人)、T_release(签下去触达世界)。
|
|
4. **人的门要建成一项,不要挂成某一步收步条件里的半句。** 「缺东西才叫人」的写法,
|
|
一封齐整的询价进来那半句当场为真,引擎自己收步,人一眼没看过就被叫去验收
|
|
(用户原话:「我都没确认需求 它就让我验收了」)。做法:独立一项 + 人工激活 +
|
|
不给 doneWhen + 下一步的入场前提取**只有人的回复才产生的计数列**(`confirmed_total`)。
|
|
5. **哨兵必须有 ifPart,且读基座数、认「办完了没有」**(§3.3 放行门那行的教训)。
|
|
6. `repetitionRule` 标常来回的项;判断菱形 = 两条互斥前提的 sentry,不是一个计划项。
|
|
7. `autoComplete="true"`;里程碑前提达成即达成;`ifPart` 前缀 `semantic:` 交 judge。
|
|
8. **每个计划项(除放行)恰属一个 phase**;放行收口那格基座加,活动别自己起名。
|
|
|
|
### 5.4 剧本(steps)—— 每步一条,引擎按它组织上下文
|
|
|
|
```ts
|
|
{ item: 'S2_normalize', // CMMN 计划项 id,对不上安装拒
|
|
intent: '一句话:这一步交出什么',
|
|
guide: `这一步的做法(只在轮到它时进上下文)`,
|
|
writes: ['quote_intake.record'], // 可写哪几张表
|
|
reads: ['customer.contact'], // 要读哪几张表 —— 声明了置前不截断(不声明会被盲截,
|
|
// 实测:客户联系人表落在刀口外,老客户被当成新客户)
|
|
tools: ['crm.history-price-read'],
|
|
doneWhen: 'line_total > 0 and quote_intake_record_rows = line_total',
|
|
view: 'intake-overview', // 这一步人看哪张件 —— 写清单时定死,不归模型临场挑
|
|
}
|
|
```
|
|
|
|
**doneWhen 三坑**:
|
|
- `X != null` —— S-FEEL 未绑定名字 `!= null` 为 **true**,那一步开局即收。判「写过没有」
|
|
用正向式(`X >= 0`)或计数列;
|
|
- **布尔列不当判据** —— 合法值含 false,分不清「没写」与「写了 false」。
|
|
取同步显式写下的计数列(`route_decided_total`);
|
|
- **收步判据与验收判据同源** —— 都读基座读数,比同一件事。松出来的那一截就是返工
|
|
(收了步却过不了验收,回头补写一张已收掉的表);同源还把发现点提前
|
|
(「多写一行」从第 10 拍提前到第 2 拍)。
|
|
|
|
### 5.5 说明书与 guide 写作纪律
|
|
|
|
说明书(narrative)只讲**整单**的事(开案、成品、SLA、口径);**逐步做法归剧本 guide**
|
|
(两处各写一份会分叉出模型里不存在的项名,有测试钉死)。guide 写作十条:
|
|
|
|
1. **规则写死,不留给模型判**(隐藏思考已关换十倍速度,边界必须写到不可能读歪:
|
|
「关键字段只有 PN/EAU/交期」)。
|
|
2. **写哪张表、哪些列,点名写死**;人在哪一步答的东西谁负责落表,写清。
|
|
3. **卡的骨架照抄给它**(整段 JSON 例子,含 `steps`/`standard`/`also`/绑表的 `blocks`)——
|
|
散文描述不管用;系统提示词写死的形状盖过剧本,骨架里少一个键那个键就不存在。
|
|
4. **一拍连做指引** + 常路拍数预期;工具批量入参,一次传完。
|
|
5. **一单同一张卡只发一次**;人留空 = 给不了(§3.4)。别写「还缺就再发一张卡」——
|
|
提交闸上线后那条路不存在,模型照它办每次都撞「刚问过,人也答过了」(实测连烧四拍)。
|
|
6. 绑台账的表**别自己抄行**;人提交后**别再 write 一遍**(值没变会被「写了等于没写」拒)。
|
|
7. 回填按主键对行 —— 主键换个写法就是新开一行(实测同一颗料成了两行);
|
|
发现主键抄错交给人,别用新主键再写一行。
|
|
8. **别替下游产出**(§2.4);去向不用你选也不用人选,你只负责把判据数写对。
|
|
9. 汇总列是你的活的,写明重算时机(人补完值你要把数改对);**机检读数你改不动,
|
|
别为过判据去改自报数**。
|
|
10. **卡面是纯文本**:`steps`/`standard`/say 里别写 `**` 星号记号。
|
|
|
|
### 5.6 判定表(DMN)—— 能算的不问 LLM
|
|
|
|
单元格谓词 = S-FEEL(`'-'` 任意、`'"A","B"'` 析取、`'<= 90'`、`'[0..100]'`);
|
|
**FIRST 的行序就是业务优先级**(C 类 EOL 必须排最前);任一输入缺 → incomplete;
|
|
命中留痕、同输入必同结论、零 token。推荐做成 `pure: true` 的私有功能工具
|
|
(`quote-intake.classify`),模型只取齐判据、调工具拿结论;
|
|
**出参键名与台账列同名**(拿回直接 write)、连 `counts` 一起给(供照抄);
|
|
「没有」也当判据回(无历史 → `historyDays: 9999`),别回错误。
|
|
|
|
### 5.7 工具
|
|
|
|
声明式(名字进 `declarations.tools`,系统没有的运行时转人 —— 执行卡代办,
|
|
人填的值就是出参);批量入参;读类即调、写类转意向、未授权报错。
|
|
系统内化零声明即得:write/drop/ask/entry/decide/human/listen。
|
|
|
|
### 5.8 报告模版
|
|
|
|
**见《HTML报告生成规范.md》。** 那份是硬规矩:一单一模版贯穿始终、三个区各放什么、
|
|
报告与表单的关系、模版语言、编辑态、PRD 目标件平移法、陷阱表。
|
|
|
|
### 5.9 技能与记忆
|
|
|
|
技能 `{id, label, applicability, method[], version}`:常驻只有目录,正文按需加载,
|
|
载方法不载权力。方法里写锚点、纪律、批量惯例,不写产品文案。**剧本改了口径,
|
|
技能里同一句也要跟着改**(A1 定去向那条技能与 S6 同步删过开单指引)。
|
|
任务记忆只增;终态封卷后 `distillHooks` 蒸馏进活动记忆。
|
|
|
|
---
|
|
|
|
## 6. 自测与验收口径
|
|
|
|
### 6.1 包内自测(照 `activities/quote-intake/__tests__/`)
|
|
|
|
1. **清单合规**:过 schema;监听时限;L1/L2 有谓词且**谓词名都在台账列或基座读数里**;
|
|
必交付物都有 ledger;
|
|
2. **案例模型**:双表示同在;触达世界的项标人工激活且无 doneWhen;
|
|
**每个计划项恰属一个 phase(除放行)、一项不进两个阶段**;
|
|
剧本 item 与 CMMN 对得上;**doneWhen 开局不为真**(空局面求值必须 false);
|
|
3. **判定表逐行求值**;
|
|
4. **对齐线**:deliverable.ledger 在声明里;出口载荷标量有列承载;
|
|
剧本 view 都在 viewTemplates 里;
|
|
5. **模版纪律**(见姊妹篇 §9)。
|
|
|
|
### 6.2 五道门禁与安装两审
|
|
|
|
`pnpm gates`(typecheck / lint / depcruise / api:check / test),以退出码验。
|
|
安装静态审 error 即拒(黑名单见 §2.2;`acceptance-level-misreported` = L1/L2 没谓词)。
|
|
|
|
### 6.3 试跑口径与验收标准
|
|
|
|
- **试跑一律用 AI 账号**(密码 `jcai-dev-2026`),绝不派到用户工作台;
|
|
测完 `pnpm --filter @app/api reset-runs`。
|
|
- **三条常备案例**(形状真不同,不是同一封信改个号):
|
|
① 老客户 · BOM 附件齐整 · 全 A 类直通;② 老客户 · 正文点名一条本轮不报(测人手撤行);
|
|
③ 新客户 · 中文正文列料 · 无附件 · 缺必填(测建档 + 人补格子)。
|
|
- **验收标准:三案例全部 `released`、行动被拒 0。**
|
|
每一条「行动被拒」都是设计欠账 —— 修清单或剧本,不怪模型;
|
|
被拒理由指向死路(模型改不动的那种)是 harness 欠账,报上去。
|
|
|
|
---
|
|
|
|
## 7. 排障速查表(症状 → 根因 → 处置;全部实测账)
|
|
|
|
| 症状 | 根因 | 处置 |
|
|
|---|---|---|
|
|
| 起单后第一个项都不 Enabled | 案卷项 id 不是任务视图键名 | §5.3-2 |
|
|
| 连续「迁移非法/项不存在」熔断 | 计划项建多了/嵌套了 | §5.3-1 扁平化 |
|
|
| 一单 20+ 拍才收口 | 没写一拍连做/拍数预期;或项过多 | §5.5-4 |
|
|
| 机检判据全绿而交付物是错的 | 等式两边都由活动自报 | 一边改读基座读数(§3.2) |
|
|
| 一条 L1/L2 永远绿不了而数据合规 | 读了自报汇总列 | 标 `required`,读 `_missing`(§3.2 违反史①) |
|
|
| 台账里某个数被改小、判据变绿 | 门只接受「是」,模型去改自报数 | 机检只读基座数;数不出的降 L3/L4(违反史②) |
|
|
| 没打标就进了放行门 | 哨兵只有 onPart(写过一笔就开门) | ifPart 读基座数认「办完没有」(§3.3) |
|
|
| 同一条理由连拒到熔断而数与行都对 | 拒回把「活没干完」说成「数/行不对」 | 已修:`_rows`=0 直说那一步没办 |
|
|
| 行数对不上,模型只会改数字 | 拒回没摆主键名单 | 已修:`_keys` 名单;照名单 drop |
|
|
| 「要先 start」而 start 也被拒 | 拒回没按状态分开说 | 已修;start 已收的项无事发生 |
|
|
| 模型漏 start 白烧一拍 | 提示词的话比门窄(只说了调工具) | 已修:话与门同宽(§4.3) |
|
|
| 人答了卡,模型又发一张一样的 | 剧本残留「还缺就再发一张卡」死路 | §5.5-5:一单一卡;必填有闸兜着 |
|
|
| 人在卡上填了值,台账还是空的 | doneWhen 写成「问过就算完」 | 等「答案落了表」的计数(`answered_total`) |
|
|
| 该经过人的一步整个没发生 | 「经过人」写成了收步条件里的半句 | §5.3-4:独立一项+人工激活 |
|
|
| 人改了表,分布条/去向不动 | 汇总数没人重算;或保存没落库 | 分类列标 `tally` 由基座数;保存走件上按地址那条路(姊妹篇 §6) |
|
|
| 人在件上保存,一格都没落 | 保存借道当下那张卡的 blocks(熔断卡没有 blocks) | 已修:按 `data-edit` 地址收,授权=台账声明 |
|
|
| L4 判错(人填了它说没填) | 整条只读裁断,模型凭记忆写理由 | L4 带谓词,谓词说了算(§3.3);`expect` 别标我方写的注 |
|
|
| 判据卡面与提交门说的不一样 | 两处各算各的 | 已修:读时现算,同一段 `criterionCheck` |
|
|
| 进度条在人办事时已走过那一格 | phases 漏了那一项 | §3.6:每项恰属一格,自测钉死 |
|
|
| 收了步却过不了验收 | 收步判据比验收松 | §5.4:同源 |
|
|
| 全 0 判据过不去连撞熔断 | 没写过 ≠ 写了 0 行 | 已修:声明过的表铺 0 |
|
|
| 补全卡答了数据没进台账 | 系统不替 Agent 写 | §5.5:答案要自己 write(绑表除外) |
|
|
| 同一颗料成果表里两行 | 主键换写法 = 新开一行 | §5.5-7 |
|
|
| 数值列被写成 0 表示「没有」 | 模型拿 0 当空 | 写死:没有给 `""`(归型不写这一列) |
|
|
| 制表符 BOM 拆错列 | 连续 `\t` 是中间格空 | 附件 `rows` 有值照它拆,别自己数制表符 |
|
|
| 工作区画面不换 / 推错张 | view 名不在模版里 / 剧本没写 view | §5.4 view 定死 |
|
|
| 归档件被冲成别的屏 | 收口后按固定名推退到第一张 | 已修:按剧本推,活步收完不推 |
|
|
| 消息被「无主入站待认领」刷屏 | 台账 data 事件进了兜底 | 已修:只有 `source.kind==='channel'` 才兜 |
|
|
| 下游网关取不到值 | 出口标量没有台账列承载 | §5.2-② |
|
|
| 改了包重装没生效 | 同版重装 no-op | 升 packageVersion |
|
|
| 收口前一两拍预算耗尽 | budget 给太紧 | §5.2-⑦ 留一倍余量 |
|
|
|
|
## 8. 待实现与已知不一致
|
|
|
|
| # | 事项 | 现状 | 目标 |
|
|
|---|---|---|---|
|
|
| ④ | 受理分支误吞验收裁决 | `deliver` 的受理分支判 `awaiting-human && human` 不看 cardId | 按补全卡 cardId 匹配;开发侧规避:带缺口开跑的单尽早补齐 |
|
|
| ⑤ | 治理台白名单授权口 | 只有种子常量表 | 治理台按活动配白名单 |
|
|
| ⑦ | dist 真编译 | `index.js` 是源码文本拷贝(债 M5-3) | build-activities 真编译 |
|
|
| ⑧ | 单拍工具调用缝 | 调用经 ToolInvoker 未走总线编译 | 开环转正或接总线 |
|
|
| ⑨ | `preauthorizedActions` 死代码 | 无调用方 | manifest 里不要写 |
|
|
| ⑩ | `SchemaValidator` 双死依赖 | 声明了不再调 | 清理 |
|
|
| ⑪ | 评价卡(rate) | schema 在、链路未接 | 用户定:暂不做 |
|
|
| ⑫ | `judge 还没裁`各花一拍 | 待裁量判据不在单拍局面里,撞拒回才知道 | 把「还没裁的判据」进局面(结构改动,未做) |
|
|
|
|
小不一致(不阻塞):`declarations.tools[].version` 无消费者;`contract.resources.tools`
|
|
双份声明;replay 跳过白名单;工具 `retries` 不重试;转人答案不经出参 Schema 校验(债 M6-1)。
|