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.
|
||||
|
||||
Reference in New Issue
Block a user