키워드를 넘어: Go에서 Production-Ready Agentic Search Framework 구축

발행: (2025년 12월 29일 오후 07:00 GMT+9)
11 min read
원문: Dev.to

Source: Dev.to

Beyond Keywords: Go에서 프로덕션 수준 에이전틱 검색 프레임워크를 구축하기 위한 커버 이미지

Amit Surana

검색 시스템과 에이전시 검색

검색 시스템은 역사적으로 검색(리트리벌)에 최적화되어 왔습니다: 쿼리가 주어지면 가장 관련성 높은 문서를 반환합니다. 사용자의 의도가 정보를 찾는 것에서 문제를 해결하는 것으로 전환되는 순간, 이 모델은 붕괴됩니다.

다음과 같은 쿼리를 생각해 보세요:

“내일 시애틀의 날씨가 JFK행 항공권 가격에 어떤 영향을 미칠까요?”

이는 검색 문제가 아니라 추론 문제입니다—분해, 여러 시스템 간의 오케스트레이션, 그리고 일관된 답변으로의 통합이 필요한 문제입니다.

여기서 에이전시 검색(agentic search) 이 등장합니다.

이 글에서는 Go로 에이전시 검색 프레임워크를 설계하고 프로덕션 환경에 적용한 과정을 살펴보겠습니다—데모가 아니라 지연 시간, 비용, 동시성, 장애 모드와 같은 프로덕션 제약 조건을 고려한 실제 시스템입니다.

검색에서 에이전시 검색으로

키워드 및 벡터 검색 시스템은 쿼리를 문서와 매칭하는 데 뛰어납니다. 하지만 다음과 같은 부분은 잘 다루지 못합니다:

  • 다단계 추론
  • 도구 조정
  • 쿼리 분해
  • 답변 종합

에이전시 검색은 LLM을 텍스트 생성기가 아니라 플래너—질문에 답하기 위해 어떤 행동을 취할지 결정하는 구성 요소—로 취급합니다.

높은 수준에서 에이전시 시스템은 다음을 수행할 수 있어야 합니다:

  • 사용자 의도 이해
  • 어떤 도구를 호출할지 결정
  • 해당 도구를 안전하게 실행
  • 필요 시 반복
  • 최종 응답을 종합

어려운 부분은 LLM을 도구에 연결하는 것이 아니라, 이를 생산 환경에서 예측 가능하고 경제적으로 수행하는 것입니다.

고수준 아키텍처

우리는 시스템을 세 가지 핵심 관심사로 구조화했습니다:

관심사책임
계획무엇을 할지 결정
실행도구를 효율적으로 실행
통합최종 답변 생성

다음은 엔드‑투‑엔드 흐름입니다:

User Query → Planner → Tool Registry → Tool Execution → Response Generator → SSE Stream → User

각 단계는 의도적으로 분리되어 있습니다. 추론은 실행으로 유출되지 않으며, 실행은 계획 결정에 직접 영향을 주지 않습니다.

Flow Orchestrator: The Control Plane

Flow Orchestrator는 요청의 전체 수명 주기를 관리합니다. 그 책임은 다음과 같습니다:

  • 플래너 호출 조정
  • 도구를 동시에 실행
  • 재시도, 타임아웃 및 취소 처리
  • 부분 응답 스트리밍

선형 파이프라인 대신, 오케스트레이터는 Go의 goroutine을 사용한 병렬 실행을 지원합니다. 이는 여러 독립적인 도구가 관여할 때 필수적입니다.

Query Planner: Mandatory First Pass, Conditional Iteration

The Query Planner is always invoked at least once.

First Planner Call (Always)

On the first invocation, the planner:

  • Analyzes the user query
  • Produces an initial set of tool calls
  • Establishes a consistent reasoning baseline

Even trivial queries go through this step to maintain uniform behavior and observability.

Lightweight Classifier Gate

Before invoking the planner a second time, we run a lightweight classifier model to determine whether the query is:

  • Single‑step
  • Multi‑step

This classifier is intentionally cheap and fast.

Second Planner Call (Only for Multi‑Step Queries)

If the query is classified as multi‑step:

  1. The planner is invoked again.
  2. It receives:
    • The original user query
    • Tool responses from the first execution
  3. It determines:
    • Whether more tools are required
    • Which tools to call next
    • How to sequence them

This prevents uncontrolled planner loops — one of the most common failure modes in agentic systems.

도구 레지스트리: 추론이 현실과 만나는 곳

모든 도구는 엄격한 Go 인터페이스를 구현합니다:

// ToolInterface is the tool interface for developers to implement which uses
// generics with strongly typed
type ToolInterface[Input any, Output any] interface {
    // Execute initiates the execution of a tool.
    //
    // Parameters:
    // - ctx:        Context for cancellation/timeout.
    // - requestContext: Additional request‑specific data.
    // - input:      Strong‑typed tool request input.
    // Returns:
    // - output:     Strong‑typed tool request output.
    // - toolContext: Additional output data not used by the agent model.
    // - err:        Structured error from tool (e.g., no_response).
    Execute(ctx context.Context, requestContext *RequestContext, input Input) (output Output, toolContext ToolResponseContext, err error)

    // GetDefinition gets the tool definition sent to the Large Language Model.
    GetDefinition() ToolDefinition
}

이 설계가 제공하는 장점:

  • 플래너 피드백을 위한 자연어 출력
  • 하위 시스템에서 활용할 구조화된 메타데이터
  • 컴파일 타임 안전성
  • 안전한 병렬 실행

도구 레지스트리는 신뢰 경계 역할을 합니다. 플래너 출력은 의도로 간주되며, 직접적인 명령으로 해석되지 않습니다.

Parallel Tool Execution

플래너가 생성한 도구 호출은 가능한 경우 동시에 실행됩니다. Go의 동시성 모델이 이를 실현합니다:

  • 가벼운 고루틴
  • 컨텍스트 기반 취소
  • 효율적인 I/O‑바인드 실행

이는 에이전트 시스템이 프로토타입을 넘어설 때 Go가 Python보다 더 잘 확장되는 이유 중 하나입니다.

응답 생성 및 스트리밍

도구가 완료되면 응답이 Response Generator로 흐릅니다.

  • Knowledge‑based queries는 LLM을 사용하여 요약 및 합성됩니다.
  • Direct‑answer queries(날씨, 스포츠, 주식)는 합성을 건너뛰고 원시 도구 출력을 반환합니다.

응답은 **Server‑Sent Events (SSE)**를 통해 스트리밍되어 사용자가 부분 결과를 일찍 확인할 수 있어 인지된 지연 시간이 개선됩니다.

캐싱 전략: 에이전트 검색을 경제적으로 만들기

하나의 프로덕션 현실이 거의 즉시 명확해졌습니다: LLM 호출은 지연 시간과 비용 모두에서 실제 비용이 듭니다.

베타 트래픽을 제공하기 시작하자, 캐싱은 필수가 되었습니다. 우리의 guiding principle은 간단했습니다: 가능한 한 LLM 호출을 피하십시오.

Layer 1: Semantic Cache (Full Response)

우리는 먼저 사용자 쿼리를 키로 하는 의미 캐시를 확인합니다.

  • Cache hit → 즉시 응답 반환
  • Cache miss → 다음 레이어로 진행

히트가 발생하면 전체 에이전트 흐름이 건너뛰어 가장 큰 지연 시간 및 비용 절감을 제공합니다.

Layer 2: Planner Response Cache

의미 캐시가 미스될 경우, 플래너 출력(툴 플랜)이 캐시되어 있는지 확인합니다.

  • Cache hit → 플래너 LLM 호출을 건너뛰고 바로 툴을 실행
  • Cache miss → 플래너 LLM 호출

플래너 호출은 가장 비용이 많이 들고 변동성이 큰 작업 중 하나이므로, 이를 캐시하면 지연 시간과 비용을 모두 안정화할 수 있습니다.

Layer 3: Summarizer Cache

마지막으로, 요약기 출력도 캐시합니다.

  • 툴 결과가 자주 반복됨
  • 최종 합성 결과를 재사용 가능
  • 트래픽 급증 시 LLM 부하 감소

각 캐시 레이어는 파이프라인의 서로 다른 부분을 단축시킵니다.

프로덕션에서 얻은 교훈

몇 가지 힘들게 얻은 교훈:

  • LLM 호출은 비용이 많이 듭니다 — 대규모에서는 캐싱이 선택 사항이 아닙니다
  • 시맨틱 캐싱은 즉시 효과를 발휘합니다
  • 플래너 루프는 반드시 제한을 두어야 합니다
  • 대부분의 쿼리는 생각보다 단순합니다
  • 도구는 실패합니다 — 재시도와 대체 방안이 중요합니다
  • 관측 가능성은 협상할 수 없습니다
  • 에이전트는 자율적이지 않습니다 — 오케스트레이션이 자율성보다 우수합니다
Back to Blog

관련 글

더 보기 »