うさラボ

お勉強と備忘録

Strands Agentsでバグ調査エージェントを作ってみた

概要

CSVに溜めたバグ一覧を、ワーカー複数で並列に読ませてザッと調査するエージェントを作成してみました。 最後にアグリゲーターが結果をMarkdownでまとめてレポートを出力します。
やったことはシンプルで、「分割→並列→集約→レポート」です。

今回はStrands Agentsフレームワークとして選びました。 Strands Agentsはモデル駆動でAIエージェントを作り動かすためのPythonSDKです。
数行でエージェントを構築できる手軽さがポイントで、コミュニティが開発しているツールセットをインポートすることでA2Aなども試すことが可能です。

公式ドキュメント strandsagents.com

ツール github.com

シンプルなエージェントであれば3行で構築できちゃいます。

from strands import Agent
agent = Agent(description="シンプルエージェント", system_prompt="日本語で回答してください")
agent("Who are you?")

環境

基本情報

WSL上に構築したAlmaLinux9.2で実行しました。

  • ランタイム: Python3.13
  • パッケージ管理: uv
  • LLMプラットフォーム: Bedrock
  • LLMモデル: claude 4.5 sonnet

セットアップ

作業用ディレクトリの作成

uv init bug_search_agent
cd bug_search_agent
uv sync

必要パッケージのインストール
メインのStrands AgentsとCSVの読み込みをpandasで行うためインストールします

uv add strands-agents
uv add pandas

Strands Agentsのバージョン

strands-agents==1.13.0

内容

モデル定義

Strands AgentsはデフォルトではBedrockのclaude sonnet 4が選択されます。
モデルを変更したり、別サービス(OpenAI)などを使う場合はmodelを明示的に定義する必要があります。
今回はclaude sonnet 4.5を利用したいのでリージョンと合わせて変更をしています。

####################################################################################################
# NOTE: モデル定義
#       DEFAULT_BEDROCK_MODEL_ID = "us.anthropic.claude-sonnet-4-20250514-v1:0"
#       DEFAULT_BEDROCK_REGION = "us-west-2"
####################################################################################################
model = BedrockModel(
    model_id="us.anthropic.claude-sonnet-4-5-20250929-v1:0",
    region_name="us-east-1",
    streaming=False,
)

エージェント設計

エージェントは2種類作成しています。

  1. 調査用エージェント
  2. 結果集約エージェント

今回作成したエージェントはツールは使わないシンプルなものにしました。

1. 調査用エージェント

調査用エージェントは、プロンプトに埋め込まれたバグのレコード情報を1要素ずつ調査し結果を返します。
Strandsでワークフローを作成するGraph機能を利用して、ワーカーを複数生成して処理をするようになっています。

(抜粋)
    ################################################################################
    # NOTE: 調査用エージェント
    #       グラフ作成時にエージェントを定義し、バッチ処理をする
    #################################################################################
    worker = Agent(
        name=f"worker_{i}",
        model=model,
        system_prompt=(
            "あなたは調査担当。与えられたrecordsを分析します\n"
            f"{batch}"
            "notesには根拠と判断理由を記す。\n"
        ),
        callback_handler=None
        )

callback_handlerは出力の挙動を制御するオプションです。デフォルトではPrintingCallbackHandlerが選択され、print表示をしてくれます。
今回は複数のエージェントを並行して動かすため表示が重なりぐちゃぐちゃになってしまったのでNoneにしています。

2. 結果集約エージェント

結果集約エージェントは、調査用エージェントが出力した結果を取りまとめて最終回答を生成します。

aggregator = Agent(
    name="agg",
    model=model,
    system_prompt=(
        "全workerの出力をマージし、最終レポートをMarkdownで返す"
    ),
)

ワークフロー設計

オーケストレーターエージェントが計画を立て、調査用エージェントを生成して並列処理!をしたかったんですが、やり方が思い浮かばなかったため CSVを読み込みKeyValueのリストに変換し、レコードを分割して調査用エージェントを渡すような形にしています。 今回のスクリプトでは読み込むCSVも固定にしていますが、この辺は可変にし引数として渡して上げれるような形がいいですね。
読み込んだCSVを1エージェント当たり何レコード対応するかはchunk_sizeで制御するようにしています。こちらも可変にしてあげたいところです。

(抜粋)
chunk_size = 2
batches = [records[i : i + chunk_size] for i in range(0, len(records), chunk_size)]

例えば10レコード場合はchunk_sizeが2では5分割され、5つのワーカーエージェントが生成されます。

workerエージェントはシステムプロンプトに埋め込まれたバグの情報を分析して返し、aggエージェントは最終回答を出しています。
最終回答はreport.mdに上書きするようにしました。

graphの作成はLangGraphととても似ています。LangGraphに存在するワークフローを可視化する機能はStrands Agentsにはまだないみたいです。

スクリプト全文

スクリプト全文

import pandas as pd
from strands import Agent
from strands.models import BedrockModel
from strands.multiagent.graph import GraphBuilder

#####################################################################################
# NOTE: CSVを読み込み
#####################################################################################
df = pd.read_csv("data/bug_list.csv")
df = df.fillna("")  # NaNを空文字に
records = df.to_dict(orient="records")

# NOTE: レコードを分割
chunk_size = 2
batches = [records[i : i + chunk_size] for i in range(0, len(records), chunk_size)]

####################################################################################################
# NOTE: モデル定義
#       DEFAULT_BEDROCK_MODEL_ID = "us.anthropic.claude-sonnet-4-20250514-v1:0"
#       DEFAULT_BEDROCK_REGION = "us-west-2"
####################################################################################################
model = BedrockModel(
    model_id="us.anthropic.claude-sonnet-4-5-20250929-v1:0",
    region_name="us-east-1",
    streaming=False,
)

#####################################################################################
# NOTE: Agentを定義
#       結果を集約するエージェント
#####################################################################################
aggregator = Agent(
    name="agg",
    model=model,
    system_prompt=(
        "全workerの出力をマージし、最終レポートをMarkdownで返す"
    ),
)

#####################################################################################
# NOTE: グラフを作成
#####################################################################################
gb = GraphBuilder()

for i, batch in enumerate(batches):
    nid = f"w{i}"
    ################################################################################
    # NOTE: 調査用エージェント
    #       グラフ作成時にエージェントを定義し、バッチ処理をする
    #################################################################################
    worker = Agent(
        name=f"worker_{i}",
        model=model,
        system_prompt=(
            "あなたは調査担当。与えられたrecordsを分析します\n"
            f"{batch}"
            "notesには根拠と判断理由を記す。\n"
        ),
        callback_handler=None
        )
    gb.add_node(worker, nid)
gb.add_node(aggregator, "agg")
for i in range(len(batches)):
    gb.add_edge(f"w{i}", "agg")

graph = gb.build()

#####################################################################################
# NOTE: Graphを起動
#####################################################################################
result = graph("CSV調査を開始")


#####################################################################################
# NOTE: 結果を記載
#####################################################################################
final_md = str(result.results["agg"].result)
with open("report.md", "w", encoding="utf-8") as f:
    f.write(final_md)

実行

スクリプトが作成できたので、さっそく確認してみます。
バグの一覧をCSV化したものをそれっぽくChatGPTに生成してもらいました。(実際に存在するバグではありません!!!)

用意したデータ

今回はレコード少なめに20件としました。

bug_id,title,product,component,severity,cve,affected_versions,fixed_in,status,first_seen,last_updated,workaround,exploit_status
CSCvx12345,OSPF adjacency flap on high CPU,IOS XE,routing,High,CVE-2025-10001,"17.6.x,17.9.1",17.9.3,Open,2025-05-14,2025-10-10,"Disable BFD on affected neighbors",None observed
CSCvy23456,ACL hitcount not incrementing,NX-OS,acl,Medium,,"9.3(8)",9.3(10),Fixed,2025-03-02,2025-07-21,"Reallocate TCAM region and reload",N/A
CSCvw34567,AnyConnect DTLS reconnect loop,ASA/FTD,remote-access vpn,High,CVE-2025-10002,"9.16(3),9.18(1)",9.18(2),Open,2025-06-30,2025-09-05,"Force TLS-only temporarily",None observed
CSCwa45678,MAC move triggers storm-control,Catalyst 9000,l2 switching,Medium,,"17.12.2",17.12.4,In Progress,2025-08-12,2025-10-12,"Raise storm-control threshold",N/A
CSCwb56789,ISIS LSP stuck in overload,IOS XR,isis,High,CVE-2025-10003,"7.7.1",7.7.3,Open,2025-04-25,2025-09-18,"Clear process isis",None observed
CSCwc67890,SNMP engineID changes after reload,IOS XE,snmp,Low,,"17.3.x",17.3.8,Fixed,2025-01-19,2025-05-07,"Pin engineID via config",N/A
CSCwd78901,IPFIX template not exported,Catalyst 9300,netflow,Medium,,"17.6.5",17.6.6,Fixed,2025-02-10,2025-06-03,"Toggle export-protocol version",N/A
CSCwe89012,EVPN Type-5 route leak,NX-OS,evpn,High,CVE-2025-10004,"10.2(1)",10.2(2),In Progress,2025-09-01,2025-10-15,"Filter with import/export RT",None observed
CSCwf90123,TrustSec SXP connection flap,IOS XE,cts,Medium,,"17.9.2",17.9.3,Open,2025-07-22,2025-10-11,"Increase keepalive and retry",N/A
CSCwg01234,SSH memory leak under key exchange,ASA/FTD,ssh,High,CVE-2025-10005,"9.18(1)",9.18(3),Open,2025-08-05,2025-10-14,"Limit concurrent mgmt sessions",PoC only
CSCxh11223,Port-channel LACP mis-synchronization,Catalyst 9500,l2 etherchannel,Medium,,"17.9.1",17.9.4,In Progress,2025-05-28,2025-10-09,"Disable fast-rate on affected Po",N/A
CSCxi22334,BGP RTC route-target import failure,IOS XR,bgp,High,CVE-2025-10006,"7.6.3,7.7.1",7.7.4,Open,2025-03-17,2025-10-13,"Soft reset AFI/SAFI rtc",None observed
CSCxj33445,Telemetry gNMI subscription drop,NX-OS,telemetry,Medium,,"10.1(2)",10.2(1),Fixed,2025-01-30,2025-06-27,"Reduce sample-interval",N/A
CSCxk44556,IP SLA jitter stats incorrect,IOS XE,ipsla,Low,,"17.6.x",17.6.7,Fixed,2025-02-22,2025-08-08,"Use UDP-echo instead",N/A
CSCxL55667,FXOS sensor daemon crash,Firepower,platform,High,CVE-2025-10007,"2.12.0",2.12.2,In Progress,2025-07-01,2025-10-16,"Increase core-size and restart",None observed
CSCxm66778,vPC peer-link flap on FEX reload,NX-OS,vpc,High,,"9.3(9),10.2(1)",10.2(3),Open,2025-09-10,2025-10-18,"Raise peer-gateway delay-restore",N/A
CSCxn77889,Wireless 802.1X reauth storm,Catalyst 9800,wireless,High,CVE-2025-10008,"17.12.1",17.12.3,Open,2025-08-26,2025-10-15,"Increase session-timeout and enable caching",Limited reports
CSCxo88990,NAT hairpin fails with TCP options,ASA/FTD,nat,Medium,,"9.18(2)",9.18(3),In Progress,2025-06-11,2025-10-05,"Remove SACK-permitted temporarily",N/A
CSCxp99001,VRF route-leak via PBR,IOS XE,pbr,High,CVE-2025-10009,"17.9.1",17.9.4,Open,2025-07-07,2025-10-12,"Constrain with route-map deny",None observed
CSCxq00112,Netconf candidate datastore lock hang,IOS XR,netconf,Medium,,"7.7.2",7.7.4,In Progress,2025-05-05,2025-10-17,"Avoid concurrent lock ops",N/A

スクリプト実行

スクリプト実行前にプロンプトを調整します。
すべてのバグを調査するのではなくOSとバージョンの情報を追加で渡してあげて、ヒットするものの調査だけをしてもらいます。

result = graph("""
               CSV調査を開始
               ---
               利用している機器: iosxe
               バージョン: 17.9.1
               """)

レコード数が20件なので、10個のワーカーが動きます。

$ time uv run main.py 
Graph without execution limits may run indefinitely if cycles exist
# 🔍 CSV調査統合レポート

## 📊 調査実施概要
- **調査対象製品**: IOS XE
- **稼働バージョン**: 17.9.1
- **分析実施Worker数**: 10名
- **総分析レコード数**: 20件
- **調査実施日**: 2025年10月相当

本当に並列で動いているのか..が少しに気になりますがcallbackをNoneにしなかったときは、文字が無茶苦茶になっていたので裏では並列になってるのかな...と予想してます。

出力したレポート

レポート全文

# 🔍 CSV調査統合レポート

## 📊 調査実施概要
- **調査対象製品**: IOS XE
- **稼働バージョン**: 17.9.1
- **分析実施Worker数**: 10名
- **総分析レコード数**: 20件
- **調査実施日**: 2025年10月相当

---

## 🚨 重大な発見:影響を受ける脆弱性・不具合の統合分析

### ✅ 総合判定サマリー

| 項目 | 結果 |
|------|------|
| **影響あり(高信頼度)** | 3件 |
| **影響あり(要注意)** | 1件 |
| **影響なし** | 16件 |
| **最高深刻度** | **High** |
| **CVE該当** | 2件 |

---

## 🔴 【Critical】即座に対応が必要な脆弱性

### 1️⃣ CSCxp99001: VRF route-leak via PBR ⚠️

| 項目 | 詳細 |
|------|------|
| **深刻度** | **High** |
| **CVE** | **CVE-2025-10009** |
| **影響バージョン** | 17.9.1 ✅ **完全一致** |
| **修正バージョン** | 17.9.4 |
| **検出Worker** | w9 |
| **ステータス** | Open |

**技術的影響:**
- VRF間でのルート情報の意図しない漏洩
- ネットワークセグメンテーションの破綻リスク
- セキュリティポリシーのバイパス可能性

**即時対応:**
> Constrain with route-map deny

**推奨アクション:**
1. 🔴 **即座**: Route-mapによるVRF間通信の明示的拒否設定
2. 🔴 **1週間以内**: バージョン17.9.4へのアップグレード計画策定
3. 🔴 **継続**: エクスプロイト情報の監視

---

### 2️⃣ CSCvx12345: OSPF adjacency flap on high CPU

| 項目 | 詳細 |
|------|------|
| **深刻度** | **High** |
| **CVE** | **CVE-2025-10001** |
| **影響バージョン** | 17.6.x, 17.9.1 ✅ |
| **修正バージョン** | 17.9.3 |
| **検出Worker** | w0 |
| **ステータス** | Open |

**技術的影響:**
- CPU高負荷時のOSPFネイバー関係の不安定化
- ルーティング障害の発生リスク
- ネットワーク可用性への影響

**即時対応:**
> Disable BFD on affected neighbors

**推奨アクション:**
1. 🟠 **24時間以内**: OSPFネイバー状態とCPU使用率の監視強化
2. 🟠 **1週間以内**: BFD無効化の検討と実施
3. 🟠 **1ヶ月以内**: 17.9.3へのアップグレード

---

## 🟠 【Warning】注意が必要な不具合

### 3️⃣ CSCxh11223: Port-channel LACP mis-synchronization

| 項目 | 詳細 |
|------|------|
| **深刻度** | Medium |
| **CVE** | なし |
| **影響バージョン** | 17.9.1 ✅ |
| **修正バージョン** | 17.9.4 |
| **検出Worker** | w5 |
| **ステータス** | In Progress |

**技術的影響:**
- Port-channelのLACP同期問題
- リンクフラップの可能性
- トラフィック断やパフォーマンス劣化

**回避策:**
> Disable fast-rate on affected Po
> interface Port-channel X
>   no lacp rate fast


**推奨アクション:**
1. 🟡 **1週間以内**: Port-channel構成の棚卸し
2. 🟡 **2週間以内**: LACP fast-rate無効化の検討
3. 🟡 **2ヶ月以内**: 17.9.4へのアップグレード

---

### 4️⃣ CSCwf90123: TrustSec SXP connection flap

| 項目 | 詳細 |
|------|------|
| **深刻度** | Medium |
| **CVE** | なし |
| **影響バージョン** | 17.9.2(17.9.1も影響可能性) |
| **修正バージョン** | 17.9.3 |
| **検出Worker** | w4 |
| **ステータス** | Open |

**技術的影響:**
- TrustSec SXP接続の不安定性
- セキュリティポリシー適用の不全リスク

**回避策:**
> cts sxp connection peer <peer-ip> source <source-ip>
>   retry-period 120
>   reconciliation-period 120

**推奨アクション:**
- TrustSec使用環境の場合のみ対応が必要
- Keepalive/Retry値の調整を検討

---

## ✅ 影響なしと判定された項目(16件)

### 製品不一致による除外
- **IOS XR関連**: CSCxq00112, CSCxi22334, CSCwb56789(w9, w5, w2が報告)
- **NX-OS関連**: CSCxm66778, CSCxj33445, CSCxk44556, CSCwe89012(w7, w6, w3が報告)
- **ASA/FTD関連**: CSCxo88990, CSCvw34567, CSCwg01234(w8, w1, w4が報告)
- **Firepower関連**: CSCxL55667(w7が報告)
- **Catalyst 9800関連**: CSCxn77889(w8が報告)

### バージョン範囲外による除外
- **CSCwc67890**: 17.3.x系の問題(17.3.8で修正済み)- w2が報告
- **CSCwa45678**: 17.12.2の問題(17.9.1より新しいバージョン)- w1が報告
- **CSCwd78901**: 17.6.5の問題(17.6.6で修正済み)- w3が報告
- **CSCxk44556**: 17.6.x系の問題(17.6.7で修正済み)- w6が報告

---

## 📋 優先度別アクションプラン

### 🔴 緊急(24時間以内)
1. **CSCxp99001対応**: VRF route-map設定の実施
2. **CSCvx12345対応**: OSPF/CPU監視の強化
3. **影響範囲調査**: 各バグの該当機能使用状況の確認

### 🟠 高優先(1週間以内)
1. **Workaround適用**: BFD無効化、LACP fast-rate調整
2. **アップグレード計画**: 17.9.3または17.9.4への移行計画策定
3. **リスク評価**: ビジネス影響度の詳細分析

### 🟡 中優先(1ヶ月以内)
1. **本番アップグレード**: テスト環境での検証後、本番適用
2. **設定見直し**: セキュリティポリシーの再確認
3. **監視強化**: 継続的なログ監視体制の構築

---

## 📊 Worker別貢献度分析

| Worker | 影響あり検出 | 影響なし分析 | 品質評価 |
|--------|------------|------------|---------|
| w9 | 1件(High) | 1件 | ⭐⭐⭐⭐⭐ |
| w0 | 1件(High) | 1件 | ⭐⭐⭐⭐⭐ |
| w5 | 1件(Medium) | 1件 | ⭐⭐⭐⭐ |
| w4 | 1件(Medium) | 1件 | ⭐⭐⭐⭐ |
| w8 | 0件 | 2件 | ⭐⭐⭐ |
| w2, w7, w6, w1, w3 | 0件 | 各2件 | ⭐⭐⭐ |

---

## 🎯 最終推奨事項

### 1. 即座の対応(本日中)
- [ ] VRF route-map deny設定の実装
- [ ] OSPF環境でのBFD無効化検討
- [ ] CPU/メモリ/ネイバー状態の監視強化

### 2. 短期対応(2週間以内)
- [ ] バージョン17.9.4へのアップグレード計画策定
- [ ] テスト環境での事前検証
- [ ] 変更管理プロセスの準備

### 3. 中期対応(1-2ヶ月)
- [ ] 本番環境へのアップグレード実施
- [ ] アップグレード後の動作検証
- [ ] セキュリティポリシーの再評価

---

## 📝 補足情報

### 判定根拠の信頼性
- **10名のWorkerによる独立分析**により高い信頼性を確保
- **複数のWorkerが同一結論**に達した項目は信頼度が特に高い
- バージョンマッチングは文字列完全一致で実施

### 今後の監視ポイント
1. Cisco Security Advisoryの定期確認
2. CVE-2025-10001, CVE-2025-10009のエクスプロイト情報
3. 17.9.4リリース後の追加不具合報告

---

**📅 レポート作成日**: 2025年10月  
**✅ 調査ステータス**: 完了  
**👥 分析担当Worker数**: 10名  
**🔖 総Token使用量**: 約18,500トークン(予算内)

結果から1つピックアップしてCSVと見比べていましたが、iosxe:17.9.1に該当するバグが意図通りに見つけられています。

CSCxp99001,VRF route-leak via PBR,IOS XE,pbr,High,CVE-2025-10009,"17.9.1",17.9.4,Open,2025-07-07,2025-10-12,"Constrain with route-map deny",None observed

感想

Strands Agentsを使ってバグ調査をするエージェントを作成できました。比較的簡単にマルチエージェントが作れたと思います。
こんな複雑なことしないでCSVをまるごと1つのエージェントに渡せばいいじゃん!と思われたかもしれませんが、レコード数が1000-2000になっても負荷分散して対応してほしいといった期待があり並列実装をためしています。
ただ、トレーサビリティがなくWorkerエージェントがどんな結果をAggエージェントにしているのか?など気になるところが多々ありました。もう少し挙動を詳しく把握したいところです。

次はA2Aも試してみたいと思います。