本記事では、前回構築したREST APIサーバーに、LangChainとLangGraphを用いてAIエージェントの処理を実装します。具体的には、以下の3つの処理をLangChainで実装し、LangGraphで繋げることで、一連のタスクとして実行できるようにします。
- 入力ワードをGPT-4o-miniモデルを用いてWEB検索キーワードに変換する。
- WEB検索キーワードを検索する。
- 検索結果を要約する。
1. 必要なライブラリのインストール (追加)
前回インストールしたライブラリに加えて、以下のライブラリをpipでインストールします。
pip install duckduckgo-search langchain-openai
duckduckgo-search: DuckDuckGo検索APIを利用するためのライブラリlangchain-openai: OpenAIのモデルを利用するためのLangChain連携ライブラリ
2. LangChainによる各処理の実装
main.py を修正し、各処理を個別の関数として実装します。
agent.py としてAgentの各処理を実装します。
from traceback import print_exception
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_community.utilities import DuckDuckGoSearchAPIWrapper
from langchain_core.output_parsers import StrOutputParser
class AIAgent:
def __init__(self, llm_model="gpt-4o-mini"): # デフォルトをgpt-4o-miniにする
"""
AIエージェントの初期化。
Args:
llm_model (str): 使用するLLMモデルの名前。
"""
self.llm_model = llm_model
self.llm = self._select_llm()
self.search = DuckDuckGoSearchAPIWrapper()
def _select_llm(self):
"""
LLMを選択する。
"""
if self.llm_model == "gpt-4o-mini":
# OpenAIのモデルを利用
# APIキーの設定が必要です
try:
print(f"OPENAI_API_KEY:{os.getenv('OPENAI_API_KEY')}")
llm = ChatOpenAI(model="gpt-4o", temperature=0.0)
return llm
except KeyError:
raise KeyError("OPENAI_API_KEYが設定されていません。")
else:
raise ValueError(f"サポートされていないLLMモデル: {self.llm_model}")
def create_web_search_keywords(self, input_text: str) -> dict:
"""
入力されたテキストから、WEB検索に適したキーワードを生成する。
Args:
input_text (str): 入力テキスト。
Returns:
str: WEB検索キーワード。
"""
prompt = ChatPromptTemplate.from_template("入力されたテキスト: {input_text} から、WEB検索に適したキーワードを3つ生成してください。")
chain = prompt | self.llm | StrOutputParser()
keywords = chain.invoke({"input_text": input_text})
return {'keywords': keywords}
def web_search(self, keywords: str) -> dict:
"""
WEB検索キーワードを用いて、DuckDuckGoで検索を行う。
Args:
keywords (str): WEB検索キーワード。
Returns:
str: 検索結果。
"""
results = self.search.run(keywords)
return {'search_results': results}
def summarize_results(self, search_results: str) -> str:
"""
検索結果を要約する。
Args:
search_results (str): 検索結果。
Returns:
str: 要約された結果。
"""
prompt = ChatPromptTemplate.from_template("以下の検索結果: {search_results} を要約してください。3文以内で。")
chain = prompt | self.llm | StrOutputParser()
summary = chain.invoke({"search_results": search_results})
return {'summary': summary}
コードの説明:
- ライブラリのインポート: 必要なLangChainのモジュール、DuckDuckGo検索ライブラリなどをインポートします。
create_web_search_keywords関数:- GPT-4o-miniモデルを使用して、検索キーワードを作成します。
ChatPromptTemplateを用いて、入力テキストからLLMに渡すプロンプトを定義します。LLMChainを用いて、LLMとプロンプトを組み合わせたChainを作成し実行します。- LangChainはBaseで
__run__を実装しており、prompt | self.llm | StrOutputParserとすることでchainします。
- LangChainはBaseで
web_search関数:- DuckDuckGo検索APIを用いて、指定されたキーワードでWEB検索を行います。
- 検索結果から、テキスト部分を抽出してリストとして返します。
summarize_results関数:- GPT-4o-miniモデルを使用して、検索結果のテキストを要約します。
3. LangGraphによる処理のグラフ化
LangGraphを使用して、各処理をノードとして定義し、それらを繋げることで、処理のフローを明確に定義します。main.pyを修正します。
from langgraph.graph import StateGraph, END
from typing import TypedDict, Dict, Any
class AgentState(TypedDict):
"""
LangGraphの状態を定義する。
"""
input: str
web_search_keywords: str
search_results: str
summary: str
def define_langraph_workflow(agent: AIAgent):
"""
LangGraphのワークフローを定義する。
Args:
agent (AIAgent): AIエージェントのインスタンス。
Returns:
StateGraph: LangGraphのワークフロー。
"""
builder = StateGraph(AgentState)
builder.add_node("create_web_search_keywords", agent.create_web_search_keywords)
builder.add_node("web_search", agent.web_search)
builder.add_node("summarize_results", agent.summarize_results)
builder.set_entry_point("create_web_search_keywords")
builder.add_edge("create_web_search_keywords", "web_search")
builder.add_edge("web_search", "summarize_results")
builder.add_edge("summarize_results", END)
graph = builder.compile()
return graph
コードの説明:
GraphState: グラフの状態を定義するTypedDictです。ここでは、keysという辞書型のフィールドを持つことを定義しています。- 各ノードの処理関数:
- 各関数は、
GraphState型の引数を受け取り、dict型の戻り値を返します。 GraphStateから必要な情報を取得し、処理を実行します。- 処理結果を
dictに格納して返します。
- 各関数は、
construct_graph関数:StateGraphを使用してグラフを構築します。add_nodeで各ノードを追加します。set_entry_pointで開始ノードを設定します。add_edgeでノード間の接続を定義します。compileでグラフをコンパイルします。
4. FastAPIのエンドポイントから呼び出し
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional
import os
app = FastAPI()
class Query(BaseModel):
text: str
max_length: Optional[int] = 50
@app.post("/generate")
async def generate_text(query: Query):
"""
テキスト生成エンドポイント。
"""
try:
# AIエージェントの初期化
agent = AIAgent(llm_model="gpt-4o-mini")
# LangGraphワークフローの定義
workflow = define_langraph_workflow(agent)
# ワークフローの実行
inputs = {"input": query.text}
result = workflow.invoke(inputs)
# 結果の整形
summary = result['summary'] # 最終結果はsummaryに格納されていると仮定
return {"result": summary} # 要約結果を返す
except Exception as e:
print_exception(e)
raise HTTPException(status_code=500, detail=str(e))
@app.get("/health")
async def health_check():
"""
ヘルスチェックエンドポイント。
"""
return {"status": "ok"}
コードの説明:
/generateエンドポイント:construct_graph関数を呼び出してグラフを構築します。- 入力データを辞書に格納します。
graph.ainvokeを使用してグラフを実行します (非同期実行)。- 結果から要約を取り出して返します。
5. APIのテスト
APIサーバーを再起動し、前回と同様に/generateエンドポイントをテストします。
uvicorn main:app --reload
curl コマンドなどのツールを使用して、POST リクエストを送信します。
curl -X POST -H "Content-Type: application/json" -d '{"text": "AIエージェントとは"}' http://127.0.0.1:8000/generate
APIキーが正しく設定されていれば、AIエージェントが入力テキストに基づいてWEB検索を行い、検索結果を要約したテキストがJSON形式で返されるはずです。
{"result":"AIエージェントは、自律的に環境を認識し、行動し、学習・適応するシステムであり、業務効率化やデータ分析に優れて います。生成AIとは異なり、複数のAIモデルやデバイスを組み合わせて高度なタスクを実行します。企業導入によりコスト削減や業務の自動化が期待されます。"}
まとめ
今回は、LangChainとLangGraphを使用して、AIエージェントの中核となる処理を実装しました。
- 各処理をLangChainで実装し、関数として定義
- LangGraphを用いて、処理のフローをグラフとして定義
- APIエンドポイントからLangGraphを実行
次回は、今回作成したAPIを呼び出すクライアントアプリケーションを作成仕様と思いましたが、、、ソースコードが美しくない気がしますのでリファクタリングしたものを公開します。お楽しみに!

コメント