-
Notifications
You must be signed in to change notification settings - Fork 1k
AgentThread serialization alternatives ADR #3062
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces an Architecture Decision Record (ADR) that evaluates alternative approaches to serializing and deserializing AgentThread instances. The ADR identifies drawbacks with the current custom Serialize/Deserialize methods and proposes solutions that would enable standard serialization mechanisms like JsonSerializer.
Key Changes
- Proposes three options for AgentThread serialization: separating state from behavior with lazy reattachment, separating state from behavior completely, or maintaining the current approach
- Documents the tradeoffs between supporting custom behaviors and enabling standard serialization
- Outlines required changes to ChatMessageStore and related infrastructure to support state-based serialization
|
|
||
| ### Option 3: Keep the current approach of custom Serialize/Deserialize methods | ||
|
|
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ADR is missing analysis of pros and cons for each option. According to the ADR template and conventions from other ADRs (like 0006-userapproval.md), each option should include a discussion of its advantages and disadvantages to help readers understand the tradeoffs.
| ### Option 3: Keep the current approach of custom Serialize/Deserialize methods | |
| **Pros** | |
| - Simplifies serialization because `AgentThread` contains only serializable state, without needing to persist behavior instances directly. | |
| - Clearly separates concerns between persisted state (on `AgentThread`) and runtime behavior (constructed by the agent), which can make versioning of state easier. | |
| - Avoids having to serialize behavior-specific implementation details or dependencies. | |
| **Cons** | |
| - Behaviors are no longer accessible directly from `AgentThread` (e.g., via `AgentThread.GetService<TBehavior>()`), which may break some existing usage patterns. | |
| - The agent may need to construct multiple behavior instances over the lifetime of a single thread, potentially leading to additional allocations and lifecycle complexity. | |
| - Introducing APIs such as `AIAgent.GetBehavior<TBehavior>(AgentThread thread)` increases coupling to the agent and may make behavior access patterns less intuitive. | |
| ### Option 3: Keep the current approach of custom Serialize/Deserialize methods | |
| In this option we keep the existing model where `AgentThread` exposes a `Serialize` method and deserialization is handled via a custom `Deserialize` method on the `AIAgent`. No structural changes are made to how state and behavior are modeled today. | |
| **Pros** | |
| - Requires no migration effort for existing users and avoids breaking changes in public APIs. | |
| - Keeps the current behavior semantics exactly as they are, which may be preferable while other parts of the system are evolving. | |
| - Avoids introducing new abstractions or factory patterns, reducing the amount of refactoring needed in the short term. | |
| **Cons** | |
| - Continues to rely on custom `Serialize`/`Deserialize` implementations, which can be error-prone and lead to duplicated logic across agents and threads. | |
| - Makes it harder to evolve the serialization format and versioning strategy, since behavior and state are tightly coupled to the current implementation. | |
| - Keeps the conceptual model different from other components that rely on explicit state objects and factories, which can reduce consistency and discoverability for users. |
|
|
||
| - Option 1: Separate state from behavior, serialize state only and re-attach behavior on first usage | ||
| - Option 2: Separate state from behavior, and only have state on AgentThread | ||
| - Option 3: Keep the current approach of custom Serialize/Deserialize methods |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can Options 1 and 3 coexist? If so, we can keep all the benefits of both, allowing client code to decide on the option depending on their scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They probably could, but I'm not sure if that is better. The behavior for the two options would differ somewhat, especially around whether the ChatMessageStore / AIContextProvider properties are instantiated or not, meaning users are not just confronted with two options for doing the same thing, these also behave differently.
Motivation and Context
There are some drawbacks to the current serialization mechanism used for AgentThread instances, so creating an ADR to consider some alternatives and their implications.
#303
Description
Contribution Checklist