본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 24. 02:01

HECE — AI 에이전트 사고를 위한 포렌식 프로토콜

요약

AI 에이전트의 오작동 발생 시 추측이 아닌 체계적인 진단을 위해 HECE(Hypothesize, Evidence signatures, Check, Eliminate) 포렌식 프로토콜을 제안합니다. 가설 설정부터 증거 확인, 제거 단계로 이어지는 규율을 통해 에이전트의 실패 모드를 정확히 파악하는 방법을 다룹니다.

핵심 포인트

  • AI 에이전트 사고 대응 시 '느낌'이 아닌 포렌식 규율이 필요함
  • HECE 프로토콜: 가설 설정, 증거 시그니처, 확인, 제거 단계 수행
  • 모든 가능성 있는 원인을 철저하게 나열하여 테스트 대상으로 삼아야 함
  • 프롬프트 인젝션, 프레임워크 버그, 메모리 오염 등 다양한 실패 모드 고려

4월 17일 아침, 제 에이전트가 앞뒤가 맞지 않는 응답을 보내기 시작했을 때 저는 모바일 핫스팟을 사용하는 버스 안에 있었습니다. 에이전트가 하이재킹(hijacked)되었는지, 프롬프트 인젝션(prompt-injected)을 당했는지, 프레임워크 버그(framework bug)가 발생했는지, 아니면 단순히 자체적인 부하를 견디지 못하고 고장 난 것인지 판단할 방법이 없었습니다.

그 상황에서 유일하게 올바른 조치는 '봉쇄 우선(Containment-first)'이었습니다. 봇을 오프라인으로 전환하고, 신뢰할 수 있는 네트워크에 접속한 다음 진단하는 것이었습니다. 이 시리즈의 첫 번째 포스트에서 그 이야기를 다루었습니다. 이번 포스트는 제가 실제로 키보드 앞에 앉은 후에 무엇을 했는지에 관한 것입니다.

저는 추측하지 않았습니다. 저는 HECE를 수행했습니다.

가설 설정(Hypothesize). 증거 시그니처(Evidence signatures). 확인(Check). 제거(Eliminate).

화려하지는 않지만, 이번과 같은 첫 번째 사고에서는 효과적이었습니다. 이것은 프로토콜이며, 제가 제 에이전트에 실제로 실행했던 명령어들, 그것이 해결한 두 가지 잘못된 단서들, 그리고 여러분의 에이전트에서도 실행할 수 있는 체크리스트입니다.

AI 에이전트를 다룰 때 추측이 디버깅 방법이 될 수 없는 이유

제가 보는 대부분의 AI 사고 대응은 '느낌(vibes)'에 의존합니다. 에이전트가 이상한 행동을 하면, 개발자는 추측하고, 그 추측을 바탕으로 패치를 적용합니다. 그러면 증상이 나중에 다른 형태로 다시 나타나거나, 나타나지 않기 때문에 개발자는 자신의 추측이 맞았다고 결론짓습니다. 두 결과 모두 진단(diagnosis)은 아닙니다.

진단적인 질문은 "증상에 부합하는 이야기가 무엇인가?"가 아닙니다. "각 후보 원인이 어떤 증거를 남길 것이며, 실제로 존재하는 증거는 무엇인가?"입니다. 다른 모든 사고와 마찬가지로 동일한 포렌식 규율(forensic discipline)이 필요합니다. 실패 모드(failure modes)가 더 새롭다고 해서 에이전트라고 해서 예외는 아닙니다.

HECE는 실제 장애 상황에서도 살아남을 수 있는, 제가 발견한 가장 단순한 형태의 규율입니다.

1단계 — 가설 설정 (Hypothesize)

가능하다고 생각되는 모든 원인을 적으십시오. 가능성이 낮다고 생각되는 것까지 포함해야 합니다. 이 단계의 목적은 정답을 맞히는 것이 아닙니다. 다음 단계에서 테스트할 대상이 있도록 철저하게(exhaustive) 나열하는 것이 목적입니다.

4월 17일 사고의 경우:

  1. 계정 탈취 (Account compromise) — 누군가가 나를 사칭하여 내 에이전트와 대화하고 있음
  2. 프롬프트 인젝션 (Prompt injection) — RSS 피드, 웹 페치 (web fetch), 또는 메시지 콘텐츠를 통해 악성 페이로드 (malicious payload)가 유입됨
  3. 프레임워크 버그 (Framework bug) — python-telegram-bot, LiteLLM, 또는 다른 의존성 (dep) 라이브러리가 잘못 동작함
  4. 의존성 저하 (Dependency degradation) — Ollama, SearXNG, 또는 다른 서비스가 오작동함
  5. 웹훅 하이재킹 (Webhook hijack) — Telegram이 다른 사람의 엔드포인트 (endpoint)로 라우팅함
  6. 메모리 오염 (Memory poisoning) — 에이전트가 잘못된 사실을 회상하고 이를 전파함

여섯 가지 가설. 네 가지는 틀린 것으로 판명되었습니다. 두 가지가 핵심적인 역할을 했습니다.

완전성에 관한 참고 사항: 당신이 생각하기에 어리석은 가설이라도 포함시키세요. 멍청해 보이는 방향이야말로 외부 독자가 지적했을 법하지만 당신이 놓쳤던 바로 그 지점입니다. 이번 과정에서 저를 놀라게 했던 두 가지는 메모리 오염 (실제로 발생한 방식대로 기록된 것을 본 적이 없었습니다)과 의존성으로 인한 다른 모델로의 폴백 (fallback) (의도적으로 설정해 두었지만 그 _실패 모드 (failure mode)_에 대해서는 모델링하지 않았었습니다)이었습니다.

2단계 — 증거 시그니처 (Evidence signatures)

각 가설에 대해, 만약 그 가설이 사실이라면 어떤 증거가 남을지 적어보세요. 구체적이어야 합니다.

가설사실일 경우, 다음과 같은 현상이 나타날 것으로 예상됨
계정 탈취 (Account compromise)auth 로그의 새로운 user_id, 익숙하지 않은 IP에서의 요청, 로그인 이벤트
...
이 표의 목적은 다음 단계를 기계적으로 만드는 것입니다. 로그를 멍하니 바라보며 이야기가 튀어나오기를 기대하는 것이 아닙니다. 당신은 _특정한 형태 (specific shapes)_를 찾는 것입니다.

3단계 — 확인 (Check)

명령어를 실행하세요. 해석 단계로 건너뛰지 마세요. 수집한 다음 읽으세요.

TONY (Ubuntu, SQLite, systemd, Telegram bot)에서 실행한 구체적인 명령어들:

계정 탈취 (Account compromise):

sqlite3 data/nexus.db "SELECT DISTINCT user_id FROM conversations \\
  WHERE timestamp > datetime('now', '-7 days');"

하나의 user_id (내 것)가 발견되었습니다. 가설 1을 기각합니다.

프롬프트 인젝션 (Prompt injection):

sqlite3 data/nexus.db "SELECT content FROM conversations \\
  WHERE timestamp BETWEEN '2026-04-17 06:00' AND '2026-04-17 08:00' \\
  AND role = 'user';"

조작된 페이로드(payloads)는 없었습니다. 그저 저의 일반적인 질문들이었습니다. 가설 2를 기각했습니다.

프레임워크 버그 (Framework bug):

journalctl -u nexus.service --since "2026-04-17 06:00" \
  --until "2026-04-17 08:00" | grep -iE "traceback|exception|error"

장애 발생 시간대 내에 스택 트레이스(stack traces)가 없었습니다. 가설 3을 기각했습니다.

의존성 저하 (Dependency degradation):

journalctl -u nexus.service --since "2026-04-17 06:00" | \
  grep -iE "fallback|timeout|connection"

이 부분에서 신호가 포착되었습니다. 다음과 같은 라인들이 나타났습니다:

WARNING: LLM call failed with 'ollama' provider, falling back to 'anthropic':
  litellm.Timeout: Connection timed out after 60.0 seconds

장애 발생 시간대 내의 모든 오케스트레이터(orchestrator) 호출에서 이 패턴이 발견되었습니다.

웹훅 하이재킹 (Webhook hijack):

curl -s "https://api.telegram.org/bot${TOKEN}/getWebhookInfo" | jq .

URL이 저의 Caddy 엔드포인트와 일치했습니다. 가설 5를 기각했습니다.

메모리 포이즈닝 (Memory poisoning):

sqlite3 data/nexus.db "SELECT id, category, source, content FROM memories \
  WHERE created_at BETWEEN '2026-04-17 06:00' AND '2026-04-17 08:00';"

다음과 같은 행들이 있었습니다:

499|fact|summary|Claude Mythos is not a real AI model or cybersecurity system

모델이 생성한 단언(assertion)이 source=summary와 함께 category=fact로 저장되어 있었습니다. 가설 6이 부분적으로 확인되었습니다.

4단계 — 제거

증거에 의해 뒷받침되지 않는 모든 가설을 목록에서 지우십시오. 남은 것이 바로 실제 진단 결과입니다.

네 개의 가설이 기각되었습니다. 두 개가 생존했습니다:

  • 의존성 저하 (Ollama 타임아웃 발생, 모든 호출이 Anthropic으로 전환됨)
  • 메모리 포이즈닝 (출처(provenance) 없이 사실로 저장된 모델의 단언들)

그리고 HECE가 실제로 존재하는 이유가 드러납니다. 이 두 가지는 별개의 것이 아닙니다. 두 개의 계층에서 발생한 동일한 사건입니다. Ollama가 작동을 멈추자 모든 오케스트레이터 호출이 시스템이 신뢰하도록 설정된 클라우드 모델로 향했고, 클라우드 모델은 거짓된 내용을 확신을 가지고 단언했습니다. 요약(summarization) 계층은 그 단언을 [fact]로 메모리에 기록했고, 이후의 세션들이 이를 기저 사실(ground truth)로 읽어 들인 것입니다.

Ollama-timeout 수정만으로는 데이터베이스에 오염된 메모리 행(memory rows)이 그대로 남아 있었을 것이며, 다음 새로운 세션에서도 여전히 환각(hallucination)이 재현되었을 것입니다. 이 2계층 관점(two-layer view) 덕분에 두 번째 수정 사항이 명확해졌습니다.

HECE가 나를 빠져나가게 해준 두 가지 잘못된 단서

이 부분에 대해 구체적으로 말씀드리고 싶은데, 프로토콜의 가치는 당신이 무엇을 쫓지 않게 만들어 주느냐에 달려 있기 때문입니다.

첫 번째 잘못된 단서: 계정 탈취 (account compromise). 저의 첫 번째 본능은 하이재킹(hijack)이었습니다. 저는 공개 엔드포인트(public endpoint)에 Telegram 봇을 띄워 놓았고, 수상한 네트워크에 접속해 있었으며, 응답은 터무니없었습니다. 모든 레드팀(red-team)적 반사 신경이 "누군가 침입했다"라고 말하고 있었습니다. 하지만 3단계(Step 3)를 수행하는 데는 30초밖에 걸리지 않았고, 문제는 완전히 종결되었습니다. 제 에이전트의 인증 로그(auth logs)에는 정확히 하나의 user_id만 존재하며, 그것은 바로 제 것이었습니다.

두 번째 잘못된 단서: 프레임워크 버그 (framework bug). 저의 두 번째 본능은 최근의 의존성 업데이트(dependency bump)로 인해 python-telegram-bot이나 LiteLLM 중 무언가가 고장 났을 것이라는 생각이었습니다. 3단계를 수행하는 데는 2분이 걸렸습니다 — 사고 발생 시간대에 대해 journalctl | grep traceback을 실행했습니다 — 결과는 예외(exception)가 없었습니다. 무슨 일이 일어나고 있었든 간에, 코드 경로(code paths)는 충돌(crash) 없이 완료되고 있었습니다. 단지 잘못된 방식으로 완료되고 있었을 뿐입니다.

만약 제가 이 사실을 확인하는 대신 첫 번째로 그럴듯해 보이는 시나리오를 따라갔다면, 두 가지 잘못된 단서 모두에 수 시간을 허비했을 것입니다.

당신의 에이전트에 직접 실행해 볼 수 있는 체크리스트

만약 당신이 프로덕션(production) 환경에서 에이전트를 운영 중이고, 사고 발생 시 1시간 이내에 HECE를 적용하고 싶다면, 지금 바로 다음 사항들을 갖추어 놓으십시오:

  • 타임스탬프(timestamps), user_id, 역할(role)이 포함된 대화 로그(Conversation log). SQLite도 괜찮습니다. 다만, 요약(summarization) 과정에서 원본 기록(raw history)을 잃어버리지 마십시오.
  • 호출별 제공자 로깅(Per-call provider logging). 모든 LLM 호출 시, 단순히 어떤 모델이 요청되었는지가 아니라 실제로 어떤 제공자/모델이 응답했는지를 기록해야 합니다. (TONY의 사고 당시 agent_logs.model_used 컬럼은 비어 있었습니다. 그런 실수를 반복하지 마십시오.)
  • 구조화된 journald 출력. JSON 포맷터를 사용한 표준 라이브러리(stdlib) 로깅을 적용하십시오. journalctl | grep이 당신의 포렌식 기질(forensic substrate)이 될 것입니다.
  • 소스(source) 필드가 포함된 메모리 행(Memory rows). 해당 필드가 "summary"이든 "manual"이든, 필터링할 수 있는 무언가가 반드시 필요합니다.
  • getWebhookInfo 및 그에 상응하는 제어 평면(control-plane) 확인 명령어를 북마크해 둘 것. 장애(outage)가 발생했을 때 자신의 웹훅(webhook)을 어떻게 검증할지 고민하고 있어서는 안 됩니다.
  • 압박 상황에서도 작동하는 DB 스냅샷 절차. 저의 경우 sqlite3 data/nexus.db ".backup data/snapshots/$(date -u +%Y%m%dT%H%M%SZ).db"를 사용합니다. 실제로 필요해지기 전에 연습해 두십시오.

이 중 하나라도 누락된다면, 당신은 진단할 수 없습니다. 오직 추측만 할 수 있을 뿐입니다.

HECE가 작동하지 않는 경우

HECE는 증거가 존재한다는 사실에 의존합니다. 만약 당신의 에이전트가 가설을 구분할 수 있는 정보들을 로깅하고 있지 않다면, 확인(Check) 단계는 빈 상태가 되며 당신은 다시 '감(vibes)'에 의존하는 상태로 돌아가게 됩니다.

이것이 바로 계측(instrumentation)이 사고 발생 후가 아니라, 사고 발생 전에 완료되어야 하는 이유입니다. HECE 프로토콜은 그 아래에 깔린 기질(substrate)만큼만 유효합니다. 제가 조사했던 몇몇 빌더 에이전트들(제 에이전트 포함)에서 나타난 지배적인 실패 모드는 포렌식 맹목(forensic blindness)이었습니다. 즉, 에이전트가 잘못된 동작을 수행했음에도 어떤 서브시스템(subsystem)이 이를 수행했는지 구분할 수 있는 로그가 없는 상태를 말합니다. 표본은 적지만, 일관된 형태를 보입니다.

이 글을 읽고 있는 당신의 에이전트가 포렌식 맹목 상태라면, 이번 주에 할 수 있는 가장 영향력 있는 한 시간의 작업은 model_used를 추가하고 구조화된 journald 포맷터를 도입하는 것입니다. 자정 무렵, 테더링(hotspot)을 사용하며 고군분투할 미래의 당신이 당신에게 감사할 것입니다.

동반 포스트

아키텍처 포스트 — 첫 번째 포스트의 댓글 스레드가 제 수정 사항을 재구성한 이후, 제가 메모리 레이어 (memory layer)를 중심으로 재구축하고 있는 내용 — 는 이 포스트의 동반 포스트입니다. 해당 스레드에서 나온 사용 시점 게이팅 (use-time gating) 아이디어 — 즉, 쓰기 시점에 프로모션 (promoting at write) 하는 것만으로는 충분하지 않으며, 소비자 (consumers)가 행동하기 전에 출처 (provenance) 를 확인해야 한다는 아이디어 — 가 v2의 중추입니다.

포스트 링크는 여기 있습니다 ->

만약 여러분이 HECE나 그와 유사한 것을 자신의 에이전트 (agent) 에 사용해 보았고 프로토콜이 어딘가에서 무너졌다면, 어느 지점이었는지 듣고 싶습니다. 댓글, 답글, 또는 DM을 주세요 — 도전적인 피드백이 단순한 긍정보다 더 큰 도움이 되므로, 사정을 봐주지 말고 말씀해 주세요.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0