🧠 OpenAI APIの弱点...。 え、これって Memory機能ないの!!???
👤「昨日の話の続き、覚えてる?」
🤖「申し訳ありませんが、過去の会話は覚えていません。」
👤「チョットマッテ…(゜Д゜)」
これ、もどかしくないですか!?
💡 そこで! LangMem を使って「記憶するAI」を作っちゃいます!
💡 今回は、細かいロジックは一旦置いておいて、まずは動かすところにフォーカスして説明します!
📌 この記事で学べること
- LangMem を使った 記憶するAI(API)の実装方法
- 爆速で開発 するためのシンプルなコード
- さらに進化 させるためのアイデア
最初に考えたこと、なんか解決方法ないかな...。
最近、LangChainからヤバげなリリースがあったことを思い出しました....。
本日、エージェントが長期記憶を通じて学習し、改善するのに役立つライブラリであるLangMem SDKをリリースします。
会話から情報を抽出し、迅速な更新を通じてエージェントの動作を最適化し、行動、事実、イベントに関する長期記憶を維持するためのツールを提供します。
ということで、リリースしたてのLangMemを使っていこうと思いますー!!(え...。ドキュメント全部英語?、日本語のチュートリアルあんまない???💦
1️⃣ まずは環境構築
📌 こんな感じのディレクトリ構成で、ファイルだけ作ってしまってください!
.
├── backend
│ ├── Dockerfile
│ └── api
│ ├── main.py
│ └── routers
│ └── chat.py
└── docker-compose.yaml
📌 Dockerfile
とdocker-compose.yaml
をこんな感じで設定してください!
FROM python:3.11-slim
ENV PYTHONUNBUFFERED=1
ENV OPENAI_API_KEY="your_api_key"
WORKDIR /src
COPY api ./
RUN pip install --upgrade pip
RUN pip install fastapi
RUN pip install "uvicorn[standard]"
RUN pip install requests
RUN pip install langchain
RUN pip install langchain_openai
RUN pip install langgraph
RUN pip install -U langmem
RUN pip install numpy
RUN pip install langchain_community
ENTRYPOINT ["uvicorn", "main:app", "--host", "0.0.0.0", "--reload"]
docker-compose.yaml
version: '3'
services:
demo-api:
build:
context: "./backend"
dockerfile: "Dockerfile"
ports:
- "8000:8000"
volumes:
- "./backend:/src"
environment:
- PYTHONPATH=/src
working_dir: /src
entrypoint: ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
2️⃣ 実際のコード
📌 FastAPIのエントリーポイントを作る。
main.py
from fastapi import FastAPI
from api.routers import chat
app = FastAPI(
title="BonBon AI",
description="LangMEM を用いた自己意識型AI",
version="1.0.0"
)
@app.get("/")
async def root():
return {"message": "BonBon AI is running!"}
app.include_router(chat.router, prefix="/chat", tags=["Chat"])
# FastAPIアプリの起動(デバッグ用)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
📌 LangMemを用いてRooterを記述する
chat.py
from typing import Any, Dict, List
from langgraph.store.memory import InMemoryStore
from openai import OpenAI
from langmem import create_manage_memory_tool, create_search_memory_tool
from pydantic import BaseModel
from fastapi import APIRouter
router = APIRouter()
store = InMemoryStore(
index={
"dims": 1536,
"embed": "openai:text-embedding-3-small",
}
)
class ChatRequest(BaseModel):
user_id: str
user_message: str
@router.post("/")
def chat(request: ChatRequest):
user_id = request.user_id
user_message = request.user_message
memory_tools = [
create_manage_memory_tool(namespace=("memories",user_id), store=store),
create_search_memory_tool(namespace=("memories",user_id), store=store),
]
result = run_agent(
tools=memory_tools,
user_input=user_message,
)
user_memories = store.search(("memories",user_id))
serialized_memories = [memory.dict() for memory in user_memories]
response = {
"message": result,
"memories": serialized_memories
}
return response
def execute_tool(tools_by_name: Dict[str, Any], tool_call: Dict[str, Any]) -> str:
"""Execute a tool call and return the result"""
tool_name = tool_call["function"]["name"]
if tool_name not in tools_by_name:
return f"Error: Tool {tool_name} not found"
tool = tools_by_name[tool_name]
try:
result = tool.invoke(tool_call["function"]["arguments"])
return str(result)
except Exception as e:
return f"Error executing {tool_name}: {str(e)}"
def run_agent(tools: List[Any], user_input: str, max_steps: int = 5) -> str:
"""Run a simple agent loop that can use tools"""
client = OpenAI()
tools_by_name = {tool.name: tool for tool in tools}
openai_tools = [
{
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"parameters": tool.tool_call_schema.model_json_schema(),
},
}
for tool in tools
]
messages = [{"role": "user", "content": user_input}]
for step in range(max_steps):
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=openai_tools if step < max_steps - 1 else [],
tool_choice="auto",
)
message = response.choices[0].message
tool_calls = message.tool_calls
if not tool_calls:
return message.content
messages.append(
{"role": "assistant", "content": message.content, "tool_calls": tool_calls}
)
for tool_call in tool_calls:
tool_result = execute_tool(tools_by_name, tool_call.model_dump())
messages.append(
{
"role": "tool",
"tool_call_id": tool_call.id,
"content": tool_result,
}
)
return "Reached maximum number of steps"
3️⃣ サーバー実行
docker-compose up --build
4️⃣ API実行!!
📌 エンドポイントはここです!
POST http://127.0.0.1:8000/chat
あなたのAgentも、記憶を持てるようになりましたか...?
これで最小構成は完成です!!!🎉
🚀 もっと進化させるには~~今後やりたいこと。
- InMemoryStore を PostgreSQL に置き換えて、再起動時にデータを保持できるようにする。
- 記憶と一緒に感情パラメータを保存させ、記憶想起のweightとして利用すると、より人間らしいリアクションをできるようになるかも。
- 日本語の情報あんまりないから、作りながらいっぱい記事のネタにしたいと目論んでます😂。
⚙️ Git