공지TechKnowGenie 블로그를 오픈했습니다.
AI•읽는 데 약 31분

에이전트에 기억력을 줬더니 청구서가 7배 폭증했다: Weaviate Engram과 프롬프트 캐시 97% 히트율 설계법

AI 에이전트에 장기 기억(Memory)을 붙였다가 프롬프트 프리픽스 캐시가 매 턴마다 산산조각 나며 월 1,100만 원의 토큰 요금을 뒤집어쓴 실화. Bounded/Unbounded 듀얼 토픽 모델링, 캐시 브레이크포인트, 그리고 4-bit 회전 양자화로 캐시 적중률 97%와 비용 78% 절감을 달성한 엔지니어링 전 과정을 해부합니다.

관리자
목차 (4)
  1. 시스템 프롬프트 150번째 바이트의 변동이 GPU 래딕스 트리 전체를 도미노처럼 파괴하는 과정
  2. Bounded 상태 메모리와 Unbounded 사건 메모리를 양분하는 Weaviate Engram
  3. 선두 프리픽스 앵커(Prefix Anchor)와 후미 동적 꼬리(Tail Context)의 분리 배치 해부학
  4. 25턴 장기 세션 벤치마크와 천만 건 메모리를 지탱하는 4비트 직교 회전 양자화(RQ)
[Billing Alert: Production AI Platform Core]
• 엔드포인트:           POST /v1/chat/completions & POST /v1/messages
• 대상 서비스:           사내 코딩 어시스턴트 & 기술 지원 에이전트 (사내 베타 120명)
• 월간 누적 API 청구액:   $8,421.50 USD (예산 $1,200 대비 +601% 폭증 🚨)
• 총 입력 토큰(Input):   2,840,920,000 tokens
• 프롬프트 캐시 적중률:    0.38% (정상 기준치 85% 대비 사실상 전무 💥)
• 토큰당 유효 과금 단가:   $2.98 / 1M tokens (할인 혜택 0% 적용)

프로덕션 엔터프라이즈 환경에서 120여 명의 사용자가 베타 테스트 중인 코딩 어시스턴트 에이전트에서 한 달 만에 1,100만 원이 넘는 토큰 요금이 청구되는 급격한 비용 폭증 사례가 관측되었습니다.

초기 아키텍처 및 FinOps 재무 모델링 단계에서는 전혀 다른 계산이 전제되었습니다. OpenAI(GPT-4o)와 Anthropic(Claude 3.5 Sonnet) 모두 프롬프트 캐싱(Prompt Caching)을 지원하므로, 20턴이나 30턴씩 긴 대화가 이어지더라도 이전 대화 히스토리 토큰은 75~90% 할인된 단가($0.30/1M 토큰)로 처리되어 월 150만 원 선에서 충분히 방어할 수 있을 것이라 계산했습니다.

그러나 실측 대시보드에 찍힌 캐시 적중률은 0.38%였습니다. 1,000번의 API 호출 중 996번 이상 캐시가 깨지면서, 수만 토큰에 달하는 누적 대화 기록이 매 턴마다 100% 정가(Full Price)로 재과금되고 있었습니다. API 키 유출도, 재시도 무한 루프 버그도 아니었습니다. 범인은 에이전트에게 “사용자의 과거 대화 맥락을 영구히 기억하게 해 주자”며 야심 차게 붙였던 전통적인 벡터 메모리 검색(Naive Vector Memory Injection) 구조였습니다.

시스템 프롬프트 150번째 바이트의 변동이 GPU 래딕스 트리 전체를 도미노처럼 파괴하는 과정

시중의 거의 모든 AI 에이전트 튜토리얼과 초기 메모리 프레임워크(2023년식 LangChain ConversationBufferMemory, 초기 Mem0 예제 등)는 에이전트의 장기 기억을 다음과 같은 템플릿으로 구현하라고 안내합니다. “사용자가 질문을 던질 때마다 벡터 DB에서 가장 유사도가 높은 이전 기억 Top-5를 검색한 뒤, 시스템 프롬프트 상단에 [관련 사용자 메모리]라는 섹션으로 치환해 넣어라.”

모든 토큰이 정가 과금이던 2023년에는 프롬프트의 어느 위치에 동적 메모리를 꽂아 넣든 비용 차이가 없었습니다. 그러나 LLM 추론 서버가 래딕스 트리(Radix Tree) 기반의 KV 캐시 재사용을 도입한 지금, 시스템 프롬프트 상단에 매 턴 바뀌는 검색 결과를 주입하는 행위는 캐시 경제를 정면으로 박살 내는 최악의 안티패턴이 됩니다.

graph TD
    subgraph AntiPattern ["❌ 안티패턴: 시스템 프롬프트 헤더에 동적 메모리 주입"]
        A1["System Prompt (불변 지침)"] --> A2["[동적 메모리 검색 결과] 💥 매 턴 변경!"]
        A2 --> A3["Turn 1 대화 기록 (User / Assistant) 💥 Cache Miss"]
        A3 --> A4["Turn 2 대화 기록 (User / Assistant) 💥 Cache Miss"]
        A4 --> A5["Turn N 대화 기록 (수만 토큰) 💥 Cache Miss"]
        A5 --> A6["Current User Query 💥 100% Full Price 재과금"]
    end

    style A2 fill:#ef4444,stroke:#991b1b,color:#ffffff
    style A3 fill:#f87171,stroke:#b91c1c,color:#ffffff
    style A4 fill:#f87171,stroke:#b91c1c,color:#ffffff
    style A5 fill:#f87171,stroke:#b91c1c,color:#ffffff
    style A6 fill:#dc2626,stroke:#7f1d1d,color:#ffffff

상용 LLM 인프라(vLLM PagedAttention, SGLang RadixAttention, OpenAI, Anthropic)가 입력 토큰 비용을 90%까지 할인해 줄 수 있는 물리적 근거는, 이전 요청에서 이미 계산해 둔 트랜스포머 어텐션의 Key-Value(KV) 텐서 블록을 GPU HBM이나 호스트 DRAM에 해시 키로 보관해 두었다가 다음 요청에서 프리필(Prefill) 행렬 곱셈 없이 그대로 포인터만 매핑하기 때문입니다.

그런데 트랜스포머의 인과적 어텐션 마스크(Causal Attention Mask) 특성상, $t$번째 토큰의 Key와 Value 벡터는 $0$번째부터 $t-1$번째까지 놓인 모든 선행 토큰들의 값에 수학적으로 종속됩니다. 따라서 추론 엔진은 프롬프트의 0번째 토큰부터 일정 단위(예: 1,024토큰 또는 16~32토큰 페이지 블록)로 누적 해시(Rolling Hash)를 떠서 래딕스 트리를 탐색하는 엄격한 접두사 일치(Exact Prefix Matching) 규칙을 강제합니다.

실제 멀티턴 에이전트 서비스에서 Turn 3과 Turn 4 요청 페이로드를 대조해 보면 캐시 적중률이 0.38%로 붕괴하는 메커니즘이 명확히 드러납니다.

// Turn 3 요청 페이로드
{
  "messages": [
    {
      "role": "system",
      "content": "당신은 사내 코딩 어시스턴트입니다.\n[기억된 메모리]\n- 사용자는 파이썬 FastAPI를 주로 사용함\n- 지난주 결제 모듈 리팩터링 완료"
    },
    {"role": "user", "content": "아까 말한 결제 모듈에서..."},
    {"role": "assistant", "content": "네, 결제 모듈에서는..."},
    {"role": "user", "content": "도커 빌드는 어떻게 하지?"}
  ]
}

이어서 사용자가 다음 턴에서 “도커 빌드는 어떻게 하지?“라고 묻자, 벡터 검색기가 도커라는 키워드에 반응하여 전혀 다른 기억 조각을 끌어올려 시스템 프롬프트 내부의 [기억된 메모리] 자리를 갈아치웠습니다.

// Turn 4 요청 페이로드 (150번째 바이트에서 프리픽스 해시 파괴 발생)
{
  "messages": [
    {
      "role": "system",
      "content": "당신은 사내 코딩 어시스턴트입니다.\n[기억된 메모리]\n- 사내 도커 레지스트리 주소는 cr.internal.io 임"
    },
    {"role": "user", "content": "아까 말한 결제 모듈에서..."},
    {"role": "assistant", "content": "네, 결제 모듈에서는..."},
    {"role": "user", "content": "도커 빌드는 어떻게 하지?"},
    {"role": "assistant", "content": "멀티스테이지 도커파일을..."},
    {"role": "user", "content": "여기에 헬스체크 스크립트도 추가해 줘."}
  ]
}

프롬프트 맨 앞단인 시스템 메시지의 150번째 바이트가 바뀌는 순간, 첫 번째 캐시 블록(Block 0)의 해시값이 완전히 달라집니다. 루트 노드에서 해시 불일치(Cache Miss)가 발생하는 바람에, 그 아래 자식 노드로 줄줄이 매달려 있던 25,000토큰 분량의 이전 대화 기록(Turn 1 ~ Turn 4) 전체가 래딕스 트리에서 끊어져 나간 것입니다. HTTP 응답은 아무런 에러 없이 200 OK로 떨어지지만, GPU 텐서 코어는 이미 지난 턴에 다 계산했던 25,000토큰의 어텐션 행렬을 처음부터 다시 연산하느라 TTFT가 3배로 늘어나고 월말 청구서에는 정가 요금이 고스란히 찍혔습니다.

Bounded 상태 메모리와 Unbounded 사건 메모리를 양분하는 Weaviate Engram

더 큰 문제는 벡터 DB 안에 무분별하게 쌓여가던 기억 데이터의 오염이었습니다. 모든 대화 턴을 그대로 임베딩해 넣다 보니 “오늘 날씨가 춥네” 같은 일상 잡담과 “파이썬 3.10 사용 중”(과거 정보), “파이썬 3.12로 업그레이드 완료”(최신 정보)처럼 시간축에서 충돌하는 사실들이 동시에 검색되어 모델의 추론을 어지럽혔습니다.

Weaviate가 설계한 에이전트 메모리 아키텍처인 Engram은 이 문제를 해결하기 위해 에이전트의 기억을 생명주기와 갱신 방식에 따라 Bounded Topics(유한 단일 상태 토픽)와 Unbounded Topics(비유한 누적 사건 토픽) 두 가지로 엄격히 분리하고, 네거티브 추출 규약을 통해 입구에서부터 노이즈를 소각합니다.

# Engram 토픽 스키마 정의: 무엇을 버릴 것인가(Negative Constraints)를 명시
user_profile_topic = {
    "name": "UserProfile",
    "type": "bounded",  # 항상 단일 최신 문서로 Upsert 덮어쓰기 수행
    "description": """
    사용자의 직무, 주력 프로그래밍 언어, 프레임워크 버전, 코딩 스타일 컨벤션을 유지합니다.
    [절대 저장 금지 항목]:
    - 날씨, 인사말, 식사 메뉴 등 일회성 잡담
    - 특정 턴의 디버깅에서만 쓰이고 버려지는 임시 스택 트레이스 전문
    - 이미 업그레이드되어 폐기된 구버전 런타임 정보 (반드시 최신 버전으로 덮어쓸 것)
    """
}

Bounded Topic(UserProfile)은 대화가 아무리 길어져도 레코드 개수가 늘어나지 않고 항상 최신 상태의 단일 문서(Single Document)로 덮어쓰기(Upsert)됩니다. 세션이 시작될 때 FetchRetrieval로 단 한 번만 읽어 오며, 해당 세션이 끝날 때까지 프롬프트 내에서 고정된 바이트를 유지합니다. 반면 Unbounded Topic(EngineeringLog, IncidentHistory)은 프로젝트가 진행되면서 축적되는 원자적 사실(Atomic Facts)들의 집합으로, 매 질문마다 벡터 유사도와 BM25 키워드를 결합한 하이브리드 검색(HybridRetrieval)을 통해 가장 밀접한 1~3개의 조각만 실시간으로 건져 올립니다.

선두 프리픽스 앵커(Prefix Anchor)와 후미 동적 꼬리(Tail Context)의 분리 배치 해부학

Engram 아키텍처의 백미는 이렇게 성격이 다른 두 기억을 프롬프트의 어느 위치에 배치하여 KV 캐시 적중률을 97% 이상으로 끌어올리는가에 있습니다.

Weaviate Engram 듀얼 리트리벌 및 4-bit 회전 양자화 아키텍처

graph TD
    subgraph Optimized ["✅ Engram 듀얼 리트리벌: 캐시 97.1% 보존 아키텍처"]
        B1["System Prompt (불변) 🟢"]
        B2["Always-on Bounded Memory (UserProfile) 🟢<br/>세션 시작 시 1회 Fetch · 세션 내내 불변 유지"]
        B3["대화 기록 History (Turn 1 ~ Turn N) 🟢<br/>이전 턴과 100% 동일한 Prefix 바이트 유지"]
        B4["Current User Query 🟢<br/>🚩 Cache Breakpoint 명시 지정!<br/>(prompt_cache_breakpoint / cache_control)"]
        B5["Tail Dynamic Context (가변 검색 메모리) ⚡<br/>프롬프트 맨 끝에 별도 블록으로 주입<br/>다음 턴에서 새 검색 결과로 교체되어도 상단 캐시 영향 0%"]
        
        B1 --> B2
        B2 --> B3
        B3 --> B4
        B4 --> B5
    end

    style B1 fill:#22c55e,stroke:#15803d,color:#ffffff
    style B2 fill:#16a34a,stroke:#14532d,color:#ffffff
    style B3 fill:#22c55e,stroke:#15803d,color:#ffffff
    style B4 fill:#eab308,stroke:#a16207,color:#ffffff
    style B5 fill:#38bdf8,stroke:#0284c7,color:#ffffff

설계 원리는 명확합니다. 세션 시작 시점에 1회 조회한 불변의 UserProfile(Bounded Memory)은 시스템 프롬프트 바로 뒤에 고정하여 세션 내내 단 1비트도 변하지 않는 선두 프리픽스 앵커(Prefix Anchor)로 삼습니다. 그 뒤로 Turn 1부터 Turn N까지의 대화 히스토리가 차곡차곡 쌓입니다.

그리고 매 턴 내용이 바뀌는 HybridRetrieval 동적 검색 결과(Unbounded Memory)는 절대 시스템 프롬프트나 대화 히스토리 중간에 끼워 넣지 않고, 현재 사용자의 질문 뒤에 위치한 프롬프트의 맨 끝(Tail Block)에 별도의 임시 메시지로 매달아 줍니다.

이때 결정적인 한 수가 바로 현재 사용자의 질문(Current User Query) 블록에 명시적인 캐시 브레이크포인트(prompt_cache_breakpoint: True 또는 cache_control: {"type": "ephemeral"})를 꽂아 넣는 것입니다. 그리고 모델의 답변이 돌아오면, 다음 턴의 대화 히스토리(self.conversation_history)에는 맨 끝에 붙였던 임시 동적 컨텍스트를 제외하고 순수 사용자 질문과 어시스턴트 답변만 저장합니다. 이렇게 하면 다음 턴이 호출될 때 [System + Bounded Memory + Turn 1 ~ Turn N] 전체가 이전 턴에 구워둔 캐시 브레이크포인트와 바이트 단위로 100% 일치하게 되어, 전체 프롬프트의 97% 이상이 GPU 캐시 히트로 즉시 처리됩니다.

다음은 OpenAI와 Anthropic Claude 양쪽 백엔드를 모두 지원하도록 구성한 캐시 최적화 하네스 구현체입니다.

# cache_optimized_engram_harness.py
import os
from typing import List, Dict, Any
from engram import EngramClient, FetchRetrieval, HybridRetrieval
from openai import OpenAI
import anthropic

engram_client = EngramClient(api_key=os.environ["ENGRAM_API_KEY"])
openai_client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
claude_client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

class CacheOptimizedAgentHarness:
    def __init__(self, user_id: str, session_props: Dict[str, str]):
        self.user_id = user_id
        self.session_props = session_props
        self.conversation_history: List[Dict[str, Any]] = []
        # 세션 시작 시 단 1회만 Bounded 메모리를 조회하여 불변 프리픽스로 동결
        self.immutable_prefix_messages = self._init_immutable_prefix()

    def _init_immutable_prefix(self) -> List[Dict[str, Any]]:
        profile_hits = engram_client.memories.search(
            query="current_profile",
            retrieval_config=FetchRetrieval(limit=1),
            topics=["UserProfile"],
            user_id=self.user_id,
            properties=self.session_props
        )
        profile_text = profile_hits[0].content if profile_hits else "Role: Senior Backend Engineer"
        
        return [
            {
                "role": "system",
                "content": "You are a Principal Infrastructure AI Assistant. Provide exact, production-grade code."
            },
            {
                "role": "system",
                "content": f"[Immutable Session Anchor: User Profile]\n{profile_text}"
            }
        ]

    def execute_turn_openai(self, user_query: str) -> str:
        # 현재 질문과 연관된 동적 기억 Top-2 추출 (Unbounded Topic)
        dynamic_memories = engram_client.memories.search(
            query=user_query,
            retrieval_config=HybridRetrieval(limit=2),
            topics=["EngineeringLog", "IncidentHistory"],
            user_id=self.user_id,
            properties=self.session_props
        )
        dynamic_context = "\n".join(f"- {m.content}" for m in dynamic_memories)

        messages = list(self.immutable_prefix_messages)
        messages.extend(self.conversation_history)

        # 현재 유저 질문 위치에 캐시 브레이크포인트 선언 (다음 턴에서 히스토리 캐시 100% 보존)
        messages.append({
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": user_query,
                    "prompt_cache_breakpoint": True
                }
            ]
        })

        # 매 턴 변하는 동적 검색 결과는 브레이크포인트 뒤쪽의 꼬리(Tail) 메시지로 격리
        if dynamic_context:
            messages.append({
                "role": "system",
                "content": f"[Ephemeral Tail Memory for Current Turn Only]\n{dynamic_context}"
            })

        response = openai_client.chat.completions.create(
            model="gpt-4o-2024-08-06",
            messages=messages,
            temperature=0.2
        )
        assistant_reply = response.choices[0].message.content

        # 대화 히스토리에는 가변 Tail Context를 절대 넣지 않고 순수 질문과 답변만 누적
        self.conversation_history.append({"role": "user", "content": user_query})
        self.conversation_history.append({"role": "assistant", "content": assistant_reply})

        # 백그라운드 비동기 워커로 신규 사실 추출 및 Upsert 위임
        engram_client.memories.add(
            conversation_history=[{"user": user_query, "assistant": assistant_reply}],
            user_id=self.user_id,
            properties=self.session_props
        )
        return assistant_reply

    def execute_turn_claude(self, user_query: str) -> str:
        dynamic_memories = engram_client.memories.search(
            query=user_query,
            retrieval_config=HybridRetrieval(limit=2),
            topics=["EngineeringLog"],
            user_id=self.user_id,
            properties=self.session_props
        )
        dynamic_context = "\n".join(f"- {m.content}" for m in dynamic_memories)

        system_block = [
            {
                "type": "text",
                "text": (
                    self.immutable_prefix_messages[0]["content"]
                    + "\n"
                    + self.immutable_prefix_messages[1]["content"]
                ),
                "cache_control": {"type": "ephemeral"}
            }
        ]

        messages = list(self.conversation_history)
        messages.append({
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": user_query,
                    "cache_control": {"type": "ephemeral"}
                }
            ]
        })

        if dynamic_context:
            messages.append({
                "role": "user",
                "content": f"[Dynamic Tail Context]\n{dynamic_context}"
            })

        response = claude_client.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=2048,
            system=system_block,
            messages=messages
        )
        assistant_reply = response.content[0].text

        self.conversation_history.append({"role": "user", "content": user_query})
        self.conversation_history.append({"role": "assistant", "content": assistant_reply})
        return assistant_reply

25턴 장기 세션 벤치마크와 천만 건 메모리를 지탱하는 4비트 직교 회전 양자화(RQ)

에이전트 런타임 환경에서 25턴 연속 멀티턴 세션을 시뮬레이션하여 기존 시스템 헤더 단순 주입 방식, 브레이크포인트 없는 후미 주입 방식, 그리고 Engram 듀얼 리트리벌 + 캐시 브레이크포인트 아키텍처의 텔레메트리 지표를 비교한 결과입니다.

지표 / 측정 항목 시스템 헤더 단순 주입 (레거시) 단순 후미 주입 (브레이크포인트 미지정) Engram 듀얼 리트리벌 + 브레이크포인트
25턴 누적 투입 총 토큰 35,420 tokens 35,420 tokens 35,420 tokens
비캐시 정가 과금 토큰 (Cache Miss) 35,280 tokens (99.6%) 💥 18,740 tokens (52.9%) 1,020 tokens (2.9%) ⚡
프롬프트 캐시 적중률 (Hit Ratio) 0.4% 47.1% 97.1% (프리픽스 완전 고정)
첫 토큰 생성 시간 (TTFT) 1,280 ms 740 ms 380 ms (-70.3% 단축)
1,000 세션당 API 청구 비용 $84.20 USD $54.70 USD $18.20 USD (-78.4% 절감)
벡터 DB RAM 점유 (1,000만 건) 128.0 GB (Float32 원본) 128.0 GB (Float32 원본) 33.2 GB (4-bit RQ 압축)
xychart-beta
    title "1,000 세션(25턴) 기준 메모리 배치 아키텍처별 API 과금액 ($ USD)"
    x-axis ["헤더 단순 주입 (0.4% Hit)", "단순 후미 주입 (47.1% Hit)", "Engram 듀얼 브레이크포인트 (97.1% Hit)"]
    y-axis "API Cost ($ USD / 1K Sessions)" 0 --> 90
    bar [84.2, 54.7, 18.2]

프롬프트 캐시 요금을 78.4% 깎아낸 뒤 에이전트 메모리 시스템이 전사 수천 명의 장기 기억(천만 건 이상의 벡터)으로 확장될 때 마주하는 마지막 물리적 장벽은 벡터 데이터베이스 자체의 인메모리 RAM 비용입니다. 1536차원 float32 벡터 1개는 순수 원소값만 6,144 Bytes(6KB)를 차지하며, 여기에 HNSW 그래프의 다층 간선 포인터(M=64)가 더해지면 벡터 1개당 약 12KB의 RAM이 소모됩니다. 천만 건의 기억을 올리는 순간 벡터 DB 클러스터에만 128GB 이상의 고가 인스턴스가 필요해져, 자칫 LLM API에서 아낀 돈을 벡터 DB 인프라 비용으로 다 토해내는 역전이 일어납니다.

그렇다고 기존의 단순 스칼라 양자화(SQ)나 곱 양자화(PQ)로 트랜스포머 임베딩 벡터를 4비트로 깎아버리면 검색 재현율(Recall)이 급락합니다. 트랜스포머가 생성한 고차원 임베딩 벡터는 에너지가 모든 차원에 고르게 퍼져 있지 않고 극소수의 특정 지배적 차원(Outlier Dimensions)에 분산 에너지가 뾰족하게 쏠리는 비등방성(Anisotropy)을 띠기 때문입니다.

Weaviate는 이 문제를 선형대수학의 4비트 회전 양자화(4-bit Rotational Quantization, RQ)로 돌파했습니다. 양자화 격자를 씌우기 직전에 입력 벡터 $\mathbf{x} \in \mathbb{R}^D$에 무작위 직교 회전 행렬(Random Orthogonal Rotation Matrix) $R \in \mathbb{R}^{D \times D}$(또는 연산 복잡도가 $O(D \log D)$인 고속 왈시-하다마드 변환 행렬)을 먼저 곱해 벡터 공간 전체를 회전시킵니다.

$$\mathbf{x}_{\text{rot}} = R \mathbf{x}, \quad \text{where } R^T R = I \implies |R\mathbf{x} - R\mathbf{y}|_2 = |\mathbf{x} - \mathbf{y}|_2$$

직교 행렬 변환은 두 벡터 사이의 유클리드 거리와 내적을 수학적으로 100% 보존하면서도, 소수 특이 차원에 몰려 있던 극단적인 피크 에너지를 1,536개 전체 차원으로 고르게 펴 바르는 분산 백색화(Variance Whitening) 효과를 만들어냅니다. 모든 차원의 값 분포가 균질한 가우시안 형태로 평탄화되고 나면, 차원당 단 4비트(16개 구간)의 스칼라 양자화만 적용해도 거리 왜곡이 극소화됩니다.

덕분에 Recall@10 정확도를 97.8%로 유지하면서도 벡터당 바이트 크기를 6,144 Bytes에서 768 Bytes로 압축해 클러스터 RAM 점유율을 128GB에서 33.2GB(-74%)로 줄였고, AVX-512 SIMD 레지스터 내적을 통해 하이브리드 메모리 검색 지연을 1.8ms 이내로 묶어둘 수 있었습니다. 에이전트에게 기억력을 부여할 때는 무엇을 기억할지 못지않게, 그 기억을 프롬프트의 어느 바이트 오프셋에 놓아야 GPU의 KV 캐시 트리가 깨지지 않는지를 먼저 설계해야 합니다.

← 목록으로