Archived
M4: 范式引擎与任务运行时
skill-engine(L4):载方法不载权力(白名单外工具即拒)、结晶片段经 ToolInvoker 留痕、 装配入信封、技能目录渐进披露 flow-engine(L5):四种步型、确定性谓词跳步、重试与声明给定的兜底、 异步步即监听(时限必配)、写类工具登记动作意向、步序位置持久 loop-engine(L5):自研 CMMN 子集解析器(表外构件直接报错,子集封顶可机器证明)、 两副标准生命周期与迁移拓扑(引擎不得私改)、Enabled ≠ Active、 语义性前提标待裁量评估(引擎不代裁量)、DMN 确定性求值 + 命中行留痕、 DRD 缺口演绎与拓扑序、双向驱动与交集导航(断点即人环)、三型行动、 终局唯放行与 Harness task(L6):终态五名裁定(released 唯一成功)、Harness 预算环外强制与停滞熔断 (监听中是健康态)、控制三语义(叫停不是终态,记最重档纠正)、改派留痕通知双方、 断点续跑三份持久状态、封卷触发蒸馏钩子 301 个测试通过,门禁五道全绿 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
co-authored by
Claude Opus 5
parent
2850e6d4ef
commit
3c974d2860
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* flow 步序推进 —— 纯逻辑,零 IO(编码规范 §5.3;语义出处 AM 05)。
|
||||
*
|
||||
* 「flow 是活动内的**确定性范式**:业务模型 = 步序声明 —— 一条谱;
|
||||
* 引擎照谱执行,**零临场调度**,调度权归系统。
|
||||
* 差异只有一处:下一步由**声明给定**,不由 AI 决断。
|
||||
* LLM 在 flow 里可以出现,但只以『被调用的工具』出现,不是调度者。」
|
||||
*/
|
||||
import type { FlowStep } from '@contracts';
|
||||
|
||||
export const STEP_STATES = ['pending', 'running', 'awaiting', 'done', 'skipped', 'failed'] as const;
|
||||
export type StepState = (typeof STEP_STATES)[number];
|
||||
|
||||
export type StepRuntime = {
|
||||
readonly name: string;
|
||||
readonly state: StepState;
|
||||
readonly attempt: number;
|
||||
readonly output?: Record<string, unknown>;
|
||||
readonly error?: string;
|
||||
};
|
||||
|
||||
export type FlowPosition = {
|
||||
/** 步序位置 —— 三份持久状态之一(OS 09 断点续跑) */
|
||||
readonly index: number;
|
||||
readonly steps: readonly StepRuntime[];
|
||||
};
|
||||
|
||||
export function initialPosition(steps: readonly FlowStep[]): FlowPosition {
|
||||
return { index: 0, steps: steps.map((s) => ({ name: s.name, state: 'pending', attempt: 0 })) };
|
||||
}
|
||||
|
||||
/** 条件跳步:条件必须是任务数据上的**确定性谓词**,由引擎求值,零 LLM(AM 5.2) */
|
||||
export type PredicateEvaluator = (expression: string, scope: Readonly<Record<string, unknown>>) => boolean;
|
||||
|
||||
export type NextAction =
|
||||
| { readonly kind: 'run'; readonly step: FlowStep; readonly index: number }
|
||||
| { readonly kind: 'skip'; readonly step: FlowStep; readonly index: number; readonly reason: string }
|
||||
| { readonly kind: 'await'; readonly step: FlowStep; readonly index: number }
|
||||
| { readonly kind: 'finished' };
|
||||
|
||||
/**
|
||||
* 下一步由声明给定。异步步(人类工具步 / 等事件的步)进入 `await` —— 完成条件 = 监听。
|
||||
*/
|
||||
export function nextAction(
|
||||
declaration: readonly FlowStep[],
|
||||
position: FlowPosition,
|
||||
scope: Readonly<Record<string, unknown>>,
|
||||
evaluate: PredicateEvaluator,
|
||||
): NextAction {
|
||||
for (let i = position.index; i < declaration.length; i += 1) {
|
||||
const step = declaration[i]!;
|
||||
const runtime = position.steps[i];
|
||||
if (runtime?.state === 'done' || runtime?.state === 'skipped') continue;
|
||||
if (runtime?.state === 'awaiting') return { kind: 'await', step, index: i };
|
||||
|
||||
if (step.when !== undefined && !evaluate(step.when, scope)) {
|
||||
return { kind: 'skip', step, index: i, reason: `条件不成立:${step.when}` };
|
||||
}
|
||||
return { kind: 'run', step, index: i };
|
||||
}
|
||||
return { kind: 'finished' };
|
||||
}
|
||||
|
||||
export function markStep(position: FlowPosition, index: number, patch: Partial<StepRuntime>): FlowPosition {
|
||||
const steps = position.steps.map((s, i) => (i === index ? { ...s, ...patch } : s));
|
||||
const advanced = patch.state === 'done' || patch.state === 'skipped';
|
||||
return { index: advanced ? index + 1 : index, steps };
|
||||
}
|
||||
|
||||
/* ── 失败与缺口:声明给定,不临场(AM 5.3) ────────────────────────── */
|
||||
|
||||
export type FailureAction =
|
||||
| { readonly kind: 'retry'; readonly attempt: number; readonly backoff: string }
|
||||
/** 转人兜底:改发人类工具卡,卡内容随声明 */
|
||||
| { readonly kind: 'human'; readonly workOrder: 'execution' | 'acceptance' }
|
||||
/** 受阻待人:挂起,携阻因 */
|
||||
| { readonly kind: 'blocked' };
|
||||
|
||||
/**
|
||||
* 「每步声明重试次数与间隔;重试仍败,按声明走下一步。
|
||||
* **flow 内不得出现『由 AI 决定怎么办』的失败处理** —— 需要临场,即说明该活动不该是 flow。」
|
||||
*/
|
||||
export function failureAction(step: FlowStep, attempt: number): FailureAction {
|
||||
if (attempt < step.failure.retry.times) {
|
||||
return { kind: 'retry', attempt: attempt + 1, backoff: step.failure.retry.backoff };
|
||||
}
|
||||
return step.failure.fallback.kind === 'human'
|
||||
? { kind: 'human', workOrder: step.failure.fallback.workOrder }
|
||||
: { kind: 'blocked' };
|
||||
}
|
||||
|
||||
/* ── 静态检查:谱必须全程可静态检查(AM 12.3) ────────────────────── */
|
||||
|
||||
export type FlowIssue = { readonly step: string; readonly message: string };
|
||||
|
||||
export function checkDeclaration(steps: readonly FlowStep[]): FlowIssue[] {
|
||||
const out: FlowIssue[] = [];
|
||||
const names = new Set<string>();
|
||||
for (const step of steps) {
|
||||
if (names.has(step.name)) out.push({ step: step.name, message: '步名重复' });
|
||||
names.add(step.name);
|
||||
// 步序声明完整:步型 / 入参映射 / 出参写入 / 完成条件 / 失败策略齐备
|
||||
if (step.failure.retry.times < 0) out.push({ step: step.name, message: '重试次数不得为负' });
|
||||
if (step.kind === 'human' && step.deadline.trim() === '') {
|
||||
out.push({ step: step.name, message: '人类工具步必配时限 —— 无死等' });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 终态映射(AM 5.3):放行完成 = 末步验收工单「放行通过」 */
|
||||
export function lastStepIsAcceptance(steps: readonly FlowStep[]): boolean {
|
||||
const last = steps.at(-1);
|
||||
return last !== undefined && last.kind === 'human' && last.workOrder === 'acceptance';
|
||||
}
|
||||
|
||||
/** 并行组:组内步无依赖、**全完成再继续** */
|
||||
export function parallelGroupComplete(position: FlowPosition, members: readonly string[]): boolean {
|
||||
return members.every((name) => {
|
||||
const runtime = position.steps.find((s) => s.name === name);
|
||||
return runtime?.state === 'done' || runtime?.state === 'skipped';
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user