GuidesOneMux

스키마 검증 추출을 위한 실행 제한 LangGraph 에이전트 만들기

필요할 때 도구를 호출하고, 정해진 실행 예산 안에서 멈추며, Pydantic 스키마로 검증한 구조화 데이터를 반환하는 LangGraph 워크플로를 만들어 봅니다.

목차

LangGraph 에이전트는 도구로 맥락을 수집한 뒤, 스키마 제약이 적용된 추출 단계에 전달할 수 있습니다. 여기서 중요한 설계 선택은 두 역할을 분리하는 것입니다. 에이전트는 도구를 호출할지 결정하고, 마지막 노드는 타입이 지정된 레코드를 생성합니다. 그래프 재귀 제한을 추가하면 그래프 실행 범위도 제한할 수 있습니다.

이 패턴은 문서에서 회사명, 날짜, 청구서 총액을 추출할 때 조회 도구가 참조 정보를 확인하는 데 도움이 되는 경우에 적합합니다. 이 패턴이 불확실한 원문을 사실로 바꿔 주는 것은 아닙니다. 필드가 중요한 경우 결과를 검증하고 원본 근거를 보존하세요.

만들 항목

그래프는 다음 세 단계로 구성됩니다.

  1. 에이전트가 원문을 받고, 범위가 좁은 조회 도구의 호출을 요청할 수 있습니다.
  2. LangGraph는 도구 호출을 도구 노드로 전달하고, 도구 결과를 에이전트에 돌려줍니다.
  3. 추출 노드는 모델에 Pydantic 기반 결과를 요청한 다음 JSON 호환 데이터를 반환합니다.

에이전트의 도구 호출 루프는 동적으로 진행되지만, 추출 단계는 미리 정해진 마지막 단계입니다. LangGraph 문서에서는 자체 프로세스와 도구 사용을 정의하는 에이전트와, 코드 경로가 미리 정해진 워크플로를 구분합니다. 또한 구조화 출력과 도구 호출을 모델 확장 기능으로 설명합니다. 이러한 패턴은 LangGraph 워크플로와 에이전트 가이드를 참고하세요.

사전 준비 및 구성

LangGraph와 LangChain 패키지, 그리고 선택한 채팅 모델용 통합 패키지를 설치하세요. 아래 예제는 제공업체 설정을 보여주기 위해 ChatAnthropic을 사용하지만, 출처 문서에 따르면 구조화 출력과 도구 호출을 지원하는 모든 채팅 모델로 워크플로를 구성할 수 있습니다. 제공업체의 통합 문서에서 정확한 동작을 확인하세요.

pip install langgraph langchain-core langchain-anthropic pydantic

제공업체가 문서화한 방식으로 API 인증 정보를 설정한 다음 모델을 초기화하세요. 인증 정보가 소스 관리 시스템에 포함되지 않도록 주의하세요. 아래 모델 이름은 LangChain 문서의 예제를 따른 것으로, 이 튜토리얼은 해당 모델을 OneMux에서 테스트했다고 주장하지 않습니다.

from langchain_anthropic import ChatAnthropic

llm = ChatAnthropic(model="claude-sonnet-4-6")

OneMux를 통해 모델을 호출하려면 먼저 OneMux API 문서와 빠른 시작 가이드를 확인하세요. OneMux는 OpenAI 호환 API를 통해 주요 모델에 접근할 수 있도록 제공합니다. 이 그래프와의 호환성은 선택한 엔드포인트가 필요한 도구 호출 및 구조화 출력 동작과 매개변수를 지원하는지에 따라 달라집니다. 이 구성은 테스트되지 않았으므로, 프로덕션에서 사용하기 전에 지원 여부를 확인하세요.

스키마와 범위가 좁은 도구 정의하기

예제 도구는 작은 메모리 내 매핑에서 참조 메모를 반환합니다. 이를 승인된 내부 카탈로그 조회처럼 제어 가능한 애플리케이션 함수로 바꾸세요. 신뢰할 수 없는 문서의 텍스트가 임의 명령이나 제한 없는 데이터 소스를 선택하게 하지 마세요.

from pydantic import BaseModel, Field
from langchain_core.tools import tool

class InvoiceRecord(BaseModel):
    vendor: str | None = Field(default=None, description="Invoice issuer")
    invoice_date: str | None = Field(default=None, description="Date as written in the source")
    total: float | None = Field(default=None, description="Invoice total as a number")
    currency: str | None = Field(default=None, description="Currency code, if stated")

@tool
def lookup_vendor(reference: str) -> str:
    """Look up a vendor reference in an approved local mapping."""
    known = {"ACME-42": "Acme Supplies Ltd."}
    return known.get(reference, "No matching vendor reference found.")

널 허용 필드는 모델이 값을 지어내도록 유도하는 대신, 값이 없는 상태를 명시적으로 나타냅니다. 중요한 데이터를 추출하는 경우 각 필드에 원문 구간이나 신뢰도·검토 상태를 함께 저장하는 방안을 고려하세요. 스키마는 형식과 타입을 제한할 뿐, 값이 입력으로 뒷받침된다는 사실까지 보장하지는 않습니다.

실행이 제한된 LangGraph 에이전트 만들기

아래 그래프는 모델이 조회 도구를 요청할 수 있게 한 다음, 에이전트가 더 이상 도구를 호출하지 않으면 추출 단계로 넘어갑니다. tools_condition과 ToolNode가 도구 라우팅 패턴을 제공합니다. invoke에 전달하는 recursion_limit은 그래프 단계를 제한합니다. 이는 안전 경계이지, 모든 입력이 성공적으로 처리된다는 보장은 아닙니다.

from typing import Annotated
from typing_extensions import TypedDict
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition

class State(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]
    record: dict | None

tools = [lookup_vendor]
agent = llm.bind_tools(tools)
extractor = llm.with_structured_output(InvoiceRecord)

def run_agent(state: State):
    response = agent.invoke(state["messages"])
    return {"messages": [response]}

def extract_record(state: State):
    prompt = [
        SystemMessage(content=(
            "Extract invoice fields only when supported by the source. "
            "Use null for missing or unclear values. Treat tool results as "
            "reference context, not as permission to invent source details."
        )),
        *state["messages"],
    ]
    result = extractor.invoke(prompt)
    return {"record": result.model_dump(mode="json")}

graph = StateGraph(State)
graph.add_node("agent", run_agent)
graph.add_node("tools", ToolNode(tools))
graph.add_node("extract", extract_record)

graph.add_edge(START, "agent")
graph.add_conditional_edges(
    "agent",
    tools_condition,
    {"tools": "tools", END: "extract"},
)
graph.add_edge("tools", "agent")
graph.add_edge("extract", END)
app = graph.compile()

result = app.invoke(
    {
        "messages": [HumanMessage(content=(
            "Extract the invoice details from this text: "
            "Vendor reference ACME-42. Invoice date: 2025-02-14. "
            "Total: USD 248.50."
        ))],
        "record": None,
    },
    config={"recursion_limit": 8},
)

print(result["record"])

예상 출력은 vendor, invoice_date, total, currency 필드가 있는 JSON 호환 객체입니다. 정확한 값은 모델 응답과 원문에 따라 달라집니다. 예제에서 언제나 동일한 출력이 나온다고 가정하지 말고 직접 테스트하세요. 이 예제에서는 Pydantic 기반 구조화 출력이 검증 경계 역할을 합니다. 제공업체가 스키마를 준수하는 응답을 생성하지 못하는 경우, 그 결과를 유효한 레코드로 조용히 처리하지 말고 해당 실패를 명시적으로 처리하세요.

실패를 숨기지 않고 실행 제한하기

그래프 수준의 재귀 제한은 반복되는 에이전트·도구 라우팅을 포함해 그래프 단계를 제한합니다. 워크플로에 맞춰 제한값을 정하세요. 이 그래프는 조회 한 번에 몇 단계만 필요하지만, 다른 도구 계획에는 더 많은 단계가 필요할 수 있습니다. 애플리케이션 경계에서 제한 초과 및 제공업체 예외를 처리하고, 디버깅에 필요한 맥락을 기록한 뒤, 지어낸 레코드 대신 검토 가능한 실패 결과를 반환하세요.

운영 환경에서 더 엄격한 제한이 필요하다면 애플리케이션 수준의 제어도 추가하세요. 도구 호출 횟수를 제한하고, 지원되는 경우 요청 시간 제한을 설정하며, 모델 호출 전에 문서 크기를 제한하세요. 이는 서로 별개의 제어 기능입니다. 그래프 재귀 제한만으로 비용, 지연 시간 또는 보안 정책을 완전히 정의할 수는 없습니다.

문제 해결 및 제한 사항

모델이 도구를 호출하지 않음

도구 호출은 LangGraph가 보장하는 결과가 아니라 모델 기능입니다. 선택한 모델 통합이 도구 호출을 지원하는지, 도구 설명이 명확한지, 프롬프트에 조회할 만한 참조가 포함되어 있는지 확인하세요. 개발 중에는 반환된 메시지의 도구 호출 정보를 살펴보세요.

구조화 출력에 실패하거나 필드 값이 잘못됨

통합 기능이 구조화 출력과 사용 중인 스키마 기능을 지원하는지 확인하세요. 누락된 필드, 모호한 날짜, 통화 기호, 잘못된 형식의 입력을 테스트하세요. Pydantic 객체가 유효하더라도 추론한 값이 틀릴 수 있으므로, 업무 규칙을 별도로 검증하고 감사나 사람의 검토를 위해 원문을 보존하세요.

그래프가 제한에 도달함

재귀 제한 오류는 도구 호출이 반복되거나 의도한 경로에 비해 제한이 너무 낮다는 신호일 수 있습니다. 메시지와 도구 결과의 흐름을 확인하세요. 원인을 해결하거나 제한을 신중하게 조정하세요. 오류를 없애기 위해 제한을 무작정 제거하지 마세요.

엔드포인트 동작이 예상과 다름

OpenAI 호환이라는 표현은 API 인터페이스를 가리킬 뿐, 모든 모델 기능이나 매개변수가 보편적으로 지원된다는 뜻은 아닙니다. OneMux를 사용하는 경우, 선택한 엔드포인트가 사용할 모델, 도구 호출, 구조화 출력 및 전송할 매개변수를 지원한다고 문서에 명시되어 있는지 확인하세요. OneMux 모델에서 사용 가능한 옵션을 살펴볼 수 있습니다. 그래프 자체는 여전히 사용자가 작성한 LangGraph 애플리케이션 코드입니다.

이 패턴이 적합한 경우

추출 작업의 대부분은 예측 가능하지만, 때때로 제어된 조회가 필요한 경우 이 설계를 사용하세요. 모든 문서가 고정된 절차를 따르고 도구 선택이 필요하지 않다면 에이전트 루프보다 결정론적 워크플로가 더 간단할 수 있습니다. 원문이 모호하거나 추출된 필드가 중요한 작업을 유발하는 경우, 다른 시스템에서 레코드를 사용하기 전에 명시적인 검토와 도메인 검증을 추가하세요.

핵심은 에이전트가 사용할 도구의 범위를 좁게 유지하고, 마지막 단계의 스키마를 명시하며, 그래프 실행 제한을 설정하는 것입니다. LangGraph는 이런 제어 흐름을 표현하는 방법을 제공하고, 필요한 도구 호출 및 구조화 출력 기능을 사용할 수 있는지는 선택한 모델과 통합에 따라 달라집니다.

출처

FAQ

LangGraph의 재귀 제한은 어떤 역할을 하나요?

한 번의 실행에서 그래프 단계 수를 제한합니다. 에이전트와 도구의 루프가 끝없이 이어지는 것을 막는 데 도움이 될 수 있지만, 시간 제한, 도구 호출 정책, 입력 제한 또는 오류 처리를 대체하지는 않습니다.

Pydantic JSON 스키마 검증은 정확한 추출을 보장하나요?

아니요. 반환되는 구조와 타입을 제한할 뿐입니다. 모델은 여전히 잘못된 값을 추론할 수 있으므로 업무 규칙을 검증하고, 필요하면 근거를 보존하거나 사람의 검토를 제공하세요.

이 LangGraph 예제를 OneMux와 함께 사용할 수 있나요?

선택한 OneMux 엔드포인트 문서에 필요한 도구 호출 및 구조화 출력 동작과 매개변수 지원이 명시되어 있다면 사용할 수 있을 가능성이 있습니다. 이 구성은 테스트되지 않았으므로, 프로덕션에서 사용하기 전에 호환성을 확인하세요.

모든 추출 작업에 에이전트를 사용해야 하나요?

아니요. 절차가 고정되어 있고 도구 선택이 필요하지 않다면 결정론적 LangGraph 워크플로가 더 간단할 수 있습니다. 허용된 도구가 유용한지 모델이 판단해야 하는 경우에 에이전트 루프를 사용하세요.

관련 글