Agentic AI 開発実践 - Strands Agents 版
ホーム
演習ガイド
ホーム
演習ガイド
  • 演習1: 最小構成エージェント
  • 演習2: 複数ツールを持つ単一エージェント
  • 演習3: Graphによる一次切り分け
  • 演習4: 永続化・Hooks・HITL
  • 演習5: MCPサーバーへの接続
  • 演習6: Guardrails設定と評価結果の確認

演習1: 最小構成エージェント

所要時間の目安: 20分

この演習でやること

社内FAQを検索するsearch_faqツールを1つだけ持った、最小構成のエージェントを実装します。これが、コース全体を通じて育てていく「社内ITヘルプデスクエージェント」の出発点になります。

ベースコードファイル: code/ex1_minimal_agent/agent.py

このファイルを開き、TODO 1、TODO 2、…と番号が振られた場所に、以下の手順のコードをコピペしながら完成させてください(番号はファイル上の位置ではなく、追加する順番を表しています)。

手順

手順1: FAQデータを用意する

agent.pyのTODO 1に、以下のFAQデータを貼り付けます。

agent.py(TODO 1)
FAQ_DATA = {
    "VPN": "VPNに接続できない場合は、まず社内ポータルから最新のVPNクライアントを"
           "再インストールし、二要素認証アプリの時刻同期を確認してください。",
    "パスワード": "パスワードを忘れた場合は、社内ポータルの『パスワード再設定』から"
                 "手続きできます。ロックされた場合の解除は情報システム部での対応が必要です。",
    "Wi-Fi": "社内Wi-Fiに接続できない場合は、端末のWi-Fi設定を一度削除し、"
             "社内ポータルに掲載のパスワードで再接続してください。",
    "プリンター": "プリンターが認識されない場合は、社内ポータルの『プリンタードライバー』"
                 "ページから該当機種のドライバーを再インストールしてください。",
    "メール": "社用メールが送受信できない場合は、Webメール版で状況を確認し、"
             "改善しない場合は情報システム部に問い合わせてください。",
    "会議室予約": "会議室予約システムにログインできない場合は、社内SSOのパスワードを"
                 "確認し、改善しない場合は総務部に問い合わせてください。",
    "ソフトウェア": "新しいソフトウェアのインストールには、情報システム部への"
                   "利用申請(社内ポータルの『ソフトウェア利用申請』)が必要です。",
    "PC持ち出し": "社外へのPC持ち出しには、上長承認を得たうえで『PC持ち出し申請』を"
                 "情報システム部に提出してください。",
}

手順2: search_faqツールを定義する

まずTODO 2(ファイル先頭のインポート部分)に、以下を貼り付けます。

agent.py(TODO 2)
from strands import tool

続けてTODO 3に、ツールの定義を貼り付けます。

agent.py(TODO 3)
@tool
def search_faq(keyword: str) -> str:
    """社内FAQをキーワードで検索し、よくある質問への回答を返す。

    VPN・パスワード・Wi-Fiなど、社内の手続きややり方に関する
    「どうすればよいか」という問い合わせに使う。

    Args:
        keyword: 検索キーワード(例: VPN, パスワード, Wi-Fi)
    """
    hits = [text for key, text in FAQ_DATA.items() if keyword in key or key in keyword]
    if hits:
        return "\n".join(hits)
    return "該当するFAQが見つかりませんでした。"

ここで重要なのは、search_faqのdocstring(説明文)の書き方です。「何のためのツールか」「どんな場面で使うべきか」「引数に何を渡すか」を明記することで、LLMが「今回の質問にはこのツールを使うべきか」を正しく判断できるようになります。

手順3: Agentを定義する

TODO 4に、以下のコードを貼り付けます。

agent.py(TODO 4)
agent = Agent(
    model=model,
    tools=[search_faq],
    system_prompt="あなたは社内ITヘルプデスクの担当者です。FAQに関する質問にはsearch_faqツールを使って回答してください。",
)

手順4: 動作確認する

TODO 5(if __name__ == "__main__":ブロック)に、以下のコードを貼り付けます。コードはif __name__ == "__main__":の中に入るため、半角スペース4つでインデント済みです。TODOコメントの行を、行頭の空白も含めて選択し、その上から貼り付けてください(インデントの途中や後ろにカーソルを置いて貼ると、1行目だけインデントがずれてエラーになります)。

agent.py(TODO 5)
    result = agent("パスワードを忘れてしまいました。どうすればいいですか?")
    print(result)

    result2 = agent("プリンターの色設定がおかしいです。")
    print(result2)

ターミナルで実行します。

python agent.py

1つ目の問い合わせには、FAQの「パスワード」の内容に基づいた回答が返ることを確認してください。2つ目の問い合わせは、チェックリスト通りであれば「該当するFAQが見つかりませんでした」という応答になるはずです(プリンターのFAQはキーワード「プリンター」で検索されるため、この問い合わせ文だと一致しない点に注目してください)。

手順5: 構造化出力に対応する

最後に、応答をSupportAnswerという構造化データとして受け取れるようにします。TODO 6に、以下のインポートを貼り付けます。

agent.py(TODO 6)
from pydantic import BaseModel, Field

TODO 7に、以下のクラス定義を貼り付けます。

agent.py(TODO 7)
class SupportAnswer(BaseModel):
    answer: str = Field(description="問い合わせへの回答本文")
    needs_escalation: bool = Field(description="情報システム部へのエスカレーションが必要か")

最後に、TODO 5に書いたコードを、次のように書き換えます(先ほど貼り付けたコードを、行頭の空白も含めてまとめて選択し、その上から貼り付けます。こちらも半角スペース4つでインデント済みです)。

agent.py(TODO 5・書き換え)
    result = agent(
        "パスワードを忘れてしまいました。どうすればいいですか?",
        structured_output_model=SupportAnswer,
    )
    answer = result.structured_output
    print(answer.answer)
    print(answer.needs_escalation)

max_tokensを忘れずに。 指定しないと、モデルによっては長大な応答を生成しようとしてMaxTokensReachedExceptionになることがあります。ベースコードのmodelには、あらかじめmax_tokens=1024を設定済みです。

模範解答(agent.py 全体)を表示
agent.py(完成形)
from strands import Agent, tool
from strands.models import BedrockModel
from pydantic import BaseModel, Field


model = BedrockModel(
    model_id="jp.amazon.nova-2-lite-v1:0",
    region_name="ap-northeast-1",
    max_tokens=1024,
)


FAQ_DATA = {
    "VPN": "VPNに接続できない場合は、まず社内ポータルから最新のVPNクライアントを"
           "再インストールし、二要素認証アプリの時刻同期を確認してください。",
    "パスワード": "パスワードを忘れた場合は、社内ポータルの『パスワード再設定』から"
                 "手続きできます。ロックされた場合の解除は情報システム部での対応が必要です。",
    "Wi-Fi": "社内Wi-Fiに接続できない場合は、端末のWi-Fi設定を一度削除し、"
             "社内ポータルに掲載のパスワードで再接続してください。",
    "プリンター": "プリンターが認識されない場合は、社内ポータルの『プリンタードライバー』"
                 "ページから該当機種のドライバーを再インストールしてください。",
    "メール": "社用メールが送受信できない場合は、Webメール版で状況を確認し、"
             "改善しない場合は情報システム部に問い合わせてください。",
    "会議室予約": "会議室予約システムにログインできない場合は、社内SSOのパスワードを"
                 "確認し、改善しない場合は総務部に問い合わせてください。",
    "ソフトウェア": "新しいソフトウェアのインストールには、情報システム部への"
                   "利用申請(社内ポータルの『ソフトウェア利用申請』)が必要です。",
    "PC持ち出し": "社外へのPC持ち出しには、上長承認を得たうえで『PC持ち出し申請』を"
                 "情報システム部に提出してください。",
}


@tool
def search_faq(keyword: str) -> str:
    """社内FAQをキーワードで検索し、よくある質問への回答を返す。

    VPN・パスワード・Wi-Fiなど、社内の手続きややり方に関する
    「どうすればよいか」という問い合わせに使う。

    Args:
        keyword: 検索キーワード(例: VPN, パスワード, Wi-Fi)
    """
    hits = [text for key, text in FAQ_DATA.items() if keyword in key or key in keyword]
    if hits:
        return "\n".join(hits)
    return "該当するFAQが見つかりませんでした。"


class SupportAnswer(BaseModel):
    answer: str = Field(description="問い合わせへの回答本文")
    needs_escalation: bool = Field(description="情報システム部へのエスカレーションが必要か")


agent = Agent(
    model=model,
    tools=[search_faq],
    system_prompt="あなたは社内ITヘルプデスクの担当者です。FAQに関する質問にはsearch_faqツールを使って回答してください。",
)


if __name__ == "__main__":
    result = agent(
        "パスワードを忘れてしまいました。どうすればいいですか?",
        structured_output_model=SupportAnswer,
    )
    answer = result.structured_output
    print(answer.answer)
    print(answer.needs_escalation)

動作確認チェックリスト

この演習が完了すると、次のことができているはずです。

チャレンジ演習(任意)

  1. FAQデータに新しいトピックを1件追加し、正しく検索できることを確認してください。
  2. SupportAnswerにcategory(問い合わせのカテゴリ)フィールドを追加し、エージェントが適切なカテゴリを判定できるか試してみましょう。
チャレンジ演習の模範解答例を表示
# FAQ_DATAに1件追加する例(TODO 1に追記)
FAQ_DATA["座席予約"] = (
    "フリーアドレス座席の予約は、社内ポータルの『座席予約』から行えます。"
    "当日キャンセルが続く場合は、総務部からの利用停止の対象になることがあります。"
)


# SupportAnswerにcategoryフィールドを追加する例(TODO 7を書き換え)
class SupportAnswer(BaseModel):
    answer: str = Field(description="問い合わせへの回答本文")
    needs_escalation: bool = Field(description="情報システム部へのエスカレーションが必要か")
    category: str = Field(description="問い合わせのカテゴリ(例: VPN, パスワード, Wi-Fi等)")

categoryは、フィールドを追加しただけでは画面に表示されません。TODO 5(手順5で書き換えたコード)の末尾に、printを1行追加します(下記のうち、追加するのは最後のprint(answer.category)の1行だけです)。

    # categoryをprintする例(TODO 5の末尾に追記)
    result = agent(
        "パスワードを忘れてしまいました。どうすればいいですか?",
        structured_output_model=SupportAnswer,
    )
    answer = result.structured_output
    print(answer.answer)
    print(answer.needs_escalation)
    print(answer.category)  # 追加: categoryを表示する

次の演習へ

演習2では、このエージェントに「チケット状況確認」「パスワードリセット手順案内」の2つのツールを追加し、複数ツールを持つエージェントに拡張します。演習2のベースコード(code/ex2_multi_tool/agent.py)には、この演習の完成形がすでに組み込まれています。

最終更新: 2026/10/20 1:23
Next
演習2: 複数ツールを持つ単一エージェント