에이전트 게이트웨이
에이전트 게이트웨이 (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 호환 에이전트를 추가할 수 있어요.
- Agents 탭으로 이동합니다
- Add Agent 를 클릭합니다
- 에이전트 이름(예:
ij-local)과 A2A 에이전트의 URL을 입력합니다 - 프로토콜 버전(
1.0또는0.3)을 선택합니다 — LiteLLM이 클라이언트에 제공하는 wire format이에요
URL은 A2A 에이전트의 호출 URL(예: http://localhost:10001)이어야 합니다.
config.yaml에서 에이전트 정의하기
에이전트는 top-level agents 키 아래 config.yaml에서도 선언할 수 있어요. 게이트웨이가 ConfigMap이나 다른 읽기 전용 소스에서 배포될 때 유용하답니다. agent_name과 agent_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 SDK —
a2a/모델 접두사를 사용하는 익숙한/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/send와 message/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_params에 require_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 오류는 가능할 때 요청과 같은 id로 error 필드에 반환됩니다. 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_permission에 mcp_tool_search_enabled: true가 있는 키는 /mcp/와 /mcp-rest 양쪽의 tools/list에서 mcp_tool_search와 mcp_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 문서를 참고하세요.