Memory in BlogWriter

BlogWriter uses the Responses API which can help with state maintenance. BlogWriter, however, implements important state (context) using a session object, and manages long-term memory by writing to a Cosmos Database.

I’m reusing this image because (a) it is apt and (b) he’s so cute!

You can see that we are using the Responses API in the Researcher agent where the AI agent is reused but is stateless across turns. ResearcherAgent.cs specifically says that “no agentSession is retained.”

What is stateful:

  1. Application session state
    Stored in BlogSession.State as a ResearchState in BlogSession.cs. This is the real durable state.
  2. ResearchState fields
    Held in ResearchState.cs:
    • MainTask
    • MinWords
    • MaxWords
    • ResearchFindings
    • SearchRefinements
    • Draft
    • ReviewNotes
    • RevisionNumber
    • NextStep
    • CurrentSubTask
  3. Persistence
    Sessions are saved to Cosmos DB in CosmosBlogSessionStore.cs. Cosmos also tracks OwnerId, CreatedAt, UpdatedAt, and ETag.
  4. Per-process token budget
    TokenCapChatClient tracks cumulative input, output, reasoning, and total tokens across model calls in TokenCapChatClient.cs. That is stateful, but it is spend-control state, not conversation memory.

What is not being held:

  • No persisted Responses API response_id
  • No retained MAF AgentSession
  • No explicit conversation/thread object passed between turns
  • No server-side conversation history intentionally reused by this app

Each agent call builds a fresh message from the current ResearchState. For example, the Author call includes the main task, research findings, current draft, review notes, follow-up, and target word count in AuthorAgent.cs. So continuity comes from app state, not from the Responses API maintaining the conversation.

There may be transient state inside a single Responses run, especially tool-call state for the Researcher’s hosted web search, but once that run returns, the durable continuity is whatever the app writes back into ResearchState.

Storing in Cosmos

State is saved as one Cosmos DB document per blog session.

The shape is defined by BlogSession.cs: each document contains:

{
  "id": "<session-guid>",
  "OwnerId": "<current-user-owner-id>",
  "CreatedAt": "...",
  "UpdatedAt": "...",
  "State": {
    "MainTask": "...",
    "MinWords": 1000,
    "MaxWords": 2000,
    "ResearchFindings": [],
    "SearchRefinements": [],
    "Draft": "...",
    "ReviewNotes": "...",
    "RevisionNumber": 1,
    "NextStep": "...",
    "CurrentSubTask": "..."
  }
}

The actual save logic is in CosmosBlogSessionStore.cs:

  • CreateAsync(...) creates a new BlogSession, assigns a GUID id, gets the current OwnerId, sets timestamps, and calls CreateItemAsync(...).
  • The Cosmos partition key is OwnerId, so each user’s sessions are partitioned by owner.
  • SaveAsync(...) updates UpdatedAt and calls ReplaceItemAsync(...), replacing the whole session document.
  • It uses the session’s ETag with IfMatchEtag, so concurrent stale saves are rejected.
  • If Cosmos returns PreconditionFailed, the app throws SessionConflictException.

The ETag itself is not stored inside the document because BlogSession.ETag has [JsonIgnore]; it is only kept in memory after reads/writes for optimistic concurrency.

So: the app does not store chat history as separate Cosmos rows. It stores the full workflow state as a nested State object inside the session document, then replaces that document after each completed workflow run or revision.

Note: The initial draft of this post was written by BlogWriter and then edited by Jesse Liberty.
Illustrations by Copilot. Caution: LLMs make mistakes; this post is offered as is.

Unknown's avatar

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. Liberty is a Senior AI Engineer at the University of Pittsburgh Medical Center, and was a Team Lead and Senior Software Engineer for various corporations, a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a 21 year Microsoft MVP.
This entry was posted in AI. Bookmark the permalink.