AI 에이전트 작업을 위한 불변의 감사 추적 (FastAPI + async SQLAlchemy)
요약
AI 에이전트의 신뢰성을 보장하기 위한 불변의 감사 추적(Audit Trail) 설계 원칙을 다룹니다. FastAPI와 async SQLAlchemy를 사용하여 추가 전용(Append-only) 로그 시스템을 구축하고, 해시 체인을 통해 데이터 변조를 방지하는 방법을 설명합니다.
핵심 포인트
- 감사 로그는 수정/삭제가 불가능한 추가 전용(Append-only) 구조로 설계해야 함
- FastAPI의 BackgroundTasks를 활용해 로깅이 에이전트 실행을 방해하지 않도록 분리
- 비동기 환경에서는 반드시 asyncpg와 같은 비동기 DB 드라이버를 사용
- 해시 체인 기술을 적용하여 데이터베이스 수준의 변조를 방지하고 무결성 입증
규칙 0: 감사 로그는 추가 전용(append-only)이어야 한다
가장 중요한 단 하나의 설계 결정은 라이브러리가 아닌 규율입니다. 즉, 감사 테이블(audit table)은 절대 업데이트하거나 삭제해서는 안 됩니다. 기능에 의해서도, 마이그레이션(migration)에 의해서도, 혹은 서두르는 관리자에 의해서도 안 됩니다. 이벤트가 발생했으므로 행(row)은 영원히 존재합니다.
많은 ORM(Object-Relational Mapping)이 메서드 호출 한 번으로 UPDATE와 DELETE를 수행할 수 있다는 점을 깨닫기 전까지는 이 말이 당연하게 들릴 것입니다. 따라서 이 규칙은 관례와 리뷰를 통해 강제되어야 합니다. 이 코드베이스에서 감사 모듈은 정확히 하나의 작업인 append만을 노출합니다.
from datetime import datetime, timezone
from sqlalchemy import String, DateTime, JSON
from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase
...
event_type과 timestamp에 설정된 인덱스(index)에 주목하십시오. 여러분은 반드시 "이 에이전트에게 이 시간 범위 동안 어떤 일이 일어났는가"를 기준으로 쿼리(query)하게 될 것이며, 감사관이 기다리고 있을 때 수백만 개의 행을 순차 스캔(sequential scan)하고 싶지는 않을 것입니다.
규칙 1: 로그 기록이 에이전트의 경로를 방해해서는 안 된다
만약 감사 기록 작업이 핫 패스(hot path)에 있고 데이터베이스에 문제가 발생한다면, 여러분은 단지 로그 한 줄 때문에 에이전트를 중단시킨 셈이 됩니다. 이는 주객전도입니다. 로깅은 컴플라이언스(compliance)를 위해 중요하지만, 실행의 단일 장애점(single point of failure)이 되어서는 안 됩니다.
FastAPI의 BackgroundTasks는 응답이 전송된 후 기록을 지연시킬 수 있는 간단하고 의존성 없는 방법입니다:
from fastapi import APIRouter, BackgroundTasks
from sqlalchemy.ext.asyncio import AsyncSession
...
제가 시간을 허비했던 몇 가지 비동기(async) 주의사항을 언급하겠습니다:
- 비동기 앱에서는 절대 동기 드라이버를 사용하지 말고
asyncpg드라이버(postgresql+asyncpg://...)를 사용하십시오. 비동기 이벤트 루프(event loop)에 동기 DB 호출을 섞는 것은 모든 것을 차단(block)하는 전형적인 방법입니다. - 만약
DATABASE_URL이 환경 변수에서 온다면,.strip()을 사용하십시오. 대시보드 환경 변수에 붙여넣은 끝부분의 줄바꿈 문자는 모든 것 같기도 하고 아무것도 아닌 것 같기도 한 연결 오류를 발생시킵니다.
규칙 2: 단순히 추가 전용일 뿐만 아니라 변조를 입증할 수 있게 하라
추가 전용 (Append-only) 방식은 여러분 자신의 코드로부터 여러분을 보호합니다. 하지만 이 방식 자체만으로는 데이터베이스 접근 권한을 가진 누군가가 조용히 행(row)을 수정하지 않았다는 사실을 _제3자 (third party)_에게 증명할 수는 없습니다. 이를 위해서는 각 엔트리가 이전 엔트리와 암호학적으로 연결되어야 합니다. 이는 해시 체인 (hash chain)의 원리와 동일합니다.
그 기술은 다음과 같습니다. 이벤트를 추가할 때, 해당 이벤트의 내용과 이전 이벤트의 해시를 함께 해싱합니다:
import hashlib
import json
...
이 해시를 각 행에 저장합니다. 이제 추적 기록이 변조되지 않았음을 검증하려면, 처음부터 체인을 다시 계산하면 됩니다. 만약 단 하나의 행이라도 변경, 삭제 또는 순서가 바뀌었다면, 그 이후의 모든 해시가 깨지게 되며 최종 해시가 일치하지 않게 됩니다. 여러분은 저장 계층 (storage layer)을 신뢰할 필요가 없습니다. 재계산을 통해 무결성 (integrity)을 _증명_할 수 있기 때문입니다.
(현재 상태에 대해 솔직히 말씀드리자면: 위에서 설명한 추가 전용 및 비차단 (non-blocking) 설계가 현재 실행 중인 방식입니다. 해시 체이닝 (hash-chaining)은 제가 다음에 추가할 방향입니다. 만약 동일한 것을 구축하신다면 이 순서대로 진행하십시오. 규율에 의한 불변성 (immutability-by-discipline)이 체이닝을 의미 있게 만들기 때문입니다.)
이것이 화려하지 않은 핵심인 이유
아무도 감사 테이블 (audit table)을 시연하지는 않습니다. 하지만 감사 테이블은 "에이전트가 그렇게 행동했을 것이라고 생각합니다"라는 말을 "여기 순서대로 기록되어 있으며, 변조되지 않았음이 증명 가능하고, 증거로 내보낼 수 있는 기록이 있습니다"라는 말로 바꿔주는 요소입니다. 은행, 보험사, 병원 등에서 이러한 차이는 에이전트가 프로토타입에 머무느냐, 아니면 운영 환경 (production)에 투입될 수 있느냐를 결정짓는 차이입니다.
https://horkos.eu — AI 에이전트 앞에 위치하는 거버넌스 계층 (governance layer)입니다. 만약 여러분이 감사인에게 자동화된 시스템을 방어해야 했던 적이 있다면, 그들이 실제로 무엇을 요구했는지 정말 알고 싶습니다. 그것이 바로 제가 맞추고자 하는 사양 (spec)입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기