mirror of
https://github.com/hoshikawa2/agent_platform_oci.git
synced 2026-11-06 03:14:12 +00:00
A2A Protocol
This commit is contained in:
9
templates/agent_template_backend/.env.a2a.example
Normal file
9
templates/agent_template_backend/.env.a2a.example
Normal file
@@ -0,0 +1,9 @@
|
||||
# Complemento opcional ao .env do agente. Ajuste para o endereço público real.
|
||||
ENABLE_A2A=true
|
||||
A2A_TASK_MODE=true
|
||||
A2A_PUBLIC_URL=http://localhost:8000
|
||||
A2A_TASK_TIMEOUT_SECONDS=240
|
||||
# AGENT_AUTH_* configura o middleware já fornecido pelo framework.
|
||||
# Exemplo de declaração no Agent Card (não habilita autenticação):
|
||||
# A2A_SECURITY_SCHEMES={"gatewayBearer":{"httpAuthSecurityScheme":{"scheme":"bearer"}}}
|
||||
# A2A_SECURITY=[{"gatewayBearer":[]}]
|
||||
8
templates/agent_template_backend/Dockerfile.a2a
Normal file
8
templates/agent_template_backend/Dockerfile.a2a
Normal file
@@ -0,0 +1,8 @@
|
||||
FROM python:3.12-slim
|
||||
WORKDIR /app
|
||||
COPY libs/agent_framework /agent_framework
|
||||
COPY templates/agent_template_backend /app
|
||||
RUN pip install --no-cache-dir -e '/agent_framework[a2a]' -r requirements.txt
|
||||
ENV ENABLE_A2A=true
|
||||
ENV A2A_TASK_MODE=true
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
@@ -4226,3 +4226,10 @@ Este template inclui o arquivo opcional `config/tool_policies.yaml`. Use `operat
|
||||
Além da execução direta de MCP tools, uma operação transacional pode usar um workflow LangGraph determinístico após `clarification` e confirmação explícita. Configure `execution.mode: workflow` em `config/tool_policies.yaml`, mantenha as definições versionadas em `workflows/` e implemente as actions do domínio no projeto do agente. O runtime genérico está em `agent_framework.workflows`.
|
||||
|
||||
Consulte `libs/agent_framework/docs/TRANSACTIONAL_WORKFLOWS_PT.md` e o exemplo `workflows/devolucao_pedido.v1.yaml`.
|
||||
|
||||
## Agente A2A assíncrono
|
||||
|
||||
O template também pode expor `/a2a`, com Tasks, consulta de status e streaming
|
||||
SSE, usando `ENABLE_A2A=true` e `A2A_TASK_MODE=true`. Consulte
|
||||
[A2A assíncrono](../../docs/A2A_ASSINCRONO.md) para ativação e integração ao Gateway.
|
||||
Os endpoints nativos permanecem disponíveis.
|
||||
|
||||
@@ -168,7 +168,14 @@ def _resolve_identity(req: GatewayRequest, msg) -> tuple[AgentIdentity, dict, Bu
|
||||
return identity, context, business_context, missing_identity_keys
|
||||
|
||||
|
||||
async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False) -> dict:
|
||||
async def _emit_gateway_event(session_id, event, payload, emit_sse, progress):
|
||||
if emit_sse:
|
||||
await sse_hub.emit(session_id, event, payload)
|
||||
if progress is not None:
|
||||
await progress(event, payload)
|
||||
|
||||
|
||||
async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False, progress=None) -> dict:
|
||||
try:
|
||||
msg = await gateway.normalize(req.channel, req.payload)
|
||||
except ValueError as exc:
|
||||
@@ -191,7 +198,7 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
|
||||
stream = sse_hub.stream_for(agent_session_id)
|
||||
async with stream.lock:
|
||||
await sse_hub.emit(agent_session_id, "flow.start", {"session_id": agent_session_id, "message_id": message_id, "agent_id": identity.agent_id}) if emit_sse else None
|
||||
await _emit_gateway_event(agent_session_id, "flow.start", {"session_id": agent_session_id, "message_id": message_id, "agent_id": identity.agent_id}, emit_sse, progress)
|
||||
|
||||
session = await sessions.get(agent_session_id)
|
||||
if not session:
|
||||
@@ -296,11 +303,11 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
)
|
||||
rendered = await gateway.render(response)
|
||||
await telemetry.event("gateway.message.replayed", {"session_id": agent_session_id, "reason": interruption.reason})
|
||||
await sse_hub.emit(agent_session_id, "message.responded", rendered) if emit_sse else None
|
||||
await _emit_gateway_event(agent_session_id, "message.responded", rendered, emit_sse, progress)
|
||||
return rendered
|
||||
|
||||
effective_text = interruption.text
|
||||
await sse_hub.emit(agent_session_id, "session.upserted", {"session_id": agent_session_id, "business_context": business_context.model_dump()}) if emit_sse else None
|
||||
await _emit_gateway_event(agent_session_id, "session.upserted", {"session_id": agent_session_id, "business_context": business_context.model_dump()}, emit_sse, progress)
|
||||
|
||||
await memory.append(
|
||||
agent_session_id,
|
||||
@@ -317,7 +324,7 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
},
|
||||
),
|
||||
)
|
||||
await sse_hub.emit(agent_session_id, "message.received", {"session_id": agent_session_id, "role": "user"}) if emit_sse else None
|
||||
await _emit_gateway_event(agent_session_id, "message.received", {"session_id": agent_session_id, "role": "user"}, emit_sse, progress)
|
||||
history = [m.model_dump(mode="json") for m in await memory.list(agent_session_id)]
|
||||
|
||||
cms_input = {
|
||||
@@ -362,7 +369,7 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
_root_span=True,
|
||||
) as root_span:
|
||||
await telemetry.event("gateway.message.received", trace_context)
|
||||
await sse_hub.emit(agent_session_id, "workflow.started", trace_context) if emit_sse else None
|
||||
await _emit_gateway_event(agent_session_id, "workflow.started", trace_context, emit_sse, progress)
|
||||
result = await workflow.ainvoke(
|
||||
{
|
||||
"tenant_id": identity.tenant_id,
|
||||
@@ -396,7 +403,7 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
)
|
||||
|
||||
await checkpoints.put(agent_session_id, {"state": result, "message_id": message_id})
|
||||
await sse_hub.emit(agent_session_id, "workflow.completed", {"session_id": agent_session_id, "route": result.get("route"), "intent": result.get("intent")}) if emit_sse else None
|
||||
await _emit_gateway_event(agent_session_id, "workflow.completed", {"session_id": agent_session_id, "route": result.get("route"), "intent": result.get("intent")}, emit_sse, progress)
|
||||
|
||||
answer = result.get("final_answer") or result.get("answer") or ""
|
||||
await memory.append(
|
||||
@@ -480,8 +487,8 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
)
|
||||
rendered = await gateway.render(response)
|
||||
root_span.set_output(rendered)
|
||||
await sse_hub.emit(agent_session_id, "message.responded", rendered) if emit_sse else None
|
||||
await sse_hub.emit(agent_session_id, "flow.end", {"session_id": agent_session_id, "message_id": message_id}) if emit_sse else None
|
||||
await _emit_gateway_event(agent_session_id, "message.responded", rendered, emit_sse, progress)
|
||||
await _emit_gateway_event(agent_session_id, "flow.end", {"session_id": agent_session_id, "message_id": message_id}, emit_sse, progress)
|
||||
return rendered
|
||||
|
||||
|
||||
@@ -649,3 +656,17 @@ async def get_session_checkpoint(session_id: str):
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown():
|
||||
telemetry.shutdown()
|
||||
|
||||
|
||||
# Optional direct A2A endpoint for agents built with the framework template.
|
||||
if settings.ENABLE_A2A:
|
||||
from agent_framework.a2a.progress import report_progress
|
||||
from agent_framework.a2a.server import install_backend_a2a
|
||||
from agent_framework.security import install_authentication
|
||||
|
||||
install_authentication(app, prefix="AGENT_AUTH")
|
||||
|
||||
async def _a2a_backend_handler(data: dict):
|
||||
return await _process_gateway_message(GatewayRequest(**data), progress=report_progress)
|
||||
|
||||
install_backend_a2a(app, _a2a_backend_handler, agent_profiles, settings)
|
||||
|
||||
Reference in New Issue
Block a user