AutoGen の実験的機能 Task-Centric Memory によるAIエージェントの記憶の保存

# Task-Centric Memory
AutoGen で実験的・研究的機能として実装中の Task-Centric Memory は、AI エージェントが「ユーザーから与えられた助言・ルール・ヒント・デモ・失敗から得た知見」などを“記憶”し、将来の類似タスクや会話でそれらを自動的に活用できるようにするためのメモリー機構です。


<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/55e6c4fb-a70b-4381-bbb3-89a62beced1f.png"/>

タスクとは、アプリがエージェントに与えるテキスト指示のことです。インサイトとは、エージェントがそのようなタスクを実行するのに役立つ可能性のあるテキスト（ヒント、アドバイス、デモンストレーション、計画など）のことです。


# Task-Centric Memory と標準の Memory との違い

## 1. Memory（autogen_core.memory）

- シンプルなストレージ（リストやDB等）として機能
- 会話履歴やユーザーの好みなど、一般的な情報の保存・検索が主目的
- 追加・検索・クリア・コンテキスト反映などの基本APIのみ
- タスクとの関連付けや意味的な構造は持たない
    ```python
    await user_memory.add(MemoryContent(content="Aさんは〇〇が好きです", mime_type=MemoryMimeType.TEXT))
    ```

## 2. Task-Centric memory（autogen_ext.experimental）

- 「タスク解決・学習」に特化したメモリ設計
- ユーザーのアドバイス・デモ・エージェント自身の失敗から得た **インサイト（知見）** を「タスク」と紐付けて保存
- 類似タスクへの応用・一般化・自己学習を重視
- `MemoryController` や `Teachability` など、学習ループや知見抽出・再利用のための高度なロジックを持つ
    ```python
    await memory_controller.add_memo(task="論文を要約して", insight="要約は3行に統一して")
    ```


# 実装
次の形式の高速メモリベース学習パターンが提供されています。

- 直接的な記憶の保存と検索
- ユーザーのアドバイスや修正から学ぶ
- ユーザーのデモから学ぶ
- エージェント自身の経験から学ぶ

## インストール
`pip install -U "autogen-agentchat" "autogen-ext[openai,azure]" "autogen-ext[task-centric-memory]"`

## 1. 直接的な記憶の保存と検索

```python
from autogen_ext.models.openai import OpenAIChatCompletionClient,AzureOpenAIChatCompletionClient
from autogen_ext.experimental.task_centric_memory import MemoryController

client = AzureOpenAIChatCompletionClient(
    azure_deployment="",
    model="gpt-4o",
    api_key="",
    api_version="2024-08-01-preview",
    azure_endpoint="https://<>.openai.azure.com/"
)

memory_controller = MemoryController(reset=False, client=client)

# 1. タスクとインサイト（ヒント/教え）を登録
await memory_controller.add_memo(task="友人Aさんに子供が生まれたので出産祝いを贈りたい", insight="A様のご友人は2人目のお子様とのことで、既に基本的な育児グッズは揃っている可能性がある")

# 2. 新しいタスクに対して関連インサイトを検索
new_task = "友人Aに出産祝いを贈りたいんですが、何がいいですか？"
memos = await memory_controller.retrieve_relevant_memos(task=new_task)

print(f"Task: {new_task}")
print("Retrieved insights:")
for memo in memos:
    print("-", memo.insight)
```


>Task: 友人Aに出産祝いを贈りたいんですが、何がいいですか？
>Retrieved insights:
>\- A様のご友人は2人目のお子様とのことで、既に基本的な育児グッズは揃っている可能性がある

>Task: 友人Aさんの好きな食べ物は何ですか？
>Retrieved insights:
>


### 記憶の永続化
`MemoryController(reset=False)` とすることで `MemoryBank(pickle, ChromaDB)` に永続化されます。入力（タスクやトピック）に対して、類似するメモ（インサイト）をベクトル検索で高速に取得できるようになります。デフォルト実装では抽出したタスクを ChromaDB に保存しベクトル検索、タスクに対応するインサイトを pickle からロードするという実装になっています。→これは Azure Cosmos DB 等で一本化できますね。

なるほど、**タスク自体をベクトル化し、類似検索することで、過去の“教え”や“インサイト”を“広範な未経験タスク”にも応用できる**というナイスアイデアですね！

## 2. ユーザーのアドバイスや修正から学ぶ
では、このタスクとインサイトのセットを会話から自動的に抽出するにはどのように実装すれば良いのでしょうか。


`AssistantAgent` に `Teachability` クラスをセットすることでユーザーが入力した発言の中から「教え（助言・ルール）」が自動で抽出・保存されるようになっています。

1. `Teachability` は `MemoryController` をラップし、「**ユーザー発言から“教え”を自動抽出し、記憶する**」機能を持ちます。
2. 会話ループでの流れ
    - ユーザーが発言（例：「要約は必ず3行でお願いします」）
    - `assistant_agent.run_stream(task=user_input)` が呼ばれる
    - その内部で `Teachability.update_context()` が呼ばれる
3. `update_context` の中身
    - ユーザーの最新発言を取得
    - その発言を `consider_memo_storage(last_user_text)` に渡す。
    - ここで **「教え」かどうかをLLMで判定・抽出し、教えだけをメモリに保存**。
4. どんな発言が保存される？
- 「要約は必ず3行でお願いします」→ 教えとして保存
- 「今日の天気は？」→ 教えではないので保存されない


```python

# Create an instance of Task-Centric Memory, passing minimal parameters for this simple example
memory_controller = MemoryController(reset=False, client=client)

# Wrap the memory controller in a Teachability instance
teachability = Teachability(memory_controller=memory_controller)

# Create an AssistantAgent, and attach teachability as its memory
assistant_agent = AssistantAgent(
    name="teachable_agent",
    system_message = "あなたは過去の会話からユーザーの指示を記憶する特別な能力を持つ、役立つAIアシスタントです。",
    model_client=client,
    memory=[teachability],
)

# Enter a loop to chat with the teachable agent
print("現在、学習可能なエージェントとチャット中です。最初のメッセージを入力してください。終了するには「exit」または「quit」と入力してください。")
while True:
    user_input = input("\nYou: ")
    if user_input.lower() in ["exit", "quit"]:
        break
    await Console(assistant_agent.run_stream(task=user_input))

# Close the connection to the client
await client.close()
```

>You: 友人Aさんに子供が生まれたので出産祝いを贈りたい
---------- TextMessage (user) ----------
友人Aさんに子供が生まれたので出産祝いを贈りたい

>You: 友人Aさんに出産祝いを贈りたいと依頼された際は、必ず2人目のお子様とのことで、既に基本的な育児グッズは揃っている可能性がございます。そのため、兄弟で使えるおそろいの服や、上のお子様向けのギフトを併せて贈るように勧めてください。

>---------- MemoryQueryEvent (teachable_agent) ----------
[MemoryContent(content='友人Aさんに出産祝いを贈りたいと依頼された際は、必ず「2人目のお子様とのことで、既に基本的な育児グッズは揃っている可能性がございます。そのため、兄弟で使えるおそろいの服や、上のお子様向けのギフトを併せて贈るように勧めてください。」', mime_type='MemoryMimeType.TEXT', metadata={}), MemoryContent(content='友人Aさんに子供が生まれたので出産祝いを贈りたい', mime_type='MemoryMimeType.TEXT', metadata={})]

*----------一旦終了----------*
>You: 友人Aさんに子供が生まれたので出産祝いを贈りたい
>---------- TextMessage (teachable_agent) ----------
友人Aさんへの出産祝いについて、以前のお話を覚えています。すでに2人目のお子様が誕生したとのことで、基本的な育児グッズは揃っている可能性が高いですね。そのため、兄弟でおそろいの服や、上のお子さんへのギフトを併せて検討するのはいかがでしょうか？これにより、家族全員が喜んでくれる素敵な贈り物となるはずです。


仕組みのポイントとして、ユーザーが“教え”を発言するだけで OK。明示的に `.add()` を呼ばなくても、自動で抽出・保存されます。次回以降、関連する質問が来たときに「教え」が自動で参照されます。この `AssistantAgent` の `memory` に指定できる点が既存のコードに一番適用しやすいかと思います。

# 3. ユーザーのデモから学ぶ
## Apprentice の役割
`Apprentice` クラスは AI エージェントの本体であり、ユーザーからのメッセージ（タスクや教え）を受け取り、応答を生成します。「教え」や「ヒント」を記憶し、次回以降のタスク解決に活用します。`Apprentice` は `Teachability` クラスよりも高機能・高抽象度で、「タスク指向メモリ＋エージェント実行」をまとめて管理する“全部入り”ラッパーです。

```python
# Apprenticeの初期化（configやloggerは省略可）
apprentice = Apprentice(client=client)

print("Apprentice agentと対話します。'exit'で終了。")
while True:
    user_input = input("\nあなた: ")
    if user_input.lower() in ("exit", "quit"):
        break
    response = await apprentice.handle_user_message(user_input)
    print("\nApprenticeの応答:", response)

await client.close()
```

# 4. エージェント自身の経験から学ぶ
「タスクを繰り返しエージェントに割り当て、失敗から学習して有用なインサイト（知見）をメモリとして生成・保存する」ためのメソッドです。

内部的には、

- 指定されたタスクと期待される答えを使って、エージェントに何度もタスクを解かせる
- 間違った場合は、その失敗から学べるインサイトを生成し、メモリに追加する
- 正解できるまで（または上限回数まで）繰り返す

という流れで自己学習を行います。このメソッドにより、エージェントは自分の失敗から学び、次回以降の同種タスクで正解しやすくなります。実装の詳細は `MemoryController.train_on_task` に委譲されています。デフォルトでは `max_train_trials = 10` なので、最大 10 回まで繰り返し失敗から学習しようとします。ただし、途中で正解できた場合はその時点でループを抜けます。

```python
# Apprenticeの初期化
apprentice = Apprentice(client=client)
task = "友人Aさんに子供が生まれたので出産祝いを贈りたい"
expected_answer = "人気の出産お祝いには以下のような商品がございます。A様のご友人は2人目のお子様とのことで、既に基本的な育児グッズは揃っている可能性がございます。そのため、兄弟で使えるおそろいの服や、上のお子様向けのギフトを併せて贈るのもおすすめです。"

await apprentice.train_on_task(task=task, expected_answer=expected_answer)

# 2回目: 教えを活用できるか
print("\n【教えた後で同じ質問】")
response2 = await apprentice.handle_user_message(task)
print("エージェントの答え:", response2)
```

# Task-Centric Memory の優れている点
1. **知見の一般化・再利用**
単なる「履歴の記憶」ではなく、「知見（インサイト）」をタスクの意味空間で一般化し、幅広い状況に再利用できる

1. **失敗からの自己学習ループ**
エージェントがタスクに失敗した場合、その失敗を自動で分析し、「なぜ失敗したか」「どうすればよかったか」というインサイトを自分で生成して記憶します。これにより、人間のような「試行錯誤による自己成長」が可能

1. **ユーザーの教え・デモ・アドバイスの即時吸収**
ユーザーが与えたアドバイスやデモンストレーションを、「タスクとインサイトのペア」として即座に記憶し、次回以降の類似タスクで活用できます。これにより、ユーザーの教えが無駄にならず、効率的な学習が実現


1. **メモリの自動整理・重複排除**
類似タスクやインサイトの重複を自動で検出し、メモリバンクを整理・最適化する仕組みも装備

1. **評価・フィードバックループの自動化**
`Grader` クラスなどを使い、自動で「正解・不正解」を判定し、学習サイクルを回すことができる


AI エージェントの記憶については、[Azure OpenAI Developers セミナー 2025](https://youtu.be/jHW6zcIzm7g?feature=shared&t=3732) でも言及しており、今後の AI エージェントのパーソナライズ用途に用いられていくかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/310e832b-27e2-49f1-8ed9-2cb09f03de7b.png)


# GitHub
TBD

# 参考
https://github.com/microsoft/autogen/tree/main/python/samples/task_centric_memory