Tillståndshantering mellan körningssteg
Bevara variabler, dataframes och importerade bibliotek mellan flera kodkörningssteg, så att agenten kan bygga vidare på tidigare resultat utan att köra om tidigare beräkningar.
Tillståndshantering mellan körningssteg är en gratis lektion i AI Engineering Academy på CoddyKit. Detta är lektion 3 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för AI Engineering Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i AI Engineering Academy innehåller totalt 4 lektioner.
Problemet med tillståndslöshet
Varje körning av en Docker-container startar en ny Python-tolk. Variabler som definieras i iteration 1 finns inte i iteration 2. Denna tillståndslöshet tvingar agenten att beräkna om eller läsa in allt från början vid varje körningssteg — om ni inte implementerar en uttrycklig strategi för tillståndshantering som sparar och återställer tillstånd mellan iterationer. Utan detta är analyser i flera steg omöjliga.
# Iteration 1 - works fine
df = pd.read_csv('data.csv') # df is in memory
df_cleaned = df.dropna()
print('Rows after cleaning:', len(df_cleaned))
# Iteration 2 - NEW container, df_cleaned is GONE
result = df_cleaned.groupby('category').sum() # NameError: df_cleaned is not defined
print(result) # This will fail!Filbaserad persistens av tillstånd
Det enklaste och mest portabla tillvägagångssättet är att spara tillstånd i filer. I slutet av varje kodblock sparar agenten DataFrames, dictionaries eller andra objekt i filer i arbetsytans katalog. Nästa iteration läser in dem igen. Parquet är idealiskt för DataFrames, JSON för dictionaries och pickle för godtyckliga Python-objekt (även om pickle från otillförlitlig kod innebär en säkerhetsrisk).
import pandas as pd
import json
from pathlib import Path
WORKSPACE = Path('/workspace')
# Iteration 1: process and SAVE
df = pd.read_csv(WORKSPACE / 'raw_data.csv')
df_cleaned = df.dropna().reset_index(drop=True)
df_cleaned.to_parquet(WORKSPACE / 'cleaned.parquet') # save for next iteration
stats = {'rows': len(df_cleaned), 'columns': list(df_cleaned.columns)}
with open(WORKSPACE / 'stats.json', 'w') as f:
json.dump(stats, f)
print('Saved cleaned data:', len(df_cleaned), 'rows')
# Iteration 2: LOAD and continue
df_cleaned = pd.read_parquet(WORKSPACE / 'cleaned.parquet') # restore state
with open(WORKSPACE / 'stats.json') as f:
stats = json.load(f)
result = df_cleaned.groupby('category')['value'].sum()
print(result)Lär agenten filbaserat tillstånd
Det räcker inte att implementera filbaserat tillstånd i infrastrukturen — LLM:en måste också känna till konventionen och använda den konsekvent. Inkludera uttryckliga instruktioner i systemprompten: när ett resultat har beräknats och kommer att behövas senare ska det alltid sparas med ett beskrivande filnamn, och i början av varje iteration ska de filer som skapats i tidigare steg alltid läsas in innan arbetet fortsätter.
STATE_MANAGEMENT_INSTRUCTIONS = '''
State persistence rules:
- You have a persistent workspace at /workspace/
- After computing any result you will need later, SAVE it to /workspace/
- DataFrames: use .to_parquet('/workspace/name.parquet')
- Dicts/lists: use json.dump to /workspace/name.json
- Text: write to /workspace/name.txt
- At the start of each code block, LOAD the files you need from previous steps
- Use clear, descriptive filenames like 'cleaned_data.parquet', not 'tmp1.parquet'
- When listing files, use: import os; print(os.listdir('/workspace/'))
'''Kärnbaserat tillstånd med beständiga Python-processer
Ett alternativ till filbaserat tillstånd är att hålla en beständig Python-process (till exempel en Jupyter-kärna) igång mellan iterationerna och mata in varje kodblock i den med exec(). Variabler som definieras i en iteration förblir tillgängliga i nästa. Detta tillvägagångssätt är snabbare och mer naturligt, men kräver att en beständig process körs för varje agentsession i stället för tillfälliga containrar.
import jupyter_client
class PersistentKernel:
def __init__(self):
km, self.kc = jupyter_client.manager.start_new_kernel(kernel_name='python3')
self.kc.wait_for_ready(timeout=30)
print('Kernel started')
def execute(self, code: str, timeout=60) -> tuple[str, str]:
msg_id = self.kc.execute(code)
outputs, errors = [], []
while True:
msg = self.kc.get_iopub_msg(timeout=timeout)
if msg['msg_type'] == 'stream':
if msg['content']['name'] == 'stdout':
outputs.append(msg['content']['text'])
else:
errors.append(msg['content']['text'])
if msg['msg_type'] == 'status' and msg['content']['execution_state'] == 'idle':
break
return ''.join(outputs), ''.join(errors)
def shutdown(self):
self.kc.shutdown()
# Variables persist across execute() calls!
kernel = PersistentKernel()
kernel.execute('x = 42') # x is now defined in kernel
output, _ = kernel.execute('print(x)') # prints 42 - state persists!
kernel.shutdown()Hantera tillstånd med ett tillståndsobjekt
På agentens orkestreringslager (utanför sandlådan) bör ni underhålla ett uttryckligt tillståndsobjekt som följer agentens aktuella arbete: vilka filer som har skapats, vad som har beräknats, vilket steg agenten befinner sig i och vilket det slutliga målet är. Skicka en sammanfattning av detta tillståndsobjekt till LLM:en i varje iteration, så att den alltid vet var den befinner sig i den övergripande uppgiften.
from dataclasses import dataclass, field
from typing import Any
@dataclass
class ExecutionState:
task: str
iteration: int = 0
workspace_files: list[str] = field(default_factory=list)
computed_values: dict[str, Any] = field(default_factory=dict)
completed_steps: list[str] = field(default_factory=list)
last_output: str = ''
def to_context_summary(self) -> str:
return f'''Current task: {self.task}
Iteration: {self.iteration}
Completed steps: {', '.join(self.completed_steps) or 'None yet'}
Workspace files: {', '.join(self.workspace_files) or 'None yet'}
Key values: {self.computed_values}
Last output: {self.last_output[:500]}'''
def after_execution(self, output: str, step_name: str):
import os
self.workspace_files = os.listdir('/workspace')
self.completed_steps.append(step_name)
self.last_output = output
self.iteration += 1Infoga tillståndskontext i prompter
Varje gång ni anropar LLM:en för nästa kodblock ska ni infoga den aktuella tillståndskontexten i användarmeddelandet. Då får LLM:en korrekt information om vilka filer som är tillgängliga, vad som har beräknats och vad som återstår att göra. Utan denna kontext kan LLM:en försöka återskapa filer som redan finns eller hoppa över steg som redan är slutförda.
def build_iteration_prompt(task: str, state: ExecutionState, last_observation: str) -> str:
return f'''ORIGINAL TASK: {task}
CURRENT STATE:
{state.to_context_summary()}
LAST EXECUTION OUTPUT:
{last_observation}
What is the next step? Write Python code to continue.
Remember:
- Load data from workspace files if needed
- Save any results you will need in future steps
- If the task is complete, say TASK COMPLETE and summarize the result'''
# Use in the main loop
for step_num in range(max_iterations):
context = build_iteration_prompt(task, state, last_observation)
response = llm.complete(messages + [{'role': 'user', 'content': context}])
code = extract_code_block(response)
if not code:
break # done
output, err = execute_in_sandbox(code)
state.after_execution(output, step_name=f'step_{step_num}')
last_observation = format_observation(output, err)Hantera stora mellanliggande datamängder
Dataanalysagenter skapar ofta stora mellanliggande datamängder som är dyra att läsa in igen vid varje iteration. Tillämpa lazy loading: läs bara in de data ni behöver för det aktuella steget. Använd kolumnorienterade format som Parquet, som stöder effektiva läsningar av utvalda kolumner. För verkligt stora datamängder (100 MB eller mer) bör ni behålla en beständig kärna så att DataFrame-objektet ligger kvar i minnet mellan iterationerna i stället för att serialiseras och deserialiseras varje gång.
# Good: load only needed columns
df = pd.read_parquet('/workspace/full_data.parquet', columns=['date', 'revenue', 'region'])
# Bad: load everything even if you only need 2 columns
# df = pd.read_parquet('/workspace/full_data.parquet') # loads 50 columns you don't need
# Good: filter early before loading full dataset
df = pd.read_parquet('/workspace/full_data.parquet', filters=[('region', '=', 'EMEA')])
# For very large files, tell the LLM about data shape upfront
df_info = {'shape': (1_000_000, 50), 'size_mb': 850, 'columns': [...]}
# Include df_info in state so LLM plans accordinglySkapa kontrollpunkter för långkörande uppgifter
För uppgifter som sträcker sig över många iterationer bör ni implementera kontrollpunkter: spara regelbundet hela agentens tillstånd (slutförda steg, filer i arbetsytan och aktuella framsteg) i ett beständigt datalager, så att uppgiften kan återupptas efter ett avbrott. Detta är särskilt viktigt för långvariga dataanalysjobb som kan ta mer än 30 minuter och avbrytas av nätverksproblem, API-fel eller omstarter av servern.
import json
import time
def checkpoint_state(state: ExecutionState, checkpoint_id: str, db):
data = {
'task': state.task,
'iteration': state.iteration,
'completed_steps': state.completed_steps,
'workspace_files': state.workspace_files,
'timestamp': time.time()
}
db.execute(
'INSERT INTO agent_checkpoints (id, state) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET state=excluded.state',
(checkpoint_id, json.dumps(data))
)
print(f'Checkpoint saved at iteration {state.iteration}')
def resume_from_checkpoint(checkpoint_id: str, db) -> ExecutionState | None:
row = db.execute('SELECT state FROM agent_checkpoints WHERE id = ?', (checkpoint_id,)).fetchone()
if not row:
return None
data = json.loads(row[0])
state = ExecutionState(task=data['task'])
state.iteration = data['iteration']
state.completed_steps = data['completed_steps']
print(f'Resumed from iteration {state.iteration}')
return stateRensa tillstånd efter slutförande
Agentens arbetsytor samlar på sig filer och kan växa sig stora med tiden. Implementera alltid en rensningsfas som körs när en uppgift slutförs eller misslyckas: ta bort mellanliggande filer (cleaned.parquet, tmp_output.csv) och behåll endast de slutliga utdatafiler som användaren behöver. För molnlagring bör ni ange livscykelregler som automatiskt tar bort arbetsytefiler efter en viss lagringsperiod.
import shutil
from pathlib import Path
INTERMEDIATE_PATTERNS = ['*.parquet', 'tmp_*.csv', 'step_*.json', 'debug_*.txt']
FINAL_OUTPUT_PATTERNS = ['report.pdf', 'final_*.csv', 'summary.json']
def cleanup_workspace(workspace_dir: str, keep_final=True):
workspace = Path(workspace_dir)
final_outputs = []
if keep_final:
for pattern in FINAL_OUTPUT_PATTERNS:
final_outputs.extend(workspace.glob(pattern))
# Move final outputs to output directory
output_dir = workspace.parent / 'outputs'
output_dir.mkdir(exist_ok=True)
for f in final_outputs:
shutil.move(str(f), str(output_dir / f.name))
# Delete the workspace
shutil.rmtree(workspace_dir)
print(f'Workspace cleaned up. Kept {len(final_outputs)} output files.')
return [str(f) for f in final_outputs]Versionshantering av tillstånd för felsökning
När en kodagent ger felaktiga resultat måste ni kunna följa dess körningshistorik bakåt för att hitta var felet uppstod. Implementera versionshantering av tillstånd genom att spara en ögonblicksbild av arbetsytan efter varje iteration. Då kan ni spela upp agentens körning, granska mellanliggande tillstånd och identifiera exakt i vilken iteration felet uppstod. Spara endast diffen (ändrade filer) för att spara utrymme.
Kontextfönster kontra externt tillstånd
Vid utformning av kodagenter finns en grundläggande avvägning mellan att placera tillstånd i LLM:ens kontextfönster (omedelbart men begränsat) och i extern lagring (obegränsad men kräver uttrycklig hantering). Den optimala strategin är: behåll det aktuella stegets data i kontexten, behåll stora mellanliggande resultat i filer, behåll uppgiftens mål och övergripande plan i kontexten och lagra alltid rådata på disken. LLM:en hanterar uppgiften; filsystemet hanterar data.
Snabbkontroll
Testa era kunskaper om tillståndshantering mellan kodkörningssteg från den här lektionen.
Sammanfattning av lektionen
I den här lektionen lärde ni er att filbaserad persistens av tillstånd med Parquet och JSON är det mest portabla sättet att dela tillstånd mellan körningar av tillfälliga containrar, att beständiga kärnor eliminerar kostnaden för omladdning genom att behålla en aktiv Python-process mellan iterationerna och att infogning av tillståndskontext ger LLM:en korrekt information om tillgängliga filer och slutförda steg i varje iteration. Nästa avsnitt bygger en fullständig dataanalysagent.
Lär dig Python med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 30
- Lektioner
- 120
Vanliga frågor
Är lektionen ”Tillståndshantering mellan körningssteg” gratis?
Ja – hela texten till ”Tillståndshantering mellan körningssteg” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i AI Engineering Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i AI Engineering Academy innehåller totalt 4 lektioner.
Vad lär jag mig i ”Tillståndshantering mellan körningssteg”?
Bevara variabler, dataframes och importerade bibliotek mellan flera kodkörningssteg, så att agenten kan bygga vidare på tidigare resultat utan att köra om tidigare beräkningar. Ni övar på AI Engineering Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig AI Engineering Academy?
Du behöver inga förkunskaper. Utbildningen i AI Engineering Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 3 av 4.
Hur lång tid tar lektionen ”Tillståndshantering mellan körningssteg”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här AI Engineering Academy-lektionen?
Ja. Varje AI Engineering Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Kodkörningsloopen
- Sandboxning med Docker och RestrictedPython
- Tillståndshantering mellan körningssteg
- Bygg en dataanalysagent