> Discover all available pages from the documentation index: https://mastra.zisheng.pro/zh-TW/llms.txt # 建置 coding agent 本指南將帶你建置一個與 Mastra Code、Claude Code 或 Codex 同類的小型 coding agent 應用程式。你會使用 `buildBasePrompt()` 與 `createCodingAgent()` 建立 coding agent,將它包裝在 `AgentController` 中以支援互動式 session 與 Tool 核准,並在使用 pi-tui 建置的終端機 UI 中執行 controller。 下方影片展示你將建置的 coding agent 實際運作情形。 ## 先決條件 - 已安裝 Node.js `v22.19.0` 或更新版本 - 受支援的 [Model Provider](https://mastra.zisheng.pro/zh-TW/models) 所提供的 API 金鑰 - 現有的 Mastra 專案。如有需要,請依照[安裝指南](https://mastra.zisheng.pro/zh-TW/guides/getting-started/quickstart)操作。 ## 安裝終端機相依套件 安裝 [pi-tui](https://github.com/earendil-works/pi/tree/main/packages/tui) 與 `tsx`: **npm**: ```bash npm install @earendil-works/pi-tui npm install --save-dev tsx ``` **pnpm**: ```bash pnpm add @earendil-works/pi-tui pnpm add --save-dev tsx ``` **Yarn**: ```bash yarn add @earendil-works/pi-tui yarn add --dev tsx ``` **Bun**: ```bash bun add @earendil-works/pi-tui bun add --dev tsx ``` pi-tui 提供終端機 renderer 與輸入編輯器。`tsx` 會直接執行 TypeScript 進入點。 ## 建立 coding agent 建立 `src/mastra/agents/coding-agent.ts`。prompt 會描述目前的專案,並將 prompt 中的一般 Tool 名稱對應至預設 Workspace 提供的 Tool。 ```typescript import { basename } from 'node:path' import { buildBasePrompt, createCodingAgent } from '@mastra/core/coding-agent' export const projectPath = process.cwd() const model = 'openai/gpt-5.6-sol' const instructions = buildBasePrompt({ projectPath, projectName: basename(projectPath), platform: process.platform, date: new Date().toISOString().slice(0, 10), mode: 'build', modelId: model, productName: 'My Coding Agent', coAuthorName: 'My Coding Agent', coAuthorEmail: 'coding-agent@example.com', toolGuidance: `# Workspace tools - Use mastra_workspace_read_file for view. - Use mastra_workspace_list_files for find_files. - Use mastra_workspace_grep for search_content. - Use mastra_workspace_execute_command for execute_command. - Use mastra_workspace_write_file, mastra_workspace_edit_file, and mastra_workspace_file_stat for writing, editing, and inspecting file metadata. - Use only the workspace tools provided to you. Do not attempt unavailable capabilities.`, }) export const codingAgent = createCodingAgent({ id: 'coding-agent', name: 'Coding Agent', model, instructions, basePath: projectPath, }) ``` 請將品牌相關值替換為 Agent 的名稱與共同作者詳細資料。`createCodingAgent()` 會提供本機 filesystem 與 Sandbox Workspace,以及從暫時性 Provider 錯誤中復原的預設設定。`basePath` 會限定 filesystem Tool 的作用範圍,並設定命令的初始工作目錄。你傳給 factory 的任何設定都會優先於其預設值。 預設的本機 Sandbox 會直接在 host 上執行命令,不提供隔離,因此 `basePath` 並非作業系統層級的安全邊界。此範例在終端機中加入逐次 Tool 呼叫核准,但你仍應只針對可信任的本機專案執行。 ## 註冊 coding agent 在 `src/mastra/index.ts` 中,像註冊其他 Mastra Agent 一樣註冊回傳的 Agent。註冊後,也能在 Studio 與透過 Mastra server 使用底層 Agent。 ```typescript import { Mastra } from '@mastra/core/mastra' import { codingAgent } from './agents/coding-agent' export const mastra = new Mastra({ agents: { codingAgent }, }) ``` ## 測試 coding agent 新增終端機介面前,請先在 Studio 中驗證底層 Agent。開發伺服器啟動時,其工作目錄是 `src/mastra/public`,因此請在該處加入不含敏感資訊的檔案供 Agent 檢查: ```md # Project notes Name: Acme support portal Status: In development Owner: Platform team ``` 啟動開發伺服器: **npm**: ```bash npm run dev ``` **pnpm**: ```bash pnpm run dev ``` **Yarn**: ```bash yarn dev ``` **Bun**: ```bash bun run dev ``` 開啟 [Studio](https://mastra.zisheng.pro/zh-TW/docs/studio/overview),選取 **Coding Agent**,然後輸入: ```text Inspect project-notes.md and report the project name, status, and owner. Do not modify files. ``` 回應應辨識出 Acme support portal 並說明其開發狀態,也應指出專案由 Platform team 負責,且不變更檔案。Model 的措辭可能有所不同。 ## 建立 Agent controller 建立 `src/mastra/coding-agent-controller.ts`。controller 負責管理互動式 session、公開 UI 事件,並暫停 Workspace Tool 以等待核准。 ```typescript import { AgentController } from '@mastra/core/agent-controller' import { codingAgent, projectPath } from './agents/coding-agent' export async function createCodingAgentSession() { const workspace = await codingAgent.getWorkspace() if (!workspace) { throw new Error('The coding agent requires a workspace.') } const controller = new AgentController({ id: 'coding-agent-controller', agent: codingAgent, workspace, modes: [{ id: 'build', name: 'Build', metadata: { default: true } }], disableBuiltinTools: [ 'ask_user', 'submit_plan', 'task_write', 'task_update', 'task_complete', 'task_check', 'subagent', ], }) await controller.init() const session = await controller.createSession({ id: 'local-session', ownerId: 'local-user', resourceId: projectPath, }) return { controller, session } } ``` 此範例使用一種 mode,並停用 controller 的其他內建 Tool,讓入門 UI 能專注於 Workspace 執行。這個簡化設定是為本教學而設計。在正式環境的應用程式中,請啟用產品所需的內建 Tool 並實作其 UI 流程:`ask_user` 與 `submit_plan` 等互動式 Tool 會暫停,直到介面恢復執行;task 與 subagent Tool 則有各自的生命週期事件。請參閱 [Tool 核准與暫停](https://mastra.zisheng.pro/zh-TW/docs/harness/agent-controller)。 此範例也省略了 storage,因此對話只會在目前處理程序存續期間保留。日後需要恢復 session 時,可以再加入 storage。 ## 建置終端機 UI 建立 `src/coding-agent-tui.ts`。UI 會 render 助理訊息更新並顯示 Tool 活動,也會要求使用者核准或拒絕每次 Workspace Tool 呼叫。 ```typescript import { pathToFileURL } from 'node:url' import { Editor, matchesKey, ProcessTerminal, Text, TUI, type EditorTheme, type Terminal, } from '@earendil-works/pi-tui' import { createCodingAgentSession } from './mastra/coding-agent-controller' const plain = (text: string) => text const editorTheme: EditorTheme = { borderColor: plain, selectList: { selectedPrefix: plain, selectedText: plain, description: plain, scrollInfo: plain, noMatch: plain, }, } function getText(message: { content: Array<{ type: string; text?: string }> }) { return message.content .filter(part => part.type === 'text') .map(part => part.text ?? '') .join('') } export async function startCodingAgentTui(terminal: Terminal = new ProcessTerminal()) { const { controller, session } = await createCodingAgentSession() const tui = new TUI(terminal) const output = new Text('Ask me to inspect or change this project.', 1, 0) const editor = new Editor(tui, editorTheme) let busy = false let pendingApproval: { toolCallId: string; toolName: string } | undefined const showError = (error: unknown) => { output.setText(`Error: ${error instanceof Error ? error.message : String(error)}`) busy = false pendingApproval = undefined tui.requestRender() } const unsubscribe = session.subscribe(event => { if (event.type === 'message_update' && event.message.role === 'assistant') { output.setText(getText(event.message)) } else if (event.type === 'tool_start') { output.setText(`Running ${event.toolName}...`) } else if (event.type === 'tool_approval_required') { pendingApproval = { toolCallId: event.toolCallId, toolName: event.toolName } output.setText(`Allow ${event.toolName}? Enter y or n.`) } else if (event.type === 'agent_end') { busy = false } else if (event.type === 'error') { showError(event.error) return } tui.requestRender() }) editor.onSubmit = value => { if (pendingApproval) { const answer = value.trim().toLowerCase() if (answer !== 'y' && answer !== 'n') { output.setText(`Allow ${pendingApproval.toolName}? Enter y or n.`) tui.requestRender() return } const approval = pendingApproval pendingApproval = undefined session.respondToToolApproval({ toolCallId: approval.toolCallId, decision: answer === 'y' ? 'approve' : 'decline', }) return } if (busy || !value.trim()) return busy = true output.setText('Thinking...') tui.requestRender() void session.sendMessage({ content: value.trim() }).catch(showError) } tui.addChild(new Text('My Coding Agent', 1, 0)) tui.addChild(output) tui.addChild(editor) tui.setFocus(editor) let stopPromise: Promise | undefined let removeInputListener = () => {} const stop = () => { stopPromise ??= (async () => { process.off('SIGINT', handleExit) removeInputListener() session.abort() unsubscribe() tui.stop() await controller.destroy() })() return stopPromise } const handleExit = () => { void stop().catch(error => { console.error(error) process.exitCode = 1 }) } removeInputListener = tui.addInputListener(data => { if (!matchesKey(data, 'ctrl+c')) return handleExit() return { consume: true } }) process.once('SIGINT', handleExit) tui.start() return { stop } } if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { await startCodingAgentTui() } ``` 選用的 `Terminal` 參數可支援自動化測試,正常執行時則使用 `ProcessTerminal`。UI 刻意只 render 最新回應;`Session` 仍會保留對話 context,以供後續 prompt 使用。 ## 執行 coding agent 從專案根目錄啟動終端機應用程式,讓 `process.cwd()` 指向你要 Agent 使用的專案: **npm**: ```bash npx tsx src/coding-agent-tui.ts ``` **pnpm**: ```bash pnpm dlx tsx src/coding-agent-tui.ts ``` **Yarn**: ```bash yarn dlx tsx src/coding-agent-tui.ts ``` **Bun**: ```bash bun x tsx src/coding-agent-tui.ts ``` 輸入以下 prompt: ```text Inspect package.json and report the package name and available scripts. Do not modify files. ``` 當 controller 詢問是否允許 `mastra_workspace_read_file` 時,輸入 `y`。Agent 會讀取 `package.json` 並回報找到的內容。Model 的措辭可能有所不同,但回應應包含 package 名稱及其 scripts,且不變更檔案。 按下 **Ctrl+C** 關閉應用程式並銷毀 controller。 ## 後續步驟 你可以在此基礎上進一步擴充: - 新增 storage,以保存及恢復 controller session - 新增更多具有不同 instructions 與 Workspace Tool allowlist 的 mode - 將最新回應元件替換為能 render Tool 呼叫與結果的逐字稿 - 在接受不可信任的 prompt 或散布應用程式前,加入 Sandbox 隔離 深入瞭解: - [`createCodingAgent()` 參考文件](https://mastra.zisheng.pro/zh-TW/reference/coding-agent/create-coding-agent) - [`buildBasePrompt()` 參考文件](https://mastra.zisheng.pro/zh-TW/reference/coding-agent/build-base-prompt) - [AgentController 概觀](https://mastra.zisheng.pro/zh-TW/docs/harness/agent-controller) - [`AgentController` 參考文件](https://mastra.zisheng.pro/zh-TW/reference/agent-controller/agent-controller-class) - [Workspace 概觀](https://mastra.zisheng.pro/zh-TW/docs/workspace/overview)