📑

AI エージェントの課題解決の成果を、再利用可能な形で残す

に公開

エージェントが実行した手順を再利用したい

例えば、「AWS What's New の RSSフィードから指定されたキーワードにマッチする記事を抽出して」とエージェントに頼んだとしましょう。

エージェントはリクエストを受け取って、記事を返してくれます。

このとき、エージェントが実行した手続きを再利用したくなりませんか。エージェントが最終結果を取得した手順をスクリプト化して再利用したくなりませんか。

私は、そう思ったので、エージェントが実行した手順を記録して再現する仕組みと、エージェントが最終結果を取得した手段を成果物として入手する仕組みを試してみました。

試したアイデア


AIの作業工程を記録し、再利用可能なPythonスクリプトとして抽出する仕組み

  • 成果物はPythonスクリプトで - 再利用可能な形式として、エージェントにPythonスクリプトで実装してもらう。
  • 実装の根拠も記録 - 成果物が最終的な実装内容になった根拠も一緒に記録してもらう
  • ツールはPython REPLのみ - 確実に最終結果の取得がPythonスクリプトで行われるよう、エージェントにはPython REPLしかツールを渡さない。Pythonスクリプトを受け取って実行して結果を返すツールだけで、改題を解決してもらう。
  • 作業を「探索」と「最終関数取得」に分離 - 成果物に不要な実装が含まれないよう、エージェントの作業を明確に分ける。
  • 実行記録はトレースログから取得 - エージェントが探索と最終関数取得に集中できるよう、実行記録はエージェント自身には行わせず、SDK等が提供するトレースログから取得する。
  • プロンプトの役割を明確化 - システムプロンプトには要件(機能・非機能)のみ、ユーザープロンプトには入手する成果物で解決したい課題のみを記述する。

試したシナリオ

システムプロンプトには「要件(機能・非機能)」を、ユーザープロンプトには「具体的に解決したい課題」を記述する形にしました。

用意したデータ

  • AWS What's New の RSSフィード(XML形式)

システムプロンプト(主要な要素のみ抜粋)

  • 利用可能なツール:python_repl のみ
  • データソースの場所とフォーマット(RSS 2.0、各フィールドの説明)
  • 期待する出力形式(JSON)
  • 抽出条件の仕様(期間、キーワード、重複排除、ソート順など)
  • 最終的に再利用可能な単一の関数として取得すること
  • 関数には設計判断の根拠をコメントとして含めること

※ 実際のシステムプロンプトはmain.pyを参照

ユーザープロンプト(課題)

2025年下半期のAWSニュース(XML形式)から、生成AI関連かつ東京リージョン対応の記事を自動抽出する関数を開発したい

採用した技術スタック

  • AIエージェントの実装とトレース記録:Strands Agents
  • エージェントの探索過程と成果物の再現先:Jupyter Notebook
  • トレース記録からNotebookへの情報抽出:Pythonスクリプトによる独自実装

https://strandsagents.com/latest/documentation/docs/

得られたアウトプット

  • トレースファイル(trace.jsonl:エージェントの全思考・実行・失敗を含む完全な記録
  • Jupyter Notebook(agent_replay.ipynb
    • エージェントの「探索」の過程を、時系列で、Pythonスクリプトで表現した実行履歴
    • エージェントが取得した「最終関数」の完全な実装と、実装の設計判断と根拠

これがまさに欲しかったものでした。詳細な内容は agent_replay.ipynb を参照してください。人間の手は一切入っていません。エージェントの実行結果のJupyter Notebookへの記録はPythonスクリプトによって機械的に行われています。

得られた実行履歴

エージェントの探索の進め方

エージェントは以下のような段階を踏んで、最終的な実装にたどり着いていました:

  1. データソースの確認(ファイル数、サイズ)
  2. XML構造の解析(要素名、階層構造)
  3. 日付フォーマットの調査(RFC 2822形式の確認)
  4. フィルタリングロジックの試作と検証
  5. 最終関数への整形と設計判断の記録

探索の具体例(抜粋)

例えば、XML構造の解析フェーズでは、以下のようなPythonコードが実行されていました:

# XMLファイルの構造を確認
import xml.etree.ElementTree as ET

xml_file = xml_files[0]
tree = ET.parse(xml_file)
root = tree.getroot()

# ルート要素を確認
print(f"Root tag: {root.tag}")
print(f"Root attribs: {root.attrib}")

# 最初のitemを確認して構造を理解
for item in root.findall('.//item')[:1]:
    print("\nFirst item structure:")
    for child in item:
        text_preview = child.text[:100] if child.text else "(empty)"
        print(f"  <{child.tag}>: {text_preview}...")

Pythonで記述されることのメリット

探索過程がPythonコードとして記録されることで:

  • 曖昧さがない:自然言語の説明ではなく、実際に実行されたコードが残る
  • 再実行可能:探索の途中経過も含めて、そのまま再実行できる
  • 検証しやすい:「本当にこの手順で確認したのか」を疑う余地がない
  • 理解の助けになる:エージェントがどう考えて何を確認したのか、コードから読み取れる

詳細な探索過程は agent_replay.ipynb を参照してください。

入手できた最終関数

実装の設計判断と根拠

Notebookには、最終関数の直前に設計判断とその根拠が記録されています。例えば:

### 実装の設計判断と根拠

- ライブラリ選択: xml.etree.ElementTree(標準ライブラリ、RSS/XMLパース標準)
  email.utils.parsedate_to_datetime(RFC 2822日付パース標準)
  json(出力フォーマット標準)
- データ構造: dict/list(JSONシリアライズ対応、メモリ効率)
  set でguid重複排除後、list に戻す(順序保証)
- アルゴリズム処理順序:
  1. XMLファイル全体をパース(複数ファイル対応)
  2. 日付フィルタ(効率的な絞り込み)
  3. キーワード照合(トピック AND リージョン条件)
  4. guid による重複排除
  5. pubDate 降順ソート
- エラー処理: 不正な日付形式・空要素を安全にスキップ
  XMLパースエラーで該当ファイルスキップ、処理継続

関数の構造

生成された関数は以下のような構造になっています(抜粋):

def extract_aws_articles(
    data_dir: Path,
    from_date: datetime,
    to_date: datetime,
    topic_keywords: List[str],
    region_keywords: List[str]
) -> Dict[str, Any]:
    """XMLファイルからAWSニュース記事をフィルタリングして抽出"""
    
    # ヘルパー関数: キーワード照合
    def contains_keyword(text: str, keywords: List[str]) -> bool:
        ...
    
    # ヘルパー関数: 日付変換
    def parse_pub_date(date_str: str) -> tuple:
        ...
    
    # XMLファイルをパース
    for xml_file in xml_files:
        for item in root.findall('.//item'):
            # 日付フィルタ、キーワード照合、重複排除
            ...
    
    # ソートして結果を返却
    return {"articles": [...], "summary": {...}}

詳細な実装は agent_replay.ipynb を参照してください。

関数の実行方法

Notebookには、エージェントが最終結果を取得するために使ったパラメータを初期値とした実行例も含まれています:

# パラメータ設定
data_dir_path = Path('data/01_raw')
from_date = datetime(2025, 7, 1, 0, 0, 0, tzinfo=timezone.utc)
to_date = datetime(2025, 12, 31, 23, 59, 59, tzinfo=timezone.utc)

topic_keywords = [
    'Bedrock', 'SageMaker', 'Claude', 'LLM', 'foundation model', 'generative AI',
    'embedding', 'fine-tuning', 'inference', 'RAG', 'prompt', 'model training',
    'neural network', 'transformer', 'deep learning', 'machine learning'
]

region_keywords = [
    'Tokyo', 'ap-northeast-1', 'Asia Pacific (Tokyo)', 'available in Tokyo',
    'Tokyo region', 'all regions', 'all aws regions'
]

# 関数実行
result = extract_aws_articles(
    data_dir=data_dir_path,
    from_date=from_date,
    to_date=to_date,
    topic_keywords=topic_keywords,
    region_keywords=region_keywords
)

print(json.dumps(result, indent=2, ensure_ascii=False))

Jupyter Notebookなので、このコードをそのまま実行することも可能です。

この方法で最終関数を入手することのメリット

  • 実績のあるコード:既に課題を解決した実績のあるコードが入手できる
  • LLM不要で再利用可能:そのまま再利用できれば、Pythonの実行環境のみで課題を解決できる
  • ロジックの透明性:エージェントが提示した最終結果がどのようなロジックで得られたのか明確に説明できる

まとめ・振り返り

エージェントが実際に利用した課題の解決策がPythonコードとして再利用可能な状態で手に入るという点に、活用価値を感じています。実装だけでなく、設計根拠までセットで得られたのは想定以上の成果でした。

Python REPLのみをツールとして渡した結果、想定以上に明確な探索履歴が得られました。エージェントの課題解決プロセスを説明・分析できるようになったのは予想外の収穫です。探索過程が明確に残ることで、もしかしたら課題解決の教材としても使えるかもしれません。

再現手順や詳細は GitHub に置いています。実際の Notebook を見てみたい方は、ぜひリポジトリを覗いてみてください。


GitHub リポジトリ
https://github.com/mahitotsu/brontes

Discussion