MODULE 04 · LESSON 4.2
Manage chat history and AI sessions
Keep history and AI session state consistent across turns.
Lesson 17 of 86 · FabrCore 2.0
Overview
Chat history contains messages. AgentSession can also contain provider-specific state. A single session fits an ongoing conversation; a new session per request is intentionally stateless. Changing thread selection accidentally can make a healthy agent appear to forget everything.
History and session state are related but different
Chat history is the retained sequence of messages. AgentSession may also contain model-runtime or provider state. FabrCore integrates history with the agent factory, while your proxy decides which thread/session to reuse. Reconstructing a new session for every OnMessage deliberately removes continuity even if the HTTP connection stays open.
A connection is not a conversation key
Users can reconnect, change pages or use another device while continuing the same logical thread. Conversely, one connection can address multiple threads. Select the thread intentionally and keep it scoped to the same principal and instance. Diagnose apparent forgetting by comparing those keys before raising model token limits or adding long-term Memory.
Reuse the intended conversation
- Choose a stable thread ID and create the chat agent/session once for that conversation. Use RunAsync or RunStreamingAsync as appropriate.
- Keep unrelated conversations on separate threads. Use the supported history APIs when clearing or replacing history rather than editing Orleans JSON.
- For a harness, use its wrapper so its additional session state is snapshotted; plain chat history persistence does not automatically serialize arbitrary StateBag values.
private AIAgent? _agent;
private AgentSession? _session;
public override async Task OnInitialize()
{
var tools = await ResolveConfiguredToolsAsync();
var result = await CreateChatClientAgent(
config.Models ?? "default",
threadId: config.Handle ?? fabrcoreAgentHost.GetHandle(),
tools: tools);
_agent = result.Agent;
_session = result.Session;
}
public override async Task<AgentMessage> OnMessage(AgentMessage message)
{
var response = message.Response();
await foreach (var update in _agent!.RunStreamingAsync(
new ChatMessage(ChatRole.User, message.Message), _session!))
{
response.Message += update.Text;
}
return response;
}
// Set custom state
session.StateBag.SetValue("userPreferences", new UserPrefs { Theme = "dark" });
// Read custom state
var prefs = session.StateBag.GetValue<UserPrefs>("userPreferences");
// Check and remove
if (session.StateBag.TryGetValue<string>("tempKey", out var value))
session.StateBag.TryRemoveValue("tempKey");
Run a same-thread and new-thread comparison
- Tell the AI assistant “The affected printer is in Bay 7.” In the same thread, ask “Which bay?” Expect Bay 7 from the supplied conversation.
- Start a new thread without repeating the fact. It should not claim to know Bay 7 solely from the previous thread, unless you have explicitly added a shared knowledge source.
- Switch back to the original thread and inspect its retained messages. Confirm the initial fact is present and that the application selects the correct session when resuming.
This checks conversation selection and continuity. It does not test durable Memory or survival of a full standalone Host restart.
If the result is different
A restored history and a restored harness plan are different things. Check both when diagnosing restart behavior.
Go deeper
Explore the related documentation.