본문 바로가기
C.W.K.
Stream
Lesson 07 of 08 · published

세션은 이어 가고 JSONL은 다시 세울 근거를 남겨

~16 min · sessions, resume, jsonl, persistence

Level 0Observer
0 XP0/64 lessons0/13 achievements
0/150 XP to next level150 XP to go0% complete

세션 ID가 대화 상태를 잇는 열쇠야

Agent SDK는 세션 ID를 발급하고 대화 상태를 내부에 보관해. ClaudeSDKClient의 세션은 클라이언트 수명과 함께 가고 query()는 한 번 쓰고 끝나. 같은 ID로 재개하면 저장된 상태를 불러와 새 프롬프트부터 이어 갈 수 있어.

JSONL을 내구성 있는 기준 기록으로 둬

SDK가 내놓는 메시지를 도착하는 순간 네 코드가 JSONL에 적으면, 어시스턴트 텍스트와 도구 호출·도구 결과·스트림 델타·최종 결과가 타입을 가진 한 줄씩 남아. cwkPippa는 이 파일을 기준으로 삼고 SQLite와 ChromaDB는 다시 만들 수 있는 파생 색인으로 둬. 불일치가 생기면 부분 수정으로 꿰매지 않고 지운 뒤 원본을 재생해.

프로세스가 바뀌어도 같은 세션을 되살려

작업자 프로세스가 재시작되면 저장해 둔 세션 ID를 resume에 넣고 새 ClaudeSDKClient를 만들어. 그러면 CLI가 그 세션을 다시 불러와. cwkPippa는 2026-05-14에 반대쪽 길을 골랐어. CLI 세션 재개를 그만두고, 턴마다 자기 기록에서 대화 전체를 새 클라이언트에 다시 넣어. 그래서 재시작해도 JSONL에 있는 건 하나도 안 잃어. 둘 다 정당한 길이야. 하나를 고르고, 뭘 골랐는지 알고 있으면 돼.

원칙: JSONL을 유일한 기준 기록으로 두고 DB 행과 색인은 언제든 다시 만들 수 있게 해.

Code

SDK 메시지를 보여 주기 전에 전부 JSONL에 적기·python
import dataclasses, json, pathlib
from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient

def to_record(message) -> dict:
    # SDK 메시지는 dataclass야(.dict()는 없어). asdict()가 안쪽 블록까지 따라가.
    return {"type": type(message).__name__, **dataclasses.asdict(message)}

async def run_with_jsonl(conv_id: str, prompt: str, forward_to_user):
    log_path = pathlib.Path(f"/var/lib/myagent/sessions/{conv_id}.jsonl")
    log_path.parent.mkdir(parents=True, exist_ok=True)
    options = ClaudeAgentOptions(cwd="/srv", include_partial_messages=True)  # 델타는 StreamEvent로 와
    with log_path.open("a", buffering=1) as log:  # line-buffered
        async with ClaudeSDKClient(options=options) as client:
            await client.query(prompt)
            async for message in client.receive_response():
                log.write(json.dumps(to_record(message), default=str) + "\n")
                log.flush()  # write before show
                forward_to_user(message)
저장한 id로 세션 resume·python
from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient

# ResultMessage(그리고 스트림으로 온 메시지)마다 CLI 세션 id가 실려 와.
# 대화 옆에 저장해 둬. 여기선 지난 턴 결과에서 꺼냈어.
session_id = last_result.session_id

options = ClaudeAgentOptions(cwd="/srv", resume=session_id)  # CLI가 그 세션을 다시 불러와
async with ClaudeSDKClient(options=options) as client:
    await client.query("Continue where we left off.")
    async for message in client.receive_response():
        print(message)

External links

Exercise

Agent SDK 호출 하나를 partial message를 켠 채 JSONL에 남겨. 스트림 중간에 프로세스를 끊어. 저장한 세션 ID를 resume에 넣고 다시 시작해서 대화를 이어 가고, 죽기 전에 흘러나온 델타가 JSONL에 전부 있는지 확인해.
Hint
하류 저장소와 JSONL이 다르면 JSONL을 믿고 파생 상태를 다시 만들어.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인 — 댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.