Módulo de Entrada HTTP¶
O módulo de Entrada HTTP fornece um servidor webhook que permite que sistemas externos invoquem agentes Symbiont através de requisições HTTP. Este módulo permite integração com serviços externos, webhooks e APIs expondo agentes através de endpoints HTTP.
Visão Geral¶
Nesta branch, cada requisição HTTP de raciocínio é executada de forma
independente, mesmo que o agente registrado já esteja ativo. O código-fonte
registrado e a camada de segurança selecionam um executor de ferramentas
congelado antes da inferência. Limites de CPU, de memória e de tempo de execução
restringem essa invocação. Os workers governados também compartilham a CPU, a
memória e o pool de workers configurados do supervisor com as execuções do
agendador e da CLI que usam o mesmo diretório de estado privado; veja
orçamentos compartilhados. As respostas bem-sucedidas incluem
audit com run_id, path e public_key. Falhas no armazenamento obrigatório
interrompem os efeitos seguintes, e as requisições descartadas mantêm a
responsabilidade pela limpeza. Veja auditoria de execução e o
guia da branch.
O módulo de Entrada HTTP consiste em:
- Servidor HTTP: Um servidor web baseado em Axum que escuta requisições HTTP recebidas
- Autenticação: Suporte para autenticação baseada em Bearer token e JWT
- Roteamento de Requisições: Regras de roteamento flexíveis para direcionar requisições para agentes específicos
- Controle de Resposta: Formatação de resposta configurável e códigos de status
- Recursos de Segurança: Suporte CORS, limites de tamanho de requisição e registro de auditoria
- Gerenciamento de Concorrência: Limitação de taxa de requisições integrada e controle de concorrência
- Invocação de LLM com ToolClad: Cada requisição invoca o agente registrado de forma independente através do provedor de LLM configurado e do loop governado de chamada de ferramentas ORGA, inclusive quando outra invocação está ativa
O módulo é compilado condicionalmente com a flag de recurso http-input e integra-se perfeitamente com o runtime de agentes Symbiont.
Configuração¶
O módulo de Entrada HTTP é configurado usando a estrutura HttpInputConfig:
Configuração Básica¶
use symbiont_runtime::http_input::HttpInputConfig;
use symbiont_runtime::types::AgentId;
let config = HttpInputConfig {
bind_address: "127.0.0.1".to_string(),
port: 8081,
path: "/webhook".to_string(),
agent: AgentId::from_str("webhook_handler")?,
// ... other fields
..Default::default()
};
Campos de Configuração¶
| Campo | Tipo | Padrão | Descrição |
|---|---|---|---|
bind_address |
String |
"127.0.0.1" |
Endereço IP para vincular o servidor HTTP |
port |
u16 |
8081 |
Número da porta para escutar |
path |
String |
"/webhook" |
Endpoint de caminho HTTP |
agent |
AgentId |
Novo ID | Agente padrão para invocar para requisições |
auth_header |
Option<String> |
None |
Bearer token para autenticação |
jwt_public_key_path |
Option<String> |
None |
Caminho para arquivo de chave pública JWT |
max_body_bytes |
usize |
65536 |
Tamanho máximo do corpo da requisição (64 KB) |
concurrency |
usize |
10 |
Máximo de requisições concorrentes |
routing_rules |
Option<Vec<AgentRoutingRule>> |
None |
Regras de roteamento de requisições |
response_control |
Option<ResponseControlConfig> |
None |
Configuração de formatação de resposta |
forward_headers |
Vec<String> |
[] |
Cabeçalhos para encaminhar aos agentes |
cors_origins |
Vec<String> |
[] |
Origens CORS permitidas (vazio = CORS desabilitado) |
audit_enabled |
bool |
true |
Habilitar registro de auditoria de requisições |
Regras de Roteamento de Agentes¶
Rotear requisições para diferentes agentes baseado nas características da requisição:
use symbiont_runtime::http_input::{AgentRoutingRule, RouteMatch};
let routing_rules = vec![
AgentRoutingRule {
condition: RouteMatch::PathPrefix("/api/github".to_string()),
agent: AgentId::from_str("github_handler")?,
},
AgentRoutingRule {
condition: RouteMatch::HeaderEquals("X-Source".to_string(), "slack".to_string()),
agent: AgentId::from_str("slack_handler")?,
},
AgentRoutingRule {
condition: RouteMatch::JsonFieldEquals("source".to_string(), "twilio".to_string()),
agent: AgentId::from_str("sms_handler")?,
},
];
Controle de Resposta¶
Personalizar respostas HTTP com ResponseControlConfig:
use symbiont_runtime::http_input::ResponseControlConfig;
let response_control = ResponseControlConfig {
default_status: 200,
agent_output_to_json: true,
error_status: 500,
echo_input_on_error: false,
};
Recursos de Segurança¶
Autenticação¶
O módulo de Entrada HTTP suporta múltiplos métodos de autenticação:
Autenticação com Bearer Token¶
Configurar um bearer token estático:
let config = HttpInputConfig {
auth_header: Some("Bearer your-secret-token".to_string()),
..Default::default()
};
Integração com Armazenamento de Segredos¶
Usar referências de segredos para segurança aprimorada:
let config = HttpInputConfig {
auth_header: Some("vault://webhook/auth_token".to_string()),
..Default::default()
};
Autenticação JWT (EdDSA)¶
Configurar autenticação baseada em JWT com chaves públicas Ed25519:
let config = HttpInputConfig {
jwt_public_key_path: Some("/path/to/jwt/ed25519-public.pem".to_string()),
jwt_issuer: Some("https://identity.example.com".to_string()),
jwt_audience: Some("symbiont-http-input".to_string()),
..Default::default()
};
Os JWTs devem conter exp, sub, iss e aud. O emissor deve corresponder a jwt_issuer e a audiência deve incluir jwt_audience. Ambos os valores de configuração são obrigatórios quando a autenticação JWT está ativa. O sujeito não pode estar vazio nem exceder 512 bytes. A expiração permite cinco segundos de diferença de relógio. A renovação com o mesmo sujeito, emissor e chave preserva a identidade do chamador.
Migração: configure o emissor e a audiência esperados e emita tokens para este serviço. Tokens para outros serviços são rejeitados mesmo quando usam a mesma chave. A assinatura do webhook continua sendo uma verificação adicional.
Endpoint de Saúde¶
O módulo de Entrada HTTP não expõe seu próprio endpoint /health. Verificações de saúde estão disponíveis através da API HTTP principal em /api/v1/health ao executar symbi up, que inicia o runtime completo incluindo o servidor de API:
# Verificação de saúde via o servidor de API principal (porta padrão 8080)
curl http://127.0.0.1:8080/api/v1/health
# => {"status": "ok"}
Se você precisar de probes de saúde especificamente para o servidor de Entrada HTTP, redirecione seu load balancer para o endpoint de saúde da API principal.
Controles de Segurança¶
- Apenas Loopback por Padrão:
bind_addresspadrão é127.0.0.1-- o servidor só aceita conexões locais a menos que configurado explicitamente de outra forma - CORS Desabilitado por Padrão:
cors_originspadrão é uma lista vazia, significando que CORS está desabilitado; adicione origens específicas para habilitar acesso cross-origin. Um"*"literal emcors_originsé rejeitado no startup — o servidor de Entrada HTTP se recusará a iniciar com uma origem curinga. (Adicionado na auditoria pós-v1.13.0; vejaSECURITY_AUDIT.mdM1.) - Limites de Tamanho de Requisição: Tamanho máximo configurável do corpo previne esgotamento de recursos
- Limites de Concorrência: Semáforo integrado controla processamento de requisições concorrentes
- Registro de Auditoria: Registro estruturado de todas as requisições recebidas quando habilitado
- Resolução de Segredos: Integração com Vault e armazenamentos de segredos baseados em arquivo
Exemplo de Uso¶
Iniciando o Servidor de Entrada HTTP¶
use symbiont_runtime::http_input::{HttpInputConfig, start_http_input};
use symbiont_runtime::secrets::SecretsConfig;
use std::sync::Arc;
// Configurar o servidor de entrada HTTP
let config = HttpInputConfig {
bind_address: "127.0.0.1".to_string(),
port: 8081,
path: "/webhook".to_string(),
agent: AgentId::from_str("webhook_handler")?,
auth_header: Some("Bearer secret-token".to_string()),
audit_enabled: true,
cors_origins: vec!["https://example.com".to_string()],
..Default::default()
};
// Opcional: Configurar segredos
let secrets_config = SecretsConfig::default();
// Iniciar o servidor
start_http_input(config, Some(runtime), Some(secrets_config)).await?;
Exemplo de Definição de Agente¶
Criar um agente manipulador de webhook em webhook_handler.symbi:
agent webhook_handler(body: JSON) -> Maybe<Alert> {
capabilities = ["http_input", "event_processing", "alerting"]
memory = "ephemeral"
privacy = "strict"
policy webhook_guard {
allow: use("llm") if body.source == "slack" || body.user.ends_with("@company.com")
allow: publish("topic://alerts") if body.type == "security_alert"
audit: all_operations
}
with context = {} {
if body.type == "security_alert" {
alert = {
"summary": body.message,
"source": body.source,
"level": body.severity,
"user": body.user
}
publish("topic://alerts", alert)
return alert
}
return None
}
}
Exemplo de Requisição HTTP¶
Enviar uma requisição webhook para acionar o agente:
curl -X POST http://localhost:8081/webhook \
-H "Content-Type: application/json" \
-H "Authorization: Bearer secret-token" \
-H "Idempotency-Key: 72d6a833-b825-4b22-b50c-206337d77f7c" \
-d '{
"type": "security_alert",
"message": "Suspicious login detected",
"source": "slack",
"severity": "high",
"user": "admin@company.com"
}'
Escolha e guarde um UUID novo para cada tarefa pretendida. Todo envio HTTP exige
exatamente um cabeçalho Idempotency-Key; repita a tentativa com o mesmo ID, a
mesma URI e o mesmo payload JSON. Reutilizar o ID deste exemplo para um trabalho
diferente será recusado. Os remetentes de webhook devem manter um UUID estável por
entrega, ou usar um adaptador que mapeie a identidade de entrega deles para um
UUID estável antes do envio. O servidor não infere identidade a partir da saída do
modelo nem gera um substituto quando o cabeçalho está ausente.
Estados de retentativa¶
Os IDs ocupam um domínio HTTP por projeto canônico. A reivindicação durável
vincula o chamador verificado, a URI da requisição, a entrada JSON e o
código-fonte/as configurações confiáveis do alvo. Os IDs de agentes registrados
podem mudar na reinicialização sem criar uma requisição diferente. Um servidor SDK
autônomo deve preservar o AgentId configurado entre reinicializações. Alterar o
código-fonte, o alvo, o chamador ou o payload sob um ID existente recusa o
trabalho; nenhum conteúdo de cache ou referência de auditoria é retornado a um
chamador diferente.
| Status HTTP | status do corpo |
Significado |
|---|---|---|
| 200 por padrão | completed |
Resultado original persistido; replayed identifica uma resposta salva. |
| 422 | failed |
Uma falha terminal com evidências completas e rastreadas foi persistida; as retentativas a retornam. |
| 409 | in_progress |
Outro dono detém o ID; esta requisição não inicia trabalho algum. |
| 409 | unresolved |
A execução original precisa de reconciliação; inclui a referência de auditoria dela quando disponível. |
| 409 | reconciled |
Retorna uma avaliação do operador assinada separadamente; o ID original não pode executar de novo. |
| 409 | conflict |
O ID está vinculado a outro chamador ou a outra requisição. |
| 400 | invalid_invocation_id |
Cabeçalho UUID ausente, repetido ou inválido. |
| 503 | unavailable |
O armazenamento de invocações obrigatório não pôde autorizar a execução. |
As respostas de estado de invocação incluem os cabeçalhos Idempotency-Key,
Idempotency-Replayed e Cache-Control: no-store. A formatação de sucesso
configurada continua se aplicando aos resultados concluídos; ela não pode
transformar desfechos não resolvidos ou conflitantes em respostas bem-sucedidas.
As origens CORS configuradas permitem e expõem os cabeçalhos de invocação.
Um dono retido mantém a reivindicação durante a preparação, a execução, a limpeza e a gravação do resultado. A desconexão do cliente cancela o trabalho desse dono, mas não libera o ID para executar de novo. A perda do processo antes da persistência do resultado deixa uma reivindicação não resolvida. Os resultados salvos são verificados contra a auditoria assinada original antes do retorno; a recuperação não executa o provedor nem o executor novamente.
Credenciais estáticas compartilhadas representam um único chamador. Um chamador JWT vincula a chave configurada, o emissor assinado e o subject; os campos de expiração/renovação não o alteram. Rotacionar credenciais ou material de chave faz um ID existente entrar em conflito, em vez de criar silenciosamente uma segunda tarefa. As reivindicações valem para todo o projeto, em todos os listeners HTTP, então use UUIDs aleatórios novos e preserve o armazenamento junto com as evidências de auditoria dele. Veja identidades persistentes de invocação para os limites de armazenamento.
Resposta Esperada¶
Cada requisição de raciocínio retorna o próprio resultado, inclusive quando outra
invocação do mesmo agente está ativa. A antiga resposta de entrega
execution_started/message_id não é mais usada nesta rota. As respostas
bem-sucedidas incluem a referência pública de auditoria da execução,
invocation_id, replayed, total_usage e o snapshot do budget compartilhado.
Valores ilustrativos:
{
"status": "completed",
"agent_id": "11111111-1111-4111-8111-111111111111",
"response": "Task complete.",
"tool_runs": [],
"termination_reason": "Completed",
"iterations": 1,
"audit": {
"run_id": "22222222-2222-4222-8222-222222222222",
"path": "/srv/control/.symbiont/governed/11111111-1111-4111-8111-111111111111.22222222-2222-4222-8222-222222222222.jsonl",
"public_key": "<hex-encoded-public-key>"
},
"model": "<configured-model>",
"provider": "<configured-provider>",
"latency_ms": 4821,
"timestamp": "2024-01-15T10:30:00Z"
}
tool_runs resume as observações de ferramentas correlacionadas, incluindo
negações e falhas de validação. A presença dele não prova que um efeito foi
executado, e status: completed pode acompanhar uma resposta de recusa por
política. Use os argumentos normalizados exatos, a decisão e os registros de
efeito do journal protegido para verificação. Falhas na auditoria obrigatória ou
na limpeza retornam erro, mesmo que um efeito anterior já tenha ocorrido. O
audit.path é um caminho no host do runtime, não uma URL de download.
Invocação de LLM com Ferramentas ToolClad¶
Cada requisição HTTP de raciocínio inicia uma invocação governada independente, inclusive quando o agente registrado já tem outra invocação ativa.
Como funciona¶
- Com um runtime anexado, resolva o agente a partir do registro confiável. Congele o código-fonte, o sandbox e as configurações de recursos selecionados; rejeite agentes ausentes, seleções ambíguas e camadas de segurança incompatíveis antes da inferência. Um servidor SDK autônomo usa o agente/executor genérico configurado explicitamente nele.
- Construa o system prompt apenas a partir do código-fonte do agente
selecionado. Um
system_promptopcional fornecido pelo chamador continua com limite de tamanho e registrado; ele não fornece autoridade de política, de principal nem de sandbox. Construa a mensagem de usuário a partir do payload da requisição. - Descubra as ferramentas ToolClad no projeto congelado e abra um journal assinado, privado e obrigatório. O loop ORGA permite até 15 iterações. Os prazos registrados e os selecionados pelo agente restringem os limites do loop e das ferramentas; o limite padrão por ferramenta é de 120 segundos.
- Prepare e normalize as chamadas propostas antes do Cedar. Aprovações exatas obrigatórias, auditoria obrigatória e autorização de uso único precedem os efeitos. IDs de chamada duplicados ou vazios são rejeitados; os resultados precisam se correlacionar com as chamadas efetivamente preparadas.
- Aguarde a limpeza do worker e o registro terminal no journal. As respostas
bem-sucedidas incluem a resposta final, os desfechos das ferramentas, os
metadados de provedor/modelo e a referência
audit. O cancelamento mantém a responsabilidade pela limpeza; erros no armazenamento obrigatório ou na limpeza não podem produzir silenciosamente um resultado bem-sucedido.
Veja chamadas preparadas e auditoria de execução. Os limites de recursos por invocação não estabelecem a admissão agregada de requisições.
Detecção automática de provedor¶
O cliente LLM é inicializado a partir de variáveis de ambiente na inicialização do servidor. O primeiro provedor cuja chave de API está definida vence, nesta ordem:
| Variável de ambiente | Provedor | Override de modelo | Override de URL base |
|---|---|---|---|
OPENROUTER_API_KEY |
OpenRouter | OPENROUTER_MODEL (padrão: anthropic/claude-sonnet-4) |
OPENROUTER_BASE_URL |
OPENAI_API_KEY |
OpenAI | CHAT_MODEL (padrão: gpt-4o) |
OPENAI_BASE_URL |
ANTHROPIC_API_KEY |
Anthropic | ANTHROPIC_MODEL (padrão: claude-sonnet-4-20250514) |
ANTHROPIC_BASE_URL |
Sem um provedor de inferência configurado, as requisições de raciocínio retornam um erro. Endpoints locais configurados pelo operador continuam suportados.
Campos de entrada¶
O corpo JSON do webhook é interpretado da seguinte forma quando o caminho de LLM é tomado:
promptoumessage-- usado como a mensagem de usuário. Se nenhum estiver presente, o payload inteiro é formatado e passado como a descrição da tarefa.system_prompt-- system prompt opcional fornecido pelo chamador, anexado ao system prompt derivado do DSL. Limitado a 4096 bytes e registrado. Trate como uma superfície de prompt-injection: sempre aplique autenticação ao expor este endpoint a chamadores não confiáveis.
Formato normalizado de chamada de ferramentas¶
O cliente LLM normaliza o function calling do OpenAI/OpenRouter para o mesmo formato de bloco de conteúdo usado pela API de Messages da Anthropic. Independentemente do provedor, cada bloco de conteúdo de resposta é {"type": "text", "text": "..."} ou {"type": "tool_use", "id": "...", "name": "...", "input": {...}}, e stop_reason é "end_turn" ou "tool_use".
Padrões de Integração¶
Endpoints de Webhook¶
Configurar diferentes agentes para diferentes fontes de webhook:
let routing_rules = vec![
AgentRoutingRule {
condition: RouteMatch::HeaderEquals("X-GitHub-Event".to_string(), "push".to_string()),
agent: AgentId::from_str("github_push_handler")?,
},
AgentRoutingRule {
condition: RouteMatch::JsonFieldEquals("source".to_string(), "stripe".to_string()),
agent: AgentId::from_str("payment_processor")?,
},
];
Integração com Gateway de API¶
Usar como serviço backend atrás de um gateway de API:
let config = HttpInputConfig {
bind_address: "0.0.0.0".to_string(),
port: 8081,
path: "/api/webhook".to_string(),
cors_origins: vec!["https://example.com".to_string()],
forward_headers: vec![
"X-Forwarded-For".to_string(),
"X-Request-ID".to_string(),
],
..Default::default()
};
Integração com Verificação de Saúde¶
O módulo de Entrada HTTP não inclui um endpoint de saúde dedicado. Use o endpoint de saúde da API principal (/api/v1/health) para integração com load balancers e monitoramento. Veja a seção Endpoint de Saúde acima para detalhes.
Tratamento de Erros¶
O módulo de Entrada HTTP fornece tratamento de erros abrangente:
- Erros de Autenticação: Retorna
401 Unauthorizedpara tokens inválidos - Limitação de Taxa: Retorna
429 Too Many Requestsquando limites de concorrência são excedidos - Erros de Payload: Retorna
400 Bad Requestpara JSON malformado - Desfechos de Invocação: Retorna os estados explícitos de retentativa acima; trabalho não resolvido nunca é reportado como concluído.
- Erros do Servidor: Falhas de runtime não classificadas retornam um status configurável com uma mensagem pública genérica.
Monitoramento e Observabilidade¶
Registro de Auditoria¶
Quando audit_enabled é true, o módulo registra informações estruturadas sobre todas as requisições:
INFO HTTP Input: Received request with 5 headers
INFO Agent webhook_handler is running, dispatching via communication bus
INFO Runtime execution dispatched for agent webhook_handler: message_id=… latency=3ms
Quando o caminho de invocação de LLM é usado, linhas adicionais rastreiam o loop ORGA:
INFO Agent webhook_handler is not running, using LLM invocation path
INFO Invoking LLM for agent webhook_handler: provider=Anthropic model=… tools=4 …
INFO ORGA ACT: executing tool 'nmap_scan' (id=…) for agent webhook_handler
INFO Tool 'nmap_scan' executed successfully
INFO ORGA loop iteration 1 for agent webhook_handler: executed 1 tool(s), continuing
INFO LLM invocation completed for agent webhook_handler: latency=4821ms tool_runs=1 response_len=…
Integração de Métricas¶
O módulo integra-se com o sistema de métricas do runtime Symbiont para fornecer:
- Contagem e taxa de requisições
- Distribuições de tempo de resposta
- Taxas de erro por tipo
- Contagens de conexões ativas
- Utilização de concorrência
Melhores Práticas¶
- Segurança: Sempre usar autenticação em ambientes de produção
- Limitação de Taxa: Configurar limites de concorrência apropriados baseados na sua infraestrutura
- Monitoramento: Habilitar registro de auditoria e integrar com sua stack de monitoramento
- Tratamento de Erros: Configurar respostas de erro apropriadas para seu caso de uso
- Design de Agentes: Projetar agentes para lidar com formatos de entrada específicos de webhook
- Limites de Recursos: Definir limites razoáveis de tamanho de corpo para prevenir esgotamento de recursos
Veja Também¶
- Guia de Introdução
- Guia DSL
- Referência da API
- Loop de Raciocínio (ORGA)
- Contratos de Ferramentas ToolClad
- Documentação do Runtime de Agentes
Uma invocação retida com uma resolução do operador retorna HTTP 409 e
status: "reconciled", a referência de auditoria original dela e um recibo de
resolution assinado separadamente. Ela não retorna um resultado bem-sucedido
fabricado nem executa novamente. Veja
reconciliação pelo operador.