개요
Agent-to-agent (A2A) 통신을 통해 한 에이전트 런타임에 동일한 프로젝트 에 있는 다른 에이전트를 검색하고 호출할 수 있습니다. 오케스트레이션 엔진(OE)은 다음 조치를 수행하여 모든 호출을 중개합니다.
액세스 유효성 검사
자식 실행을 만듭니다.
대상 에이전트 에 요청 전달합니다.
결과를 반환합니다.
HITL(Human-in-the-Loop) 보장 엔드 투 엔드 적용 . 인적 검토 위해 일시 중단된 대상 에이전트 실패를 반환하는 대신 호출을 일시 중지합니다.
A2A를 활성화 하면 이 시퀀스를 따릅니다.
agent.yaml파일 에a2a:차단 추가합니다. 플랫폼은 처음 실행될 때 에이전트의 구성을 OE에 등록하여 동일한 프로젝트 의 다른 에이전트가 에이전트 검색하고 호출할 수 있도록 합니다.OE가 에이전트 에 실행을 디스패치하면 실행 컨텍스트에 수명이 짧은 토큰을 삽입합니다. 에이전트 는 다른 에이전트를 호출할 때 해당 토큰을 투명하게 사용합니다.
에이전트 코드에서
app.a2a_tools()(권장)을 사용하여 A2A를 LLM 도구로 노출하거나 결정론적 라우팅을 위해app._runtime.a2a을 통해 A2A 클라이언트 직접 호출합니다.OE는 액세스 제어를 적용하고, 하위 실행을 상위 실행에 연결하고, 대상에 디스패치하고 결과를 반환합니다.
에이전트.yaml에서 A2A 활성화
Add an a2a: section to your agent.yaml file to make your agent discoverable and callable. If you omit this section, or set a2a.enabled to false, your agent remains invisible to A2A callers and is fully backward compatible with existing deployments.
참고
A2A를 로컬에서 테스트하려면 A2A_JWT_SECRET 환경 변수를 A2A를 호출하거나 호출하는 모든 에이전트 의 .env 파일 에서 동일한 값으로 설정하다 .
다음 예시 완전히 구성된 a2a: 섹션을 보여줍니다.
name: insurance-agent entrypoint: insurance_agent.main:app a2a: enabled: true allowed_callers: - "ws-router-002" skills: - name: policy-lookup description: Look up insurance policy details example_input: '{"policy_id": "POL-123"}' example_output: '{"status": "active", "premium": 240}' - name: get-quote description: Generate an insurance quote input_modes: - "text/plain" output_modes: - "text/plain"
A2A 필드
다음 표에서는 agent.yaml 파일 의 a2a: 섹션에 있는 필드에 대해 설명합니다.
필드 | 유형 | 기본값 | 설명 |
|---|---|---|---|
| 부울 |
| A2A를 통해 에이전트 를 검색 및 호출 가능하게 만듭니다. |
| list[str] |
| 이 에이전트 호출할 수 있는 작업 공간 ID입니다. 빈 목록은 동일한 프로젝트 에 있는 모든 A2A 지원 에이전트 허용합니다. 비어 있지 않은 목록은 허용 목록 역할을 합니다. |
| list[object] |
| 에이전트 검색을 위해 광고하는 스킬입니다. 각 항목에는 |
| list[str] |
| 에이전트 허용하는 MIME (Multi Purpose Internet Mail Extensions) 유형( 예시 : |
| list[str] |
| 에이전트 생성하는 MIME 유형입니다. 검색 필터하다 로 사용됩니다. |
참고
에이전트의 name, skills, input_modes, output_modes, allowed_callers는 스타트업 시 자동으로 OE로 푸시됩니다. 검색 결과에 표시되는 무료 텍스트 설명 및 기능 태그는 agent.yaml가 아닌 API Gateway 작업 공간 엔드포인트를 통해 구성하는 작업 공간의 AgentCard에서 가져옵니다.
다른 상담원에게 전화 걸기
두 가지 방법, 즉 A2A를 LLM 도구로 노출(권장)하거나 A2A 클라이언트 직접 사용하는 두 가지 방법으로 에이전트 코드에서 다른 에이전트를 호출할 수 있습니다. 둘 다 동일한 기본 클라이언트 사용합니다. LLM으로 라우팅 결정을 내릴지, 아니면 결정론적 제어가 필요한지에 따라 접근 방식을 선택하세요.
A2A를 LLM 도구로 사용
app.a2a_tools() 메서드는 두 개의 LangChain StructuredTool 객체 discover_available_agents 및 invoke_a2a_agent을 반환합니다. 이러한 객체를 통해 LLM은 자동으로 다른 에이전트를 검색하고 호출할 수 있습니다. 모델이 자체적으로 다른 에이전트로 라우팅할 수 있도록 기존 도구와 함께 바인딩합니다.
app.a2a_tools() 메서드는 agent.yaml 파일 에서 a2a.enabled이 false인 경우 빈 목록을 반환하므로 모든 에이전트에 포함하는 것이 안전합니다.
다음 예시 A2A 도구를 기존 도구와 함께 바인딩합니다.
from agent_engine_sdk_langgraph import App from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph from langgraph.prebuilt import ToolNode app = App(app_name="router-agent") def build_agent(): llm = app.llm(ChatOpenAI(model="gpt-5.4")) a2a = app.a2a_tools() llm_with_tools = llm.bind_tools( app.get_tool_schemas() + a2a ) tool_node = ToolNode(app.get_tools() + a2a) graph = StateGraph(...) return graph.compile(checkpointer=app.checkpointer())
LLM에 바인딩되면 모델은 두 도구를 이름으로 호출합니다. 이 도구는 다음과 같은 함수 시그니처를 노출합니다.
discover_available_agents()호출 에이전트 가 볼 수 있는 에이전트의 JSON 목록을 반환합니다. 각 항목에는agent_id,name,description,skills및capabilities가 포함됩니다. 호출 에이전트 자체 결과에서 필터링됩니다.invoke_a2a_agent(agent_id, message, custom_headers="")대상 에이전트 호출하고 결과를 JSON 으로 반환합니다.custom_headers을 제공하는 경우 JSON 문자열로 전달합니다( 예시 :'{"x-tenant": "acme"}').
A2A 클라이언트 직접 호출
결정적 라우팅을 사용하려는 경우, 클라이언트 노출하는 속성인 runtime.a2a를 통해 AgentToAgent 클라이언트 에 직접 액세스 할 수 있습니다.
다음 예시 스킬 별로 상담원을 검색하고 첫 번째 일치 항목을 호출합니다.
agents = app._runtime.a2a.discover_agents( skills=["policy-lookup"], limit=10, ) if agents: resp = app._runtime.a2a.invoke_agent( agent_id=agents[0].agent_id, message="Look up policy POL-123", skill="policy-lookup", timeout=120, ) if resp.status == "completed": print(resp.result)
runtime.a2a 속성은 A2A를 사용할 수 없을 때 None을 반환합니다. 이러한 상황이 발생하는 경우를 학습 고려 사항을 참조하세요. 다음 예시 와 같이 클라이언트 메서드를 호출하기 전에 항상 이 대소문자를 확인하세요.
a2a = app._runtime.a2a if a2a is None: ...
참고
app._runtime.a2a를 통해 에이전트 코드의 runtime.a2a에 액세스합니다. 공개 app.a2a 또는 app.runtime 접근자가 존재하지 않습니다. 대부분의 사용 사례에서 app.a2a_tools()는 비공개 속성에 의존하지 않기 때문에 권장되는 대안입니다.
A2클라이언트 SDK 참조
이 섹션에서는 에이전트 코드에서 프로그래밍 방식으로 에이전트를 검색하고 호출하는 메서드를 제공하는 A2A 클라이언트 SDK에 대해 학습 수 있습니다.
클라이언트 SDK를 사용하려면 다음 예시 와 같이 agent_engine_runner_shared.a2a에서 A2A 유형을 가져옵니다.
from agent_engine_runner_shared.a2a import ( AgentToAgent, DiscoveredAgent, AgentSkill, AgentResponse, )
AgentToAgent 클래스
AgentToAgent 는 OE /a2a/discover 및 /a2a/invoke 엔드포인트와 통신하는 HTTP 클라이언트 입니다. 에이전트 코드에서 이 클래스를 직접 구성하는 대신 runtime.a2a의 사전 인증된 인스턴스 사용합니다.
다음 예시 생성자와 해당 매개변수를 보여줍니다.
AgentToAgent(oe_url: str, auth_token: str = "")
다음 표에서는 AgentToAgent 생성자 매개변수에 대해 설명합니다:
Argument | 설명 |
|---|---|
| OE HTTP 기본 URL( 예시 : |
| A2 |
discover_agents() 메서드
discover_agents() 메서드는 호출자가 볼 수 있도록 허용된 에이전트를 반환합니다. 단일 필터하다 매개변수에 여러 값을 전달할 때 에이전트 해당 값 중 하나를 충족하면 일치합니다. 예시 를 들어, skills=["a", "b"]은 두 스킬 중 하나를 광고하는 상담원을 반환합니다. 여러 필터하다 매개변수를 전달하는 경우 에이전트 해당 매개변수를 모두 충족해야 합니다. A2A가 활성화되지 않았고 allowed_callers를 통해 발신자를 제외한 에이전트는 반환되지 않습니다.
다음 예시 이 메서드와 해당 매개변수를 보여줍니다.
discover_agents( project_id: str = "", skills: list[str] | None = None, capabilities: list[str] | None = None, input_modes: list[str] | None = None, limit: int = 50, ) -> list[DiscoveredAgent]
다음 표에서는 discover_agents() 메서드의 매개변수에 대해 설명합니다.
Parameter | 유형 | 설명 |
|---|---|---|
| str | 프로젝트를 검색. 빈 문자열은 호출자의 프로젝트 사용합니다. |
| list[str] | 스킬 이름으로 필터링합니다. 나열된 스킬 중 하나라도 보유한 상담원을 반환합니다. |
| list[str] | 역량 태그를 지정하다 로 필터링합니다. 나열된 기능 중 하나를 가진 에이전트를 반환합니다. |
| list[str] | 허용되는 MIME 유형으로 필터링합니다. 나열된 MIME 유형 중 하나를 허용하는 에이전트를 반환합니다. |
| int | 반환할 최대 결과 수입니다. 기본값은 |
invoke_agent() 메서드
invoke_agent() 메서드는 대상 에이전트 호출하고 결과를 기다립니다. OE는 토큰의 유효성을 검사하고, 액세스 제어를 확인하고, 하위 실행을 생성하고, 에이전트 완료되거나, 사람의 검토 위해 일시 중단되거나, 시간이 초과될 때까지 폴링합니다.
다음 예시 이 메서드와 해당 매개변수를 보여줍니다.
invoke_agent( agent_id: str, message: str, skill: str = "", timeout: float | None = None, custom_headers: dict[str, str] | None = None, ) -> AgentResponse
다음 표에서는 invoke_agent() 메서드의 매개변수에 대해 설명합니다.
Parameter | 유형 | 설명 |
|---|---|---|
| str | 대상 작업 공간 ID. |
| str | 대상 에이전트 에 보낼 프롬프트 또는 메시지입니다. |
| str | 많은 스킬을 광고하는 상담원을 위한 선택적 스킬 힌트입니다. |
| float | 없음 | 시간이 초과되기 전에 대기할 시간(초)입니다. |
| dict[str, str] | None | 대상 에이전트의 실행 컨텍스트로 전달된 키-값 헤더입니다. 키는 예약된 |
응답 데이터 클래스
다음 표에는 모두 변경할 수 없는 데이터 클래스인 응답 유형이 나열되어 있습니다.
클래스 | 필드 |
|---|---|
|
|
|
|
|
|
AgentResponse 상태 처리
다음 표에서는 AgentResponse.status 필드 에 사용할 수 있는 각 값에 대해 설명합니다.
상태 | 의미 | 무엇을 |
|---|---|---|
| 대상 에이전트 성공적으로 완료되었습니다. |
|
| 대상 에이전트 오류를 반환했습니다. |
|
| 대상 에이전트 인적 검토 위해 일시 중단되었습니다. | 일시 중지를 사용자 또는 흐름에 표시합니다. 이를 실패로 간주하지 마세요. |
시간 초과 또는 OE의 4xx 및 5xx 응답과 같은 네트워크 및 HTTP 수준 오류는 discover_agents() 및 invoke_agent() 메서드에서 httpx.HTTPError를 발생시킵니다. status 값이 failed인 AgentResponse는 호출이 OE에 도달했지만 대상 에이전트 자체가 실패했음을 나타냅니다. 네트워크 오류와 에이전트 장애를 모두 처리하다 하려면 호출을 try/except 블록으로 래핑하고 status 값을 확인합니다.
다음 예시 세 가지 상태 값을 모두 처리합니다.
resp = app._runtime.a2a.invoke_agent( agent_id="ws-billing-001", message="Process the payment", ) if resp.status == "completed": handle(resp.result) elif resp.status == "input-required": notify_user( "The billing agent needs human approval before continuing." ) else: log.error("A2A call failed: %s", resp.error)
사용자 지정 헤더 전달
A2A를 사용하여 에이전트 호출에 임의의 키-값 헤더를 첨부하여 테넌트 ID, 추적 태그를 지정하다 또는 위임된 OAuth 토큰과 같은 요청 범위 컨텍스트를 전파할 수 있습니다.
호출된 에이전트에게 헤더 전달
호출된 에이전트 에 헤더를 첨부하려면 invoke_agent()의 custom_headers 매개변수를 사용하여 dict을 전달합니다. invoke_a2a_agent LLM 도구를 사용하는 경우, 헤더를 JSON 문자열로 전달합니다.
다음 예시 직접 클라이언트 사용하여 사용자 지정 헤더를 전달합니다.
app._runtime.a2a.invoke_agent( agent_id="ws-billing-001", message="Charge the customer", custom_headers={ "x-tenant": "acme", "oauth-token": "<delegated-token>", }, )
다음 예시 LLM 도구를 사용하여 사용자 지정 헤더를 전달합니다.
invoke_a2a_agent( agent_id="ws-billing-001", message="Charge the customer", custom_headers='{"x-tenant": "acme"}', )
헤더 키는 대소문자를 구분하지 않는 예약된 a2a- 접두사를 사용해서는 안 됩니다. OE는 a2a-로 시작하는 키가 있는 경우 400 Bad Request 응답으로 요청을 거부합니다. a2a- 접두사는 a2a-token 및 a2a-caller-workspace와 같은 플랫폼 라우팅 및 ID 헤더용으로 예약되어 있습니다.
호출자로부터 헤더 읽기
호출 에이전트 가 전달한 사용자 지정 헤더를 읽으려면 agent_engine_runner_shared.context 클래스의 get_current_custom_headers() 메서드를 사용합니다. 다음 예시 이 메서드를 호출합니다.
from agent_engine_runner_shared.context import get_current_custom_headers headers = get_current_custom_headers() tenant = headers.get("x-tenant")
get_current_custom_headers() dict[str, str]을 반환합니다. 이 메서드는 반환하기 전에 모든 a2a- 플랫폼 헤더를 제거하므로 에이전트 코드에 A2A 토큰이나 라우팅 메타데이터 표시되지 않습니다. 호출자가 사용자 지정 헤더를 보내지 않으면 메서드는 빈 사전을 반환합니다.
액세스 제어 구성
agent.yaml 파일 의 a2a.allowed_callers 필드 다음 동작을 통해 사용자를 검색할 수 있는 에이전트를 제어합니다.
빈 목록(기본값 ): 동일한 프로젝트 의 모든 A2A 지원 에이전트 에이전트 호출하고 검색 결과에서 이를 볼 수 있습니다.
비어 있지 않은 목록: 나열된 작업 공간 ID만 에이전트 호출할 수 있습니다. 다른 모든 에이전트는
403응답을 받으며 검색 결과에서 귀하의 에이전트 볼 수 없습니다.
OE는 발송하기 전에 allowed_callers 목록과 비교하여 호출 에이전트의 ID를 확인합니다. 에이전트 에서 코드를 변경할 필요가 없습니다. 다음 예시 호출을 단일 라우터 에이전트 로 제한합니다.
a2a: enabled: true allowed_callers: - "ws-router-002"
UI 에서 A2A 호출 보기
모든 A2A 호출은 세션의 추적 패널에 자동으로 표시됩니다. 계측이 필요하지 않습니다. 대상 에이전트의 활동은 동일한 세션 추적 내에 중첩되어 표시되므로 한 곳에서 전체 에이전트 간 호출 트리를 추적할 수 있습니다.
각 호출은 A2A: <target agent name> 레이블이 지정된 하위 에이전트 노드 로 렌더링되며, 표시 이름이 없는 경우 대상의 작업 공간 ID 로 돌아갑니다. 노드 통화 상태와 지속 시간이 표시됩니다. 상태는 running로 시작하여 성공 시 done, 실패 시 error로 변경됩니다. 노드 선택하면 이름, 상태, 기간, 디스패치 설명, 스트리밍 출력 및 모든 오류를 보여주는 세부 정보 패널이 열립니다.
대상 에이전트의 자체 도구 호출, LLM 단계 및 메모리 작업은 하위 에이전트 노드 아래에 중첩되어 표시되며, 에이전트 간에 전체 호출 트리를 재구성합니다.
고려 사항
런타임.a2반환 없음
runtime.a2a 다음 조건이 모두 참인 경우에만 클라이언트 반환합니다.
에이전트 코드가 에이전트 샌드박스에서 실행 입니다. 도구 샌드박스에서 실행되는 코드에서는 A2A를 사용할 수 없습니다.
OE URL 은 실행 컨텍스트에 있습니다.
수신 헤더에
a2a-token이(가) 있습니다. OE는 A2A가 구성될 때마다 디스패치할 때마다 이 토큰을 자동으로 삽입합니다.
조건이 충족되지 않으면 runtime.a2a이 None을 반환합니다. app.a2a_tools()를 사용할 때 도구는 예외를 발생시키는 대신 구조화된 오류 JSON 반환합니다.
토큰 수명
A2A 토큰은 기본값 으로 5분 후에 만료됩니다. 일반적인 요청 및 응답 흐름의 경우 이 정도면 충분합니다. 토큰 수명보다 오래 실행 에이전트는 A2A 호출에 대해 401 응답을 받을 수 있습니다. 자동 토큰 새로 고침은 아직 구현되지 않았습니다. 장기 실행 호출의 401 응답을 일시적 실패로 처리합니다.
자체 검색 필터
discover_available_agents LLM 도구는 모델이 자체적으로 호출하지 않도록 결과에서 호출 에이전트의 자체 작업 공간을 필터링합니다. 원시 discover_agents() 클라이언트 메서드를 사용하는 경우, OE는 결과에 자체 에이전트 포함할 수 있습니다. 필요한 경우 직접 필터링합니다.
프로젝트 내 범위
현재 출시하다 에서는2A 검색 및 호출이 동일한 프로젝트 의 에이전트로 제한됩니다. 프로젝트 간 라우팅은 아직 구현되지 않았습니다.
관찰 가능성
이 플랫폼은 모든 A2A 호출을 자동으로 기록하고 연결합니다. 계측을 추가할 필요가 없습니다. 대상은 parent_execution_id를 통해 호출자에 연결된 하위 실행으로 실행됩니다. 공유 root_session_id는 원래 사용자 세션 아래에 전체 교차 에이전트 호출 트리를 그룹화합니다.
예시: 라우터 에이전트
다음 예시 LLM을 사용하여 전문 에이전트 찾아 위임하는 라우터 에이전트 보여줍니다. agent.yaml 파일 A2A를 활성화하고 route 스킬 선언합니다.
name: router-agent entrypoint: router_agent.main:app a2a: enabled: true skills: - name: route description: Route a user request to the best specialist agent
다음 에이전트 코드는 A2A 도구를 자체 도구와 함께 바인딩하고 LLM이 라우팅을 처리하다 수 있도록 합니다.
from agent_engine_sdk_langgraph import App from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph, MessagesState from langgraph.prebuilt import ToolNode app = App(app_name="router-agent") def build_agent(): llm = app.llm(ChatOpenAI(model="gpt-5.4")) a2a = app.a2a_tools() llm_with_tools = llm.bind_tools( app.get_tool_schemas() + a2a ) def call_model(state: MessagesState): return { "messages": [llm_with_tools.invoke(state["messages"])] } graph = StateGraph(MessagesState) graph.add_node("model", call_model) graph.add_node("tools", ToolNode(app.get_tools() + a2a)) graph.set_entry_point("model") graph.add_conditional_edges( "model", lambda s: ( "tools" if s["messages"][-1].tool_calls else "__end__" ), ) graph.add_edge("tools", "model") return graph.compile(checkpointer=app.checkpointer())
런타임에 LLM은 매칭 스킬 광고하는 전문 에이전트 발견하고 invoke_a2a_agent을(를) 통해 호출하고, OE는 액세스 제어를 적용하고 호출의 양쪽을 기록하면서 호출을 중개합니다.
LLM에 의존하는 대신 결정론적으로 라우팅하려면 그래프 노드 내에서 직접 클라이언트 호출합니다.
def delegate(state): a2a = app._runtime.a2a if a2a is None: return {"messages": [("ai", "A2A is unavailable.")]} resp = a2a.invoke_agent( agent_id="ws-insurance-001", message=state["messages"][-1].content, skill="policy-lookup", custom_headers={"x-request-source": "router-agent"}, ) text = ( resp.result if resp.status == "completed" else f"({resp.status}) {resp.error}" ) return {"messages": [("ai", text)]}
다음 단계
상담원 간 통신을 활성화한 후 다음 가이드를 탐색하여 관련 작업에 대해 자세히 학습 수 있습니다.
에이전트의 성능과 배포 상태를 모니터 하려면 에이전트 모니터링을 참조하세요.
배포된 에이전트 인증하고 호출하려면 에이전트 호출을 참조하세요.
에이전트 테스트하고 코드를 반복하려면 에이전트 테스트를 참조하세요.