adjustments: manual v4

This commit is contained in:
2026-09-13 08:52:57 -03:00
parent e16d19083d
commit 4712bcc0dc
12 changed files with 374 additions and 48 deletions

View File

@@ -144,6 +144,34 @@ Exemplos:
Se o core precisa importar um módulo concreto do agente para funcionar, essa separação provavelmente foi quebrada.
### Pastas de extensão do agente no template
O `agent_template_backend` materializa alguns pontos de extensão do framework em pastas locais. Essas pastas **não duplicam o core**: elas contêm configuração declarativa ou implementação específica do domínio que é registrada/consumida pelos mecanismos genéricos de `agent_framework`.
```text
templates/agent_template_backend/
├── workflows/
│ ├── <workflow>.vN.yaml # definição versionada do fluxo determinístico
│ └── <workflow>.active.yaml # versão ativa selecionada pelo agente
└── app/
├── workflow_actions/ # actions de negócio chamadas pelos workflows YAML
├── presentation/ # renderers determinísticos específicos do domínio
└── observability/ # adapters/observers e integração local de telemetria
```
A fronteira arquitetural é:
| Pasta do template | Deve conter | Permanece no core |
| --- | --- | --- |
| `workflows/` | YAMLs declarativos e versionados do domínio | parser, validação, registry, executor, pause/resume e runtime de workflow |
| `app/workflow_actions/` | funções de negócio registradas com `@workflow_action` | decorator, registry, execução, política de retry e infraestrutura de idempotência |
| `app/presentation/` | renderers que transformam resultados confiáveis em resposta de domínio | registry de renderers, seleção do modo de resposta e fallback do runtime |
| `app/observability/` | observer/adapters, mappings e enriquecimento específicos do agente | contratos IC/NOC/GRL, correlação, OTEL/Langfuse, exporters e runtime de observabilidade |
Regra prática: se uma implementação poderia ser usada sem conhecer telecom, varejo, cobrança, pedido ou outro domínio específico, ela provavelmente pertence ao `agent_framework`, e não a uma dessas pastas do agente. O core também não deve importar módulos concretos dessas pastas; o agente registra suas extensões no startup.
Detalhes: [Workflows Transacionais e Estado](./03_transaction_workflows_and_state.md), [Workflows transacionais avançados](./13_workflows_transacionais_avancados.md), [Observabilidade, Persistência e Prontidão Operacional](./11_observability_persistence_and_operational_readiness.md) e [Apresentação, recuperação e observabilidade](./15_apresentacao_recuperacao_e_observabilidade.md).
### Estado, memória e checkpoint são conceitos diferentes
**Estado de execução** representa o que está acontecendo no turno e no workflow.

View File

@@ -257,6 +257,27 @@ Esperado: active_transaction é restaurado e concluído sem reiniciar a tool.
- `Tuning-Performance/Transaction_Pre_Validation/`
- `Tuning-Performance/Transaction_Evidence/`
### Organização física de `workflows/` no template
No `agent_template_backend`, as definições declarativas ficam na raiz `workflows/` do próprio agente:
```text
templates/agent_template_backend/
└── workflows/
├── devolucao_pedido.v1.yaml
└── devolucao_pedido.active.yaml
```
Essa pasta pertence ao **agente**, porque expressa a sequência de negócio, versões e decisões daquele domínio. O mecanismo que interpreta esses arquivos pertence ao **framework**. Portanto:
- não implemente parser, executor ou registry dentro de `workflows/`;
- não coloque código Python nessa pasta; actions Python ficam em `app/workflow_actions/`;
- mantenha versões imutáveis (`*.vN.yaml`) e uma definição ativa válida (`*.active.yaml`);
- use `WORKFLOWS_PATH` quando o host precisar apontar para outro diretório;
- mudanças genéricas em pause/resume, retry, validação ou execução devem ser feitas no módulo `agent_framework.workflows`, não copiadas para o template.
O fluxo completo é: **YAML em `workflows/` → engine do framework → `node.action` → registry de actions → função em `app/workflow_actions/`**.
### Decisão arquitetural do motor de workflows
> Conteúdo consolidado a partir de `docs/ADR_TRANSACTIONAL_WORKFLOW_ENGINE.md`.

View File

@@ -683,6 +683,44 @@ Validações executadas:
Observação:
- O proxy SSE do gateway foi deixado como etapa futura. O endpoint /gateway/message/sse já roteia e encaminha como mensagem normal; para SSE fim-a-fim, pode-se implementar proxy de /gateway/events/{session_id} para o backend ativo.
### Papel de `app/observability/` no agente
O framework contém a infraestrutura genérica de observabilidade; o template contém apenas o ponto de integração específico do agente. No `agent_template_backend`, a estrutura de referência é:
```text
app/observability/
├── __init__.py
└── telemetry_observer.py
```
`app/observability/` é apropriado para:
- construir/configurar o observer usado pelo agente;
- enriquecer eventos com metadados específicos disponíveis no host;
- adaptar o contrato do agente a integrações externas;
- registrar callbacks/adapters do template;
- aplicar mappings/overlays específicos do agente quando previstos por configuração.
Ele **não** deve conter uma segunda implementação de OpenTelemetry, Langfuse, IC/NOC/GRL, sequence, exporters, correlação ou persistência. Esses mecanismos são transversais e pertencem ao `agent_framework`.
A dependência esperada é:
```text
app/observability/telemetry_observer.py
│ configura/compõe
▼
agent_framework.observability + contratos compartilhados
│
├── Langfuse
├── OpenTelemetry
├── IC / NOC / GRL
└── exporters / mappings / pub-sub
```
Ao criar um novo agente, prefira reutilizar o adapter de referência e alterar somente configuração/metadados de domínio. Se uma mudança for necessária para vários agentes, implemente-a no core e mantenha `app/observability/` fino.
**Anti-padrões:** importar diretamente SDKs de telemetria em cada agente para emitir eventos equivalentes; codificar labels históricos no runtime do agente; usar eventos de observabilidade como fonte de verdade do estado transacional; enviar prompt integral, segredo, PII ou payload financeiro bruto.
### Arquivos de origem
Os arquivos abaixo foram consolidados neste manual:

View File

@@ -114,6 +114,37 @@ async def registrar_devolucao(params: dict, state: dict) -> dict:
O nome do decorator deve ser igual a `node.action`. A assinatura obrigatória é `(params: dict, state: dict) -> dict`. O módulo precisa ser importado no startup para registrar decorators. Actions devem devolver dados serializáveis; não devolva cliente HTTP, conexão, exception ou coroutine.
### Organização de `app/workflow_actions/`
No template, actions específicas do domínio ficam em `app/workflow_actions/`. O exemplo distribuído usa:
```text
app/workflow_actions/
├── __init__.py
└── devolucao.py
```
Esse diretório deve conter **somente implementação de negócio acionável pelo workflow** e pequenas funções auxiliares diretamente relacionadas. O motor de workflows, o decorator, o registry, a persistência, o mecanismo de pause/resume e a infraestrutura genérica de idempotência continuam no `agent_framework`.
Uma organização maior pode separar actions por domínio ou agregado:
```text
app/workflow_actions/
├── billing.py
├── orders.py
└── vas.py
```
Regras:
- o `node.action` do YAML deve corresponder ao nome registrado em `@workflow_action`;
- importe os módulos necessários no startup para que o registry seja populado antes da primeira execução;
- não faça o core importar `app.workflow_actions.*`; o sentido da dependência é do agente para o framework;
- não use a action como um segundo router ou como um novo runtime de agentes; ela deve executar uma etapa de negócio bem delimitada;
- para efeitos externos, use a política e a infraestrutura de idempotência do framework e persista apenas resultados confirmados.
**Anti-padrão:** copiar classes de `agent_framework.workflows` para `app/workflow_actions/` para customizar um caso local. Se o mecanismo genérico não atende, estenda o framework por uma interface reutilizável; mantenha apenas a regra específica no agente.
### Idempotência do efeito externo
```python idempotency-example

View File

@@ -37,6 +37,27 @@ def register_financial_renderers() -> None:
Importe e execute `register_financial_renderers()` uma vez no startup. A assinatura é keyword-only e deve aceitar `tool_name`, `result`, `state` e `agent_label`. Retorne `None` quando faltarem campos obrigatórios para permitir o fallback previsto pelo runtime. Nunca interpolar o payload inteiro: selecione campos permitidos, mascare identidade e formate valores explicitamente.
### Organização de `app/presentation/`
No `agent_template_backend`, renderers específicos do domínio ficam em:
```text
app/presentation/
├── __init__.py
└── tool_renderers.py
```
A pasta existe para separar **como um resultado confiável do domínio deve ser apresentado** da infraestrutura genérica que seleciona e executa renderers. Assim:
- `app/presentation/` implementa funções como `financial.balance`, `billing.invoice` ou outro renderer específico do agente;
- `agent_framework.presentation` mantém registry, contrato de chamada e mecanismo genérico de integração com o runtime;
- `config/tools.yaml` seleciona `response.mode: renderer` e o nome do renderer;
- o startup do agente importa/registra os renderers uma única vez.
Renderers não devem buscar dados, decidir autorização, executar tools, alterar estado transacional ou reinterpretar um resultado usando LLM. A entrada já deve representar um resultado autorizado/confiável; o renderer apenas seleciona, mascara e formata campos.
**Anti-padrão:** criar em `app/presentation/` uma cópia do registry ou do pipeline de apresentação do framework. Se vários agentes precisarem do mesmo mecanismo de formatação/seleção, ele deve evoluir no core; somente a apresentação específica do domínio fica no agente.
### Output Supervisor
O Output Supervisor valida a resposta candidata antes da saída final. Ele pode permitir, sanitizar, solicitar retry, bloquear, entregar a humano ou observar. `OUTPUT_SUPERVISOR_MAX_RETRIES` deve ter limite pequeno; cada retry precisa de orientação objetiva e a resposta reescrita deve passar novamente pelos controles. Não use retry para erro permanente de autorização ou indisponibilidade.