> Discover all available pages from the documentation index: https://mastra.zisheng.pro/llms.txt # 构建 Coding Agent 在本指南中,你将构建一个与 Mastra Code、Claude Code 或 Codex 同类的小型 Coding Agent 应用。你会使用 `buildBasePrompt()` 和 `createCodingAgent()` 创建 Coding Agent,再用 `AgentController` 封装它,以支持交互式会话和 Tool 审批,最后在使用 pi-tui 构建的终端 UI 中运行 controller。 下面的视频展示了你将构建的 Coding Agent 的实际运行效果。 ## 前提条件 - 已安装 Node.js `v22.19.0` 或更高版本 - 受支持的 [Model Provider](https://mastra.zisheng.pro/models) 提供的 API 密钥 - 现有的 Mastra 项目。如有需要,请按照[安装指南](https://mastra.zisheng.pro/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 提供终端渲染器和输入编辑器。`tsx` 用于直接运行 TypeScript 入口点。 ## 创建 Coding Agent 创建 `src/mastra/agents/coding-agent.ts`。提示词会描述当前项目,并将提示词中的通用 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()` 会提供本地文件系统和 Sandbox Workspace,并附带从临时 Provider 错误中恢复的默认设置。`basePath` 会限定文件系统 Tool 的作用域,并设置命令的初始工作目录。传给 factory 的任何配置都会优先于其默认值。 默认的本地 Sandbox 直接在宿主机上运行命令,不提供隔离,因此 `basePath` 并不是操作系统级的安全边界。本示例会在终端中为每次 Tool 调用添加审批,但你仍然应该只针对可信的本地项目运行它。 ## 注册 Coding Agent 像注册其他 Mastra Agent 一样,在 `src/mastra/index.ts` 中注册返回的 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/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 负责,且不更改文件。模型的具体措辞可能有所不同。 ## 创建 Agent controller 创建 `src/mastra/coding-agent-controller.ts`。controller 负责管理交互式会话、提供 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 } } ``` 本示例只使用一种模式,并禁用 controller 的额外内置 Tool,让入门 UI 可以专注于 Workspace 执行。这项简化设置仅用于本教程。在生产应用中,请启用产品需要的内置 Tool 并实现其 UI 流程:`ask_user` 和 `submit_plan` 等交互式 Tool 会暂停,直到界面将其恢复;任务和子 Agent Tool 则有自己的生命周期事件。请参阅 [Tool 审批和暂停](https://mastra.zisheng.pro/docs/harness/agent-controller)。 本示例也未配置存储,因此对话只会在当前进程期间保留。需要恢复会话时,可以在之后添加存储。 ## 构建终端 UI 创建 `src/coding-agent-tui.ts`。该 UI 会渲染助手消息更新并显示 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 有意只渲染最新响应。`Session` 仍会保留对话上下文,以供后续提示使用。 ## 运行 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 ``` 输入以下提示词: ```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` 并报告其中内容。模型的具体措辞会有所不同,但响应应该包含软件包名称及其脚本,且不会更改文件。 按 **Ctrl+C** 关闭应用并销毁 controller。 ## 后续步骤 你可以扩展此基础实现: - 添加存储,以持久化并恢复 controller 会话 - 添加更多具有不同 instructions 和 Workspace Tool allowlist 的模式 - 将只显示最新响应的组件替换成可渲染 Tool 调用和结果的对话记录 - 在接受不可信提示或分发应用前添加 Sandbox 隔离 了解更多: - [`createCodingAgent()` 参考文档](https://mastra.zisheng.pro/reference/coding-agent/create-coding-agent) - [`buildBasePrompt()` 参考文档](https://mastra.zisheng.pro/reference/coding-agent/build-base-prompt) - [AgentController 概览](https://mastra.zisheng.pro/docs/harness/agent-controller) - [`AgentController` 参考文档](https://mastra.zisheng.pro/reference/agent-controller/agent-controller-class) - [Workspace 概览](https://mastra.zisheng.pro/docs/workspace/overview)