본문으로 건너뛰기
cd /blog

그라운드 트루스: AI 에이전트를 현실에 고정하기

[아키텍처][그라운딩]

> AI 에이전트는 자신 있게 헛소리를 지어냅니다. 기준 사실은 에이전트의 행동을 현실에 고정하는, 버전 관리되고 범위가 명확한 사실들입니다. 우리가 이를 어떻게 만들고 강제하는지 소개합니다.

이 글의 수치는 게시 시점(2026년 1월) 기준입니다. 최신 수치는 팀 페이지를 참고하세요.

AI 에이전트는 놀라울 만큼 유능합니다. 추론하고, 종합하고, 생성할 수 있습니다. 하지만 이들에게는 근본적인 약점이 있습니다. 무언가를 지어낸다는 것입니다. 악의적으로가 아니라 자신 있게 말이죠. 에이전트는 존재하지 않는 API 파라미터를 지어내거나, 한 번도 정의된 적 없는 설정을 참조하거나, 학습 데이터에서 온 패턴을 여러분의 실제 아키텍처와 모순되게 적용할 수 있습니다.

표준적인 완화책은 “에이전트에게 더 많은 컨텍스트를 주라”는 것입니다. 하지만 컨텍스트는 서로 모순될 수 있습니다. 문서는 구현에서 벗어나 표류합니다. 주석은 거짓말을 합니다. 의도를 이해하지 못한 채 읽으면 코드조차 사람을 오도할 수 있습니다.

우리에게는 더 명시적인 무언가가 필요했습니다. 무시되거나 오해될 수 없는 무언가. 에이전트를 검증 가능한 현실에 고정할 수 있는 무언가.

우리는 이것을 기준 사실이라고 부릅니다.

기준 사실이란 무엇인가?

기준 사실은 에이전트가 반드시 존중해야 하는, 명시적이고 버전 관리되는 사실 진술입니다. 문서가 아닙니다. 주석도 아닙니다. 시스템 안의 1급 개체이며 다음을 갖습니다.

  • 고유 식별자 (GT-MAG-015 또는 GT-MAG-036 같은 형태)
  • 라이프사이클 상태 (current, tentative, deprecated)
  • 범위 (플랫폼 전체, 패키지별, 혹은 도메인 한정)
  • 근거 (진술을 뒷받침하는 파일 경로, URL, 참조)
  • 에이전트 가이던스 (명시적인 권장/금지 지침)

우리 Maguyva 코드 인텔리전스 플랫폼의 예시입니다.

- id: GT-MAG-015
  status: current
  scope: package
  statement: |
    Fuzzy symbol matching is opt-in via `find_similar=true`.
    Default behavior returns empty results for non-existent symbols;
    `exact_match=true` enforces strict matching and disables all fuzzy fallbacks.
  rationale: |
    Deterministic defaults prevent agents from receiving misleading results.
    Typos should fail explicitly rather than silently returning unrelated symbols.
  evidence:
    - "packages/maguyva/server/src/maguyva/services/code_analysis_service.py"
    - "packages/maguyva/server/docs/quick_reference/parameters.md"
  last_verified: "2026-01-25"
  tags:
    - product
    - ai_first
    - principle

이것은 산문이 아닙니다. 계약입니다. 에이전트가 이 기준 사실을 마주치면 다음을 알게 됩니다.

  1. 기본값은 결정적입니다(모호한 추측이 아니라 빈 결과).
  2. 정의된 동작을 가진 특정 파라미터(find_similar, exact_match)가 존재합니다.
  3. 검증 가능한 특정 파일에 근거가 존재합니다.
  4. 이 진술은 특정 날짜에 검증되었습니다.

기준 사실 레지스트리의 해부

기준 사실은 ai_assets/reference/ground_truths.yaml 아래의 YAML 레지스트리에 존재합니다. 각 패키지나 도메인은 자체 레지스트리를 가질 수 있습니다. 구조는 다음과 같습니다.

metadata:
  title: "Maguyva Ground Truths"
  summary: "Foundational constraints and principles that guide Maguyva."
  last_updated: "2026-01-26"
  owner: "maguyva"
  render:
    include_statuses: [current, tentative]
    show_deprecated: true
    groups:
      - title: "Product Principles"
        tags: [product, principle, brand]
      - title: "Architecture & Boundaries"
        tags: [architecture, boundaries, cqrs]

statements:
  - id: GT-MAG-001
    status: current
    scope: package
    statement: "Maguyva is read-only with respect to user repositories..."
    ...

레지스트리에는 컬렉션 자체에 대한 메타데이터, 문서 생성을 위한 렌더 설정, 그리고 진술 자체가 포함됩니다. 각 진술은 Pydantic 모델로 검증되는 엄격한 스키마를 따릅니다.

class GroundTruthStatement(BaseModel):
    id: str
    status: GTStatus  # current, tentative, deprecated
    source: GTSource | None  # claude-code, orkestra, discipline
    scope: GTScope  # platform, package, domain
    statement: str
    rationale: str | None
    evidence: list[str]
    last_verified: str | None
    tags: list[str]
    agent_guidance: AgentGuidance | None

에이전트가 기준 사실에 접근하는 방법

기준 사실은 여러 경로로 노출됩니다.

1. 렌더링된 문서

orkestra sync 명령은 YAML 레지스트리를 읽을 수 있는 마크다운으로 변환합니다.

uv run orkestra sync

이는 에이전트 컨텍스트에 포함되는 GROUND_TRUTHS.md 파일을 생성합니다. 렌더링된 결과는 진술을 상태와 카테고리별로 그룹화합니다.

## Current
### Product Principles
- Maguyva is read-only with respect to user repositories... (GT-MAG-001)
- Design tool responses for AI agents first... (GT-MAG-009)

### Architecture & Boundaries
- Pipeline and Maguyva boundaries are intentional... (GT-MAG-006)

2. CLI 검색

셸 접근 권한이 있는 에이전트는 그라운드 트루스를 프로그래밍 방식으로 검색할 수 있습니다.

uv run orkestra context ground-truths search "embedding"
uv run orkestra context ground-truths search --tag security
uv run orkestra context ground-truths list --status current

검색 함수는 여러 필드에 걸쳐 가중치를 둔 관련성으로 일치 항목을 채점합니다.

def truth_fields(gt: GroundTruthStatement) -> list[FieldSpec]:
    return [
        FieldSpec(name="id", weight=6, values=[gt.id]),
        FieldSpec(name="statement", weight=5, values=[gt.statement]),
        FieldSpec(name="rationale", weight=4, values=[gt.rationale] if gt.rationale else []),
        FieldSpec(name="tags", weight=3, values=gt.tags or []),
        FieldSpec(name="evidence", weight=2, values=gt.evidence or []),
    ]

3. 컨텍스트 구성

에이전트가 YAML 정의로부터 렌더링될 때, 그 컨텍스트는 그라운드 트루스 레지스트리를 참조할 수 있습니다.

context_composition:
  domain_knowledge:
    - packages/maguyva/ai_assets/reference/ground_truths.yaml

이는 에이전트가 작업을 시작하기 전에 관련 그라운드 트루스가 로드되도록 보장합니다.

그라운드 트루스의 범주

우리 레지스트리 전반을 살펴보면, 그라운드 트루스는 몇 가지 패턴으로 뭉칩니다.

제품 원칙

제품이 무엇이고 무엇이 아닌지에 대한 제약:

“Maguyva는 사용자 저장소에 대해 읽기 전용이다. 재생성 불가능한 유일한 자산은 유료 임베딩 캐시다.” (GT-MAG-001)

아키텍처 경계

책임이 어디에 있고 왜 그런지:

“파이프라인과 Maguyva의 경계는 의도적이다. 파이프라인은 재사용 가능하고, Maguyva는 코드 특화 로직을 갖고 있으며, CQRS가 단계별 쓰기와 서버 읽기를 분리한다.” (GT-MAG-006)

환각 방지 규칙

도구 계약을 추론이 아니라 결정적으로 유지하는 명시적 지침:

“퍼지 심볼 매칭은 find_similar=true을 통한 옵트인이다. 기본 동작은 존재하지 않는 심볼에 대해 빈 결과를 반환한다. exact_match=true은 엄격한 매칭을 강제하고 모든 퍼지 폴백을 비활성화한다.” (GT-MAG-015)

품질 게이트

유지해야 하는 기준:

“공유 인프라(post_filters.py, 관계 추출기, 공유 핸들러)에 대한 변경은 커밋 전에 전체 매니페스트 생성을 통해 지원되는 모든 언어에 대해 반드시 검증되어야 한다. 단일 언어 검증만으로는 공유 코드에 충분하지 않다.” (GT-MAG-036)

코드 패턴

구현 요구 사항:

“비동기 컨텍스트에서 CPU 바운드 작업에는 asyncio.to_thread()를 사용하라. 더 이상 사용되지 않는 loop.run_in_executor() 패턴은 새 코드에 사용해서는 안 된다.” (GT-MAG-018)

그라운드 트루스의 라이프사이클

그라운드 트루스는 고정된 것이 아닙니다. 정해진 라이프사이클을 거쳐 진화합니다.

Tentative(잠정)

평가 중인 제안된 진실. 진술은 기록되지만 변경될 수 있습니다.

- id: GT-MAG-044
  status: tentative
  statement: |
    get_file with include_metadata=false may still return metadata in the
    response because middleware may re-inject it for AI agent disambiguation.

Current(현재)

검증된, 에이전트가 반드시 존중해야 하는 진실. 근거가 검증되었습니다.

- id: GT-MAG-017
  status: current
  last_verified: "2026-01-26"
  evidence:
    - "packages/maguyva/server/src/maguyva/services/supabase.py"

Deprecated(폐기됨)

더 이상 적용되지 않는 진실. 무엇으로 대체되었는지에 대한 포인터와 함께 역사적 참고용으로 보존됩니다.

- id: GT-MAG-099
  status: deprecated
  superseded_by: GT-MAG-015
  notes: "Replaced when we moved to explicit matching behavior"

왜 그냥 문서로는 안 되는가?

문서는 다른 목적을 수행합니다. 설명합니다. 가르칩니다. 모호할 수 있고, “대체로”나 “보통” 같은 수식어를 쓸 수 있습니다.

그라운드 트루스는 모호할 수 없습니다. 이것은 단언입니다. 적용되거나 적용되지 않거나 둘 중 하나입니다.

차이를 살펴봅시다.

문서: “심볼을 찾지 못하면 API는 대체로 빈 결과를 반환하지만, 일부 설정에서는 퍼지 매칭이 활성화되어 있을 수 있다.”

그라운드 트루스: “기본 동작은 존재하지 않는 심볼에 대해 빈 결과를 반환한다. exact_match=true은 엄격한 매칭을 강제하고 모든 퍼지 폴백을 비활성화한다.”

첫 번째는 시스템을 배우는 사람에게 도움이 됩니다. 두 번째는 결정을 내리는 에이전트가 실제로 행동에 옮길 수 있는 것입니다.

에이전트 가이던스: 해야 할 것과 피해야 할 것

일부 그라운드 트루스는 명시적인 에이전트 가이던스를 포함합니다.

- id: GT-MAG-022
  statement: |
    Accuracy fixes must happen at extraction time via production code,
    never via validator filters.
  agent_guidance:
    do:
      - "Fix extraction bugs in YAML config, handlers, or tree-sitter queries"
      - "Add test cases at the layer where the fix lives"
    avoid:
      - "Adding validator filters to mask production bugs"
      - "Creating test-only workarounds for extraction issues"

이는 모호함을 없앱니다. 이를 읽는 에이전트는 무엇이 사실인지뿐 아니라 그 사실이 어떤 행동을 암시하는지도 알게 됩니다.

검증과 유지보수

그라운드 트루스는 유지보수가 필요합니다. 우리는 다음을 추적합니다.

  • last_verified: 누군가 이 진술이 여전히 유효함을 확인한 시점
  • evidence: 진술을 뒷받침하는 파일(존재 여부를 확인할 수 있음)
  • source: 이 진실이 어디서 비롯되었는지(CLI 조사, 아키텍처 검토, 인시던트 이후 학습)

검증 날짜가 오래되었거나 근거 링크가 깨진 그라운드 트루스는 조사가 필요하다는 신호입니다. 진실이 여전히 유효해서 재검증이 필요하거나, 아니면 현실이 바뀌어서 진실을 업데이트해야 하거나 둘 중 하나입니다.

프로덕션의 실제 사례

보안 경계

- id: GT-MAG-014
  statement: |
    Maguyva queries are search patterns, not executable code.
    SQL injection prevention is handled by PostgREST parameterization;
    application-layer SQL keyword blocking must never be added.
  rationale: |
    Blocking SQL keywords breaks legitimate code search. Users search FOR
    code containing patterns like 'DROP TABLE', they don't execute them.

이 그라운드 트루스는 제품을 망가뜨릴 수 있는 잘못된 방향의 “보안 개선” 부류를 방지합니다.

추출 시점 정확도

- id: GT-MAG-022
  statement: |
    Accuracy fixes must happen at extraction time via production code
    (YAML config, handlers, queries), never via validator filters.
  rationale: |
    Validator filters only run during tests. They can hide extractor bugs
    while production responses remain wrong.

이는 고통스러운 경험에서 나왔습니다. 에이전트들은 테스트 하니스만 더 초록색으로 보이게 만드는 검증기 전용 필터를 추가하는 방식으로 실패하는 언어 팩을 땜질했지만, 실제 라이브 Maguyva 추출기는 여전히 잘못된 엣지를 내보내고 있었습니다. 이 규칙은 수정 사항을 YAML 설정, 쿼리, 혹은 핸들러라는 진짜 경로로 되돌려 강제합니다.

다중 계층 필터링

- id: GT-MAG-023
  statement: |
    Language engine uses three-tier filtering: external_method_patterns
    (builtins/stdlib), allowlists (legitimate idioms), and expected_call_targets
    (validation-time deduplication). Each tier serves a distinct purpose.
  rationale: |
    Conflating filter purposes leads to either over-filtering (missing real
    relationships) or under-filtering (noise).

이는 에이전트가 잘못된 위치에 필터를 추가하는 것을 막습니다. 이는 정확도 리그레션을 유발했던 흔한 실수입니다.

오케스트레이션 시스템과의 통합

그라운드 트루스는 더 넓은 컨텍스트 시스템의 한 계층입니다.

  1. 아키텍처 결정(ADR) - 접근법 A를 B 대신 선택한 이유를 기록합니다
  2. 그라운드 트루스 - 지금 명확하게 사실인 것을 진술합니다
  3. 도메인 패턴 - 무언가를 올바르게 하는 방법을 설명합니다
  4. 안티패턴 - 무엇을 피해야 하고 왜 그런지 설명합니다

이 시스템에서 일하는 에이전트는 이 네 가지 모두에 접근할 수 있습니다. 그라운드 트루스는 사실적 고정점을 제공하고, 결정은 역사를 설명하며, 패턴은 구현을 안내하고, 안티패턴은 함정을 경고합니다.

영향 측정

그라운드 트루스를 도입한 이후 우리는 다음을 관찰했습니다.

  • “환각으로 만든 수정을 다시 고치는” 순환의 감소
  • 사실이 명확할 때 더 확신에 찬 에이전트의 의사결정
  • 기대치가 명시적이기 때문에 더 나아진 PR 리뷰
  • 새 에이전트(그리고 사람)의 온보딩 시간 단축

그라운드 트루스를 유지보수하는 데 들이는 투자는 디버깅 감소와 더 명확한 시스템 경계로 보상받습니다.

시작하기

여러분의 시스템에 그라운드 트루스를 추가하려면:

  1. 패키지의 ai_assets/reference/ 디렉터리에 ground_truths.yaml를 만듭니다
  2. 메타데이터와 렌더 설정을 정의합니다
  3. 스키마를 따르는 진술을 추가합니다
  4. 문서를 생성하기 위해 uv run orkestra sync를 실행합니다
  5. 레지스트리를 에이전트 컨텍스트 구성에 포함시킵니다

가장 많은 혼란을 일으키는 사실이나 가장 자주 위반되는 제약부터 시작하세요. 그것들이 여러분에게 가장 가치 있는 그라운드 트루스입니다.

결론

AI 에이전트는 환각을 일으킬 것입니다. 그것이 그들의 본성입니다. 하지만 우리는 환각이 제약되고, 특정 사실은 타협 불가능하며, 에이전트가 자신의 가정을 검증된 현실과 대조해 확인할 수 있는 환경을 만들 수 있습니다.

그라운드 트루스는 완전한 해결책이 아닙니다. 유지보수가 필요합니다. 낡을 수 있습니다. 개발 프로세스에 오버헤드를 더합니다.

하지만 이것은 가치 있는 무언가를 제공합니다. 사람과 에이전트 모두가 신뢰할 수 있는 공유된 사실의 어휘 말입니다. 에이전트가 소프트웨어 개발에 점점 더 많이 참여하는 세상에서, 그 공유된 기반은 필수적이 됩니다.

대안은 에이전트가 확신에 찬 실수를 저지르고 사람이 이를 고쳐주는 끝없는 순환입니다. 그라운드 트루스는 수정을 명시적이고 지속적으로 만들어 그 순환을 끊습니다.

여러분의 에이전트는 무엇이 사실인지 알 자격이 있습니다. 알려주세요.

관련 글

Maguyva 빌드 로그의 다른 글들