adjustments: manual v2

This commit is contained in:
2026-09-12 08:04:25 -03:00
parent b7d68317ca
commit 3cb970d8ab
21 changed files with 1174 additions and 76 deletions

View File

@@ -32,7 +32,15 @@ A documentação possui três níveis:
1. **Tutorial principal:** este [`README.md`](README.md) — criação, configuração, execução e teste de um agente do início ao fim.
2. **Arquitetura:** [01 — Arquitetura e Conceitos](docs/developer/pt/01_architecture_and_concepts.md) — componentes, responsabilidades e onde implementar cada coisa.
3. **Referências especializadas:** manuais `02` a `12` — implementação profunda e troubleshooting por capacidade.
3. **Referências especializadas:** manuais `02` a `16` — implementação profunda, código copiável e troubleshooting por capacidade.
As funcionalidades mais recentes estão consolidadas em:
- [13 — Workflows transacionais avançados](docs/developer/pt/13_workflows_transacionais_avancados.md)
- [14 — Routing, parâmetros e estado avançados](docs/developer/pt/14_routing_parametros_e_estado_avancados.md)
- [15 — Apresentação, recuperação e observabilidade](docs/developer/pt/15_apresentacao_recuperacao_e_observabilidade.md)
- [16 — Multiagente, composição e ciclo de vida](docs/developer/pt/16_multiagente_composicao_e_ciclo_de_vida.md)
- [Guardrails e judges externos — implementação completa](docs/EXTERNAL_GUARDRAILS_JUDGES.md)
Se você está começando um novo agente, siga este `README.md` desde o início. Para aprofundamento ou troubleshooting, use os links abaixo.
@@ -11821,9 +11829,25 @@ O conteúdo desta pasta deve ser tratado como uma extensão adicional do framewo
**Use quando:** um `COER`/guardrail de entrada gera mensagem genérica, `route=blocked` carrega resultados antigos ou há dúvida sobre a precedência entre input guardrails, routing e tools.
### [13 — Workflows Transacionais Avançados](docs/developer/pt/13_workflows_transacionais_avancados.md)
**O que é:** pré-validação, actions, versionamento, pausa/retomada, idempotência e replay.
### [14 — Routing, Parâmetros e Estado Avançados](docs/developer/pt/14_routing_parametros_e_estado_avancados.md)
**O que é:** precedência, confirmação semântica, extração híbrida, reconciliação temporal e estados dinâmicos.
### [15 — Apresentação, Recuperação e Observabilidade](docs/developer/pt/15_apresentacao_recuperacao_e_observabilidade.md)
**O que é:** renderers, Output Supervisor, recuperação, voice replay, mappings e Pub/Sub.
### [16 — Multiagente, Composição e Ciclo de Vida](docs/developer/pt/16_multiagente_composicao_e_ciclo_de_vida.md)
**O que é:** isolamento, bundles, supervisor/subagentes, segurança, readiness e certificação.
### Guias de features implementadas
Os manuais `01` a `12` acima organizam os assuntos por área de desenvolvimento. Os guias abaixo documentam cada feature executável individualmente:
Os manuais `01` a `16` acima organizam os assuntos por área de desenvolvimento. Os guias abaixo documentam cada feature executável individualmente:
1. [Autenticação](docs/features/pt-BR/01_authentication.md)
2. [Workflow Transacional Determinístico](docs/features/pt-BR/02_deterministic_transactional_workflow.md)

View File

@@ -32,7 +32,15 @@ The documentation has three clear levels:
1. **Main tutorial:** this [`README_en.md`](README_en.md) — build, configure, run and test an agent end to end.
2. **Architecture:** [01 — Architecture and Concepts](docs/developer/en/01_architecture_and_concepts.md) — components, boundaries and implementation placement.
3. **Specialized references:** manuals `02` through `12` — deep implementation and troubleshooting by capability.
3. **Specialized references:** manuals `02` through `16` — deep implementation, copyable code, and troubleshooting by capability.
The newest capabilities are consolidated in:
- [13 — Advanced transactional workflows](docs/developer/en/13_advanced_transactional_workflows.md)
- [14 — Advanced routing, parameters, and state](docs/developer/en/14_advanced_routing_parameters_and_state.md)
- [15 — Presentation, recovery, and observability](docs/developer/en/15_presentation_recovery_and_observability.md)
- [16 — Multi-agent composition and lifecycle](docs/developer/en/16_multi_agent_composition_and_lifecycle.md)
- [External guardrails and judges — complete implementation](docs/EXTERNAL_GUARDRAILS_JUDGES_en.md)
If you are creating a new agent, follow this `README_en.md` from the beginning. For deeper implementation details or troubleshooting, use the links below.
@@ -11732,9 +11740,25 @@ The content of this folder should be treated as an additional framework extensio
**Use it when:** `COER`/input guardrails generate generic messages, `route=blocked` carries stale results, or you need to reason about precedence between input guardrails, routing, and tools.
### [13 — Advanced Transactional Workflows](docs/developer/en/13_advanced_transactional_workflows.md)
**What it is:** pre-validation, actions, versioning, pause/resume, idempotency, and replay.
### [14 — Advanced Routing, Parameters, and State](docs/developer/en/14_advanced_routing_parameters_and_state.md)
**What it is:** precedence, semantic confirmation, hybrid extraction, temporal reconciliation, and dynamic states.
### [15 — Presentation, Recovery, and Observability](docs/developer/en/15_presentation_recovery_and_observability.md)
**What it is:** renderers, Output Supervisor, recovery, voice replay, mappings, and Pub/Sub.
### [16 — Multi-Agent Composition and Lifecycle](docs/developer/en/16_multi_agent_composition_and_lifecycle.md)
**What it is:** isolation, bundles, supervisor/subagents, security, readiness, and certification.
### Implemented feature guides
The `01` through `12` manuals above organize documentation by development area. The guides below document each executable feature individually:
The `01` through `16` manuals above organize documentation by development area. The guides below document each executable feature individually:
1. [Authentication](docs/features/en/01_authentication.md)
2. [Deterministic Transactional Workflow](docs/features/en/02_deterministic_transactional_workflow.md)

View File

@@ -1,11 +1,16 @@
# External Guardrails / Judges
Este exemplo parte do `agent_template_backend` e demonstra a composição de componentes nativos com políticas pertencentes ao agente.
Este cenário de tuning mede extensões de política do agente. Antes de interpretar os resultados, implemente e valide o contrato descrito no [guia completo](../../docs/EXTERNAL_GUARDRAILS_JUDGES.md).
- `type: external` ativa import dinâmico somente para o componente declarado.
- Guardrail/judge síncrono roda em worker thread via `asyncio.to_thread`.
- Implementação `async` roda concorrente no event loop.
- O framework não importa `app.extensions.*` por padrão.
- Use códigos/names próprios do domínio; não sobrescreva semanticamente o genérico sem deixar a substituição explícita no YAML.
O cenário deve comparar, no mínimo, latência p50/p95/p99, taxa de allow/deny/sanitize, erros e timeouts, custo de judges com LLM e comportamento fail-open/fail-closed. Separe resultados por código/nome e estágio, sem registrar prompts, PII ou payloads financeiros.
Veja também `agent_framework_oci/docs/EXTERNAL_GUARDRAILS_JUDGES.md` e `docs/EXTERNAL_GUARDRAILS_JUDGES.md` no Contas.
Cuidados de leitura:
- `type: external` afeta apenas o componente declarado;
- implementação síncrona usa worker thread e implementação `async` usa o event loop;
- judges executam concorrentemente, então meça contenção do provedor e do pool;
- compare a mesma carga com extensão desligada para obter o overhead incremental;
- execute casos transacionais separadamente, pois `always_run_for_transactional` ignora a amostragem;
- no template corrente os pipelines são globais; caminhos por agente não implicam isolamento automático.
Antes do benchmark, rode `pytest -q tests/test_external_guardrails_judges_documentation.py` para provar que os exemplos do manual ainda carregam e executam.

View File

@@ -1,27 +1,194 @@
# External Guardrails and Judges SPI
# Guardrails e judges externos: guia de implementação
`agent_framework_oci` supports agent-owned guardrails and judges without importing domain code into the core.
Este guia mostra como criar políticas pertencentes ao agente sem acoplar regras de negócio ao `agent_framework`. Use uma extensão externa quando a regra tiver vocabulário, limites, evidências ou critérios próprios do domínio. Prefira os componentes nativos para controles genéricos já cobertos pelo framework.
```yaml
output:
- code: ACME_POLICY
type: external
class: app.extensions.guardrails:AcmePolicyRail
## O que cada componente faz
| Componente | Momento | Contrato | Efeito |
|---|---|---|---|
| Guardrail | antes, durante ou depois da resposta | `evaluate(text, context)` | permite, sanitiza ou interrompe o fluxo |
| Judge | depois de existir uma resposta candidata | `evaluate(question, answer, context)` | mede qualidade; não reescreve a resposta |
Guardrails podem ser configurados nos estágios `input`, `retrieval`, `tool` e `output`. Judges são avaliações pós-resposta e podem ser amostrados, exceto quando `always_run_for_transactional` força a avaliação de transações.
## Estrutura recomendada no agente
```text
financial_agent/
__init__.py
extensions/
__init__.py
guardrails.py
judges.py
config/
guardrails.yaml
judges.yaml
```
```yaml
O pacote precisa estar importável no mesmo ambiente do backend. O caminho aceita `pacote.modulo:Classe` (recomendado) ou `pacote.modulo.Classe`.
## Implementar um guardrail
O método recebe o texto/argumentos serializados e um contexto do estágio. Ele deve sempre devolver `RailDecision`. Use `sanitized_text` somente quando a continuação com conteúdo alterado for segura.
```python external-guardrail-example
from __future__ import annotations
from typing import Any
from agent_framework.guardrails.base import Guardrail, RailDecision
class FinancialAmountRail(Guardrail):
"""Impede que uma ferramenta execute acima do limite do agente."""
code = "FIN_AMOUNT"
stage = "tool"
def __init__(self, max_amount: float = 1500.0) -> None:
self.max_amount = float(max_amount)
async def evaluate(self, text: str, context: dict[str, Any]) -> RailDecision:
arguments = context.get("tool_args") or {}
raw_amount = arguments.get("amount")
if raw_amount is None:
return RailDecision(code=self.code, allowed=False, reason="O argumento obrigatório 'amount' não foi informado.")
try:
amount = float(raw_amount)
except (TypeError, ValueError):
return RailDecision(code=self.code, allowed=False, reason="O argumento 'amount' deve ser numérico.")
if amount > self.max_amount:
return RailDecision(
code=self.code,
allowed=False,
reason=f"Valor {amount:.2f} excede o limite {self.max_amount:.2f}.",
metadata={"amount": amount, "max_amount": self.max_amount},
)
return RailDecision(code=self.code, allowed=True, metadata={"amount": amount, "max_amount": self.max_amount})
```
Declarações obrigatórias e seus motivos:
- herdar de `Guardrail` mantém o contrato público e facilita testes;
- `code` identifica decisão, telemetria e política; o YAML pode sobrescrevê-lo;
- `stage` documenta a intenção da classe, enquanto a seção do YAML determina onde ela roda;
- `evaluate(text, context)` deve aceitar exatamente esses argumentos;
- `allowed=False` nega a operação; a ação terminal é definida pela política no YAML;
- `reason` deve ser útil para diagnóstico, sem incluir segredo ou dado pessoal.
Métodos `async` rodam no event loop. Um `evaluate` síncrono também é aceito e roda em worker thread. Não faça I/O bloqueante dentro de um método `async`.
### Configurar o guardrail
```yaml external-guardrail-yaml
fail_fast: true
tool:
- code: FIN_AMOUNT
type: external
class: financial_agent.extensions.guardrails:FinancialAmountRail
enabled: true
kwargs:
max_amount: 1500.0
policy:
on_deny: handover
```
`type: external` ativa o import dinâmico; `class` aponta para a classe; `kwargs` vai para o construtor. `policy.on_deny` (ou o atalho `on_deny`) aceita `block`, `retry`, `handover`, `sanitize`, `observe` e `allow`. Ações terminais como `block`, `retry` e `handover` participam do `fail_fast`. Sanitizações são aplicadas na ordem estável do YAML.
O loader de guardrails não injeta `llm` nem `settings` no construtor. Quando um guardrail precisar do LLM compartilhado, leia `context["guardrail_llm"]` ou `context["llm"]`; esses valores só existem se o pipeline tiver sido criado com `GuardrailPipeline(llm=llm, ...)`. Trate ausência explicitamente.
## Implementar um judge
Um judge deve devolver `JudgeResult`. O construtor abaixo aceita todas as opções que o loader pode fornecer; `llm` e `settings` são injetados quando os parâmetros existem na assinatura.
```python external-judge-example
from __future__ import annotations
from typing import Any
from agent_framework.judges.judge import JudgeResult
class FinancialEvidenceJudge:
name = "financial_evidence"
def __init__(self, llm: Any | None = None, settings: Any | None = None,
threshold: float = 0.8, profile_name: str = "judge",
fail_closed: bool = True, max_context_chars: int = 12000,
fallback_on_block: bool = False) -> None:
self.llm = llm
self.settings = settings
self.threshold = float(threshold)
self.profile_name = profile_name
self.fail_closed = bool(fail_closed)
self.max_context_chars = int(max_context_chars)
self.fallback_on_block = bool(fallback_on_block)
def evaluate(self, question: str, answer: str, context: dict[str, Any]) -> JudgeResult:
evidence = str(context.get("evidence") or "").strip()
has_evidence = bool(evidence) and evidence.casefold() in answer.casefold()
score = 1.0 if has_evidence else 0.0
return JudgeResult(
name=self.name,
score=score,
passed=score >= self.threshold,
reason="Resposta contém a evidência financeira." if has_evidence else "Resposta sem evidência financeira verificável.",
metadata={"threshold": self.threshold, "profile": self.profile_name},
)
```
```yaml external-judge-yaml
enabled: true
sample_rate: 1.0
always_run_for_transactional: true
fail_closed: true
profile: judge
judges:
- name: acme_quality
- name: financial_evidence
type: external
class: app.extensions.judges:AcmeQualityJudge
threshold: 0.7
class: financial_agent.extensions.judges:FinancialEvidenceJudge
enabled: true
threshold: 0.8
kwargs: {}
```
Native entries remain unchanged. External synchronous `evaluate()` methods execute in worker threads via `asyncio.to_thread`; asynchronous methods execute concurrently on the framework event loop. Judges run concurrently with `asyncio.gather`, preserving YAML result order. Agent plugins should reuse the LLM supplied by the framework rather than instantiate a separate provider.
O loader completa, se ausentes em `kwargs`, `threshold`, `profile_name`, `fail_closed`, `max_context_chars` e `fallback_on_block`, além de tentar injetar `llm` e `settings`. Não instancie outro provedor dentro da extensão: reutilize o cliente recebido para manter perfis, credenciais, telemetria e limites consistentes.
The core must not reference a concrete agent package, company, product, telecom identifier or domain-specific policy. Domain-specific variants belong to the agent and should receive distinct public codes/names.
Judges síncronos rodam em worker threads; os assíncronos rodam no event loop. Todos são aguardados concorrentemente com resultado na ordem do YAML. O contexto entregue ao judge é compactado. Declare as chaves de evidência exigidas e não dependa de objetos grandes ou não serializáveis.
## Compatibility rule
Domain policies must not be replaced by cosmetically generic text inside the core while losing the original policy. The generic core implementation and the agent-specific implementation may coexist; the embedding agent explicitly selects its own code/name in YAML.
## Inicialização correta no backend
Legacy business validators should migrate to the agent domain. A temporary compatibility shim is acceptable for old imports, but new application code must import the agent-owned implementation.
Os caminhos globais funcionam por argumento ou pelas configurações `GUARDRAILS_CONFIG_PATH` e `JUDGES_CONFIG_PATH`:
```python
guardrails = GuardrailPipeline(llm=llm, config_path="config/guardrails.yaml", fail_fast=True)
judges = JudgePipeline(llm=llm, settings=settings, config_path="config/judges.yaml")
```
### Limitação atual do `agent_template_backend`
O template corrente cria um único `GuardrailPipeline` e um único `JudgePipeline` no startup. Embora o registro de agentes possua `guardrails_config_path` e `judges_config_path`, apenas declarar caminhos diferentes em `agents.yaml` **não seleciona automaticamente pipelines por requisição**. Para isolamento multiagente real, a aplicação precisa manter pipelines em cache por perfil e escolher o par correto antes de executar o grafo. Até essa composição existir, trate os YAML carregados no startup como configuração global.
No template atual, o pipeline de guardrails também é criado sem `llm`, e o de judges sem `llm/settings`. Um guardrail externo determinístico funciona, mas extensões dependentes de LLM exigem alterar a composição conforme o exemplo acima. Sem isso, o judge externo pode receber `llm=None` e o guardrail não encontrará `context["guardrail_llm"]`.
## Falhas, segurança e observabilidade
- Guardrails convertem exceções em bloqueio quando `fail_closed=True`; em fail-open a decisão vira observação.
- Uma exceção de judge externo pode propagar pelo `asyncio.gather`; capture falhas esperadas dentro da extensão e devolva um `JudgeResult` coerente.
- Não registre prompt completo, credenciais, tokens, PII ou payload financeiro. Prefira `code`, duração, ação, score e correlação.
- Use fail-closed para autorização, segurança e transação. Fail-open só é adequado para métricas não críticas e deve gerar alerta.
- Mantenha códigos e nomes do domínio no pacote do agente. O core não deve importar `financial_agent`.
## Testar antes de publicar
Teste a classe e a integração do loader com o YAML. Este repositório transforma os blocos marcados acima em pacote temporário, importa as classes e executa ambos os pipelines:
```bash
pytest -q tests/test_external_guardrails_judges_documentation.py
```
Checklist: classe importável; assinatura e retorno corretos; `kwargs` validados no startup; allow/deny, limite, ausência e exceção cobertos; ação terminal e política de falha explícitas; `always_run_for_transactional` quando necessário; telemetria sem dados sensíveis; exemplo do manual aprovado no CI.
## Compatibilidade e migração
Uma política antiga deve migrar para o pacote do agente, não ser renomeada de forma cosmética dentro do core. Implementações genérica e específica podem coexistir; o YAML escolhe explicitamente o código/nome. Um shim temporário preserva imports antigos, mas código novo deve importar a implementação pertencente ao agente.

View File

@@ -0,0 +1,46 @@
# External guardrails and judges: implementation guide
External extensions keep domain policy in the agent package instead of coupling it to `agent_framework`. The complete executable reference, including copy-and-paste implementations, is [the Portuguese guide](EXTERNAL_GUARDRAILS_JUDGES.md); its tagged examples are exercised by `tests/test_external_guardrails_judges_documentation.py`.
## Required contracts
- A guardrail normally extends `Guardrail` and implements `evaluate(text, context) -> RailDecision`. Its YAML section selects `input`, `retrieval`, `tool`, or `output`; `policy.on_deny` selects `block`, `retry`, `handover`, `sanitize`, `observe`, or `allow`.
- A judge implements `evaluate(question, answer, context) -> JudgeResult`. Its constructor can receive injected `llm` and `settings`, plus `threshold`, `profile_name`, `fail_closed`, `max_context_chars`, and `fallback_on_block`.
- Class paths accept `package.module:Class` (recommended) or `package.module.Class`; that package must be importable by the backend.
- Synchronous methods run in worker threads; asynchronous methods run on the event loop. Judges execute concurrently while result order follows YAML order.
```yaml
# guardrails.yaml
fail_fast: true
tool:
- code: FIN_AMOUNT
type: external
class: financial_agent.extensions.guardrails:FinancialAmountRail
kwargs: {max_amount: 1500.0}
policy: {on_deny: handover}
```
```yaml
# judges.yaml
enabled: true
sample_rate: 1.0
always_run_for_transactional: true
fail_closed: true
judges:
- name: financial_evidence
type: external
class: financial_agent.extensions.judges:FinancialEvidenceJudge
threshold: 0.8
```
## Current template limitation
`agent_template_backend` currently builds one guardrail pipeline and one judge pipeline at startup. Per-agent `guardrails_config_path` and `judges_config_path` entries in `agents.yaml` do not by themselves switch pipelines for each request. Until per-profile pipeline selection is implemented by the application, startup YAML is effectively global.
The template also constructs guardrails without `llm` and judges without `llm/settings`. Deterministic extensions work, but LLM-dependent extensions require `GuardrailPipeline(llm=llm, ...)` and `JudgePipeline(llm=llm, settings=settings, ...)`. Guardrail constructors do not receive LLM injection; a configured pipeline exposes it through `context["guardrail_llm"]` or `context["llm"]`.
Use fail-closed for authorization, safety, and transaction controls. Catch expected external judge failures inside the implementation because an unhandled exception may propagate from concurrent evaluation. Never log prompts, credentials, PII, or financial payloads.
```bash
pytest -q tests/test_external_guardrails_judges_documentation.py
```

View File

@@ -159,34 +159,15 @@ _, decisions = await pipeline.run_tool(
### SPI for external guardrails and judges
> Content consolidated from `docs/EXTERNAL_GUARDRAILS_JUDGES.md`.
Use extensions when a policy or quality criterion belongs to the agent domain. A guardrail implements `evaluate(text, context) -> RailDecision` and can run at `input`, `retrieval`, `tool`, or `output`. A judge implements `evaluate(question, answer, context) -> JudgeResult` and scores rather than rewrites a response.
`agent_framework_oci` supports agent-owned guardrails and judges without importing domain code into the core.
The [external guardrails and judges guide](../../EXTERNAL_GUARDRAILS_JUDGES_en.md) covers directory layout, imports, copyable implementations, YAML, constructor injection, failure policy, observability, and executable verification.
```yaml
output:
- code: ACME_POLICY
type: external
class: app.extensions.guardrails:AcmePolicyRail
```
Key constraints: `type: external` plus `class: package.module:Class` enables dynamic import; `kwargs` populate the constructor; judges may receive injected `llm` and `settings`; guardrails receive a shared LLM through context only when the pipeline was built with `llm=...`; synchronous implementations run in worker threads; and external judge exceptions should be handled by the implementation.
```yaml
judges:
- name: acme_quality
type: external
class: app.extensions.judges:AcmeQualityJudge
threshold: 0.7
```
> **Current limitation:** `agent_template_backend` creates a global pipeline pair at startup. Registry fields `guardrails_config_path` and `judges_config_path` do not automatically select a configuration per request. LLM-backed extensions also require the composition to pass `llm` and `settings` as documented in the full guide.
Native entries remain unchanged. External synchronous `evaluate()` methods execute in worker threads via `asyncio.to_thread`; asynchronous methods execute concurrently on the framework event loop. Judges run concurrently with `asyncio.gather`, preserving YAML result order. Agent plugins should reuse the LLM supplied by the framework rather than instantiate a separate provider.
The core must not reference a concrete agent package, company, product, telecom identifier, or domain-specific policy. Domain-specific variants belong to the agent and should receive distinct public codes/names.
### Compatibility rule
Domain policies must not be replaced by cosmetically generic text inside the core while losing the original policy. The generic core implementation and the agent-specific implementation may coexist; the embedding agent explicitly selects its own code/name in YAML.
Legacy business validators should migrate to the agent domain. A temporary compatibility shim is acceptable for old imports, but new application code must import the agent-owned implementation.
Verify the published examples with `pytest -q tests/test_external_guardrails_judges_documentation.py`.
### Mandatory judge execution for transactions

View File

@@ -0,0 +1,90 @@
### Advanced transactional workflows
### Goal
This chapter turns a sensitive tool into a deterministic transaction with parameter collection, pre-validation, confirmation, versioned workflow, pause/resume, idempotency, and recovery. Business rules and actions belong to the agent; the execution engine belongs to the framework.
### Actual flow
1. `routing.yaml` selects the agent and limits candidate tools.
2. `tools.yaml` describes arguments, prompts, and response behavior.
3. `mcp_parameter_mapping.yaml` resolves identity, defaults, and extraction.
4. `tool_policies.yaml` classifies the operation and requires confirmation.
5. Optional pre-validation checks whether execution may continue without producing an effect.
6. After unambiguous confirmation, execution is direct or delegated to `WorkflowToolExecutor`.
7. `WorkflowRuntime` loads the active version, runs actions, persists pauses, and returns `COMPLETED`, `PAUSED`, or `FAILED`.
8. An idempotency key prevents duplicate external effects.
### Catalog and policy are different contracts
```yaml
# config/tools.yaml
tools:
request_return:
description: Opens an order return.
mcp_server: retail
enabled: true
tool_type: action
confirmation_required: true
requires: [order_id, reason]
args_schema:
order_id:
type: string
label: the order number
user_prompt: Provide the order number.
reason:
type: string
label: the return reason
user_prompt: What is the return reason?
```
```yaml
# config/tool_policies.yaml
version: 1
defaults:
operation_type: read_only
require_confirmation: false
tool_policies:
request_return:
operation_type: transactional
require_confirmation: true
requires: [order_id, reason]
pre_validation:
enabled: true
tool: validate_return
fail_open: false
execution:
mode: workflow
workflow: order_return
version: active
```
`tools.yaml` is the public catalog; `tool_policies.yaml` governs effects. Do not rely only on catalog `confirmation_required`: effective policy must declare `operation_type` and `require_confirmation`. A pre-validation tool must be read-only and never perform the transaction early. Prefer `fail_open: false` for sensitive operations.
### Versioned workflow and actions
Store definitions under `workflows/<name>.vN.yaml` and maintain a valid active definition. Node IDs must be unique; `start`, destinations, and `resume_from` must exist; `retry` accepts 0 through 10. Paths under `$.input.*` read workflow input and `$.nodes.<id>.*` read earlier results.
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).
### 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.
### Pause, expected input, and resume
A node may declare `pause.expected_input` with `key`, `allowed_values`, normalization, `reprompt`, and a semantic classifier. Explicit confirmation remains deterministic; the classifier handles only inconclusive replies and must return an allowed option. `contextual_reentry` releases the workflow and returns the utterance to normal routing without confirming user facts.
Resume with the same `execution_id`; a new execution creates a separate transaction. The checkpoint must contain serializable data sufficient to rebuild the pause. Duplicate replies must check idempotency before repeating an effect.
### Intent shift, recovery, and replay
When a user abandons a transaction, clear pending call, collected arguments, confirmation, and related transaction state. Do not reuse parameters from a closed transaction. A contextual question may be answered and return to the pause; a genuine intent change explicitly closes or suspends the flow according to agent policy.
- `PAUSED`: persist execution ID, prompt, and expected-input contract.
- `FAILED`: keep technical details in telemetry and return a safe message.
- timeout after sending: query idempotency or external status before resending.
- replay after `COMPLETED`: return the persisted result without execution.
- new version: in-flight runs retain their version; `active` applies to new runs.
Test invalid graphs, valid/invalid paths, retry, pause/resume, unmatched answers, intent shift, duplicates, idempotency-provider failure, and post-finalization replay. Run `pytest -q tests/test_advanced_developer_documentation.py`.

View File

@@ -0,0 +1,31 @@
### Advanced routing, parameters, and state
### Decision precedence
Routing is not a keyword list. Safe precedence is: explicit block/finalization; pending collection or transaction through `state_policies`; deterministic confirmation; deterministic intent; optional LLM fallback; and finally fallback/handoff. Pending state must win so short replies and isolated parameters do not jump to another agent.
The complete executable YAML is in the matching [Portuguese chapter](../pt/14_routing_parametros_e_estado_avancados.md). In `routing.yaml`, lower `priority` values win ties in the current template. `mcp_tools` limits candidates but does not authorize sensitive effects. `confidence_threshold` gates semantic results, `allow_handoff` permits an explicit agent change, and `domain` supplies scope metadata rather than replacing `agent`.
### Semantic confirmation
Constrain semantic confirmation with `allowed_values`, plus explicit confirm, reject, and continue sets. The prompt must include the allowed values and prohibit execution or invented facts. `include_relevant_context` contributes bounded context related to the pause. If model output does not match an allowed option, keep the transaction pending. Explicit yes/no remains deterministic; the LLM handles inconclusive language only.
### Identity, mapping, and extraction
`identity.yaml` resolves canonical customer, contract, interaction, account, resource, and session keys from the payload, prior `business_context`, or previous context. Validate required identity before MCP execution. A session identifier is not customer identity.
Recommended argument precedence is: already confirmed transaction value, explicit current value, canonical identity mapping, deterministic extraction, LLM/hybrid extraction, and default. A new explicit value may correct an old one; an inferred value must never overwrite a confirmed value.
For `strategy: hybrid`, test both regex and LLM paths. `pattern` and `group` require positive and negative tests. `description` is an operational extraction instruction, not decorative documentation. Type conversion must fail safely when ambiguous.
### Temporal reconciliation
Temporal reconciliation is a fallback only after ordinary collection leaves missing fields. Search relevant messages newest-first and bind values only to fields described by the tool schema/mapping. It must not cross tenant/agent/session, revive a closed transaction, recover a user-invalidated value, replace safe exact matching with dangerous fuzzy matching, or overwrite a confirmed field. Record value origin and correlation without retaining unnecessary sensitive text.
### Dynamic states and multi-agent behavior
Declare new collection and confirmation state fields only when they carry extra data; otherwise reuse generic transaction keys. Every new state needs a matching `state_policies` entry, cleanup on success/cancellation/intent shift, and routing tests. A new state without policy commonly loses route stickiness.
Router mode selects one agent. Supervisor mode coordinates multiple agents and changes cost, latency, state, and telemetry. Do not enable it merely to improve classification. Give subagents minimum context and authorized tools, scoped by `tenant_id:agent_id:session_id`; never share raw memory across agents.
Test keyword overlap, priority ties, below-threshold output, fallback, pending states, short confirmations, ambiguity, parameter correction, regex/LLM extraction, temporal history, and isolation. Run `pytest -q tests/test_advanced_developer_documentation.py`.

View File

@@ -0,0 +1,25 @@
### Presentation, recovery, and observability
### Deterministic renderers
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).
### 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.
Distinguish validation, transient dependency, permanent dependency, persistence, and programming failures. Before an external send, bounded retry may be safe. After an ambiguous send, query status/idempotency before resending. After external confirmation, persist the outcome before replying. A new user message must not erase evidence of uncertain execution. Checkpoint retry, integrity checks, compaction, and recovery do not make an external action idempotent.
### Voice interruption and replay
On barge-in, preserve confirmed transcript, mark interrupted output, and discard stale audio. An interrupted utterance is not transaction confirmation. Replay uses interaction/message identity and the same scoped session; resume only after revalidating pending state, confirmation, and idempotency.
### Events, mappings, and Pub/Sub
Emit functional behavior as IC, operational dependency/latency as NOC, and guardrail/supervisor decisions as GRL through the observer abstraction. Include tenant, agent, session, transaction/execution, component, and duration where available. Never record credentials, complete prompts, PII, or raw financial payloads.
Map legacy consumer labels through a default registry plus agent overlay. Keep internal events semantic; do not hardcode historical external labels in runtime code.
Pub/Sub publication occurs after the functional decision and must not change its outcome. Ordering requires explicit `sequence`, correlation, and a stable partition key; multiple pods do not provide global ordering. Define retention/TTL, duplicate handling, filters, and failure policy. A Pub/Sub filter does not remove the event from other telemetry destinations.
Validate payload masking, supervisor revalidation, bounded retry, uncertain effects, correlation, mapping, multi-consumer ordering, and voice behavior. Run `pytest -q tests/test_advanced_developer_documentation.py`.

View File

@@ -0,0 +1,31 @@
### Multi-agent composition and lifecycle
### Effective isolation
An `agent_id` is not merely a label. Real isolation includes prompt, routing, tools, MCP servers, guardrails, judges, memory, checkpoints, cache, telemetry, and identity, normally scoped as `tenant_id:agent_id:session_id`. The complete registry and runtime-bundle code are in the matching [Portuguese chapter](../pt/16_multiagente_composicao_e_ciclo_de_vida.md).
Every configured path must exist and be validated at startup. A system prefix guides the model but is not a security boundary; code must enforce tool authorization, persistence namespaces, and pipeline selection.
### Current template limitation
Although `agents.yaml` accepts per-agent paths, the template currently builds one router, one tool/RAG composition, and one global guardrail/judge pair. YAML alone does not provide per-request isolation. Use one global profile per deployment or implement an explicit cached runtime bundle per `agent_id`. Cache immutable/expensive objects rather than constructing LLM clients, routers, or pipelines for every message. Never silently fall back to another agent's bundle.
### Subagents and LLM composition
A subagent is a bounded capability with declared purpose, input/output, authorized tools, timeout/budget, and return conditions. A supervisor consolidates structured results and passes minimum relevant context with correlation and maximum delegation depth. Use routing for one-specialty turns; use supervision only for genuine decomposition. Handoff transfers conversation ownership; subagent delegation leaves final-response ownership with the supervisor.
Reuse framework LLM profiles and clients rather than creating provider SDKs inside domain components. Explicit, observable profile fallback preserves authentication, rate limits, retries, cache, and telemetry; configuration failure must not become an apparently valid answer.
### RAG, memory, and security
`RAG_PROVIDER=kbdb` works only when the execution's `RagService` uses the intended settings. A simple intent should still retrieve knowledge when agent policy requires it. Scope RAG collections, long-term memory, and checkpoints by tenant and agent. Long-term memory stores permitted summarized facts; checkpoints store execution state; neither may mix another agent's identity.
Authentication establishes a principal; authorization controls tenant, agent, route, and tool. Production secrets require hashing/rotation and TLS. Trusted-proxy headers are safe only behind an authenticated edge that removes client-supplied values.
### Startup and delivery lifecycle
At startup validate registry/paths, create providers, register actions/renderers, load workflows, construct bundles, and check mandatory dependencies. `/health` reports process liveness; `/ready` must reject traffic when a required dependency is unusable. Invalid configuration should fail before the first conversation.
Development order: copy the canonical template; create domain agent/configuration; register node and route; implement tools, mappings, policies, actions, and renderers; configure controls/RAG/memory/telemetry; run unit, loader, graph, conversational, and failure tests; run offline evaluation, load, and readiness checks; publish with version, compatibility, and rollback.
The definition of done includes isolation, confirmation, idempotency, recovery, grounding, controls, correlated telemetry, no data leakage, dependency-failure behavior, and PT/EN documentation parity. Run `pytest -q tests/test_advanced_developer_documentation.py`.

View File

@@ -125,11 +125,35 @@ If something is not working, use **Search by problem** below.
**Use when:** block messages are generic, `COER` should ask for clarification, or blocked-turn metadata contains stale routing/tool results.
### [13 — Advanced Transactional Workflows](./13_advanced_transactional_workflows.md)
**What it is:** pre-validation, versioned workflow actions, pause/resume, idempotency, replay, and recovery.
**Use when:** a sensitive tool requires a deterministic and recoverable process.
### [14 — Advanced Routing, Parameters, and State](./14_advanced_routing_parameters_and_state.md)
**What it is:** routing precedence, semantic confirmation, hybrid extraction, temporal reconciliation, dynamic states, and subagents.
**Use when:** routing or collection works on the happy path but fails in real conversations.
### [15 — Presentation, Recovery, and Observability](./15_presentation_recovery_and_observability.md)
**What it is:** renderers, Output Supervisor, recovery, voice replay, events, mappings, and ordered Pub/Sub.
**Use when:** output changes evidence, an effect may repeat, or telemetry cannot reconstruct execution.
### [16 — Multi-Agent Composition and Lifecycle](./16_multi_agent_composition_and_lifecycle.md)
**What it is:** actual isolation, per-agent bundles, supervisor/subagents, LLM/RAG/memory, and readiness.
**Use when:** multiple agents share one deployment or a new capability must reach production.
### Main tutorial
[`README_en.md`](README_en.md) remains the reference for the complete step-by-step flow:
`architecture → configuration → agent creation → registration → state → routing → tools → MCP → identity → execution → tests → gateways → memory → RAG`.
`architecture → configuration → agent creation → registration → state → routing → tools → MCP → identity → workflows → idempotency → execution → tests → gateways → memory → RAG → operations`.
### Maintenance
@@ -141,4 +165,3 @@ When evolving a feature:
- update the specialized manual with behavior, configuration, examples, and troubleshooting;
- update SPECs if the contract changed;
- keep release notes as history, not as the only current documentation.

View File

@@ -160,33 +160,23 @@ _, decisions = await pipeline.run_tool(
### SPI de guardrails e judges externos
> Conteúdo consolidado a partir de `docs/EXTERNAL_GUARDRAILS_JUDGES.md`.
Use extensões quando a política ou o critério de qualidade pertencer ao domínio do agente. Um guardrail implementa `evaluate(text, context) -> RailDecision` e pode atuar em `input`, `retrieval`, `tool` ou `output`. Um judge implementa `evaluate(question, answer, context) -> JudgeResult` e avalia a resposta sem reescrevê-la.
`agent_framework_oci` supports agent-owned guardrails and judges without importing domain code into the core.
O guia [Guardrails e judges externos](../../EXTERNAL_GUARDRAILS_JUDGES.md) contém a estrutura de diretórios, imports, classes completas para copiar, YAML, explicação de cada declaração, fail-open/fail-closed, observabilidade e teste executável.
```yaml
output:
- code: ACME_POLICY
type: external
class: app.extensions.guardrails:AcmePolicyRail
```
Pontos indispensáveis:
```yaml
judges:
- name: acme_quality
type: external
class: app.extensions.judges:AcmeQualityJudge
threshold: 0.7
```
- `type: external` e `class: pacote.modulo:Classe` ativam o import dinâmico;
- `kwargs` são argumentos do construtor; judges também podem receber `llm` e `settings` injetados;
- guardrails externos não recebem `llm` no construtor: o pipeline o disponibiliza no contexto somente quando criado com `llm=...`;
- métodos síncronos rodam em worker thread; métodos assíncronos rodam no event loop;
- judges são concorrentes e preservam a ordem declarada no YAML;
- exceções esperadas de judges externos devem ser tratadas pela implementação;
- códigos, regras e vocabulário de negócio ficam no pacote do agente, nunca importados pelo core.
Native entries remain unchanged. External synchronous `evaluate()` methods execute in worker threads via `asyncio.to_thread`; asynchronous methods execute concurrently on the framework event loop. Judges run concurrently with `asyncio.gather`, preserving YAML result order. Agent plugins should reuse the LLM supplied by the framework rather than instantiate a separate provider.
> **Limitação atual:** o `agent_template_backend` instancia um par global de pipelines no startup. Os campos `guardrails_config_path` e `judges_config_path` do registro não selecionam automaticamente configurações por requisição. Extensões dependentes de LLM também exigem que a composição passe `llm` e `settings` aos pipelines, conforme o guia completo.
The core must not reference a concrete agent package, company, product, telecom identifier or domain-specific policy. Domain-specific variants belong to the agent and should receive distinct public codes/names.
### Compatibility rule
Domain policies must not be replaced by cosmetically generic text inside the core while losing the original policy. The generic core implementation and the agent-specific implementation may coexist; the embedding agent explicitly selects its own code/name in YAML.
Legacy business validators should migrate to the agent domain. A temporary compatibility shim is acceptable for old imports, but new application code must import the agent-owned implementation.
Valide o exemplo publicado com `pytest -q tests/test_external_guardrails_judges_documentation.py`.
### Execução obrigatória de judges em transações

View File

@@ -0,0 +1,152 @@
### Workflows transacionais avançados
### Objetivo
Este capítulo ensina a transformar uma tool sensível em uma transação determinística, com coleta de parâmetros, pré-validação, confirmação, workflow versionado, pausa/retomada, idempotência e recuperação. A regra de negócio e as actions ficam no agente; o motor de execução fica no framework.
### Fluxo real
1. `routing.yaml` escolhe o agente e restringe as tools candidatas.
2. `tools.yaml` descreve argumentos, prompts e resposta.
3. `mcp_parameter_mapping.yaml` resolve identidade, defaults e extrações.
4. `tool_policies.yaml` classifica a operação e exige confirmação.
5. A pré-validação opcional verifica se a operação pode prosseguir sem causar efeito.
6. Após confirmação inequívoca, a execução é direta ou encaminhada ao `WorkflowToolExecutor`.
7. O `WorkflowRuntime` carrega a versão ativa, executa actions, persiste pausa e devolve `COMPLETED`, `PAUSED` ou `FAILED`.
8. A chave idempotente impede a repetição do efeito externo.
### Catálogo e política são contratos diferentes
```yaml
# config/tools.yaml
tools:
solicitar_devolucao:
description: Abre uma devolução de pedido.
mcp_server: retail
enabled: true
tool_type: action
confirmation_required: true
requires: [order_id, reason]
args_schema:
order_id:
type: string
label: o número do pedido
user_prompt: Informe o número do pedido.
reason:
type: string
label: o motivo da devolução
user_prompt: Qual é o motivo da devolução?
```
```yaml
# config/tool_policies.yaml
version: 1
defaults:
operation_type: read_only
require_confirmation: false
tool_policies:
solicitar_devolucao:
operation_type: transactional
require_confirmation: true
requires: [order_id, reason]
pre_validation:
enabled: true
tool: validar_devolucao
fail_open: false
execution:
mode: workflow
workflow: devolucao_pedido
version: active
```
`tools.yaml` é o catálogo público. `tool_policies.yaml` governa efeitos. Não confie somente em `confirmation_required` do catálogo: a política efetiva deve declarar `operation_type` e `require_confirmation`. `pre_validation.tool` deve ser read-only e nunca executar a transação antecipadamente. Para segurança, use `fail_open: false`.
### Definição versionada do workflow
```yaml workflow-definition-example
name: devolucao_pedido
version: 1
start: validar_pedido
nodes:
- id: validar_pedido
action: validar_pedido
input:
order_id: $.input.order_id
- id: registrar_devolucao
action: registrar_devolucao
retry: 1
input:
order_id: $.input.order_id
reason: $.input.reason
edges:
- from: validar_pedido
to: registrar_devolucao
when:
path: $.nodes.validar_pedido.valid
equals: true
- from: validar_pedido
to: END
when:
path: $.nodes.validar_pedido.valid
equals: false
- from: registrar_devolucao
to: END
```
Salve como `workflows/devolucao_pedido.v1.yaml`. O arquivo `devolucao_pedido.active.yaml` deve apontar para uma definição válida da versão ativa. IDs de nós não podem repetir; `start`, destinos e `resume_from` precisam existir. `retry` aceita de 0 a 10. Paths `$.input.*` leem a entrada e `$.nodes.<id>.*` leem resultados anteriores.
### Actions pertencentes ao agente
```python workflow-actions-example
from agent_framework.workflows import workflow_action
@workflow_action("validar_pedido")
async def validar_pedido(params: dict, state: dict) -> dict:
return {"valid": bool(params.get("order_id"))}
@workflow_action("registrar_devolucao")
async def registrar_devolucao(params: dict, state: dict) -> dict:
order_id = str(params["order_id"])
return {"protocol": f"DEV-{order_id}", "status": "REQUESTED"}
```
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.
### Idempotência do efeito externo
```python idempotency-example
async def execute_once(store, customer_key: str, order_id: str, invoke):
key = store.canonical_key(customer_key, order_id, "solicitar_devolucao", "v1")
previous = await store.get(key)
if previous is not None:
return previous
result = await invoke()
await store.set(key, result)
return result
```
Crie uma única instância no startup com `create_idempotency_store(settings, namespace="return-request", require_durable=True)` e injete-a nas actions. Use componentes de negócio estáveis na chave; nunca use apenas timestamp. `IDEMPOTENCY_PROVIDER` tem precedência sobre checkpoint, session e cache. Em produção transacional, configure `IDEMPOTENCY_REQUIRE_DURABLE=true` e um provider `oracle`, `redis` ou `sqlite` apropriado. `memory` não protege entre pods ou reinícios. Grave o resultado somente depois de o efeito ter sido confirmado pelo sistema externo.
### Pausa, entrada esperada e retomada
Um nó pode declarar `pause.expected_input` com `key`, `allowed_values`, normalização, `reprompt` e classificador semântico. A confirmação explícita continua determinística; o classificador só trata respostas inconclusivas e deve retornar uma das opções permitidas. `contextual_reentry` libera o workflow e devolve a fala ao roteamento normal, sem confirmar fatos automaticamente.
Ao retomar, use o mesmo `execution_id`. Uma nova execução cria outra transação. O checkpoint deve conter somente dados serializáveis e suficientes para reconstruir a pausa. Respostas duplicadas precisam consultar idempotência antes de repetir qualquer efeito.
### Intent shift e limpeza
Quando o usuário abandona a transação, limpe `pending_tool_call`, argumentos coletados, confirmação e estado transacional relacionado. Não reutilize parâmetros de uma transação encerrada. Uma pergunta contextual durante a pausa pode ser respondida e retornar ao workflow; uma mudança real de intenção encerra ou suspende explicitamente o fluxo conforme a política do agente.
### Recuperação e post-finalization replay
- `PAUSED`: persistir `execution_id`, prompt e contrato da entrada esperada.
- `FAILED`: manter detalhes técnicos na telemetria e uma mensagem segura ao usuário.
- timeout após envio: consultar idempotência ou status externo antes de reenviar.
- replay depois de `COMPLETED`: devolver o resultado persistido; nunca executar novamente.
- versão nova: execuções em andamento continuam com sua versão; `active` vale para novas execuções.
### Como testar
Teste grafo inválido, caminho válido/inválido, retry, pause/resume, resposta não reconhecida, intent shift, duplicidade, falha do provider de idempotência e replay após finalização. O contrato copiável deste capítulo é validado por `tests/test_advanced_developer_documentation.py`.

View File

@@ -0,0 +1,122 @@
### Routing, parâmetros e estado avançados
### Precedência de decisão
O desenvolvedor não deve tratar `routing.yaml` como uma lista de palavras. A decisão combina estado pendente, regras determinísticas e fallback semântico. A ordem segura é:
1. bloqueio ou finalização explícita;
2. transação/coleção pendente por `state_policies`;
3. confirmação determinística;
4. intenção determinística por keywords/examples/priority;
5. fallback LLM, quando habilitado;
6. `fallback_agent` ou handoff.
O estado pendente tem precedência para que “sim”, “não” ou um parâmetro isolado não sejam roteados para outro agente.
### Contrato comentado do `routing.yaml`
```yaml routing-advanced-example
router:
mode: router
fallback_agent: billing_agent
confidence_threshold: 0.65
allow_handoff: true
transaction_confirmation:
semantic_fallback:
enabled: true
allowed_values: [SIM, NAO, CONTINUAR]
confirm_values: [SIM]
reject_values: [NAO]
continue_values: [CONTINUAR]
include_relevant_context: true
profile_name: router
prompt: |
Classifique somente como {{ allowed_values }}.
Pergunta pendente: {{ pending_prompt }}
Histórico relevante: {{ relevant_conversation_context }}
Resposta atual: {{ user_input }}
state_policies:
- state: COLLECTING_FINANCIAL_PARAMETERS
agent: financeiro_agent
description: Mantém respostas curtas na coleta financeira.
- state: WAITING_FINANCIAL_CONFIRMATION
agent: financeiro_agent
description: Mantém a confirmação no fluxo financeiro.
intents:
- name: financial_analysis
domain: financial
agent: financeiro_agent
description: Consulta saldo, posição e movimentações.
priority: 10
mcp_tools: [consultar_saldo, consultar_movimentacoes]
keywords: [saldo, extrato, movimentação]
examples:
- Qual é meu saldo?
- Mostre minhas movimentações.
```
`priority` desempata intenções; menor valor tem precedência no template atual. `mcp_tools` restringe capacidades candidatas, mas não autoriza efeito sensível. `confidence_threshold` define quando aceitar o resultado semântico. `allow_handoff` permite troca explícita de agente. `domain` é metadado de escopo e não substitui `agent`.
O prompt semântico deve conter `allowed_values` e instruir o modelo a não executar ações nem inventar fatos. `include_relevant_context` inclui somente contexto limitado relacionado à pausa. Se a saída não corresponder a uma opção permitida, mantenha a transação pendente.
### Identidade antes da extração
`identity.yaml` resolve chaves canônicas (`customer_key`, `contract_key`, `interaction_key`, `account_key`, `resource_key`, `session_key`) a partir do payload, do `business_context` ou do contexto anterior. A validação de `required` deve ocorrer antes de chamar MCP. Nunca use `session_id` como identidade de cliente.
### Mapeamento e extração
```yaml parameter-mapping-example
mcp_parameter_mapping:
defaults:
use_mock: false
tools:
consultar_movimentacoes:
map:
customer_key: customer_id
account_key: account_id
session_key: session_id
defaults:
limit: 20
extract:
month:
from: message
type: int
strategy: month_name_pt
description: Converta o mês mencionado em número de 1 a 12.
transaction_id:
from: message
type: string
strategy: hybrid
pattern: '(?i)\\b(?:transação|transaction)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b'
group: 1
description: Extraia somente um identificador explicitamente informado.
```
Precedência recomendada para argumentos: valor já confirmado na transação, argumento explícito atual, identidade canônica mapeada, extração determinística, extração LLM/híbrida e default. Um valor novo explícito pode corrigir o anterior; um valor inferido nunca deve sobrescrever um valor confirmado.
`strategy: hybrid` tenta a parte determinística e usa LLM quando necessário. `pattern` e `group` devem ser testados com casos positivos e negativos. `description` é instrução operacional do extrator, não documentação decorativa. `type` deve falhar de forma controlada se a conversão não for segura.
### Reconciliação temporal
A reconciliação temporal é fallback da coleta tradicional. Quando ainda faltarem campos, percorra mensagens relevantes da mais recente para a mais antiga e associe valores somente aos campos descritos no `args_schema`/mapeamento. Ela não deve:
- reabrir transação encerrada;
- misturar agentes, tenants ou sessões;
- recuperar valor invalidado pelo usuário;
- trocar correspondência exata por aproximação perigosa;
- substituir parâmetro já confirmado.
Registre a origem (`current_message`, `business_context`, `history`, `default`), a mensagem correlacionada e a confiança, sem persistir conteúdo sensível desnecessário.
### Estados dinâmicos
Para um agente novo, declare os estados de coleta e confirmação em `AgentState` somente quando eles carregarem campos adicionais; caso contrário, use as chaves transacionais genéricas. Registre ambos em `state_policies`, implemente a limpeza em sucesso/cancelamento/intent shift e inclua os estados nos testes de roteamento. Estado novo sem política correspondente costuma causar perda de stickiness.
### Router versus supervisor
No modo `router`, uma decisão escolhe um agente. No modo `supervisor`, o supervisor pode coordenar vários agentes e consolidar resultados. Não habilite supervisor apenas para melhorar classificação: ele muda custo, latência, estado e observabilidade. Um subagente deve receber contexto mínimo, ferramentas autorizadas e namespace `tenant_id:agent_id:session_id`; nunca compartilhe memória bruta entre agentes.
### Testes obrigatórios
Teste sobreposição de keywords, empate de prioridade, abaixo do threshold, agente fallback, estado pendente, confirmação curta, resposta ambígua, correção de parâmetro, extração regex/LLM, histórico temporal e isolamento entre agentes. Os YAMLs deste capítulo são analisados por `tests/test_advanced_developer_documentation.py`.

View File

@@ -0,0 +1,87 @@
### Apresentação, recuperação e observabilidade
### Renderers de resposta
Quando uma tool retorna dados estruturados confiáveis, `response.mode: renderer` produz uma resposta determinística e evita que o LLM altere valores, protocolos ou status.
```yaml
tools:
consultar_saldo:
description: Consulta saldo da conta.
mcp_server: financial
enabled: true
response:
mode: renderer
renderer: financial.balance
```
```python renderer-example
from typing import Any
from agent_framework.presentation import register_tool_response_renderer
def render_balance(*, tool_name: str, result: dict[str, Any],
state: dict[str, Any], agent_label: str) -> str | None:
account = result.get("account_masked")
balance = result.get("balance")
currency = result.get("currency", "BRL")
if account is None or balance is None:
return None
return f"[{agent_label}] Conta {account}: saldo {currency} {float(balance):.2f}."
def register_financial_renderers() -> None:
register_tool_response_renderer("financial.balance", render_balance)
```
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.
### 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.
### Recuperação de workflow
Classifique falhas em: validação, dependência temporária, dependência permanente, persistência e erro de programação. O usuário recebe mensagem segura; a telemetria recebe classe, fase, tentativa, workflow/version/execution ID e correlação. Para efeitos externos:
- antes do envio, um retry controlado é aceitável;
- depois de envio com resultado incerto, consulte status/idempotência;
- após confirmação externa, persista o resultado antes de responder;
- uma nova mensagem do usuário não deve apagar evidência de uma execução incerta.
Checkpoints resilientes podem repetir operações de armazenamento, validar integridade, compactar histórico e recuperar o último checkpoint válido. Isso não torna automaticamente uma action externa idempotente.
### Interrupção de voz e replay
No barge-in, preserve o texto confirmado, marque a resposta interrompida e não reapresente áudio obsoleto. O replay deve usar identificadores de interação/mensagem e a mesma sessão escopada. Se uma transação estava pendente, a fala interrompida não conta como confirmação. Ao retomar, revalide estado, confirmação e idempotência.
### Eventos IC, NOC e GRL
- IC: comportamento funcional, decisão, tool, cache e workflow;
- NOC: disponibilidade, timeout, dependência e latência operacional;
- GRL: decisão e ação de guardrail/supervisor.
Use o `AgentObserver`/adapter do template em vez de chamar diretamente um backend de telemetria. Inclua `tenant_id`, `agent_id`, `session_id`, `transaction_id`/`execution_id`, componente e duração quando disponíveis. Nunca inclua credenciais, prompt integral, PII ou payload financeiro bruto.
### Mapeamento e compatibilidade
Quando consumidores históricos exigirem códigos diferentes, use um registry/overlay como `observability_mapping.yaml`: evento semântico interno → label externo. O default é compartilhado e o overlay pertence ao agente. Não hardcode labels históricas no executor nem renomeie o evento interno de forma que perca semântica.
### Pub/Sub e sequência
Publicação deve ocorrer após a decisão funcional e não pode alterar seu resultado. Configure provider, tópicos/streams, filtros e política de falha. Quando o consumidor exigir ordem, publique `sequence`, chave de partição estável e correlação; múltiplos pods não garantem ordem global. Defina TTL/retenção e comportamento para duplicatas. Eventos filtrados no Pub/Sub continuam disponíveis nos outros destinos de observabilidade.
### Checklist operacional
- renderer não expõe payload bruto;
- resposta sanitizada é revalidada;
- retries têm limite e distinguem erro transitório;
- efeitos incertos consultam idempotência/status;
- evento contém correlação e não contém segredo;
- labels externas são mapeadas por configuração;
- sequência e chave de partição são testadas com múltiplos consumidores;
- interrupção de voz não confirma nem repete transação.
O exemplo de renderer é compilado e executado por `tests/test_advanced_developer_documentation.py`.

View File

@@ -0,0 +1,93 @@
### Multiagente, composição e ciclo de vida
### O que constitui um agente isolado
Um `agent_id` não é apenas um label. O isolamento real precisa abranger prompt, routing, tools, MCP servers, guardrails, judges, memória, checkpoints, cache, telemetria e identidade. A chave operacional recomendada é `tenant_id:agent_id:session_id`.
```yaml agents-registry-example
default_agent_id: financial
agents:
- agent_id: financial
name: Agente Financeiro
description: Consultas e operações financeiras.
prompt_policy_path: ./config/agents/financial/prompt_policy.yaml
routing_config_path: ./config/agents/financial/routing.yaml
guardrails_config_path: ./config/agents/financial/guardrails.yaml
judges_config_path: ./config/agents/financial/judges.yaml
mcp_servers_config_path: ./config/agents/financial/mcp_servers.yaml
tools_config_path: ./config/agents/financial/tools.yaml
metadata:
domain: financial
system_prefix: |
Execute somente políticas e recursos do agent_id financial.
```
Todos os caminhos devem existir e ser validados no startup. O prefixo ajuda o LLM, mas não é barreira de segurança. Autorização de tools, namespaces de persistência e seleção de pipelines devem ser impostas pelo código.
### Limitação real do template atual
O registry aceita esses caminhos, mas o `AgentWorkflow` corrente ainda constrói um único router, um único conjunto de tools/RAG e um par global de guardrails/judges. Portanto, copiar o YAML acima não cria isolamento completo por requisição. Até existir uma factory/cache por perfil, use uma configuração global por deployment ou implemente a seleção explícita antes de invocar o grafo.
Uma composição correta mantém, por `agent_id`, os objetos imutáveis/caros em cache e injeta dependências na execução:
```python agent-runtime-bundle-example
from dataclasses import dataclass
from typing import Any
@dataclass(frozen=True)
class AgentRuntimeBundle:
router: Any
tool_router: Any
rag_service: Any
guardrails: Any
judges: Any
def select_bundle(registry: dict[str, AgentRuntimeBundle], agent_id: str) -> AgentRuntimeBundle:
if agent_id not in registry:
raise KeyError(f"agent_id não registrado: {agent_id}")
return registry[agent_id]
```
Não construa LLM, conexão, router ou pipeline a cada mensagem. Não use fallback silencioso para o bundle de outro agente; falhe de forma explícita ou use somente o `default_agent_id` autorizado na borda.
### Subagentes e supervisor
Subagente é uma capacidade delimitada, não apenas uma função Python. Declare objetivo, entradas, saídas, tools autorizadas, orçamento/timeout e condições de retorno. O supervisor recebe resultados estruturados e consolida; ele não deve compartilhar todo o histórico por padrão. Uma delegação deve carregar correlação e profundidade máxima para evitar ciclos.
Use router quando uma única especialidade resolve o turno. Use supervisor quando a tarefa realmente exigir decomposição ou combinação de duas ou mais capacidades. Para handoff, transfira propriedade da conversa; para subagente, mantenha o supervisor como proprietário da resposta final.
### Composição de LLM por domínio
Use `llm_profiles.yaml` e o cliente criado pelo framework. Componentes recebem `profile_name` ou o LLM compartilhado; não criam SDK/provedor com credenciais próprias. Isso preserva autenticação, rate limits, retries, cache e telemetria. Fallback entre profiles precisa ser explícito e observável; nunca converta falha de configuração em resposta aparentemente válida.
### RAG e memória por agente
`RAG_PROVIDER=kbdb` seleciona KBDB apenas quando o `RagService` daquela execução usa as settings corretas. A intenção simples também deve acionar RAG quando a política do agente exigir conhecimento. Separe coleção/namespace por tenant e agente. Memória de longo prazo guarda fatos permitidos e resumidos; checkpoint guarda execução. Nenhum dos dois deve misturar identidade de outro agente.
### Autenticação e autorização
Autenticação prova o principal; autorização decide agente, tenant, rota e tool. Basic, API key, bearer, JWT, introspection e trusted proxy são providers possíveis, mas produção deve usar segredo com hash/rotação e TLS. Headers de trusted proxy só são confiáveis quando a borda autenticada remove valores enviados pelo cliente.
### Inicialização e readiness
No startup: validar registry e caminhos; criar providers; registrar actions/renderers; carregar workflows; construir bundles; verificar conexões essenciais. `/ready` deve falhar quando uma dependência obrigatória não estiver utilizável; `/health` indica processo vivo. Configuração inválida deve falhar cedo, antes da primeira conversa.
### Ciclo de desenvolvimento e certificação
1. Copiar o template canônico sem copiar `.venv` ou caches.
2. Criar agente, estado somente se necessário, configurações e recursos de domínio.
3. Registrar nó/rota e decidir router ou supervisor.
4. Implementar tools, mappings, políticas, actions e renderers.
5. Configurar guardrails, judges, RAG, memória e observabilidade.
6. Testar unidade, loader/config, grafo, regressão conversacional e falhas.
7. Executar avaliação offline, carga e readiness.
8. Publicar com versão, compatibilidade e rollback documentados.
### Definition of done
Um agente não está pronto apenas porque responde ao happy path. Ele precisa provar isolamento, confirmação antes de efeito, idempotência, recuperação, grounding, bloqueio/sanitização, telemetria correlacionada, ausência de vazamento, comportamento sob dependência indisponível e compatibilidade PT/EN dos manuais.
Os contratos deste capítulo são validados por `tests/test_advanced_developer_documentation.py`.

View File

@@ -126,11 +126,35 @@ Se algo não está funcionando, use **Buscar pelo problema** abaixo.
**Use quando:** mensagens de bloqueio são genéricas, `COER` deveria pedir esclarecimento ou o metadata de um turno bloqueado contém routing/tools antigos.
### [13 — Workflows Transacionais Avançados](./13_workflows_transacionais_avancados.md)
**O que é:** implementação de pré-validação, workflow versionado, actions, pausa/retomada, idempotência, replay e recuperação.
**Use quando:** uma tool sensível precisa executar um processo determinístico e recuperável.
### [14 — Routing, Parâmetros e Estado Avançados](./14_routing_parametros_e_estado_avancados.md)
**O que é:** precedência do router, confirmação semântica, extração híbrida, reconciliação temporal, estados dinâmicos e subagentes.
**Use quando:** routing, coleta ou retomada funcionam no happy path, mas falham em conversas reais.
### [15 — Apresentação, Recuperação e Observabilidade](./15_apresentacao_recuperacao_e_observabilidade.md)
**O que é:** renderers, Output Supervisor, recuperação, voice replay, eventos, mappings e Pub/Sub ordenado.
**Use quando:** a resposta altera evidências, um efeito pode repetir ou a telemetria não permite reconstruir o fluxo.
### [16 — Multiagente, Composição e Ciclo de Vida](./16_multiagente_composicao_e_ciclo_de_vida.md)
**O que é:** isolamento efetivo, bundles por agente, supervisor/subagentes, LLM/RAG/memória e readiness.
**Use quando:** vários agentes compartilham o deployment ou uma nova capacidade precisa chegar à produção.
### Tutorial principal
[`README.md`](../../../README.md) continua sendo a referência para o passo a passo completo:
`arquitetura → configuração → criação do agente → registro → estado → routing → tools → MCP → identidade → execução → testes → gateways → memória → RAG`.
`arquitetura → configuração → criação do agente → registro → estado → routing → tools → MCP → identidade → workflows → idempotência → execução → testes → gateways → memória → RAG → operação`.
### Manutenção

View File

@@ -2,6 +2,8 @@
Documentation for the main `agent_framework_oci` features.
To implement these features together in a real agent, also use [13 — Advanced Transactional Workflows](../../developer/en/13_advanced_transactional_workflows.md), [14 — Advanced Routing, Parameters, and State](../../developer/en/14_advanced_routing_parameters_and_state.md), [15 — Presentation, Recovery, and Observability](../../developer/en/15_presentation_recovery_and_observability.md), and [16 — Multi-Agent Composition and Lifecycle](../../developer/en/16_multi_agent_composition_and_lifecycle.md). They contain copyable contracts and the integrated development flow not repeated in every individual guide.
- [Authentication](01_authentication.md)
- [Deterministic Transactional Workflow](02_deterministic_transactional_workflow.md)
- [Domain Requested LLM Composition](03_domain_requested_llm_composition.md)

View File

@@ -2,6 +2,8 @@
Documentação das principais features do `agent_framework_oci`.
Para implementar as features em conjunto num agente real, use também os capítulos [13 — Workflows Transacionais Avançados](../../developer/pt/13_workflows_transacionais_avancados.md), [14 — Routing, Parâmetros e Estado](../../developer/pt/14_routing_parametros_e_estado_avancados.md), [15 — Apresentação, Recuperação e Observabilidade](../../developer/pt/15_apresentacao_recuperacao_e_observabilidade.md) e [16 — Multiagente e Ciclo de Vida](../../developer/pt/16_multiagente_composicao_e_ciclo_de_vida.md). Eles contêm contratos e códigos copiáveis que os guias individuais não repetem.
- [Autenticação](01_authentication.md)
- [Workflow Transacional Determinístico](02_deterministic_transactional_workflow.md)
- [Composição por LLM Solicitada pelo Domínio](03_domain_requested_llm_composition.md)

View File

@@ -0,0 +1,110 @@
from __future__ import annotations
import ast
import re
import sys
from pathlib import Path
from types import SimpleNamespace
import pytest
import yaml
ROOT = Path(__file__).resolve().parents[1]
FRAMEWORK_SRC = ROOT / "libs" / "agent_framework" / "src"
if str(FRAMEWORK_SRC) not in sys.path:
sys.path.insert(0, str(FRAMEWORK_SRC))
from agent_framework.workflows.models import WorkflowDefinition
from agent_framework.idempotency import create_idempotency_store
PT = ROOT / "docs" / "developer" / "pt"
def _tagged(path: Path, language: str, tag: str) -> str:
text = path.read_text(encoding="utf-8")
match = re.search(rf"```{language} {re.escape(tag)}\n(.*?)```", text, re.DOTALL)
assert match, f"bloco {tag!r} ausente em {path}"
return match.group(1)
@pytest.mark.parametrize(
("filename", "tag"),
[
("13_workflows_transacionais_avancados.md", "workflow-actions-example"),
("13_workflows_transacionais_avancados.md", "idempotency-example"),
("15_apresentacao_recuperacao_e_observabilidade.md", "renderer-example"),
("16_multiagente_composicao_e_ciclo_de_vida.md", "agent-runtime-bundle-example"),
],
)
def test_python_examples_compile(filename: str, tag: str) -> None:
source = _tagged(PT / filename, "python", tag)
ast.parse(source)
compile(source, f"{filename}:{tag}", "exec")
def test_documented_workflow_is_accepted_by_runtime_model() -> None:
raw = yaml.safe_load(_tagged(PT / "13_workflows_transacionais_avancados.md", "yaml", "workflow-definition-example"))
definition = WorkflowDefinition.model_validate(raw)
assert definition.name == "devolucao_pedido"
assert definition.version == 1
assert [node.action for node in definition.nodes] == ["validar_pedido", "registrar_devolucao"]
def test_documented_routing_and_registry_contracts() -> None:
routing = yaml.safe_load(_tagged(PT / "14_routing_parametros_e_estado_avancados.md", "yaml", "routing-advanced-example"))
mapping = yaml.safe_load(_tagged(PT / "14_routing_parametros_e_estado_avancados.md", "yaml", "parameter-mapping-example"))
registry = yaml.safe_load(_tagged(PT / "16_multiagente_composicao_e_ciclo_de_vida.md", "yaml", "agents-registry-example"))
semantic = routing["router"]["transaction_confirmation"]["semantic_fallback"]
assert set(semantic["allowed_values"]) == {"SIM", "NAO", "CONTINUAR"}
assert routing["intents"][0]["mcp_tools"] == ["consultar_saldo", "consultar_movimentacoes"]
assert mapping["mcp_parameter_mapping"]["tools"]["consultar_movimentacoes"]["extract"]["transaction_id"]["group"] == 1
assert registry["agents"][0]["agent_id"] == registry["default_agent_id"]
@pytest.mark.asyncio
async def test_documented_idempotency_example_executes_only_once() -> None:
namespace: dict[str, object] = {}
exec(_tagged(PT / "13_workflows_transacionais_avancados.md", "python", "idempotency-example"), namespace)
settings = SimpleNamespace(IDEMPOTENCY_PROVIDER="memory", IDEMPOTENCY_REQUIRE_DURABLE=False)
store = create_idempotency_store(settings, namespace="return-request")
calls = 0
async def invoke():
nonlocal calls
calls += 1
return {"protocol": "DEV-123"}
first = await namespace["execute_once"](store, "customer-1", "123", invoke)
second = await namespace["execute_once"](store, "customer-1", "123", invoke)
assert first == second == {"protocol": "DEV-123"}
assert calls == 1
def test_documented_renderer_masks_and_formats_output() -> None:
namespace: dict[str, object] = {}
exec(_tagged(PT / "15_apresentacao_recuperacao_e_observabilidade.md", "python", "renderer-example"), namespace)
rendered = namespace["render_balance"](
tool_name="consultar_saldo",
result={"account_masked": "****1234", "balance": 42.5, "secret": "must-not-leak"},
state={},
agent_label="Financeiro",
)
assert rendered == "[Financeiro] Conta ****1234: saldo BRL 42.50."
assert "must-not-leak" not in rendered
def test_new_chapters_have_english_counterparts_and_are_indexed() -> None:
pairs = {
"13_workflows_transacionais_avancados.md": "13_advanced_transactional_workflows.md",
"14_routing_parametros_e_estado_avancados.md": "14_advanced_routing_parameters_and_state.md",
"15_apresentacao_recuperacao_e_observabilidade.md": "15_presentation_recovery_and_observability.md",
"16_multiagente_composicao_e_ciclo_de_vida.md": "16_multi_agent_composition_and_lifecycle.md",
}
pt_index = (PT / "INDEX_DEVELOPER_GUIDE.md").read_text(encoding="utf-8")
en_index = (ROOT / "docs" / "developer" / "en" / "INDEX_DEVELOPER_GUIDE.md").read_text(encoding="utf-8")
for pt_name, en_name in pairs.items():
assert (PT / pt_name).is_file() and pt_name in pt_index
assert (ROOT / "docs" / "developer" / "en" / en_name).is_file() and en_name in en_index

View File

@@ -0,0 +1,69 @@
from __future__ import annotations
import re
import sys
from pathlib import Path
from types import SimpleNamespace
import pytest
ROOT = Path(__file__).resolve().parents[1]
GUIDE = ROOT / "docs" / "EXTERNAL_GUARDRAILS_JUDGES.md"
FRAMEWORK_SRC = ROOT / "libs" / "agent_framework" / "src"
if str(FRAMEWORK_SRC) not in sys.path:
sys.path.insert(0, str(FRAMEWORK_SRC))
from agent_framework.guardrails.pipeline import GuardrailPipeline
from agent_framework.judges.judge import JudgePipeline
def _block(tag: str) -> str:
text = GUIDE.read_text(encoding="utf-8")
match = re.search(rf"```(?:python|yaml) {re.escape(tag)}\n(.*?)```", text, re.DOTALL)
assert match, f"bloco {tag!r} ausente do manual"
return match.group(1)
@pytest.fixture()
def documented_agent(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
package = tmp_path / "financial_agent" / "extensions"
package.mkdir(parents=True)
(tmp_path / "financial_agent" / "__init__.py").write_text("", encoding="utf-8")
(package / "__init__.py").write_text("", encoding="utf-8")
(package / "guardrails.py").write_text(_block("external-guardrail-example"), encoding="utf-8")
(package / "judges.py").write_text(_block("external-judge-example"), encoding="utf-8")
(tmp_path / "guardrails.yaml").write_text(_block("external-guardrail-yaml"), encoding="utf-8")
(tmp_path / "judges.yaml").write_text(_block("external-judge-yaml"), encoding="utf-8")
monkeypatch.syspath_prepend(str(tmp_path))
return tmp_path
@pytest.mark.asyncio
async def test_documented_external_guardrail_loads_and_enforces_policy(documented_agent: Path) -> None:
pipeline = GuardrailPipeline(config_path=str(documented_agent / "guardrails.yaml"))
_, allowed = await pipeline.run_tool("transfer", {"amount": 1200.0})
_, denied = await pipeline.run_tool("transfer", {"amount": 2000.0})
_, missing = await pipeline.run_tool("transfer", {})
assert allowed[0].code == "FIN_AMOUNT" and allowed[0].allowed
assert denied[0].code == "FIN_AMOUNT" and not denied[0].allowed
assert missing[0].code == "FIN_AMOUNT" and not missing[0].allowed
@pytest.mark.asyncio
async def test_documented_external_judge_loads_with_injection(documented_agent: Path) -> None:
fake_llm = object()
settings = SimpleNamespace(ENABLE_JUDGES=True)
pipeline = JudgePipeline(
llm=fake_llm,
settings=settings,
config_path=str(documented_agent / "judges.yaml"),
)
passed = await pipeline.evaluate_all("Qual foi o saldo?", "O saldo foi R$ 100.", {"evidence": "R$ 100"})
failed = await pipeline.evaluate_all("Qual foi o saldo?", "Não tenho dados.", {"evidence": "R$ 100"})
assert pipeline.judges[0].llm is fake_llm
assert passed[0].name == "financial_evidence" and passed[0].passed
assert failed[0].name == "financial_evidence" and not failed[0].passed