본문 바로가기
C.W.K.
Stream
Lesson 04 of 05 · published

에이전트 행동은 몇 달 뒤에도 재구성할 수 있어야 해

~12 min · audit, logging, compliance

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

누가 무엇을 왜 실행했는지 남겨

실행마다 사용자나 시스템 촉발자, 시간, 프롬프트, 도구 이름과 인자, 도구 결과, 최종 답, 사람이 내린 권한 결정을 기록해. 이 조각들이 있어야 나중에 “무슨 일이 있었나”를 추측이 아니라 시간순으로 답할 수 있어.

훅이 자연스러운 기록 지점이야

PostToolUse 훅은 도구 호출마다 구조화된 한 줄을 남기고, UserPromptSubmit은 시작 원인을, Stop은 최종 요약을 기록할 수 있어. 모든 줄에 세션 ID를 넣으면 서로 다른 사건을 한 실행 타임라인으로 묶을 수 있어.

보존 기간과 저장 층을 미리 정해

감사 기록은 계속 커지므로 사업과 규제 요구에 따라 30·90·365일 같은 정책을 정해. 최근 N일은 빠른 저장소에서 조회하고, 여러 해 보존할 자료는 S3 Glacier나 GCS Coldline 같은 저비용 층으로 옮길 수 있어. 무기한 보관도 즉시 삭제도 기본값으로 두지 마.

원칙: 감사 흔적 없는 행동은 지문 없는 사고와 같아. 멋진 기능보다 기록 경계를 먼저 만들어.

Code

Structured audit logger·python
import json, time, uuid
from claude_agent_sdk import ClaudeAgentOptions

class Audit:
    def __init__(self, path: str, run_id: str | None = None):
        self.path = path
        self.run_id = run_id or str(uuid.uuid4())

    def emit(self, event_type: str, **fields):
        line = {"ts": time.time(), "run_id": self.run_id, "event": event_type, **fields}
        with open(self.path, "a") as f:
            f.write(json.dumps(line) + "\n")

async def make_hooks(audit: Audit):
    async def on_user_prompt(context):
        audit.emit("user_prompt", text_hash=hash(context.prompt))
        return HookOutput(allow=True)

    async def on_tool_post(context):
        audit.emit(
            "tool_use",
            tool=context.tool_name,
            input_keys=sorted(context.tool_input.keys()),
            duration_ms=context.duration_ms,
            error=context.is_error,
        )

    return {"UserPromptSubmit": [on_user_prompt], "PostToolUse": [on_tool_post]}
이벤트를 세션 timeline에 엮기·python
import json
from collections import defaultdict

def session_timeline(audit_path: str):
    runs = defaultdict(list)
    for line in open(audit_path):
        ev = json.loads(line)
        runs[ev["run_id"]].append(ev)
    for run_id, events in runs.items():
        events.sort(key=lambda e: e["ts"])
        print(f"\n== run {run_id} ({len(events)} events) ==")
        for e in events:
            print(f"  {e['ts']:.3f}  {e['event']}")

External links

Exercise

에이전트 하나에 감사 기록기를 만들고 세션을 실행해. 지난 한 시간의 도구 호출 수와 도구 이름별 횟수를 답하는 질의도 써.
Hint
텍스트 한 덩어리 로그라면 JSONL로 바꿔야 관측 질문을 싸게 처리할 수 있어.

Progress

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

댓글 0

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

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