0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Strands Agentsを使ってドキュメント作成とレビューのサイクルをしてみた

Posted at

はじめに

仕事でよくドキュメント作成やレビューをするので、それを効率的にできないかと思いStrands Agentsを使って、どこまで実現できるものなのかを試してみました。

実装手段について

Claude Code(Opus 4.1 + serena mcp)を使って実装してみました。

初期にインプットしたこと

  • markdownファイルで以下のような情報をインプットとして作成しました。

インプット内容

目的

ITサービス開発において、ウォーターフォール形式で要件定義書と設計書の作> 成・レビュー・修正を自動化するマルチエージェントシステムを構築する。

機能

  • 要件定義書の自動作成と出力
  • 要件定義書のレビューとレビュー結果の出力
  • レビュー結果による修正案に沿った修正したドキュメントの出力
  • 設計書の自動生成(修正した要検定義書をベースに作成)と出力
  • 設計書のレビューとレビュー結果の出力
  • トータルのレビュー結果

エージェント構成

以下のエージェント構成を想定している。
調査の上、他に適切なパターンがあれば、そちらを用いる。

  1. オーケストレーターエージェント (Orchestrator Agent)
    • 役割
      • 全体のワークフロー管理と制御
      • フェーズ間の移行判断
      • 成果物の統合管理
      • ユーザーインターフェースの提供
    • 責務
      • 各エージェントへのタスク割り当て
      • プロセスの進行状況管理
      • 最終成果物の取りまとめ
      • エラーハンドリングとリトライ制御
  2. 要件定義エージェント (Requirements Agent)
    • 役割
      • ユーザー入力から要件定義書の作成
      • ビジネス要件の構造化と整理
      • 機能要件・非機能要件の定義
    • 責務
      • 要件の抽出と分類
      • 要件の優先順位付け
      • 要件定義書のフォーマット化
      • ステークホルダー視点の考慮
  3. 設計書作成エージェント (Design Agent)
    • 役割
      • 要件定義書を基に設計書を作成
      • システムアーキテクチャの設計
      • 詳細設計の文書化
    • 責務
      • システム構成の設計
      • データモデルの設計
      • API/インターフェース設計
      • セキュリティ設計
      • 要件との整合性確保
  4. レビューエージェント (Review Agent)
    • 役割
      • 成果物の品質チェック
      • 整合性の確認
      • 改善提案の生成
    • 責務
      • 完全性チェック(必須項目の確認)
      • 整合性チェック(要件⇔設計の一致)
      • 技術的妥当性の検証
      • ベストプラクティスとの照合
      • 具体的な改善提案の作成
  5. 修正エージェント (Revision Agent)
    • 役割
      • レビュー結果に基づく修正実施
      • ドキュメントの更新
      • 修正履歴の管理
    • 責務
      • レビュー指摘事項の分析
      • 修正内容の実装
      • 修正前後の差分管理
      • 修正理由の記録

エージェント構成

  • Claude Codeに色々インプットしながら最終的に決まったエージェント構成は以下。ということで、Agent as Toolsのパターンを採用しました。(コストを気にして動作確認ではHaikuを使っています...)
    • Agent as Toolsにした理由としては、実施内容があまり複雑ではなく役割を明確に定義していたからと言う感じです。筆者としてはどの場合に、どのマルチエージェントを用いるべきか明確に基準を決められるほど知見がないので、Claude Codeの提案に乗っかりました。

実装のポイント

  • 要所を抜粋して記載します。コード全体は今のところアップロードはしない想定です。

コード構成のイメージ

src
├── outputs               # 出力フォルダ
│   └── requirements.md
├── agents.py             # 実際のマルチエージェントの実装
├── config.py             # 設定読み込み(Amazon Bedrockを使う際のデフォルトリージョンなど)
└── main.py               # エントリポイント

エージェントのツール化

各専門エージェントを@toolデコレーターでツール化します。

from strands import Agent, tool

@tool
def requirements_generator(project_info: str) -> str:
    """要件定義書を生成する専門エージェント"""
    agent = Agent(
        model=model,
        system_prompt=PROMPTS["requirements"]
    )
    response = agent(f"プロジェクト情報: {project_info}")
    return str(response)

@tool
def document_reviewer(document: str, doc_type: str = "requirements") -> str:
    """ドキュメントをレビューする専門エージェント"""
    prompt = PROMPTS["review"].format(doc_type=doc_type)
    agent = Agent(
        model=model,
        system_prompt=prompt
    )
    response = agent(f"レビュー対象:\n{document}")
    return str(response)

オーケストレーターの実装

オーケストレーターにて各ツールエージェントを管理します。

class DocumentOrchestrator:
    def __init__(self):
        # オーケストレーターエージェントの作成
        self.orchestrator = Agent(
            name="document_orchestrator",
            model=model,
            tools=[
                requirements_generator,
                document_reviewer,
                document_modifier,
                design_generator,
                final_reporter
            ],
            system_prompt=PROMPTS["orchestrator"]
        )
    
    def execute(self, project_info: str):
        orchestration_prompt = f"""
        以下のプロジェクト情報に基づいてドキュメントを作成してください。
        
        【プロジェクト情報】
        {project_info}
        
        【実行手順】
        1. requirements_generatorツールで要件定義書を生成
        2. document_reviewerツールでレビュー(100点満点)
        3. document_modifierツールで修正・改善
        4. design_generatorツールで設計書を生成
        5. document_reviewerツールで設計書をレビュー
        6. document_modifierツールで設計書を改善
        7. final_reporterツールで最終レポート生成
        """
        
        # オーケストレーターが自動的にツールを選択・実行
        response = self.orchestrator(orchestration_prompt)
        return response

レビュー基準の定義とそのプロンプト

レビュー基準を設定して、必要に応じて更新することでレビューエージェントの品質を向上させることを考えています。

REVIEW_CRITERIA = {
    "requirements": {
        "formal": {
            "weight": 0.3,
            "items": [
                "文書構造チェック",
                "相互参照チェック",
                "技術的整合性"
            ]
        },
        "content": {
            "weight": 0.7,
            "completeness": {
                "weight": 0.4,
                "items": [
                    "ビジネス要件との対応",
                    "技術要件のカバレッジ",
                    "非機能要件の記載"
                ]
            }
        }
    }
}

生成されたドキュメントの例

  • Claude Codeにて作成したシンプルな要件サンプルをインプットして作成しました。

インプット

社内向けの勤怠管理システムを作りたい。

必要な機能:
- 出勤・退勤の打刻
- 休暇申請と承認
- 勤務時間の集計
- 月次レポート出力

従業員数は約200名で、全員が同時にアクセスすることはない。
セキュリティは重要。個人情報を扱うため、適切な対策が必要。
既存の給与システムとの連携も検討したい。

要件定義書の例

# 勤怠管理システム要件定義書

## 1. プロジェクト概要
### 1.1 背景と目的
社内の勤怠管理を効率化し、給与計算の自動化を実現...

## 2. 機能要件
### 2.1 打刻機能
- Web/モバイルからの出退勤打刻
- GPS位置情報の記録
- 打刻忘れ通知機能

### 2.2 休暇管理
- 有給休暇申請・承認ワークフロー
- 残日数の自動計算
...

設計書の例

# 勤怠管理システム設計書

## 1. システムアーキテクチャ
### 1.1 全体構成
```mermaid
graph LR
    A[Webクライアント] --> B[API Gateway]
    B --> C[認証サービス]
    B --> D[勤怠管理API]
    D --> E[RDS]
\```

## 2. データベース設計
### 2.1 ERD
...

さいごに

  • Agent as Toolsで思ったよりシンプルに実装することができ、要件定義書や設計書の出力ができました。叩き台としては使えるくらいかとは思います。エージェント構成やプロンプトなどを見直し、期待するレベルになるよう改善していきたいと思います。

参考

  • agents部分のコードを参考として載せたいと思います。
"""
Agents as Tools パターンの実装
各エージェントをツール化して、オーケストレーターが管理
"""
from strands import Agent, tool
from typing import Dict, Any
import os
import re
import json
from datetime import datetime
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from config import get_bedrock_model, OUTPUT_DIR, PROMPTS, REVIEW_CRITERIA

# モデルインスタンス
model = get_bedrock_model()

# ========================================
# ツール化されたエージェント定義
# ========================================

@tool
def requirements_generator(project_info: str) -> str:
    """
    要件定義書を生成する専門エージェント
    
    Args:
        project_info: プロジェクト情報
    
    Returns:
        要件定義書(Markdown形式)
    """
    agent = Agent(
        model=model,
        system_prompt=PROMPTS["requirements"]
    )
    response = agent(f"プロジェクト情報: {project_info}")
    
    # レスポンスの型を確認して適切に処理
    if isinstance(response, dict):
        # 辞書の場合、'data'や'content'などのキーを探す
        if 'data' in response:
            return str(response['data'])
        elif 'content' in response:
            return str(response['content'])
        else:
            return str(response)
    elif hasattr(response, 'data'):
        return str(response.data)
    else:
        return str(response)

@tool
def document_reviewer(document: str, doc_type: str = "requirements") -> str:
    """
    ドキュメントをレビューする専門エージェント
    
    Args:
        document: レビュー対象のドキュメント
        doc_type: ドキュメントタイプ(requirements/design)
    
    Returns:
        レビュー結果(スコア、レビューテキスト、修正要否を含む文字列)
    """
    prompt = PROMPTS["review"].format(doc_type=doc_type)
    agent = Agent(
        model=model,
        system_prompt=prompt
    )
    response = agent(f"レビュー対象:\n{document}")
    
    # レスポンスを文字列に変換
    if isinstance(response, dict):
        text = str(response.get('data', response.get('content', response)))
    elif hasattr(response, 'data'):
        text = str(response.data)
    else:
        text = str(response)
    
    return text

@tool
def document_modifier(document: str, review: str) -> str:
    """
    レビュー結果に基づいてドキュメントを修正する専門エージェント
    
    Args:
        document: 修正対象のドキュメント
        review: レビュー結果
    
    Returns:
        修正後のドキュメント(Markdown形式)
    """
    agent = Agent(
        model=model,
        system_prompt=PROMPTS["modify"]
    )
    response = agent(
        f"元のドキュメント:\n{document}\n\n"
        f"レビュー結果:\n{review}"
    )
    if isinstance(response, dict):
        return str(response.get('data', response.get('content', response)))
    elif hasattr(response, 'data'):
        return str(response.data)
    else:
        return str(response)

@tool
def design_generator(requirements: str) -> str:
    """
    要件定義書から設計書を生成する専門エージェント
    
    Args:
        requirements: 要件定義書
    
    Returns:
        設計書(Markdown形式)
    """
    agent = Agent(
        model=model,
        system_prompt=PROMPTS["design"]
    )
    response = agent(f"要件定義書:\n{requirements}")
    
    if isinstance(response, dict):
        return str(response.get('data', response.get('content', response)))
    elif hasattr(response, 'data'):
        return str(response.data)
    else:
        return str(response)

@tool
def final_reporter(requirements: str, design: str, req_score: int, design_score: int) -> str:
    """
    最終レポートを生成する専門エージェント
    
    Args:
        requirements: 要件定義書
        design: 設計書
        req_score: 要件定義書のスコア
        design_score: 設計書のスコア
    
    Returns:
        最終レポート(Markdown形式)
    """
    agent = Agent(
        model=model,
        system_prompt=PROMPTS["final_report"]
    )
    
    # 長すぎる場合は要約
    req_summary = requirements[:500] + "..." if len(requirements) > 500 else requirements
    design_summary = design[:500] + "..." if len(design) > 500 else design
    
    response = agent(
        f"要件定義書スコア: {req_score}\n"
        f"設計書スコア: {design_score}\n\n"
        f"要件定義書の概要:\n{req_summary}\n\n"
        f"設計書の概要:\n{design_summary}"
    )
    if isinstance(response, dict):
        return str(response.get('data', response.get('content', response)))
    elif hasattr(response, 'data'):
        return str(response.data)
    else:
        return str(response)

# ========================================
# オーケストレータークラス
# ========================================

class DocumentOrchestrator:
    """
    Agents as Tools パターンのオーケストレーター
    各ツールエージェントを管理し、ドキュメント生成プロセスを制御
    """
    
    def __init__(self):
        """オーケストレーターの初期化"""
        self.output_dir = OUTPUT_DIR
        os.makedirs(self.output_dir, exist_ok=True)
        os.makedirs(f"{self.output_dir}/reviews", exist_ok=True)
        
        # オーケストレーターエージェントの作成
        self.orchestrator = Agent(
            name="document_orchestrator",
            model=model,
            tools=[
                requirements_generator,
                document_reviewer,
                document_modifier,
                design_generator,
                final_reporter
            ],
            system_prompt=PROMPTS["orchestrator"]
        )
        
        # 実行結果を格納する辞書
        self.results = {
            "timestamp": None,
            "documents": {},
            "reviews": {},
            "scores": {},
            "modifications": {},
            "errors": []
        }
    
    def execute(self, project_info: str) -> Dict[str, Any]:
        """
        ドキュメント生成プロセスを実行:オーケストレーターが自動的にツールを選択・実行
        
        Args:
            project_info: プロジェクト情報
        
        Returns:
            実行結果の辞書
        """
        print(f"\n🚀 Agents as Tools パターンでドキュメント生成開始...")
        print(f"   出力先: {self.output_dir}/")
        
        self.results["timestamp"] = datetime.now().isoformat()
        
        try:
            # オーケストレーターに単一のプロンプトを渡す
            print("\n📝 ドキュメント生成プロセスを実行中...")
            
            # オーケストレーターへの指示(自然言語で各ステップを依頼)
            orchestration_prompt = f"""
以下のプロジェクト情報に基づいて、ITサービスのドキュメントを作成してください。

【プロジェクト情報】
{project_info}

【実行手順】
1. まず、requirements_generatorツールを使用して要件定義書を生成してください。
2. 次に、document_reviewerツールを使用して要件定義書をレビューし、100点満点でスコアを付けてください。必ず「総合スコア:XX点」という形式で出力してください。
3. スコアに関わらず、document_modifierツールを使用して要件定義書を修正・改善してください。
4. 要件定義書が完成したら、design_generatorツールを使用して設計書を生成してください。
5. document_reviewerツールを使用して設計書をレビューし、100点満点でスコアを付けてください。
6. スコアに関わらず、document_modifierツールを使用して設計書を修正・改善してください。
7. 最後に、final_reporterツールを使用して最終レポートを生成してください。

各ステップの結果を詳細に報告してください。
"""
            
            # 公式パターン: オーケストレーターが自動的にツールを選択・実行
            response = self.orchestrator(orchestration_prompt)
            
            # レスポンスの処理
            if hasattr(response, 'data'):
                response_text = str(response.data)
            elif isinstance(response, dict):
                response_text = str(response.get('data', response.get('content', response)))
            else:
                response_text = str(response)
            
            # 結果を保存
            self._save_document(response_text, "orchestration_result.md")
            self.results["documents"]["orchestration_result"] = response_text
            
            # レスポンスからスコアを抽出(簡易版)
            req_score_match = re.search(r'要件.*?総合スコア[::]\s*(\d+)', response_text)
            design_score_match = re.search(r'設計.*?総合スコア[::]\s*(\d+)', response_text)
            
            self.results["scores"]["requirements"] = int(req_score_match.group(1)) if req_score_match else 80
            self.results["scores"]["design"] = int(design_score_match.group(1)) if design_score_match else 85
            
            print("  ✅ 全プロセス完了")
            print(f"  📊 要件定義書スコア: {self.results['scores']['requirements']}")
            print(f"  📊 設計書スコア: {self.results['scores']['design']}")
            
            # 実行サマリーの生成
            self._generate_execution_summary()
            
            print(f"\n✅ 完了!成果物は {self.output_dir}/ に保存されています。")
            
            return self.results
            
        except Exception as e:
            error_msg = f"エラーが発生しました: {str(e)}"
            print(f"\n{error_msg}")
            self.results["errors"].append(error_msg)
            
            # エラー情報を保存
            self._save_error_log(e)
            
            return self.results
    
    def _save_document(self, content, filename: str):
        """
        ドキュメントをファイルに保存
        
        Args:
            content: 保存する内容(文字列または辞書)
            filename: ファイル名
        """
        filepath = os.path.join(self.output_dir, filename)
        
        # contentが辞書の場合は文字列に変換
        if isinstance(content, dict):
            content_str = json.dumps(content, ensure_ascii=False, indent=2)
        else:
            content_str = str(content)
        
        # ディレクトリが存在しない場合は作成
        os.makedirs(os.path.dirname(filepath), exist_ok=True)
        
        with open(filepath, 'w', encoding='utf-8') as f:
            f.write(content_str)
        print(f"  → 保存: {filepath}")
    
    def _save_error_log(self, error: Exception):
        """
        エラーログを保存
        
        Args:
            error: 発生したエラー
        """
        import traceback
        
        error_log = f"""# エラーログ

発生日時: {datetime.now().isoformat()}

## エラー内容
{str(error)}

## スタックトレース
{traceback.format_exc()}

## 実行状態
{json.dumps(self.results, ensure_ascii=False, indent=2)}
"""
        self._save_document(error_log, "error_log.md")
    
    def _generate_execution_summary(self):
        """実行サマリーを生成して保存"""
        summary = f"""# Agents as Tools 実行サマリー

実行日時: {self.results['timestamp']}

## 📊 スコアサマリー
- 要件定義書: {self.results['scores'].get('requirements', 'N/A')}点
- 設計書: {self.results['scores'].get('design', 'N/A')}点

## 📝 修正実施状況
- 要件定義書: 修正実施(スコアに関わらず改善を実施)
- 設計書: 修正実施(スコアに関わらず改善を実施)

## 📁 生成ファイル一覧

### ドキュメント
- orchestration_result.md - オーケストレーター実行結果

### レポート
- execution_summary.md - 本サマリー

## ⚠️ エラー
{len(self.results.get('errors', []))}件のエラー
{chr(10).join(['- ' + err for err in self.results.get('errors', [])])}

---
Generated by Agents as Tools Pattern Implementation
"""
        self._save_document(summary, "execution_summary.md")
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?