Back to blog
    Mastra + Vercel AI SDK + 裝置端 GGUF:無 API 成本的 TypeScript 行動代理堆疊
    mastravercel-ai-sdktypescriptreact-nativefine-tuningai-agentson-devicemobile

    Mastra + Vercel AI SDK + 裝置端 GGUF:無 API 成本的 TypeScript 行動代理堆疊

    TypeScript 優先的行動建構者不必使用 Python 代理框架。Mastra 與 Vercel AI SDK 加上透過 llama.cpp 在裝置端執行的微調 4B 模型,產生具有零按 token 成本的完整代理堆疊。

    EErtas Team·

    更新於 2026-05-10 — 反映本指南撰寫後 5 月初的 Mastra 釋出。5 月 1 日的釋出加入了新的 ChannelProvider 架構、具 OAuth 的 Slack provider、@mastra/nestjs 轉接器,以及 Google Drive WorkspaceFilesystem;5 月 4 日的釋出加入了基於關係的 FGA 授權、排程的 cron 工作流程,以及用於端到端瀏覽器自動化的新 @mastra/browser-viewer。這些都不改變下方的裝置端微調模型模式——它們擴展了周圍的平台。

    2026 年大多數代理框架論述仍假設 Python。LangGraph、CrewAI、AutoGen、Pydantic AI——典範參考都活在 Python 生態系。對於後端工程師與 ML 從業者,那是合理的預設。對於出貨 React Native、Expo 或混合 Capacitor app 的行動 app 建構者,那是錯的語言。TypeScript 程式碼庫不該需要 Python 旁路服務來執行代理。

    TypeScript 生態系現在有兩個解決這個的優秀代理框架。Mastra 跨越 22,000 顆 GitHub 星並於 2026 年 1 月出貨 1.0。Vercel AI SDK 近兩年來一直是事實上的串流優先工具包,現在支援以 TypeScript 撰寫的所有正式上線 LLM app 中有意義的一部分。兩者都與自託管模型乾淨運作、兩者都圍繞邊緣原生部署設計,且兩者都與在裝置端執行的微調 4B 模型異常契合。

    本指南走過完整的 TypeScript 原生行動代理堆疊:Mastra 用於編排、Vercel AI SDK 用於推論、Ertas 訓練的 Qwen3-4B 或 Gemma 4 E4B 模型匯出為 GGUF,以及 Ertas Deployment CLI 將其出貨進 React Native app。端到端,堆疊在初始訓練步驟後從不呼叫託管 API。

    兩個 TypeScript 框架,簡介

    Mastra 是較高層的選項。它在一個內建電池的套件中給你型別化代理定義、宣告式工作流程、耐久記憶、評估與 RAG 原語。工具定義是慣用 TypeScript 並搭配 Zod schema。工作流程是 step-based DAG,在程序重啟下存活。記憶與評估整合不需額外膠水。當你想要為 JavaScript 執行期塑造的完整代理平台時,Mastra 是你伸手取的。

    Vercel AI SDK 是較低層的選項。它暴露串流原語、透過 Zod 的結構化輸出,以及超過 90 個模型 provider 的通用 provider 抽象——Anthropic、OpenAI、Google、Mistral、Cohere,加上 Ollama 與 llama.cpp 等自託管 runner。SDK 也是 Mastra 用於推論呼叫的對象。所以實務上你不在一個或另一個之間選擇:Mastra 給你編排層、Vercel AI SDK 給你推論層,微調本地模型給你成本結構。

    這個組合是 TypeScript 對 Python 開發者整年建構的 Pydantic AI 加 Ollama 故事最接近的東西——但原生於行動建構者已使用的執行期。

    我們要建構什麼

    範例代理是 React Native 健身 app 的健身計畫者。代理讀取自然語言請求、選擇正確工具,並產生經驗證的計畫。它有三個工具:

    • get_user_profile() 回傳使用者的年齡、體重、訓練歷史
    • find_recent_workouts(limit: number) 將最近 N 個訓練作為結構化記錄回傳
    • propose_workout(focus: string, duration_min: number, difficulty: string) 產生結構化的訓練計畫

    輸出是 WorkoutPlan Zod 物件。Mastra 對 schema 驗證每個輸出。工具呼叫對其輸入 schema 驗證。整個代理在使用者手機內執行,除可選的遙測外無網路呼叫。

    這正是在前沿 API 上會迅速變昂貴的代理。每天記錄兩次訓練的使用者每次工作階段產生四到六次代理呼叫,多輪,具非平凡脈絡。在 10,000 月活躍使用者時,你在推論上花的錢比託管多。

    步驟 1:在 Studio 訓練模型

    開啟 Ertas Studio 並挑選基底模型。對於 2026 年 TypeScript 友善的行動部署,兩個強選擇是 Qwen3-4B-Instruct 與 Gemma 4 E4B。兩者都舒適裝在現代手機上(Q4_K_M 下約 2.5 GB)、兩者在微調後都產生可靠的結構化輸出,且兩者都與 llama.cpp 的行動 FFI 運作。Qwen3 在多步驟工具呼叫上略佔優勢;Gemma 4 E4B 在指令遵循細微之處略佔優勢。任一個都是不錯的起點。

    在 Data Craft 中定義工具 schema。Studio 讀取你的工具簽章(以 Zod schema、JSON Schema 或 TypeScript 函式簽章本身貼上)並使用結構作為訓練目標。對於健身計畫者,目標約 500 個範例,涵蓋單工具呼叫、多工具序列與拒絕(超出範圍的請求)。

    以預設工具呼叫 QLoRA 設定訓練:rank 32、三個 epoch。驗證損失通常在 epoch 2.5 附近平緩。在標準 GPU 階層上,執行在一小時內完成。Studio 的評估套件回報工具名稱準確率、參數名稱準確率與參數值準確率。正式上線就緒的模型在這三項上都清過 95%。

    步驟 2:匯出為 GGUF 並出貨

    Studio 的匯出管線產生 GGUF 二進位。對於行動上的 4B 模型,Q4_K_M 是正確預設——磁碟上約 2.5 GB、約 3 GB 工作記憶體。

    對你既有的 React Native 專案執行 Ertas Deployment CLI:

    ertas deploy mobile \
      --project ./my-fitness-app \
      --model ertas-workout-agent-4b.gguf \
      --framework react-native
    

    CLI 處理三件歷史上吃掉 20 至 40 小時 llama.cpp 建置工程的事。它安裝行動 FFI 綁定(iOS 上有 Metal 後端、Android 上有 OpenCL/Vulkan 後端)。它在打包器中註冊 GGUF 資產,使模型隨 app 出貨。並它在 app 程序內豎立本地 HTTP 風格的推論端點——通常可在裝置本地 socket 上達到——它鏡像 Vercel AI SDK 已知如何呼叫的 OpenAI 相容 API 形狀。

    同樣的 CLI 支援 Flutter、原生 iOS Swift 與原生 Android Kotlin。可交付物的 TypeScript 形狀是這裡特定的部分。

    步驟 3:設定 Vercel AI SDK

    Vercel AI SDK 有社群維護的 Ollama provider 與指向任何 OpenAI 形狀端點的通用 OpenAI 相容 provider。Ertas Deployment CLI 預設以 OpenAI 相容形式暴露其裝置端端點,所以你像任何其他 provider 一樣接通它:

    import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
    
    export const ertasLocal = createOpenAICompatible({
      name: "ertas-on-device",
      baseURL: "http://localhost:8080/v1",
      apiKey: "not-needed",
    });
    
    export const workoutModel = ertasLocal("ertas-workout-agent-4b");
    

    開發中,你將 baseURL 指向筆記型電腦上的 Ollama(連接埠 11434)。在裝置上的生產中,Ertas Deployment CLI 在設定的連接埠暴露本地端點(預設 8080),且相同的 SDK 呼叫形狀運作而不需修改。SDK 不在乎推論在 app 內執行而非跨網路執行——它無論如何都看到相同的 OpenAI 相容回應串流。

    步驟 4:定義 Mastra 代理

    現在將 Mastra 接到本地模型並定義代理與工具:

    import { Agent } from "@mastra/core/agent";
    import { createTool } from "@mastra/core/tools";
    import { z } from "zod";
    import { workoutModel } from "./ertas-local";
    
    const WorkoutPlan = z.object({
      focus: z.string(),
      duration_min: z.number(),
      difficulty: z.enum(["easy", "moderate", "hard"]),
      blocks: z.array(
        z.object({
          name: z.string(),
          sets: z.number(),
          reps: z.number(),
        }),
      ),
    });
    
    const getUserProfile = createTool({
      id: "get_user_profile",
      description: "Get the current user's age, weight, and training history.",
      inputSchema: z.object({}),
      outputSchema: z.object({
        age: z.number(),
        weight_kg: z.number(),
        history: z.array(z.string()),
      }),
      execute: async () => fitnessDb.getProfile(),
    });
    
    const findRecentWorkouts = createTool({
      id: "find_recent_workouts",
      description: "Return the user's most recent workouts.",
      inputSchema: z.object({ limit: z.number().default(5) }),
      outputSchema: z.array(
        z.object({ date: z.string(), name: z.string(), notes: z.string() }),
      ),
      execute: async ({ context }) => fitnessDb.recent(context.limit),
    });
    
    const proposeWorkout = createTool({
      id: "propose_workout",
      description: "Produce a structured workout plan for the user.",
      inputSchema: z.object({
        focus: z.string(),
        duration_min: z.number(),
        difficulty: z.enum(["easy", "moderate", "hard"]),
      }),
      outputSchema: WorkoutPlan,
      execute: async ({ context }) => planner.generate(context),
    });
    
    export const workoutAgent = new Agent({
      name: "workout-planner",
      instructions:
        "You plan workouts. Use the available tools to read the user's history before proposing a plan.",
      model: workoutModel,
      tools: { getUserProfile, findRecentWorkouts, proposeWorkout },
    });
    
    const result = await workoutAgent.generate(
      "Plan me a 45-minute moderate session focused on legs.",
      { output: WorkoutPlan },
    );
    
    console.log(result.object);
    

    有兩件事在程式碼中沒有明顯展示。首先,代理在提議計畫前讀取使用者的個人資料與最近訓練,因為微調模型在建立該模式的範例上訓練過。通用開源權重模型常會跳過歷史查詢並提議通用計畫;訓練後的模型如設計地使用可用工具。其次,輸出由 Mastra 對 WorkoutPlan 驗證。如果模型發出無效物件,驗證器拒絕它,Mastra 呈現型別化錯誤。

    代理完全在裝置上執行。推論路徑中無 API 呼叫。使用者的個人資料資料從不離開手機。代理產生的唯一網路流量是你選擇加入的任何遙測。

    為什麼 TypeScript 原生對行動很重要

    出貨 React Native 或 Expo app 的行動團隊在代理層使用與 app 相同語言時有有意義的生產力優勢。型別定義從 Zod schema 流經 Mastra 進入 React Native UI,無需任何跨語言程式碼產生。代理中拋出的錯誤以型別化例外呈現於 React 樹中。來自 Vercel AI SDK 的串流回應接入行動開發者已使用的相同 useChat 風格 hook。

    基於 Python 的替代方案需要旁路服務。你要嘛執行 React Native app 呼叫的 Python 後端、要嘛將 CPython 嵌入行動二進位、要嘛找出某種跨執行期分割工作的混合方案。這三個選項都加入部署複雜性、套件大小與崩潰表面積。如果代理層已是 TypeScript,這些都不必要。

    Mastra 加 Vercel AI SDK 的組合契合行動建構者已在的執行期。Ertas Deployment CLI 契合相同執行期。端到端,你出貨單一 TypeScript app 並隨之有一個模型檔案。

    代理成本懸崖,再次

    經濟案例與基於 Python 的裝置端代理所做的相同,按行動使用模式擴展。行動 app 中的代理呼叫往往是高頻率與多輪的——健身 app、日記 app、行事曆 app、計畫者。每次呼叫成本在前沿 API 上依脈絡長度與工具深度約 $0.01 至 $0.04。

    典型使用率下的代表性行動 app 成本曲線:

    • 1,000 MAU,平均每天 4 次呼叫 → 每月推論大約 $120
    • 10,000 MAU → 每月大約 $1,200
    • 40,000 MAU → 每月大約 $4,800
    • 100,000 MAU → 每月大約 $12,000

    這些數字往往對行動團隊比對網頁團隊衝擊更大,因為行動貨幣化通常透過訂閱而非席位授權。你的單位經濟學在 10,000 至 40,000 MAU 區段開始崩壞——正是你嘗試投資成長而非從推論成本撤退的區段。

    在裝置端,成本結構固定。推論的邊際成本是電力。固定成本是裝置上模型的儲存,在安裝時一次支付。從 10,000 到 100,000 MAU 不會移動推論線。

    代理成本懸崖一直是塑造裝置端遷移的主導力量。TypeScript 行動建構者一直在等一個讓他們能對其回應而不放棄執行期的代理堆疊。

    你不必放棄什麼

    行動團隊在考慮裝置端轉移時通常提出三個顧慮,而 Mastra 加 Vercel AI SDK 加 Ertas 訓練模型堆疊處理每一個。

    串流。Vercel AI SDK 的串流原語對本地 llama.cpp 端點以與對託管 API 相同方式運作。你 React Native UI 中的逐 token 渲染不變。

    結構化輸出。SDK 與 Mastra 中的 Zod 驗證未變執行。微調讓結構化輸出夠可靠,使驗證很少失敗。

    記憶與工作流程。Mastra 的記憶與工作流程原語不依賴模型遠端執行。同樣的向量儲存、同樣的工作流程定義、同樣的評估工具,對本地模型運作。

    你確實放棄的是輕鬆切換到前沿模型的能力。一旦你微調並出貨特定模型,切換到不同模型意味著另一次微調。實務上,這是每個移到自託管模型的團隊都接受的同一取捨,而那是大多數行動團隊樂意換取可預期經濟模型的取捨。

    從原型到出貨

    典型管線看起來像:

    1. 對 API 進行原型。透過 Vercel AI SDK 對 Anthropic 或 OpenAI 建構 Mastra 代理。把工具弄對、證明價值。
    2. 整理資料集。來自原型的數百個範例,在 Data Craft 中對你的 Zod schema 驗證。
    3. 在 Studio 微調。迭代資料集直到評估指標清過 95%。
    4. 出貨至裝置。對你的 React Native 專案執行 Ertas Deployment CLI。將 Vercel AI SDK 設定中指向 API 的 provider 換成本地 provider。你的 Mastra 代理程式碼不變。
    5. 對追蹤迭代。生產追蹤成為下一輪訓練資料。Studio 支援從追蹤進行漸進式微調,因此模型改善而使用者資料停留在裝置端。

    前三個步驟過去是難處。Mastra 與 Vercel AI SDK 將原型至生產的差距從「數週的客製化串流程式碼」移到「一個下午的代理定義」。Studio 將資料集至微調的差距從 MLE-月縮短到小時。Ertas Deployment CLI 關閉了最後的差距——大多數 TypeScript app 建構者從不費心處理的那個,因為 llama.cpp 建置工程令人卻步。

    Mastra 加 Vercel AI SDK 加 Ertas 訓練裝置端模型是 TypeScript 行動建構者一直在等的代理堆疊。無 Python 旁路服務。無按 token 帳單。在 10,000 至 100,000 使用者間無成本懸崖。

    Ship AI that runs on your users' devices.

    Early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.

    Keep reading