실행 단계 간 상태 관리
여러 코드 실행 단계에 걸쳐 변수, 데이터 프레임, 가져온 라이브러리를 유지하여 에이전트가 이전 계산을 다시 실행하지 않고도 앞선 결과를 기반으로 작업하게 합니다.
실행 단계 간 상태 관리은(는) CoddyKit의 무료 AI Engineering Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Engineering Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
무상태성 문제
각 도커 컨테이너 실행은 새로운 파이썬 인터프리터로 시작됩니다. 1번째 반복에서 정의한 변수는 2번째 반복에는 존재하지 않습니다. 이러한 무상태성 때문에 에이전트는 각 실행 단계마다 모든 것을 처음부터 다시 계산하거나 다시 로드해야 합니다. 반복 작업 사이에 상태를 유지하고 복원하는 명시적인 상태 관리 전략을 구현하지 않는 한 말입니다. 이를 구현하지 않으면 여러 단계로 이루어진 분석은 불가능합니다.
# 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!파일 기반 상태 유지
가장 간단하고 이식성이 높은 방법은 상태를 파일에 저장하는 것입니다. 각 코드 블록이 끝날 때 에이전트는 DataFrame, 사전 또는 기타 객체를 작업 공간 디렉터리의 파일에 저장합니다. 다음 반복에서는 이를 다시 로드합니다. Parquet는 DataFrame에, JSON은 사전에, pickle은 임의의 파이썬 객체에 적합합니다. 다만 신뢰할 수 없는 코드에서 생성된 pickle은 보안 위험이 있습니다.
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)에이전트에게 파일 기반 상태 가르치기
인프라에 파일 기반 상태만 구현해서는 충분하지 않습니다. LLM도 이 규칙을 알고 일관되게 사용해야 합니다. 시스템 프롬프트에 다음과 같은 명시적 지침을 포함하십시오. 나중에 필요할 결과를 계산한 후에는 항상 설명적인 파일 이름으로 저장하고, 각 반복을 시작할 때는 진행하기 전에 이전 단계에서 생성된 파일을 항상 로드하십시오.
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/'))
'''지속적인 파이썬 프로세스를 사용한 커널 기반 상태
파일 기반 상태의 대안은 반복 작업 사이에 지속적인 파이썬 프로세스(예: 주피터 커널)를 실행 상태로 유지하고 각 코드 블록을 exec()로 주입하는 것입니다. 한 반복에서 정의한 변수는 다음 반복에서도 계속 사용할 수 있습니다. 이 방법은 더 빠르고 자연스럽지만, 일시적인 컨테이너 대신 에이전트 세션마다 지속적인 프로세스를 실행해야 합니다.
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()상태 객체를 사용한 상태 관리
샌드박스 외부의 에이전트 오케스트레이터 계층에서는 에이전트 작업의 현재 상태를 추적하는 명시적인 상태 객체를 유지하십시오. 여기에는 생성된 파일, 계산된 항목, 에이전트가 현재 수행 중인 단계, 최종 목표가 포함됩니다. 각 반복마다 이 상태 객체의 요약을 LLM에 전달하여, LLM이 전체 작업에서 현재 위치를 항상 파악하도록 하십시오.
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 += 1프롬프트에 상태 컨텍스트 주입하기
다음 코드 블록을 요청하기 위해 LLM을 호출할 때마다 현재 상태 컨텍스트를 사용자 메시지에 주입하십시오. 그러면 LLM은 사용 가능한 파일, 이미 계산된 항목, 아직 수행해야 할 작업에 대한 정확한 정보를 얻습니다. 이러한 컨텍스트가 없으면 LLM이 이미 존재하는 파일을 다시 만들려고 하거나 이미 완료된 단계를 건너뛸 수 있습니다.
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)대용량 중간 데이터 처리
데이터 분석 에이전트는 각 반복마다 다시 로드하는 데 많은 비용이 드는 대용량 중간 데이터 세트를 자주 생성합니다. 지연 로딩을 적용하여 현재 단계에 필요한 데이터만 로드하십시오. 필요한 열만 효율적으로 읽을 수 있는 Parquet와 같은 열 기반 형식을 사용하십시오. 정말 큰 데이터 세트(100MB 이상)의 경우에는 지속적인 커널을 유지하여 반복 작업 사이에 DataFrame을 메모리에 보관하십시오. 그러면 매번 직렬화하고 역직렬화할 필요가 없습니다.
# 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 accordingly장시간 실행 작업의 체크포인트 저장
여러 반복에 걸쳐 진행되는 작업에는 체크포인트 저장을 구현하십시오. 완료된 단계, 작업 공간 파일, 현재 진행 상황을 포함한 전체 에이전트 상태를 주기적으로 내구성 있는 저장소에 저장하면 중단 후 작업을 재개할 수 있습니다. 네트워크 문제, API 오류 또는 서버 재시작으로 인해 중단될 수 있는 30분 이상의 장시간 데이터 분석 작업에서는 특히 중요합니다.
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 state완료 후 상태 정리
에이전트 작업 공간에는 파일이 계속 쌓여 시간이 지날수록 커질 수 있습니다. 작업이 완료되거나 실패할 때 실행되는 정리 단계를 항상 구현하십시오. 중간 파일(cleaned.parquet, tmp_output.csv)을 삭제하고 사용자가 필요로 하는 최종 출력 파일만 남기십시오. 클라우드 저장소에서는 보존 기간이 지나면 작업 공간 파일을 자동으로 삭제하는 수명 주기 정책을 설정하십시오.
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]디버깅을 위한 상태 버전 관리
코드 에이전트가 잘못된 결과를 생성하면 실행 기록을 거슬러 올라가 어디에서 문제가 발생했는지 찾아야 합니다. 각 반복 후 작업 공간의 스냅샷을 저장하여 상태 버전 관리를 구현하십시오. 그러면 에이전트의 실행을 재현하고 중간 상태를 검사하며 오류가 발생한 정확한 반복을 확인할 수 있습니다. 공간을 절약하려면 변경된 파일만 차이로 저장하십시오.
컨텍스트 창과 외부 상태
코드 에이전트 설계에는 상태를 LLM 컨텍스트 창(즉시 사용할 수 있지만 제한적)에 둘지, 외부 저장소(제한이 없지만 명시적인 관리가 필요함)에 둘지에 대한 근본적인 상충 관계가 있습니다. 최적의 전략은 현재 단계의 데이터를 컨텍스트에 두고, 큰 중간 결과는 파일에 저장하며, 작업 목표와 상위 수준 계획은 컨텍스트에 두고, 원시 데이터는 항상 디스크에 보관하는 것입니다. LLM은 작업을 관리하고 파일 시스템은 데이터를 관리합니다.
빠른 확인
이 단원에서 배운 코드 실행 단계 전반의 상태 관리에 대한 이해도를 확인해 보십시오.
단원 요약
이 단원에서는 다음을 배웠습니다. Parquet와 JSON을 사용하는 파일 기반 상태 유지는 일시적인 컨테이너 실행 사이에서 상태를 공유하는 가장 이식성 높은 방법입니다. 지속적인 커널은 반복 작업 사이에 실행 중인 파이썬 프로세스를 유지하여 다시 로드하는 오버헤드를 없앱니다. 또한 상태 컨텍스트 주입은 각 반복에서 사용 가능한 파일과 완료된 단계에 대한 정확한 정보를 LLM에 제공합니다. 다음에서는 완전한 데이터 분석 에이전트를 구축합니다.
자주 묻는 질문
“실행 단계 간 상태 관리” 강의는 무료인가요?
네 — “실행 단계 간 상태 관리” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Engineering Academy 강의 전체를 잠금 해제할 수 있습니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“실행 단계 간 상태 관리”에서 뭘 배우나요?
여러 코드 실행 단계에 걸쳐 변수, 데이터 프레임, 가져온 라이브러리를 유지하여 에이전트가 이전 계산을 다시 실행하지 않고도 앞선 결과를 기반으로 작업하게 합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Engineering Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
AI Engineering Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 AI Engineering Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“실행 단계 간 상태 관리” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 AI Engineering Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 AI Engineering Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.