Serverless Operations, inc

>_cd /blog/id_vty5c3icl

title

Amazon Bedrock AgentCore Memory 実践編:短期記憶と要約による長期記憶を試す

前回の記事ではAmazon Bedrock AgentCoreの紹介、Amazon Bedrock との違いの整理、そして基本機能である Runtimeの起動手順をご紹介しました。

今日は Memory を試していきます。

Amazon Bedrock AgentCore Memory

AIエージェントに対話の文脈を保持させる 短期メモリ(Short-term Memory) と、セッションクリア後も保持される 長期メモリ(Long-term Memory) を簡単に実装できるマネージドサービスです。

Short-term Memory(短期メモリ)と Long-term Memory(長期メモリ)

短期メモリ は、ユーザーとの会話を「イベント」としてそのまま保存し、同じセッションの中で文脈を維持するための仕組みです。ユーザーの直前の質問やエージェントの返答内容を覚えているので、途切れのない自然な対話を実現できます。ただし、セッションが終了すると内容はリセットされます。

長期メモリは、保存されたイベントから ユーザーの好み・会話の要約・事実的な知識を抽出して永続的に保存する仕組みです。セッションをまたいでも情報を利用できるため、ユーザーが以前話した嗜好を覚えて応答したり、長いやり取りを要約して効率的に活用したりできます。記憶は自動的に統合され、必要に応じて抽出方法を調整できます。

会話の流れに応じて長期メモリから必要な情報を取り出すためには戦略と言われるいくつかの抽出方法が用意されています。

UserPreferenceMemoryStrategy:ユーザーが示した 嗜好や好み(例:好きな飲み物、話題の選び方など)を記憶し、次回以降の会話に反映させるための戦略です

SemanticMemoryStrategy:会話の中から 事実的な情報や知識を抽出して長期的に保持し、ユーザー固有の背景知識として活用する戦略です。

SummaryMemoryStrategy:セッション全体のやり取りを 短く要約して保存し、後の会話で効率的に文脈を再構築できるようにする戦略。

これらを組み合わせて必要に応じて過去の会話から情報を抽出しプロンプトに反映させることで個人の好みを覚えつつ、事実を蓄積し、要約で効率化する「賢いエージェント」を開発することができます。

さっそくやってみる

では前回同様こちらの手順を試していきます。

以下のファイルを shortmemory.py という名前で作成し実行します。

from bedrock_agentcore.memory import MemoryClient

client = MemoryClient(region_name="us-west-2")

memory = client.create_memory(
    name="CustomerSupportAgentMemory",
    description="Memory for customer support conversations",
)

# The memory_id will be used in following operations
print(f"Memory ID: {memory.get('id')}")
print(f"Memory: {memory}") 
python shortmemory.py
Memory ID: CustomerSupportAgentMemory-4gCfWiFJ5y
Memory: {'arn': 'arn:aws:bedrock-agentcore:us-west-2:917561075114:memory/CustomerSupportAgentMemory-4gCfWiFJ5y', 'id': 'CustomerSupportAgentMemory-4gCfWiFJ5y', 'name': 'CustomerSupportAgentMemory', 'description': 'Memory for customer support conversations', 'eventExpiryDuration': 90, 'status': 'ACTIVE', 'createdAt': datetime.datetime(2025, 8, 17, 13, 39, 56, 357000, tzinfo=tzlocal()), 'updatedAt': datetime.datetime(2025, 8, 17, 13, 39, 56, 357000, tzinfo=tzlocal()), 'strategies': [], 'memoryId': 'CustomerSupportAgentMemory-4gCfWiFJ5y', 'memoryStrategies': []}

次に今作成された短期メモリに対して会話を書き込みます。

from bedrock_agentcore.memory import MemoryClient

client = MemoryClient(region_name="us-west-2")

for memory in client.list_memories():
    print(f"Memory Arn: {memory.get('arn')}")
    print(f"Memory ID: {memory.get('id')}")
    print("--------------------------------------------------------------------")


client.create_event(
    memory_id=memory.get("id"), # This is the id from create_memory or list_memories
    actor_id="User84",  # This is the identifier of the actor, could be an agent or end-user.
    session_id="OrderSupportSession1", #Unique id for a particular request/conversation.
    messages=[
        ("Hi, I'm having trouble with my order #12345", "USER"),
        ("I'm sorry to hear that. Let me look up your order.", "ASSISTANT"),
        ("lookup_order(order_id='12345')", "TOOL"),
        ("I see your order was shipped 3 days ago. What specific issue are you experiencing?", "ASSISTANT"),
        ("Actually, before that - I also want to change my email address", "USER"),
        (
            "Of course! I can help with both. Let's start with updating your email. What's your new email?",
            "ASSISTANT",
        ),
        ("newemail@example.com", "USER"),
        ("update_customer_email(old='old@example.com', new='newemail@example.com')", "TOOL"),
        ("Email updated successfully! Now, about your order issue?", "ASSISTANT"),
        ("The package arrived damaged", "USER"),
    ],
)
Memory Arn: arn:aws:bedrock-agentcore:us-west-2:917561075114:memory/CustomerSupportAgentMemory-4gCfWiFJ5y
Memory ID: CustomerSupportAgentMemory-4gCfWiFJ5y

次に先ほどの test.py に対して以下を追記して再度実行します。

conversations = client.list_events(
    memory_id=memory.get("id"),
    actor_id="User84",
    session_id="OrderSupportSession1",
    max_results=5,
)

for ev in conversations:
    print("------------------------------------------------")
    print(f"Event ID: {ev['eventId']}")
    print(f"Timestamp: {ev['eventTimestamp']}")

    for p in ev.get("payload", []):
        conv = p.get("conversational", {})
        role = conv.get("role")
        text = conv.get("content", {}).get("text")
        print(f"{role}: {text}")

Event ID毎に会話が記憶されている。

------------------------------------------------
Event ID: 0000001755406029000#7afa6984
Timestamp: 2025-08-17 13:47:09+09:00
USER: Hi, I'm having trouble with my order #12345
ASSISTANT: I'm sorry to hear that. Let me look up your order.
TOOL: lookup_order(order_id='12345')
ASSISTANT: I see your order was shipped 3 days ago. What specific issue are you experiencing?
USER: Actually, before that - I also want to change my email address
ASSISTANT: Of course! I can help with both. Let's start with updating your email. What's your new email?
USER: newemail@example.com
TOOL: update_customer_email(old='old@example.com', new='newemail@example.com')
ASSISTANT: Email updated successfully! Now, about your order issue?
USER: The package arrived damaged

では次に長期記憶をテストします。 longmemory.py を作成します。

from bedrock_agentcore.memory import MemoryClient

client = MemoryClient(region_name="us-west-2")

memory = client.create_memory_and_wait(
    name="longmemory",
    strategies=[{
        "summaryMemoryStrategy": {
            # Name of the extraction model/strategy
            "name": "SessionSummarizer",
            # Organize facts by session ID for easy retrieval
            # Example: "summaries/User84/session123" contains summary of session123
            "namespaces": ["/summaries/{actorId}/{sessionId}"]
        }
    }]
) 

長期メモリは短期メモリより作成に時間がかかるため client.create_memory_and_wait と待ちが発生することを前提とした異なるコマンドになっています。先ほど紹介した抽出戦略はこの通りセッション全体の要約を指定しています。 "name": "SessionSummarizer"

longmemory.py を実行してて数分待ちます。

Memory ID: longmemory-5xFyxF30b8

作成された長期メモリのIDが表示されますのでメモしておきます。

次に longconversation.py を作成します。 MEMORY_ID の部分は先ほどメモしたものに書き換えておきます。

# longconversation.py
from bedrock_agentcore.memory import MemoryClient
import time
import json
import sys

# ====== 設定 ======
REGION = "us-west-2"
MEMORY_ID = "longmemory-5xFyxF30b8"  # 既存のメモリIDに置き換え可
ACTOR_ID = "User84"
SESSION_ID = "OrderSupportSession1"
NAMESPACE = "/summaries/User84/OrderSupportSession1"  # SummaryMemoryStrategy のテンプレに合わせる

client = MemoryClient(region_name=REGION)

# ====== 会話イベントを投入 ======
try:
    event = client.create_event(
        memory_id=MEMORY_ID,         # create_memory / list_memories の ID
        actor_id=ACTOR_ID,           # エージェント or エンドユーザー識別子
        session_id=SESSION_ID,       # 会話セッションのユニークID
        messages=[
            ("Hi, I'm having trouble with my order #12345", "USER"),
            ("I'm sorry to hear that. Let me look up your order.", "ASSISTANT"),
            ("lookup_order(order_id='12345')", "TOOL"),
            ("I see your order was shipped 3 days ago. What specific issue are you experiencing?", "ASSISTANT"),
            ("Actually, before that - I also want to change my email address", "USER"),
            (
                "Of course! I can help with both. Let's start with updating your email. What's your new email?",
                "ASSISTANT",
            ),
            ("newemail@example.com", "USER"),
            ("update_customer_email(old='old@example.com', new='newemail@example.com')", "TOOL"),
            ("Email updated successfully! Now, about your order issue?", "ASSISTANT"),
            ("The package arrived damaged", "USER"),
        ],
    )
    print(f"[OK] Event created: {event.get('eventId') if isinstance(event, dict) else 'created'}")
except Exception as e:
    print(f"[ERR] create_event failed: {e}", file=sys.stderr)
    sys.exit(1)

# ====== 抽出(Long-term Summary)は非同期。少し待つ ======
print("[INFO] Waiting 60s for summary extraction...")
time.sleep(60)

# ====== 取得ユーティリティ ======
def _extract_items(res):
    """SDKの戻りがlist/dictいずれでも配列部分を取り出す"""
    if isinstance(res, list):
        return res
    if isinstance(res, dict):
        return res.get("memories") or res.get("items") or res.get("results") or []
    return []

def _get_text(m):
    if not isinstance(m, dict):
        return None

    # content.text
    txt = (m.get("content") or {}).get("text")
    if txt:
        return txt

    # conversational.content.text
    conv = m.get("conversational") or {}
    txt = (conv.get("content") or {}).get("text")
    if txt:
        return txt

    # summary / text / value
    for k in ("summary", "text", "value"):
        v = m.get(k)
        if isinstance(v, str) and v.strip():
            return v

    # document.text
    doc = m.get("document") or {}
    if isinstance(doc, dict):
        txt = doc.get("text")
        if txt:
            return txt

    return None

# ====== サマリーをポーリングして取得 ======
deadline = time.time() + 180  # 最大3分待つ
poll_interval = 5
items = []

print(f"[INFO] Retrieving summaries from namespace: {NAMESPACE}")
while time.time() < deadline:
    try:
        res = client.retrieve_memories(
            memory_id=MEMORY_ID,
            namespace=NAMESPACE,
            query="summarize the support issue"
        )
    except Exception as e:
        print(f"[WARN] retrieve_memories failed, retrying: {e}")
        time.sleep(poll_interval)
        continue

    items = _extract_items(res)
    if items:
        # 必要なら RAW を見たいときにコメントアウト解除
        # print("---- RAW ----")
        # print(json.dumps(res, indent=2, default=str))
        break

    time.sleep(poll_interval)

# ====== 出力 ======
if not items:
    print("[INFO] No summary extracted yet.")
else:
    print("\n---- SUMMARIES ----")
    for i, m in enumerate(items, 1):
        text = _get_text(m) or "[no text field]"
        score = m.get("score") or m.get("similarity")
        ns = m.get("namespace") or (m.get("metadata") or {}).get("namespace")
        print(f"[{i}] {text}")
        if score is not None or ns:
            print(f"      meta: score={score} namespace={ns}")
[OK] Event created: 0000001755407910000#3c74bcec
[INFO] Waiting 60s for summary extraction...
[INFO] Retrieving summaries from namespace: /summaries/User84/OrderSupportSession1

---- SUMMARIES ----
[1] <summary>
    <topic name="Customer Support Interaction">
        The customer contacted support about issue with order #12345. The agent confirmed the order was shipped 3 days ago and asked for more details about the problem. Before addressing the order issue, the customer requested to change their email address from old@example.com to newemail@example.com, which was successfully updated. The customer then reported that their package arrived damaged.
    </topic>
</summary>
      meta: score=0.38225272 namespace=None

このように記憶した会話の内容を最大3分待ってサマリーして出力します。

Written by
編集部

亀田 治伸

Kameda Harunobu

  • Facebook->
  • X->
  • GitHub->

Share

Facebook->X->
Back
to list
<-