從需求到可審查應用: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 和人一起維護一套可治理、可演進、能進入生產的業務系統。