mirror of
https://github.com/hoshikawa2/agent_platform_oci.git
synced 2026-11-05 19:04:13 +00:00
adjustments: manual v4
This commit is contained in:
@@ -143,6 +143,34 @@ Examples:
|
||||
|
||||
If the core needs to import a concrete agent module in order to work, this separation has probably been broken.
|
||||
|
||||
### Agent extension folders in the template
|
||||
|
||||
`agent_template_backend` materializes selected framework extension points as local folders. These folders **do not duplicate the core**: they hold declarative configuration or domain-specific implementation that is registered with or consumed by generic `agent_framework` mechanisms.
|
||||
|
||||
```text
|
||||
templates/agent_template_backend/
|
||||
├── workflows/
|
||||
│ ├── <workflow>.vN.yaml # versioned deterministic workflow definition
|
||||
│ └── <workflow>.active.yaml # active version selected by the agent
|
||||
└── app/
|
||||
├── workflow_actions/ # business actions invoked by workflow YAML files
|
||||
├── presentation/ # domain-specific deterministic renderers
|
||||
└── observability/ # local telemetry observers/adapters and integration
|
||||
```
|
||||
|
||||
The architectural boundary is:
|
||||
|
||||
| Template folder | Should contain | Remains in the core |
|
||||
| --- | --- | --- |
|
||||
| `workflows/` | declarative, versioned domain YAML files | parser, validation, registry, executor, pause/resume, and workflow runtime |
|
||||
| `app/workflow_actions/` | business functions registered with `@workflow_action` | decorator, registry, execution, retry policy, and idempotency infrastructure |
|
||||
| `app/presentation/` | renderers that turn trusted results into domain responses | renderer registry, response-mode selection, and runtime fallback |
|
||||
| `app/observability/` | agent-specific observer/adapters, mappings, and enrichment | IC/NOC/GRL contracts, correlation, OTEL/Langfuse, exporters, and observability runtime |
|
||||
|
||||
Practical rule: if an implementation could be reused without knowing a specific telecom, retail, billing, order, or other business domain, it likely belongs in `agent_framework`, not in one of these agent folders. The core must also never import concrete modules from these folders; the agent registers its extensions during startup.
|
||||
|
||||
See: [Transactional Workflows and State](./03_transaction_workflows_and_state.md), [Advanced transactional workflows](./13_advanced_transactional_workflows.md), [Observability, Persistence, and Operational Readiness](./11_observability_persistence_and_operational_readiness.md), and [Presentation, recovery, and observability](./15_presentation_recovery_and_observability.md).
|
||||
|
||||
### State, memory, and checkpoint are different concepts
|
||||
|
||||
**Execution state** represents what is happening in the turn and workflow.
|
||||
|
||||
@@ -256,6 +256,27 @@ Esperado: active_transaction é restaurado e concluído sem reiniciar a tool.
|
||||
- `Tuning-Performance/Transaction_Pre_Validation/`
|
||||
- `Tuning-Performance/Transaction_Evidence/`
|
||||
|
||||
### Physical organization of `workflows/` in the template
|
||||
|
||||
In `agent_template_backend`, declarative definitions live in the agent's root `workflows/` folder:
|
||||
|
||||
```text
|
||||
templates/agent_template_backend/
|
||||
└── workflows/
|
||||
├── devolucao_pedido.v1.yaml
|
||||
└── devolucao_pedido.active.yaml
|
||||
```
|
||||
|
||||
This folder belongs to the **agent** because it expresses domain-specific business sequencing, versions, and decisions. The mechanism that interprets these files belongs to the **framework**. Therefore:
|
||||
|
||||
- do not implement parsers, executors, or registries inside `workflows/`;
|
||||
- do not place Python code there; Python actions belong in `app/workflow_actions/`;
|
||||
- keep immutable versions (`*.vN.yaml`) and a valid active definition (`*.active.yaml`);
|
||||
- use `WORKFLOWS_PATH` when a host must point to another directory;
|
||||
- generic pause/resume, retry, validation, or execution changes belong in `agent_framework.workflows`, not in copied template code.
|
||||
|
||||
The complete path is: **YAML in `workflows/` → framework engine → `node.action` → action registry → function in `app/workflow_actions/`**.
|
||||
|
||||
### Transactional workflow engine architectural decision
|
||||
|
||||
> Content consolidated from `docs/ADR_TRANSACTIONAL_WORKFLOW_ENGINE.md`.
|
||||
|
||||
@@ -687,6 +687,44 @@ Validations performed:
|
||||
Note:
|
||||
- The gateway SSE proxy was left as a future step. The `/gateway/message/sse` endpoint already routes and forwards as a normal message; for end-to-end SSE, a proxy from `/gateway/events/{session_id}` to the active backend can be implemented.
|
||||
|
||||
### Role of `app/observability/` in an agent
|
||||
|
||||
The framework contains generic observability infrastructure; the template contains only the agent-specific integration point. In `agent_template_backend`, the reference structure is:
|
||||
|
||||
```text
|
||||
app/observability/
|
||||
├── __init__.py
|
||||
└── telemetry_observer.py
|
||||
```
|
||||
|
||||
`app/observability/` is appropriate for:
|
||||
|
||||
- constructing/configuring the observer used by the agent;
|
||||
- enriching events with host-specific metadata;
|
||||
- adapting the agent contract to external integrations;
|
||||
- registering template callbacks/adapters;
|
||||
- applying agent-specific mappings/overlays when configuration supports them.
|
||||
|
||||
It must **not** contain a second implementation of OpenTelemetry, Langfuse, IC/NOC/GRL, sequence, exporters, correlation, or persistence. Those are cross-cutting mechanisms owned by `agent_framework`.
|
||||
|
||||
Expected dependency direction:
|
||||
|
||||
```text
|
||||
app/observability/telemetry_observer.py
|
||||
│ configures/composes
|
||||
▼
|
||||
agent_framework.observability + shared contracts
|
||||
│
|
||||
├── Langfuse
|
||||
├── OpenTelemetry
|
||||
├── IC / NOC / GRL
|
||||
└── exporters / mappings / pub-sub
|
||||
```
|
||||
|
||||
When creating a new agent, prefer reusing the reference adapter and changing only configuration/domain metadata. If multiple agents need the same change, implement it in the core and keep `app/observability/` thin.
|
||||
|
||||
**Anti-patterns:** importing telemetry SDKs directly in every agent to emit equivalent events; hardcoding historical labels in the agent runtime; using observability events as the source of truth for transaction state; sending full prompts, secrets, PII, or raw financial payloads.
|
||||
|
||||
### Source files
|
||||
|
||||
The files below were consolidated into this manual:
|
||||
|
||||
@@ -67,6 +67,37 @@ Store definitions under `workflows/<name>.vN.yaml` and maintain a valid active d
|
||||
|
||||
Agent actions use `@workflow_action("name")`, matching `node.action`, and implement `(params: dict, state: dict) -> dict`. Import the action module during startup so decorators register. Return serializable data only—never an HTTP client, connection, exception, or coroutine. Complete executable YAML and Python are available in the matching [Portuguese chapter](../pt/13_workflows_transacionais_avancados.md).
|
||||
|
||||
### Organization of `app/workflow_actions/`
|
||||
|
||||
In the template, domain-specific actions live in `app/workflow_actions/`. The distributed example uses:
|
||||
|
||||
```text
|
||||
app/workflow_actions/
|
||||
├── __init__.py
|
||||
└── devolucao.py
|
||||
```
|
||||
|
||||
This directory should contain **only business implementation invoked by workflows** and small helpers directly related to those actions. The workflow engine, decorator, registry, persistence, pause/resume mechanism, and generic idempotency infrastructure remain in `agent_framework`.
|
||||
|
||||
A larger agent can split actions by domain or aggregate:
|
||||
|
||||
```text
|
||||
app/workflow_actions/
|
||||
├── billing.py
|
||||
├── orders.py
|
||||
└── vas.py
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- YAML `node.action` must match the name registered with `@workflow_action`;
|
||||
- import required modules during startup so the registry is populated before first execution;
|
||||
- never make the core import `app.workflow_actions.*`; dependency direction is agent → framework;
|
||||
- do not turn an action into a second router or agent runtime; it should perform one bounded business step;
|
||||
- for external side effects, use framework policy/idempotency infrastructure and persist only confirmed outcomes.
|
||||
|
||||
**Anti-pattern:** copying `agent_framework.workflows` classes into `app/workflow_actions/` to customize a local case. If a generic mechanism is missing, extend the framework through a reusable interface and keep only domain rules in the agent.
|
||||
|
||||
### Idempotent external effects
|
||||
|
||||
Create one `create_idempotency_store(...)` instance at startup and inject it into actions. Build the key from stable business identity, operation, resource, and contract version—not merely a timestamp. `IDEMPOTENCY_PROVIDER` takes precedence over checkpoint, session, and cache providers. For production transactions, enable `IDEMPOTENCY_REQUIRE_DURABLE=true` and select an appropriate `oracle`, `redis`, or `sqlite` provider. Memory does not protect across pods or restarts. Persist the outcome only after the external system confirms the effect.
|
||||
|
||||
@@ -4,6 +4,27 @@
|
||||
|
||||
Use `response.mode: renderer` for trusted structured tool results whose values must not be changed by an LLM. Register each renderer once at startup. Its keyword-only contract accepts `tool_name`, `result`, `state`, and `agent_label`; return `None` when required fields are absent so the configured fallback can run. Select, format, and mask allowed fields—never interpolate the complete payload. The executable YAML and implementation are in the matching [Portuguese chapter](../pt/15_apresentacao_recuperacao_e_observabilidade.md).
|
||||
|
||||
### Organization of `app/presentation/`
|
||||
|
||||
In `agent_template_backend`, domain-specific renderers live in:
|
||||
|
||||
```text
|
||||
app/presentation/
|
||||
├── __init__.py
|
||||
└── tool_renderers.py
|
||||
```
|
||||
|
||||
The folder separates **how a trusted domain result should be presented** from the generic infrastructure that selects and executes renderers. Therefore:
|
||||
|
||||
- `app/presentation/` implements functions such as `financial.balance`, `billing.invoice`, or another agent-specific renderer;
|
||||
- `agent_framework.presentation` owns the registry, call contract, and generic runtime integration;
|
||||
- `config/tools.yaml` selects `response.mode: renderer` and the renderer name;
|
||||
- agent startup imports/registers renderers once.
|
||||
|
||||
Renderers must not fetch data, decide authorization, execute tools, mutate transactional state, or reinterpret a result with an LLM. Their input should already be an authorized/trusted result; the renderer only selects, masks, and formats fields.
|
||||
|
||||
**Anti-pattern:** building a copy of the framework presentation registry or pipeline inside `app/presentation/`. If several agents need the same formatting/selection mechanism, evolve it in the core; only domain-specific presentation remains in the agent.
|
||||
|
||||
### Output supervision and recovery
|
||||
|
||||
The Output Supervisor may allow, sanitize, retry, block, hand over, or observe candidate output. Keep retries bounded, provide objective retry guidance, and revalidate rewritten output. Permanent authorization or dependency failure is not a content retry.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user