跳到正文
DEV Community· Mohammed Rafay·· 2 小時前AI 評分53

如何為 Microsoft Agent Framework 智能體加入跨會話的用戶長期記憶

Add long-term memory to a Microsoft Agent Framework agent (one context provider, per-user recall across sessions)

AI 導讀

教學示範如何透過 MemorySync 的 Microsoft Agent Framework 整合,為智能體加入跨會話的用戶長期記憶。整合會在每次執行前召回相關記憶,並在執行後傳送用戶訊息供服務擷取持久事實;傳入 memorysync_user_id 可讓同一 provider 為多位用戶處理記憶。

正文

Disclosure: I'm Mohammed, founder of MemorySync. I drafted this article with AI assistance and reviewed it for accuracy against the official MemorySync and Microsoft Agent Framework docs. Install, imports and object construction were run; calls needing an API key were checked against the docs and run only against a local mock, not the live services.

Microsoft Agent Framework handles memory through context providers: objects you pass in context_providers=[...] that add context before the model runs and process the result afterwards (Agent Framework context providers docs). The built-in InMemoryHistoryProvider gives you local conversational memory for a session. Provider state lives in the session's state. That's the right home for the current thread. It isn't the right home for "this user is vegetarian and prefers short answers" when the same person opens a new session next week.

This post adds per-user long-term memory with MemorySync's Agent Framework integration, agent-framework-memorysync. MemorySync is a hosted memory layer, and I'm building it. Agent Framework behaviour comes from Microsoft's docs and the framework's source; everything about MemorySync comes from its Microsoft Agent Framework Memory guide. I installed the packages and ran the snippets with a stub chat client and a mocked MemorySync endpoint (agent-framework-core 1.23.0, agent-framework-memorysync 1.1.0), but not against a real model or a real MemorySync key, so check it against your versions.

If you're here from AutoGen: the AutoGen repository is now archived, and its README points new users to Agent Framework and links a migration guide. MemorySync has an AutoGen guide too if you can't move yet, but I'd build new agents on Agent Framework.

What you'll set up

  1. One MemorySyncContextProvider that recalls relevant facts about the user before every run and sends the user's messages for fact extraction after it.
  2. Per-run user identity, so one agent can serve many users.
  3. Optional: memory tools the model can call itself.

How Agent Framework thinks about memory

A context provider subclasses ContextProvider and overrides two hooks (docs):

  • before_run adds instructions, messages or tools to the invocation, e.g. context.extend_instructions(self.source_id, "...").
  • after_run sees the input messages and the response, so it can store or extract things.

Microsoft's own memory sample stores the user's name in provider state, so it carries across turns of that session. You can stack several providers on one agent (history, memory, RAG). The MemorySync provider is one of these, so you're not replacing anything the framework does.

Install

pip install agent-framework-memorysync

Per the guide it needs Python 3.10+ and agent-framework-core 1.8 or newer. PyPI currently lists agent-framework-memorysync 1.1.0 (requiring agent-framework-core>=1.8,<2), and agent-framework-core is at 1.23.0. You'll need a MemorySync API key from the dashboard (Settings → API Keys, inside a project). Set it as MEMORYSYNC_API_KEY and keep it server-side.

Step 1: Create the provider

from agent_framework_memorysync import MemorySyncContextProvider

provider = MemorySyncContextProvider(
    user_id="customer-42",   # required: the end user these memories belong to
    session_id="support",    # optional: tags this conversation's facts
)
# api_key="ms_..." also works; otherwise it reads MEMORYSYNC_API_KEY

user_id is a stable string you choose: your internal user ID, an email or a UUID. Use the same value on every run, or recall comes back empty. One user_id drives both storage and retrieval.

Step 2: Attach it to the agent

from agent_framework import Agent

agent = Agent(client=..., context_providers=[provider])

result = await agent.run("Which seat should I book?")

What happens, per the guide:

  • Before every run, relevant memories about this user are recalled into the instructions layer. They aren't added as a fake user message.
  • After the run, the user's messages are sent to MemorySync. The server extracts the durable facts and stores only those. Filler like "ok" yields none, and the agent's replies aren't sent.
  • Tool loops capture once: one send per user turn, not per model round-trip. Retries aren't extracted twice.
  • Failures don't break the run. If the memory lookup fails or times out, the agent runs without memories. If the send after the run fails, it's logged, and the run's result still stands.

Extraction is asynchronous, so a fact you just stated may take a moment to become recallable.

Step 3: Keep the thread and the user separate

The provider covers facts about the user. For the current conversation, keep using a session and a history provider. Here's how I'd combine them, using InMemoryHistoryProvider and create_session() from Microsoft's docs:

from agent_framework import Agent, InMemoryHistoryProvider
from agent_framework.openai import OpenAIChatClient

agent = Agent(
    client=OpenAIChatClient(),
    instructions="You are a helpful assistant.",
    context_providers=[
        InMemoryHistoryProvider("memory", load_messages=True),  # this thread
        provider,                                               # this user, across threads
    ],
)

session = agent.create_session()
await agent.run("I'm vegetarian and I always pick the aisle seat.", session=session)

OpenAIChatClient lives in a separate package (pip install agent-framework-openai, per Microsoft's OpenAI provider docs), and with no arguments it reads OPENAI_API_KEY and OPENAI_CHAT_MODEL (or OPENAI_MODEL) from the environment. That's my own combination of the two docs, so test it. InMemoryHistoryProvider is local, as its name says. Next week's new session starts with an empty history, and that's where the MemorySync provider comes in: the user's facts are still recalled.

Step 4: One agent, many users

The guide calls out a common gap: some memory adapters fix identity when you construct them, so a multi-tenant server ends up building one provider per user. This one resolves identity fresh on every run, in this order:

  1. Per run: await agent.run(..., options={"memorysync_user_id": user_id})
  2. A resolver: MemorySyncContextProvider(user_id_resolver=lambda session: ...)
  3. The constructor's user_id
result = await agent.run(
    "What should I order?",
    options={"memorysync_user_id": current_user.id},
)

My advice: take that ID from your authenticated user record, never from the request body.

Step 5 (optional): Memory tools and read-only runs

Two settings from the guide's configuration table:

provider = MemorySyncContextProvider(
    user_id="customer-42",
    expose_search_tool=True,  # registers search_memory + save_memory each run
    capture=False,            # recall only; don't send this run's user messages
)

capture=False is handy for eval runs that shouldn't write memory. top_k (default 5) controls how many memories are considered per run, and min_prompt_chars skips recall for trivial prompts.

Coming from AutoGen: what changes

AutoGen's AgentChat had a Memory protocol with query, update_context, add, clear and close, and you passed memories to AssistantAgent(memory=[...]) (AutoGen Memory and RAG docs). update_context added retrieved entries to the agent's model context before a step. Nothing was saved unless your code called add.

In Agent Framework, the same job is split across a provider's two hooks: before_run for recall and after_run for capture. In MemorySync's two adapters, that's the practical difference:

  • AutoGen adapter: recall is automatic, but capture is your job. After each exchange you call await memory.add_turn_pair(user_text, reply), and only the user text is sent.
  • Agent Framework adapter: both are automatic. There's no per-turn code, and capture=False turns the write side off.

So when you port an AutoGen agent, delete the add_turn_pair call. If you keep it next to the provider, you'd be sending the same user text from two places. Keep the same user_id values as before. I haven't checked whether facts stored through the AutoGen adapter come back through the new one (the two tag sessions differently), so test with one user first.

Troubleshooting

From the guide:

  • ModuleNotFoundError: agent_framework. The framework itself isn't installed: pip install agent-framework.
  • Recall returns nothing right after storing. Extraction is asynchronous; give it a moment. Also confirm store and recall use the same user_id.
  • The agent ran without memories once. A slow or failed lookup is skipped rather than holding up the run. The next run recalls normally.
  • Multi-tenant identity. Don't build one provider per user. Pass options={"memorysync_user_id": ...} per run.

One more, from the guide's quota section: on a monthly plan limit, over-limit writes are accepted without storing and reads return empty, so the agent keeps working. Evaluation keys return a 429 instead, so you notice during testing.

Sources

  • MemorySync Microsoft Agent Framework Memory guide: docs.memorysync.io/guides/agent-framework (and /guides/autogen)
  • Microsoft Learn: Agent Framework context providers (learn.microsoft.com/en-us/agent-framework/concepts/agents/conversations/context-providers)
  • microsoft/agent-framework: python/packages/core/agent_framework/_sessions.py (ContextProvider, provider state in AgentSession.state) and python/samples/01-get-started/04_memory.py
  • microsoft/autogen README (archived; points to Agent Framework) and AutoGen AgentChat Memory and RAG docs
  • PyPI: agent-framework-memorysync 1.1.0, agent-framework-core 1.23.0

Try it

If you want to see the save, reload and recall loop before wiring up an agent, the MemorySync playground runs it in your browser: memorysync.io/try.

來源:DEV Community · dev.to