상태 관리와 메모리 시스템
컨텍스트 윈도우 전략, 단기/장기 메모리, 체크포인트/복원, state reducer 패턴
오케스트레이션에서 가장 자주 섞이는 두 개념이 state와 memory입니다. state는 현재 실행을 복구하기 위한 기록이고, memory는 미래 실행에서 재사용할 정보입니다. 이 둘을 구분하지 않으면 디버깅, 개인정보 관리, 품질 개선이 모두 어려워집니다.
상태와 메모리의 차이
| 항목 | State | Memory |
|---|---|---|
| 목적 | 현재 run의 진행 상황 복구 | 이후 run에서 정보 재사용 |
| 수명 | 실행 종료까지 또는 checkpoint 보관 기간 | 정책에 따라 장기 보존 |
| 예시 | 현재 단계, retry count, approval status | 사용자 선호, FAQ 요약, 조직 지식 |
| 설계 기준 | 재시작 가능성, 일관성 | 검색 가능성, 최신성, 삭제 정책 |
권장 레이어
| 레이어 | 포함 데이터 | 주의점 |
|---|---|---|
| Execution State | current step, status, task ids | workflow 엔진과 함께 관리 |
| Working Memory | 현재 작업의 중간 요약 | 실행이 끝나면 대부분 버림 |
| Semantic Memory | 재사용 가능한 사실 요약 | source와 updatedAt 저장 |
| Audit Log | 누가 무엇을 했는지 | 수정 불가능한 형태 선호 |
상태·메모리 레이어 구조
체크포인트 전략
장기 실행 시스템에서는 모든 step 뒤에 checkpoint를 만들 필요는 없지만, 다음 순간에는 만드는 편이 좋습니다.
- 외부 write 직전
- 사람 승인 대기 직전
- fan-out 후 fan-in 직전
- 비용이 큰 조사/생성 단계 직후
이 지점의 목적은 "언제든 재개"가 아니라 비싼 단계를 다시 하지 않기 위한 복구 포인트 확보입니다.
state reducer 패턴
state는 대화 로그 누적으로 관리하기보다 reducer 형태가 안정적입니다.
type RunState = {
status: 'queued' | 'running' | 'waiting_approval' | 'completed' | 'failed'
summary: string
evidenceRefs: string[]
retryCount: number
}
function reduceState(state: RunState, event: { type: string; payload?: unknown }): RunState {
switch (event.type) {
case 'STEP_COMPLETED':
return { ...state, status: 'running' }
case 'APPROVAL_REQUIRED':
return { ...state, status: 'waiting_approval' }
case 'RETRY':
return { ...state, retryCount: state.retryCount + 1 }
case 'FAILED':
return { ...state, status: 'failed' }
default:
return state
}
}이 방식은 상태 전이가 명시적이어서, trace와 재현성이 좋아집니다.
상태 전이 다이어그램
메모리 저장 규칙
모든 실행 결과를 장기 메모리로 올리면 안 됩니다. 아래 기준으로 승격 여부를 결정합니다.
| 질문 | Yes면 memory 후보 |
|---|---|
| 다음 실행에서도 반복적으로 쓸 정보인가 | 예 |
| 출처와 갱신 시점을 추적할 수 있는가 | 예 |
| 사용자 삭제 요청에 대응 가능한가 | 예 |
| 최신성 검증 없이 오래 보관해도 되는가 | 아니오면 재검토 |
컨텍스트 윈도우 전략
최근 대화만 늘리는 방식은 오래 버티지 못한다
컨텍스트 윈도우가 커져도, 중요한 정보가 buried state가 되면 품질은 떨어집니다. 따라서 "더 많이 넣기"보다 "더 잘 선택하기"가 중요합니다.
추천 전략
- 현재 단계에 필요한 working set만 직접 주입
- 나머지는 retrieval로 필요 시 가져오기
- 장기 메모리는 source와 freshness를 같이 저장
- 오래된 요약은 주기적으로 재압축
개인정보와 보존 정책
메모리 설계는 곧 데이터 거버넌스 설계입니다.
- PII는 장기 메모리에 직접 저장하지 않는 편이 안전합니다.
- 저장이 필요하면 masking 또는 surrogate key를 사용합니다.
- memory 레코드마다
source,owner,ttl,deletePolicy를 둡니다.
실패 모드
| 실패 모드 | 원인 | 대응 |
|---|---|---|
| 오래된 메모리로 잘못 답변 | freshness 메타데이터 부재 | updatedAt와 revalidation 정책 추가 |
| 재실행 시 중복 write | checkpoint가 side effect 뒤에만 존재 | write 직전/직후 상태 저장 |
| state가 대화 로그와 섞임 | 저장 목적이 불명확 | execution state와 memory store 분리 |
| 메모리 오염 | 저품질 요약을 무차별 저장 | 승격 기준과 evaluator 추가 |
ADR 스타일 결론
Decision
state는 현재 실행을 복구하기 위한 최소 기록으로, memory는 미래 실행에 재사용할 검증 가능한 사실로 분리합니다. checkpoint는 비용이 큰 단계와 side effect 경계에 두고, 장기 메모리는 source와 freshness 메타데이터가 있을 때만 저장합니다.
실무 체크리스트
- state와 memory 저장소가 분리되어 있는가
- checkpoint 지점이 side effect와 승인 경계에 맞춰져 있는가
- 장기 메모리에 source와 updatedAt가 있는가
- working memory를 실행 종료 후 정리하는 정책이 있는가
- 개인정보 삭제 요청을 메모리 시스템이 처리할 수 있는가