에이전트 게이트웨이

에이전트 게이트웨이 (A2A 프로토콜) 개요

LiteLLM AI Gateway에서 A2A 에이전트를 추가하고, A2A 프로토콜로 에이전트를 호출하며, 요청/응답 로그를 LiteLLM Logs에서 추적할 수 있어요. 어떤 팀과 키가 어떤 에이전트에 접근할 수 있는지도 관리할 수 있답니다.

이 페이지에서는 A2A 에이전트를 등록하는 방법, 프로토콜 버전 관리, 에이전트 호출, 로그 추적, 그리고 API 레퍼런스까지 한 번에 정리해 드릴게요.

출처: 문서

본문

LiteLLM은 에이전트를 호출하기 위해 A2A(Agent-to-Agent) 프로토콜을 따릅니다.

지원 기능 지원 여부
지원되는 에이전트 프로바이더 A2A, Vertex AI Agent Engine, LangGraph, Azure AI Foundry, Bedrock AgentCore, Pydantic AI
로깅 (Logging)
로드 밸런싱 (Load Balancing)
스트리밍 (Streaming)
반복 예산 (Iteration Budgets)

에이전트 추가하기 (Add A2A Agents)

LiteLLM Admin UI를 통해 A2A 호환 에이전트를 추가할 수 있어요.

  1. Agents 탭으로 이동합니다
  2. Add Agent 를 클릭합니다
  3. 에이전트 이름(예: ij-local)과 A2A 에이전트의 URL을 입력합니다
  4. 프로토콜 버전(1.0 또는 0.3)을 선택합니다 — LiteLLM이 클라이언트에 제공하는 wire format이에요

URL은 A2A 에이전트의 호출 URL(예: http://localhost:10001)이어야 합니다.

config.yaml에서 에이전트 정의하기

에이전트는 top-level agents 키 아래 config.yaml에서도 선언할 수 있어요. 게이트웨이가 ConfigMap이나 다른 읽기 전용 소스에서 배포될 때 유용하답니다. agent_nameagent_card_params는 둘 다 필수이며, 둘 중 하나라도 없는 항목은 시작 시 건너뜁니다.

agents:
  - agent_name: my-agent
    agent_card_params:
      name: "My Agent"
      url: "http://localhost:10001"
      protocolVersion: "1.0"  # or "0.3"

protocolVersion은 API로 등록할 때도 같은 방식으로 설정할 수 있어요.

선택적인 litellm_params 블록에는 api_key, headers, agent_card_path, Microsoft Entra 자격 증명 같은 에이전트별 설정을 담을 수 있어요. Foundry agents over A2A 문서에 전체 항목 예시가 있답니다.

config로 정의된 에이전트는 UI에서 만든 에이전트와 함께 Agents 탭과 GET /v1/agents에 표시되며, 주기적인 데이터베이스 재로드에서도 유지돼요. 다음과 같이 확인할 수 있습니다:

curl -s http://localhost:4000/v1/agents \
  -H "Authorization: Bearer $LITEL..._KEY"

UI에서 만든 에이전트가 config.yaml에 선언한 이름과 이미 겹친다면, 데이터베이스 레코드가 우선하고 해당 이름의 config 항목은 건너뜁니다. 그래서 이름은 항상 편집 가능한 레코드로 해석돼요. 데이터베이스 에이전트를 삭제하면 다음 재로드 시 config 항목이 그 이름을 다시 차지합니다.

config.yaml에 선언된 에이전트는 데이터베이스에 저장되지 않으므로 Admin UI에서 편집하거나 삭제할 수 없어요. config 파일을 바꾸고 게이트웨이를 재시작해야 합니다. agents 키는 다음 릴리스(v1.95.0 이후)부터 올바르게 읽혀요. 이전 버전에서는 agent_list를 사용하고, config로 정의된 에이전트는 데이터베이스가 연결된 게이트웨이에서 제거된다는 점에 주의하세요.

Azure AI Foundry 에이전트 추가하기

이 가이드를 따라 Azure AI Foundry 에이전트를 LiteLLM Agent Gateway에 추가할 수 있어요.

현재 Foundry 포털에서 만든 에이전트는 Assistants API 대신 A2A 엔드포인트를 노출합니다. Foundry agents over A2A 문서에 나온 것처럼 Entra 자격 증명과 함께 agents: 아래 등록하세요.

Vertex AI Agent Engine 추가하기

이 가이드를 따라 Vertex AI Agent Engine을 LiteLLM Agent Gateway에 추가할 수 있어요.

Bedrock AgentCore 에이전트 추가하기

이 가이드를 따라 bedrock agentcore 에이전트를 LiteLLM Agent Gateway에 추가할 수 있어요.

LangGraph 에이전트 추가하기

이 가이드를 따라 LangGraph 에이전트를 등록하고 에이전트 카드를 구성할 수 있어요.

Pydantic AI 에이전트 추가하기

이 가이드를 따라 pydantic ai 에이전트를 LiteLLM Agent Gateway에 추가할 수 있어요.

프로토콜 버전 관리

LiteLLM proxy는 a2a-sdk 1.x를 사용해 A2A 에이전트를 라우팅하며, 에이전트별로 A2A 0.3 또는 1.0 wire format을 클라이언트에 제공할 수 있어요. 업스트림 에이전트는 어느 버전이든 말할 수 있고, LiteLLM은 message/send, message/stream, extended-card 응답을 지정한 버전에 맞게 정규화합니다.

버전 Wire 형태 예시 send 결과
0.3 kind로 구분되는 객체 (message, task, status-update, …) {"kind": "message", "role": "user", "parts": [{"kind": "text", "text": "..."}]}
1.0 Protobuf JSON 봉투 (message, task, statusUpdate, artifactUpdate) {"message": {"role": "ROLE_USER", "parts": [{"text": "..."}]}}

버전 고정 (Pinning a version)

에이전트를 등록할 때 agent_card_params.protocolVersion"0.3" 또는 "1.0"으로 설정하세요 (UI 드롭다운 또는 API). LiteLLM은 프록시된 에이전트 카드에서 해당 버전을 제공하고, 업스트림 응답도 그에 맞게 변환합니다.

"0.3""1.0"만 허용되며, 다른 값은 등록 시 HTTP 400을 반환합니다.

protocolVersion이 고정되지 않은 경우

에이전트에 고정된 버전이 없으면 LiteLLM은 클라이언트 요청에서 제공할 버전을 추론합니다:

클라이언트 신호 제공되는 버전
JSON-RPC 메서드 SendMessage 또는 SendStreamingMessage 1.0
요청 헤더 a2a-version: 1.x 1.0
그 외 (예: 헤더 없는 message/send) 0.3

protocolVersion이 설정되지 않으면 프록시된 에이전트 카드는 기본적으로 1.0을 사용하지만, a2a-version 헤더가 없는 기존 message/send 호출자는 0.3 형태의 응답을 받아요. 카드와 응답이 항상 일치하도록 protocolVersion을 명시적으로 고정하세요.

작업 메서드(tasks/get, tasks/list, …)는 업스트림 에이전트에 그대로 전달됩니다. 버전 변환은 LiteLLM에 통합된 메시징 경로에만 적용돼요.

의존성 (Dependency)

LiteLLM proxy의 A2A 라우트는 a2a-sdk >= 1.1.0 이 필요합니다 (proxy / proxy-dev 의존성 그룹에 포함돼 있어요). 직접 코드에서 에이전트를 호출한다면 일치하는 SDK 버전을 설치하세요:

pip install "a2a-sdk>=1.1.0,<2.0"

에이전트 호출하기

에이전트 호출 방법은 Invoking A2A Agents 가이드를 참고하세요:

  • A2A SDK — 작업과 아티팩트를 완전히 지원하는 네이티브 A2A 프로토콜
  • OpenAI SDKa2a/ 모델 접두사를 사용하는 익숙한 /chat/completions 인터페이스

에이전트 로그 추적하기

에이전트를 호출한 후 LiteLLM Logs 탭에서 요청 로그를 볼 수 있어요.

로그에는 다음이 표시됩니다:

  • 에이전트에 보내고 받은 요청/응답 내용
  • 요청을 만든 사용자, 키, 팀 정보
  • 지연 시간(latency)과 비용(cost) 지표

LiteLLM 컨텍스트 헤더 전달하기

LiteLLM이 A2A 에이전트를 호출할 때 특별한 헤더를 보내 다음과 같은 기능을 활성화해요:

  • 트레이스 그룹핑 (Trace Grouping): 같은 에이전트 실행에서 발생한 모든 LLM 호출이 하나의 트레이스로 묶임
  • 에이전트 지출 추적 (Agent Spend Tracking): 비용이 특정 에이전트에 귀속됨
헤더 용도
X-LiteLLM-Trace-Id 모든 LLM 호출을 같은 실행 흐름으로 연결
X-LiteLLM-Agent-Id 지출(spend)을 올바른 에이전트에 귀속

이 기능을 사용하려면 A2A 서버가 LiteLLM으로 되돌아가는 모든 LLM 호출에 이 헤더들을 전달해야 해요.

구현 단계

1단계: 수신 A2A 요청에서 헤더 추출하기

def get_litellm_headers(request) -> dict:
    """Extract X-LiteLLM-* headers from incoming A2A request."""
    all_headers = request.call_context.state.get('headers', {})
    return {
        k: v for k, v in all_headers.items() 
        if k.lower().startswith('x-litellm-')
    }

2단계: LLM 호출에 헤더 전달하기 추출한 헤더를 LiteLLM으로 되돌아가는 호출에 전달하세요:

  • OpenAI SDK
  • LangChain
  • LiteLLM SDK
  • HTTP (requests/httpx)
from openai import OpenAI

headers = get_litellm_headers(request)

client = OpenAI(
    api_key="«redacted:sk-…»",
    base_url="http://localhost:4000",
    default_headers=headers,  # Forward headers
)

response = client.chat.completions.create(
    model="gpt-5.6-terra",
    messages=[{"role": "user", "content": "Hello"}]
)
from langchain_openai import ChatOpenAI

headers = get_litellm_headers(request)

llm = ChatOpenAI(
    model="gpt-5.6-terra",
    openai_api_key="«redacted:sk-…»",
    base_url="http://localhost:4000",
    default_headers=headers,  # Forward headers
)
import litellm

headers = get_litellm_headers(request)

response = litellm.completion(
    model="gpt-5.6-terra",
    messages=[{"role": "user", "content": "Hello"}],
    api_base="http://localhost:4000",
    extra_headers=headers,  # Forward headers
)
import httpx

headers = get_litellm_headers(request)
headers["Authorization"] = "Bearer «redacted:sk-…»"

response = httpx.post(
    "http://localhost:4000/v1/chat/completions",
    headers=headers,
    json={"model": "gpt-5.6-terra", "messages": [{"role": "user", "content": "Hello"}]}
)

결과

헤더 전달을 활성화하면 다음을 볼 수 있어요:

  • Langfuse에서의 트레이스 그룹핑
  • 에이전트 지출 귀속

API 레퍼런스

엔드포인트

엔드포인트 메서드 용도
POST /a2a/{agent_id} JSON-RPC 2.0 기본 — 모든 A2A 메서드 (아래 표 참고)
POST /a2a/{agent_id}/message/send JSON-RPC message/send 전용 별칭
POST /v1/a2a/{agent_id}/message/send JSON-RPC message/send 전용 별칭
GET /a2a/{agent_id}/.well-known/agent.json Agent card 디스커버리 (url 필드에 프록시 URL)
GET /a2a/{agent_id}/.well-known/agent-card.json Agent card 디스커버리 (표준 경로)

{agent_id}는 에이전트 UUID 또는 등록된 에이전트 이름일 수 있어요.

지원되는 JSON-RPC 메서드

POST /a2a/{agent_id}method 필드에 다음 중 하나를 보낼 수 있어요:

메서드 설명
message/send 메시지 전송; task 또는 message 반환 (LiteLLM 통합 경로)
message/stream 스트리밍 변형 (NDJSON/SSE)
tasks/get params.id로 task 상태 조회
tasks/list task 목록 (선택적 params.contextId)
tasks/cancel params.id로 task 취소
tasks/resubscribe task 업데이트 구독 (스트리밍)
tasks/pushNotificationConfig/set push 알림 config 등록
tasks/pushNotificationConfig/get push config 조회
tasks/pushNotificationConfig/list task의 push config 목록
tasks/pushNotificationConfig/delete push config 삭제
agent/getAuthenticatedExtendedCard 인증된 확장 에이전트 카드

라우팅: message/sendmessage/stream은 LiteLLM의 A2A 클라이언트를 거칩니다 (로깅, 가드레일, 지출 추적). 다른 모든 메서드는 agent_card_params.url의 업스트림 URL로 전달돼요. Task API에는 그 URL이 필요하며, completion-bridge 전용 에이전트는 메시징 메서드만 지원합니다.

Supported A2A methods 문서에서 예시, 별칭, 제한 사항을 확인하세요.

인증

두 헤더 중 하나에 LiteLLM Virtual Key를 포함하세요. 인바운드 Authorization 헤더가 백엔드 에이전트를 위한 토큰을 담을 수 있을 때(예: 호출자 신원을 전달하는 관례 기반 passthrough를 사용하는 경우) x-litellm-api-key가 선호됩니다.

Authorization: Bearer ***
# or
x-litellm-api-key: *** «redacted:sk-…»
에이전트별 권한 검사

가상 키가 인증된 후, LiteLLM은 해당 키(및 그 팀)가 요청한 에이전트를 호출할 수 있는지 확인합니다. 아니면 HTTP 403을 반환해요. 전체 교차(intersection) 모델과 접근 그룹은 Agent Permission Management 문서를 참고하세요.

트레이스 ID 강제 (선택, 에이전트별)

에이전트는 모든 인바운드 요청이 시스템 간 감사 추적을 위해 트레이스 ID를 갖도록 요구할 수 있어요. 에이전트의 litellm_paramsrequire_trace_id_on_calls_to_agent: true를 설정하세요. 설정하면 x-litellm-trace-id(또는 x-litellm-session-id)가 없는 요청은 HTTP 400으로 거부됩니다.

curl -X POST http://localhost:4000/v1/agents \
  -H "Authorization: Bearer ***" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_name": "audit-critical-agent",
    "agent_card_params": { ... },
    "litellm_params": {
      "require_trace_id_on_calls_to_agent": true
    }
  }'

에이전트가 소유한 키가 만드는 아웃바운드 호출에 트레이스 ID를 강제할지는 같은 litellm_params 블록의 require_trace_id_on_calls_by_agent로 제어합니다.

하위 에이전트 신원 전파

백엔드 에이전트가 LiteLLM을 직접 호출할 때(채팅 컴플리션 또는 하위 에이전트 호출), LiteLLM은 추적 연속성을 유지하기 위해 두 헤더를 전달합니다:

  • X-LiteLLM-Trace-Id — 체인의 모든 호출을 하나의 트레이스로 연결
  • X-LiteLLM-Agent-Id — 지출을 원래 에이전트에 귀속

호출자의 가상 키와 최종 사용자 ID는 자동으로 전달되지 않아요. 하위 에이전트가 사용자 신원을 필요로 하면 extra_headers 또는 x-a2a-{agent_name_or_id}-{header} 관례를 통해 명시적으로 전파하세요.

요청 형식

LiteLLM은 A2A JSON-RPC 2.0 스펙을 따릅니다. 메시지 body 형태는 에이전트의 고정된 protocolVersion(또는 고정되지 않은 경우 위의 클라이언트 신호)에 따라 달라져요.

  • 0.3 wire format
  • 1.0 wire format
{
  "jsonrpc": "2.0",
  "id": "unique-request-id",
  "method": "message/send",
  "params": {
    "message": {
      "role": "user",
      "parts": [{"kind": "text", "text": "Your message here"}],
      "messageId": "unique-message-id"
    }
  }
}

에이전트가 1.0으로 고정된 경우 a2a-sdk 1.x 클라이언트(권장)를 사용하거나, PascalCase 메서드 / a2a-version: 1.0 헤더와 함께 JSON-RPC를 보내세요. 요청 본문(1.0 SDK — protobuf 타입)은 a2a.types.Message, Part, Role로 만들고 SendMessageRequest로 감싸면 돼요.

응답 형식

  • 0.3 응답
  • 1.0 응답
{
  "jsonrpc": "2.0",
  "id": "unique-request-id",
  "result": {
    "kind": "task",
    "id": "task-id",
    "contextId": "context-id",
    "status": {"state": "completed", "timestamp": "2025-01-01T00:00:00Z"},
    "artifacts": [
      {
        "artifactId": "artifact-id",
        "name": "response",
        "parts": [{"kind": "text", "text": "Agent response here"}]
      }
    ]
  }
}
{
  "jsonrpc": "2.0",
  "id": "unique-request-id",
  "result": {
    "message": {
      "role": "ROLE_AGENT",
      "messageId": "msg-abc",
      "parts": [{"text": "Agent response here"}]
    }
  }
}

스트리밍 이벤트는 kind: "status-update" 대신 statusUpdate / artifactUpdate 키를 사용해요.

에이전트 JSON-RPC 오류는 가능할 때 요청과 같은 iderror 필드에 반환됩니다. message/send가 submitted task를 반환하면 긴 작업은 tasks/get으로 폴링하세요.

예시: tasks/get

curl -X POST "http://localhost:4000/a2a/my-agent" \
  -H "Authorization: Bearer ***" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": "req-2",
    "method": "tasks/get",
    "params": {"id": "task-id-from-send-response"}
  }'

에이전트 레지스트리

회사 내에서 어떤 에이전트를 사용할 수 있는지 팀이 발견할 수 있도록 중앙 레지스트리를 만들고 싶나요?

AI Hub를 사용해 에이전트를 조직 전체에 공개하고 발견 가능하게 만들 수 있어요. 개발자가 에이전트를 다시 만들 필요 없이 사용 가능한 에이전트를 탐색할 수 있게 해줍니다.

레지스트리 검색

GET /v1/agents는 키가 도달할 수 있는 모든 에이전트를 나열합니다. query=<task>를 추가하면 같은 에이전트들을 task와 각 에이전트의 이름, 설명, 스킬 사이의 의미적 유사성으로 순위를 매겨요. 먼저 임베딩 모델을 선택하세요:

model_list:
  - model_name: text-embedding-3-small
    litellm_params:
      model: openai/text-embedding-3-small
      api_key: os.environ/OPENAI_API_KEY

litellm_settings:
  agent_search_embedding_model: text-embedding-3-small
$ curl -s "http://localhost:4000/v1/agents?query=translate+a+pdf+document⊤_k=3" \
    -H "Authorization: Bearer ***" | jq -c '.[] | {agent_name, search_score}'
{"agent_name":"document-translator","search_score":0.6732403392080719}
{"agent_name":"trip-planner","search_score":0.1121043964671429}
{"agent_name":"warehouse-sql-analyst","search_score":0.0946962275274791}

top_k는 기본 5이고 최대 100으로 제한됩니다. 각 결과는 일반 에이전트 객체에 search_score(코사인 유사도, 높을수록 좋음)를 더한 형태예요. 순위는 키가 볼 수 있는 에이전트만 대상으로 하므로, 두 에이전트로 제한된 키는 어떤 쿼리든 그 두 개만 반환받아요. agent_search_embedding_model이 없으면 쿼리는 400 agent_search_not_configured를 반환하고, 임베딩 호출이 실패하면 요청은 503 agent_search_unavailable을 반환합니다. 에이전트 임베딩은 프로세스당 한 번 계산되어 에이전트 카드가 바뀔 때까지 재사용돼요.

MCP 클라이언트는 같은 검색을 가상 도구로 사용할 수 있어요. object_permissionmcp_tool_search_enabled: true가 있는 키는 /mcp//mcp-rest 양쪽의 tools/list에서 mcp_tool_searchmcp_tool_call 옆에 agent_search(query, top_k)를 보게 됩니다:

$ curl -s -X POST http://localhost:4000/mcp-rest/tools/call \
    -H "Authorization: Bearer ***" \
    -d '{"name":"agent_search","arguments":{"query":"check how many units are left in stock","top_k":1}}' \
  | jq -r '.content[0].text | fromjson'
[
  {
    "agent_id": "b21b8787-8b9e-4c5f-a45c-3f5e4061d70e",
    "agent_name": "warehouse-sql-analyst",
    "description": "Answers questions about stock by running SQL queries against the inventory database",
    "skills": [{"name": "Query stock levels", "description": "Run a SQL query against the inventory database and summarize the stock levels it returns", "tags": ["sql", "analytics"]}],
    "score": 0.37912064119364597
  }
]

키에서 가상 도구를 활성화하는 방법은 MCP Tool Search 문서를 참고하세요.

더 알아보기 (Learn more)