如何在重新啟動後儲存 AI 智能體狀態?
How to store agent states across restarts?
文章説明如何讓 AI 智能體在重新啟動後恢復執行,並把需保存的內容分為執行狀態、目前計劃、進行中的工具呼叫,以及跨執行記憶。文中比較 LangGraph、Google ADK 及 OpenAI Agents SDK 的文檔,指出純記憶體儲存會在重啟時遺失狀態,而各框架均記載了持久化替代方案。對於已開始但未確認完成的工具呼叫,只有在被呼叫的服務以穩定意圖生成冪等鍵並執行去重時,重試才安全。
Adam Lewis framed the practical problem in a dev.to comment on 30 September 2026: "Where does the halfway state live when you close the terminal, is it in the message list or reconstructed from the repo each time it starts?" He then identified the difficult part: "saving the transcript is easy and the plan the agent was carrying is hard. If the plan is implied by the files it has already touched, restarting is re-deriving it." (comment)
Store four kinds of state separately. Run state goes in a database-backed framework checkpointer or session service, the current plan goes explicitly inside that state, tool calls in flight get an outcome status and an idempotency key derived from stable intent, and learned memory lives outside the individual run.
TL;DR
- A restart must preserve four things: run state, the agent's current plan, tool calls in flight, and what the agent learned. In the LangGraph, Google ADK, and OpenAI Agents SDK documentation read on 2 November 2026, the in-memory storage option loses state at restart, while each framework documents a persistent alternative.
- An operation that started but did not report completion has an unknown outcome. It should not be retried blindly or allowed to block later work. Changes in Codex and Hy-Memory separated that condition from ordinary retryable failure in October 2026.
- A retry is safe only when the called service enforces an idempotency key derived from stable intent. ADK says resumed tools run "at least once" and may run more than once.
- Run state belongs in the framework's persistent execution facility. Learned memory belongs outside one run. LangGraph documents these as separate systems: checkpointers persist graph state, while stores hold application-defined data outside it.
Mnemoverse (the author's company) makes a memory layer of the kind described in the section on what an agent learned.
What does an agent have to keep across a restart?
Run state is the durable record of where one execution is, including the data needed to continue it.
A checkpoint is a persisted snapshot of run state from which execution can continue or replay. LangGraph describes the mechanism directly: "Checkpointers persist a thread's graph state as checkpoints." (source)
A side effect is an externally visible change caused by a tool call, such as sending a message or writing to another service.
An unknown outcome is a call state in which the agent knows an operation started but cannot tell whether its side effect committed.
An idempotency key is a stable operation identifier that the called service checks to prevent the same intended effect from being applied twice. Temporal notes that such protection is enforced by the called service, "not by the Activity itself." (source)
Learned memory is information retained for later threads or sessions rather than for continuing one current execution.
These records answer different recovery questions: where execution should continue, what plan the agent was following, which external actions completed, failed or remain uncertain, and what the agent should still know in a later session.
A Hugging Face forum question from 2 January 2026 asked whether developers "struggle with preserving deterministic agent state across restarts?" (thread). The difficulty is not one missing database. It is the need to represent each state explicitly.
If the agent is a coding tool you use rather than one you build, see What survives when AI agents restart?
Persistent agent state APIs: where do LangGraph, ADK and the OpenAI Agents SDK keep run state?
| Framework and documentation | What in-memory storage loses at restart | Documented persistent facility | What else matters |
|---|---|---|---|
| Google ADK, read 2 November 2026 |
InMemorySessionService: "All conversation data is lost if the application restarts." InMemoryMemoryService: "None, data is lost on restart". |
DatabaseSessionService: "Data survives application restarts." |
temp: state is discarded after an invocation. user: and app: state stored in memory is lost at restart. Resume is available in ADK Python 1.16 and higher, reinstates completed events, and continues from partially completed state. Resume is not supported by default for Custom Agents. (session, memory, state, resume) |
| LangGraph, read 2 November 2026 | "MemorySaver and InMemorySaver store checkpoints in RAM. When the process restarts, all checkpoints are lost." | A database-backed checkpointer for run state and a database-backed store for cross-thread data. | A checkpointer saves graph state at each super-step. Cooperative shutdown can finish the current super-step and save a resumable checkpoint. A node already running finishes before draining takes effect. (persistence, memory, fault tolerance) |
| OpenAI Agents SDK, read 2 November 2026 |
SQLiteSession without a file path uses an "In-memory database (lost when process ends)". |
SQLiteSession with a file uses a "Persistent file-based database". A suspended run can be represented by durable RunState. |
Serialized RunState includes approval decisions, pending tool calls, and tool arguments. The documentation says loading does "not authenticate the snapshot or the person submitting it." (sessions, human in the loop) |
From each framework's own documentation, read on 2 November 2026. Examples, not a full list of backends. The row order is alphabetical, not evaluative.
Anis Meziani described the operational default in his own ADK-based system: "And if you don't configure where sessions live, ADK falls back to an in-memory session service." In a later comment he wrote that ADK has three in-memory defaults, covering sessions, long-term memory, and artifacts; that is his reading, not an independent claim here about every ADK facility (article, comment).
Why is saving the conversation not enough to resume an agent?
A persistent conversation database solves only part of recovery. Meziani put the boundary plainly: "Postgres alone doesn't save the half-finished task. It saves the conversation. The run that was in flight still dies with the pod." (comment)
A conventional workflow can resume from a named step because its control path is explicit. An agent may choose its next action from model output and current context. Meziani wrote, "With an LLM agent, there is no step 3." Max Quimby made the related point that "you can't cleanly resume a non-deterministic trajectory" (article, comment).
The practical response is to persist the plan explicitly. Record the current objective, completed step identifiers, pending branches, and required inputs in run state, and update that record at each durable boundary. ADK's guidance for resumable custom logic says to "generate and save the agent state for each completed step of the agent's overall task." (source) Do not put a resumable plan in ADK temp: state. ADK says that state is "Discarded after the invocation completes and does not carry over to the next one." (source)
Without the original input and an explicit plan, recovery has to infer intent from the transcript and observed effects. Meziani summarized his own incident: "What was missing wasn't observability. It was the input." (source)
What happens to a tool call that was running when the agent restarted?
The dangerous interval is after the external service commits but before the agent records success.
Temporal gives a direct example: "The Activity function completes successfully, but the Worker crashes just before it notifies the Temporal Service." The event history then lacks completion, "so the Activity will be retried." Completed activities do not execute again during workflow replay, and failed workflow execution continues from the last recorded event (activity definition, workflow execution).
LangGraph documents a related boundary at graph-node level. If one node fails during a super-step, pending writes from other nodes that completed successfully are stored, and resuming that super-step does not rerun those successful nodes. Replay from an earlier checkpoint re-executes later nodes, "including any LLM calls, API requests, or interrupts". LangGraph also records failure provenance (checkpointers, fault tolerance).
ADK states that Resume runs tools "at least once, and may run more than once when resuming a workflow." It tells developers whose tools can cause harmful duplicates to make the tool check for and prevent them (source).
Meziani's system records invocation rather than waiting for a response: "We count the call, not the response." Its tools_executed field distinguishes no call, confirmed calls, and uncertain calls. He treats an uncertain call as "probably something" because the cost of the two errors differs: "Refusing a safe relaunch costs a click. Approving an unsafe one costs a duplicate email to a customer." (Fix 7)
Changes in two projects in October 2026 show how that status becomes operational:
- On 16 October, Codex PR #54664 distinguished transient errors "known to be safe to retry" from writes "that may already have committed". It also states: "Recovery remains the operation owner's responsibility; neither category automatically triggers retries."
- A Hy-Memory issue opened on 17 October described extraction tasks left as
unknownafter a network interruption. The reporter expected the endpoint'sbacklog_pendingvalue to match the database and reported reinstallation as the only recovery they had found. A maintainer linked PR #27 and closed the issue that day. - PR #27 preserved unknown rows for reconciliation so one uncertain write would not freeze later writes from the same source. It retried an unresolved first attempt once after reconciliation, then parked a second unknown result for explicit recovery. The PR says: "This does not blindly treat unknown as definitely unsent."
- Hy-Memory PR #63, merged on 18 October, separated waiting for model activation from timed retry. Its prior path returned the backlog to pending every minute without attempting a provider call. The change readmitted those rows after successful activation.
A durable write record therefore needs at least four states: completed, safe to retry, outcome unknown, and waiting for an event. Each needs a separate recovery branch. A queue counter that cannot represent unknown work can look empty while progress has stopped.
How do I make a retried step safe to run twice?
Temporal recommends idempotent activities. Its suggested key combines Workflow Run ID and Activity ID because that pair remains stable across retry attempts while differing between workflow executions (source).
The key must identify intent, not whichever arguments the model generated this time. Quimby described effectful tools that compute a key from arguments. Meziani identified the failure mode: "a key hashed from the args breaks when the LLM rewrites them on replay. The key has to come from stable intent, never from generated content." He cited provider message identity, a content digest for dataset imports, and human approval for outbound campaigns as examples from his system (discussion).
A forum respondent described another useful pattern: "I store the outputs of anything nondeterministic, like tool calls and external requests, so a replay reads the stored result rather than calling the outside world again." The same system gives each step an identifier so retries do not duplicate side effects (thread).
Retries can also hide inside transport code. Meziani reported that the ApowerB runtime built on ADK resent the same request after a 429 up to twice without checking whether a streamed function call had already passed. This describes ApowerB code, not ADK behavior. His resulting rule was: "once a call has passed, forward the 429 and let a human decide." The author of this article suggested that the forwarded error could identify calls already completed in that turn (Meziani, Izgorodin).
Meziani also names the boundary of his approach: "A replay is a new run that cites the original". His system did not yet provide true mid-flight resume that reused completed tool results (source).
Where should what the agent learned live?
Learned information should survive beyond one execution. It should not depend on whether a particular run remains resumable.
LangGraph separates these responsibilities: "Stores persist application-defined data outside the graph state." It recommends stores for "long-term, cross-thread memory, including user preferences, facts, and shared knowledge", with a database-backed store for production (persistence, memory).
ADK likewise distinguishes sessions, state, and memory. Its in-memory memory service loses data at restart, and state with user: or app: scope, which can span conversations, is lost at restart when its in-memory service holds it (memory, state).
An external memory layer is another option. Mnemoverse is one example for learned memory. Its REST POST /memory/write accepts an external_ref, described as a client-provided unique reference for idempotent writes. A write that reuses one is answered with 409 CONFLICT, and the reference can be looked up to recover an atom identifier after restart. The documented /memory/write-batch route accepts an Idempotency-Key; retrying with the same key after a dropped response returns the cached response instead of writing the batch twice. An external_ref derived from stable intent, such as a decision and its date rather than model-generated wording, applies the same rule used for effectful tools.
What changes when the code is upgraded between the crash and the restart?
A stored execution may outlive the code that created it. ADK gives a direct warning: "Do not modify a stopped agent workflow before resuming it." (source)
Schema compatibility needs the same attention. Debashish Ghosal, replying to the author of this article about his own recovery guard, wrote: "So the guard is build-vs-manifest, and the restore path never reads alembic_version from the target DB." He agreed that the rolling deployment window is the gap, because an old pod could restore into a database one revision ahead (comment).
A recovery check should compare checkpoint metadata with the database and schema into which it will restore, not only with the application build. During a rollout, old and new processes may write the same durable state. Compatibility rules must cover that overlap.
How do I test that agent state survives a restart?
Treat restart recovery as a fault-injection test, not as a database connectivity check.
- Kill the process during an effectful tool call. Restart it and verify that the external effect exists once. Confirm that the call is recorded as completed or unknown rather than silently repeated.
- Kill the process between plan steps. Verify that execution continues from the recorded step and plan. It should not infer a fresh trajectory solely from the transcript.
- Write a user preference, then start a new thread after restart. Verify that cross-session memory still contains the preference.
- Run a negative control with the documented in-memory option. It should lose state. If it does not, the test may be exercising an unintended persistent component.
Meziani proposes the same basic fault injection in his own walkthrough: "kill the container mid-run, restart it, and watch the session come back." (source)
What this article does not claim
No performance figure is given for any framework or product. The frameworks in the table are examples, and only their own documentation is quoted, as read on 2 November 2026. This article tests none of them. Practitioners' accounts describe their own systems and code. The four recovery tests are proposals, not a description of any team's test suite.
Sources and method. Every quotation was read on 2 November 2026 with two instruments, a browser-like request and a reader service with its cache off (dev.to comments and the article also through the dev.to API, the GitHub pull requests and the issue also through the GitHub API); a made-up address on each host answered 404 as a control. The text describes those pages as of that date. A longer version, with the common questions and the full list of sources, is in the Mnemoverse documentation library: How to store agent states across restarts?
Edward Izgorodin · Mnemoverse · 2026-11-02
來源:DEV Community · dev.to