🚀[5分でできるRAG入門] 「記憶するAI」爆誕！ OpenAIの"忘れっぽい問題"を LangMem で解決する！[爆速テンプレート]

### 🧠 OpenAI APIの弱点...。　え、これって Memory機能ないの！！？？？
👤「昨日の話の続き、覚えてる？」
🤖「申し訳ありませんが、過去の会話は覚えていません。」
👤「ﾁｮｯﾄﾏｯﾃ…(゜Д゜)」

これ、もどかしくないですか！？
💡 そこで！ LangMem を使って「記憶するAI」を作っちゃいます！
💡 今回は、細かいロジックは一旦置いておいて、まずは動かすところにフォーカスして説明します！

## 📌 この記事で学べること
- LangMem を使った 記憶するAI(API)の実装方法
- 爆速で開発 するためのシンプルなコード
- さらに進化 させるためのアイデア

## 最初に考えたこと、なんか解決方法ないかな...。
最近、LangChainからヤバげなリリースがあったことを思い出しました....。

> 本日、エージェントが長期記憶を通じて学習し、改善するのに役立つライブラリであるLangMem SDKをリリースします。
> 会話から情報を抽出し、迅速な更新を通じてエージェントの動作を最適化し、行動、事実、イベントに関する長期記憶を維持するためのツールを提供します。

https://blog.langchain.dev/langmem-sdk-launch/

ということで、リリースしたてのLangMemを使っていこうと思いますー！！（え...。ドキュメント全部英語？、日本語のチュートリアルあんまない？？？💦

## 1️⃣ まずは環境構築
📌 こんな感じのディレクトリ構成で、ファイルだけ作ってしまってください！
```
.
├── backend
│   ├── Dockerfile
│   └── api
│       ├── main.py
│       └── routers
│           └── chat.py
└── docker-compose.yaml
```

📌 `Dockerfile`と`docker-compose.yaml`をこんな感じで設定してください！
```Dockerfile
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
```

✅　まずは、記憶がないことを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3722068/1fa100eb-e7f1-48f6-8cd1-0a30f11b81e1.png)

✅　次に名前を教えてあげる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3722068/d9d14e67-9a80-4c74-a706-4d7b5f206598.png)

✅　もう一回質問！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3722068/d9367536-558c-4f98-b2fd-27f5f5943d39.png)

### あなたのAgentも、記憶を持てるようになりましたか...？
### これで最小構成は完成です！！！🎉

# 🚀　もっと進化させるには~~今後やりたいこと。
- InMemoryStore を PostgreSQL に置き換えて、再起動時にデータを保持できるようにする。
- 記憶と一緒に感情パラメータを保存させ、記憶想起のweightとして利用すると、より人間らしいリアクションをできるようになるかも。
- 日本語の情報あんまりないから、作りながらいっぱい記事のネタにしたいと目論んでます😂。

# ⚙️ Git
https://github.com/dev-cat-v/langmem-api

#### 本当に励みになるので...。
### ⭐️ この記事が役に立ったら「いいね」＆「ブクマ」よろしくお願いします！