> Discover all available pages from the documentation index: https://mastra.zisheng.pro/ko/llms.txt # 코딩 Agent 구축 이 가이드에서는 Mastra Code, Claude Code 또는 Codex와 동일한 범주에 있는 소규모 코딩 Agent 애플리케이션을 구축합니다. 다음을 사용하여 코딩 Agent를 만듭니다.`buildBasePrompt()`그리고`createCodingAgent()`, 그것을 포장`AgentController`대화형 세션 및 Tool 승인을 위해 pi-tui로 구축된 터미널 UI에서 컨트롤러를 실행합니다. 아래 비디오는 실제로 구축할 코딩 Agent를 보여줍니다. ## 전제조건 - Node.js `v22.19.0` 이상 설치 - 지원되는 [Model Provider](https://mastra.zisheng.pro/ko/models)의 API 키 - 기존 Mastra 프로젝트. 필요한 경우 [설치 가이드](https://mastra.zisheng.pro/ko/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 진입점을 직접 실행합니다. ## 코딩 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()`는 일시적인 Provider 오류를 복구하기 위한 기본 설정과 함께 로컬 파일 시스템 및 Sandbox Workspace를 제공합니다. `basePath`는 파일 시스템 Tool의 범위를 제한하고 명령의 초기 작업 디렉터리를 설정합니다. 팩터리에 전달한 구성은 모두 기본값보다 우선합니다. 기본 로컬 Sandbox는 격리 없이 호스트에서 직접 명령을 실행하므로 `basePath`는 운영 체제 수준의 보안 경계가 아닙니다. 이 예제는 터미널에서 Tool 호출별 승인을 추가하지만, 그래도 신뢰할 수 있는 로컬 프로젝트에서만 실행해야 합니다. ## 코딩 Agent 등록 다른 Mastra Agent와 마찬가지로 반환된 Agent를 `src/mastra/index.ts`에 등록합니다. 등록하면 기반 Agent를 Studio와 Mastra 서버에서도 사용할 수 있습니다. ```typescript import { Mastra } from '@mastra/core/mastra' import { codingAgent } from './agents/coding-agent' export const mastra = new Mastra({ agents: { codingAgent }, }) ``` ## 코딩 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/ko/docs/studio/overview)를 열고 **Coding Agent**를 선택한 후 다음을 입력하세요. ```text Inspect project-notes.md and report the project name, status, and owner. Do not modify files. ``` 응답에서는 Acme 지원 포털을 식별하고 개발 상태를 설명해야 합니다. 플랫폼 팀이 프로젝트를 소유하고 있음을 보고하고 파일을 변경하지 않은 상태로 두어야 합니다. Model 문구는 다를 수 있습니다. ## Agent 컨트롤러 만들기 `src/mastra/coding-agent-controller.ts`를 만드세요. 컨트롤러는 대화형 세션을 관리하고 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 } } ``` 이 예제에서는 하나의 모드를 사용하고 컨트롤러의 추가 내장 Tool을 비활성화하여 소개 UI가 Workspace 실행에 집중하도록 합니다. 이 튜토리얼에서는 단순화된 설정을 사용합니다. 프로덕션 애플리케이션에서는 제품에 필요한 내장 Tool을 활성화하고 UI 흐름을 구현하세요. `ask_user`와 `submit_plan`은 인터페이스에서 재개할 때까지 일시 중지되며, 작업 및 하위 Agent Tool에는 자체 수명 주기 이벤트가 있습니다. [Tool 승인 및 일시 중지](https://mastra.zisheng.pro/ko/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`은 후속 Prompt를 위한 대화 컨텍스트를 계속 유지합니다. ## 코딩 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. ``` 컨트롤러가 `mastra_workspace_read_file`의 허용 여부를 물으면 `y`를 입력하세요. Agent는 `package.json`을 읽고 확인한 내용을 보고합니다. Model의 표현은 달라질 수 있지만, 파일을 변경하지 않고 패키지 이름과 스크립트를 응답에 포함해야 합니다. **Ctrl+C**를 눌러 애플리케이션을 닫고 컨트롤러를 제거하세요. ## 다음 단계 이 기반을 다음으로 확장할 수 있습니다. - 컨트롤러 세션을 유지하고 재개하려면 스토리지를 추가하세요. - 다양한 지침과 작업 공간 Tool 허용 목록을 사용하여 더 많은 모드를 추가하세요. - 최신 응답 구성 요소를 Tool 호출 및 결과를 렌더링하는 기록으로 교체 - 신뢰할 수 없는 메시지를 수락하거나 애플리케이션을 배포하기 전에 샌드박스 격리를 추가하세요. 자세히 알아보기: - [`createCodingAgent()`참조](https://mastra.zisheng.pro/ko/reference/coding-agent/create-coding-agent) - [`buildBasePrompt()`참조](https://mastra.zisheng.pro/ko/reference/coding-agent/build-base-prompt) - [AgentController 개요](https://mastra.zisheng.pro/ko/docs/harness/agent-controller) - [`AgentController`참조](https://mastra.zisheng.pro/ko/reference/agent-controller/agent-controller-class) - [작업공간 개요](https://mastra.zisheng.pro/ko/docs/workspace/overview)