RooCode分析memo
emoji: "🤖"
type: "tech"
topics: ["ai", "vscode", "プロンプトエンジニアリング", "typescript", "architecture"]
published: false
title: "Roo Code AIエージェントの内部設計を徹底解剖 - プロンプトエンジニアリングから実装まで"Roo Code AIエージェントの内部設計を徹底解剖
目次
- はじめに
- Roo Codeとは何か
- システムアーキテクチャの全体像
- AIエージェントの動作フロー
- プロンプトエンジニアリングの技術
- ツールシステムの設計と実装
- APIプロバイダーシステム
- 実際の動作例で見る内部処理
- コード例とシーケンス図
- パフォーマンスと最適化
- セキュリティと安全性
- まとめと考察
はじめに
AIエージェントの時代が到来しています。その中でも、VS Code拡張として動作するRoo Codeは、自然言語でコミュニケーションし、ファイル操作からターミナル実行まで幅広いタスクを自律的に実行できる高度なAIコーディングエージェントです。
本記事では、Roo Code (v3.23.7) の内部実装を詳細に分析し、どのようなアーキテクチャでAIエージェントが動作しているかを解明します。プロンプトエンジニアリングのテクニック、ツール選択のメカニズム、APIプロバイダーの統合など、現代的なAI開発における実装の詳細を知りたい開発者・研究者の方に向けた技術深掘り記事です。
本記事で扱う内容
- 対象読者: AIエージェント開発者、プロンプトエンジニア、VS Code拡張開発者
- 分析対象: Roo-Code TypeScriptコードベース (約20の主要コンポーネント)
- 焦点: 内部アーキテクチャ、プロンプト設計、ツール実装、API統合
それでは、Roo CodeのAIエージェントがどのような「魔法」を使っているのか、一緒に探っていきましょう。
Roo Codeとは何か
AIコーディングエージェントの新たな地平
Roo Codeは、VS Code拡張機能として提供される自律型AIコーディングエージェントです。従来のAIコーディングアシスタントとは一線を画する特徴を持っています:
- 自然言語での対話: 「RESTAPIを作って」「バグを修正して」などの自然な指示で動作
- 自律的なツール選択: 必要に応じてファイル読み書き、ターミナル実行、ブラウザ操作を自動選択
- マルチモード対応: コード作成、アーキテクチャ設計、デバッグ、質問応答など用途別の最適化
- プラガブル設計: MCPプロトコルによる外部ツール統合
技術的な位置づけ
Roo Codeは、AIエージェントの分類ではツールベースエージェントに位置します。事前に用意されたツール群を組み合わせてタスクを解決するアプローチを採用しており、以下の特徴を持ちます:
従来のAIアシスタント: コード提案のみ
Roo Code: コード提案 + 実行 + 検証 + 修正のループ
なぜ内部設計を知る必要があるのか
AIエージェントが「魔法のように」動作する背景には、緻密に設計されたアーキテクチャがあります。本記事では、以下の疑問に答えます:
- どのようなプロンプトでAIを制御しているのか?
- 数十のツールから適切なものをどう選択しているのか?
- 異なるAIプロバイダーをどう統合しているのか?
- リアルタイムなストリーミング応答をどう実現しているのか?
- 安全性とパフォーマンスをどう両立しているのか?
本記事の価値
この分析を通じて、読者は以下を得られます:
- 最新のプロンプトエンジニアリング技法の実践例
- AIエージェント開発のベストプラクティス
- マルチプロバイダー対応の設計パターン
- VS Code拡張機能における高度な統合手法
システムアーキテクチャの全体像
5層アーキテクチャの設計思想
Roo Codeは、明確に分離された5つの主要レイヤーから構成されています:
┌─────────────────────┐
│ Webview UI │ ← ユーザーインターフェース
├─────────────────────┤
│ ClineProvider │ ← タスク管理・状態管理
├─────────────────────┤
│ Task Manager │ ← 実行制御・ライフサイクル
├─────────────────────┤
│ Tool System │ ← ツール実行・結果処理
├─────────────────────┤
│ API Providers │ ← AIモデルとの通信
└─────────────────────┘
この設計により、以下の利点が実現されています:
- 責任の分離: 各層が明確な役割を持つ
- テスタビリティ: 層ごとの独立テストが可能
- 拡張性: 新しいツールやプロバイダーの追加が容易
- 保守性: 変更影響の局所化
各コンポーネントの詳細
1. Webview UI レイヤー
ファイル: webview-ui/src/App.tsx
ユーザーとの接点となるReactベースのUI層です:
// 主要な責務
- チャット形式でのユーザー入力
- リアルタイムでの実行進捗表示
- ツール実行結果の可視化
- 設定・モード管理
特徴的な機能:
- ストリーミング表示: AIの応答をリアルタイムで表示
- 差分ビューア: ファイル変更の視覚的確認
- 承認システム: 危険な操作前の確認UI
2. ClineProvider レイヤー
ファイル: src/core/webview/ClineProvider.ts
VSCode拡張機能の中核となる管理層です:
export class ClineProvider implements WebviewViewProvider {
private clineStack: Task[] = []
// タスクスタック管理
async addClineToStack(cline: Task) { /* */ }
async removeClineFromStack() { /* */ }
// API設定とプロファイル管理
private async getApiConfiguration() { /* */ }
}
主要機能:
- タスクスタック管理: 複数タスクの並行実行
- プロファイル管理: API設定の保存・切り替え
- Webviewとの通信: UI層とのメッセージハンドリング
3. Task Manager レイヤー
ファイル: src/core/task/Task.ts
タスクの実行制御とライフサイクル管理を担当:
export class Task extends EventEmitter<ClineEvents> {
readonly taskId: string
readonly instanceId: string
readonly rootTask: Task | undefined
readonly parentTask: Task | undefined
// メインの実行ループ
async initiateTaskLoop() {
while (this.state === "running") {
const userInput = await this.getUserInput()
const response = await this.processWithAI(userInput)
await this.executeTools(response)
}
}
}
設計の特徴:
- 階層的タスク: 親子関係のあるサブタスク対応
- イベント駆動: EventEmitterベースの状態通知
- 中断・再開: 任意のタイミングでの実行制御
4. Tool System レイヤー
ファイル: src/core/tools/
AIが使用する各種ツールの実装と管理:
// 統一されたツール実行インターフェース
export async function toolName(
args: ToolArgs,
cwd: string,
context: Context
): Promise<ToolResponse> {
// 1. パラメータ検証
// 2. 権限チェック
// 3. 実行処理
// 4. 結果の構造化
return response
}
主要ツール群:
-
writeToFileTool
: ファイル書き込み・差分表示 -
readFileTool
: ファイル読み込み・バッチ処理 -
executeCommandTool
: ターミナル実行・プロセス管理 -
codebaseSearchTool
: セマンティック検索・ベクトル検索
5. API Providers レイヤー
ファイル: src/api/providers/
複数のAIプロバイダーとの統合を担当:
export abstract class BaseProvider implements ApiHandler {
abstract createMessage(...): ApiStream
abstract getModel(): { id: string; info: ModelInfo }
// 統一されたストリーミングインターフェース
async *streamResponse(): AsyncGenerator<ApiStreamChunk> {
// プロバイダー固有の実装
}
}
対応プロバイダー:
- Anthropic: Claude系モデル(プロンプトキャッシング対応)
- OpenAI: GPT系モデル(O3/R1対応)
- Claude Code: CLI統合
- その他: Gemini、DeepSeek、Groq等
アーキテクチャの設計パターン
1. プラガブルアーキテクチャ
新しいツールやプロバイダーは、既存のインターフェースを実装するだけで追加可能です:
// 新しいツールの追加例
export async function customTool(args: ToolArgs): Promise<ToolResponse> {
// インターフェースに従った実装
}
// ツールマップに登録
const toolDescriptionMap = {
custom_tool: getCustomToolDescription,
// ...
}
2. イベント駆動パターン
各レイヤー間の通信は、EventEmitterベースのイベント駆動で実現:
// タスクイベントの例
task.on('response_started', () => { /* UI更新 */ })
task.on('tool_execution_started', (data) => { /* 進捗表示 */ })
task.on('tool_execution_finished', (result) => { /* 結果表示 */ })
3. ストリーミングファーストパターン
すべてのAPI通信は、AsyncGeneratorベースのストリーミングで設計:
for await (const chunk of apiStream) {
switch (chunk.type) {
case 'text':
await updateUI(chunk.content)
break
case 'tool_use':
await executeToolStreamingly(chunk)
break
}
}
なぜこのアーキテクチャなのか
この5層アーキテクチャが選択された理由は:
- 複雑性の管理: 各層が独立して進化可能
- パフォーマンス: ストリーミングによるレスポンシブな体験
- 拡張性: 新機能の追加時の影響を最小化
- テスタビリティ: 層ごとのモックとテストが容易
- 保守性: 責任境界の明確化
プロンプトエンジニアリングの技術
動的システムプロンプトの仕組み
Roo Codeの最も印象的な特徴の一つが、動的に構築されるシステムプロンプトです。これは静的なテンプレートではなく、実行環境や設定に応じてリアルタイムで組み立てられます。
システムプロンプトの構成要素
ファイル: src/core/prompts/system.ts
export const SYSTEM_PROMPT = async (context: ExtensionContext, ...args) => {
const basePrompt = `${roleDefinition}
${markdownFormattingSection()} // フォーマット指示
${getSharedToolUseSection()} // ツール使用共通規則
${getToolDescriptionsForMode(...)} // モード別ツール定義
${getToolUseGuidelinesSection(...)} // ツール使用ガイドライン
${mcpServersSection} // MCP統合情報
${getCapabilitiesSection(...)} // 利用可能機能
${modesSection} // モード別指示
${getRulesSection(...)} // 実行ルール
${getSystemInfoSection(cwd)} // システム情報
${getObjectiveSection(...)} // 目標・指針
${await addCustomInstructions(...)}` // カスタムインストラクション
return basePrompt
}
この構成により、以下が実現されています:
- 環境適応: ワークスペース、OS、シェル等の情報を自動反映
- モード対応: 選択されたモードに最適化された指示
- 機能制御: 利用可能なツールや機能の動的決定
- 個別化: ユーザー固有のカスタムインストラクション
制約ベースプロンプト設計
1. 明確な行動ルールの設定
ファイル: src/core/prompts/sections/rules.ts
Roo Codeでは、AIの行動を制御するため、非常に具体的なルールが設定されています:
export const getRulesSection = (config: RulesConfig) => {
return `
## 実行ルール
### ファイル操作の制約
- ファイル編集時は必ず完全な内容を提供すること
- 部分更新は絶対に行わないこと
- 編集前に必ずファイル内容を確認すること
### 検索の優先順位
- codebase_searchが利用可能な場合は必ず使用すること
- grep_searchは補助的な用途のみ
- ファイル名での検索より内容での検索を優先
### 会話スタイルの制約
- "Great", "Certainly", "I'll help you"等の定型句は使用禁止
- 実行内容を直接説明すること
- 不必要な前置きは避けること
`
}
2. エラー予防指示
AIが陥りがちな典型的なミスを事前に防ぐ指示が組み込まれています:
export const getToolUseGuidelinesSection = () => {
return `
## ツール使用ガイドライン
### ファイル読み込み前の確認
- 存在しないファイルを読み込もうとしてエラーになることを避ける
- list_filesで事前にファイル構造を確認する
### コマンド実行の注意点
- 長時間実行されるコマンドは事前に警告する
- 危険な操作(削除等)は必ずユーザーに確認を求める
- 相対パスではなく絶対パスを使用する
### 編集の品質管理
- コード欠損が発生していないか確認する
- インデントや構文エラーがないかチェックする
`
}
モード別プロンプト最適化
各モードの特徴的な指示
Roo Codeでは、タスクの種類に応じて4つの主要モードが用意されており、それぞれ異なるプロンプト設定を持ちます:
1. コードモード(デフォルト)
const codeMode = {
systemPrompt: "あなたは実践的なコーディングアシスタントです。",
availableTools: ["write_to_file", "read_file", "execute_command", "list_files"],
guidelines: "実装重視で、動作するコードの提供を最優先にしてください。"
}
2. アーキテクトモード
const architectMode = {
systemPrompt: "あなたは技術リーダーとして設計と計画を担当します。",
availableTools: ["codebase_search", "list_files", "read_file"],
guidelines: "実装前の設計検討を重視し、技術的な判断を行ってください。",
dynamicToolSelection: true // 動的ツール選択を有効化
}
3. 質問モード
const askMode = {
systemPrompt: "あなたは知識豊富な技術アドバイザーです。",
availableTools: ["read_file", "codebase_search"],
guidelines: "説明に重点を置き、実際の実装は行わないでください。"
}
プロンプト変数システム
動的な情報の注入
プロンプト内では、実行環境の情報が変数として注入されます:
export const getSystemInfoSection = (cwd: string) => {
return `
## 実行環境情報
現在の作業ディレクトリ: ${cwd}
OS: ${process.platform}
Node.js バージョン: ${process.version}
VSCode ワークスペース: ${workspace.name}
現在時刻: ${new Date().toISOString()}
利用可能なシェル: ${availableShells.join(', ')}
使用言語: ${userLanguage}
`
}
この情報により、AIは以下を適切に判断できます:
- パス解決: 相対パス vs 絶対パス
- コマンド選択: OS固有のコマンド
- ファイル形式: プロジェクトタイプの推定
XMLベースのツール制御
構造化されたツール使用指示
AIがツールを使用する際は、XMLタグを使った構造化された形式が使用されます:
export const getSharedToolUseSection = () => {
return `
## ツール使用形式
ツールを使用する際は、以下のXML形式を使用してください:
<tool_name>
<parameter_name>parameter_value</parameter_name>
</tool_name>
### 使用例
<write_to_file>
<path>src/example.ts</path>
<content>
export function example() {
return "Hello, World!"
}
</content>
</write_to_file>
### 重要な制約
- 1つのメッセージで1つのツールのみ使用可能
- パラメータは必ず指定された形式で提供
- ツール使用後は結果を待ってから次の行動を決定
`
}
プロンプトエンジニアリングのベストプラクティス
1. 段階的制約の適用
Roo Codeでは、段階的に制約を適用することで、AIの行動を細かく制御しています:
レベル1: 基本的な役割定義
レベル2: ツール使用ルール
レベル3: モード固有の指示
レベル4: 環境固有の制約
レベル5: ユーザー固有の設定
2. 具体例による指示
抽象的な指示ではなく、具体的な例を示すことで、一貫した動作を実現:
export const getCapabilitiesSection = () => {
return `
## 実行例
### ファイル作成の流れ
1. 既存ファイルの確認(list_files使用)
2. 関連ファイルの調査(read_file使用)
3. 新しいファイルの作成(write_to_file使用)
4. 動作確認(execute_command使用)
### デバッグの流れ
1. エラー内容の確認
2. 関連ファイルの調査(codebase_search使用)
3. 修正の実装
4. テスト実行で確認
`
}
3. エラーハンドリングの組み込み
よくある失敗パターンを事前に想定し、プロンプト内で対処法を指示:
export const getErrorHandlingSection = () => {
return `
## エラー対処指針
### ファイルが見つからない場合
1. list_filesでディレクトリ構造を確認
2. 類似名のファイルを検索
3. 新規作成が必要か判断
### コマンド実行エラーの場合
1. エラーメッセージを詳細に分析
2. 必要な依存関係の確認
3. 代替手段の検討
### 権限エラーの場合
1. 適切な権限での実行を試行
2. ユーザーに確認を求める
3. 代替アプローチの提案
`
}
カスタムプロンプトシステム
ファイルベースのオーバーライド
ユーザーは、カスタムプロンプトファイルを作成することで、システムプロンプトをオーバーライドできます:
// .roo/custom_instructions.md
export const addCustomInstructions = async (cwd: string) => {
const customPath = path.join(cwd, '.roo', 'custom_instructions.md')
if (await fs.pathExists(customPath)) {
const customContent = await fs.readFile(customPath, 'utf8')
return `\n## カスタムインストラクション\n${customContent}`
}
return ''
}
これにより、プロジェクト固有の制約や指示を追加できます。
ツールシステムの設計と実装
統一されたツールインターフェース
Roo Codeのツールシステムは、すべてのツールが統一されたインターフェースを持つ設計となっています:
// src/core/tools/ - 統一されたツール実行パターン
export async function toolName(
args: ToolArgs,
cwd: string,
context: Context
): Promise<ToolResponse> {
// 1. パラメータ検証とバリデーション
const { param1, param2 } = args
if (!param1) {
throw new Error("必須パラメータが不足しています")
}
// 2. 権限チェック(セキュリティ層)
await checkPermissions(param1, context)
// 3. 実行処理(ツール固有のロジック)
const result = await executeToolLogic(param1, param2)
// 4. 結果の構造化とUI更新
return {
success: true,
result: result,
metadata: { timestamp: Date.now() }
}
}
主要ツールの詳細実装
1. writeToFileTool - 高度なファイル書き込み
ファイル: src/core/tools/writeToFileTool.ts
最も複雑で洗練されたツールの一つです:
export async function writeToFileTool(args: WriteToFileArgs, cwd: string, context: Context) {
const { path: filePath, content } = args
// リアルタイム差分表示
const diffViewProvider = new DiffViewProvider(
context.extensionContext,
context.outputChannel
)
// コード欠損検出
const omissionDetection = detectOmission(content, existingContent)
if (omissionDetection.hasOmission) {
throw new Error(`コードの一部が欠損している可能性があります: ${omissionDetection.details}`)
}
// ストリーミング対応の段階的更新
await diffViewProvider.open(filePath, content, targetPath)
// モデル別前処理(GPT vs Claude etc.)
const processedContent = await preprocessForModel(content, context.model)
return {
success: true,
path: filePath,
changes: diffSummary
}
}
特徴的な機能:
- リアルタイム差分表示: DiffViewProviderとの統合
- コード欠損検出: AIが生成した不完全なコードを自動検出
- モデル固有の最適化: 異なるAIモデルの特性に対応
- 段階的更新: 大きなファイルの効率的な処理
2. readFileTool - バッチ処理対応の読み込み
export async function readFileTool(args: ReadFileArgs, cwd: string, context: Context) {
const { paths } = args
// 複数ファイルのバッチ処理
const results = await Promise.all(
paths.map(async (path) => {
// 個別承認システム
const approved = await context.ask(`ファイル ${path} を読み込みますか?`)
if (!approved) return null
// Tree-sitter解析(該当する場合)
const content = await readFileWithAnalysis(path, context)
return {
path,
content,
metadata: { size: content.length, language: detectLanguage(path) }
}
})
)
// XML形式での構造化出力
return formatAsXML(results.filter(r => r !== null))
}
3. executeCommandTool - ターミナル統合
export async function executeCommandTool(args: ExecuteCommandArgs, cwd: string, context: Context) {
const { command } = args
// VSCodeターミナル統合
const terminalManager = context.terminalManager
if (terminalManager.shellIntegration) {
// シェル統合利用時
const execution = await terminalManager.runCommand(command)
// リアルタイム出力ストリーミング
execution.onDidWriteData((data) => {
context.outputChannel.append(data)
})
return await execution.waitForCompletion()
} else {
// フォールバック: execa使用
return await executeWithExeca(command, cwd)
}
}
4. codebaseSearchTool - セマンティック検索
export async function codebaseSearchTool(args: CodebaseSearchArgs, cwd: string, context: Context) {
const { query, filePattern } = args
// ベクトル検索(OpenAI embeddings + Qdrant)
const vectorSearch = context.codeIndexManager
if (vectorSearch.isInitialized()) {
// セマンティック検索
const results = await vectorSearch.searchSimilar(query, {
limit: 10,
scoreThreshold: 0.7,
filePattern: filePattern
})
// スコアリングと関連度ランキング
return results.map(result => ({
path: result.path,
content: result.content,
score: result.score,
startLine: result.startLine,
endLine: result.endLine
}))
} else {
// フォールバック: ripgrep使用
return await fallbackTextSearch(query, filePattern, cwd)
}
}
ツール選択メカニズム
動的ツール決定
// src/core/prompts/tools/index.ts
export function getToolDescriptionsForMode(
mode: string,
experiments: ExperimentManager,
codeIndexManager: CodeIndexManager
): string {
const availableTools = getAvailableToolsForMode(mode)
// 実行環境に応じた動的決定
if (codeIndexManager.isAvailable()) {
availableTools.push('codebase_search')
}
if (experiments.isEnabled('browser_actions')) {
availableTools.push('browser_action')
}
// モード別制約の適用
if (mode === 'ask') {
// 質問モードでは実行系ツールを除外
availableTools = availableTools.filter(
tool => !['write_to_file', 'execute_command'].includes(tool)
)
}
return availableTools.map(tool => toolDescriptionMap[tool]).join('\n\n')
}
権限とセキュリティ
RooIgnoreController
// src/core/ignore/RooIgnoreController.ts
export class RooIgnoreController {
async shouldIgnore(filePath: string): Promise<boolean> {
// .rooignore ファイルのパターンマッチング
const patterns = await this.loadIgnorePatterns()
return patterns.some(pattern => {
return minimatch(filePath, pattern)
})
}
async validateOperation(operation: 'read' | 'write', filePath: string): Promise<void> {
if (await this.shouldIgnore(filePath)) {
throw new Error(`操作が禁止されています: ${filePath}`)
}
// セキュリティルールの適用
await this.checkSecurityRules(operation, filePath)
}
}
APIプロバイダーシステム
統一インターフェースによるマルチプロバイダー対応
Roo Codeは、複数のAIプロバイダーを統一されたインターフェースで扱う設計を採用しています:
// src/api/providers/base-provider.ts
export abstract class BaseProvider implements ApiHandler {
abstract createMessage(params: CreateMessageParams): ApiStream
abstract getModel(): { id: string; info: ModelInfo }
// 共通のトークン計算機能
async countTokens(content: Anthropic.Messages.ContentBlockParam[]): Promise<number> {
return await this.tokenCounter.count(content)
}
}
主要プロバイダーの実装
1. AnthropicHandler - プロンプトキャッシング
// src/api/providers/anthropic.ts
export class AnthropicHandler extends BaseProvider {
async *createMessage(params: CreateMessageParams): ApiStream {
// プロンプトキャッシング戦略
const cachedMessages = await this.applyCaching(params.messages)
const stream = await this.anthropic.messages.create({
model: this.model,
messages: cachedMessages,
system: params.systemPrompt,
stream: true,
// ephemeralキャッシュの使用
cache_control: { type: "ephemeral" }
})
// ストリーミング処理
for await (const chunk of stream) {
yield this.transformChunk(chunk)
}
}
}
2. OpenAiHandler - マルチフォーマット対応
// src/api/providers/openai.ts
export class OpenAiHandler extends BaseProvider {
async *createMessage(params: CreateMessageParams): ApiStream {
// フォーマット変換(Anthropic → OpenAI)
const openaiMessages = await transformToOpenAIFormat(params.messages)
// O3/R1モデル対応
const requestParams = this.isO3Family(this.model) ? {
model: this.model,
messages: openaiMessages,
reasoning_effort: "high",
max_completion_tokens: params.maxTokens
} : {
model: this.model,
messages: openaiMessages,
max_tokens: params.maxTokens
}
const stream = await this.openai.chat.completions.create({
...requestParams,
stream: true
})
for await (const chunk of stream) {
yield this.transformOpenAIChunk(chunk)
}
}
}
メッセージ変換システム
統一フォーマットへの変換
// src/api/transform/openai-format.ts
export function transformToOpenAI(
message: Anthropic.Messages.MessageParam
): OpenAI.Chat.ChatCompletionMessageParam {
// ツール使用の変換
if (message.content.some(block => block.type === 'tool_use')) {
return {
role: 'assistant',
tool_calls: message.content
.filter(block => block.type === 'tool_use')
.map(block => ({
id: block.id,
type: 'function',
function: {
name: block.name,
arguments: JSON.stringify(block.input)
}
}))
}
}
// 画像コンテンツの変換
const content = message.content.map(block => {
if (block.type === 'image') {
return {
type: 'image_url',
image_url: {
url: `data:${block.source.media_type};base64,${block.source.data}`
}
}
}
return block
})
return { role: message.role, content }
}
実際の動作例で見る内部処理
🎯 このセクションで学べること
Roo CodeのAIエージェントが、あなたの「Reactコンポーネントを作って」という一言から、実際にコードを生成するまでの舞台裏を詳しく見ていきます。まるで料理番組で、シェフが材料から料理を作る過程を見るように、AIがどのように考え、判断し、行動するかを追体験できます。
💡 ポイント:
- AIとの実際の「会話」の中身を見ることができます
- どのような指示でAIが動いているかがわかります
- エラーが起きた時の対処法も学べます
ユーザーリクエストの処理フロー
1. リクエスト受信 - あなたの言葉をAIが受け取る瞬間 🍽️
🔍 何が起きているか
あなたが「Reactコンポーネントを作成して」とチャットに入力すると、まずVS Codeの拡張機能がそのメッセージを受け取ります。これは、レストランで注文を受けるウェイターのような役割です。メッセージは整理され、AIが理解できる形式に変換されます。
💬 実際のメッセージフォーマット
{
"type": "webviewDidReceiveMessage",
"text": "Reactコンポーネントを作成して",
"userId": "user-123",
"timestamp": "2025-07-13T10:30:00Z",
"context": {
"workspacePath": "/Users/project",
"currentFile": "src/App.tsx"
}
}
💻 技術的な詳細
// ClineProvider.ts
webview.onDidReceiveMessage(async (message) => {
if (message.type === 'webviewDidReceiveMessage') {
const task = new Task(...)
await task.initiateTaskLoop()
}
})
2. システムプロンプト構築 - AIに「役割」を教える 👨🍳
🔍 何が起きているか
AIに仕事をしてもらう前に、まず「あなたはプログラマーのアシスタントです」という役割を教える必要があります。これをシステムプロンプトと呼びます。料理で言えば、「今日はイタリアン料理を作ってください」とシェフに伝えるようなものです。
📝 実際のシステムプロンプト例(約3,250文字)
システムプロンプトとは: AIに「あなたはどんな役割で、何ができて、どう振る舞うべきか」を教える指示書のようなものです。
あなたはRoo Codeという高度なコーディングアシスタントです。ユーザーのプログラミングタスクを効率的に支援し、高品質なコードとソリューションを提供することが使命です。
## 基本的な役割と責務
### コーディングアシスタントとしての役割
- ユーザーの開発タスクを理解し、適切なソリューションを提供する
- ファイルの読み書き、コマンドの実行、プロジェクト分析を行う
- 安全で効率的な、保守性の高いコードを作成する
### 品質基準
- 動作するコードを提供する(動作確認まで責任を持つ)
- 適切なエラーハンドリングを含める
- 読みやすく保守性の高いコードを書く
## 利用可能なツール
1. **list_files**: ディレクトリ内のファイル・フォルダ一覧を取得
2. **read_file**: ファイルの内容を読み取り
3. **write_to_file**: ファイルの作成・編集
4. **execute_command**: ターミナルコマンドの実行
5. **codebase_search**: プロジェクト全体からのコード検索
6. **new_task**: 複雑なタスクを小さなサブタスクに分割
## 現在の実行環境
- 作業ディレクトリ: /Users/project
- オペレーティングシステム: macOS Sonoma 14.0
- Node.js バージョン: v20.0.0
- 現在時刻: 2025年7月13日 10:30 JST
## 実行ルールと制約
### ファイル操作の制約
- ファイルを編集する前に必ず既存の内容を確認する
- 部分的な更新ではなく、常に完全なファイル内容を提供する
- 危険な操作前は必ずユーザーに確認を求める
💻 技術的な詳細
// system.ts - 動的プロンプト生成
const systemPrompt = await SYSTEM_PROMPT(context, {
mode: 'code',
workspace: '/Users/project',
availableTools: ['write_to_file', 'read_file', 'list_files', 'execute_command'],
codeIndexEnabled: true
})
3. AIへのAPI呼び出し - 実際の「会話」開始 📞
🔍 何が起きているか
準備が整ったら、いよいよAI(Claude、GPT-4など)に実際にリクエストを送信します。これは、準備した材料と指示書を持って、実際に料理を始めるようなものです。
📤 実際のAPIリクエスト例
{
"model": "claude-3-5-sonnet-20241022",
"max_tokens": 4096,
"system": "あなたはRoo Codeという高度なコーディングアシスタントです...",
"messages": [
{
"role": "user",
"content": "Reactコンポーネントを作成して"
}
],
"stream": true,
"temperature": 0.7
}
📥 AIからの応答例(リアルタイムストリーミング)
ストリーミングとは: AIが考えながら少しずつ返答を送ってくる仕組みです。全部考え終わってから一度に返すのではなく、考えた部分から順に表示されます。
タイムライン0:00秒 - 思考開始
{
"type": "text",
"content": "Reactコンポーネントを作成します"
}
タイムライン0:01秒 - 計画立案
{
"type": "text",
"content": "。まず、プロジェクトの構造を確認させてください。"
}
タイムライン0:02秒 - ツール使用決定
{
"type": "tool_use",
"name": "list_files",
"arguments": {
"path": "src/components"
}
}
💻 技術的な詳細
// Task.ts
const response = await this.apiHandler.createMessage({
systemPrompt: systemPrompt,
messages: this.conversationHistory,
userMessage: "React コンポーネントを作成して"
})
4. ツール実行 - AIが実際に作業する 🔧
🔍 何が起きているか
AIは指示を理解したら、実際にファイルを作成したり、コマンドを実行したりします。これは、レシピを読んだシェフが実際に包丁を使って材料を切り始めるようなものです。
🛠️ ツール実行の流れ
1️⃣ ファイル一覧を確認
XMLタグとは: AIがツールを使う時の「命令書」の書き方です。<list_files>
は「ファイル一覧を見て」という命令を表します。
<list_files>
<path>src/components</path>
</list_files>
結果:
src/components/
├── Header.tsx
├── Footer.tsx
├── Layout.tsx
└── ui/
├── Button.tsx
└── Input.tsx
2️⃣ 新しいコンポーネントファイルを作成
<write_to_file>
<path>src/components/Button.tsx</path>
<content>import React from 'react';
interface ButtonProps {
children: React.ReactNode;
variant?: 'primary' | 'secondary' | 'danger';
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
onClick?: () => void;
type?: 'button' | 'submit' | 'reset';
}
const Button: React.FC<ButtonProps> = ({
children,
variant = 'primary',
size = 'medium',
disabled = false,
onClick,
type = 'button',
}) => {
const baseClasses = 'font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2';
const variantClasses = {
primary: 'bg-blue-600 hover:bg-blue-700 text-white focus:ring-blue-500',
secondary: 'bg-gray-600 hover:bg-gray-700 text-white focus:ring-gray-500',
danger: 'bg-red-600 hover:bg-red-700 text-white focus:ring-red-500',
};
const sizeClasses = {
small: 'px-3 py-1.5 text-sm',
medium: 'px-4 py-2 text-base',
large: 'px-6 py-3 text-lg',
};
const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : '';
const className = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${disabledClasses}`.trim();
return (
<button
type={type}
className={className}
onClick={onClick}
disabled={disabled}
>
{children}
</button>
);
};
export default Button;</content>
</write_to_file>
結果: ✅ ファイルが正常に作成されました: src/components/Button.tsx
3️⃣ 動作確認のためのテスト実行
<execute_command>
<command>npm run type-check</command>
</execute_command>
結果:
✓ TypeScript型チェック完了 - エラーなし
💻 技術的な詳細
// presentAssistantMessage.ts
if (toolName === 'list_files') {
const result = await listFilesTool(toolArgs, cwd, context)
this.conversationHistory.push({ role: 'tool', content: result })
}
5. タスク分割機能 - 複雑な作業を整理する 📋
🔍 何が起きているか
「Eコマースサイトを作って」のような複雑なリクエストの場合、AIは自動的にタスクを小さな部分に分割します。これは、大きな料理を作る時に、前菜、メイン、デザートに分けて準備するようなものです。
🛠️ new_taskツールの使用例
<new_task>
<task_name>Eコマースサイトの構築</task_name>
<description>商品表示、カート機能、決済機能を含むEコマースサイトを構築する</description>
<dependencies>[]</dependencies>
<estimated_time>6時間</estimated_time>
</new_task>
AIからの提案:
🆕 大きなタスクを検出しました。以下のサブタスクに分割することをお勧めします:
1. プロジェクト構造の設計(30分)
2. 商品一覧ページの作成(90分)
3. 商品詳細ページの作成(60分)
4. ショッピングカート機能(120分)
5. 決済機能の実装(120分)
6. テストとデバッグ(60分)
この順序で進めますか?各段階で動作確認を行いながら進行します。
🔄 段階的実行
AI は各サブタスクを順次実行し、次のタスクに進む前に動作確認を行います:
📋 進捗状況:
✅ 1. プロジェクト構造の設計 - 完了
🔄 2. 商品一覧ページの作成 - 実行中
⏳ 3. 商品詳細ページの作成 - 待機中
⏳ 4. ショッピングカート機能 - 待機中
⏳ 5. 決済機能の実装 - 待機中
⏳ 6. テストとデバッグ - 待機中
6. エラーハンドリング - 問題が起きた時の対処 ⚠️
🔍 何が起きているか
AIも時々ミスをします。料理中に材料を間違えたり、手順を間違えることがあるように、AIもエラーに遭遇することがあります。しかし、優秀なシェフのように、エラーを認識して自動的に修正を試みます。
❌ エラー例: ファイル既存エラー
エラー発生:
{
"scenario": "ファイル既存エラー",
"user_input": "Buttonコンポーネントを作成してください",
"error_trigger": "同名ファイルが既に存在する"
}
AIの対応:
❌ **ファイル作成エラーが発生しました**
Button.tsx は既に存在しています。どうしますか?
🔄 **回復オプション**:
1. 🔄 既存ファイルを更新 - 現在のButton.tsxを新しい内容で置き換えます
2. 🆕 別名で作成 - CustomButton.tsx や NewButton.tsx として作成します
3. 👀 既存ファイルを確認 - まず現在のButton.tsxの内容を見てから判断します
4. ❌ 操作をキャンセル - 何もしません
どのオプションを選びますか?
自動回復の例:
✅ **自動修正を実行しました**
既存のButtonコンポーネントを確認したところ、古いバージョンでした。
より高機能なButtonコンポーネントで置き換えました。
🔄 **変更内容**:
- TypeScript型定義を追加
- 複数のバリアント対応
- Tailwind CSSスタイリング
- アクセシビリティ改善
❌ エラー例: 依存関係エラー
エラー発生:
❌ **依存関係エラーが発生しました**
エラー: `@mui/material` パッケージが見つかりません
原因: Material-UIライブラリがインストールされていません
自動解決:
🔧 **自動解決を試行中...**
💿 **必要なパッケージを自動インストール**
```bash
npm install @mui/material @emotion/react @emotion/styled
✅ インストール完了 - 作業を継続します
#### 7. ストリーミング体験 - リアルタイムで見る作業過程 ⚡
##### 🔍 何が起きているか
Roo Codeでは、AIの作業過程がリアルタイムで表示されます。これは、オープンキッチンでシェフの調理を見ているような体験です。
##### 📺 実際のストリーミング表示(22秒間)
```text
00:00 💭 思考中...
00:01 📝 ログインフォームコンポーネントを作成します
00:02 🔍 プロジェクト構造を確認中...
00:03 ⚙️ list_files を実行中...
00:04 ✅ 既存のButtonとInputコンポーネントを活用できます
00:05 📖 Buttonコンポーネントの仕様を確認中...
00:08 🎯 ログインフォームの実装を開始します
00:12 ✍️ LoginForm.tsx を作成中...
00:15 ✅ LoginForm.tsx を作成完了
00:18 🧪 TypeScript型チェック実行中...
00:20 ✅ 型チェック完了 - エラーなし
00:22 🎉 ログインフォームコンポーネントの実装が完了しました!
🔄 並行処理の可視化
複雑なタスクでは、複数の作業が同時に進行します:
🔄 **並行作業進行中**
┌─ 📦 プロジェクト構造確認 ✅ 完了
├─ 🎨 デザインシステム分析 ✅ 完了
├─ 🏗️ コンポーネント設計 ⚙️ 実行中
└─ 📝 型定義作成 ⚙️ 実行中
📊 **進捗: 75%**
成功ケースの詳細
シンプルなコンポーネント作成(45秒で完成)
ユーザー入力: 「Reactコンポーネントを作成して」
完成した成果物:
✅ **作成されたファイル**:
- src/components/Button.tsx
✨ **実装された機能**:
- 3つのバリアント (primary, secondary, danger)
- 3つのサイズ (small, medium, large)
- 無効状態の対応
- TypeScript対応
- Tailwind CSSスタイリング
💡 **使用例**:
```tsx
import Button from './components/Button';
<Button variant="primary" size="medium">
クリックして
</Button>
複雑な機能実装(3分で完成)
ユーザー入力: 「ユーザー登録フォームのReactコンポーネントを作成してください。バリデーション機能付きでお願いします。」
完成した成果物:
📁 **作成されたファイル**:
- src/components/forms/RegisterForm.tsx
- src/components/forms/__tests__/RegisterForm.test.tsx
✨ **実装された機能**:
- React Hook Form + Zod による強力なバリデーション
- リアルタイムバリデーション
- パスワード強度インジケーター
- レスポンシブデザイン
- アクセシビリティ対応
- 包括的なテストカバレッジ
🔧 **バリデーション機能**:
- メールアドレス形式チェック
- パスワード強度チェック(8文字以上、大文字・小文字・数字)
- パスワード確認の一致チェック
- 必須フィールドチェック
- 利用規約同意チェック
🧪 **テスト結果**: 5つのテストが全て通過
パフォーマンス最適化の仕組み
1. プロンプトキャッシング - 高速化の秘密 🚀
🔍 何が起きているか
同じようなプロンプトを何度も送信する際に、AIプロバイダー側でキャッシュを活用することで、応答時間を大幅に短縮しています。これは、レストランで人気メニューの材料を事前に準備しておくようなものです。
// キャッシュ適用前: 3-5秒
// キャッシュ適用後: 0.5-1秒(80%の高速化)
export function addCacheBreakpoints(systemPrompt: string, messages: array) {
// システムプロンプトにキャッシュ制御を付与
systemPrompt = {
text: systemPrompt,
cache_control: { type: "ephemeral" }
}
}
2. ストリーミング最適化 - 即座の反応 ⚡
// リアルタイム処理
for await (const chunk of apiStream) {
switch (chunk.type) {
case 'text':
await this.updateUI(chunk.content) // 即座にテキスト表示
break
case 'tool_use':
await this.executeToolStreamingly(chunk) // ツール実行開始
break
case 'reasoning':
await this.showReasoningProcess(chunk) // 思考過程表示
break
}
}
これにより、ユーザーは結果を待つことなく、AIの作業過程をリアルタイムで確認できます。
セキュリティと安全性
多層防御アプローチ
Roo Codeは、複数の層でセキュリティを確保しています:
// セキュリティ層の構成
1. プロンプトレベル: 危険な操作の禁止指示
2. ツールレベル: パラメータバリデーション
3. ファイルシステムレベル: rooIgnore/rooProtected
4. ユーザーインターフェースレベル: 承認システム
5. 実行レベル: サンドボックス化
承認システム
// Task.ts
async ask(type: 'tool_use' | 'file_access', params: any): Promise<boolean> {
return new Promise((resolve) => {
this.webviewPanel.postMessage({
type: 'approval_request',
data: { type, params }
})
// ユーザーの応答を待機
this.once('approval_response', (approved: boolean) => {
resolve(approved)
})
})
}
まとめと考察
Roo Codeから学ぶAIエージェント設計の要点
-
プロンプトエンジニアリングの重要性
- 動的生成によるコンテキスト適応
- 具体的制約による行動制御
- エラー予防指示の組み込み
-
アーキテクチャ設計の原則
- 明確な責任分離
- プラガブルな拡張性
- イベント駆動による疎結合
-
パフォーマンス最適化
- プロンプトキャッシングの活用
- ストリーミングファーストの設計
- 段階的処理による応答性向上
-
セキュリティの考慮
- 多層防御の実装
- ユーザー承認システム
- 権限ベースのアクセス制御
今後の展望
Roo Codeのアーキテクチャは、以下の発展可能性を示しています:
- マルチモーダル対応: 画像・音声入力の統合
- 協調エージェント: 複数エージェントの連携
- ドメイン特化: 分野別の専門エージェント
- 自律性向上: より高度な判断能力
AIエージェント開発における実践的な知見として、Roo Codeの設計思想と実装詳細は大いに参考になるでしょう。
参考リソース
この記事があなたのAIエージェント開発の一助となれば幸いです。
Discussion