Compiled AI:把提示變成可審計工作流

📌 本文重點

  • 用 LLM 在編譯階段產生狹義程式碼,執行期不再依賴 LLM
  • 業務邏輯可測試、可審計,安全面積與成本大幅下降
  • 特別適合高要求、高流量且規則穩定的企業級工作流

在多數專案裡,我們會每個請求都叫一次模型,把非結構化的 prompt 丟給 LLM,期待它「大致照規則」行事。問題是:結果不穩、難回溯、成本高,還容易被 prompt injection 玩壞。Compiled AI 要解的是這個痛點:

把原本寫在 prompt 里的業務邏輯,在編譯階段用 LLM 產生狹義程式碼,透過型別檢查、靜態分析與測試驗收,一旦通過就當成普通程式部署,執行期不再依賴 LLM

對開發者的直接好處:

  • 結果可預測且可審計:輸出由 deterministic code 決定,不是每次抽樣
  • 安全面縮小:執行期不再接受任意自然語言指令
  • 成本與延遲大幅下降:大量交易共用同一份已編譯邏輯,token 成本被攤薄

💡 關鍵: Compiled AI 把「每次都叫 LLM」改成「先編譯一次,再重複執行」,大幅提升穩定性並攤平成本。


重點說明

1. 架構:LLM 只在「編譯階段」出現

典型 Compiled AI 架構可以拆成三層:

  1. 模板/SDK 層(受控框架)
  2. 你先定義好已驗證的 workflow 模板與 SDK,例如:HTTP 呼叫、DB 查詢、RAG 檢索、權限檢查等
  3. LLM 只能在模板內部空洞生成狹義業務邏輯函式,例如 select_customer_segment()build_rag_query()

  4. 編譯管線(有 LLM)

  5. Step 1 – Prompt 設計:描述任務、提供 API 規格 & 範例,要求輸出符合特定語言/框架(如 TypeScript + 自家 SDK)
  6. Step 2 – 產生 code artifact:LLM 產生一份或多份候選程式碼
  7. Step 3 – 謝與測試:型別檢查、靜態分析、單元測試/合約測試,不過關就自動 loop 回 LLM 要求修正
  8. Step 4 – 人工審核 + 簽章(視風險):例如醫療或金流流程

  9. 執行期(無 LLM)

  10. 線上請求只執行已編譯出的 code artifact,全程 deterministic,可以完整 log & replay

核心概念:限制 LLM 的自由度,換取可測試、可版本控管的業務邏輯程式碼


2. 工作流設計:把一個傳統 RAG / Agent 重構成 Compiled AI

以一個典型 RAG QA 流程為例:

使用者問問題 → LLM 根據 prompt 決定如何搜尋 → 呼叫向量庫 → LLM 基於檢索結果生成答案

在傳統設計中,LLM 負責檢索策略 + 答案生成,兩段都高度隨機。重構成 Compiled AI,可以這樣拆:

  1. 編譯階段
  2. 用 LLM 生成一個狹義函式:build_search_plan(question: string) => SearchPlan,被嵌在已驗證的 RAG 模板中
  3. SearchPlan 僅包含:要查哪個 index、使用哪些 filter、topK、是否需要 fallback 等
  4. 再用 LLM 生成:synthesize_answer(question, contexts) => string,但這個函式需遵守強約束(例如:不可編造、必須引用 context id)

  5. 執行階段

  6. 每次 QA 時:
    • 用 deterministic code 呼叫 build_search_plan() → 打 DB / 向量庫
    • 把檢索到的 contexts 丟給 synthesize_answer()(也是普通函式)
  7. 整個過程再也不直接丟自然語言給 LLM,所有 decision path 都是可測試、可覆盤的程式碼

  8. 版本控管與灰度發布

  9. 每次重新編譯:產出新版本如 rag_workflow_v3.ts
  10. 用 Git tag & CI pipeline 把版本與測試報告綁在一起
  11. 線上:用 routing 或 feature flag 灰度流量,對比 v2 / v3 的成功率、平均延遲、查詢成本

3. 工程實務:與「每請求叫一次模型」的差異

基於現有研究與業界實測,Compiled AI 在幾個維度的典型差異:

  • 可靠性(確定性 vs 隨機)
  • 傳統:每次請求都用 sampling(temperature > 0),同一輸入結果可能不同
  • Compiled AI:執行期只跑 deterministic code + DB/RAG,相同輸入必然同樣輸出

  • 安全

  • 傳統:使用者輸入直接進 prompt,prompt injection 面積很大
  • Compiled AI:執行期輸入只餵進已定義的函式參數(string / enum / id 等),無法直接改變控制流

  • 成本與延遲

  • 編譯階段比較貴,但只做一次,之後可在大量請求上攤平
  • 實務上常見:十幾次交易後就開始比傳統架構便宜,長期 token 使用量可降數十倍

💡 關鍵: 只要請求次數夠多,執行期省下的 token 成本與延遲,會很快抵消一次性的編譯開銷。


實作範例

以下用 TypeScript + 假想 SDK 示範一個簡化版 pipeline。

1. 已驗證模板/SDK

// sdk.ts - 由你維護的安全 SDK

export type SearchPlan = {
  index: 'faq' | 'policy' | 'logs';
  topK: number;
  filters?: Record<string, string>;
};

export async function runRagWorkflow(
  question: string,
  buildSearchPlan: (q: string) => SearchPlan,
  synthesizeAnswer: (q: string, ctxs: string[]) => string
): Promise<string> {
  const plan = buildSearchPlan(question);
  const contexts = await vectorSearch(plan.index, question, plan.topK, plan.filters);
  return synthesizeAnswer(question, contexts);
}

async function vectorSearch(
  index: string,
  query: string,
  topK: number,
  filters?: Record<string, string>
): Promise<string[]> {
  // 已驗證的檢索實作
  /* ... */
  return [];
}

2. LLM 產生的狹義業務邏輯(編譯產物)

在編譯階段,你用一個 prompt 要求模型只實作兩個函式:

// generated_v3.ts - 由 LLM 產生,但要通過型別檢查與測試

import { SearchPlan } from './sdk';

export function buildSearchPlan(question: string): SearchPlan {
  const q = question.toLowerCase();

  if (q.includes('退款') || q.includes('billing') || q.includes('invoice')) {
    return { index: 'policy', topK: 5, filters: { category: 'refund' } };
  }

  if (q.includes('錯誤') || q.includes('error code')) {
    return { index: 'logs', topK: 10 };
  }

  return { index: 'faq', topK: 8 };
}

export function synthesizeAnswer(question: string, contexts: string[]): string {
  // 嚴格規定:
  // 1. 不得回答與 contexts 無關內容
  // 2. 必須在文末列出引用的 context 索引
  const summary = summarizeWithRules(question, contexts); // 你事先實作好的工具
  const citations = contexts
    .map((_, i) => `[#${i + 1}]`)
    .join(' ');

  return `${summary}\n\n引用來源:${citations}`;
}

3. 編譯 pipeline(CI 裡跑)

// compile.ts - 只在 CI / 開發環境執行

import { z } from 'zod';
import { callLLM } from './llm_client';
import { execSync } from 'child_process';

const schema = z.object({
  code: z.string(),
});

async function generateCode() {
  const prompt = `
你是一個 TypeScript AI,請只輸出一個檔案內容,實作:
- export function buildSearchPlan(question: string): SearchPlan
- export function synthesizeAnswer(question: string, contexts: string[]): string

必須符合已存在的型別定義:
- SearchPlan { index: 'faq' | 'policy' | 'logs'; topK: number; filters?: Record<string, string> }

禁止:
- 呼叫任何未在註解中允許的函式
- 使用 eval / new Function
- 動態匯入
  `;

  const raw = await callLLM({
    model: '**gpt-4.1**',
    temperature: 0,
    response_format: { type: 'json_schema', schema },
    messages: [{ role: 'user', content: prompt }],
  });

  const { code } = schema.parse(JSON.parse(raw));
  return code;
}

async function main() {
  const code = await generateCode();
  require('fs').writeFileSync('generated_v3.ts', code, 'utf8');

  // 1) 型別檢查
  execSync('npx tsc --noEmit', { stdio: 'inherit' });

  // 2) 靜態分析
  execSync('npx eslint generated_v3.ts', { stdio: 'inherit' });

  // 3) 測試
  execSync('npm test -- generated_v3.test.ts', { stdio: 'inherit' });
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});

搭配 CI/CD:

  • CI:觸發 compile.ts → 產出 generated_v3.ts → 跑測試 → 產出報表
  • CD:若通過,打 tag rag_workflow_v3,並更新配置讓 5% 流量導到 v3;監控成功率、latency、成本指標再逐步擴大

💡 關鍵: 把 LLM 產物納入 CI/CD(型別檢查、靜態分析、測試與灰度發佈),就能像管理普通程式碼一樣管理 AI 邏輯。


建議與注意事項

1. 業務規則變更頻率

Compiled AI 適合:

  • 規則相對穩定、但正確性要求高的流程(客服 QA、金融函數呼叫、醫療文件處理)

不適合:

  • 每天都在改規則、或依賴即時實驗的場景,因為每次改都要走「重新編譯 + 測試」流程
  • 高度探索型、open-ended 任務(創作、策略 brainstorm、UX 研究等)

實務建議:

  • 把流程拆成兩層:核心決策邏輯用 Compiled AI,外層的探索與創意仍然用即時 LLM

2. 測試覆蓋不足 = 把錯誤「編進系統」

Compiled AI 的風險是:一旦編譯出的邏輯有 bug,它會一直穩定地錯

必做:

  • 為生成函式設計合約測試:同一輸入 → 必須產出指定的 SearchPlan / 函式呼叫序列
  • 對關鍵場景建立 golden QA 測試集,每次編譯都跑完整 regression
  • 對模型產物加上防呆檢查(如:topK 不可大於 50,index 只能是白名單)

3. 安全與最小權限

  • 模板 / SDK 層要實施 最小權限
  • 不給生成函式直接打 DB 連線,只能呼叫封裝好的 queryCustomerById(id) 等高階 API
  • 不允許檔案寫入、外網 request 等敏感操作
  • 透過靜態分析檢查:是否有 eval、動態 import、直接執行 shell 等 pattern

4. 成本模型與「何時值得編譯」

可以用簡單估算:

  • 編譯一次成本:C_compile(幾萬 token)
  • 傳統架構每次請求成本:C_online
  • Compiled AI 每次請求成本:C_compiled(通常 ≪ C_online

只要請求數 N 滿足:

C_compile + N * C_compiled < N * C_online

就值得改成 Compiled AI。研究與實務常見:十幾到幾十次請求後就開始回本,高流量業務會很划算。

5. 與現有專案整合的落地步驟

  1. 先挑一個穩定且高流量的子流程(例如:FAQ RAG 檢索策略、金融 function calling 的參數整理)
  2. 為它抽象出一個窄介面函式(例如 buildSearchPlanprepareFunctionCallParams
  3. 建立 minimal 的編譯 pipeline(LLM → 生成 code → tsc + 測試)
  4. 先 offline 對比:新老流程在測試集上的正確率 / 成本
  5. 小流量灰度上線,配合 metrics 監控,逐步拓展到更多流程

結論:Compiled AI 的核心不是「用 LLM 寫程式」,而是把 prompt 中模糊的規則,轉成可測試、可簽章、可審計的程式碼工件。對需要穩定性、安全與成本控制的企業工作流,特別是 RAG、function calling、金融與醫療場景,是非常值得實驗的架構升級方向。

🚀 你現在可以做的事

  • 在現有系統中挑一段高流量、規則穩定的 RAG 或 function calling 流程,先抽象出一個窄介面函式如 buildSearchPlan
  • 建一個最小可行的編譯 pipeline:用 LLM 產生 TypeScript 函式 → 跑 tsc + 單元/合約測試 → 輸出 generated_v1.ts
  • 用 feature flag 灰度導入新流程,監控正確率、延遲與 token 成本,評估轉為 Compiled AI 的回本點

留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *