✨
メモリ機能付きのエージェントを頑張って作ってみた🧠
LangChainで会話エージェントに記憶を持たせてみた話
最近「AIエージェントとチャットでお話ししたい!」という衝動に駆られて、LangChainを使ってチャットボットを作ってみたい道半ば👒。
今回頑張ったのが、「メモリ機能の実装」。
今回は、LangChainを使ってエージェントに「会話の記憶」を持たせる方法と、
その効果を確認したテスト結果をご紹介します。
やりたいこと
- エージェント実行のインスタンスに** 記憶機能(Memory) **を持たせる
- 会話の履歴をプロンプトに反映させて、より文脈の通った返答を実現したい
実装概要
ConversationBufferMemory
を使う
LangChainのfrom langchain.memory import ConversationBufferMemory
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
memory = ConversationBufferMemory(return_messages=True)
prompt = ChatPromptTemplate.from_messages(
[
("system", f"{system_message}"),
MessagesPlaceholder(variable_name="history"), # 会話履歴をプロンプトに挿入
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
]
)
このようにして、プロンプトテンプレート内にhistory
を挿入することで、過去の会話内容が次の出力に影響を与えるようになります。
テスト実験
実験内容
以下のプロンプトを順番に実行し、記憶なしと記憶ありで返答がどう変わるかを比較しました。
入力シーケンス:
- 問1:1 + 2 =
- 問2:2 + 3 =
- 問1と問2の答えは?また、それらを足し算するといくつ?
- Pythonについて、最新のバージョンを教えて
- Pythonを使って、問1と問2を計算するスクリプトを書いて
使用したTOOLS
今回はシンプルに、、、
from langchain_community.tools import DuckDuckGoSearchRun
from langchain_core.tools import tool
# === カスタムツール定義 ===
# 足し算をするカスタムツール
@tool
def add_numbers(inputs: str) -> int:
"""
2つの整数を「カンマ区切り」の文字列で受け取り、合計を返す。
例: "3,5"
"""
a, b = [int(x) for x in inputs.split(",")]
return a + b
# web検索をしてくれるツール(langchain公式から拝借)
@tool
def duckduckgo_search(inputs: str) -> str:
"""DuckDuckGoSearchRunを使用して、web検索をします。"""
search = DuckDuckGoSearchRun()
return search.invoke(inputs)
# === 使用ツール一覧取得 ===
def setput_all_tools():
# エージェントに登録するツールを管理する。
return [
add_numbers,
duckduckgo_search,
]
用意したエージェント
# メモリ機能ありのエージェント
def create_memory_agent(
api_key: str,
model: str = deault_model,
system_message="You are a helpful assistant"
):
llm = ChatOpenAI(
model=model,
openai_api_key="<api_key>",
)
tools = setput_all_tools()
# メモリー作成
memory = ConversationBufferMemory(memory_key="history", return_messages=True)
# メモリー対応のプロンプトテンプレート
prompt = ChatPromptTemplate.from_messages(
[
("system", f"{system_message}"),
MessagesPlaceholder(variable_name="history"), # プロンプトに過去の履歴を追加
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
]
)
agent = create_tool_calling_agent(
llm=llm,
tools=tools,
prompt=prompt
)
return agent, memory
# メモリ機能搭載AIエージェントの実行
def create_memory_agent_executor(agent, memory):
tools = setput_all_tools()
return AgentExecutor(agent=agent, tools=tools, memory=memory, verbose=True)
結果比較
✅ メモリなしの場合:
問1:1 + 2 = → 「1 + 2 = 3 です。」
問2:2 + 3 = → 「2 + 3 = 5 です。」
問1と問2の答えは? → 「問1と問2の内容を教えていただけますか?」
→ 文脈を理解しておらず、会話が繋がっていない。(そりゃそう、、、💦)
✅ メモリありの場合:
問1:1 + 2 = → 「1 + 2 = 3 です。」
問2:2 + 3 = → 「2 + 3 = 5 です。」
問1と問2の答えは? → 「問1の答えは3で、問2の答えは5です。これらを足すと8になります。」
Pythonでスクリプト → 以下のスクリプトを出力。
# 問1と問2の計算
answer_1 = 1 + 2
answer_2 = 2 + 3
# 答えを表示
print("問1の答え:", answer_1)
print("問2の答え:", answer_2)
# 答えを足し算
total = answer_1 + answer_2
print("問1と問2の合計:", total)
→ 過去のやり取りを覚えており、会話がスムーズかつ一貫性のあるものに。
まとめ
- LangChainの
ConversationBufferMemory
を使うことで、簡単に「記憶を持つエージェント」が実装できる - 実際にテストしてみると、メモリがあるかどうかで会話の質が大きく変わる
- 一歩進んだチャットボットの開発には、メモリ実装が必須!
おわりに
これからチャットボットを作ろうと思っている方や、LangChainを試してみたい方の参考になれば幸いです。
今後は、記憶の形式を「要約ベース」にしたり、RAG(Retrieval-Augmented Generation)と組み合わせたりすることも試してみたいです!
最後まで読んでいただきありがとうございました。
Discussion