从需求到可审查应用:AI 如何生成 ObjectStack 元数据
用设备报修场景拆开 AI Builder 的生成过程:对象、字段、关系、视图、权限、动作、流程、API 和 agent 工具如何由同一份元数据驱动。
先给结论:用设备报修场景走一遍——对象、字段、视图、权限、动作、流程、API 和 agent 工具都来自同一份元数据;这就是“生成可审查应用”和“一次性甩出代码”的根本区别。
你对 AI Builder 说一句话:
帮我做一个设备报修系统:员工能提交故障,系统能关联设备、分派工程师、跟踪状态、计算停机时间,高优先级自动派单,关闭工单前要填写维修结果和成本。
AI Builder 生成的第一版不只是页面,也有对象、字段、关系、权限、视图、流程、动作、API 和 agent 工具。人要 review 的不是一堆散落实现,而是一份能被运行时校验的业务系统规格。
这篇不泛讲“AI 生成应用”,而是用这个具体业务场景拆开看:ObjectStack 元数据到底生成了什么,为什么它比一次性代码生成更适合要审查、要权限、要审计的企业应用。

业务场景先说清楚
设备报修看起来简单,实际包含一条完整业务链:
- 一线员工发现设备异常,提交报修;
- 系统根据设备、位置、优先级和班组分派工程师;
- 工程师接单、到场、维修、补充照片和备注;
- 主管查看超时、停机时间和成本;
- 高风险或高成本工单进入审批;
- 工单关闭后形成设备维修历史。
如果用传统方式做,这会散落在数据表、API、页面、权限、状态机、通知、审计和报表里。
ObjectStack 的核心思路是把这些东西先描述成元数据。AI Builder 的任务不是凭空写一堆胶水代码,而是先起草一份业务系统规格。
| 业务问题 | 元数据能力 |
|---|---|
| 设备、工单、工程师是什么 | 对象与关系 |
| 哪些字段必填、可选、枚举 | 字段类型与校验 |
| 员工、工程师、主管看到什么 | 权限集与字段级权限 |
| 列表、看板、详情如何展示 | 视图元数据 |
| 派单、升级、关闭如何执行 | 动作与流程 |
| AI agent 能查询和操作什么 | 受控工具与审计 |
先建对象,而不是先画页面
AI Builder 第一步应该拆业务对象:
device:设备台账;repair_order:报修工单;repair_comment:维修备注和现场记录;user:员工、工程师、主管复用现有用户对象;team:班组或维修队。
真正的应用从对象开始,因为对象会同时影响 API、UI、权限、流程和 agent 工具。
下面是简化后的设备对象:
import { ObjectSchema, Field } from '@objectstack/spec/data';
export const Device = ObjectSchema.create({
name: 'device',
label: '设备',
fields: {
name: Field.text({ label: '设备名称', required: true }),
code: Field.text({ label: '设备编号', required: true, unique: true }),
location: Field.text({ label: '所在位置' }),
team: Field.lookup('team', { label: '负责班组' }),
status: Field.select({
label: '设备状态',
options: [
{ label: '运行中', value: 'running', default: true },
{ label: '维修中', value: 'maintenance' },
{ label: '停用', value: 'disabled' },
],
}),
},
});
注意这里已经不是“表单字段”这么简单。team 是关系,status 是枚举,code 是唯一约束。这些信息后面会被 API、页面、筛选、权限和 agent 一起使用。
工单对象承载业务规则
报修工单是业务主对象。它不仅记录文本,还要表达状态、优先级、关系、时间和成本。
export const RepairOrder = ObjectSchema.create({
name: 'repair_order',
label: '报修工单',
fields: {
title: Field.text({ label: '故障描述', required: true }),
device: Field.lookup('device', { label: '设备', required: true }),
reporter: Field.lookup('user', { label: '报修人', required: true }),
assignee: Field.lookup('user', { label: '工程师' }),
priority: Field.select({
label: '优先级',
options: [
{ label: '低', value: 'low' },
{ label: '中', value: 'medium', default: true },
{ label: '高', value: 'high' },
],
}),
status: Field.select({
label: '状态',
options: [
{ label: '待派单', value: 'pending', default: true },
{ label: '维修中', value: 'in_repair' },
{ label: '待验收', value: 'waiting_acceptance' },
{ label: '已关闭', value: 'closed' },
],
}),
reported_at: Field.datetime({ label: '报修时间', required: true }),
started_at: Field.datetime({ label: '开始维修时间' }),
closed_at: Field.datetime({ label: '关闭时间' }),
cost: Field.currency({ label: '维修成本' }),
photos: Field.image({ label: '现场照片', multiple: true }),
},
});
这些字段会带来几种派生能力:
lookup让工单和设备、用户建立关系;select让状态可以用于筛选、看板和流程判断;datetime让 SLA、停机时间和超时提醒可以被计算;currency让成本字段可以做字段级权限和审批条件;image让现场照片自动进入表单、详情页和文件权限体系。
公式和校验也应该是元数据
设备报修常见的指标是停机时间。它不一定需要落库,可以用公式字段表达:
import { cel } from '@objectstack/spec';
downtime_hours: Field.formula({
label: '停机时长',
expression: cel`
closed_at == null || reported_at == null
? null
: hours_between(reported_at, closed_at)
`,
});
关闭工单前,也可以要求填写维修结果:
resolution: Field.textarea({
label: '维修结果',
requiredWhen: cel`status == "closed"`,
});
这类规则如果写在页面里,API、agent 和批量操作可能绕过去。写成元数据后,校验会成为运行时的一部分。
视图元数据决定用户看到什么
企业应用不是只有一张列表。报修系统至少需要三类视图:
- 员工看“我提交的工单”;
- 工程师看“分派给我的工单”;
- 主管看“全部工单、超时工单、高成本工单”。
这些可以由视图元数据描述:
export const EngineerQueueView = {
object: 'repair_order',
label: '我的维修队列',
type: 'list',
filter: cel`assignee == $currentUser && status != "closed"`,
columns: ['title', 'device', 'priority', 'status', 'reported_at'],
sort: [{ field: 'priority', direction: 'desc' }, { field: 'reported_at', direction: 'asc' }],
};
export const RepairBoardView = {
object: 'repair_order',
label: '维修看板',
type: 'kanban',
groupBy: 'status',
cardFields: ['title', 'device', 'assignee', 'priority'],
};
视图不是纯前端配置。它定义了用户如何扫描业务,也影响 agent 如何向用户展示结果。例如 agent 回答“哪些高优先级工单还没处理”时,可以复用同样的对象、字段和筛选语义。
权限不是最后补的
同一个 repair_order,不同角色边界完全不同:
- 报修人只能创建和查看自己的工单;
- 工程师可以更新维修状态和备注,但不能看维修成本;
- 主管可以分派工程师、查看成本、关闭异常工单;
- 财务可以看成本,但不一定能改维修状态。
所以权限也必须进入元数据:
export const EngineerPermission = {
role: 'maintenance_engineer',
object: 'repair_order',
readable: cel`assignee == $currentUser`,
editable: cel`assignee == $currentUser && status != "closed"`,
fields: {
cost: { readable: false, editable: false },
resolution: { readable: true, editable: true },
photos: { readable: true, editable: true },
},
actions: {
start_repair: true,
submit_acceptance: true,
close_order: false,
},
};
这段元数据会影响 UI、API 和 agent。工程师在页面上看不到成本,通过 API 也读不到成本,agent 以工程师身份查询时同样拿不到成本。
这就是企业级 AI 应用必须强调权限的原因:agent 不能比用户拥有更多业务权力。
动作让业务行为可控
“开始维修”“提交验收”“关闭工单”不是普通字段更新,而是业务动作。动作可以定义输入、前置条件、权限和审计。
export const StartRepairAction = {
name: 'start_repair',
label: '开始维修',
object: 'repair_order',
availableWhen: cel`status == "pending" && assignee == $currentUser`,
input: {
started_at: Field.datetime({ label: '开始时间', default: 'now' }),
},
changes: {
status: 'in_repair',
started_at: '$input.started_at',
},
audit: true,
};
当动作被元数据化,三件事会变得清楚:
- 谁能看到这个按钮;
- 调用动作前要满足什么条件;
- 执行后哪些字段会变化,审计如何记录。
agent 调用的也应该是这种动作,而不是直接发一个任意 PATCH 请求。
流程把自动化和审批连起来
高优先级自动派单可以写成流程:
export const AutoAssignHighPriorityRepair = {
name: 'auto_assign_high_priority_repair',
trigger: {
object: 'repair_order',
event: 'afterInsert',
when: cel`priority == "high" && assignee == null`,
},
steps: [
{ action: 'find_on_duty_engineer', output: 'engineer' },
{ action: 'assign_repair_order', input: { assignee: '$engineer.id' } },
{ action: 'notify_user', input: { user: '$engineer.id' } },
],
};
高成本关闭则应该进入审批:
export const HighCostCloseApproval = {
object: 'repair_order',
action: 'close_order',
when: cel`cost > 5000`,
approvers: ['maintenance_manager'],
reason: '高成本维修需要主管审批',
};
这样 AI 可以建议流程,也可以触发低风险动作;但高风险动作仍然被审批和审计拦住。
API 和 agent 工具来自同一份元数据
对象定义完成后,平台可以投影出 API:
curl -X POST /api/v1/data/repair_order \
-d '{ "title": "3 号机床异响", "device": "dev_012", "priority": "high" }'
也可以投影出 agent 工具:
export const RepairAgentTools = [
tool.queryRecords('repair_order'),
tool.getRecord('repair_order'),
tool.runAction('repair_order', 'start_repair'),
tool.runAction('repair_order', 'submit_acceptance'),
];
于是用户可以问:
帮我找出今天还没开始维修的高优先级工单,并说明可能影响哪些设备。
agent 不需要猜数据库结构。它通过受控工具查询 repair_order,展开关联的 device,遵守当前用户权限,然后把结果总结给用户。
如果用户继续说“把第一条工单派给今天值班工程师”,agent 调用的是 assign_repair_order 动作;如果成本过高或权限不足,运行时会拒绝或进入审批。
这和一次性代码生成的区别
一次性代码生成的结果通常是很多文件:页面、API、数据库、权限判断、脚本。第一版看起来快,但后续需求变化会让代码逐渐分叉。
ObjectStack 元数据的价值在于:业务结构只有一个源头。
- 对象字段变了,API、表单、详情页和 agent 工具一起变;
- 权限变了,UI、API 和 agent 都遵守同一套规则;
- 动作变了,按钮、流程、审计和工具调用都跟着变;
- 视图变了,人和 agent 对业务队列的理解也同步变化。
AI Builder 不是替你绕过软件工程,而是把软件工程里最重要的结构先起草出来,让人可以审核,让平台可以运行,让后续变化继续落在同一份规格上。
从一句话到应用,真正发生了什么
用设备报修这个场景看,中间发生的是:
- 自然语言被拆成真实业务对象;
- 对象、字段、关系、公式和校验进入元数据;
- 视图、权限、动作和流程补齐业务边界;
- 平台把元数据投影成 API、界面和 agent 工具;
- AI 在权限内查询和建议,高风险动作进入审批;
- 人 review 一份小 diff 后继续迭代同一份规格。
这才是 AI-native 应用平台真正有价值的地方:不是让 AI 一次性写出一堆代码,而是让 AI 和人一起维护一套可治理、可演进、能进入生产的业务系统。