How AI CareerBot preserved conversation context with GigaChat
We look at one part of the GigaChat API integration in an HR landing page: how we built dialog history so resume follow-ups wouldn’t start from scratch.

“Does it remember what it already suggested for the resume, or does it start from scratch again?”
In AI CareerBot, we connected the GigaChat API to a product HR landing page for job seekers. The use cases were practical: a user comes for resume advice, refines wording, then switches to interview prep and asks a follow-up on a previous answer.
In the first days after launch, thousands of users went through the landing page, and the weak point became obvious quickly: if the chat does not retain context, it turns into a “ask a question — get a paragraph” form. For resumes, that is a problem. A user asks, “What if I describe this experience more briefly?” and expects the AI to remember which experience they meant.
Why we could not send the entire conversation
GigaChat has a limited context window. A full dialog grows quickly: resume fragments, assistant replies, clarifications, interview prep. If you simply append every message to the request, sooner or later important parts stop fitting.
The most contentious issue was not the landing page animations, but the memory boundary: the frontend needed a continuous dialog, while the backend needed a predictable request size. In the end, we stored the history on the service side and, before calling GigaChat, assembled a short context: a stable instruction, a compact dialog memory, and the latest turns.
The key part: building messages before GigaChat
In simplified form, it looked like this:
const SYSTEM_PROMPT = `
You are an AI assistant for a job seeker.
Help improve resumes and prepare for interviews.
Do not invent experience on behalf of the user; first clarify missing details.
`;
export async function askCareerBot(sessionId: string, userText: string) {
const dialog = await dialogStore.load(sessionId);
const messages = [
{ role: 'system', content: SYSTEM_PROMPT },
...(dialog.summary
? [{ role: 'system', content: `Brief dialog memory:\n${dialog.summary}` }]
: []),
...dialog.messages.slice(-config.recentMessagesLimit),
{ role: 'user', content: userText.trim() },
];
const answer = await gigaChat.chat({ messages });
await dialogStore.append(sessionId,
{ role: 'user', content: userText },
{ role: 'assistant', content: answer.content },
);
await dialogStore.refreshSummary(sessionId);
return answer.content;
}
There are three layers here.
The first is SYSTEM_PROMPT. It sets the product frame: resume advice and interview preparation. We also explicitly state that the assistant must not make up experience for the user. For an HR scenario, that is critical: a nicely phrased lie does not help the user.
The second is dialog.summary. This is not the full log, but a short memory: what the user has already said, which edits were discussed, and what type of role they are preparing for. It is needed for questions like “make this bullet point sound stronger” or “what questions could they ask about this project.”
The third is the latest messages. They preserve local context: pronouns, tone, and the specific wording the user returned to. Without this, the model sees only a dry summary and may answer too generically.
Why the history was not stored in the frontend
The landing page was animated: the chat had to respond quickly, show a loading state, and keep the user engaged in the dialog. But the dialog memory could not be tied only to UI state. A page refresh, a return visit, or a client-side failure should not reset the consultation.
That is why the frontend handled the conversation flow and animations, while the server handled the history, context assembly, and the GigaChat call. This made it easier to control the size of messages and change memory rules without shipping a new client release.
Another practical benefit: we could separately see which follow-ups broke. If a user уточняет “make this paragraph shorter,” and the original paragraph did not make it into the GigaChat request, the problem is not the model. The problem is how we assembled the context.
What we checked in real conversations
After launch, we did not look at abstract “AI quality,” but at specific transitions inside the dialog:
- the user sent a resume and asked to rewrite one bullet;
- then asked to make the wording shorter;
- then switched to interview questions about the same experience;
- after that, returned to the resume and asked for a final version.
For this kind of scenario, saved history matters more than a nice first answer. You can get the first answer without memory. A proper follow-up only appears when the request to GigaChat contains what the user refers to with phrases like “this,” “that project,” and “the previous version.”
In code reviews for integrations like this, we now look at the messages assembly first: what goes into the context, what gets compressed into a summary, and where the boundary for the latest turns is.