Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Day 4: Checkpoint and Resume

Days 1 through 3 build one uninterrupted coding-agent run. The model proposes an action, the harness executes it, and the result becomes the next model observation. But if the process stops, a new model object does not know which conversation prefix it had already processed.

Day 4 makes one boundary visible: save the conversation and the small model snapshot immediately after a complete tool observation, then restore both into a fresh scripted model and continue at the next response. The completed tools stay completed; resume starts after their observations instead of replaying them.

This is a teaching checkpoint for one process and the course’s fake model. It is not a session tree, rewind feature, persistent KV store, transaction log, or exactly-once effect system.

Files and Public Surface

Implement the TODO-only surfaces in:

FilePublic namesResponsibility
src/tiny_llm/agent/checkpoint.pyModelCheckpoint, AgentCheckpoint, create_checkpointRepresent and validate one in-memory conversation/model snapshot.
src/tiny_llm/agent/loop.pyrun_to_checkpoint, resume_agentStop after a complete observation, then continue with a fresh model.
src/tiny_llm/agent/__init__.pythe names aboveExport the cumulative Day 4 API.

Run the learner checkpoint from the repository root:

pdm run test --week 4 --day 4

Before you implement the TODOs, all seven Day 4 tasks are expected to fail. The test uses a scripted model, fake cache metadata, a temporary workspace, and one exact Python validation command. It does not load model weights.

Course maintainers can check the supplied implementation without copying the learner test:

pdm run test-refsol --week 4 --day 4

One Safe Loop Boundary

A checkpoint is saved only after the harness has appended both halves of a tool interaction:

assistant: {"tool":"edit_file", ...}
user:      Tool result:\nedited app.py
                                      ^ checkpoint here

Saving before the observation would leave the restored model unable to tell whether the tool ran. Day 4 therefore counts completed tool calls and saves at the boundary after _append_tool_result(...) has produced the next complete conversation.

The checkpoint stores the semantic messages, not the AgentEvent history. Day 3 receipts remain separate evidence about the edit or command. They are not copied into the checkpoint and they do not become a replay controller.

Task 1: Represent the Fake Model Snapshot

ModelCheckpoint contains four fields, in order:

conversation_position: int
response_index: int
cached_token_ids: tuple[int, ...]
layer_offsets: tuple[int, ...]

conversation_position is the number of semantic messages at the saved boundary. response_index tells the scripted model which response comes next. cached_token_ids represents the prompt prefix in the fake cache, and every layer_offsets entry must equal its length.

Reject negative positions, invalid token IDs, a missing layer snapshot, or offsets that disagree with the cached prefix. These checks make the fake model state internally coherent without introducing a production cache format.

Task 2: Bind Conversation and Model State

AgentCheckpoint contains:

checkpoint_id: str
task: str
messages: tuple[tuple[str, str], ...]
model: ModelCheckpoint

create_checkpoint(task, messages, model) copies each mutable message into an immutable (role, content) pair. It computes checkpoint_id as the SHA-256 of canonical JSON containing the task, messages, and model fields.

AgentCheckpoint.validate() checks the ordinary resume contract:

  • the task and messages are structurally valid;
  • the model’s conversation position equals the saved message count;
  • the checkpoint ID still matches the content.

This identity check catches an accidental mismatch. It is not a hostile-tamper or authentication scheme.

Task 3: Stop After a Complete Observation

run_to_checkpoint(task, generate, workspace, after_tool_calls=1, limits=None) starts with the same prompt and validation rules as run_agent. It counts a tool call only after execution and observation append. At the requested count, it calls:

model_state = generate.save_checkpoint(messages)

and returns an AgentCheckpoint.

The generator must return a ModelCheckpoint. A missing checkpoint method, a non-positive tool-call count, or a run that finishes before the boundary is a clear error instead of a partial checkpoint.

Task 4: Restore a Fresh Model

resume_agent(checkpoint, fresh_generate, workspace, limits=None) validates the checkpoint, calls:

fresh_generate.restore_checkpoint(checkpoint.model)

rebuilds the semantic message list, and enters the normal loop. The fresh model therefore sees the exact conversation prefix and produces the response at the saved response_index.

No old tool action is submitted again. The first model call after restore sees the already-recorded tool result and chooses the next action or final answer.

Task 5: Make the Fake Cache Visible

The test’s FakeCheckpointModel turns each message content length into one fake token ID. save_checkpoint(messages) records those token IDs, two matching layer offsets, and the next scripted-response index. A new model object restores that state and asserts that its first resumed input has the same token prefix and conversation position.

This deliberately small representation exposes the inference/harness integration: the harness owns semantic conversation state, while the model owns the cache snapshot that accelerates exactly that state.

Task 6: Resume Without Replaying Effects

The end-to-end test scripts:

read app.py
edit app.py
run the exact validation command
checkpoint after the validation observation
construct a fresh scripted model
resume to the final answer

Before resume, the Day 3 store already contains the edit receipt and validation receipt. After resume, the approval log and receipt count are unchanged: neither completed effect ran twice. The checkpoint contains no receipt IDs and makes no exactly-once claim; it simply resumes after the conversation already says those effects completed.

Task 7: Keep the Boundary Small

The Day 4 starter adds only checkpoint.py and two loop entry points. Do not add session IDs, parent pointers, branches, rewind methods, compaction summaries, steering queues, disk cache files, or later-day modules. If the in-memory checkpoint is lost, start a new run.

Checkpoint

You can now stop after a complete tool observation and continue with a fresh scripted model from the same conversation and fake-cache position. Inspect the checkpoint’s messages and model fields, then confirm that the pre-checkpoint edit and command remain single completed effects.

Continue with Day 5: Compact Completed Work to derive a smaller model-visible transcript while keeping the exact effect receipts.

Your feedback is greatly appreciated. Join our Discord community.
Found an issue? Open an issue or pull request at github.com/skyzh/tiny-llm.
tiny-llm-book © 2025 by Alex Chi Z is licensed under CC BY-NC-SA 4.0.