AI Agent入門 第3部:ツール統合とWebUI – 高度なAI Agentシステム構築

AI Agent入門 第3部:ツール統合とWebUI – 高度なAI Agentシステム構築

AI Agent開発シリーズの最終章へようこそ!第1部で基本エージェント、第2部でメモリ機能を実装しましたが、今度は「考えて、ツールを使って、問題を解決する」真の自律型エージェントを構築します。

従来のチャットボットの限界を超え、計算、検索、分析を自動実行し、プロフェッショナルなWebUIで操作できる本格システムを完成させます。

この記事で実装するもの:

  • ✅ 高度な計算ツール(安全性考慮)
  • ✅ Web検索ツール(SerpAPI + フォールバック)
  • ✅ OpenAI Function Callingによるツール統合
  • ✅ Streamlit製プロフェッショナルWebUI

第3部で身につけるスキル:

  • ツール開発とセキュリティ実装
  • Function Callingによる動的ツール選択
  • 本格的なWebアプリケーション開発
  • プロダクション環境への対応

前提条件:

完成時には、企業でも使える本格的なAI Agentシステムが手に入ります!

🛠️ ツール統合エージェントの実装

🧮 高度な計算ツールの実装

まず、数学計算を安全に実行するツールを実装します。セキュリティを重視し、悪意のあるコード実行を防ぎます:

# tools/calculator.py
from langchain.tools import BaseTool
from typing import Type, Optional
from pydantic import BaseModel, Field
import math
import operator
import ast
import re

class CalculatorInput(BaseModel):
    """計算ツールの入力スキーマ"""
    expression: str = Field(
        description="数学式(例:2 + 3 * 4, sin(30), sqrt(16), log(100))"
    )

class AdvancedCalculator(BaseTool):
    """高度な計算機能を提供するツール

    基本的な四則演算から三角関数、対数、統計関数まで
    幅広い数学的計算をサポートします。
    セキュリティ対策として、安全な関数のみを許可します。
    """

    name = "calculator"
    description = """数学的計算を安全に実行します。

対応している計算:
- 基本演算: +, -, *, /, %, **(べき乗)
- 三角関数: sin, cos, tan(度数で入力)
- 逆三角関数: asin, acos, atan
- 対数: log(自然対数), log10(常用対数)
- 平方根: sqrt
- 指数関数: exp
- 絶対値: abs
- 円周率: pi
- ネイピア数: e

使用例:
- 基本計算: "2 + 3 * 4"
- 三角関数: "sin(30)" (30度のサイン)
- 対数: "log10(100)"
- 平方根: "sqrt(16)"
- 複合計算: "sin(30) + cos(60)"
"""

    args_schema: Type[BaseModel] = CalculatorInput

    def __init__(self):
        super().__init__()

        # 安全な数学関数のマッピング
        self.safe_functions = {
            # 基本数学関数
            'sin': lambda x: math.sin(math.radians(x)),  # 度数入力
            'cos': lambda x: math.cos(math.radians(x)),
            'tan': lambda x: math.tan(math.radians(x)),
            'asin': lambda x: math.degrees(math.asin(x)),  # 度数出力
            'acos': lambda x: math.degrees(math.acos(x)),
            'atan': lambda x: math.degrees(math.atan(x)),

            # 対数・指数関数
            'sqrt': math.sqrt,
            'log': math.log,      # 自然対数
            'log10': math.log10,  # 常用対数
            'log2': math.log2,    # 二進対数
            'exp': math.exp,

            # その他の関数
            'abs': abs,
            'round': round,
            'ceil': math.ceil,
            'floor': math.floor,
            'pow': pow,

            # 定数
            'pi': math.pi,
            'e': math.e,
        }

    def _run(self, expression: str) -> str:
        """計算を安全に実行"""
        try:
            # 入力の前処理
            expression = expression.strip()

            # 危険な文字列をチェック
            dangerous_patterns = [
                '__', 'import', 'exec', 'eval', 'open', 'file',
                'input', 'raw_input', 'compile', 'reload'
            ]

            for pattern in dangerous_patterns:
                if pattern in expression.lower():
                    return "エラー: 安全でない式が検出されました"

            # 数学関数の名前空間を設定
            namespace = {
                "__builtins__": {},  # 組み込み関数を無効化
                **self.safe_functions
            }

            # 安全な評価の実行
            result = eval(expression, namespace)

            # 結果の型チェックと整形
            if isinstance(result, (int, float)):
                # 無限大・NaNのチェック
                if math.isinf(result):
                    return "エラー: 結果が無限大です"
                elif math.isnan(result):
                    return "エラー: 結果が数値ではありません(NaN)"

                # 整数の場合は小数点を表示しない
                if isinstance(result, float) and result.is_integer():
                    return str(int(result))
                elif isinstance(result, float):
                    # 小数点以下10桁まで表示、末尾のゼロは削除
                    return f"{result:.10f}".rstrip('0').rstrip('.')
                else:
                    return str(result)
            else:
                return str(result)

        except ZeroDivisionError:
            return "エラー: ゼロ除算は実行できません"
        except ValueError as e:
            return f"エラー: 数学的エラー - {str(e)}"
        except OverflowError:
            return "エラー: 数値が大きすぎます"
        except SyntaxError:
            return "エラー: 数式の構文が正しくありません"
        except NameError as e:
            return f"エラー: 未定義の関数または変数 - {str(e)}"
        except Exception as e:
            return f"エラー: 計算できませんでした - {str(e)}"

    async def _arun(self, expression: str) -> str:
        """非同期版計算実行"""
        return self._run(expression)

# テスト関数
def test_calculator():
    """計算ツールのテスト"""
    calc = AdvancedCalculator()

    test_cases = [
        ("2 + 3 * 4", "基本計算"),
        ("sqrt(16)", "平方根"),
        ("sin(30)", "三角関数(30度)"),
        ("cos(60)", "三角関数(60度)"),
        ("log10(100)", "常用対数"),
        ("log(e)", "自然対数"),
        ("2 ** 8", "べき乗"),
        ("pi * 2", "円周率"),
        ("abs(-5)", "絶対値"),
        ("sin(30) + cos(60)", "複合計算"),
        ("sqrt(2) * sqrt(8)", "複合計算2"),
    ]

    print("🧮 計算ツールのテスト:")
    for expression, description in test_cases:
        result = calc._run(expression)
        print(f"  {expression:20} = {result:15} ({description})")

if __name__ == "__main__":
    test_calculator()

🌐 Web検索ツールの実装

次に、リアルタイム情報検索機能を提供するツールを実装します:

# tools/web_search.py
from langchain.tools import BaseTool
from typing import Type, Optional, List, Dict, Any
from pydantic import BaseModel, Field
import requests
import json
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse
import os
import time
from dotenv import load_dotenv
load_dotenv()
class WebSearchInput(BaseModel):
"""Web検索ツールの入力スキーマ"""
query: str = Field(description="検索したいキーワードや質問")
num_results: int = Field(
default=5, 
description="取得する検索結果数(1-10)",
ge=1,
le=10
)
class WebSearchTool(BaseTool):
"""Web検索機能を提供するツール
SerpAPIまたはフォールバック手法を使用して
インターネット上の最新情報を検索します。
"""
name = "web_search"
description = """インターネット上の最新情報を検索して取得します。
使用場面:
- 最新のニュースや情報
- 特定のトピックについての詳細情報
- 統計データや事実確認
- 技術的な情報やチュートリアル
使用例:
- "2024年 Python 最新トレンド"
- "ChatGPT 最新アップデート"
- "東京オリンピック 結果"
- "機械学習 入門 チュートリアル"
注意: 正確性は情報源に依存するため、重要な情報は複数のソースで確認することをお勧めします。
"""
args_schema: Type[BaseModel] = WebSearchInput
def __init__(self):
super().__init__()
self.serpapi_key = os.getenv("SERPAPI_API_KEY")
self.fallback_enabled = True
# リクエスト間の待機時間(レート制限対策)
self.request_delay = 1.0
self.last_request_time = 0
def _wait_for_rate_limit(self):
"""レート制限のための待機"""
current_time = time.time()
time_since_last = current_time - self.last_request_time
if time_since_last < self.request_delay:
time.sleep(self.request_delay - time_since_last)
self.last_request_time = time.time()
def _run(self, query: str, num_results: int = 5) -> str:
"""Web検索を実行"""
try:
# レート制限対策
self._wait_for_rate_limit()
if self.serpapi_key:
return self._search_with_serpapi(query, num_results)
elif self.fallback_enabled:
return self._search_with_fallback(query, num_results)
else:
return "エラー: 検索APIが設定されていません。SERPAPI_API_KEYを設定してください。"
except Exception as e:
return f"検索エラー: {str(e)}"
def _search_with_serpapi(self, query: str, num_results: int) -> str:
"""SerpAPIを使用した本格的な検索"""
try:
url = "https://serpapi.com/search.json"
params = {
"engine": "google",
"q": query,
"api_key": self.serpapi_key,
"num": min(num_results, 10),
"hl": "ja",  # 日本語
"gl": "jp",  # 日本の検索結果
"safe": "active"  # セーフサーチ
}
response = requests.get(url, params=params, timeout=15)
response.raise_for_status()
data = response.json()
# エラーチェック
if "error" in data:
return f"検索APIエラー: {data['error']}"
organic_results = data.get("organic_results", [])
if not organic_results:
return f"「{query}」に関する検索結果が見つかりませんでした。"
# 結果の整形
results = []
for i, result in enumerate(organic_results[:num_results], 1):
title = result.get("title", "タイトルなし")
link = result.get("link", "")
snippet = result.get("snippet", "説明なし")
# 長すぎるスニペットは切り詰め
if len(snippet) > 200:
snippet = snippet[:200] + "..."
results.append(f"""
🔗 **{i}. {title}**
URL: {link}
📄 概要: {snippet}
""")
search_info = data.get("search_information", {})
total_results = search_info.get("total_results", "不明")
header = f"「{query}」の検索結果 (総件数: {total_results}):n"
return header + "n".join(results)
except requests.exceptions.Timeout:
return "検索がタイムアウトしました。しばらくしてから再試行してください。"
except requests.exceptions.RequestException as e:
return f"検索リクエストエラー: {str(e)}"
except Exception as e:
return f"SerpAPI検索エラー: {str(e)}"
def _search_with_fallback(self, query: str, num_results: int) -> str:
"""フォールバック検索(デモ用の模擬検索)"""
# 実際のプロダクションでは、他の検索APIや
# ウェブスクレイピングを実装することができます
fallback_database = {
"python": {
"title": "Python プログラミング言語",
"description": "Pythonは、読みやすく書きやすい高水準プログラミング言語です。データサイエンス、ウェブ開発、AI開発など幅広い分野で使用されています。",
"url": "https://www.python.org/"
},
"ai": {
"title": "人工知能(AI)技術",
"description": "人工知能は、人間の知能を模倣する技術分野です。機械学習、深層学習、自然言語処理などが主要な技術です。",
"url": "https://ja.wikipedia.org/wiki/人工知能"
},
"機械学習": {
"title": "機械学習入門",
"description": "機械学習は、データから自動的にパターンを学習し、予測や分類を行うAI技術です。教師あり学習、教師なし学習、強化学習に分類されます。",
"url": "https://ja.wikipedia.org/wiki/機械学習"
},
"langchain": {
"title": "LangChain フレームワーク",
"description": "LangChainは、大規模言語モデル(LLM)を使用したアプリケーション開発のためのフレームワークです。チェーン、エージェント、メモリなどの機能を提供します。",
"url": "https://python.langchain.com/"
},
"openai": {
"title": "OpenAI GPT",
"description": "OpenAIが開発したGPTシリーズは、自然言語処理における画期的な言語モデルです。ChatGPT、GPT-4などが有名です。",
"url": "https://openai.com/"
},
"chatgpt": {
"title": "ChatGPT - 対話型AI",
"description": "ChatGPTは、OpenAIが開発した対話型AI システムです。自然言語での会話が可能で、質問応答、文章作成、コード生成など様々なタスクをこなします。",
"url": "https://chat.openai.com/"
},
"streamlit": {
"title": "Streamlit - Pythonでデータアプリ作成",
"description": "Streamlitは、データサイエンティストや機械学習エンジニアが簡単にWebアプリケーションを作成できるPythonライブラリです。",
"url": "https://streamlit.io/"
}
}
# キーワードマッチング
query_lower = query.lower()
matched_results = []
for keyword, info in fallback_database.items():
if keyword.lower() in query_lower or any(word in keyword.lower() for word in query_lower.split()):
matched_results.append(info)
if matched_results:
results = []
for i, result in enumerate(matched_results[:num_results], 1):
results.append(f"""
🔗 **{i}. {result['title']}**
URL: {result['url']}
📄 概要: {result['description']}
""")
warning = "n⚠️ 注意: これは制限された検索結果です。より詳細で最新の情報を得るには、SERPAPI_API_KEYを設定してください。"
return f"「{query}」に関する情報:n" + "n".join(results) + warning
else:
return f"""「{query}」に関する具体的な情報がフォールバックデータベースで見つかりませんでした。
💡 より詳細な検索を行うには:
1. SERPAPI_API_KEYを環境変数に設定
2. https://serpapi.com/ でアカウントを作成
3. 無料枠で月1000回の検索が可能
フォールバック検索では以下のトピックに対応:
- Python プログラミング
- AI・機械学習
- LangChain
- OpenAI・ChatGPT
- Streamlit
"""
async def _arun(self, query: str, num_results: int = 5) -> str:
"""非同期版検索実行"""
return self._run(query, num_results)
# テスト関数
def test_web_search():
"""Web検索ツールのテスト"""
search = WebSearchTool()
test_queries = [
"Python チュートリアル",
"機械学習 基礎",
"LangChain 使い方",
"ChatGPT 最新情報"
]
print("🌐 Web検索ツールのテスト:")
for query in test_queries:
print(f"n検索: {query}")
result = search._run(query, num_results=2)
print(result)
print("-" * 50)
if __name__ == "__main__":
test_web_search()

🤖 ツール統合エージェントの実装

それでは、計算ツールと検索ツールを統合した高度なエージェントを作成しましょう:

# agents/tool_enhanced_agent.py
from langchain_openai import ChatOpenAI
from langchain.agents import create_openai_tools_agent, AgentExecutor
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.memory import ConversationBufferWindowMemory
from langchain_core.messages import SystemMessage
from typing import List, Dict, Any
import sys
from pathlib import Path
# プロジェクトルートをパスに追加
project_root = Path(__file__).parent.parent
sys.path.append(str(project_root))
from config.settings import settings, validate_environment
from tools.calculator import AdvancedCalculator
from tools.web_search import WebSearchTool
class ToolEnhancedAgent:
"""ツール統合エージェント
このクラスは複数のツールを統合し、問題解決に必要な
ツールを自動選択して実行する高度なエージェントです。
"""
def __init__(self, model_name: str = None, memory_window: int = 10):
"""ツール統合エージェントを初期化
Args:
model_name: 使用するOpenAIモデル名
memory_window: メモリに保持する会話数
"""
# 設定を取得
model_config = settings.get_model_config(model_name)
# LLMの初期化(ツール使用時は低Temperatureを推奨)
self.llm = ChatOpenAI(
model=model_config["model_name"],
temperature=0.1,  # ツール使用時は一貫性を重視
api_key=model_config["api_key"]
)
# ツールの初期化
self.tools = self._initialize_tools()
# メモリの設定
self.memory = ConversationBufferWindowMemory(
k=memory_window,
return_messages=True,
memory_key="chat_history"
)
# システムプロンプトの定義
self.system_prompt = """あなたはツールを使いこなす高度なAIアシスタントです。
ユーザーの問題を解決するために、適切なツールを選択し、組み合わせて使用してください。
利用可能なツール:
1. calculator: 数学的計算(基本演算、三角関数、対数など)
2. web_search: インターネット検索(リアルタイム情報取得)
ツール使用のガイドライン:
- 計算が必要な場合は calculator を使用
- 最新情報や具体的な事実確認が必要な場合は web_search を使用
- 複数のツールが必要な場合は、論理的な順序で実行
- ツールの結果を基に、分かりやすい回答を提供
- ツールを使用しない場合でも、質の高い回答を心がける
重要な指針:
1. ユーザーの意図を正確に理解する
2. 必要に応じて複数のツールを組み合わせる
3. ツールの実行結果を適切に解釈・説明する
4. エラーが発生した場合は代替手段を提案する
5. 常に正確で有用な情報提供を目指す"""
# プロンプトテンプレートの作成
self.prompt = ChatPromptTemplate.from_messages([
SystemMessage(content=self.system_prompt),
MessagesPlaceholder(variable_name="chat_history"),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad")
])
# OpenAI Tools Agentの作成
self.agent = create_openai_tools_agent(
llm=self.llm,
tools=self.tools,
prompt=self.prompt
)
# AgentExecutorの作成(メモリ付き)
self.agent_executor = AgentExecutor(
agent=self.agent,
tools=self.tools,
memory=self.memory,
verbose=True,  # デバッグ用
max_iterations=5,  # 無限ループ防止
early_stopping_method="generate",
handle_parsing_errors=True
)
def _initialize_tools(self) -> List:
"""利用可能なツールを初期化"""
tools = []
# 計算ツール
calculator = AdvancedCalculator()
tools.append(calculator)
# Web検索ツール
web_search = WebSearchTool()
tools.append(web_search)
return tools
def chat_with_tools(self, user_input: str) -> str:
"""ツール統合チャット
Args:
user_input: ユーザーからの入力
Returns:
AI生成の回答(ツール実行結果を含む)
"""
try:
# エージェント実行
response = self.agent_executor.invoke({
"input": user_input,
"chat_history": self.memory.chat_memory.messages
})
return response["output"]
except Exception as e:
error_msg = f"申し訳ありません。処理中にエラーが発生しました: {str(e)}"
# エラーもメモリに記録
self.memory.chat_memory.add_user_message(user_input)
self.memory.chat_memory.add_ai_message(error_msg)
return error_msg
def get_available_tools(self) -> List[Dict[str, Any]]:
"""利用可能なツール一覧を取得"""
return [
{
"name": tool.name,
"description": tool.description,
"args_schema": tool.args_schema.schema() if hasattr(tool, 'args_schema') else {}
}
for tool in self.tools
]
def clear_memory(self):
"""メモリをクリア"""
self.memory.clear()
def get_conversation_length(self) -> int:
"""現在の会話長を取得"""
return len(self.memory.chat_memory.messages)
def demonstrate_tool_agent():
"""ツール統合エージェントのデモンストレーション"""
print("🔧 ツール統合エージェントのデモンストレーション")
print("=" * 60)
agent = ToolEnhancedAgent()
# 利用可能なツールを表示
print("📋 利用可能なツール:")
for tool in agent.get_available_tools():
print(f"  - {tool['name']}: {tool['description'].split('.')[0]}")
print()
# テストケース
test_cases = [
{
"input": "2の10乗を計算してください",
"expected_tool": "calculator",
"description": "基本的な計算"
},
{
"input": "sin(45度) + cos(30度)を計算してください", 
"expected_tool": "calculator",
"description": "三角関数の計算"
},
{
"input": "円周率の値を使って、半径5cmの円の面積を計算してください",
"expected_tool": "calculator",
"description": "円周率を使った計算"
},
{
"input": "2024年のPython最新トレンドについて調べてください",
"expected_tool": "web_search", 
"description": "Web検索"
}
]
for i, test_case in enumerate(test_cases, 1):
print(f"🔍 テストケース {i}: {test_case['description']}")
print(f"入力: {test_case['input']}")
print(f"期待ツール: {test_case['expected_tool']}")
print("-" * 40)
try:
response = agent.chat_with_tools(test_case['input'])
print(f"📝 AI応答:n{response}")
except Exception as e:
print(f"❌ エラー: {e}")
print("=" * 60)
def interactive_tool_chat():
"""対話型ツール統合チャット"""
agent = ToolEnhancedAgent()
print("🛠️ ツール統合エージェントが起動しました!")
print("このエージェントは計算とWeb検索ができます。")
print("'quit'、'exit'、'終了'で終了します。")
print("'tools'で利用可能なツール一覧を表示します。n")
# サンプル質問の提案
sample_questions = [
"sin(30) + cos(60) を計算して",
"2024年のPython最新トレンドを調べて",
"円周率の10乗を計算してください",
"ChatGPTの最新ニュースを調べて"
]
print("💡 サンプル質問:")
for i, question in enumerate(sample_questions, 1):
print(f"  {i}. {question}")
print()
while True:
user_input = input("あなた: ")
if user_input.lower() in ['quit', 'exit', '終了', 'q']:
print(f"n📊 総会話数: {agent.get_conversation_length()}")
print("👋 ありがとうございました!")
break
# 特別コマンド
if user_input.lower() == 'tools':
print("n📋 利用可能なツール:")
for tool in agent.get_available_tools():
print(f"  - {tool['name']}: {tool['description'].split('.')[0]}")
continue
if user_input.lower() == 'clear':
agent.clear_memory()
print("🧹 メモリをクリアしました")
continue
if not user_input.strip():
print("❓ 何か質問をしてください。")
continue
# ツール統合チャット実行
print("🤔 考え中...")
response = agent.chat_with_tools(user_input)
print(f"🔧 AI: {response}n")
# 使用例とテスト
if __name__ == "__main__":
# 環境設定の検証
if not validate_environment():
print("環境設定を確認してから再実行してください。")
exit(1)
print("AI Agent ツール統合システムが起動しました!n")
# 実行モードの選択
mode = input("実行モード選択 [1: デモ, 2: 対話型]: ").strip()
if mode == "1":
demonstrate_tool_agent()
elif mode == "2":
interactive_tool_chat()
else:
print("デフォルトで対話型モードを開始します。")
interactive_tool_chat()

🎨 StreamlitによるWebUI実装

最後に、作成したエージェントを使いやすいWebインターフェースで操作できるようにしましょう:

# ui/streamlit_app.py
import streamlit as st
import sys
import os
from pathlib import Path
from typing import Dict, Any
import json
from datetime import datetime
# プロジェクトルートをパスに追加
project_root = Path(__file__).parent.parent
sys.path.append(str(project_root))
# エージェントのインポート
from agents.basic_chat_agent import BasicChatAgent
from agents.memory_agent import MemoryAgent
from agents.tool_enhanced_agent import ToolEnhancedAgent
from config.settings import get_settings, validate_environment
# 設定の読み込み
settings = get_settings()
class StreamlitChatApp:
"""Streamlit チャットアプリケーション"""
def __init__(self):
self.setup_page_config()
self.initialize_session_state()
def setup_page_config(self):
"""ページ設定"""
st.set_page_config(
page_title="AI Agent Chat Interface",
page_icon="🤖",
layout="wide",
initial_sidebar_state="expanded"
)
# カスタムCSS
st.markdown("""
<style>
.main-header {
font-size: 3rem;
color: #1f77b4;
text-align: center;
margin-bottom: 2rem;
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
}
.agent-info {
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 1rem;
border-radius: 10px;
margin-bottom: 1rem;
}
.chat-message {
padding: 1rem;
border-radius: 10px;
margin: 0.5rem 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.user-message {
background-color: #e3f2fd;
border-left: 4px solid #2196f3;
margin-left: 20px;
}
.ai-message {
background-color: #f3e5f5;
border-left: 4px solid #9c27b0;
margin-right: 20px;
}
</style>
""", unsafe_allow_html=True)
def initialize_session_state(self):
"""セッション状態の初期化"""
if "messages" not in st.session_state:
st.session_state.messages = []
if "agent_type" not in st.session_state:
st.session_state.agent_type = "basic"
if "agent" not in st.session_state:
st.session_state.agent = None
if "session_id" not in st.session_state:
st.session_state.session_id = f"streamlit_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
if "settings_validated" not in st.session_state:
st.session_state.settings_validated = False
def validate_environment_ui(self):
"""環境設定の検証(UI表示付き)"""
if st.session_state.settings_validated:
return True
validation = settings.validate_settings()
if not validation["valid"]:
st.error("⚠️ 環境設定にエラーがあります")
for error in validation["errors"]:
st.error(f"• {error}")
with st.expander("🔧 設定方法"):
st.markdown("""
**環境変数の設定方法:**
1. プロジェクトルートに `.env` ファイルを作成
2. 以下の内容を記載:
            OPENAI_API_KEY=your_openai_api_key_here
SERPAPI_API_KEY=your_serpapi_key_here  # オプション
DEFAULT_MODEL=gpt-3.5-turbo
DEFAULT_TEMPERATURE=0.7
```
**API キーの取得:**
- OpenAI: https://platform.openai.com/api-keys
- SerpAPI: https://serpapi.com/ (無料枠あり)
""")
return False
if validation["warnings"]:
st.warning("⚠️ 設定に関する警告があります")
for warning in validation["warnings"]:
st.warning(f"• {warning}")
st.session_state.settings_validated = True
return True
def render_sidebar(self):
"""サイドバーの描画"""
with st.sidebar:
st.title("🤖 AI Agent 設定")
# エージェントタイプ選択
st.subheader("エージェントタイプ")
agent_type = st.selectbox(
"使用するエージェント",
["basic", "memory", "tool_enhanced"],
format_func=lambda x: {
"basic": "🤖 基本チャット",
"memory": "🧠 メモリ付きチャット", 
"tool_enhanced": "🔧 ツール統合チャット"
}[x],
key="agent_selector"
)
# エージェントタイプが変更された場合
if agent_type != st.session_state.agent_type:
st.session_state.agent_type = agent_type
st.session_state.agent = None  # エージェントをリセット
st.rerun()
# モデル設定
st.subheader("モデル設定")
model_name = st.selectbox(
"使用モデル",
["gpt-3.5-turbo", "gpt-4", "gpt-4-turbo-preview"],
index=0
)
temperature = st.slider(
"Temperature(創造性)",
min_value=0.0,
max_value=1.0,
value=0.7,
step=0.1
)
# メモリ設定(メモリエージェント用)
if agent_type in ["memory", "tool_enhanced"]:
st.subheader("メモリ設定")
memory_window = st.slider(
"メモリウィンドウ",
min_value=5,
max_value=50,
value=10,
help="保持する会話数"
)
else:
memory_window = 10
# セッション管理
st.subheader("セッション管理")
st.text(f"セッションID: {st.session_state.session_id}")
if st.button("🧹 チャット履歴をクリア"):
st.session_state.messages = []
if st.session_state.agent and hasattr(st.session_state.agent, 'clear_memory'):
st.session_state.agent.clear_memory()
st.rerun()
# ツール情報(ツール統合エージェント用)
if agent_type == "tool_enhanced" and st.session_state.agent:
st.subheader("🔧 利用可能なツール")
tools = st.session_state.agent.get_available_tools()
for tool in tools:
with st.expander(f"📋 {tool['name']}"):
st.write(tool['description'])
return {
"agent_type": agent_type,
"model_name": model_name,
"temperature": temperature,
"memory_window": memory_window
}
def create_agent(self, config: Dict[str, Any]):
"""設定に基づいてエージェントを作成"""
if st.session_state.agent is None:
try:
if config["agent_type"] == "basic":
st.session_state.agent = BasicChatAgent(
model_name=config["model_name"],
temperature=config["temperature"]
)
elif config["agent_type"] == "memory":
st.session_state.agent = MemoryAgent(
model_name=config["model_name"],
memory_window=config["memory_window"]
)
elif config["agent_type"] == "tool_enhanced":
st.session_state.agent = ToolEnhancedAgent(
model_name=config["model_name"],
memory_window=config["memory_window"]
)
st.success(f"✅ {config['agent_type']} エージェントを初期化しました")
except Exception as e:
st.error(f"❌ エージェント初期化エラー: {str(e)}")
return False
return True
def render_chat_interface(self, config: Dict[str, Any]):
"""チャットインターフェースの描画"""
st.markdown('<h1 class="main-header">🤖 AI Agent Chat Interface</h1>', 
unsafe_allow_html=True)
# エージェント情報表示
agent_info = {
"basic": "🤖 基本的な質問応答機能",
"memory": "🧠 会話履歴を記憶して継続的な対話が可能",
"tool_enhanced": "🔧 計算とWeb検索ツールを使用可能"
}
st.markdown(f'''
<div class="agent-info">
<h3>現在のエージェント</h3>
<p>{agent_info[config["agent_type"]]}</p>
</div>
''', unsafe_allow_html=True)
# チャット履歴表示
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.write(message["content"])
# チャット入力
if prompt := st.chat_input("メッセージを入力してください..."):
# ユーザーメッセージを追加
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.write(prompt)
# AI応答を生成
with st.chat_message("assistant"):
with st.spinner("考え中..."):
try:
if config["agent_type"] == "basic":
response = st.session_state.agent.chat(prompt)
elif config["agent_type"] == "memory":
response = st.session_state.agent.chat_with_memory(
prompt, 
st.session_state.session_id
)
elif config["agent_type"] == "tool_enhanced":
response = st.session_state.agent.chat_with_tools(prompt)
st.write(response)
# レスポンスを履歴に追加
st.session_state.messages.append({
"role": "assistant", 
"content": response
})
except Exception as e:
error_msg = f"エラーが発生しました: {str(e)}"
st.error(error_msg)
st.session_state.messages.append({
"role": "assistant",
"content": error_msg
})
def render_sample_prompts(self):
"""サンプルプロンプトの表示"""
st.subheader("💡 サンプルプロンプト")
sample_prompts = {
"basic": [
"Pythonの特徴について教えて",
"機械学習とは何ですか?",
"LangChainについて説明して"
],
"memory": [
"私の名前はタロウです。覚えておいて",
"私の名前を覚えてる?",
"今までの会話を要約して"
],
"tool_enhanced": [
"sin(30) + cos(60) を計算して",
"2024年のPython最新トレンドを調べて",
"円周率の10乗を計算してください"
]
}
current_samples = sample_prompts.get(st.session_state.agent_type, [])
cols = st.columns(len(current_samples))
for i, (col, prompt) in enumerate(zip(cols, current_samples)):
with col:
if st.button(f"💬 {prompt}", key=f"sample_{i}"):
# サンプルプロンプトをチャット入力に追加
st.session_state.messages.append({"role": "user", "content": prompt})
st.rerun()
def run(self):
"""アプリケーションの実行"""
# 環境設定の検証
if not self.validate_environment_ui():
st.stop()
# サイドバー設定
config = self.render_sidebar()
# エージェントの初期化
if not self.create_agent(config):
st.stop()
# メインのチャットインターフェース
self.render_chat_interface(config)
# サンプルプロンプト
self.render_sample_prompts()
# フッター
st.markdown("---")
st.markdown("**AI Agent Chat Interface** - Built with Streamlit & LangChain")

def main():
“””メイン関数”””
app = StreamlitChatApp()
app.run()

if name == “main“:
main()


## 🚀 実行とテスト
### 📋 プロジェクト実行の準備
**1. 必要なファイルの作成**
まず、requirements.txtを作成します:
```bash
# requirements.txt
langchain>=0.1.0
langchain-openai>=0.0.5
langchain-community>=0.0.20
streamlit>=1.30.0
python-dotenv>=1.0.0
requests>=2.31.0
beautifulsoup4>=4.12.0
pydantic>=2.0.0

次に、.envファイルを作成(実際のAPIキーを設定):

# .env
OPENAI_API_KEY=sk-your-actual-openai-api-key-here
SERPAPI_API_KEY=your-serpapi-key      # オプション
DEFAULT_MODEL=gpt-3.5-turbo
DEFAULT_TEMPERATURE=0.7

2. インストールと実行

# 依存関係のインストール
pip install -r requirements.txt
# 各エージェントのテスト
python agents/basic_chat_agent.py
python agents/memory_agent.py
python agents/tool_enhanced_agent.py
# ツール単体テスト
python tools/calculator.py
python tools/web_search.py
# WebUIの起動
streamlit run ui/streamlit_app.py

🎯 実際の使用例

ツール統合エージェント:

あなた: 2の10乗を計算してください
AI: 計算ツールを使用して2の10乗を求めます。
> Entering new AgentExecutor chain...
I need to calculate 2 to the power of 10.
Action: calculator
Action Input: 2 ** 10
Observation: 1024
Thought: I now know the final answer.
Final Answer: 2の10乗は1024です。
あなた: ChatGPTの最新情報を調べて
AI: Web検索ツールを使用してChatGPTの最新情報を調べます。
> Entering new AgentExecutor chain...
I need to search for the latest information about ChatGPT.
Action: web_search
Action Input: ChatGPT 最新情報 2024
Observation: 「ChatGPT 最新情報 2024」の検索結果 (総件数: 約2,340,000件):
🔗 **1. ChatGPT最新アップデート - 2024年3月版**
URL: https://example.com/chatgpt-update-2024
📄 概要: 2024年3月にリリースされたChatGPTの新機能について...
Thought: I have found recent information about ChatGPT updates.
Final Answer: ChatGPTの最新情報をお調べしました。2024年3月に大きなアップデートがあり、新機能として...

🌐 WebUIでの操作

StreamlitアプリケーションをWebブラウザで開くと:

  1. エージェント選択: サイドバーで3種類のエージェントから選択
  2. モデル設定: GPTモデルとTemperatureの調整
  3. チャットインターフェース: 直感的な対話画面
  4. ツール情報: 利用可能なツールの詳細表示
  5. サンプルプロンプト: ワンクリックでテスト可能

📈 次のステップと応用

🔮 さらなる機能拡張

1. カスタムツールの追加

# tools/custom_tools.py
class WeatherTool(BaseTool):
name = "weather"
description = "天気情報を取得"
# 実装...
class EmailTool(BaseTool):
name = "email_sender"
description = "メール送信"
# 実装...

2. より高度なメモリ

# 長期記憶、ベクターサーチなど
from langchain.memory import ConversationSummaryBufferMemory
from langchain.vectorstores import Chroma

3. マルチモーダル対応

# 画像、音声の処理
from langchain.schema import HumanMessage, AIMessage
from langchain.schema.messages import ImageMessage

🏢 実用的なビジネス応用

1. カスタマーサポート

  • FAQ検索機能
  • チケット管理連携
  • エスカレーション判定

2. データ分析アシスタント

  • データベース連携
  • グラフ生成
  • レポート自動作成

3. コンテンツ生成

  • SEO最適化
  • 多言語対応
  • ブランドガイドライン準拠

🎯 第3部のまとめ

📚 この記事で学んだこと

ツール開発面:

  • ✅ 安全な計算ツールの実装
  • ✅ Web検索APIの統合
  • ✅ セキュリティを考慮したコード実行

エージェント統合面:

  • ✅ OpenAI Function Callingの活用
  • ✅ 複数ツールの動的選択
  • ✅ エラーハンドリングと回復機能

WebUI開発面:

  • ✅ Streamlitによる本格的なWebアプリ開発
  • ✅ リアルタイム対話インターフェース
  • ✅ プロダクションレディな機能実装

システム設計面:

  • ✅ スケーラブルなアーキテクチャ
  • ✅ 設定管理とデプロイ対応
  • ✅ ユーザビリティとパフォーマンス最適化

🚀 シリーズ全体の成果物

AI Agent開発シリーズを通じて、以下が完成しました:

  1. 基本エージェント: 質問応答機能
  2. メモリエージェント: 会話履歴記憶機能
  3. ツール統合エージェント: 計算・検索機能
  4. WebUIシステム: プロフェッショナルなインターフェース
  5. データベース管理: 効率的なデータ永続化
  6. 設定管理システム: 環境変数の安全な管理

💡 重要なポイント

  1. 段階的な構築: 基礎→応用→実践の体系的アプローチ
  2. 実用性重視: 企業でも使える本格的な実装
  3. 拡張可能性: カスタマイズと機能追加の容易さ
  4. セキュリティ考慮: 安全性を重視した設計

このAI Agentシステムは、単なる学習教材ではなく、実際のビジネスで活用できる本格的なツールです。


🔗 関連記事・今後の学習

📖 AI Agent開発シリーズ(完結)

🎓 次のステップとなる学習リソース

高度な開発技術

基礎スキル強化

Web3・ブロックチェーン応用

💡 AI Agentの世界はまだ始まったばかりです。このシステムを基盤に、さらに高度な自動化と効率化を実現していきましょう!

基本的な実装をマスターしたら、次は業務自動化やマルチモーダル対応など、より高度な機能に挑戦してみてください!

コメントする