三面拆分⑫:设计台九区一级目录 —— 一区一内容,测试台收成单例仿真

设计中心从「蓝图 + 四库」摊成九区,顺序即设计的次序:
蓝图 → 触发器 → 流程包 → 活动库 → 工具箱 → 数据域 → 知识库 → 大模型 → 测试台。
触发器与大模型从「并进流程包 / 塞在系统区」里拿出来独立成区 —— 前者与流程包平级
(不入包结构、不随包版本),后者是设计者天天要碰的东西;知识库先立个诚实的空占位。

一区一内容,于是区内页签栏整条删掉(壳层在只剩一格时收起):
  · 流程包:总览的三枚计数并进货架顶端
  · 测试台:入站邮件演练移到触发器区就地验(它验的正是那一区定的规则)
  · 大模型:从系统区拆出,系统区只剩通道实例

测试台一次只跑一条流。起单前先销毁上一单,刷新页面能认回在跑的那一单,
「停止仿真」把工作流连同它派出的任务、工单、实例、留痕一并拔掉 ——
不留半单挂在别人工作台上。实测:起单 → 1 单落到 AI 工作台 → 停止 → 库里零残留。

功能工具补中文名(migration 0018,读回退到逻辑名),治理台一律主显示中文名。
顺带清掉几处说教式文案(「这些是工具,不是模型 —— 模型是它们跑在上面的发动机…」那类)。

五闸:typecheck / lint / depcruise / test 绿;api:check 快照随本提交入库。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
2026-07-29 04:35:36 +08:00
co-authored by Claude Opus 5
parent ffb71def4b
commit 8cb00d1ff6
28 changed files with 367 additions and 135 deletions
+4
View File
@@ -29,6 +29,10 @@
`human`(**人类工具**,系统内化、零声明即得 —— 调人是 Harness 给的能力)、
`llm`(**LLM 五件**:抽取/归类/摘要/裁断/改写,经模型网关一缝;
仅当活动声明了模型用途名才上总线 —— 没声明却会问模型是越权,用量也无从归因)
- **一名两用**(2026-07-29):`name` 是**机器名**(全局唯一逻辑名,活动按它声明白名单、
调用按它寻址),`displayName` 是**人名**(治理台一律主显示中文名,逻辑名退居副标)。
`crm.history-price-read` 对机器精确、对人不友好,一屏几十件工具时尤甚;
旧工具未填时读回退到 `name`(加法,非破坏)
- **一切工具执行经 `ToolInvoker`** —— 绕过它,回放审就对它失明(§12.5#10)
- `replay` 模式**绝不回退实调**:缺录即 `ToolReplayMissError`
- **白名单在结构上拦截**:功能工具按活动白名单,纯工具按 `ownerActivityId` 私有作用域;
@@ -11,6 +11,7 @@ import type { ToolContract } from '../internal/domain/tool-contract.ts';
const CONTRACT: ToolContract = {
name: 'erp.stock-read',
displayName: 'ERP 库存查询',
version: '1.0.0',
description: '读取 ERP 现货库存:PN → 库存量',
input: { type: 'object', required: ['pn'], properties: { pn: { type: 'string' } } },
@@ -6,6 +6,7 @@ import { auditPassed, staticAudit } from '../internal/domain/registration-audit.
const contract = (over: Partial<ToolContract> = {}): ToolContract => ({
name: 'crm.customer-read',
displayName: '客户档案查询',
version: '1.0.0',
description: '读取客户资料库中的客户档案,用于取客户等级与付款条款',
input: { type: 'object', properties: { customerCode: { type: 'string' } }, required: ['customerCode'] },
@@ -7,6 +7,8 @@ import { sys } from '@shared/db.ts';
export const toolTable = sys.table(
'tool',
{
/** 中文名 —— 人读的主名(治理台主显示);存量行以逻辑名兜底 */
displayName: text('display_name'),
name: text('name').primaryKey(),
version: text('version').notNull(),
description: text('description').notNull(),
@@ -15,6 +15,8 @@ type Row = typeof toolTable.$inferSelect;
function toContract(row: Row): ToolContract {
return {
name: row.name,
// 存量行没中文名就以逻辑名兜底 —— 界面上宁可显示机器名,不显示空白
displayName: row.displayName ?? row.name,
version: row.version,
description: row.description,
input: row.input as Record<string, unknown>,
@@ -50,6 +52,7 @@ export function createToolRegistry(db: Db): ToolRegistry {
name: contract.name,
version: contract.version,
description: contract.description,
displayName: contract.displayName,
input: contract.input,
output: contract.output,
effect: contract.effect,
@@ -70,6 +73,7 @@ export function createToolRegistry(db: Db): ToolRegistry {
target: toolTable.name,
set: {
version: values.version,
displayName: values.displayName,
description: values.description,
input: values.input,
output: values.output,
@@ -11,8 +11,13 @@ export type EffectLevel = (typeof EFFECT_LEVELS)[number];
export const EFFECT_LEVEL_LABEL: Readonly<Record<EffectLevel, string>> = { read: '读', write: '写' };
export type ToolContract = {
/** 全局唯一逻辑名 */
/** 全局唯一逻辑名 —— 机器用:活动按它声明白名单、调用按它寻址 */
readonly name: string;
/**
* 中文名 —— **人用**:治理台一律主显示它,逻辑名退居副标。
* 「crm.history-price-read」对机器精确,对人不友好;一屏几十件工具时尤其。
*/
readonly displayName: string;
readonly version: string;
/** 面向 AI 的适用说明:何时用、语义、边界 */
readonly description: string;