27 KiB
SPEC-002 — Agent Runtime
1. Visão geral
O Agent Runtime coordena a execução conversacional dos agentes da plataforma OCI. O runtime combina grafos LangGraph, estado por conversa, roteamento de intenções, agentes de domínio, guardrails, judges, memória de longo prazo e persistência. A biblioteca reutilizável do framework fornece serviços e contratos consumidos pelos backends; o backend de referência agent_template_backend compõe esses serviços em um workflow executável.
2. Organização da implementação
| Elemento | Localização | Função |
|---|---|---|
| Runtime compartilhado | libs/agent_framework/src/agent_framework/runtime/ |
Capacidades reutilizáveis para execução dos agentes. |
| Workflow de referência | templates/agent_template_backend/app/workflows/agent_graph.py |
Construção do grafo e implementação dos nós do workflow. |
| Estado do workflow | templates/agent_template_backend/app/state.py |
Definição tipada dos dados transportados entre nós. |
| Configuração dos agentes | templates/agent_template_backend/config/agents.yaml |
Cadastro de agentes e caminhos de políticas/configurações. |
| Configuração de roteamento | templates/agent_template_backend/config/routing.yaml |
Modo do roteador, fallback, limiar e catálogo de intenções. |
| Configuração das ferramentas | templates/agent_template_backend/config/tools.yaml |
Definições de ferramentas utilizadas pelos agentes. |
3. Estado de execução
O workflow utiliza o AgentState abaixo. A declaração TypedDict(total=False) permite que os nós adicionem os campos pertinentes a cada etapa sem exigir a presença de todos os campos em cada estado.
from typing import Any, TypedDict
class AgentState(TypedDict, total=False):
tenant_id: str
agent_id: str
session_id: str
conversation_key: str
workflow_id: str
agent_profile: dict[str, Any]
user_text: str
sanitized_input: str
route: str
intent: str
route_decision: dict[str, Any]
answer: str
final_answer: str
history: list[dict[str, Any]]
context: dict[str, Any]
guardrail_decisions: list[dict[str, Any]]
judge_results: list[dict[str, Any]]
next_state: str
domain: str
mcp_tools: list[str]
mcp_results: list[dict[str, Any]]
rag_results: list[dict[str, Any]]
available_mcp_tools: list[str]
selected_tool_call: dict[str, Any]
pending_tool_call: dict[str, Any]
active_transaction: dict[str, Any]
last_transaction: dict[str, Any]
transaction_status: str
transaction_pre_validation: dict[str, Any]
transaction_evidence: list[dict[str, Any]]
last_transaction_evidence: dict[str, Any]
relevant_transaction_evidence: list[dict[str, Any]]
confirmation_required: bool
confirmation_received: bool
tool_policy_result: dict[str, Any]
missing_parameters: list[str]
supervisor_plan: dict[str, Any]
supervisor_results: list[dict[str, Any]]
agent_responses: list[dict[str, Any]]
pending_topics: list[dict[str, Any]]
handled_topics: list[dict[str, Any]]
operation_results: dict[str, dict[str, Any]]
multi_intent_plan: dict[str, Any]
active_agent: str
route_bypassed: bool
continuity_signal: dict[str, Any]
session_control: str
session_ended: bool
human_handoff_requested: bool
blocked: bool
supervisor_action: str
supervisor_guidance: str
supervisor_attempt: int
supervisor_handover_reason: str
output_supervisor_results: list[dict[str, Any]]
output_guardrails_already_applied: bool
long_term_memories: list[dict[str, Any]]
long_term_memory_context: str
long_term_memory_write_result: dict[str, Any]
long_term_memory_subject_key: str
long_term_memory_load_error: str
4. Fluxo de execução
O workflow é compilado com FrameworkStateGraph(AgentState) e utiliza um checkpointer criado por create_langgraph_checkpointer(self.settings).
flowchart TD
START([START]) --> input_guardrails
input_guardrails -->|continue| load_long_term_memory
input_guardrails -->|blocked| output_guardrails
load_long_term_memory --> routing_decision
routing_decision --> billing_agent
routing_decision --> product_agent
routing_decision --> orders_agent
routing_decision --> support_agent
routing_decision --> handoff
routing_decision --> human_handoff
routing_decision --> end_session
routing_decision --> supervisor_agent
billing_agent --> output_supervisor
product_agent --> output_supervisor
orders_agent --> output_supervisor
support_agent --> output_supervisor
handoff --> output_supervisor
human_handoff --> output_supervisor
end_session --> output_supervisor
supervisor_agent --> output_supervisor
output_supervisor --> output_guardrails
output_guardrails -->|continue| judge
output_guardrails -->|blocked| persist
judge --> supervisor_review
supervisor_review --> persist_long_term_memory
persist_long_term_memory --> persist
persist --> END([END])
O roteamento após input_guardrails depende de state['blocked']. Após routing_decision, a rota em state['route'] seleciona um dos nós registrados, com valor padrão billing_agent quando a chave não está presente. O roteamento após output_guardrails também consulta state['blocked'].
5. Nós do workflow
| Nó | Responsabilidade |
|---|---|
input_guardrails |
Avalia e processa a entrada antes do roteamento. |
load_long_term_memory |
Carrega contexto de memória de longo prazo. |
routing_decision |
Determina o destino de execução da conversa. |
billing_agent |
Executa o agente de faturamento. |
product_agent |
Executa o agente de produtos e serviços. |
orders_agent |
Executa o agente de pedidos. |
support_agent |
Executa o agente de suporte. |
handoff |
Processa o fluxo de transferência previsto pelo workflow. |
human_handoff |
Processa a solicitação de transferência para atendimento humano. |
end_session |
Processa o encerramento de sessão. |
supervisor_agent |
Executa o fluxo associado ao supervisor. |
output_supervisor |
Processa a etapa de supervisão da resposta. |
output_guardrails |
Aplica as políticas de saída. |
judge |
Realiza a avaliação da resposta. |
supervisor_review |
Processa a revisão após o judge. |
persist_long_term_memory |
Executa a etapa de escrita de memória de longo prazo. |
persist |
Executa a etapa final de persistência do workflow. |
Os nós são encapsulados pelo método _node, que os executa no contexto self.langgraph_telemetry.node(name, state).
6. Roteamento
O arquivo config/routing.yaml define as opções principais sob a chave router:
# Roteamento enterprise configurável com MCP-aware intents.
router:
# mode também pode ser definido por variável de ambiente ROUTING_MODE.
# Valores: router | supervisor
mode: router
fallback_agent: billing_agent
confidence_threshold: 0.65
allow_handoff: true
A configuração contém também state_policies para manter determinadas etapas de confirmação ou coleta vinculadas ao agente correspondente, além do catálogo intents com atributos como name, domain, agent, priority, mcp_tools, keywords e examples.
O catálogo de agentes em config/agents.yaml declara os agentes telecom_contas e retail_orders. Cada registro aponta para arquivos de política de prompts, roteamento, guardrails, judges, servidores MCP e ferramentas, além de metadados do domínio.
7. Execução dos agentes e ferramentas
Os nós de domínio encaminham a solicitação para as instâncias dos agentes de faturamento, produtos, pedidos e suporte. A integração com ferramentas MCP é disponibilizada aos agentes por meio da configuração de ferramentas e dos componentes de roteamento de tools. Os resultados de ferramentas podem ser transportados no estado pelo campo mcp_results; os nomes selecionados ficam em mcp_tools.
O estado também comporta selected_tool_call, pending_tool_call, tool_policy_result, missing_parameters e os campos de evidências transacionais. Esses elementos permitem que etapas de preparação, validação, confirmação e execução compartilhem informações ao longo da conversa.
8. Estado transacional
O AgentState mantém em active_transaction os dados da transação em andamento e em last_transaction o registro da última transação encerrada. Os campos transaction_status, transaction_pre_validation, transaction_evidence, last_transaction_evidence, relevant_transaction_evidence, confirmation_required e confirmation_received transportam as informações necessárias ao processamento transacional.
A configuração de roteamento inclui políticas de estado para as etapas WAITING_BILLING_CONFIRMATION, WAITING_PRODUCT_CONFIRMATION, WAITING_ORDER_CONFIRMATION, WAITING_SUPPORT_CONFIRMATION, COLLECTING_BILLING_PARAMETERS, COLLECTING_PRODUCT_PARAMETERS, COLLECTING_ORDER_PARAMETERS e COLLECTING_SUPPORT_PARAMETERS.
A documentação de referência dos fluxos transacionais encontra-se em docs/TRANSACTION_STATE_DEVELOPER_GUIDE.md.
9. Memória e checkpoints
O workflow integra duas etapas explícitas de memória de longo prazo: load_long_term_memory, anterior ao roteamento, e persist_long_term_memory, anterior à persistência final. Os campos long_term_memories, long_term_memory_context, long_term_memory_write_result, long_term_memory_subject_key e long_term_memory_load_error compõem a interface de estado dessa capacidade.
A construção do grafo fornece o resultado de create_langgraph_checkpointer(self.settings) ao método compile. O armazenamento e a recuperação dos checkpoints são executados pelo checkpointer configurado, preservando a continuidade do estado entre interações conforme o backend selecionado.
10. Guardrails, supervisão e judges
A entrada é submetida a input_guardrails antes da carga de memória e do roteamento. Quando a entrada é bloqueada, o fluxo segue diretamente para output_guardrails.
As respostas dos agentes passam por output_supervisor e output_guardrails. Uma resposta não bloqueada segue para judge e supervisor_review; uma resposta bloqueada segue para persist. Os resultados são transportados nos campos guardrail_decisions, output_supervisor_results, judge_results, supervisor_action, supervisor_guidance e campos correlatos do estado.
11. Observabilidade
O workflow envolve cada nó com a instrumentação langgraph_telemetry.node. Essa instrumentação associa a execução à identificação do nó e ao estado da conversa. A rastreabilidade das decisões e etapas de execução é tratada pelos mecanismos de telemetria e observabilidade do framework.
12. Tratamento de erros e recuperação por componente
12.1. Princípios de propagação de falhas
O runtime distingue falhas absorvidas pela própria etapa, resultados negativos de políticas/ferramentas e exceções que impedem a execução do grafo. O contrato de cada componente determina se o processamento continua, se a resposta é alterada ou se a exceção é devolvida ao chamador. O campo blocked indica uma decisão de política no estado, e não equivale genericamente a uma exceção Python.
| Área | Representação utilizada | Efeito na execução |
|---|---|---|
| Guardrails | Decisões com allowed, code e reason; decisões da supervisão com action |
Pode interromper roteamento, substituir/sanitizar a resposta ou continuar. |
| MCP | Resultado de ferramenta com ok, error, tool_name e informações de execução conforme a origem |
O agente utiliza o resultado para controlar a continuação da operação. |
| Memória de longo prazo | Campos long_term_memory_load_error e long_term_memory_write_result |
Uma falha de leitura ou escrita capturada no nó não encerra o grafo. |
| RAG | Resultado de recuperação ou objeto de erro técnico | A falha pode ser incorporada ao contexto de execução do agente. |
| Checkpoints | Exceções do repositório, verificação de integridade e recuperação de versões | A leitura pode selecionar checkpoint válido anterior ou iniciar estado limpo; falhas de armazenamento esgotadas são propagadas. |
| Workflow | Exceções emitidas pelos nós ou por graph.ainvoke |
A execução é marcada como falha na telemetria; a exceção é relançada. |
12.2. Entrada, sessão e guardrails de entrada
O nó input_guardrails verifica primeiro session_ended. Caso a sessão já esteja encerrada, define blocked=True, session_control="END_SESSION", session_ended=True e next_state="SESSION_ENDED", compõe a mensagem configurada em SESSION_ALREADY_ENDED_MESSAGE e registra session.message.rejected_after_end. Essa condição impede que a mensagem prossiga para roteamento e agentes.
Para mensagens de sessões ativas, o pipeline avalia os guardrails e guarda as decisões em guardrail_decisions. Se alguma decisão possuir allowed=False, o nó produz uma resposta segura, estabelece route="blocked", intent="input_guardrail_blocked", limpa as listas de ferramentas e resultados MCP daquele turno e segue pelo ramo de saída. A função _input_guardrail_user_message contém respostas específicas para COER, INPUT_SIZE, DLEX_IN, PINJ, TOX e CMP, além de uma resposta padrão para os demais códigos. O motivo detalhado permanece nas decisões e na telemetria; a mensagem ao usuário é controlada pelo runtime.
A avaliação registra guardrails.input.completed, decisões individuais na telemetria de guardrails e eventos GRL por meio do observer. No bloqueio de entrada, não são executados o nó de memória anterior ao roteamento, o roteador nem ferramentas do agente. O ramo segue para output_guardrails e, conforme as condições do grafo, termina em persist.
Referência: templates/agent_template_backend/app/workflows/agent_graph.py (input_guardrails, _input_guardrail_user_message, _after_input_guardrails).
12.3. Roteamento, classificação e continuidade
A configuração do roteamento é carregada por routing/config_loader.py; a ausência do arquivo resulta em FileNotFoundError. O EnterpriseRouter contém tratamento localizado para erros de chamadas de classificação e de interpretação das saídas do modelo. No classificador semântico de entradas esperadas, uma falha de llm.ainvoke é registrada com logger.warning e a função retorna (None, None), permitindo o uso dos caminhos subsequentes do roteamento em vez de transformar essa falha em uma classificação válida.
O fluxo utiliza as configurações fallback_agent e confidence_threshold, assim como políticas de estado em routing.yaml. A seleção de fallback é uma decisão de roteamento; não constitui, por si só, um código de erro. As estruturas route_decision, continuity_signal, pending_topics e multi_intent_plan preservam informações para retomada e roteamento de turnos correlatos.
A recuperação de falhas deve ser interpretada conforme o método efetivamente invocado: nem todo erro interno de roteamento é absorvido pelo mesmo except; exceções que escapem do nó alcançam o tratamento de falha do workflow em ainvoke.
Referências: libs/agent_framework/src/agent_framework/routing/config_loader.py, routing/enterprise_router.py, routing/continuity.py; templates/agent_template_backend/app/workflows/agent_graph.py (routing_decision, ainvoke).
12.4. Execução de agentes, argumentos e ferramentas MCP
A preparação das ferramentas é realizada pelo runtime reutilizável em runtime/agent_runtime.py. O método _prepare_mcp_call consulta o roteador MCP e devolve o erro de preparação como resultado estruturado quando prepare_call o retorna. Se o roteador de ferramentas não estiver disponível, o runtime produz um resultado negativo com ok=False, tool_name e a descrição MCP Tool Router indisponível.
No mapeamento de parâmetros, erros de expressão regular (re.error, IndexError, ValueError) são capturados e registrados como mcp.parameter.regex_extract_failed. Na extração por LLM, indisponibilidade do modelo ou da mensagem e exceções de inferência são registradas como mcp.parameter.llm_extract_failed. O processamento dos argumentos e o retorno da chamada continuam sujeitos à validação da ferramenta e às políticas do agente; não se assume sucesso pela simples ausência de exceção.
Resultados MCP podem expor ok=False e um campo error, inclusive para falhas de validação e execução. A implementação emite NOC.MCP_TOOL_FAILED em um dos caminhos que agregam resultados negativos de ferramentas. O tratamento de transações, confirmações, evidências e resultados parciais é realizado sobre os dados de execução, incluindo active_transaction, transaction_pre_validation, transaction_evidence e operation_results, conforme o tipo de operação.
Referência: libs/agent_framework/src/agent_framework/runtime/agent_runtime.py (_prepare_mcp_call, processamento de parâmetros, execução e agregação de resultados das ferramentas).
12.5. RAG e recuperação de conhecimento
O runtime encapsula falhas de recuperação de conhecimento em um resultado técnico que inclui technical_error=True, technical_error_in_rag=True, status="error" e a descrição em error. Essa representação permite que o agente diferencie falha de recuperação de uma busca concluída sem evidência útil.
O serviço RAG também valida pré-condições: RagService.generate_answer lança RuntimeError quando utilizado sem LLM, e o kbdb_service.py possui verificações de configuração/assinatura e impede operações de ingestão/publicação quando o provider KBDB está configurado exclusivamente para serving. Na ingestão, diretório inexistente pode produzir FileNotFoundError e chunk_overlap >= chunk_size produz ValueError. Esses erros de inicialização e administração não devem ser confundidos com um resultado de busca vazio em atendimento.
Referências: libs/agent_framework/src/agent_framework/runtime/agent_runtime.py (tratamento técnico RAG); rag/rag_service.py, rag/kbdb_service.py, rag/ingest.py.
12.6. Memória de longo prazo
O nó load_long_term_memory captura exceções do long_term_memory_manager.load(state). Quando a leitura falha, publica long_term_memory.load.failed, retorna long_term_memories=[], long_term_memory_context="" e registra a exceção textual em long_term_memory_load_error. A conversa continua sem o contexto de memória recuperado naquele turno.
O nó persist_long_term_memory captura exceções do long_term_memory_manager.persist_turn(state). Uma gravação concluída publica long_term_memory.persist.completed e armazena seu retorno em long_term_memory_write_result; uma falha publica long_term_memory.persist.failed e retorna {"saved": 0, "error": str(exc)} no mesmo campo. A falha de persistência de memória de longo prazo é, portanto, isolada no nó e não provoca automaticamente o encerramento excepcional do grafo.
Esses campos descrevem a memória de longo prazo do workflow e são distintos da persistência de checkpoints LangGraph.
Referência: templates/agent_template_backend/app/workflows/agent_graph.py (load_long_term_memory, persist_long_term_memory).
12.7. Checkpoints, tentativas e integridade
O grafo é compilado com o checkpointer criado por create_langgraph_checkpointer(self.settings) e utiliza conversation_key ou session_id como thread_id em ainvoke. A camada de repositório em checkpoints/checkpoint_repository.py implementa tentativas para operações protegidas por _with_retry: após uma exceção, calcula espera exponencial limitada com jitter, registra checkpoint.<operation>.retry e tenta novamente até retry_policy.max_attempts. Esgotadas as tentativas, relança a última exceção.
Na recuperação, recover_latest examina registros recentes, desconsidera registros que não satisfazem is_valid_checkpoint e ignora checkpoints com CheckpointIntegrityError, registrando checkpoint.recovery.skip_corrupt. Quando não resta checkpoint válido após falhas de integridade, a recuperação retorna None para permitir início sem estado restaurado. A compactação executada após uma gravação é isolada: uma falha registra checkpoint.compaction.failed e não desfaz a gravação já concluída.
A estratégia de recuperação de integridade não garante preservação do histórico se nenhum checkpoint válido puder ser recuperado. Falhas de comunicação com o armazenamento que excedam a política de retry seguem o caminho de exceção do componente que invocou a operação.
Referências: libs/agent_framework/src/agent_framework/checkpoints/checkpoint_repository.py, checkpoints/langgraph_saver.py; templates/agent_template_backend/app/workflows/agent_graph.py (ainvoke).
12.8. Supervisão de saída e guardrails de saída
O output_supervisor avalia a resposta e interpreta RailAction. Para ALLOW, SANITIZE e OBSERVE, utiliza decision.candidate; para HANDOVER, entrega uma mensagem de encaminhamento ao especialista; para as demais ações, utiliza decision.fallback_message. A ação, orientação e resultados são gravados em supervisor_action, supervisor_guidance e output_supervisor_results.
O nó marca output_guardrails_already_applied=True. Quando output_guardrails recebe esse sinal, retorna a resposta já preparada sem executar novamente o pipeline. Caso contrário, executa guardrails.run_output, registra as decisões, os bloqueios e a conclusão GRL, e devolve final_answer.
O executor paralelo de rails (guardrails/parallel_executor.py) converte falhas inesperadas de tarefas em RailResult: escolhe RailAction.BLOCK quando fail_closed está ativo e RailAction.OBSERVE no outro caso. Esse comportamento é uma política do executor e não implica que todo guardrail falhe fechado. O executor também administra cancelamentos e término antecipado quando uma ação terminal é produzida.
Referências: templates/agent_template_backend/app/workflows/agent_graph.py (output_supervisor, output_guardrails); libs/agent_framework/src/agent_framework/guardrails/parallel_executor.py.
12.9. Judges e revisão do supervisor
O nó judge constrói o contexto de avaliação com resultados MCP, RAG e evidências transacionais relevantes, executa a avaliação e grava os resultados em judge_results. A revisão posterior utiliza self.supervisor.review e registra supervisor.review.completed. Uma reprovação ou recomendação de um judge integra o mecanismo de avaliação e revisão; não é, por definição, uma exceção de infraestrutura.
A camada de judges contém validação de configuração e de resposta estruturada. O cliente do judge calibrado lança ValueError para tarefas não suportadas e quando a saída estruturada é inválida. A construção do LLM e a leitura de configurações de judges também podem produzir RuntimeError ou ValueError nas condições verificadas em judges/judge.py. As exceções não capturadas pelo nó propagam-se para ainvoke.
Referências: templates/agent_template_backend/app/workflows/agent_graph.py (judge, supervisor_review); libs/agent_framework/src/agent_framework/judges/judge.py, judges/calibrated/llm_client.py.
12.10. Persistência final e falha global do workflow
O nó persist produz a observabilidade de conclusão do atendimento, incluindo o evento IC AGENT_COMPLETED. A execução pública do grafo acontece em ainvoke, que cria a configuração de thread, inicia a telemetria de workflow e emite os eventos de início NOC.001 e AGENT_STARTED antes de invocar self.graph.ainvoke.
Se graph.ainvoke conclui, o workflow é marcado como concluído e seu estado final é retornado. Se uma exceção escapa do grafo, o except Exception de ainvoke chama workflow_telemetry.failed("agent_workflow", exc), emite NOC.005 com error e exception_type e relança a mesma exceção. Nesse caminho, o método não converte automaticamente o erro em uma resposta conversacional de sucesso.
Falhas absorvidas por nós como memória de longo prazo não acionam esse except global. Já erros não tratados durante roteamento, execução dos agentes, salvamento de checkpoints ou outras etapas podem alcançar esse limite e produzir a falha geral do workflow.
Referência: templates/agent_template_backend/app/workflows/agent_graph.py (persist, ainvoke).
12.11. Eventos e rastreamento operacional
A instrumentação _node envolve a execução de cada nó em self.langgraph_telemetry.node(name, state). Os mecanismos complementares usam spans de workflow, eventos semânticos e o observer para GRL, IC e NOC. Entre os registros concretos presentes no fluxo estão session.message.rejected_after_end, guardrails.input.completed, guardrails.output.completed, output_supervisor.completed, supervisor.review.completed, long_term_memory.load.failed, long_term_memory.persist.failed, long_term_memory.persist.completed e workflow_telemetry.failed na falha global.
Erros operacionais devem ser correlacionados por conversation_key ou session_id, juntamente com tenant_id e agent_id quando disponíveis. A ocorrência de um evento de falha de memória ou de ferramenta não implica necessariamente falha global do workflow; a determinação depende de o componente ter retornado um estado/resultado ou propagado uma exceção.
Referências: templates/agent_template_backend/app/workflows/agent_graph.py; libs/agent_framework/src/agent_framework/observability/.
13. Interfaces de integração
| Interface | Integração no runtime |
|---|---|
| LangGraph | Construção, compilação e execução do grafo de estado. |
| Agentes de domínio | Nós especializados para faturamento, produtos, pedidos e suporte. |
| MCP | Seleção, parametrização e execução de ferramentas pelos agentes. |
| RAG | Recuperação de informações incorporada à execução dos agentes. |
| Guardrails | Avaliação da entrada e da saída. |
| Judges | Avaliação posterior à geração da resposta. |
| Memória | Leitura e escrita de informações de longo prazo. |
| Checkpointer | Persistência do estado do grafo. |
| Observabilidade | Telemetria por nó e rastreamento do processamento. |
14. Referências de implementação
libs/agent_framework/src/agent_framework/runtime/templates/agent_template_backend/app/state.pytemplates/agent_template_backend/app/workflows/agent_graph.pytemplates/agent_template_backend/config/agents.yamltemplates/agent_template_backend/config/routing.yamltemplates/agent_template_backend/config/tools.yamlSPEC-004 — MCP GatewaySPEC-005 — GuardrailsSPEC-007 — ObservabilitySPEC-012 — Canonical Contracts