To Every Agent Its Own Database

Author: Joe Reis | Source: joereis.substack.com | Published: 2026-07-22


한 줄 요약

“데이터 웨어하우스를 에이전트에 맞게 준비한다”는 접근이 아키텍처의 틀린 끝에서 시작한다고 보고 문제를 뒤집어, 각 에이전트에 임베디드 분석 엔진을 주고 immutable slice를 P2P로 교환하는 참조 아키텍처를 실제 코드로 만든 실험.

핵심 주장/내용

  • 웨어하우스는 다른 워크로드 가정 위에 설계되었다: 전통적 웨어하우스·레이크하우스 운영 모델은 사람 속도의 분석 소비(예약 리포트, 대시보드, 탐색 쿼리) 중심으로 조직되었다. 사람은 매출이 하룻밤에 40% 떨어지면 멈춰서 비즈니스가 바뀐 건지 데이터가 깨진 건지 묻고, 회의·문서·Slack·스프레드시트에서 온 암묵적 컨텍스트를 지니고 있다
    • 에이전트는 다른 워크로드다. 수가 많고 고도로 동시적이며 fan-out하기 쉽다. 요청 하나가 여러 에이전트를 촉발하고 각자 추가 작업을 만든다. 출력이 밀리초 안에 다운스트림 계산으로 넘어가 중간 결과를 검사할 시간이 거의 없다. 그리고 신선도·시맨틱 의도·비정상성을 명시적으로 표현하지 않으면 추론하리라 기대할 수 없다 — 사람 분석가는 회계팀이 회의에서 명확히 해줘서 “revenue”가 환불 차감 후라는 걸 알지만, 에이전트가 같은 제도적 지식을 일관되게 복원·적용하리라 신뢰할 수 없다
    • 결과적 아키텍처 압력: 더 높은 쿼리 동시성, 투기적·중복 작업, 중간 결과의 빠른 연쇄, 에러의 빠른 전파, 기계 검증 가능한 시맨틱·provenance·신선도·신뢰에 대한 강한 요구
  • 각 에이전트가 임베디드 DB를 갖는다: 프로토타입에서 각 에이전트(= peer)가 자기 DuckDB 인스턴스, 로컬 캐시, mesh 접근, 선택적으로 system of record 직접 연결을 갖는다. 대부분의 peer는 웨어하우스와 직접 통신하지 않고 다른 peer에서 immutable 분석 slice를 가져온다. Quack이 각 DuckDB를 loopback TCP 포트로 노출해 요청이 별도 DuckDB 프로세스 사이의 실제 소켓을 타고 간다. 모든 교환이 통과해야 하는 필수 중앙 데이터 서버가 없고 각 holder가 자기가 소유한 slice를 서빙
    • 에이전트의 역할이 바뀐다 — 더 이상 데이터 플랫폼의 클라이언트일 뿐이 아니라 분석 데이터를 보유하고 peer에게 서빙하고 보유한 것에서 새 제품을 파생할 수 있다. Data Mesh 용어로 소유권이 더 구체적이 된다 — 도메인이 공유 웨어하우스 안의 스키마를 “소유”하는 데 그치지 않고 자기 제품을 물리적으로 보유·서빙하는 엔진을 운영할 수 있다
    • 에이전트 자체는 disposable로 남는다 — 생성되고, 작업하고, durable 결과를 넘기고, 회수된다. DuckDB 프로세스와 포트는 사라지지만 만든 제품은 DuckLake·Parquet 등 durable holder에 남는다
  • immutable slice 교환: 에이전트는 “최신”이라는 미검증 개념을 교환하지 않는다. “최신”은 mutable해서 지금은 이 스냅샷, 잠시 후엔 저 스냅샷으로 해소될 수 있다. 교환되는 데이터는 immutable할 수 있다 — 라이브 대시보드와 스크린샷의 차이
    • SliceRef가 slice를 식별: catalog_id, dataset, snapshot_id(DuckLake 카탈로그 내 커밋 식별자, 콘텐츠 해시가 아니며 한 스냅샷이 여러 테이블 변경을 담을 수 있음), slice_digest(materialize된 콘텐츠 또는 canonical manifest — DB 상태만이 아니라 정확한 콘텐츠를 식별하게 해줌), contract_digest(데이터가 해석되어야 할 시맨틱 아티팩트)
    • 데이터가 바뀌면 기존 slice를 변형하지 않고 새 참조를 만든다. 이것이 모든 캐시 일관성 문제를 없애지는 않는다 — “newest” 같은 mutable alias, 라우팅 레코드, 인가 상태, revocation, holder 가용성은 여전히 control-plane 관심사다. 좁은 이득은 pinned된 분석 콘텐츠가 더 이상 무효화·조정을 요구하지 않는다는 것
  • 계약 3계층 — 기존 data contract 작업은 생산자에 집중해 스키마를 게시하고 조용히 바꾸지 않겠다고 약속했지만, 소비자의 가정은 대체로 암묵적으로 남는다(쿼리·대시보드·문서·원래 만든 분석가의 기억 안에)
    1. Semantic contract — 생산자의 계약을 Malloy 모델로 표현: 타입 있는 source, measure, governed query를 정의하고 Malloy가 이를 DuckDB가 실행할 SQL로 컴파일. 계약을 해시해 slice 참조에 포함하므로 다른 계약 파일을 쓰는 두 에이전트는 다르게 이름 붙은 slice를 가리키게 된다. sum(qty * unit_price)를 sum(qty * unit_price) * 0.9로 바꾸면 digest가 바뀌고 참조가 바뀌어 시맨틱 불일치가 다운스트림 계산 안에 숨지 않고 교환·해소 시점에 드러난다
      • 다만 해시가 모든 관련 시맨틱 차이를 포착했음을 증명하지는 않는다. 같은 모델을 다른 Malloy·DuckDB 버전, 다른 런타임 설정, 다른 외부 아티팩트로 해석할 수 있다. 프로덕션 시맨틱 identity는 hash(canonical_contract, malloy_version, duckdb_version, dialect, runtime_settings, dependency_digests) 처럼 실행 관련 컨텍스트를 포함해야 한다. 주장은 해시가 의미를 증명한다는 게 아니라 명시적 시맨틱 아티팩트가 제품 identity의 일부가 된다는 것
    2. Compatibility relation — 콘텐츠 해시는 두 아티팩트가 바이트 단위로 동일한지만 말해주고 호환되는지는 판단할 수 없다. 포맷 변경(sum(qty * unit_price) vs sum(qty*unit_price))이 의미를 안 바꾸면서 해시를 바꿀 수 있고, 더 중요하게 additive 변경과 breaking 재정의가 둘 다 그냥 “다른 해시”로만 보인다. 그래서 각 Malloy 모델을 정규화된 ContractSpec으로 파싱해 measure·query·source·의존성·실행 컨텍스트를 기록하고 비교: IDENTICAL / ADDITIVE / BREAKING / UNRELATED
      • BREAKING이 가장 중요한 관계다. 두 에이전트가 gross_revenue를 쓰는데 하나는 환불 전, 하나는 환불 후로 정의한다면 둘 다 내부적으로는 유효하지만 출력을 동등하게 취급해서는 안 된다 → BREAKING: redefined measure ['gross_revenue']
      • 계약 호환이 두 결과를 합쳐도 안전하다는 뜻은 아니다. 기저 slice가 겹치거나, 다른 grain·기간·통화·모집단일 수 있다. 계약 호환성은 한 계약이 다른 계약이 요구하는 정의를 보존하는지만 답하고, 두 slice를 결합할 수 있는지는 grain·파티션·시간 커버리지·단위·차원 도메인·중복·집계 동작을 고려하는 별도의 composition 규칙이 정해야 한다
    3. Intent contract — 소비자의 요구사항. 에이전트는 어떤 스냅샷이 있는지 모를 수 있으므로 스냅샷 ID를 직접 요청해서는 안 되고 원하는 데이터셋, 요구하는 시맨틱 정의, 신선도 선호를 진술한다. resolver가 이 mutable 요청을 하나의 구체적 immutable 참조로 변환. 가용한 모든 계약이 소비자가 의존하는 measure나 의존성을 바꾸면 해소가 실패한다
  • 파생 데이터는 스스로를 source authority로 승격할 수 없다: 모든 slice에 provenance tier(WAREHOUSE / DERIVED)를 부여. 파생 slice는 authoritative source 데이터를 명시적으로 요구하는 요청을 만족시킬 수 없다 → PermissionError("refusing to launder provenance"). tier는 서명된 아티팩트와 함께 이동한다 — mutable side table에만 두면 잃거나 재작성하기 쉬워진다
    • 단일 tier 플래그로는 여러 파생 단계 후 결과의 출처를 설명하기 부족하므로 각 slice가 lineage DAG를 지닌다. 노드마다 입력, 선언된 연산, 생산 principal, tier, digest, 서명/attestation을 담고 각 노드의 digest가 자기 메타데이터와 입력들의 digest를 커버하므로 이전 노드를 바꾸면 위쪽 digest가 전부 바뀐다
    • 검증의 한계를 저자가 명시: 체인 무결성과 lineage root가 신뢰 정책을 만족하는지는 확인하지만, 기저 데이터가 올바른지, 선언된 연산이 충실히 실행됐는지, 관련 입력이 빠지지 않았는지는 증명하지 않는다. 서명은 principal이 attestation을 했다는 것을 증명할 뿐 그 진술의 참을 증명하지 않는다. 프로토타입은 HMAC + in-process keyring이라 검증자가 생산자와 같은 대칭 키를 가지면 attestation을 위조할 수도 있다
    • Data Mesh 반론에 대한 응답: 웨어하우스 하나가 신뢰 데이터의 유일한 origin이면 P2P 전송 아래 중앙 권위를 보존한 것 아니냐는 지적에 대해 — 권위는 제품 단위로 스코프된다. 한 웨어하우스가 orders에 authoritative이고 다른 도메인 시스템이 고객 지원이나 제품 텔레메트리에 authoritative일 수 있다. peer는 데이터를 보유·서빙하는 능력에서는 대칭이지만 provenance 권위에서는 동등하지 않다
  • 컴퓨트를 데이터 쪽으로 밀어라: 에이전트 무리는 필요한 결과가 집계값 하나일 때 큰 slice를 전송할 여유가 없다. bulk fetch(요청자가 상당한 로컬 파생을 할 때), holder에 계산을 푸시하는 query, 메타데이터 전용 연산(prove가 lineage DAG를, contract가 정규화된 계약 스펙을 반환). 같은 slice가 전체 데이터셋·필터 결과·집계·계약·provenance proof로 소비될 수 있다
    • 원격 쿼리 pushdown은 심각한 보안 경계를 도입한다. 신뢰되지 않는 peer에게 다른 peer의 DuckDB 연결에 대한 무제한 SQL 접근을 주면 안 된다 — 무관한 테이블 접근, 로컬 파일 검사, 확장 호출, 상태 변형, 무제한 CPU·메모리 소비가 가능하다. 프로덕션은 read-only 격리 프로세스, 참조된 slice만 노출하는 capability-scoped 뷰, 제한된 쿼리 문법 또는 검증된 논리 플랜, CPU·메모리 한도, statement timeout, row 한도, 확장 제한, 파일·네트워크 접근 통제가 필요
    • 검증 gap도 있다: 서명된 입력 slice가 원격 peer가 올바른 집계를 반환했음을 증명하지 않는다. 악의적이거나 침해된 holder는 입력 provenance가 유효해도 틀린 결과를 반환할 수 있다. 재현 가능한 로컬 실행, 복제된 계산, TEE, 서명된 런타임 attestation, 암호학적 증명 시스템 같은 별도 메커니즘이 필요
  • LLM이 있어야 할 자리: 아키텍처는 에이전트를 위한 것이지만 핵심 신뢰 결정은 결정론적이다. 계약 해싱, 구조적 비교, intent 해소, 스냅샷 pinning, digest 검증, 서명 확인, 신뢰 정책 강제는 같은 입력에 대해 모든 peer에서 같은 결과를 내야 한다. 계약 변경이 IDENTICAL/ADDITIVE/BREAKING인지를 LLM에게 결정하게 하면 호환성 경계가 확률적이 되고 두 모델이 두 지표를 함께 써도 되는지에 대해 다른 결론에 도달할 수 있다. LLM은 모호한 사용자 요청을 구조화된 intent로 번역하는 데 더 적합하다 — 모델이 intent를 제안하고 결정론적 코어가 검증·해소한다. 모델이 존재하지 않는 measure를 지어내거나 없는 계약을 요구하면 해소가 실패해야 하고, 모델은 그럴듯한 설명을 생산하는 것만으로 slice를 신뢰할 만하다고 선언할 수 없다

주요 수치 / 사실

  • 스택: DuckDB(임베디드 엔진), DuckLake(system of record, 스냅샷 메타데이터), Quack(DuckDB 클라이언트-서버 프로토콜, 실제 DuckDB↔DuckDB TCP 통신), Malloy(시맨틱 모델·governed query 컴파일). 아키텍처는 이 제품들에 의존하지 않으며 Iceberg/Delta, dbt/Cube, Arrow Flight, in-toto/SLSA로 대체 가능 — 참조 구현이지 필수 플랫폼이 아니다
  • 진짜 구현된 것: DuckLake가 SoR, 스냅샷 메타데이터, Parquet 디스크 저장, Quack 실제 TCP 통신, peer가 실제 loopback 포트 바인딩, Malloy가 SQL 컴파일, 실제 콘텐츠 해시 기반 계약 identity, 실제 digest를 담은 lineage 그래프, 검증이 digest 재계산, root trust·provenance tier 확인
  • 의도적으로 단순화된 것: peer 토큰이 OIDC가 아닌 in-process 생성, lineage가 비대칭 identity가 아닌 HMAC, holder 디렉토리가 복제 레지스트리가 아닌 Python 딕셔너리, 원격 SQL이 완전 샌드박스되지 않음, 원격 쿼리 결과가 암호학적으로 검증 불가
  • “레지스트리로 병목을 옮긴 것 아니냐”는 자기 비판에 대한 응답: 디렉토리는 분석 바이트를 서빙하거나 분석 워크로드를 실행하지 않고 immutable 참조를 holder로 해소하는 일을 하므로 웨어하우스보다 네임서버에 가깝다. 하지만 사소하지 않다 — DNS도 계층·위임·복제·캐싱·TTL과 많은 운영 기계로 확장한다. 침해된 레지스트리는 최신 항목을 withhold하는 것 이상을 할 수 있다 — 서명된 항목이 완전한 객체 identity·holder identity·유효 구간·시퀀스 정보·신뢰 정책을 바인딩하지 않으면 옛 참조를 replay하거나, revocation을 억제하거나, 클라이언트마다 다른 말을 하거나(equivocate), 악의적/불가용 holder로 라우팅할 수 있다
  • 기여는 개별 메커니즘이 아니라 조합: 불변 스냅샷(Iceberg, Delta, DuckLake), 콘텐츠 주소 아티팩트(IPFS, Git, lakeFS, Nessie, Dolt), 호환성 클래스(스키마 레지스트리, API 버저닝), 서명된 provenance 그래프(in-toto, SLSA), 시맨틱 모델(Malloy, dbt, Cube), 컴퓨트 pushdown 모두 상당한 선행 연구가 있다. 조합 — pinned 데이터 상태 + materialize된 콘텐츠 digest + 실행 가능한 시맨틱 계약으로 공동 식별되고, P2P로 교환되고, 구조적 호환성이 확인되고, 평범한 파생 peer가 source authority를 주장하지 못하게 하는 인증된 lineage를 동반하는 immutable 분석 slice — 가 새롭다는 주장
  • 아키텍처적 주장: 중앙 웨어하우스는 여전히 유용하고 많은 조직이 계속 확장할 것이다. 이 접근은 일관성·identity·보안·신선도·관측성을 없애지 않고 어디서 처리할지를 바꾼다 — 콘텐츠 identity는 immutable 참조에, 시맨틱 호환성은 결정론적 계약 비교에, source authority는 제품 스코프 신뢰 정책과 서명에, 발견과 신선도는 control plane에

관련 위키


Source: 원문 보기