Archived
人手改的那一手真正落得下去;界面不撒谎那几处补齐
手改是**兜 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:
+200
-7
@@ -4,7 +4,7 @@
|
||||
import type { LoopModel, StaffId, TaskId } from '@contracts';
|
||||
import type { CaseModel } from './internal/domain/cmmn-parse.ts';
|
||||
import type { CaseRuntime, EnabledSet } from './internal/domain/lifecycle.ts';
|
||||
import type { AgentAction } from './contract.ts';
|
||||
import type { AgentTurn } from './contract.ts';
|
||||
export type Clock = {
|
||||
now(): Date;
|
||||
};
|
||||
@@ -27,13 +27,53 @@ export type Sfeel = {
|
||||
/** 任务数据 —— app 注入 `ledger`(L1):引擎对**任务视图**求值,不依赖事件 */
|
||||
export type TaskDataGateway = {
|
||||
view(taskId: TaskId): Promise<Record<string, unknown>>;
|
||||
/**
|
||||
* 本活动**声明过**的台账对象(域.对象 → 列名与中文标)。
|
||||
*
|
||||
* 两用:喂给 Agent(不知道能写哪儿就只会瞎猜列名),以及校验 `write` 行动 ——
|
||||
* 没声明过的地址一律拒写。越域写与越权调工具同罪,都得在结构上拦住。
|
||||
*/
|
||||
writable(taskId: TaskId): Promise<Array<{
|
||||
domain: string;
|
||||
object: string;
|
||||
label: string;
|
||||
keys: string[];
|
||||
fields: Array<{
|
||||
name: string;
|
||||
label: string;
|
||||
type?: string;
|
||||
}>;
|
||||
}>>;
|
||||
/**
|
||||
* 写草稿,并回报**这一笔到底有没有改变什么**。
|
||||
*
|
||||
* 值和上次一模一样的写是空转。不回报,Agent 就看不出自己在原地打转 ——
|
||||
* 实测:同一行 verdict 被反复重写十几拍,每拍烧一次模型,预算就这么淌完。
|
||||
*/
|
||||
draftWrite(input: {
|
||||
taskId: TaskId;
|
||||
domain: string;
|
||||
object: string;
|
||||
key: Record<string, string | number>;
|
||||
patch: Record<string, unknown>;
|
||||
}): Promise<void>;
|
||||
}): Promise<{
|
||||
changed: boolean;
|
||||
}>;
|
||||
/**
|
||||
* **撤掉本单草稿层的一行** —— 写错了得有退路。
|
||||
*
|
||||
* 回 `false` = 草稿层里根本没有这一行(多半是主键写法对不上,
|
||||
* `DAG…` 与 `DAG…-R0` 是两行)。如实说比静默成功有用:静默成功会让模型
|
||||
* 以为撤掉了,下一拍判据照旧不过,它又去撤一次。
|
||||
*/
|
||||
draftDrop(input: {
|
||||
taskId: TaskId;
|
||||
domain: string;
|
||||
object: string;
|
||||
key: Record<string, string | number>;
|
||||
}): Promise<{
|
||||
dropped: boolean;
|
||||
}>;
|
||||
};
|
||||
/**
|
||||
* Agent 回路 —— app 注入(Mastra Agent 在 adapter,§6.1)。
|
||||
@@ -46,7 +86,32 @@ export type AgentPort = {
|
||||
narrative: string;
|
||||
enabled: EnabledSet;
|
||||
scope: Record<string, unknown>;
|
||||
}): Promise<AgentAction>;
|
||||
/**
|
||||
* 这一单能调什么 —— **声明 × 目录**编译出来的工具清单(`compileToolBus`)。
|
||||
*
|
||||
* 不给它这份清单,它就只知道说明书里提过的工具名,连入参长什么样都得猜 ——
|
||||
* 实测的后果是:该调工具的那一步改成了发一张卡问人(「请为以下三个PN查询历史价格」),
|
||||
* 明明系统里就有这件工具。工具是声明式的,清单得摆在它眼前。
|
||||
*/
|
||||
tools: Array<{
|
||||
id: string;
|
||||
description: string;
|
||||
input: Record<string, unknown>;
|
||||
effect: 'read' | 'write';
|
||||
}>;
|
||||
/** 本活动可写的台账地址与列名 —— 不给它字典,它只会照想象编列名 */
|
||||
writable: Array<{
|
||||
domain: string;
|
||||
object: string;
|
||||
label: string;
|
||||
keys: string[];
|
||||
fields: Array<{
|
||||
name: string;
|
||||
label: string;
|
||||
type?: string;
|
||||
}>;
|
||||
}>;
|
||||
}): Promise<AgentTurn>;
|
||||
};
|
||||
/** 工具 / 人类工具 / 监听 —— 三型行动的落点 */
|
||||
export type ToolGateway = {
|
||||
@@ -61,6 +126,16 @@ export type ToolGateway = {
|
||||
error?: {
|
||||
message: string;
|
||||
};
|
||||
/**
|
||||
* 已转人代办,等人回填(§6.2)——「人是工具的一种实现」。
|
||||
*
|
||||
* 系统里没有这件工具时,不是失败:基座已开了一张执行卡并立了监听。
|
||||
* 这一拍该**挂起等人**,不该把计划项判 Failed —— 判 Failed 等于告诉 Agent
|
||||
* 「这条路走不通,换一条」,可这条路明明正在走,只是走的人换成了人。
|
||||
*/
|
||||
awaitingHuman?: {
|
||||
readonly workOrderId: string;
|
||||
};
|
||||
}>;
|
||||
isWriteEffect(tool: string): Promise<boolean>;
|
||||
registerIntent(input: {
|
||||
@@ -69,6 +144,13 @@ export type ToolGateway = {
|
||||
input: Record<string, unknown>;
|
||||
label: string;
|
||||
}): Promise<void>;
|
||||
/** 这一单能调什么(声明 × 目录)—— 单拍路径与开环路径吃同一份清单 */
|
||||
busOf(activityId: string): Promise<Array<{
|
||||
id: string;
|
||||
description: string;
|
||||
input: Record<string, unknown>;
|
||||
effect: 'read' | 'write';
|
||||
}>>;
|
||||
};
|
||||
/**
|
||||
* 在途验收(提交放行的持久痕):卡发出即记,裁决到达再记 verdict ——
|
||||
@@ -80,6 +162,39 @@ export type PendingAcceptance = {
|
||||
readonly selectedPorts?: Record<string, string>;
|
||||
};
|
||||
export type CardGateway = {
|
||||
/** 数据录入卡 —— 人确认那一下由基座直接写草稿层,不回到 Agent 手里 */
|
||||
entry(input: {
|
||||
taskId: TaskId;
|
||||
ownerId: StaffId;
|
||||
humanCallCap: number;
|
||||
ask: string;
|
||||
ledger: string;
|
||||
key: Readonly<Record<string, string>>;
|
||||
fields: ReadonlyArray<{
|
||||
key: string;
|
||||
label: string;
|
||||
value?: string;
|
||||
required?: boolean;
|
||||
}>;
|
||||
/** 同一次确认还要落的另几张表 —— 建一位新客户 = 公司档 + 联系人档 */
|
||||
also?: ReadonlyArray<{
|
||||
title: string;
|
||||
ledger: string;
|
||||
key: Readonly<Record<string, string>>;
|
||||
fields: ReadonlyArray<{
|
||||
key: string;
|
||||
label: string;
|
||||
value?: string;
|
||||
required?: boolean;
|
||||
}>;
|
||||
}>;
|
||||
basis?: ReadonlyArray<{
|
||||
k: string;
|
||||
v: string;
|
||||
}>;
|
||||
}): Promise<{
|
||||
id: string;
|
||||
}>;
|
||||
issue(input: {
|
||||
taskId: TaskId;
|
||||
ownerId: StaffId;
|
||||
@@ -89,6 +204,56 @@ export type CardGateway = {
|
||||
}): Promise<{
|
||||
id: string;
|
||||
}>;
|
||||
/**
|
||||
* 发**补全卡**(对话卡族)—— 缺口清单摆在对话区、带一张表单,人就地填齐。
|
||||
*
|
||||
* 与执行工单分开是因为它们是两件事:工单是「派一件活给你办」,
|
||||
* 派一张工单去问几个字段,收卡的人得进待处理、办完再回来,重得不成比例。
|
||||
* SOP 上画着「补全卡」的那一步(A1-S2),要的就是这一件。
|
||||
*/
|
||||
ask(input: {
|
||||
taskId: TaskId;
|
||||
ownerId: StaffId;
|
||||
humanCallCap: number;
|
||||
ask: string;
|
||||
fields: ReadonlyArray<{
|
||||
key: string;
|
||||
label: string;
|
||||
hint?: string;
|
||||
}>;
|
||||
basis?: ReadonlyArray<{
|
||||
k: string;
|
||||
v: string;
|
||||
}>;
|
||||
}): Promise<{
|
||||
id: string;
|
||||
}>;
|
||||
/**
|
||||
* 发**决策卡**(对话卡族)—— 岔路口摆给人**点一个**,不是让人敲字。
|
||||
*
|
||||
* 与补全卡的分工:补全卡问事实(缺哪几个字段,你填);决策卡问处置(往哪走,你选)。
|
||||
* 两条路都合法、选哪条是业务判断的时候,该发的是这一件。
|
||||
*/
|
||||
decide(input: {
|
||||
taskId: TaskId;
|
||||
ownerId: StaffId;
|
||||
humanCallCap: number;
|
||||
ask: string;
|
||||
options: ReadonlyArray<{
|
||||
value: string;
|
||||
label: string;
|
||||
detail?: string;
|
||||
consequence?: string;
|
||||
recommended?: boolean;
|
||||
}>;
|
||||
basis?: ReadonlyArray<{
|
||||
k: string;
|
||||
v: string;
|
||||
}>;
|
||||
allowCustom?: boolean;
|
||||
}): Promise<{
|
||||
id: string;
|
||||
}>;
|
||||
};
|
||||
export type ListenerRegistrar = {
|
||||
register(input: {
|
||||
@@ -107,12 +272,21 @@ export type ListenerRegistrar = {
|
||||
* 零 LLM 即可成屏;模型只决定「下一步做什么」,不负责画面。
|
||||
*/
|
||||
export type ViewGateway = {
|
||||
/**
|
||||
* 推一帧画面。**回报推没推上** —— 从前是 `Promise<void>` 且模版查不到就静默 return:
|
||||
* 引擎每拍推一个硬编码的 `case-overview`,而 A1 声明的四张模版一个都不叫这个名字,
|
||||
* 于是**每一帧都被悄悄丢掉**,工作区从头到尾空白,日志里也没有一个字。
|
||||
* 静默失败比报错坏得多:它让「没画面」看起来像「活动还没产出」。
|
||||
*/
|
||||
push(input: {
|
||||
taskId: TaskId;
|
||||
activityId: string;
|
||||
template: string;
|
||||
scope: Record<string, unknown>;
|
||||
}): Promise<void>;
|
||||
}): Promise<{
|
||||
pushed: boolean;
|
||||
templates: string[];
|
||||
}>;
|
||||
};
|
||||
/**
|
||||
* 技能 —— app 注入 `skill-engine`(L4)。
|
||||
@@ -134,7 +308,7 @@ export type SkillGateway = {
|
||||
export type MemoryRecorder = {
|
||||
append(input: {
|
||||
taskId: TaskId;
|
||||
kind: 'engine-transition' | 'evaluation' | 'thought' | 'action' | 'result' | 'observation';
|
||||
kind: 'engine-transition' | 'evaluation' | 'thought' | 'action' | 'result' | 'observation' | 'narration';
|
||||
title: string;
|
||||
detail?: Record<string, unknown>;
|
||||
}): Promise<void>;
|
||||
@@ -143,10 +317,19 @@ export type MemoryRecorder = {
|
||||
layer: string;
|
||||
statement: string;
|
||||
}>>;
|
||||
/** 本任务自己的最近轨迹(窗口大小由活动声明的 memoryWindow 定)—— 喂回叙事面,Agent 每拍不失忆 */
|
||||
window(taskId: TaskId): Promise<Array<{
|
||||
/**
|
||||
* 本任务自己的最近轨迹 —— 喂回叙事面,Agent 每拍不失忆。
|
||||
*
|
||||
* 不给 `size` 就用活动声明的 `memoryWindow`(叙事面按它给;窗口越大越贵)。
|
||||
* **熔断检测要另开一个更大的窗**:它数的是「同一堵墙撞了几次」,
|
||||
* 而一次提交被拒会连带写下交代、评估、推画面好几条 —— 20 条的叙事窗里
|
||||
* 至多装得下三四次撞墙,于是撞十次也数不到 5(实测:同一条判据拒回 10 次没熔断,
|
||||
* 另一单拒回 33 次,40 万 token 烧到 exhausted 才停)。
|
||||
*/
|
||||
window(taskId: TaskId, size?: number): Promise<Array<{
|
||||
kind: string;
|
||||
title: string;
|
||||
detail?: Record<string, unknown> | undefined;
|
||||
}>>;
|
||||
};
|
||||
export type TaskContext = {
|
||||
@@ -155,4 +338,14 @@ export type TaskContext = {
|
||||
ownerId: StaffId;
|
||||
humanCallCap: number;
|
||||
}>;
|
||||
/**
|
||||
* 这一单的**机检判据名**(归一化后的 key 与 label 都在里头)。
|
||||
*
|
||||
* 给 `judge` 行动当门用:L1/L2 是拿 S-FEEL 对台账求值的,Agent 裁不动它 ——
|
||||
* 判据投影压根不读 Agent 对机检项的裁定。可从前引擎照收不误,还往轨迹里
|
||||
* 写一句「语义性前提评估:XXX → 满足」:模型于是以为这条已经过了,
|
||||
* 转头就 submit,被门以同一条判据拒回;再裁、再拒,连拒五拍熔断(实测 A2 死于此)。
|
||||
* 轨迹里那句「满足」是**假话** —— 界面不撒谎,对 Agent 也一样。
|
||||
*/
|
||||
machineCriteria(taskId: TaskId): Promise<ReadonlySet<string>>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user