論文概要(Abstract)
MASAI(Modular Architecture for Software-engineering AI agents)は、GitHub Issueの自動解決タスクを5つの専門サブエージェントに分解することで、SWE-bench Liteで28.33%(85/300件解決)を達成したシステムである。2024年6月時点でSWE-benchリーダーボードの最高性能を記録した。従来の単一ReActエージェント方式(約18%)と比較して、タスク分解と専門化による10%以上の絶対改善を実現している。各サブエージェントが独立した戦略・ツールセット・トークン予算を持ち、構造化された出力で連携する設計は、Zenn記事のGemini 3.1 Proマルチエージェント構成の理論的基盤となる。
この記事は Zenn記事: Gemini 3.1 Proで構築するマルチエージェント協調コーディングの実践手法 の深掘りです。
情報源
- arXiv ID: 2406.14928
- URL: https://arxiv.org/abs/2406.14928
- 著者: Daman Arora, Atharv Sonwane, Nalin Wadhwa, Abhav Mehrotra, Saiteja Utpala, Ramakrishna Bairi, Aditya Kanade, Nagarajan Natarajan(Microsoft Research India / IIT Bombay)
- 発表年: 2024年
- 分野: cs.SE, cs.AI
背景と動機(Background & Motivation)
GitHub Issueの自動解決は、コードベースの理解、バグの再現、修正パッチの生成、テストによる検証という複数のステップを必要とする複雑なタスクである。SWE-bench(Jimenez et al., 2023)はこのタスクの標準ベンチマークとして、12のPythonリポジトリから2,294件の実Issue-パッチペアを収集している。
従来のアプローチは大きく2つに分類される:
- 単一エージェント方式(SWE-agent等): 1つのReActエージェントが全サブタスクを担当。シンプルだが、異なるサブタスクに最適な戦略を使い分けられない
- 固定パイプライン方式(Agentless等): エージェントを使わず、固定のパイプラインで処理。柔軟性に欠ける
MASAIの着眼点は、SEタスクのサブタスクごとに最適な戦略が異なるという観察にある。例えば、バグの再現にはコード実行が必要だが、修正箇所の特定にはAST解析が有効であり、パッチ生成には精密なコード編集能力が求められる。これらを1つのエージェントに任せるのは非効率である。
主要な貢献(Key Contributions)
- 5サブエージェントのモジュラー設計: SEタスクを5つの明確なサブタスクに分解し、各々に専門エージェントを割り当て
- Issue Reproducer Agentの導入: バグ再現テストを自動生成する専用エージェントにより、修正の検証品質を大幅向上
- 階層的コードローカライゼーション: ファイル→クラス/関数→行の3段階で修正箇所を絞り込むEdit Localizer設計
- 体系的なアブレーション研究: 各設計選択の貢献度を定量的に分析し、モジュラー設計の有効性を実証
技術的詳細(Technical Details)
5つのサブエージェントアーキテクチャ
MASAIは以下の5つの専門サブエージェントで構成される:
1. Project Setup Agent
目的: プロジェクトの実行環境を正しく構築する
戦略: ReActアプローチ(シェルアクセス付き)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class ProjectSetupAgent:
"""プロジェクト環境を構築するエージェント
README・setup.pyを読み、conda環境の作成、
依存関係のインストールを自動実行する
"""
TOKEN_BUDGET = 20_000
async def setup(self, repo_path: str) -> bool:
"""環境構築を実行
Args:
repo_path: リポジトリのパス
Returns:
成功フラグ
"""
# 1. README・setup.pyを読み込み
setup_info = await self._read_setup_files(repo_path)
# 2. Pythonバージョンを判定
python_version = self._detect_python_version(setup_info)
# 3. conda環境を作成
await self._create_conda_env(python_version)
# 4. 依存関係をインストール
await self._install_deps(repo_path)
# 5. importテストで検証
return await self._verify_import(repo_path)
重要性: 環境構築の失敗は後続の全エージェントに波及するカスケード障害を引き起こすため、最優先で実行される。
2. Issue Reproducer Agent
目的: Issueで報告されたバグを再現するテストスクリプトを生成する
戦略: ReActアプローチ + コード生成 + 反復実行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class IssueReproducerAgent:
"""バグ再現テストを生成するエージェント
Issue記述から再現スクリプトを生成し、
実行によって再現を確認する
"""
TOKEN_BUDGET = 40_000
SUCCESS_RATE = 0.60 # 約60%のIssueで再現に成功
async def reproduce(
self,
issue_text: str,
repo_path: str,
) -> tuple[bool, str]:
"""バグ再現テストを生成・実行
Args:
issue_text: Issueの本文
repo_path: リポジトリのパス
Returns:
(再現成功フラグ, テストスクリプトのパス)
"""
for attempt in range(5):
# テストスクリプトを生成
script = await self._generate_test_script(
issue_text, repo_path
)
# 実行して再現を確認
result = await self._run_script(script, repo_path)
if result.exit_code != 0: # 失敗=バグ再現成功
return True, script.path
return False, ""
MASAIの最大の差別化要素: Issue Reproducer Agentの存在により、Fixer Agentは「再現テストが通るかどうか」という明確な成功基準でパッチの正しさを検証できる。アブレーション研究では、Issue Reproducer Agentなしの場合24.67%(-3.66%)に低下することが確認されている。
3. Edit Localizer Agent
目的: 修正が必要なファイルと行範囲を特定する
戦略: 3段階の階層的コード検索
\[\text{Search}_{hierarchical} = \text{File} \xrightarrow{\text{AST}} \text{Class/Function} \xrightarrow{\text{Line}} \text{Edit Location}\]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class EditLocalizerAgent:
"""修正箇所を特定するエージェント
3段階の階層的検索で、大規模コードベースから
修正が必要な具体的な行範囲を特定する
"""
TOKEN_BUDGET = 60_000 # 最大(コード探索が最も情報量を要する)
async def localize(
self,
issue_text: str,
reproduction_output: str,
repo_path: str,
) -> list[EditLocation]:
"""修正箇所を特定
Args:
issue_text: Issueの本文
reproduction_output: 再現テストの出力
repo_path: リポジトリのパス
Returns:
修正箇所のリスト
"""
# Level 1: ファイルレベル検索
candidate_files = await self._file_search(
issue_text, repo_path
)
# Level 2: クラス/関数レベル検索(AST)
candidate_functions = await self._ast_search(
candidate_files, repo_path
)
# Level 3: 行レベル特定
edit_locations = await self._line_search(
candidate_functions, reproduction_output
)
return edit_locations
AST(Abstract Syntax Tree)ツール:
Edit Localizer Agentは以下のAST系ツールを使用する:
| ツール | 機能 |
|---|---|
get_class_body(file, class_name) | クラスの本体コードを取得 |
get_function_body(file, func_name) | 関数の本体コードを取得 |
list_classes(file) | ファイル内のクラス一覧を取得 |
list_functions(file) | ファイル内の関数一覧を取得 |
これらのツールにより、LLMが大量のコードを読む必要がなく、セマンティックな単位でコードを把握できる。
4. Fixer Agent
目的: バグを修正するパッチを生成する
戦略: ローカライズ情報と再現テストを活用した反復修正
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class FixerAgent:
"""パッチを生成するエージェント
Edit Localizerが特定した箇所を修正し、
Issue Reproducerのテストで検証する
"""
TOKEN_BUDGET = 40_000
async def fix(
self,
issue_text: str,
edit_locations: list[EditLocation],
reproduction_script: str | None,
repo_path: str,
) -> str:
"""修正パッチを生成
Args:
issue_text: Issueの本文
edit_locations: 修正箇所リスト
reproduction_script: 再現テスト(あれば)
repo_path: リポジトリのパス
Returns:
unified diff形式のパッチ
"""
for attempt in range(3):
# パッチを生成
patch = await self._generate_patch(
issue_text, edit_locations, repo_path
)
# パッチを適用
await self._apply_patch(patch, repo_path)
# 再現テストで検証
if reproduction_script:
result = await self._run_test(
reproduction_script, repo_path
)
if result.exit_code == 0: # 成功=バグ修正完了
return patch
else:
# 再現テストがない場合はパッチを返す
return patch
return "" # 修正失敗
5. Test Isolator Agent
目的: 修正に関連する既存テストを特定し、バリデーションに使用する
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class TestIsolatorAgent:
"""関連テストを特定するエージェント
パッチの変更内容を解析し、関連するテスト関数を
特定して実行する
"""
TOKEN_BUDGET = 20_000
async def isolate_tests(
self,
patch: str,
repo_path: str,
) -> list[str]:
"""関連テストを特定
Args:
patch: unified diff形式のパッチ
repo_path: リポジトリのパス
Returns:
テスト関数のリスト
"""
# パッチから変更モジュールを抽出
changed_modules = self._extract_modules(patch)
# テストファイルで変更モジュールをimportしているものを検索
test_files = await self._find_test_files(
changed_modules, repo_path
)
# 関連テスト関数を特定
test_functions = await self._find_test_functions(
test_files, repo_path
)
return test_functions
実行パイプライン
5つのサブエージェントは以下の順序で実行される:
1
2
3
4
5
6
7
8
9
10
11
Project Setup Agent ─────────────────────────────────> 完了
│
├── Issue Reproducer Agent ──> 再現テスト
│ │
├── Edit Localizer Agent ──> 修正箇所リスト
│ │
│ ┌────────────────────┘
│ │
└── Fixer Agent ──> パッチ
│
└── Test Isolator Agent ──> テスト結果
Issue Reproducer AgentとEdit Localizer Agentは並列実行が可能である。両者の出力がFixer Agentの入力となる。
トークン予算管理
各サブエージェントに独立したトークン予算を設定:
| サブエージェント | トークン予算 | 理由 |
|---|---|---|
| Project Setup | 20,000 | 環境構築は定型的 |
| Issue Reproducer | 40,000 | コード生成+反復実行 |
| Edit Localizer | 60,000 | コードベース探索が最も情報量を要する |
| Fixer | 40,000 | パッチ生成+検証 |
| Test Isolator | 20,000 | テスト検索は限定的 |
合計: 約180,000トークン/Issue(実測では約200,000トークン)
\[\text{Cost}_{per\_issue} = \frac{200{,}000 \times \text{Price}_{GPT4o}}{1{,}000{,}000} \approx \$2\text{-}\$3\]実装のポイント(Implementation)
構造化出力によるエージェント間通信
各サブエージェントの出力はJSONまたはテンプレート形式で構造化されている:
1
2
3
4
5
6
7
8
9
# Edit Localizer → Fixer への出力例
@dataclass
class EditLocation:
"""修正箇所の構造化表現"""
file_path: str # 修正対象ファイル
start_line: int # 開始行(1-indexed)
end_line: int # 終了行(1-indexed、inclusive)
description: str # 修正内容の説明
code_context: str # 周辺コード(参考用)
この構造化により、下流エージェントが上流の出力を確実にパースでき、情報の欠落を防いでいる。
ファイル読み込みツールの行番号表示
Edit Localizer Agentのファイル読み込みツールは必ず行番号を表示する:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def read_file_with_line_numbers(
file_path: str,
start: int = 1,
end: int | None = None,
) -> str:
"""行番号付きでファイルを読み込む
Args:
file_path: ファイルパス
start: 開始行
end: 終了行(Noneで末尾まで)
Returns:
行番号付きのファイル内容
"""
with open(file_path) as f:
lines = f.readlines()
if end is None:
end = len(lines)
result = []
for i, line in enumerate(lines[start-1:end], start=start):
result.append(f"{i:4d} | {line.rstrip()}")
return "\n".join(result)
行番号表示により、LLMが「142行目から155行目を修正する」という精密な指示を生成でき、ローカライゼーションの精度が向上する。
Gemini 3.1 Proとの対応
MASAIの設計はZenn記事の構成と以下のように対応する:
| MASAIのサブエージェント | Zenn記事の対応 | thinking_level |
|---|---|---|
| Project Setup Agent | — (環境構築はADK外) | — |
| Issue Reproducer Agent | Planner層の一部 | high |
| Edit Localizer Agent | Planner層の一部 | high |
| Fixer Agent | Coder層 | medium |
| Test Isolator Agent | Reviewer層の一部 | medium |
MASAIの5エージェント構成をADKで実装する場合:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from google.adk.agents import SequentialAgent, ParallelAgent, LlmAgent
# MASAI on ADK
masai_pipeline = SequentialAgent(
name="masai",
sub_agents=[
LlmAgent(name="setup", model="gemini-3.1-pro-preview",
instruction="環境を構築..."),
ParallelAgent(
name="analysis",
sub_agents=[
LlmAgent(name="reproducer",
model="gemini-3.1-pro-preview",
instruction="バグを再現..."),
LlmAgent(name="localizer",
model="gemini-3.1-pro-preview",
instruction="修正箇所を特定..."),
],
),
LlmAgent(name="fixer", model="gemini-3.1-pro-preview",
instruction="パッチを生成..."),
LlmAgent(name="test_isolator",
model="gemini-3.1-pro-preview",
instruction="関連テストを実行..."),
],
)
Production Deployment Guide
AWS実装パターン(コスト最適化重視)
| 規模 | 月間リクエスト | 推奨構成 | 月額コスト | 主要サービス |
|---|---|---|---|---|
| Small | ~3,000 (100/日) | Serverless | $100-300 | Step Functions + Lambda + Bedrock |
| Medium | ~30,000 (1,000/日) | Hybrid | $600-1,500 | ECS Fargate + Step Functions + Bedrock |
| Large | 300,000+ (10,000/日) | Container | $4,000-10,000 | EKS + Step Functions + Bedrock |
Small構成の詳細 (月額$100-300):
- Step Functions: 5エージェントのオーケストレーション(並列分岐含む) ($20/月)
- Lambda × 5: 各サブエージェント用 ($30/月)
- Bedrock: Claude 3.5 Sonnet (Localizer/Fixer) + Haiku (Setup/Isolator) ($200/月)
- S3: パッチ・テスト結果保存 ($5/月)
- CloudWatch: 監視 ($5/月)
コスト削減テクニック:
- サブエージェント別モデル選択: Setup/Isolator=Haiku($0.25/MTok), Reproducer/Fixer=Sonnet($3/MTok), Localizer=Sonnet
- トークン予算の厳守: 各サブエージェントのmax_tokensを設定し過剰消費防止
- 並列実行: Issue ReproducerとEdit Localizerの並列実行で壁時計時間50%削減
- Bedrock Batch API: 非リアルタイムのバグ修正で50%割引
コスト試算の注意事項:
- 上記は2026年2月時点のAWS ap-northeast-1(東京)リージョン料金に基づく概算値です
- Issue1件あたりの実コストは約$2-3(GPT-4o相当モデル使用時)
- 最新料金は AWS料金計算ツール で確認してください
Terraformインフラコード
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# --- Step Functions: MASAIパイプライン ---
resource "aws_sfn_state_machine" "masai" {
name = "masai-pipeline"
role_arn = aws_iam_role.sfn_role.arn
definition = jsonencode({
StartAt = "ProjectSetup"
States = {
ProjectSetup = {
Type = "Task"
Resource = aws_lambda_function.setup_agent.arn
Next = "ParallelAnalysis"
}
ParallelAnalysis = {
Type = "Parallel"
Branches = [
{
StartAt = "IssueReproducer"
States = {
IssueReproducer = {
Type = "Task"
Resource = aws_lambda_function.reproducer_agent.arn
End = true
}
}
},
{
StartAt = "EditLocalizer"
States = {
EditLocalizer = {
Type = "Task"
Resource = aws_lambda_function.localizer_agent.arn
End = true
}
}
}
]
Next = "Fixer"
}
Fixer = {
Type = "Task"
Resource = aws_lambda_function.fixer_agent.arn
Next = "TestIsolator"
}
TestIsolator = {
Type = "Task"
Resource = aws_lambda_function.test_isolator_agent.arn
End = true
}
}
})
}
# --- Lambda: 各サブエージェント ---
locals {
agents = {
setup = { memory = 512, timeout = 120, model = "haiku" }
reproducer = { memory = 1024, timeout = 300, model = "sonnet" }
localizer = { memory = 1024, timeout = 300, model = "sonnet" }
fixer = { memory = 1024, timeout = 300, model = "sonnet" }
test_isolator = { memory = 512, timeout = 120, model = "haiku" }
}
}
resource "aws_lambda_function" "agents" {
for_each = local.agents
filename = "${each.key}_agent.zip"
function_name = "masai-${each.key}"
role = aws_iam_role.lambda_role.arn
handler = "main.handler"
runtime = "python3.12"
timeout = each.value.timeout
memory_size = each.value.memory
environment {
variables = {
BEDROCK_MODEL = each.value.model == "sonnet" ? "anthropic.claude-3-5-sonnet-20241022-v2:0" : "anthropic.claude-3-5-haiku-20241022-v1:0"
TOKEN_BUDGET = each.key == "localizer" ? "60000" : each.key == "setup" || each.key == "test_isolator" ? "20000" : "40000"
}
}
}
運用・監視設定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import boto3
cloudwatch = boto3.client('cloudwatch')
# サブエージェント別の成功率監視
for agent_name in ['setup', 'reproducer', 'localizer', 'fixer', 'test_isolator']:
cloudwatch.put_metric_alarm(
AlarmName=f'masai-{agent_name}-failure-rate',
ComparisonOperator='GreaterThanThreshold',
EvaluationPeriods=1,
MetricName=f'{agent_name}_failure_rate',
Namespace='Custom/MASAI',
Period=3600,
Statistic='Average',
Threshold=0.5,
AlarmDescription=f'MASAI {agent_name}の失敗率が50%を超過'
)
コスト最適化チェックリスト
- サブエージェント別モデル選択: Setup/Isolator=Haiku, Others=Sonnet
- トークン予算: 各エージェントにmax_tokens設定
- 並列実行: Reproducer + Localizer の並列化
- Bedrock Batch API: 非リアルタイム処理で50%割引
- Step Functions Express: 短時間ワークフロー最適化
- Lambda メモリ最適化: Setup/Isolator=512MB, Others=1024MB
- S3 ライフサイクル: パッチ・テスト結果の自動削除(7日)
- CloudWatch Logs: 保持期間14日
- AWS Budgets: 月額予算アラート
- Issue Reproducer成功率の監視: 60%未満で調査
実験結果(Results)
SWE-bench Lite(300タスク)メイン結果
| システム | 解決率 | 差分 vs MASAI |
|---|---|---|
| MASAI | 28.33% (85/300) | ベースライン |
| Agentless | ~24% | -4.33% |
| AutoCodeRover | ~19% | -9.33% |
| SWE-agent | 12.47% | -15.86% |
| 単一ReActエージェント | ~18% | -10.33% |
アブレーション研究
| 設定 | 解決率 | 差分 |
|---|---|---|
| Full MASAI | 28.33% | ベースライン |
| Issue Reproducer Agentなし | 24.67% | -3.66% |
| Test Isolator Agentなし | 27.33% | -1.00% |
| 単一ReActエージェント | ~18% | -10.33% |
Issue Reproducer Agentの効果が最も大きい。再現テストの存在により、Fixer Agentの修正精度が大幅に向上する。
リポジトリ別成績
| リポジトリ | 解決率 | 特徴 |
|---|---|---|
| sympy | 41.5% (27/65) | 数学ライブラリ、テストが明確 |
| scikit-learn | 35.3% (12/34) | ML系、API変更が多い |
| astropy | 27.3% (6/22) | 天文学系、複雑な依存関係 |
| django | 12.3% (14/114) | 大規模フレームワーク、最も困難 |
Djangoの解決率が最も低い(12.3%)ことは、大規模・複雑なフレームワークにおけるマルチエージェントの限界を示唆している。
実運用への応用(Practical Applications)
MASAIの設計原則は、Zenn記事のGemini 3.1 Pro構成に以下のように応用できる:
サブエージェント分業の粒度: MASAIの5エージェント構成は、ADKのSequentialAgent + ParallelAgentで直接実装可能。thinking_level制御により、探索的タスク(Localizer)にはhigh、定型的タスク(Setup, Isolator)にはlowを割り当て
Issue Reproducer パターンの汎用化: バグ修正に限らず、「成功基準を自動生成するエージェント」として汎用化できる。コード生成タスクでも、まずテストを生成してからコードを生成するTDD的アプローチが有効
トークン予算管理: MASAIの予算配分(Localizer=60K, 他=20-40K)は、Gemini 3.1 Proのthinking_levelとの組み合わせで実装可能。thinking_level=highは多くのthinkingトークンを消費するため、Localizerのみhigh、他はmedium/lowとすることでコスト最適化
関連研究(Related Work)
- SWE-agent (Yang et al., 2024): 単一エージェント+ACI設計。MASAIはACIの概念を各サブエージェントのツールセットとして取り入れつつ、モジュラー設計で性能を上回った
- Agentless (Xia et al., 2024): エージェントなしの固定パイプライン。MASAIは各ステップをエージェント化することで、動的な探索と反復修正を実現
- MetaGPT (Hong et al., 2023): SOP駆動のマルチエージェント。MASAIはSE特化のモジュラー設計で、MetaGPTの汎用設計とは異なるアプローチ
- OpenHands (Wang et al., 2024): CodeAct設計のオープンプラットフォーム。MASAIの設計原則をOpenHandsに統合する研究が進行中
まとめと今後の展望
MASAIの最大の貢献は、SEタスクの分解粒度と各サブエージェントの専門化が性能に大きな影響を与えることを実証した点にある。単一エージェント(~18%)からモジュラー5エージェント(28.33%)への改善は、タスク分解の重要性を示している。
今後の方向性:
- マルチLLMアーキテクチャ: 各サブエージェントに最適なLLMを選択(例:LocalizerにはGemini 3.1 Pro、FixerにはClaude 3.5 Sonnet)
- バグ再現テスト生成の改善: 現在60%の成功率を80%以上に向上
- 多言語対応: 現在Pythonのみ。TypeScript、Java、Go等への拡張
- 複数ファイル変更の改善: 大規模リファクタリングへの対応
参考文献
- arXiv: https://arxiv.org/abs/2406.14928
- SWE-bench: https://github.com/princeton-nlp/SWE-bench
- Related Zenn article: https://zenn.dev/0h_n0/articles/a7935e0412571c