First commit
This commit is contained in:
293
docs/developer/pt/01_architecture_and_concepts.md
Normal file
293
docs/developer/pt/01_architecture_and_concepts.md
Normal file
@@ -0,0 +1,293 @@
|
||||
|
||||
### Arquitetura e Conceitos do Agent Framework OCI
|
||||
|
||||
### Propósito deste documento
|
||||
|
||||
Este documento **não substitui o `README.md` da raiz** e não repete o tutorial de criação de agente.
|
||||
|
||||
Use:
|
||||
|
||||
- [`README.md`](../../../README.md) para desenvolver, configurar, executar e testar um agente de ponta a ponta;
|
||||
- este documento para compreender a arquitetura, os limites de responsabilidade, os componentes e onde cada tipo de implementação deve ficar;
|
||||
- os demais manuais desta pasta para aprofundar uma capacidade específica ou solucionar um problema.
|
||||
|
||||
A separação é intencional: existe **um único tutorial principal** e vários **manuais de referência especializados**.
|
||||
|
||||
### Fonte de verdade
|
||||
|
||||
Quando existir divergência documental, use esta ordem:
|
||||
|
||||
1. código da versão em uso;
|
||||
2. `README.md` / `README_en.md` da mesma versão;
|
||||
3. SPECs/SDDs normativas;
|
||||
4. manuais especializados desta pasta;
|
||||
5. release notes e `README_old*` apenas como histórico.
|
||||
|
||||
### Modelo mental da plataforma
|
||||
|
||||
O Agent Framework OCI deve ser entendido como uma plataforma em camadas.
|
||||
|
||||
O **framework core** fornece mecanismos reutilizáveis e neutros de domínio: runtime, estado, memória, roteamento, integração de tools, guardrails, judges, persistência, observabilidade e contratos comuns.
|
||||
|
||||
O **agente** contém aquilo que é específico do caso de uso: intents, prompts, regras de domínio, policies específicas, workflow de negócio, mapeamentos, integrações e componentes externos pertencentes àquele agente.
|
||||
|
||||
Os **gateways** tratam responsabilidades transversais de entrada, governança e integração. Eles não devem absorver a lógica de negócio do agente.
|
||||
|
||||
Os **MCP Servers** encapsulam ferramentas e integrações com serviços de domínio ou legados. O **MCP Gateway** fornece catálogo e governança centralizada dessas tools.
|
||||
|
||||
### Componentes principais
|
||||
|
||||
| Componente | Responsabilidade principal | Não deve conter |
|
||||
|---|---|---|
|
||||
| `libs/agent_framework/` | Runtime genérico, contratos, estado, memória, routing, guardrails, judges, integrações comuns | Regra específica de uma empresa ou agente |
|
||||
| `templates/agent_template_backend/` | Referência executável para criação de agentes | Fork permanente do core |
|
||||
| `apps/agent_gateway/` | Entrada governada, policies transversais, rate limit, autenticação, metadados | Workflow de negócio |
|
||||
| `apps/channel_gateway/` | Adaptação dos canais ao contrato canônico | Regra de negócio do agente |
|
||||
| `apps/mcp_gateway/` | Catálogo, autorização e execução central de tools | Lógica conversacional |
|
||||
| `mcp/servers/` | Integrações e tools por domínio | Orquestração global do agente |
|
||||
| `evals/` | Certificação e regressão | Lógica produtiva |
|
||||
| `deploy/` | Containers e Kubernetes | Regras funcionais |
|
||||
|
||||
### Fluxo conceitual de uma requisição
|
||||
|
||||
Uma requisição típica percorre as seguintes responsabilidades:
|
||||
|
||||
```text
|
||||
Canal
|
||||
|
|
||||
v
|
||||
Channel Gateway
|
||||
|
|
||||
v
|
||||
Agent Gateway
|
||||
| governança / autenticação / rate limit / metadata
|
||||
v
|
||||
Backend do agente
|
||||
|
|
||||
+--> Routing / stickiness / intent
|
||||
|
|
||||
+--> Estado / memória / checkpoint
|
||||
|
|
||||
+--> Guardrails / judges
|
||||
|
|
||||
+--> Workflow / políticas transacionais
|
||||
|
|
||||
+--> MCP Gateway
|
||||
|
|
||||
+--> MCP Server A --> sistema legado
|
||||
+--> MCP Server B --> serviço externo
|
||||
+--> MCP Server C --> API de domínio
|
||||
```
|
||||
|
||||
Nem toda implantação precisa utilizar todos os componentes. A composição deve seguir a necessidade do agente e os contratos da plataforma.
|
||||
|
||||
### Runtime do agente
|
||||
|
||||
O runtime atual é baseado em `AgentRuntimeMixin` e `RuntimeContext`.
|
||||
|
||||
O template importa o runtime através de `app.agents.runtime`, que reexporta a implementação oficial do framework. O objetivo é impedir que cada agente mantenha sua própria cópia divergente do runtime.
|
||||
|
||||
Entre as APIs atuais confirmadas no código estão:
|
||||
|
||||
```python
|
||||
AgentRuntimeMixin.get_runtime_context()
|
||||
AgentRuntimeMixin.normalize_tools_by_intent()
|
||||
AgentRuntimeMixin.build_tool_arguments()
|
||||
AgentRuntimeMixin.execute_tools_for_intent()
|
||||
AgentRuntimeMixin.prepare_memory_context()
|
||||
AgentRuntimeMixin.build_messages()
|
||||
AgentRuntimeMixin.transaction_state_patch()
|
||||
AgentRuntimeMixin.transaction_clarification_message()
|
||||
AgentRuntimeMixin.transaction_confirmation_message()
|
||||
AgentRuntimeMixin.build_direct_mcp_answer()
|
||||
```
|
||||
|
||||
Essas APIs representam capacidades do runtime. O desenvolvedor deve preferi-las a reconstruir manualmente a mesma lógica dentro de cada agente.
|
||||
|
||||
### Configuração versus código
|
||||
|
||||
Uma diretriz central do framework é que comportamento configurável permaneça em configuração.
|
||||
|
||||
Exemplos:
|
||||
|
||||
- agentes e metadados: `config/agents.yaml`;
|
||||
- roteamento: `config/routing.yaml`;
|
||||
- tools: `config/tools.yaml`;
|
||||
- MCP Servers e mappings: configuração MCP correspondente;
|
||||
- perfis de LLM: `llm_profiles.yaml`;
|
||||
- policies e extensões: arquivos de configuração específicos da capacidade.
|
||||
|
||||
O código deve implementar mecanismos. YAML/config deve escolher comportamento sempre que isso puder ser feito sem comprometer segurança ou contratos.
|
||||
|
||||
### Separação entre framework e agente
|
||||
|
||||
Uma mudança pertence ao **framework** quando introduz um mecanismo reutilizável por diferentes agentes.
|
||||
|
||||
Exemplos:
|
||||
|
||||
- nova SPI de guardrail;
|
||||
- novo contrato de resposta rica de LLM;
|
||||
- nova capacidade genérica de checkpoint;
|
||||
- novo mecanismo configurável de tool policy;
|
||||
- nova estratégia genérica de routing.
|
||||
|
||||
Uma mudança pertence ao **agente** quando expressa uma regra de um domínio ou empresa.
|
||||
|
||||
Exemplos:
|
||||
|
||||
- quais cobranças podem ser contestadas;
|
||||
- um prompt específico de telecom;
|
||||
- regras de VAS;
|
||||
- códigos internos de uma empresa;
|
||||
- mapeamento de um serviço legado;
|
||||
- fraseologia específica.
|
||||
|
||||
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.
|
||||
|
||||
**Memória de conversa** preserva contexto conversacional.
|
||||
|
||||
**Long-Term Memory** guarda fatos duráveis associados a uma identidade de negócio.
|
||||
|
||||
**Checkpoint** persiste snapshots do estado LangGraph para retomada.
|
||||
|
||||
Um checkpoint antigo não deve, sozinho, determinar qual transação está ativa. A decisão funcional deve usar o estado transacional canônico.
|
||||
|
||||
### Routing e execução são responsabilidades diferentes
|
||||
|
||||
O routing responde: **qual agente/intent deve tratar esta mensagem?**
|
||||
|
||||
A execução responde: **o que esse agente deve fazer agora?**
|
||||
|
||||
Route stickiness preserva continuidade, mas não deve impedir uma mudança explícita de intenção. Durante uma transação, parâmetros esperados e confirmação válida têm precedência para evitar falsos intent shifts.
|
||||
|
||||
Detalhes completos: [Roteamento, Stickiness e Intent Shift](./02_routing_stickiness_and_intent_shift.md).
|
||||
|
||||
### Tools e MCP
|
||||
|
||||
Uma tool representa uma capacidade invocável.
|
||||
|
||||
O MCP Server implementa ou expõe essa capacidade.
|
||||
|
||||
O MCP Gateway organiza catálogo, autorização, mapping e execução centralizada.
|
||||
|
||||
O agente decide **quando** uma tool deve ser usada dentro do seu fluxo; a tool/MCP decide **como** acessar o serviço correspondente.
|
||||
|
||||
Detalhes completos: [MCP, Tools, Policies e Extração de Parâmetros](./04_mcp_integration_tools_and_policies.md).
|
||||
|
||||
### Transações
|
||||
|
||||
Operações com efeitos colaterais exigem tratamento diferente de consultas.
|
||||
|
||||
O framework fornece mecanismos de estado, confirmação, políticas e workflow determinístico. Regras concretas permanecem no agente.
|
||||
|
||||
O LLM pode participar da interpretação e composição, mas não deve ser a única fonte de verdade para afirmar que uma operação crítica foi executada.
|
||||
|
||||
Detalhes completos: [Workflows Transacionais e Estado](./03_transaction_workflows_and_state.md).
|
||||
|
||||
### Guardrails e Judges
|
||||
|
||||
Guardrails controlam ou validam comportamento durante o processamento.
|
||||
|
||||
Judges avaliam qualidade, grounding e outros critérios.
|
||||
|
||||
O core fornece mecanismos nativos e pontos de extensão. Guardrails/judges específicos de um domínio devem ser carregados pelo agente por configuração, evitando imports específicos dentro do framework.
|
||||
|
||||
Detalhes completos: [Guardrails, Judges e Avaliação Transacional](./06_guardrails_judges_and_transaction_evaluation.md).
|
||||
|
||||
### RAG, memória e ferramentas não são equivalentes
|
||||
|
||||
- **RAG** recupera conhecimento.
|
||||
- **Memory** preserva contexto/fatos.
|
||||
- **Tool** executa ou consulta uma capacidade externa.
|
||||
|
||||
Escolher o mecanismo errado cria bugs difíceis de diagnosticar. Uma informação que precisa ser atualizada em sistema não deve ser resolvida apenas por RAG; um fato durável do cliente não deve depender apenas do histórico do prompt.
|
||||
|
||||
### Observabilidade como contrato transversal
|
||||
|
||||
Roteamento, agente, transação, tool, guardrail, judge e falha precisam ser correlacionáveis.
|
||||
|
||||
Observabilidade deve registrar o que aconteceu, mas não controlar estado de negócio. Sequence, trace IDs e labels são infraestrutura de diagnóstico e auditoria.
|
||||
|
||||
Detalhes completos: [Observabilidade, Persistência e Prontidão Operacional](./11_observability_persistence_and_operational_readiness.md).
|
||||
|
||||
### Onde colocar uma nova funcionalidade
|
||||
|
||||
Antes de implementar, faça estas perguntas:
|
||||
|
||||
1. A capacidade é reutilizável por diferentes agentes?
|
||||
2. Existe regra específica de domínio?
|
||||
3. Precisa de estado entre turnos?
|
||||
4. Produz efeito colateral?
|
||||
5. Depende de sistema externo?
|
||||
6. Deve ser configurável?
|
||||
7. Precisa aparecer em observabilidade?
|
||||
8. Precisa ser avaliada por guardrail/judge?
|
||||
|
||||
Uma feature reutilizável normalmente começa no core e é habilitada/configurada pelo agente. Uma regra de negócio normalmente começa no agente e usa interfaces do core.
|
||||
|
||||
### Anti-padrões
|
||||
|
||||
Evite:
|
||||
|
||||
- importar pacote concreto de um agente dentro do core;
|
||||
- duplicar `AgentRuntimeMixin` em cada agente;
|
||||
- codificar nomes de agentes, intents, tools ou empresas no runtime;
|
||||
- usar resposta do LLM como prova de execução de operação;
|
||||
- confundir checkpoint antigo com transação ativa;
|
||||
- executar operação transacional sem política/confirmacão quando ela é requerida;
|
||||
- acoplar agente diretamente a dezenas de serviços quando o MCP Gateway é a camada prevista;
|
||||
- criar um novo documento funcional para cada bug fix em vez de atualizar o manual da feature.
|
||||
|
||||
### Caminho recomendado para um novo desenvolvedor
|
||||
|
||||
1. Leia a visão arquitetural neste documento.
|
||||
2. Siga o [`README.md`](../../../README.md) do início ao fim para criar e executar um agente.
|
||||
3. Quando chegar a uma capacidade específica, use o manual especializado correspondente.
|
||||
4. Para falhas, comece pelo [Índice de Desenvolvimento](./INDEX_DEVELOPER_GUIDE.md), na seção **Buscar pelo problema**.
|
||||
5. Antes de copiar código antigo, confirme API/import no template e no core atuais.
|
||||
|
||||
### Documentos relacionados
|
||||
|
||||
- [Tutorial principal — README.md](../../../README.md)
|
||||
- [Roteamento, Stickiness e Intent Shift](./02_routing_stickiness_and_intent_shift.md)
|
||||
- [Workflows Transacionais e Estado](./03_transaction_workflows_and_state.md)
|
||||
- [MCP, Tools, Policies e Parâmetros](./04_mcp_integration_tools_and_policies.md)
|
||||
- [Gateways e Autenticação](./05_agent_gateway_mcp_gateway_and_auth.md)
|
||||
- [Guardrails e Judges](./06_guardrails_judges_and_transaction_evaluation.md)
|
||||
- [RAG e BusinessContext](./07_rag_business_context_and_grounding.md)
|
||||
- [Long-Term Memory e Checkpoint](./08_long_term_memory_and_checkpoint.md)
|
||||
- [LLM Rich Response](./09_llm_rich_response_reasoning.md)
|
||||
- [Performance, Cache e Runtime Assíncrono](./10_performance_cache_and_async_runtime.md)
|
||||
- [Observabilidade e Prontidão Operacional](./11_observability_persistence_and_operational_readiness.md)
|
||||
1491
docs/developer/pt/02_routing_stickiness_and_intent_shift.md
Normal file
1491
docs/developer/pt/02_routing_stickiness_and_intent_shift.md
Normal file
File diff suppressed because it is too large
Load Diff
782
docs/developer/pt/03_transaction_workflows_and_state.md
Normal file
782
docs/developer/pt/03_transaction_workflows_and_state.md
Normal file
@@ -0,0 +1,782 @@
|
||||
|
||||
### Workflows Transacionais e Estado
|
||||
|
||||
### Como usar este manual
|
||||
|
||||
Este é um **manual de referência especializado**. Ele não substitui o tutorial principal.
|
||||
|
||||
- Para criar um agente do início ao fim, use [`README.md`](../../../README.md).
|
||||
- Use este documento quando precisar implementar, aprofundar ou diagnosticar **estado transacional, coleta de parâmetros, confirmação, pausa/retomada e evidência operacional**.
|
||||
- Os exemplos históricos consolidados aqui devem ser lidos à luz da API atual do framework.
|
||||
- Em caso de divergência, o código da versão e o `README.md` atual prevalecem.
|
||||
|
||||
### Relação com o tutorial principal
|
||||
|
||||
O `README.md` apresenta essa capacidade no fluxo normal de desenvolvimento. Este manual reúne detalhes que estavam distribuídos em `docs/`, `Documentacao/`, release notes, validações e guias especializados.
|
||||
|
||||
O objetivo aqui é responder **“como essa feature funciona em profundidade e como eu resolvo problemas nela?”**, sem transformar este arquivo em uma segunda cópia do tutorial principal.
|
||||
|
||||
### Escopo
|
||||
|
||||
Estado transacional, coleta de parâmetros, confirmação, pausa/retomada e evidência operacional.
|
||||
|
||||
### Conteúdo técnico consolidado
|
||||
|
||||
### Workflows Transacionais, Estado Multi-turno e Retomada
|
||||
|
||||
Guia de implementação para operações multi-etapas, fonte canônica do estado transacional, confirmação, merge de parâmetros, pausa/retomada, evidência operacional e interação com roteamento.
|
||||
|
||||
### Como usar este documento
|
||||
|
||||
Este é o documento consolidado de desenvolvimento para este assunto. Ele reúne arquitetura, configuração, exemplos, comportamento de runtime, compatibilidade, testes e troubleshooting que antes estavam distribuídos em vários arquivos. As seções de origem foram preservadas quando traziam detalhes técnicos distintos; notas de release foram incorporadas como comportamento atual ou histórico de correção.
|
||||
|
||||
### Guia de estado transacional multi-turno
|
||||
|
||||
> Conteúdo consolidado a partir de `docs/TRANSACTION_STATE_DEVELOPER_GUIDE.md`.
|
||||
|
||||
Este documento define o contrato operacional para transações multi-turno no Agent Framework OCI. Ele é normativo para hosts e templates que utilizam `AgentRuntime`, checkpoint LangGraph e tools transacionais.
|
||||
|
||||
### 1. Objetivo
|
||||
|
||||
Uma transação pode atravessar vários turnos. Exemplo:
|
||||
|
||||
```text
|
||||
Usuário: quero cancelar o pedido
|
||||
Framework: informe o número do pedido
|
||||
Usuário: PED-1001
|
||||
Framework: confirma o cancelamento?
|
||||
Usuário: sim
|
||||
Framework: executa a tool
|
||||
```
|
||||
|
||||
O framework precisa preservar a transação entre todos esses turnos sem depender de reclassificação por LLM, keyword routing ou reextração de parâmetros já obtidos.
|
||||
|
||||
### 2. Fonte canônica do estado transacional
|
||||
|
||||
O estado canônico da transação em andamento é `active_transaction`.
|
||||
|
||||
```python
|
||||
active_transaction: dict[str, Any]
|
||||
last_transaction: dict[str, Any]
|
||||
```
|
||||
|
||||
Todo `AgentState` usado por um host que habilita transações multi-turno **DEVE** declarar os dois campos. Como o LangGraph usa o schema do state para persistência/checkpoint, um campo criado apenas dinamicamente pelo runtime não é um contrato durável seguro.
|
||||
|
||||
Exemplo mínimo:
|
||||
|
||||
```python
|
||||
from typing import Any, TypedDict
|
||||
|
||||
class AgentState(TypedDict, total=False):
|
||||
# ...campos normais...
|
||||
selected_tool_call: dict[str, Any]
|
||||
pending_tool_call: dict[str, Any]
|
||||
active_transaction: dict[str, Any]
|
||||
last_transaction: dict[str, Any]
|
||||
transaction_status: str
|
||||
missing_parameters: list[str]
|
||||
confirmation_required: bool
|
||||
confirmation_received: bool
|
||||
```
|
||||
|
||||
### 3. Papel de cada campo
|
||||
|
||||
| Campo | Papel | Regra |
|
||||
|---|---|---|
|
||||
| `active_transaction` | Fonte canônica da transação ativa | Deve sobreviver a checkpoint/resume enquanto a transação estiver ativa. |
|
||||
| `last_transaction` | Snapshot da última transação terminal | Usado para auditoria, evidência e continuidade controlada; não reativa automaticamente a transação. |
|
||||
| `transaction_status` | Estado lógico atual | Ex.: `COLLECTING_PARAMETERS`, `AWAITING_CONFIRMATION`, `COMPLETED`, `CANCELLED`, `OUT_OF_SCOPE`. |
|
||||
| `missing_parameters` | Parâmetros ainda necessários | Deve refletir o estado canônico da transação, não apenas a mensagem corrente. |
|
||||
| `selected_tool_call` | Estado auxiliar/compatibilidade | Não deve substituir `active_transaction` como fonte canônica. |
|
||||
| `pending_tool_call` | Estado auxiliar/compatibilidade | Pode ser usado por compatibilidade, mas não como latch principal. |
|
||||
| `next_state` | Orientação de roteamento do workflow | Ajuda a manter o nó/agente correto durante coleta/confirmação. |
|
||||
| `transaction_pre_validation` | Evidência de pré-validação | Mantém resultado de validação antes da confirmação/execução. |
|
||||
| `transaction_evidence` | Evidências da execução | Mantém resultados e trilha de execução da transação. |
|
||||
|
||||
### 4. Ciclo de vida recomendado
|
||||
|
||||
```text
|
||||
IDLE
|
||||
↓ intenção transacional
|
||||
COLLECTING_PARAMETERS
|
||||
↓ parâmetros completos
|
||||
PRE_VALIDATION (quando configurado)
|
||||
↓ elegível
|
||||
AWAITING_CONFIRMATION
|
||||
↓ confirmação positiva
|
||||
EXECUTING
|
||||
↓
|
||||
COMPLETED
|
||||
```
|
||||
|
||||
Saídas terminais alternativas:
|
||||
|
||||
```text
|
||||
CANCELLED
|
||||
OUT_OF_SCOPE
|
||||
FAILED
|
||||
```
|
||||
|
||||
O runtime pode representar algumas fases internamente sem um `transaction_status` público separado. O requisito é preservar o latch e não perder argumentos já coletados.
|
||||
|
||||
### 5. Merge incremental de parâmetros
|
||||
|
||||
Uma resposta posterior deve complementar a transação existente, nunca recriá-la apenas a partir do texto atual.
|
||||
|
||||
```python
|
||||
existing = dict((state.get("active_transaction") or {}).get("arguments") or {})
|
||||
new_values = {"valor": "71.99"}
|
||||
arguments = {**existing, **new_values}
|
||||
```
|
||||
|
||||
Exemplo esperado:
|
||||
|
||||
```text
|
||||
Turno 1: subject = "TIM CTRL Redes Sociais 8.0"
|
||||
Turno 2: valor = "71.99"
|
||||
Resultado: subject + valor permanecem disponíveis
|
||||
```
|
||||
|
||||
### 6. Precedência de roteamento durante transação
|
||||
|
||||
Quando existe `active_transaction` em `COLLECTING_PARAMETERS`, a mensagem deve primeiro ser avaliada como possível resposta aos parâmetros pendentes.
|
||||
|
||||
Precedência normativa:
|
||||
|
||||
1. parâmetro pendente claramente preenchido → continuar a transação;
|
||||
2. cancelamento/abandono explícito → cancelar a transação;
|
||||
3. nova intenção inequívoca → interromper a transação e rotear;
|
||||
4. keyword genérica do mesmo domínio/agente → **não** interromper a transação;
|
||||
5. mensagem ambígua → manter a transação e clarificar.
|
||||
|
||||
Exemplos:
|
||||
|
||||
| Estado atual | Mensagem | Resultado correto |
|
||||
|---|---|---|
|
||||
| `retail_order_cancel`, falta `order_id` | `PED-1001` | Continua cancelamento e preenche `order_id`. |
|
||||
| `retail_order_cancel`, falta `order_id` | `o pedido é o PED-1001` | Continua cancelamento; `pedido` não deve virar tracking. |
|
||||
| contestação, falta `valor` | `R$ 71,99` | Continua contestação e preenche `valor`. |
|
||||
| cancelamento pendente | `esquece, quero ver minha fatura` | Interrupção explícita permitida. |
|
||||
| cancelamento pendente | `quero rastrear pedido` | Mudança inequívoca para tracking permitida. |
|
||||
|
||||
### 7. Checkpoint e retomada
|
||||
|
||||
Antes de executar roteamento normal, o host deve restaurar o checkpoint usando a mesma identidade de conversa (`tenant_id`, `agent_id`, `session_id`/`conversation_key` conforme contrato do host).
|
||||
|
||||
Após a restauração:
|
||||
|
||||
```text
|
||||
active_transaction existe
|
||||
↓
|
||||
status ativo?
|
||||
↓ sim
|
||||
retomar a transação antes de keyword routing / continuity LLM
|
||||
```
|
||||
|
||||
Um estado `COLLECTING_PARAMETERS` sem `active_transaction` deve ser tratado como inconsistência de estado e observado/diagnosticado; não deve silenciosamente reiniciar a tool a partir da mensagem corrente.
|
||||
|
||||
### 8. O que pertence ao framework e ao agente
|
||||
|
||||
Framework:
|
||||
|
||||
- persistência do latch;
|
||||
- merge de argumentos;
|
||||
- estados de coleta/confirmação;
|
||||
- precedência de retomada;
|
||||
- confirmação determinística;
|
||||
- idempotência e evidência;
|
||||
- checkpoint/resume.
|
||||
|
||||
Agente:
|
||||
|
||||
- definição das tools de domínio;
|
||||
- parâmetros obrigatórios e mensagens de domínio;
|
||||
- regras de elegibilidade específicas;
|
||||
- pre-validation específica, quando houver;
|
||||
- resposta final ao cliente.
|
||||
|
||||
O agente não deve implementar um segundo motor transacional paralelo ao `AgentRuntime`.
|
||||
|
||||
### 9. Checklist para novos hosts/templates
|
||||
|
||||
- [ ] `AgentState` declara `active_transaction`.
|
||||
- [ ] `AgentState` declara `last_transaction`.
|
||||
- [ ] `transaction_status` e `missing_parameters` fazem parte do state quando usados.
|
||||
- [ ] O host usa checkpoint compatível com o schema do state.
|
||||
- [ ] A mesma `conversation_key` é usada entre turnos da mesma conversa.
|
||||
- [ ] Parâmetros já coletados são mesclados com novos valores.
|
||||
- [ ] Respostas a parâmetros têm precedência sobre keyword routing genérico.
|
||||
- [ ] Mudança explícita de intenção continua possível.
|
||||
- [ ] O agente usa `transaction_state_patch(state)` ao retornar respostas transacionais quando o template o exige.
|
||||
- [ ] Existem testes multi-turno para coleta, confirmação, interrupção e resume.
|
||||
|
||||
### 10. Testes regressivos mínimos
|
||||
|
||||
```text
|
||||
A. cancelamento de pedido
|
||||
1. "quero cancelar pedido"
|
||||
2. "o pedido é o PED-1001"
|
||||
Esperado: continua retail_order_cancel; não vira retail_order_tracking.
|
||||
|
||||
B. contestação
|
||||
1. "não contratei TIM CTRL Redes Sociais 8.0"
|
||||
2. "R$ 71,99"
|
||||
Esperado: subject e valor chegam juntos à pre-validation.
|
||||
|
||||
C. interrupção explícita
|
||||
1. iniciar transação e deixar parâmetro pendente
|
||||
2. "esquece, quero ver minha fatura"
|
||||
Esperado: transação é interrompida e nova intenção é roteada.
|
||||
|
||||
D. checkpoint/resume
|
||||
1. iniciar transação
|
||||
2. persistir/checkpoint
|
||||
3. reconstruir execução usando a mesma conversation_key
|
||||
4. fornecer o parâmetro faltante
|
||||
Esperado: active_transaction é restaurado e concluído sem reiniciar a tool.
|
||||
```
|
||||
|
||||
### 11. Anti-patterns
|
||||
|
||||
- reconstruir a transação somente a partir da última mensagem;
|
||||
- usar `selected_tool_call` como única fonte do latch;
|
||||
- remover `active_transaction` do `AgentState` por parecer redundante;
|
||||
- permitir uma keyword genérica como `pedido` interromper coleta de `order_id`;
|
||||
- armazenar parâmetros apenas em variáveis locais do nó;
|
||||
- duplicar confirmação transacional no prompt do agente;
|
||||
- limpar o latch antes do estado terminal.
|
||||
|
||||
### 12. Referências no projeto
|
||||
|
||||
- `specs/SPEC-002-Agent-Runtime.md`
|
||||
- `specs/SPEC-010-Agent-Development.md`
|
||||
- `templates/agent_template_backend/app/state.py`
|
||||
- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py`
|
||||
- `libs/agent_framework/src/agent_framework/routing/enterprise_router.py`
|
||||
- `Tuning-Performance/Deterministic_Transactional_Workflow/`
|
||||
- `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`.
|
||||
|
||||
### Decisão
|
||||
|
||||
Adicionar ao framework uma capacidade opcional de execução determinística baseada em LangGraph. O motor é genérico; definições YAML e actions de domínio permanecem nos agentes.
|
||||
|
||||
### Razão
|
||||
|
||||
Operações multi-etapas com efeitos colaterais não devem depender do LLM para escolher a sequência crítica. A solução reduz tokens, latência e variação, além de melhorar auditoria, testes e versionamento.
|
||||
|
||||
### Compatibilidade
|
||||
|
||||
`execution.mode` assume `direct_tool`. Projetos existentes continuam usando MCP diretamente. A adoção de workflow é explícita por tool e pode ser controlada por `ENABLE_TRANSACTIONAL_WORKFLOWS`.
|
||||
|
||||
### Limites desta entrega
|
||||
|
||||
A base inclui validação, versionamento por arquivo, registry, execução sync/async, condições, retry por nó, cache de grafos e adapter de policy. Persistência corporativa de execution records, compensação/Saga, autorização por escopo e emissão de IC/NOC específica devem ser conectadas às abstrações existentes de cada deployment antes do uso em transações financeiras críticas.
|
||||
|
||||
### Implementação dos workflows determinísticos
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/IMPLEMENTACAO_WORKFLOWS_TRANSACIONAIS.md`.
|
||||
|
||||
### Entrega
|
||||
|
||||
Foi adicionada ao `agent_framework_oci` uma capacidade opcional para executar transações multi-etapas como workflows determinísticos compilados em LangGraph.
|
||||
|
||||
### Módulo novo
|
||||
|
||||
`libs/agent_framework/src/agent_framework/workflows/`
|
||||
|
||||
- `models.py`: contratos Pydantic e validação estrutural;
|
||||
- `repository.py`: resolução de versão ativa e leitura de YAML imutável;
|
||||
- `registry.py`: registro desacoplado de actions sync/async;
|
||||
- `runtime.py`: compilação, cache e execução do StateGraph;
|
||||
- `tool_executor.py`: integração com a política da tool;
|
||||
- `__init__.py`: API pública.
|
||||
|
||||
### Política expandida
|
||||
|
||||
`ToolPolicy` agora aceita:
|
||||
|
||||
```yaml
|
||||
execution:
|
||||
mode: direct_tool | workflow | agent
|
||||
workflow: nome_do_workflow
|
||||
version: active | 1
|
||||
```
|
||||
|
||||
O default permanece `direct_tool`, preservando compatibilidade.
|
||||
|
||||
### Configuração
|
||||
|
||||
Foram adicionados:
|
||||
|
||||
- `ENABLE_TRANSACTIONAL_WORKFLOWS=false`;
|
||||
- `WORKFLOWS_PATH=./workflows`.
|
||||
|
||||
### Template
|
||||
|
||||
Inclui um exemplo completo de devolução de pedido com:
|
||||
|
||||
- confirmação e campos obrigatórios pela política;
|
||||
- workflow YAML versionado;
|
||||
- actions de domínio no backend;
|
||||
- bifurcação determinística baseada no resultado da validação.
|
||||
|
||||
### Validação realizada
|
||||
|
||||
- `tests/unit/test_tool_policies.py`: 4 testes aprovados;
|
||||
- compilação Python de framework, template e novos testes: aprovada;
|
||||
- o teste funcional novo do LangGraph foi criado, mas não pôde ser executado neste container porque `langgraph` não está instalado no ambiente. A dependência já está declarada no `pyproject.toml` do framework.
|
||||
|
||||
### Escopo e segurança
|
||||
|
||||
Esta entrega cria o motor e a integração de política. Para operações críticas em produção ainda é necessário conectar:
|
||||
|
||||
- execution store persistente;
|
||||
- idempotência de negócio nas actions/APIs;
|
||||
- autorização por escopo;
|
||||
- telemetria IC/NOC específica de workflow;
|
||||
- compensação/Saga quando aplicável;
|
||||
- estratégia corporativa de timeout e retry.
|
||||
|
||||
Esses itens foram explicitamente documentados para evitar a falsa impressão de que retry por si só garante segurança transacional.
|
||||
|
||||
### Precedência da coleta de parâmetros
|
||||
|
||||
> Conteúdo consolidado a partir de `FIX_TRANSACTION_PARAMETER_PRECEDENCE.md`.
|
||||
|
||||
Esta correção remove a extração textual hardcoded de parâmetros transacionais e faz a coleta de `policy.requires` por um extrator LLM genérico.
|
||||
|
||||
### Regra de precedência
|
||||
|
||||
Enquanto existir uma transação ativa, o framework trata o turno nesta ordem:
|
||||
|
||||
```text
|
||||
ACTIVE_TRANSACTION
|
||||
|
|
||||
+-- COLLECTING_PARAMETERS
|
||||
| |
|
||||
| +-- LLM tenta extrair SOMENTE os parâmetros ainda pendentes
|
||||
| |
|
||||
| +-- extraiu >= 1 ?
|
||||
| |
|
||||
| +-- SIM -> continua a transação; NÃO avalia intent_shift
|
||||
| |
|
||||
| +-- NÃO -> libera EnterpriseRouter para avaliar intent_shift
|
||||
|
|
||||
+-- AWAITING_CONFIRMATION
|
||||
|
|
||||
+-- reconhece confirmação/rejeição explícita
|
||||
|
|
||||
+-- reconheceu ?
|
||||
|
|
||||
+-- SIM -> continua/cancela a transação; NÃO avalia intent_shift
|
||||
|
|
||||
+-- NÃO -> libera EnterpriseRouter para avaliar intent_shift
|
||||
```
|
||||
|
||||
### TransactionParameterExtractor
|
||||
|
||||
Novo componente:
|
||||
|
||||
`libs/agent_framework/src/agent_framework/runtime/transaction_parameters.py`
|
||||
|
||||
A extração textual dos parâmetros de negócio é feita exclusivamente por LLM. O componente recebe:
|
||||
|
||||
- nome da tool/transação ativa;
|
||||
- parâmetros atualmente pendentes;
|
||||
- argumentos já conhecidos;
|
||||
- schema/tipos declarados em `tools.yaml` quando disponíveis;
|
||||
- descrição da tool;
|
||||
- mensagem atual do usuário.
|
||||
|
||||
Ele não conhece nomes de domínio como `order_id`, `reason`, `subject`, `valor`, TIM ou retail. Não há regex de entidades de negócio.
|
||||
|
||||
A LLM pode interpretar, por exemplo:
|
||||
|
||||
- `PED-1001` quando só há um parâmetro compatível pendente;
|
||||
- `o pedido é PED-1001`;
|
||||
- `PED-1001, desisti da compra` preenchendo dois parâmetros no mesmo turno;
|
||||
- respostas com o nome do parâmetro seguido do valor;
|
||||
- respostas apenas com o valor, quando semanticamente inequívocas.
|
||||
|
||||
Em caso de dúvida, o prompt manda retornar `null`. Uma nova solicitação não deve ser transformada em valor de parâmetro.
|
||||
|
||||
### Separação de responsabilidades
|
||||
|
||||
`tool_policies.yaml` continua sendo a fonte de verdade para `requires`.
|
||||
|
||||
`tools.yaml` pode fornecer tipos via `args_schema` e descrição da tool para melhorar a interpretação sem introduzir código específico de domínio.
|
||||
|
||||
`mcp_parameter_mapping.yaml` continua responsável pelos parâmetros auxiliares/contrato MCP. As strategies do mapper são explicitamente excluídas dos campos presentes em `policy.requires`, para não misturar extração MCP com coleta transacional.
|
||||
|
||||
O `EnterpriseRouter` usa o mesmo extrator LLM apenas como *probe* de precedência. Se pelo menos um parâmetro pendente for encontrado, o turno permanece no estado transacional. Os valores extraídos são colocados no metadata da decisão e reutilizados pelo runtime, evitando uma segunda chamada LLM no mesmo turno.
|
||||
|
||||
### Profile LLM
|
||||
|
||||
Foi adicionado aos templates:
|
||||
|
||||
```yaml
|
||||
transaction_parameter_extraction:
|
||||
provider: oci_openai
|
||||
model: openai.gpt-4.1-mini
|
||||
temperature: 0
|
||||
max_tokens: 500
|
||||
timeout_seconds: 8
|
||||
```
|
||||
|
||||
Generation/component:
|
||||
|
||||
- `llm.transaction_parameter_extraction`
|
||||
- `transaction_parameter_extraction`
|
||||
|
||||
### Limpeza de estado
|
||||
|
||||
Em `intent_shift`, `transaction_pre_validation` da transação abandonada é removido para não contaminar a nova transação. O resultado de pre-validation continua preservado enquanto pertence à própria transação para auditoria.
|
||||
|
||||
### Testes adicionados
|
||||
|
||||
`tests/test_transaction_parameter_llm_precedence.py`
|
||||
|
||||
Cobertura:
|
||||
|
||||
1. dois parâmetros extraídos no mesmo turno;
|
||||
2. um parâmetro preenchido ganha precedência sobre keyword que indicaria outra intent;
|
||||
3. nenhum parâmetro encontrado libera `intent_shift`;
|
||||
4. ausência do antigo `_extract_action_arguments()` hardcoded;
|
||||
5. confirmação `sim` ganha precedência sobre intent shift.
|
||||
|
||||
### Correção de loop entre transação e intent
|
||||
|
||||
> Conteúdo consolidado a partir de `FIX_TRANSACTION_INTENT_LOOP.md`.
|
||||
|
||||
Correção aplicada em 2026-08-20 para impedir que uma sessão fique presa em `COLLECTING_PARAMETERS` ou `AWAITING_CONFIRMATION` quando o usuário muda explicitamente de assunto.
|
||||
|
||||
### Comportamento corrigido
|
||||
|
||||
Antes:
|
||||
|
||||
1. uma transação entrava em `COLLECTING_PARAMETERS`;
|
||||
2. `next_state` forçava o mesmo agente via `state_policies`;
|
||||
3. toda mensagem seguinte era tratada como tentativa de preencher o parâmetro faltante;
|
||||
4. uma nova intenção como `quais sao meus servicos` permanecia presa no fluxo anterior.
|
||||
|
||||
Agora:
|
||||
|
||||
- o `EnterpriseRouter` verifica mudança explícita de intenção antes de aplicar o lock de estado;
|
||||
- keyword explícita tem prioridade;
|
||||
- quando necessário, o LLM router pode detectar mudança com confiança >= `router.confidence_threshold`;
|
||||
- a decisão recebe `metadata.transaction_interruption=intent_shift`;
|
||||
- o runtime encerra a transação pendente como `CANCELLED`, limpa `next_state`, parâmetros e latches, e prossegue com a nova intent;
|
||||
- cancelamentos explícitos como `cancele essa operação anterior` funcionam também durante `COLLECTING_PARAMETERS`.
|
||||
|
||||
### Testes adicionados
|
||||
|
||||
- mudança de intent durante `COLLECTING_PARAMETERS`;
|
||||
- resposta curta/baixa confiança permanece na transação;
|
||||
- cancelamento explícito durante coleta de parâmetros;
|
||||
- limpeza do estado transacional antes de executar a nova intent.
|
||||
|
||||
Testes focados: 19 passed.
|
||||
|
||||
### Evidência operacional de execução
|
||||
|
||||
> Conteúdo consolidado a partir de `docs/TRANSACTION_OPERATIONAL_EVIDENCE_FIX.md`.
|
||||
|
||||
### Problem
|
||||
|
||||
A confirmed transactional tool result was available only in the execution turn. On a later read-only turn, conversational memory could still mention the prior transaction (for example, a cancellation protocol), while the groundedness judge received only the current MCP results. This could classify a factually correct follow-up as unsupported.
|
||||
|
||||
### Fix
|
||||
|
||||
The framework now records completed/failed transactional tool outcomes as bounded operational evidence in LangGraph state/checkpoint (`transaction_evidence`). This is operational state, not Long Term Memory.
|
||||
|
||||
For each new turn, the runtime correlates previous transaction evidence with the current resource using generic identifiers (`*_id`, `order_id`, `invoice_id`, `asset_id`, `resource_key`, etc.). Only relevant evidence is materialized as `relevant_transaction_evidence`.
|
||||
|
||||
The same relevant evidence is:
|
||||
|
||||
- injected into the answering LLM prompt;
|
||||
- merged with current MCP results for groundedness judges;
|
||||
- exposed in response metadata as `transaction_evidence` for diagnostics;
|
||||
- emitted with the completion telemetry event.
|
||||
|
||||
The history is bounded to the 10 most recent transaction outcomes, and at most 5 correlated entries are injected for a turn.
|
||||
|
||||
### Expected retail example
|
||||
|
||||
1. `cancelar_pedido(PED-1001)` returns protocol `CANCEL-2026-001`.
|
||||
2. The result is persisted as transaction evidence.
|
||||
3. The next `consultar_pedido(PED-1001)` returns `EM_TRANSPORTE`.
|
||||
4. The answering agent and groundedness judge receive both the current order result and the prior cancellation evidence.
|
||||
5. A response that mentions `CANCEL-2026-001` is grounded rather than treated as an unsupported claim.
|
||||
|
||||
### Validação integrada Backend/MCP
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/VALIDACAO_TRANSACIONAL_BACKEND_MCP.md`.
|
||||
|
||||
### Correções implementadas
|
||||
|
||||
- `mcp_tools` é tratado como allowlist, não como lista de execução automática.
|
||||
- Tools `read_only` continuam disponíveis para enriquecimento de contexto.
|
||||
- Somente uma tool transacional compatível com a solicitação é selecionada.
|
||||
- `require_confirmation: true` cria `pending_tool_call` e `AWAITING_CONFIRMATION`.
|
||||
- O turno de confirmação executa a chamada pendente com `confirmed: true`.
|
||||
- O estado expõe `selected_tool_call`, `tool_policy_result`, `confirmation_required`, `confirmation_received` e `transaction_status`.
|
||||
- `reason` foi padronizado entre catálogo, mapping e FastMCP Retail.
|
||||
- Pedido `123` e `PED-ENTREGUE` retornam status `ENTREGUE` para testes positivos.
|
||||
- A keyword genérica `produto` foi removida da intenção Telecom para não capturar devoluções Retail.
|
||||
- Templates `Normal` e `Route_Stickness` em `Tuning-Performance` foram atualizados.
|
||||
|
||||
### Teste recomendado
|
||||
|
||||
1. `Quero devolver o pedido 123 porque me arrependi da compra.`
|
||||
2. Esperado: `transaction_status=AWAITING_CONFIRMATION`, sem execução de `solicitar_devolucao`.
|
||||
3. `Sim, confirmo a devolução.`
|
||||
4. Esperado: `transaction_status=COMPLETED` e execução única de `solicitar_devolucao`.
|
||||
|
||||
### Resultado automatizado
|
||||
|
||||
```text
|
||||
7 passed
|
||||
```
|
||||
|
||||
### Arquivos de origem
|
||||
|
||||
Os arquivos abaixo foram consolidados neste manual:
|
||||
|
||||
- `docs/TRANSACTION_STATE_DEVELOPER_GUIDE.md`
|
||||
- `docs/ADR_TRANSACTIONAL_WORKFLOW_ENGINE.md`
|
||||
- `Documentacao/IMPLEMENTACAO_WORKFLOWS_TRANSACIONAIS.md`
|
||||
- `FIX_TRANSACTION_PARAMETER_PRECEDENCE.md`
|
||||
- `FIX_TRANSACTION_INTENT_LOOP.md`
|
||||
- `docs/TRANSACTION_OPERATIONAL_EVIDENCE_FIX.md`
|
||||
- `Documentacao/VALIDACAO_TRANSACIONAL_BACKEND_MCP.md`
|
||||
|
||||
### Regra de manutenção
|
||||
|
||||
Novas correções ou evoluções deste tema devem atualizar este documento consolidado. Release notes podem continuar existindo como histórico, mas não devem ser necessárias para compreender ou implementar a funcionalidade.
|
||||
|
||||
|
||||
## Resolução canônica e revalidação de domínio antes da execução
|
||||
|
||||
Quando uma pré-validação resolve uma referência do usuário para uma entidade canônica, o framework **não deve simplesmente sobrescrever o parâmetro e executar a tool originalmente escolhida**. O contrato separa três valores:
|
||||
|
||||
```text
|
||||
requested_subject = "youtube"
|
||||
resolved_subject = "Youtube Premium"
|
||||
execution_subject = "Youtube Premium"
|
||||
```
|
||||
|
||||
O validador de domínio pode devolver `transaction_decision` com:
|
||||
|
||||
```json
|
||||
{
|
||||
"resolved_arguments": {"subject": "Youtube Premium"},
|
||||
"target_tool": "tratar_vas_estrategico",
|
||||
"action_changed": true,
|
||||
"requires_reconfirmation": true,
|
||||
"confirmation_message": "Identifiquei o serviço Youtube Premium. Esse serviço possui tratamento específico. Você deseja prosseguir?"
|
||||
}
|
||||
```
|
||||
|
||||
Responsabilidades:
|
||||
|
||||
- **Framework:** preserva argumentos solicitados, aplica apenas os argumentos canônicos declarados pelo validador, atualiza a transação para a `target_tool`, respeita `requires_reconfirmation` e mantém a decisão na evidência de pré-validação.
|
||||
- **Agente/domínio:** decide classe, política e tool efetiva. O framework não conhece regras como “Youtube Premium é estratégico”.
|
||||
- **MCP/backend:** executa a operação final já decidida pelo domínio.
|
||||
|
||||
Se a canonicalização não alterar a ação (`Tamboro` → `Tamboro Mensal`, por exemplo), a tool pode permanecer a mesma. Se a resolução alterar classe/política/tool, a decisão de domínio precisa ocorrer **antes da confirmação e da execução**. Em caso de ambiguidade ou baixa confiança, o validador deve pedir nova coleta/clarificação em vez de promover silenciosamente um candidato.
|
||||
|
||||
### Troubleshooting: resolved_subject correto, mas tool recebe o texto original
|
||||
|
||||
Sintoma: a pré-validação registra `resolved_subject="Youtube Premium"`, porém a execução ainda recebe `subject="youtube"`. Verifique se o validador retorna `transaction_decision.resolved_arguments` e se o runtime aplicou a decisão antes de congelar `pending_tool_call`/`confirmation_snapshot`.
|
||||
|
||||
Sintoma: a entidade foi resolvida corretamente, mas a tool final continua inadequada. Verifique `transaction_decision.target_tool`; a reclassificação de domínio pertence ao agente/validador, não ao framework.
|
||||
|
||||
Para domínios que possuem uma classificação autoritativa no detalhe do backend, a revalidação deve usar essa evidência antes de categorias agregadas. No Contas, por exemplo, `invoice_detail.parsed_content` preserva `classe=avulso|estrategico|bundle`; `billing_analysis` pode agrupar o mesmo item em seções mais amplas como `streaming` ou serviços de parceiros. A entidade canônica pode ser descoberta por qualquer evidência autorizada, mas a **decisão de negócio** deve priorizar a fonte que preserva a classificação de domínio. Se houver conflito de classificação, não troque a ação silenciosamente: mantenha a operação original ou peça esclarecimento conforme a política do agente.
|
||||
|
||||
|
||||
## Confirmação transacional semântica: SIM / NAO / CONTINUAR
|
||||
|
||||
Transações em `AWAITING_CONFIRMATION` usam duas camadas, nesta ordem:
|
||||
|
||||
1. **Parser determinístico** para confirmações/recusas explícitas (`sim`, `não`, `confirmo`, `pode fazer`, etc.). Esse caminho continua sendo o mais barato, rápido e seguro e **não chama LLM**.
|
||||
2. **Fallback semântico por LLM** somente quando o parser determinístico retorna inconclusivo. O fallback reutiliza o mesmo mecanismo declarativo de `expected_input.semantic_classifier` dos workflows pausados e injeta a pergunta pendente, o histórico recente relacionado ao mesmo tema e a fala atual.
|
||||
|
||||
A configuração fica em `config/routing.yaml`, sob `router.transaction_confirmation.semantic_fallback`:
|
||||
|
||||
```yaml
|
||||
router:
|
||||
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: |
|
||||
Classes permitidas: {{ allowed_values }}
|
||||
Pergunta pendente:
|
||||
{{ pending_prompt }}
|
||||
Histórico relevante:
|
||||
{{ relevant_conversation_context }}
|
||||
Resposta atual:
|
||||
{{ user_input }}
|
||||
```
|
||||
|
||||
### Significado das classes
|
||||
|
||||
- `SIM`: aceite inequívoco da ação pendente. Exemplos: `isso mesmo, pode confirmar`, `é isso`, `pode seguir`, quando o contexto torna o aceite claro.
|
||||
- `NAO`: recusa inequívoca da ação pendente. Exemplos: `melhor não`, `não quero mais`, `cancela isso`.
|
||||
- `CONTINUAR`: a fala não confirma nem rejeita de forma inequívoca. Exemplos: pergunta adicional, correção de parâmetro, informação nova, ambiguidade ou possível mudança de assunto. Nesse caso a tool não é executada por confirmação.
|
||||
|
||||
### Exemplo
|
||||
|
||||
Contexto:
|
||||
|
||||
```text
|
||||
Cliente: quero cancelar o Tamboro Mensal
|
||||
Agente: Você confirma o cancelamento do serviço Tamboro Mensal?
|
||||
Cliente: isso mesmo, pode confirmar
|
||||
```
|
||||
|
||||
O parser determinístico não precisa conhecer literalmente `isso mesmo, pode confirmar`. O fallback recebe:
|
||||
|
||||
```text
|
||||
pending_prompt = "Você confirma o cancelamento do serviço Tamboro Mensal?"
|
||||
relevant_conversation_context = histórico recente do mesmo fluxo
|
||||
user_input = "isso mesmo, pode confirmar"
|
||||
```
|
||||
|
||||
e deve retornar apenas:
|
||||
|
||||
```text
|
||||
SIM
|
||||
```
|
||||
|
||||
O router então publica em `route_decision.metadata`:
|
||||
|
||||
```json
|
||||
{
|
||||
"transaction_turn_consumed": true,
|
||||
"transaction_confirmation_decision": "confirm",
|
||||
"transaction_confirmation_source": "semantic"
|
||||
}
|
||||
```
|
||||
|
||||
O `AgentRuntime` reutiliza essa decisão e **não tenta reclassificar a mesma fala com o parser determinístico**. Isso evita a regressão em que o router entende semanticamente a confirmação, mas o runtime volta a tratá-la como inconclusiva.
|
||||
|
||||
### Precedência e compatibilidade
|
||||
|
||||
A funcionalidade é aditiva. Entradas determinísticas já suportadas continuam com o mesmo comportamento e sem custo adicional de LLM. O fallback semântico só roda quando a primeira camada não consegue decidir. Assim, `sim` e `não` continuam tendo precedência absoluta sobre `intent_shift`. Uma saída `CONTINUAR` não confirma nem rejeita automaticamente a transação; o fluxo normal pode então avaliar continuação contextual ou mudança de intenção conforme as políticas existentes.
|
||||
|
||||
### Observabilidade
|
||||
|
||||
Para confirmações semânticas, o framework registra a geração como `transaction.confirmation.semantic_classifier` e acrescenta ao metadata do roteamento a fonte `semantic`, a classificação retornada e o contexto conversacional relevante utilizado. Para confirmações literais, a fonte permanece `deterministic`.
|
||||
|
||||
### `expected_input.semantic_classifier.unmatched_value` e `reprompt`
|
||||
|
||||
Workflows pausados podem aceitar respostas literais e também interpretar respostas livres por semântica. Quando o `semantic_classifier` é habilitado, uma fala que **não representa com segurança nenhuma das classes válidas** não deve ser forçada para `SIM`, `NAO` ou `CONTINUAR`. Para esse caso, o workflow pode declarar um valor sentinela com `unmatched_value`.
|
||||
|
||||
Exemplo:
|
||||
|
||||
```yaml
|
||||
expected_input:
|
||||
key: resposta_usuario
|
||||
allowed_values:
|
||||
- SIM
|
||||
- NAO
|
||||
- CONTINUAR
|
||||
normalize: upper_strip
|
||||
reprompt: >
|
||||
Não entendi. Essa explicação resolveu sua dúvida?
|
||||
Responda sim ou não.
|
||||
|
||||
semantic_classifier:
|
||||
enabled: true
|
||||
include_relevant_context: true
|
||||
unmatched_value: OUTRO
|
||||
prompt: |
|
||||
Classifique a fala do cliente em exatamente uma das opções válidas quando houver
|
||||
significado suficiente.
|
||||
|
||||
SIM: entendimento/aceite inequívoco.
|
||||
NAO: negativa inequívoca.
|
||||
CONTINUAR: fala compreensível que complementa ou continua a solicitação.
|
||||
OUTRO: fala incompreensível, desconexa ou insuficiente para qualquer classe válida.
|
||||
|
||||
Retorne somente SIM, NAO, CONTINUAR ou OUTRO.
|
||||
|
||||
option_actions:
|
||||
CONTINUAR:
|
||||
action: contextual_reentry
|
||||
```
|
||||
|
||||
O contrato é:
|
||||
|
||||
```text
|
||||
SIM -> resume com SIM
|
||||
NAO -> resume com NAO
|
||||
CONTINUAR -> executa a option_action configurada (por exemplo contextual_reentry)
|
||||
OUTRO -> não é usado como valor de resume; devolve o reprompt e mantém o workflow pausado
|
||||
```
|
||||
|
||||
Regras importantes:
|
||||
|
||||
- `unmatched_value` é **sentinela do classificador**, não uma resposta de negócio;
|
||||
- não adicione o sentinela a `allowed_values`; `allowed_values` continua descrevendo apenas valores que podem ser consumidos pelo workflow;
|
||||
- `CONTINUAR` deve ser usado somente quando a fala é compreensível e realmente continua o assunto; uma fala sem significado contextual suficiente deve produzir `unmatched_value` e cair em `reprompt`;
|
||||
- o `reprompt` não encerra nem reinicia a execução: o mesmo workflow permanece `PAUSED`, aguardando nova resposta;
|
||||
- valores internos como `SIM`, `NAO`, `CONTINUAR` e o sentinela não devem vazar como resposta final do agente.
|
||||
|
||||
Exemplo comportamental:
|
||||
|
||||
```text
|
||||
Pergunta pendente: "Com essa explicação, sanei sua dúvida?"
|
||||
|
||||
"sim" -> SIM
|
||||
"não resolveu" -> NAO
|
||||
"mas e a cobrança de R$ 14,99?" -> CONTINUAR -> contextual_reentry
|
||||
"ano" -> OUTRO -> reprompt
|
||||
```
|
||||
|
||||
A opção é **opt-in por workflow**. Workflows que não declaram `semantic_classifier.unmatched_value` preservam o contrato anterior. Isso evita que uma evolução específica de um fluxo altere silenciosamente os demais workflows/agentes.
|
||||
|
||||
### Compatibilidade de interrupts duráveis no pause/resume
|
||||
|
||||
O runtime não usa `snapshot.next` isoladamente para decidir se um workflow está pausado. Um `next` pode representar trabalho auxiliar do LangGraph, inclusive nós sintéticos criados pelo framework como `__pause` e `__continue`.
|
||||
|
||||
A pausa é reconhecida por um interrupt real. Dependendo da versão do LangGraph/checkpointer, esse interrupt pode aparecer em `task.interrupts` ou persistido em `snapshot.values["__interrupt__"]`. O runtime aceita ambas as formas e deduplica o payload quando as duas são expostas simultaneamente.
|
||||
|
||||
Isso evita dois falsos diagnósticos:
|
||||
|
||||
- considerar `snapshot.next` como `PAUSED` quando não existe interrupt real;
|
||||
- considerar um `next=("<node>__pause",)` como erro de trabalho pendente quando o interrupt está persistido em `__interrupt__`.
|
||||
|
||||
Em workflows com `expected_input.semantic_classifier`, os tokens internos `SIM`, `NAO` e `CONTINUAR` continuam sendo valores de controle do resume e não devem ser confundidos com resposta final ao cliente.
|
||||
851
docs/developer/pt/04_mcp_integration_tools_and_policies.md
Normal file
851
docs/developer/pt/04_mcp_integration_tools_and_policies.md
Normal file
@@ -0,0 +1,851 @@
|
||||
|
||||
### MCP, Tools, Policies e Extração de Parâmetros
|
||||
|
||||
### Como usar este manual
|
||||
|
||||
Este é um **manual de referência especializado**. Ele não substitui o tutorial principal.
|
||||
|
||||
- Para criar um agente do início ao fim, use [`README.md`](../../../README.md).
|
||||
- Use este documento quando precisar implementar, aprofundar ou diagnosticar **tools, MCP Servers, mappings, policies read-only/transacionais e extração de parâmetros**.
|
||||
- Os exemplos históricos consolidados aqui devem ser lidos à luz da API atual do framework.
|
||||
- Em caso de divergência, o código da versão e o `README.md` atual prevalecem.
|
||||
|
||||
### Relação com o tutorial principal
|
||||
|
||||
O `README.md` apresenta essa capacidade no fluxo normal de desenvolvimento. Este manual reúne detalhes que estavam distribuídos em `docs/`, `Documentacao/`, release notes, validações e guias especializados.
|
||||
|
||||
O objetivo aqui é responder **“como essa feature funciona em profundidade e como eu resolvo problemas nela?”**, sem transformar este arquivo em uma segunda cópia do tutorial principal.
|
||||
|
||||
### Escopo
|
||||
|
||||
Tools, mcp servers, mappings, policies read-only/transacionais e extração de parâmetros.
|
||||
|
||||
### Conteúdo técnico consolidado
|
||||
|
||||
### Integração MCP, Tools, Políticas e Extração de Parâmetros
|
||||
|
||||
Manual de desenvolvimento para integrar MCP Servers, registrar tools, isolar tools por agente, configurar políticas read-only/transacionais, confirmação e extração contextual de parâmetros.
|
||||
|
||||
### Como usar este documento
|
||||
|
||||
Este é o documento consolidado de desenvolvimento para este assunto. Ele reúne arquitetura, configuração, exemplos, comportamento de runtime, compatibilidade, testes e troubleshooting que antes estavam distribuídos em vários arquivos. As seções de origem foram preservadas quando traziam detalhes técnicos distintos; notas de release foram incorporadas como comportamento atual ou histórico de correção.
|
||||
|
||||
### Manual completo de integração MCP Servers
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/Manual_Integracao_MCP_Servers_Agent_Framework.docx`.
|
||||
|
||||
Manual de Integração com Servidores MCP
|
||||
Agent Framework Multi-Agent - Router, Supervisor, Tools e Servidores Externos
|
||||
Este documento explica os conceitos de MCP, como o projeto atual integra servidores MCP, como subir os servidores de exemplo Telecom e Retail, como configurar tools por agente e como evoluir a implementação para um MCP mais aderente ao padrão oficial. O objetivo é servir como guia de desenvolvimento, operação local e implantação em container/OCI.
|
||||
|
||||
### Conceitos de MCP
|
||||
|
||||
MCP significa Model Context Protocol. Ele define uma forma padronizada para aplicações de IA acessarem contexto externo, ferramentas e capacidades de sistemas fora do modelo. Em vez de colocar integrações diretamente dentro do prompt ou dentro do agente, o MCP separa a responsabilidade: o agente decide o que precisa, e um servidor MCP oferece tools, resources e prompts de forma controlada.
|
||||
No padrão oficial, o MCP usa mensagens JSON-RPC e define transportes como stdio e Streamable HTTP. O projeto atual usa uma implementação HTTP simplificada para facilitar entendimento e testes locais, com endpoints REST /mcp/tools/list e /mcp/tools/call. Isso é adequado para tutorial e prototipação, mas pode ser evoluído para um client MCP oficial posteriormente.
|
||||
|
||||
### Como o projeto atual organiza MCP
|
||||
|
||||
A estrutura relevante do projeto é:
|
||||
```
|
||||
projeto_multi_agent_isolado/
|
||||
agent_framework/
|
||||
src/agent_framework/mcp/
|
||||
client.py
|
||||
models.py
|
||||
registry.py
|
||||
tool_router.py
|
||||
|
||||
agent_template_backend/
|
||||
config/
|
||||
mcp_servers.yaml
|
||||
mcp_servers.docker.yaml
|
||||
tools.yaml
|
||||
mcp_parameter_mapping.yaml
|
||||
app/
|
||||
main.py
|
||||
workflows/agent_graph.py
|
||||
|
||||
mcp_servers/
|
||||
telecom_mcp_server/
|
||||
main.py
|
||||
requirements.txt
|
||||
Dockerfile
|
||||
retail_mcp_server/
|
||||
main.py
|
||||
requirements.txt
|
||||
Dockerfile
|
||||
|
||||
scripts/
|
||||
run_mcp_servers.sh
|
||||
docker-compose.yml
|
||||
```
|
||||
|
||||
### Componentes principais
|
||||
|
||||
|
||||
|
||||
|
||||
### MCP multi-item: listas de produtos, pedidos e outros recursos
|
||||
|
||||
Quando uma única intenção precisa operar sobre vários itens, **não crie um loop MCP dentro do agente**. O framework suporta contratos escalares ou `array/list`, pre-validation com `resolved_arguments`, confirmação transacional do conjunto e normalização de `results[]` por item em `SUCCESS`, `PARTIAL_SUCCESS` ou `FAILED`. Multi-item não depende do tipo do parâmetro de entrada.
|
||||
|
||||
O contrato completo, exemplos de `tools.yaml`, `tool_policies.yaml`, payload MCP, retorno por item, workflows, validação parcial e anti-patterns estão em:
|
||||
|
||||
- [MCP Multi-item — Guia do Desenvolvedor](../../MCP_MULTI_ITEM_DEVELOPER_GUIDE.md)
|
||||
|
||||
Regra estrutural mínima do retorno da tool primária:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"results": [
|
||||
{"subject": "A", "success": true},
|
||||
{"subject": "B", "success": false, "reason": "not_found"}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Cada item deve expor `success` ou `ok` booleano. Para tool baseada em workflow, prefira `workflow.output[tool_name].results`. Uma falha auxiliar posterior não deve apagar resultados terminais já executados.
|
||||
|
||||
### Contrato HTTP simplificado usado no projeto
|
||||
|
||||
```
|
||||
GET /mcp/tools/list
|
||||
POST /mcp/tools/call
|
||||
|
||||
Payload de chamada:
|
||||
{
|
||||
"tool_name": "consultar_fatura",
|
||||
"arguments": {
|
||||
"msisdn": "11999999999",
|
||||
"invoice_id": "INV-001"
|
||||
}
|
||||
}
|
||||
|
||||
Resposta esperada:
|
||||
{
|
||||
"ok": true,
|
||||
"result": { ... },
|
||||
"metadata": {
|
||||
"server": "telecom",
|
||||
"tool": "consultar_fatura"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Como subir os servidores MCP de exemplo
|
||||
|
||||
O projeto possui dois servidores MCP de exemplo: Telecom e Retail. Eles são FastAPI apps independentes. O servidor Telecom roda na porta 8100 e expõe tools como consultar_fatura, consultar_pagamentos, consultar_plano e listar_servicos. O servidor Retail roda na porta 8200 e expõe tools como consultar_pedido, consultar_entrega, solicitar_troca e solicitar_devolucao.
|
||||
|
||||
### Subida local via script
|
||||
|
||||
```
|
||||
cd projeto_multi_agent_isolado
|
||||
bash ./scripts/run_mcp_servers.sh
|
||||
```
|
||||
O script cria uma venv no diretório raiz, instala as dependências dos servidores MCP e sobe os dois processos uvicorn em background:
|
||||
```
|
||||
Telecom MCP: http://localhost:8100
|
||||
Retail MCP: http://localhost:8200
|
||||
```
|
||||
|
||||
### Subida manual do Telecom MCP
|
||||
|
||||
```
|
||||
cd projeto_multi_agent_isolado
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r mcp_servers/telecom_mcp_server/requirements.txt
|
||||
uvicorn --app-dir mcp_servers/telecom_mcp_server main:app --host 0.0.0.0 --port 8100
|
||||
```
|
||||
|
||||
### Subida manual do Retail MCP
|
||||
|
||||
```
|
||||
cd projeto_multi_agent_isolado
|
||||
source .venv/bin/activate
|
||||
pip install -r mcp_servers/retail_mcp_server/requirements.txt
|
||||
uvicorn --app-dir mcp_servers/retail_mcp_server main:app --host 0.0.0.0 --port 8200
|
||||
```
|
||||
|
||||
### Subida com Docker Compose
|
||||
|
||||
```
|
||||
cd projeto_multi_agent_isolado
|
||||
docker compose up --build
|
||||
```
|
||||
No Docker Compose, o backend usa mcp_servers.docker.yaml porque, dentro da rede do compose, localhost apontaria para o próprio container do backend. Por isso os endpoints usam nomes de serviço: telecom-mcp e retail-mcp.
|
||||
```
|
||||
services:
|
||||
telecom-mcp:
|
||||
ports:
|
||||
- "8100:8100"
|
||||
|
||||
retail-mcp:
|
||||
ports:
|
||||
- "8200:8200"
|
||||
|
||||
backend:
|
||||
environment:
|
||||
MCP_SERVERS_CONFIG_PATH: /app/config/mcp_servers.docker.yaml
|
||||
depends_on:
|
||||
- telecom-mcp
|
||||
- retail-mcp
|
||||
```
|
||||
|
||||
### Como testar as tools MCP
|
||||
|
||||
|
||||
### Health check direto nos servidores
|
||||
|
||||
```
|
||||
curl http://localhost:8100/health
|
||||
curl http://localhost:8200/health
|
||||
```
|
||||
|
||||
### Listar tools diretamente no Telecom MCP
|
||||
|
||||
```
|
||||
curl http://localhost:8100/mcp/tools/list
|
||||
```
|
||||
|
||||
### Chamar tool diretamente no Telecom MCP
|
||||
|
||||
```
|
||||
curl -X POST http://localhost:8100/mcp/tools/call -H 'Content-Type: application/json' -d '{
|
||||
"tool_name": "consultar_fatura",
|
||||
"arguments": {
|
||||
"msisdn": "11999999999",
|
||||
"invoice_id": "INV-001"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Chamar tool diretamente no Retail MCP
|
||||
|
||||
```
|
||||
curl -X POST http://localhost:8200/mcp/tools/call -H 'Content-Type: application/json' -d '{
|
||||
"tool_name": "consultar_pedido",
|
||||
"arguments": {
|
||||
"order_id": "PED-1001",
|
||||
"customer_id": "C-001"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Testar via backend do agente
|
||||
|
||||
Após subir os servidores MCP e o backend, o backend disponibiliza endpoints de debug para listar e chamar tools através do MCPToolRouter.
|
||||
```
|
||||
cd agent_template_backend
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -e ../agent_framework
|
||||
pip install -r requirements.txt
|
||||
uvicorn app.main:app --reload --reload-dir app --reload-dir config --port 8000
|
||||
curl http://localhost:8000/debug/mcp/tools
|
||||
|
||||
curl -X POST http://localhost:8000/debug/mcp/call/consultar_fatura -H 'Content-Type: application/json' -d '{"msisdn":"11999999999","invoice_id":"INV-001"}'
|
||||
```
|
||||
|
||||
### Como o agente chama MCP no fluxo
|
||||
|
||||
O agente não precisa conhecer a URL do servidor. Ele chama uma tool lógica pelo MCPToolRouter. O fluxo esperado é:
|
||||
```
|
||||
Usuário
|
||||
-> FastAPI /gateway/message
|
||||
-> Guardrails de input
|
||||
-> Router ou Supervisor escolhe o agente
|
||||
-> LangGraph executa o agent graph
|
||||
-> Agent decide usar uma tool
|
||||
-> MCPToolRouter.call("consultar_fatura", {...})
|
||||
-> MCPRegistry resolve servidor telecom
|
||||
-> MCPHttpClient chama http://localhost:8100/mcp/tools/call
|
||||
-> Resultado volta ao agent graph
|
||||
-> Guardrails de output
|
||||
-> Judges
|
||||
-> Resposta final
|
||||
```
|
||||
|
||||
### Exemplo conceitual em Python
|
||||
|
||||
```
|
||||
result = await tool_router.call(
|
||||
"consultar_fatura",
|
||||
{
|
||||
"msisdn": context.get("msisdn"),
|
||||
"invoice_id": context.get("invoice_id"),
|
||||
},
|
||||
)
|
||||
|
||||
if result.ok:
|
||||
dados_fatura = result.result
|
||||
else:
|
||||
# fallback controlado, telemetria e resposta segura
|
||||
erro = result.error
|
||||
```
|
||||
|
||||
### Exemplo via mensagem do gateway
|
||||
|
||||
```
|
||||
curl -X POST http://localhost:8000/gateway/message -H 'Content-Type: application/json' -d '{
|
||||
"channel": "web",
|
||||
"payload": {
|
||||
"session_id": "sess-tel-1",
|
||||
"message": "Minha fatura veio alta",
|
||||
"context": {
|
||||
"msisdn": "11999999999",
|
||||
"invoice_id": "INV-001"
|
||||
}
|
||||
}
|
||||
}'
|
||||
curl -X POST http://localhost:8000/gateway/message -H 'Content-Type: application/json' -d '{
|
||||
"channel": "web",
|
||||
"payload": {
|
||||
"session_id": "sess-ret-1",
|
||||
"message": "Meu pedido não chegou",
|
||||
"context": {
|
||||
"order_id": "PED-1001",
|
||||
"customer_id": "C-001"
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Como configurar novos servidores e tools
|
||||
|
||||
|
||||
### Adicionar um novo MCP Server
|
||||
|
||||
Edite agent_template_backend/config/mcp_servers.yaml para execução local:
|
||||
```
|
||||
servers:
|
||||
crm:
|
||||
transport: http
|
||||
endpoint: http://localhost:8300/mcp
|
||||
enabled: true
|
||||
description: MCP Server de CRM.
|
||||
```
|
||||
Edite agent_template_backend/config/mcp_servers.docker.yaml para execução em Docker:
|
||||
```
|
||||
servers:
|
||||
crm:
|
||||
transport: http
|
||||
endpoint: http://crm-mcp:8300/mcp
|
||||
enabled: true
|
||||
description: MCP Server de CRM via docker-compose.
|
||||
```
|
||||
|
||||
### Registrar uma nova tool
|
||||
|
||||
Edite agent_template_backend/config/tools.yaml:
|
||||
```
|
||||
tools:
|
||||
consultar_cliente:
|
||||
description: Consulta dados cadastrais resumidos do cliente.
|
||||
mcp_server: crm
|
||||
enabled: true
|
||||
args_schema:
|
||||
customer_id: string
|
||||
document_id: string
|
||||
```
|
||||
|
||||
### Implementar o endpoint no servidor MCP
|
||||
|
||||
```
|
||||
TOOLS = {
|
||||
"consultar_cliente": {
|
||||
"description": "Consulta dados cadastrais resumidos do cliente.",
|
||||
"input_schema": {
|
||||
"customer_id": "string",
|
||||
"document_id": "string"
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@app.post("/mcp/tools/call")
|
||||
async def call_tool(call: ToolCall):
|
||||
if call.tool_name == "consultar_cliente":
|
||||
return {
|
||||
"ok": True,
|
||||
"result": {
|
||||
"customer_id": call.arguments.get("customer_id"),
|
||||
"status": "ATIVO",
|
||||
"segmento": "PREMIUM"
|
||||
},
|
||||
"metadata": {"server": "crm", "tool": "consultar_cliente"}
|
||||
}
|
||||
```
|
||||
|
||||
### Como isolar MCP por agente
|
||||
|
||||
Em uma arquitetura multi-agent, nem todo agente deve enxergar todas as tools. O agente de pedidos pode usar consultar_pedido e consultar_entrega. O agente de contas pode usar consultar_fatura e consultar_pagamentos. Esse isolamento reduz risco operacional, melhora governança e simplifica o prompt de cada agente.
|
||||
|
||||
### Opção simples: allowlist por agente
|
||||
|
||||
```
|
||||
agents:
|
||||
- agent_id: billing_agent
|
||||
allowed_tools:
|
||||
- consultar_fatura
|
||||
- consultar_pagamentos
|
||||
- consultar_plano
|
||||
- listar_servicos
|
||||
|
||||
- agent_id: orders_agent
|
||||
allowed_tools:
|
||||
- consultar_pedido
|
||||
- consultar_entrega
|
||||
- solicitar_troca
|
||||
- solicitar_devolucao
|
||||
```
|
||||
|
||||
### Opção recomendada: tools por arquivo de configuração
|
||||
|
||||
Para projetos grandes, cada agente pode ter seu próprio arquivo tools.yaml, guardrails.yaml e judges.yaml. Isso mantém isolamento real por agente e facilita versionamento.
|
||||
```
|
||||
config/agents/telecom_contas/
|
||||
prompt_policy.yaml
|
||||
guardrails.yaml
|
||||
judges.yaml
|
||||
tools.yaml
|
||||
|
||||
config/agents/retail_orders/
|
||||
prompt_policy.yaml
|
||||
guardrails.yaml
|
||||
judges.yaml
|
||||
tools.yaml
|
||||
```
|
||||
|
||||
### Como implantar com Docker e OCI
|
||||
|
||||
|
||||
### Implantação local com Docker Compose
|
||||
|
||||
O docker-compose.yml atual já possui serviços separados para telecom-mcp, retail-mcp, backend e frontend. Essa separação é correta porque MCP Servers devem ser escaláveis e versionáveis de forma independente do backend do agente.
|
||||
```
|
||||
docker compose up --build
|
||||
|
||||
# URLs externas para teste local:
|
||||
http://localhost:8100/health
|
||||
http://localhost:8200/health
|
||||
http://localhost:8000/debug/mcp/tools
|
||||
http://localhost:5173
|
||||
```
|
||||
|
||||
### Implantação em OCI/OKE
|
||||
|
||||
Em Kubernetes/OKE, cada MCP Server deve ser implantado como Deployment + Service. O backend do agente aponta para o DNS interno do Service. Exemplo conceitual:
|
||||
```
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: telecom-mcp
|
||||
spec:
|
||||
selector:
|
||||
app: telecom-mcp
|
||||
ports:
|
||||
- port: 8100
|
||||
targetPort: 8100
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: telecom-mcp
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: telecom-mcp
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: telecom-mcp
|
||||
spec:
|
||||
containers:
|
||||
- name: telecom-mcp
|
||||
image: <registry>/telecom-mcp:1.0.0
|
||||
ports:
|
||||
- containerPort: 8100
|
||||
```
|
||||
|
||||
### Configuração do backend em Kubernetes
|
||||
|
||||
```
|
||||
servers:
|
||||
telecom:
|
||||
transport: http
|
||||
endpoint: http://telecom-mcp.default.svc.cluster.local:8100/mcp
|
||||
enabled: true
|
||||
|
||||
retail:
|
||||
transport: http
|
||||
endpoint: http://retail-mcp.default.svc.cluster.local:8200/mcp
|
||||
enabled: true
|
||||
```
|
||||
|
||||
### Segurança, guardrails e observabilidade
|
||||
|
||||
MCP aumenta muito a capacidade do agente, mas também aumenta a superfície de risco. Uma tool pode consultar dados sensíveis, abrir protocolos, cancelar serviços, gerar créditos ou executar ações de negócio. Por isso, a integração precisa ser protegida antes, durante e depois da chamada.
|
||||
|
||||
### Checklist de segurança mínimo
|
||||
|
||||
- Toda tool deve ter descrição clara e schema de argumentos.
|
||||
- Toda tool de ação deve exigir confirmação explícita do usuário antes da execução.
|
||||
- Cada agente deve ter allowlist de tools.
|
||||
- Dados sensíveis retornados por MCP devem passar por masking/sanitização antes da resposta final.
|
||||
- Toda chamada MCP deve gerar trace/span/event em Langfuse ou OpenTelemetry.
|
||||
- Timeouts e limites de retries devem ser configurados por tool ou por servidor.
|
||||
- Não expor MCP Servers diretamente à internet sem autenticação, TLS e controle de rede.
|
||||
- Separar tools read-only de tools transacionais.
|
||||
|
||||
### Telemetria recomendada
|
||||
|
||||
```
|
||||
span: mcp.tool_call
|
||||
attributes:
|
||||
tenant_id
|
||||
agent_id
|
||||
session_id
|
||||
tool_name
|
||||
mcp_server
|
||||
latency_ms
|
||||
ok
|
||||
error
|
||||
input_argument_keys
|
||||
result_size
|
||||
|
||||
event: mcp.tool_call.completed
|
||||
metadata:
|
||||
tool_name
|
||||
server
|
||||
ok
|
||||
error
|
||||
```
|
||||
|
||||
### Evolução para MCP oficial
|
||||
|
||||
O projeto atual usa um contrato HTTP simplificado. Para produção corporativa, existem duas opções. A primeira é manter esse contrato interno por simplicidade, desde que ele seja bem documentado, seguro e versionado. A segunda é evoluir para um client/server MCP oficial com JSON-RPC, stdio ou Streamable HTTP.
|
||||
|
||||
### Passo a passo completo para o desenvolvedor
|
||||
|
||||
```
|
||||
# 1. Baixar e abrir o projeto
|
||||
cd projeto_multi_agent_isolado
|
||||
|
||||
# 2. Subir servidores MCP de exemplo
|
||||
bash ./scripts/run_mcp_servers.sh
|
||||
|
||||
# 3. Em outro terminal, subir backend
|
||||
cd agent_template_backend
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -e ../agent_framework
|
||||
pip install -r requirements.txt
|
||||
uvicorn app.main:app --reload --reload-dir app --reload-dir config --port 8000
|
||||
|
||||
# 4. Validar tools carregadas pelo backend
|
||||
curl http://localhost:8000/debug/mcp/tools
|
||||
|
||||
# 5. Chamar tool Telecom
|
||||
curl -X POST http://localhost:8000/debug/mcp/call/consultar_fatura -H 'Content-Type: application/json' -d '{"msisdn":"11999999999","invoice_id":"INV-001"}'
|
||||
|
||||
# 6. Chamar tool Retail
|
||||
curl -X POST http://localhost:8000/debug/mcp/call/consultar_pedido -H 'Content-Type: application/json' -d '{"order_id":"PED-1001","customer_id":"C-001"}'
|
||||
|
||||
# 7. Testar pelo gateway conversacional
|
||||
curl -X POST http://localhost:8000/gateway/message -H 'Content-Type: application/json' -d '{"channel":"web","payload":{"session_id":"sess-ret-1","message":"Meu pedido não chegou","context":{"order_id":"PED-1001","customer_id":"C-001"}}}'
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
|
||||
### Referências
|
||||
|
||||
- Model Context Protocol Specification: https://modelcontextprotocol.io/specification
|
||||
- MCP Transports: https://modelcontextprotocol.io/specification/2025-11-25/basic/transports
|
||||
- MCP Resources: https://modelcontextprotocol.io/specification/2025-06-18/server/resources
|
||||
- Reference MCP Servers: https://github.com/modelcontextprotocol/servers
|
||||
- LangChain MCP Adapters: https://docs.langchain.com/oss/python/langchain/mcp
|
||||
- Arquivos do projeto: agent_framework/src/agent_framework/mcp/*, agent_template_backend/config/mcp_servers.yaml, agent_template_backend/config/tools.yaml, mcp_servers/*
|
||||
|
||||
### Políticas read-only e transacionais
|
||||
|
||||
O framework aplica uma política conversacional mínima imediatamente antes da chamada MCP. A classificação read_only identifica consultas; transactional identifica operações que alteram estado. Autorização, idempotência, validação e atomicidade continuam sob responsabilidade do MCP Server.
|
||||
|
||||
### Configuração no backend
|
||||
|
||||
A configuração é opcional e fica em config/tool_policies.yaml no agent_template_backend. O caminho pode ser definido por TOOL_POLICIES_PATH. Não coloque políticas de domínio dentro da biblioteca compartilhada.
|
||||
Exemplo:
|
||||
defaults:
|
||||
operation_type: read_only
|
||||
require_confirmation: false
|
||||
tool_policies:
|
||||
alterar_plano:
|
||||
operation_type: transactional
|
||||
require_confirmation: true
|
||||
requires: [new_plan_id]
|
||||
|
||||
### Execução e compatibilidade
|
||||
|
||||
- A confirmação deve chegar como confirmed: true ou confirmation: true; texto com valor true não é suficiente.
|
||||
- Se tool_policies.yaml não existir, permanecem válidos tool_type, requires, confirmation_required e execution_policy de tools.yaml.
|
||||
- Tools antigas sem política continuam funcionando sem alteração de comportamento.
|
||||
- Uma chamada bloqueada não alcança o MCP e retorna metadados blocked_by_policy, operation_type e policy_source.
|
||||
|
||||
### Políticas read-only e transacionais
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/README_TOOL_POLICIES.md`.
|
||||
|
||||
### Objetivo
|
||||
|
||||
O framework diferencia operações de consulta (`read_only`) e operações que alteram estado (`transactional`) imediatamente antes da chamada MCP. Essa classificação não substitui autorização, idempotência ou regras de negócio do servidor MCP; ela acrescenta somente a proteção conversacional mínima, especialmente confirmação explícita.
|
||||
|
||||
### Onde configurar
|
||||
|
||||
A parametrização pertence ao backend da aplicação:
|
||||
|
||||
```text
|
||||
templates/agent_template_backend/config/tool_policies.yaml
|
||||
```
|
||||
|
||||
A biblioteca compartilhada contém apenas o loader e a validação. O caminho é opcional:
|
||||
|
||||
```dotenv
|
||||
TOOL_POLICIES_PATH=./config/tool_policies.yaml
|
||||
```
|
||||
|
||||
### Exemplo
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
|
||||
defaults:
|
||||
operation_type: read_only
|
||||
require_confirmation: false
|
||||
|
||||
tool_policies:
|
||||
consultar_plano:
|
||||
operation_type: read_only
|
||||
|
||||
alterar_plano:
|
||||
operation_type: transactional
|
||||
require_confirmation: true
|
||||
requires: [new_plan_id]
|
||||
```
|
||||
|
||||
Para executar `alterar_plano`, os argumentos precisam conter `new_plan_id` e um booleano literal de confirmação:
|
||||
|
||||
```json
|
||||
{"new_plan_id": "CONTROLE_100", "confirmed": true}
|
||||
```
|
||||
|
||||
Também é aceito `"confirmation": true`. Strings como `"true"` não são aceitas como confirmação.
|
||||
|
||||
### Compatibilidade
|
||||
|
||||
- Se `tool_policies.yaml` não existir, o framework continua usando `tool_type`, `requires`, `confirmation_required` e `execution_policy` de `tools.yaml`.
|
||||
- Tools antigas sem política continuam executando como antes.
|
||||
- Uma política explícita no arquivo novo prevalece para `operation_type` e confirmação daquela tool.
|
||||
- O catálogo `tools.yaml` continua sendo a fonte de endpoint, schema, habilitação e cache.
|
||||
- O novo arquivo não deve ser colocado em `libs/agent_framework`, pois as decisões variam por aplicação e domínio.
|
||||
|
||||
### Fluxo de execução
|
||||
|
||||
```text
|
||||
agente -> MCPToolRouter -> validação da política -> mapeamento de parâmetros -> MCP Gateway/Server
|
||||
```
|
||||
|
||||
Uma chamada bloqueada retorna `ok=false`, `metadata.blocked_by_policy=true`, o tipo da operação e a origem da política. O servidor MCP permanece a autoridade final para autenticação, autorização, validação, idempotência e transação de negócio.
|
||||
|
||||
### Migração recomendada
|
||||
|
||||
1. Atualize a biblioteca sem criar o arquivo: o comportamento permanece legado.
|
||||
2. Crie `config/tool_policies.yaml` no backend.
|
||||
3. Cadastre primeiro apenas operações transacionais que exigem confirmação.
|
||||
4. Teste chamadas sem confirmação, com confirmação booleana e com campos obrigatórios ausentes.
|
||||
5. Remova gradualmente duplicações de confirmação de `tools.yaml` quando todos os templates consumidores já usarem a nova configuração.
|
||||
|
||||
|
||||
### Runtime transacional mínimo (correção de amarração)
|
||||
|
||||
A lista `mcp_tools` do roteamento é uma **allowlist**, não uma ordem para executar todas as ferramentas. O runtime agora:
|
||||
|
||||
1. executa automaticamente somente ferramentas `read_only`;
|
||||
2. seleciona no máximo uma ação transacional compatível com o pedido do usuário;
|
||||
3. quando `require_confirmation: true`, persiste `pending_tool_call` e `transaction_status: AWAITING_CONFIRMATION`;
|
||||
4. no turno de confirmação, reutiliza a mesma chamada e executa com `confirmed: true`;
|
||||
5. publica no estado `available_mcp_tools`, `selected_tool_call`, `tool_policy_result`, `confirmation_required` e `confirmation_received`.
|
||||
|
||||
Para o cenário de exemplo, o pedido `123` (ou `PED-ENTREGUE`) retorna `ENTREGUE` no MCP Retail. Use:
|
||||
|
||||
```text
|
||||
Quero devolver o pedido 123 porque me arrependi da compra.
|
||||
Sim, confirmo a devolução.
|
||||
```
|
||||
|
||||
O contrato MCP foi padronizado para usar `reason` tanto no catálogo quanto no servidor FastMCP. `tool_policies.yaml` prevalece sobre os campos legados de `tools.yaml`; estes permanecem alinhados nos templates para compatibilidade.
|
||||
|
||||
### Integração e compatibilidade das tool policies
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/RELEASE_NOTES_TOOL_POLICIES.md`.
|
||||
|
||||
### Alterações
|
||||
|
||||
- Novo `ToolPolicyRegistry` opcional na biblioteca compartilhada.
|
||||
- Validação central no `MCPToolRouter`, inclusive para chamadas diretas.
|
||||
- Tipos mínimos `read_only` e `transactional`.
|
||||
- Confirmação estrita por `confirmed: true` ou `confirmation: true`.
|
||||
- Suporte opcional a campos obrigatórios por política.
|
||||
- Fallback automático para `tool_type`, `requires`, `confirmation_required` e `execution_policy` de `tools.yaml`.
|
||||
- `config/tool_policies.yaml` e variável `TOOL_POLICIES_PATH` nos templates principais, Day Zero e variantes de `Tuning-Performance/Normal` e `Tuning-Performance/Route_Stickness`.
|
||||
- Testes unitários de política e compatibilidade adicionados em `tests/unit/test_tool_policies.py`.
|
||||
|
||||
### Verificações executadas
|
||||
|
||||
- Compilação de `libs`, `templates`, `Tuning-Performance` e `tests`: aprovada.
|
||||
- Validação estrutural dos seis arquivos YAML: aprovada.
|
||||
- Casos isolados do loader (política transacional, confirmação, ausência de arquivo e ausência de cadastro): aprovados.
|
||||
- Renderização dos dois manuais Word atualizados: aprovada, sem cortes ou sobreposição nas páginas adicionadas.
|
||||
|
||||
### Limitação do ambiente de validação
|
||||
|
||||
A suíte `pytest` foi preparada, mas não pôde ser executada integralmente neste ambiente porque `pytest` e as dependências de runtime do projeto não estavam instalados e o acesso ao índice de pacotes expirou. Para reproduzir em um ambiente do projeto:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=libs/agent_framework/src:templates/agent_template_backend python -m pytest -q
|
||||
```
|
||||
|
||||
### Correção de integração backend/MCP
|
||||
- `mcp_tools` passou a ser tratado como allowlist.
|
||||
- Ações não são mais executadas automaticamente junto com consultas.
|
||||
- Confirmação transacional é persistida e retomada no turno seguinte.
|
||||
- Corrigida incompatibilidade `reason`/`motivo` no MCP Retail.
|
||||
- Adicionado pedido entregue determinístico para testes (`123`).
|
||||
- Removida keyword genérica `produto` da intenção Telecom para evitar colisão com devoluções Retail.
|
||||
- Templates Normal e Route_Stickness em `Tuning-Performance` foram sincronizados.
|
||||
|
||||
### Extração contextual de parâmetros MCP
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/RELEASE_NOTES_MCP_PARAMETER_EXTRACTION_FIX.md`.
|
||||
|
||||
### Problema corrigido
|
||||
|
||||
O bloco `extract` do `mcp_parameter_mapping.yaml` existia na configuração e na
|
||||
documentação, mas não era executado pelo runtime. Além disso, valores do
|
||||
Business Context podiam sobrescrever argumentos explícitos, fazendo
|
||||
`contract_key` substituir o `order_id` informado pelo usuário.
|
||||
|
||||
### Correções
|
||||
|
||||
- implementação da extração genérica `strategy: llm` após a escolha da tool;
|
||||
- suporte preservado para `strategy: month_name_pt`;
|
||||
- profile dedicado `mcp_parameter_extraction`;
|
||||
- telemetria `llm.mcp_parameter_extraction`;
|
||||
- `extract` deixou de ser interpretado como mapeamento simples;
|
||||
- argumentos explícitos/extraídos têm precedência sobre Business Context;
|
||||
- remoção de `contract_key: order_id` dos templates;
|
||||
- `order_id` configurado como `string`;
|
||||
- atualização das variantes em `Tuning-Performance`.
|
||||
|
||||
### Resultado esperado
|
||||
|
||||
Para a mensagem `consultar pedido 123`, a chamada MCP deve receber
|
||||
`order_id=123`, mesmo quando o Business Context contém outro `contract_key`.
|
||||
|
||||
### Uso local de MCP tools
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/README_MCP.md`.
|
||||
|
||||
Esta versão adiciona uma camada MCP ao framework:
|
||||
|
||||
- `agent_framework.mcp.MCPToolRouter`
|
||||
- `agent_template_backend/config/mcp_servers.yaml`
|
||||
- `agent_template_backend/config/tools.yaml`
|
||||
- `mcp_servers/telecom_mcp_server`
|
||||
- `mcp_servers/retail_mcp_server`
|
||||
|
||||
### Subir localmente
|
||||
|
||||
Terminal 1:
|
||||
|
||||
```bash
|
||||
bash ./scripts/run_mcp_servers.sh
|
||||
```
|
||||
|
||||
Terminal 2:
|
||||
|
||||
```bash
|
||||
cd agent_template_backend
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -e ../agent_framework
|
||||
pip install -r requirements.txt
|
||||
uvicorn app.main:app --reload --reload-dir app --reload-dir config --port 8000
|
||||
```
|
||||
|
||||
Terminal 3:
|
||||
|
||||
```bash
|
||||
cd agent_frontend
|
||||
python -m http.server 5173
|
||||
```
|
||||
|
||||
### Testes rápidos
|
||||
|
||||
Listar tools MCP carregadas pelo backend:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/debug/mcp/tools
|
||||
```
|
||||
|
||||
Chamar tool diretamente via backend:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/debug/mcp/call/consultar_fatura \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"msisdn":"11999999999","invoice_id":"INV-001"}'
|
||||
```
|
||||
|
||||
Roteamento Telecom + MCP:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/gateway/message \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"channel":"web","payload":{"session_id":"sess-tel-1","message":"Minha fatura veio alta","context":{"msisdn":"11999999999","invoice_id":"INV-001"}}}'
|
||||
```
|
||||
|
||||
Roteamento Retail + MCP:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/gateway/message \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"channel":"web","payload":{"session_id":"sess-ret-1","message":"Meu pedido não chegou","context":{"order_id":"PED-1001","customer_id":"C-001"}}}'
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
No compose, o backend usa `config/mcp_servers.docker.yaml` para apontar para `telecom-mcp` e `retail-mcp`.
|
||||
|
||||
### Operações read-only e transacionais
|
||||
|
||||
Use `config/tool_policies.yaml` no backend para classificar somente as operações que precisam de tratamento adicional. A validação é aplicada no roteador central antes do MCP Gateway/Server. O arquivo é opcional e templates antigos continuam usando as políticas já presentes em `tools.yaml`. A configuração completa e o roteiro de migração estão em [README_TOOL_POLICIES.md](README_TOOL_POLICIES.md).
|
||||
|
||||
### Arquivos de origem
|
||||
|
||||
Os arquivos abaixo foram consolidados neste manual:
|
||||
|
||||
- `Documentacao/Manual_Integracao_MCP_Servers_Agent_Framework.docx`
|
||||
- `Documentacao/README_TOOL_POLICIES.md`
|
||||
- `Documentacao/RELEASE_NOTES_TOOL_POLICIES.md`
|
||||
- `Documentacao/RELEASE_NOTES_MCP_PARAMETER_EXTRACTION_FIX.md`
|
||||
- `Documentacao/README_MCP.md`
|
||||
|
||||
### Regra de manutenção
|
||||
|
||||
Novas correções ou evoluções deste tema devem atualizar este documento consolidado. Release notes podem continuar existindo como histórico, mas não devem ser necessárias para compreender ou implementar a funcionalidade.
|
||||
2466
docs/developer/pt/05_agent_gateway_mcp_gateway_and_auth.md
Normal file
2466
docs/developer/pt/05_agent_gateway_mcp_gateway_and_auth.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,282 @@
|
||||
|
||||
### Guardrails, Judges e Avaliação Transacional
|
||||
|
||||
### Como usar este manual
|
||||
|
||||
Este é um **manual de referência especializado**. Ele não substitui o tutorial principal.
|
||||
|
||||
- Para criar um agente do início ao fim, use [`README.md`](../../../README.md).
|
||||
- Use este documento quando precisar implementar, aprofundar ou diagnosticar **guardrails nativos/externos, judges, sampling transacional e grounding**.
|
||||
- Os exemplos históricos consolidados aqui devem ser lidos à luz da API atual do framework.
|
||||
- Em caso de divergência, o código da versão e o `README.md` atual prevalecem.
|
||||
|
||||
### Relação com o tutorial principal
|
||||
|
||||
O `README.md` apresenta essa capacidade no fluxo normal de desenvolvimento. Este manual reúne detalhes que estavam distribuídos em `docs/`, `Documentacao/`, release notes, validações e guias especializados.
|
||||
|
||||
O objetivo aqui é responder **“como essa feature funciona em profundidade e como eu resolvo problemas nela?”**, sem transformar este arquivo em uma segunda cópia do tutorial principal.
|
||||
|
||||
### Escopo
|
||||
|
||||
Guardrails nativos/externos, judges, sampling transacional e grounding.
|
||||
|
||||
### Conteúdo técnico consolidado
|
||||
|
||||
### Guardrails, Judges e Avaliação Transacional
|
||||
|
||||
Manual para guardrails de entrada/saída, extensões específicas por agente, judges externos, execução obrigatória em transações e sinais/evidências usados na avaliação.
|
||||
|
||||
### Como usar este documento
|
||||
|
||||
Este é o documento consolidado de desenvolvimento para este assunto. Ele reúne arquitetura, configuração, exemplos, comportamento de runtime, compatibilidade, testes e troubleshooting que antes estavam distribuídos em vários arquivos. As seções de origem foram preservadas quando traziam detalhes técnicos distintos; notas de release foram incorporadas como comportamento atual ou histórico de correção.
|
||||
|
||||
### Guardrails implementados no framework
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/README_GUARDRAILS_IMPLEMENTADOS.md`.
|
||||
|
||||
Esta versão adiciona uma camada pragmática de guardrails ao `agent_framework`, inspirada na separação de rails por estágio: input, output, retrieval e execução/tool.
|
||||
|
||||
### Rails de input
|
||||
|
||||
- `MSIZE` — bloqueia mensagens excessivamente grandes.
|
||||
- `MSK` — mascara CPF, CNPJ, telefone, e-mail, cartão, CEP, RG, tokens e chaves.
|
||||
- `TOX` — detecta toxicidade e registra severidade sem bloquear por padrão.
|
||||
- `PINJ` — detecta prompt injection e registra score.
|
||||
- `JBRK` — detecta jailbreak/roleplay de burla e registra score.
|
||||
- `VLOOP` — bloqueia loop conversacional repetitivo.
|
||||
|
||||
### Rails de output
|
||||
|
||||
- `PII_OUT` — mascara PII na resposta do agente.
|
||||
- `CMP` — suaviza promessas absolutas e linguagem de garantia excessiva.
|
||||
- `REVPREC` — bloqueia verbalização de ação operacional sem confirmação de tool.
|
||||
- `GND` — sinaliza groundedness/risco quando há resposta específica sem evidência.
|
||||
- `ALUC_RISK` — marca risco de alucinação para telemetria e judges.
|
||||
|
||||
### Rails opcionais
|
||||
|
||||
- `RET_REL` — valida relevância de chunks de retrieval por score mínimo.
|
||||
- `TOOL_VAL` — valida ferramenta MCP/tool, argumentos obrigatórios, valores negativos e allowlist.
|
||||
|
||||
### Contrato para protocolos autorizados em guardrails de saída
|
||||
|
||||
Quando um workflow ou tool produz um **protocolo que deve ser exibido ao próprio cliente**, o código de integração do agente deve registrar esse valor no contexto de saída antes da execução dos guardrails:
|
||||
|
||||
```python
|
||||
ctx["expected_protocols"] = [protocol_number]
|
||||
```
|
||||
|
||||
Esse campo é um **contrato do framework**. Ele informa que aqueles valores específicos foram produzidos ou validados pelo fluxo atual e, portanto, podem ser usados pelos guardrails de saída como evidência de autorização.
|
||||
|
||||
Fluxo esperado:
|
||||
|
||||
```text
|
||||
workflow/tool gera protocolo
|
||||
↓
|
||||
agente registra em expected_protocols
|
||||
↓
|
||||
CMP valida que o protocolo exibido pertence aos valores esperados
|
||||
↓
|
||||
DLEX_OUT não bloqueia esse protocolo apenas por classificá-lo como identificador
|
||||
↓
|
||||
resposta pode informar o protocolo ao cliente
|
||||
```
|
||||
|
||||
Regras importantes:
|
||||
|
||||
- `expected_protocols` deve conter **somente protocolos realmente produzidos/esperados no turno ou transação atual**.
|
||||
- Não use `expected_protocols` para liberar tokens, credenciais, IDs internos arbitrários ou dados de terceiros.
|
||||
- A autorização vale somente para os valores listados; outro identificador continua sujeito às regras normais de `DLEX_OUT`.
|
||||
- O valor deve ser propagado **antes de `output_guardrails`**. Se o protocolo só for adicionado depois, a autorização não terá efeito.
|
||||
- Em respostas transacionais, mantenha a evidência do protocolo no resultado da tool/workflow para que `CMP`, `GND` e observabilidade consigam correlacionar o valor.
|
||||
|
||||
Exemplo:
|
||||
|
||||
```python
|
||||
result = await executar_workflow(...)
|
||||
protocol_number = result.get("protocol_number") or result.get("protocolo_id")
|
||||
if protocol_number:
|
||||
ctx["expected_protocols"] = [str(protocol_number)]
|
||||
```
|
||||
|
||||
#### Troubleshooting: workflow concluiu, mas a resposta foi substituída por mensagem de segurança
|
||||
|
||||
Sintoma típico:
|
||||
|
||||
```text
|
||||
workflow = COMPLETED
|
||||
CMP = allowed
|
||||
DLEX_OUT = blocked por "protocolo interno"
|
||||
resposta final = "Não consegui validar essa resposta com segurança..."
|
||||
```
|
||||
|
||||
Verifique, nesta ordem:
|
||||
|
||||
1. O protocolo gerado está presente no resultado/evidência da tool ou workflow?
|
||||
2. O agente propagou o mesmo valor em `ctx["expected_protocols"]`?
|
||||
3. `expected_protocols` foi preenchido antes de `output_guardrails`?
|
||||
4. O protocolo presente na resposta é exatamente um dos valores esperados?
|
||||
5. O `DLEX_OUT` está bloqueando por outro motivo real, como segredo, token ou dado de terceiro?
|
||||
|
||||
Se `expected_protocols` estiver ausente, o framework não deve presumir que qualquer identificador textual é seguro para divulgação.
|
||||
|
||||
|
||||
### Arquivos alterados
|
||||
|
||||
- `agent_framework/src/agent_framework/guardrails/rails.py`
|
||||
- `agent_framework/src/agent_framework/guardrails/pipeline.py`
|
||||
- `agent_framework/src/agent_framework/guardrails/__init__.py`
|
||||
|
||||
### Uso rápido
|
||||
|
||||
```python
|
||||
from agent_framework.guardrails.pipeline import GuardrailPipeline
|
||||
|
||||
pipeline = GuardrailPipeline()
|
||||
|
||||
sanitized_input, input_decisions = await pipeline.run_input(
|
||||
user_text,
|
||||
{"history_texts": history_texts},
|
||||
)
|
||||
|
||||
final_answer, output_decisions = await pipeline.run_output(
|
||||
answer,
|
||||
context,
|
||||
)
|
||||
```
|
||||
|
||||
Para tools/MCP:
|
||||
|
||||
```python
|
||||
_, decisions = await pipeline.run_tool(
|
||||
"cancelar_produto",
|
||||
{"produto": "VAS", "valor": 0},
|
||||
{
|
||||
"required_args": ["produto"],
|
||||
"allowed_tools": ["cancelar_produto", "consultar_fatura"],
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
### SPI de guardrails e judges externos
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Pontos indispensáveis:
|
||||
|
||||
- `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.
|
||||
|
||||
> **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.
|
||||
|
||||
Valide o exemplo publicado com `pytest -q tests/test_external_guardrails_judges_documentation.py`.
|
||||
|
||||
### Execução obrigatória de judges em transações
|
||||
|
||||
> Conteúdo consolidado a partir de `docs/JUDGES_TRANSACTIONAL_SAMPLING_FIX.md`.
|
||||
|
||||
### Problema
|
||||
|
||||
Mesmo com `always_run_for_transactional: true`, os judges podiam ser ignorados
|
||||
pela amostragem porque o nó `judge` enviava apenas `context`, `route`, `intent` e
|
||||
`mcp_results`. Os campos transacionais produzidos pelo runtime não chegavam ao
|
||||
`JudgePipeline`.
|
||||
|
||||
### Correção
|
||||
|
||||
O nó `judge` agora repassa:
|
||||
|
||||
- `transaction_status`
|
||||
- `confirmation_required`
|
||||
- `confirmation_received`
|
||||
- `tool_policy_result`
|
||||
- `selected_tool_call`
|
||||
- `pending_tool_call`
|
||||
- `mcp_results` como evidência
|
||||
|
||||
O `JudgePipeline` detecta transações por múltiplos sinais e avalia
|
||||
`always_run_for_transactional` antes de aplicar `sample_rate`.
|
||||
|
||||
Com a configuração abaixo, consultas comuns continuam sendo amostradas em 25%,
|
||||
mas turnos `AWAITING_CONFIRMATION`, `COMPLETED`, `FAILED` ou `CANCELLED` executam
|
||||
os judges sempre.
|
||||
|
||||
```yaml
|
||||
enabled: true
|
||||
sample_rate: 0.25
|
||||
always_run_for_transactional: true
|
||||
```
|
||||
|
||||
### Validação do Global Supervisor
|
||||
|
||||
> Conteúdo consolidado a partir de `docs/docs_GLOBAL_SUPERVISOR_VALIDATION.txt`.
|
||||
|
||||
VALIDAÇÃO - GLOBAL SUPERVISOR
|
||||
|
||||
Alterações implementadas:
|
||||
|
||||
1. Framework
|
||||
- agent_framework.global_supervisor.models
|
||||
- agent_framework.global_supervisor.config
|
||||
- agent_framework.global_supervisor.session_store
|
||||
- agent_framework.global_supervisor.router
|
||||
- agent_framework.global_supervisor.client
|
||||
|
||||
2. Novo serviço
|
||||
- agent_gateway/app/main.py
|
||||
- agent_gateway/app/settings.py
|
||||
- agent_gateway/config/backends.yaml
|
||||
- agent_gateway/README.md
|
||||
- agent_gateway/Dockerfile
|
||||
- agent_gateway/docs/ARQUITETURA_GLOBAL_SUPERVISOR.md
|
||||
|
||||
3. Docker Compose
|
||||
- serviço agent-gateway adicionado na porta 8010.
|
||||
|
||||
Validações executadas:
|
||||
|
||||
- python3 -m compileall -q agent_framework/src/agent_framework/global_supervisor agent_gateway/app
|
||||
Resultado: OK
|
||||
|
||||
- Smoke test do roteamento híbrido:
|
||||
Entrada 1: "Minha fatura veio alta" -> contas
|
||||
Entrada 2: "e esse valor?" na mesma session_id -> contas por active_backend
|
||||
Resultado: OK
|
||||
|
||||
- Smoke test de import do app FastAPI:
|
||||
from app.main import app, registry, router
|
||||
Resultado: OK
|
||||
|
||||
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.
|
||||
|
||||
### Validação de eventos de guardrail
|
||||
|
||||
> Conteúdo consolidado a partir de `docs/docs_VALIDATION_GUARDRAILS_IC.txt`.
|
||||
|
||||
VALIDATION REPORT - guardrails parallel fail-fast + observer IC
|
||||
Date: 2026-06-03
|
||||
|
||||
compileall: OK
|
||||
smoke-tests: OK
|
||||
|
||||
### Arquivos de origem
|
||||
|
||||
Os arquivos abaixo foram consolidados neste manual:
|
||||
|
||||
- `Documentacao/README_GUARDRAILS_IMPLEMENTADOS.md`
|
||||
- `docs/EXTERNAL_GUARDRAILS_JUDGES.md`
|
||||
- `docs/JUDGES_TRANSACTIONAL_SAMPLING_FIX.md`
|
||||
- `docs/docs_GLOBAL_SUPERVISOR_VALIDATION.txt`
|
||||
- `docs/docs_VALIDATION_GUARDRAILS_IC.txt`
|
||||
|
||||
### Regra de manutenção
|
||||
|
||||
Novas correções ou evoluções deste tema devem atualizar este documento consolidado. Release notes podem continuar existindo como histórico, mas não devem ser necessárias para compreender ou implementar a funcionalidade.
|
||||
396
docs/developer/pt/07_rag_business_context_and_grounding.md
Normal file
396
docs/developer/pt/07_rag_business_context_and_grounding.md
Normal file
@@ -0,0 +1,396 @@
|
||||
|
||||
### RAG, BusinessContext e Grounding
|
||||
|
||||
### Como usar este manual
|
||||
|
||||
Este é um **manual de referência especializado**. Ele não substitui o tutorial principal.
|
||||
|
||||
- Para criar um agente do início ao fim, use [`README.md`](../../../README.md).
|
||||
- Use este documento quando precisar implementar, aprofundar ou diagnosticar **RAG, providers, BusinessContext, contexto recuperado e grounding**.
|
||||
- Os exemplos históricos consolidados aqui devem ser lidos à luz da API atual do framework.
|
||||
- Em caso de divergência, o código da versão e o `README.md` atual prevalecem.
|
||||
|
||||
### Relação com o tutorial principal
|
||||
|
||||
O `README.md` apresenta essa capacidade no fluxo normal de desenvolvimento. Este manual reúne detalhes que estavam distribuídos em `docs/`, `Documentacao/`, release notes, validações e guias especializados.
|
||||
|
||||
O objetivo aqui é responder **“como essa feature funciona em profundidade e como eu resolvo problemas nela?”**, sem transformar este arquivo em uma segunda cópia do tutorial principal.
|
||||
|
||||
### Escopo
|
||||
|
||||
Rag, providers, businesscontext, contexto recuperado e grounding.
|
||||
|
||||
### Conteúdo técnico consolidado
|
||||
|
||||
### RAG, Providers Enterprise, BusinessContext e Grounding
|
||||
|
||||
Guia de integração de conhecimento recuperado, seleção entre providers de RAG, configuração KBDB, amostras, suficiência MCP e uso do BusinessContext como contrato de dados.
|
||||
|
||||
### Como usar este documento
|
||||
|
||||
Este é o documento consolidado de desenvolvimento para este assunto. Ele reúne arquitetura, configuração, exemplos, comportamento de runtime, compatibilidade, testes e troubleshooting que antes estavam distribuídos em vários arquivos. As seções de origem foram preservadas quando traziam detalhes técnicos distintos; notas de release foram incorporadas como comportamento atual ou histórico de correção.
|
||||
|
||||
### Provider RAG Standard versus KBDB Enterprise
|
||||
|
||||
> Conteúdo consolidado a partir de `docs/RAG_PROVIDER_KBDB.md`.
|
||||
|
||||
O framework passa a suportar dois backends de retrieval pelo mesmo contrato `RagService`, sem alterar os agentes nem `_retrieve_rag_context()`.
|
||||
|
||||
### Seleção
|
||||
|
||||
```env
|
||||
RAG_PROVIDER=standard # default: comportamento anterior
|
||||
# ou
|
||||
RAG_PROVIDER=kbdb # KBDB enterprise
|
||||
```
|
||||
|
||||
A seleção é exclusiva por processo. Os dois RAGs não executam juntos e não compartilham vector store, graph store ou ingestão.
|
||||
|
||||
### `standard`
|
||||
|
||||
Mantém integralmente o RAG já existente no `agent_framework_oci`: `VECTOR_STORE_PROVIDER`, `GRAPH_STORE_PROVIDER`, embedding, query rewrite, compression, retrieval guardrails e geração continuam válidos.
|
||||
|
||||
### `kbdb`
|
||||
|
||||
O framework integra somente a porta estável de serving do projeto KBDB:
|
||||
|
||||
`PKG_KB_SERVING.SEARCH_KNOWLEDGE_BASE`
|
||||
|
||||
O pipeline enterprise continua externo ao runtime do agente e preserva sua própria arquitetura RAW → SILVER → GOLD, HVI/hybrid search, property graph, publicação, lifecycle, auditoria e observabilidade.
|
||||
|
||||
O envelope KBDB é adaptado para `RagResult`/`VectorDocument`; portanto os agentes existentes continuam chamando `_retrieve_rag_context()` e os retrieval guardrails do framework continuam depois do retrieval.
|
||||
|
||||
### Configuração
|
||||
|
||||
```env
|
||||
RAG_PROVIDER=kbdb
|
||||
RAG_TOP_K=5
|
||||
KBDB_DB_USER=KB_USER
|
||||
KBDB_DB_PASSWORD=...
|
||||
KBDB_DB_DSN=...
|
||||
KBDB_DB_WALLET_LOCATION=...
|
||||
KBDB_DB_WALLET_PASSWORD=...
|
||||
KBDB_SEARCH_TYPE=hybrid
|
||||
KBDB_NODE_EXPANSION=true
|
||||
KBDB_NODE_MAX_RELATED=8
|
||||
KBDB_GRAPH_CROSS_REF=false
|
||||
KBDB_MAX_CROSS_REF_HOPS=1
|
||||
KBDB_DOCUMENT_TYPE=customer_safe
|
||||
KBDB_METADATA_JSON=
|
||||
KBDB_MIN_SCORE=
|
||||
```
|
||||
|
||||
Quando `RAG_PROVIDER=kbdb`, `KBDB_DB_USER`, `KBDB_DB_PASSWORD` e `KBDB_DB_DSN` são obrigatórios. O KBDB usa conexão isolada porque pode residir em outro Autonomous. `KBDB_DB_DSN` segue a mesma semântica de `ADB_DSN`: use o alias TNS existente no `tnsnames.ora` da wallet indicada por `KBDB_DB_WALLET_LOCATION`, e não uma URL `tcps://...`.
|
||||
|
||||
### Isolamento e compatibilidade
|
||||
|
||||
- `RAG_PROVIDER=standard` não importa nem conecta ao KBDB.
|
||||
- `RAG_PROVIDER=kbdb` não instancia vector/graph stores do RAG padrão.
|
||||
- Ingestão por `RagService.add_documents()` não é permitida no modo KBDB: deve passar pelo pipeline/publicação KBDB.
|
||||
- Query rewrite e context compression continuam opcionais e são aplicados pela camada comum do framework.
|
||||
- `AgentRuntimeMixin._retrieve_rag_context()` e os agentes permanecem inalterados.
|
||||
- Falhas do KBDB seguem a semântica existente do framework: retrieval é evidência auxiliar e a exceção é convertida em metadata técnica sem derrubar a jornada.
|
||||
|
||||
|
||||
### Resposta direta de tool e RAG
|
||||
|
||||
O framework não considera mais que um resultado MCP estruturado é, por si só, uma resposta suficiente ao usuário.
|
||||
|
||||
Uma política `response.renderer` define somente **como** apresentar o resultado. Ela não encerra o fluxo antes de RAG/LLM. Para uma tool deliberadamente produzir uma resposta final direta, a aplicação deve declarar explicitamente:
|
||||
|
||||
```yaml
|
||||
response:
|
||||
mode: renderer
|
||||
renderer: meu.renderer
|
||||
direct: true
|
||||
```
|
||||
|
||||
Sem `direct: true`, o resultado da tool permanece como evidência MCP e o fluxo segue para `_retrieve_rag_context()` e composição LLM. Isso permite, por exemplo, que uma consulta operacional de plano seja combinada com conhecimento documental do KBDB quando a pergunta pedir regras, políticas ou explicações.
|
||||
|
||||
O core do framework não possui fallback por nome de tool (`consultar_plano`, `consultar_pedido`, etc.). Regras de apresentação pertencem à aplicação/domínio.
|
||||
|
||||
|
||||
### Suficiência MCP e grounding
|
||||
|
||||
Um resultado MCP bem-sucedido **não** faz o framework pular RAG automaticamente.
|
||||
O domínio só pode declarar suficiência documental explicitamente no payload com
|
||||
`rag_sufficient=true` ou `knowledge_sufficient=true`. Essa decisão é genérica e
|
||||
não depende do nome da tool nem de palavras-chave de telecom/retail.
|
||||
|
||||
No provider `kbdb`, `KBDB_GROUNDED_ONLY=true` é o padrão. Quando a busca KBDB
|
||||
retorna vazia, bloqueada ou com erro, a composição LLM pode usar fatos comprovados
|
||||
por MCP/business context, mas não pode completar a parte documental com conhecimento
|
||||
paramétrico do modelo. Deve informar que não há evidência suficiente na base.
|
||||
|
||||
Eventos do ProductAgent registram `IC.PRODUCT_RAG_CONTEXT_EVALUATED` em toda
|
||||
tentativa/decisão e `IC.PRODUCT_RAG_CONTEXT_RETRIEVED` somente quando há contexto
|
||||
recuperado. Os metadados incluem `provider`, `status`, `document_count`, `reason`,
|
||||
`error`, `query`, `namespace` e `latency_ms`.
|
||||
|
||||
### Amostras e testes de RAG
|
||||
|
||||
> Conteúdo consolidado a partir de `docs/README_rag_samples.md`.
|
||||
|
||||
These PDF files are synthetic, searchable sample documents created to validate the RAG embedding and retrieval flow of `agent_template_backend`.
|
||||
|
||||
### Files
|
||||
|
||||
- `01_billing_agent_invoice_policy.pdf` - sample knowledge for `billing_agent`
|
||||
- `02_orders_agent_lifecycle_policy.pdf` - sample knowledge for `orders_agent`
|
||||
- `03_product_agent_catalog_policy.pdf` - sample knowledge for `product_agent`
|
||||
- `04_support_agent_sla_policy.pdf` - sample knowledge for `support_agent`
|
||||
- `05_business_context_rag_flow.pdf` - sample knowledge about BusinessContext, identity.yaml and MCP parameter mapping
|
||||
|
||||
### How to use
|
||||
|
||||
Copy the PDF files to the backend documentation directory:
|
||||
|
||||
```bash
|
||||
mkdir -p agent_template_backend/docs/rag_samples
|
||||
cp *.pdf agent_template_backend/docs/rag_samples/
|
||||
```
|
||||
|
||||
For a local smoke test, use:
|
||||
|
||||
```env
|
||||
VECTOR_STORE_PROVIDER=sqlite
|
||||
EMBEDDING_PROVIDER=mock
|
||||
SQLITE_DB_PATH=./data/agent_framework.db
|
||||
RAG_TOP_K=4
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
```bash
|
||||
python scripts/generate_rag_embeddings.py \
|
||||
--docs-dir ./agent_template_backend/docs/rag_samples \
|
||||
--namespace default
|
||||
```
|
||||
|
||||
For production-like semantic embeddings with OCI Generative AI, use:
|
||||
|
||||
```env
|
||||
VECTOR_STORE_PROVIDER=autonomous
|
||||
EMBEDDING_PROVIDER=oci
|
||||
OCI_COMPARTMENT_ID=ocid1.compartment.oc1..xxxx
|
||||
OCI_REGION=us-chicago-1
|
||||
OCI_EMBEDDING_MODEL=cohere.embed-multilingual-v3.0
|
||||
```
|
||||
|
||||
### Suggested retrieval test questions
|
||||
|
||||
- What is a prorated charge?
|
||||
- When can the OrdersAgent open an exchange request?
|
||||
- Which SKU represents the AI Agents book?
|
||||
- What is the target response for a critical support ticket?
|
||||
- How does BusinessContext map customer_key to MCP tool parameters?
|
||||
|
||||
### BusinessContext v2
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/README_TEMPLATE_BUSINESS_CONTEXT_V2.md`.
|
||||
|
||||
Este pacote atualiza o `agent_template_backend` e o `agent_frontend` para refletir o framework novo, onde as chaves vindas do canal/front-end são resolvidas uma vez como chaves canônicas e propagadas pelas camadas até o MCP Server.
|
||||
|
||||
### Fluxo implementado
|
||||
|
||||
1. O front-end envia `tenant_id`, `agent_id`, `session_id` e `business_context`.
|
||||
2. O backend normaliza a mensagem via `ChannelGateway` preservando todo o payload no `context`.
|
||||
3. O backend usa `IdentityResolver` com `config/identity.yaml` para gerar `BusinessContext`:
|
||||
- `customer_key`
|
||||
- `contract_key`
|
||||
- `interaction_key`
|
||||
- `account_key`
|
||||
- `resource_key`
|
||||
- `session_key`
|
||||
4. O workflow recebe `context.business_context`.
|
||||
5. Os agentes de exemplo não montam mais argumentos específicos como `msisdn`, `invoice_id` ou `order_id` diretamente.
|
||||
6. O `MCPToolRouter` usa `config/mcp_parameter_mapping.yaml` para converter chaves canônicas em parâmetros reais de cada tool MCP.
|
||||
|
||||
### Arquivos principais ajustados
|
||||
|
||||
- `agent_template_backend/app/main.py`
|
||||
- carrega `IdentityResolver`;
|
||||
- resolve `BusinessContext` por mensagem;
|
||||
- persiste as chaves na sessão/memória/metadata/SSE;
|
||||
- adiciona `/debug/identity`.
|
||||
|
||||
- `agent_template_backend/app/agents/runtime.py`
|
||||
- adiciona `_collect_mcp_context()` centralizado;
|
||||
- repassa `business_context` e `original_context` para o MCP Router.
|
||||
|
||||
- `agent_template_backend/app/agents/*_agent.py`
|
||||
- agentes passam a usar `_collect_mcp_context()` em vez de montar argumentos específicos.
|
||||
|
||||
- `agent_template_backend/config/identity.yaml`
|
||||
- define como campos do canal/front-end alimentam as chaves canônicas.
|
||||
|
||||
- `agent_template_backend/config/mcp_parameter_mapping.yaml`
|
||||
- define como chaves canônicas viram parâmetros reais por tool MCP.
|
||||
|
||||
- `agent_frontend/index.html` e `agent_frontend/app.js`
|
||||
- adicionam campos de `tenant`, `agent` e chaves canônicas;
|
||||
- enviam `business_context` no payload;
|
||||
- mantêm aliases de domínio para compatibilidade (`msisdn`, `invoice_id`, `order_id`, etc.).
|
||||
|
||||
### Teste rápido
|
||||
|
||||
Suba backend, frontend e MCP servers. Depois teste:
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:8000/health | jq
|
||||
|
||||
curl -s -X POST http://localhost:8000/debug/identity \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"channel":"web",
|
||||
"tenant_id":"default",
|
||||
"agent_id":"telecom_contas",
|
||||
"payload":{
|
||||
"message":"Minha fatura veio alta",
|
||||
"session_id":"teste-001",
|
||||
"msisdn":"11999999999",
|
||||
"invoice_id":"3000131180",
|
||||
"ura_call_id":"URA-123",
|
||||
"business_context":{
|
||||
"customer_key":"11999999999",
|
||||
"contract_key":"3000131180",
|
||||
"interaction_key":"URA-123",
|
||||
"session_key":"teste-001"
|
||||
}
|
||||
}
|
||||
}' | jq
|
||||
|
||||
curl -s -X POST http://localhost:8000/debug/mcp/call/consultar_fatura \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"business_context": {
|
||||
"customer_key":"11999999999",
|
||||
"contract_key":"3000131180",
|
||||
"interaction_key":"URA-123",
|
||||
"session_key":"teste-001"
|
||||
}
|
||||
}' | jq
|
||||
```
|
||||
|
||||
No log do backend, procure por `mcp.tool.mapped`. Ele deve indicar as chaves mapeadas e `has_msisdn=true`, `has_invoice_id=true` para o domínio telecom.
|
||||
|
||||
### Integração operacional de RAG e cache
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/README_FIRST_MAX_OPERATIONAL_FIXES.md`.
|
||||
|
||||
Esta versão corrige os gaps identificados na comparação contra o FIRST.
|
||||
|
||||
### Correções aplicadas
|
||||
|
||||
### 1. Checkpoint LangGraph operacional
|
||||
|
||||
O workflow não compila mais com `MemorySaver()` diretamente. Foi criado o adaptador:
|
||||
|
||||
```text
|
||||
agent_framework/checkpoints/langgraph_saver.py
|
||||
```
|
||||
|
||||
Ele conecta o LangGraph ao repository configurado do framework:
|
||||
|
||||
- `memory`
|
||||
- `sqlite`
|
||||
- `oracle` / `autonomous`
|
||||
|
||||
No workflow:
|
||||
|
||||
```python
|
||||
builder.compile(checkpointer=create_langgraph_checkpointer(self.settings))
|
||||
```
|
||||
|
||||
### 2. Telemetria LangGraph envolvendo a execução real
|
||||
|
||||
Foi adicionado wrapper de nó no workflow:
|
||||
|
||||
```python
|
||||
self._node("billing_agent", self.billing_agent)
|
||||
```
|
||||
|
||||
Assim o span/evento `langgraph.node.*` envolve a execução real do nó, não apenas um bloco vazio.
|
||||
|
||||
Eventos emitidos:
|
||||
|
||||
- `langgraph.node.started`
|
||||
- `langgraph.node.completed`
|
||||
- `langgraph.node.failed`
|
||||
- `langgraph.edge.selected`
|
||||
|
||||
### 3. RAG integrado aos agentes
|
||||
|
||||
Os agentes agora recebem `RagService` e usam o contexto recuperado no prompt:
|
||||
|
||||
- BillingAgent
|
||||
- ProductAgent
|
||||
- OrdersAgent
|
||||
- SupportAgent
|
||||
|
||||
O RAG usa:
|
||||
|
||||
- `VECTOR_STORE_PROVIDER=memory|sqlite|oracle|autonomous`
|
||||
- `GRAPH_STORE_PROVIDER=memory|oracle|autonomous`
|
||||
- `RAG_TOP_K`
|
||||
|
||||
### 4. Cache integrado ao runtime dos agentes
|
||||
|
||||
Criado mixin:
|
||||
|
||||
```text
|
||||
agent_template_backend/app/agents/runtime.py
|
||||
```
|
||||
|
||||
Ele adiciona:
|
||||
|
||||
- busca RAG padronizada;
|
||||
- chave de cache para chamada LLM;
|
||||
- hit/miss com telemetria;
|
||||
- cache distribuído via `create_cache(settings)`.
|
||||
|
||||
### 5. Testes unitários
|
||||
|
||||
Criada pasta:
|
||||
|
||||
```text
|
||||
tests/unit
|
||||
```
|
||||
|
||||
Cobertura inicial:
|
||||
|
||||
- cache;
|
||||
- SSE;
|
||||
- RAG;
|
||||
- checkpoint saver;
|
||||
- telemetria LangGraph;
|
||||
- runtime dos agentes;
|
||||
- verificação estática do workflow;
|
||||
- imports principais.
|
||||
|
||||
Validação local executada:
|
||||
|
||||
```text
|
||||
12 passed
|
||||
```
|
||||
|
||||
### Como testar
|
||||
|
||||
```bash
|
||||
cd projeto_agent_framework_first_ready
|
||||
pip install -r agent_template_backend/requirements.txt
|
||||
pytest -q tests/unit
|
||||
```
|
||||
|
||||
### Arquivos de origem
|
||||
|
||||
Os arquivos abaixo foram consolidados neste manual:
|
||||
|
||||
- `docs/RAG_PROVIDER_KBDB.md`
|
||||
- `docs/README_rag_samples.md`
|
||||
- `Documentacao/README_TEMPLATE_BUSINESS_CONTEXT_V2.md`
|
||||
- `Documentacao/README_FIRST_MAX_OPERATIONAL_FIXES.md`
|
||||
|
||||
### Regra de manutenção
|
||||
|
||||
Novas correções ou evoluções deste tema devem atualizar este documento consolidado. Release notes podem continuar existindo como histórico, mas não devem ser necessárias para compreender ou implementar a funcionalidade.
|
||||
636
docs/developer/pt/08_long_term_memory_and_checkpoint.md
Normal file
636
docs/developer/pt/08_long_term_memory_and_checkpoint.md
Normal file
@@ -0,0 +1,636 @@
|
||||
|
||||
### Long-Term Memory e Checkpoint
|
||||
|
||||
### Como usar este manual
|
||||
|
||||
Este é um **manual de referência especializado**. Ele não substitui o tutorial principal.
|
||||
|
||||
- Para criar um agente do início ao fim, use [`README.md`](../../../README.md).
|
||||
- Use este documento quando precisar implementar, aprofundar ou diagnosticar **LTM, memória de conversa, isolamento por identidade e persistência de estado**.
|
||||
- Os exemplos históricos consolidados aqui devem ser lidos à luz da API atual do framework.
|
||||
- Em caso de divergência, o código da versão e o `README.md` atual prevalecem.
|
||||
|
||||
### Relação com o tutorial principal
|
||||
|
||||
O `README.md` apresenta essa capacidade no fluxo normal de desenvolvimento. Este manual reúne detalhes que estavam distribuídos em `docs/`, `Documentacao/`, release notes, validações e guias especializados.
|
||||
|
||||
O objetivo aqui é responder **“como essa feature funciona em profundidade e como eu resolvo problemas nela?”**, sem transformar este arquivo em uma segunda cópia do tutorial principal.
|
||||
|
||||
### Escopo
|
||||
|
||||
Ltm, memória de conversa, isolamento por identidade e persistência de estado.
|
||||
|
||||
### Conteúdo técnico consolidado
|
||||
|
||||
### Long-Term Memory e Checkpoint Enterprise
|
||||
|
||||
Manual de implementação de memória durável, isolamento por identidade, stores, extração, integração LangGraph, testes de persistência e diferenças entre LTM, histórico, sumário e checkpoint.
|
||||
|
||||
### Como usar este documento
|
||||
|
||||
Este é o documento consolidado de desenvolvimento para este assunto. Ele reúne arquitetura, configuração, exemplos, comportamento de runtime, compatibilidade, testes e troubleshooting que antes estavam distribuídos em vários arquivos. As seções de origem foram preservadas quando traziam detalhes técnicos distintos; notas de release foram incorporadas como comportamento atual ou histórico de correção.
|
||||
|
||||
### Implementação completa de Long-Term Memory
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/Manual_Long_Term_Memory_PT.md`.
|
||||
|
||||
### Conceito
|
||||
|
||||
A Long-Term Memory (LTM) é a capacidade do `agent_framework` de armazenar e recuperar fatos duradouros além da duração de uma sessão de conversa.
|
||||
|
||||
Diferentemente do histórico de mensagens, que normalmente está associado a um `session_id`, a memória de longo prazo é associada à identidade de negócio do usuário ou cliente. Na implementação atual, essa identidade é composta por:
|
||||
|
||||
```text
|
||||
tenant_id
|
||||
agent_id
|
||||
customer_key
|
||||
```
|
||||
|
||||
Isso permite que um agente recupere preferências, informações de identidade, projetos e restrições mesmo quando uma nova sessão é criada.
|
||||
|
||||
### Para que serve
|
||||
|
||||
A Long-Term Memory serve para:
|
||||
|
||||
- manter continuidade entre sessões;
|
||||
- personalizar respostas;
|
||||
- evitar que o usuário repita informações já fornecidas;
|
||||
- reduzir a necessidade de enviar todo o histórico ao modelo;
|
||||
- armazenar preferências, projetos atuais, nomes preferidos e restrições;
|
||||
- isolar a memória entre tenants, agentes e clientes.
|
||||
|
||||
Exemplo:
|
||||
|
||||
```text
|
||||
Sessão A:
|
||||
"Me chame de Cris. Minha linguagem preferida é Python."
|
||||
|
||||
Sessão B, com outro session_id e o mesmo customer_key:
|
||||
"O que você lembra sobre mim?"
|
||||
|
||||
Resposta esperada:
|
||||
"Seu nome preferido é Cris e sua linguagem preferida é Python."
|
||||
```
|
||||
|
||||
### Diferença entre os tipos de memória
|
||||
|
||||
### Conversation Memory
|
||||
|
||||
Mantém as mensagens da conversa atual e normalmente está associada ao `session_id`.
|
||||
|
||||
### Summary Memory
|
||||
|
||||
Mantém um resumo da conversa para reduzir o tamanho do contexto enviado ao modelo.
|
||||
|
||||
### Long-Term Memory
|
||||
|
||||
Mantém fatos duradouros entre sessões e é associada à identidade de negócio, principalmente ao `customer_key`.
|
||||
|
||||
### Componentes da funcionalidade
|
||||
|
||||
### LongTermMemoryManager
|
||||
|
||||
Responsável por coordenar:
|
||||
|
||||
- carregamento das memórias;
|
||||
- recuperação por identidade;
|
||||
- renderização do contexto;
|
||||
- extração de novos fatos;
|
||||
- persistência dos fatos;
|
||||
- deduplicação e atualização.
|
||||
|
||||
### LongTermMemoryStore
|
||||
|
||||
Interface de persistência utilizada pelo manager.
|
||||
|
||||
### SQLiteLongTermMemoryStore
|
||||
|
||||
Implementação de referência baseada em SQLite.
|
||||
|
||||
É apropriada para:
|
||||
|
||||
- desenvolvimento local;
|
||||
- testes;
|
||||
- demonstrações;
|
||||
- ambientes de baixa escala.
|
||||
|
||||
### InMemoryLongTermMemoryStore
|
||||
|
||||
Implementação em memória utilizada para testes rápidos.
|
||||
|
||||
O conteúdo é perdido quando o processo do backend é encerrado.
|
||||
|
||||
### LongTermMemoryExtractor
|
||||
|
||||
Responsável por identificar fatos duradouros nas mensagens.
|
||||
|
||||
Exemplos de fatos:
|
||||
|
||||
```text
|
||||
preferred_name = Cris
|
||||
preferred_language = Python
|
||||
current_project = Atlas
|
||||
```
|
||||
|
||||
### LongTermMemoryItem
|
||||
|
||||
Modelo que representa um item persistido, incluindo identidade, chave, valor, categoria, confiança e metadados.
|
||||
|
||||
### AgentRuntime
|
||||
|
||||
Carrega a memória antes da execução do agente e injeta o contexto no prompt.
|
||||
|
||||
### Nó persist_long_term_memory
|
||||
|
||||
Nó do LangGraph responsável por persistir os fatos após a geração e validação da resposta final.
|
||||
|
||||
### Estrutura dos arquivos
|
||||
|
||||
```text
|
||||
libs/
|
||||
└── agent_framework/
|
||||
└── src/
|
||||
└── agent_framework/
|
||||
└── memory/
|
||||
├── __init__.py
|
||||
├── long_term_extractor.py
|
||||
├── long_term_memory.py
|
||||
├── long_term_models.py
|
||||
└── long_term_store.py
|
||||
```
|
||||
|
||||
### Fluxo de execução
|
||||
|
||||
```text
|
||||
Mensagem do usuário
|
||||
│
|
||||
▼
|
||||
AgentRuntime.prepare_memory_context()
|
||||
│
|
||||
├── Conversation Memory
|
||||
├── Summary Memory
|
||||
└── Long-Term Memory
|
||||
│
|
||||
▼
|
||||
long_term_memory_context
|
||||
│
|
||||
▼
|
||||
Prompt do agente
|
||||
│
|
||||
▼
|
||||
Agente
|
||||
│
|
||||
▼
|
||||
Guardrails / Judges / Supervisor
|
||||
│
|
||||
▼
|
||||
persist_long_term_memory
|
||||
│
|
||||
▼
|
||||
LongTermMemoryExtractor
|
||||
│
|
||||
▼
|
||||
LongTermMemoryStore
|
||||
```
|
||||
|
||||
### Configuração do framework
|
||||
|
||||
### Novos módulos
|
||||
|
||||
Copie os arquivos:
|
||||
|
||||
```text
|
||||
libs/agent_framework/src/agent_framework/memory/long_term_extractor.py
|
||||
libs/agent_framework/src/agent_framework/memory/long_term_memory.py
|
||||
libs/agent_framework/src/agent_framework/memory/long_term_models.py
|
||||
libs/agent_framework/src/agent_framework/memory/long_term_store.py
|
||||
```
|
||||
|
||||
### Atualização de memory/__init__.py
|
||||
|
||||
Exporte os componentes da Long-Term Memory:
|
||||
|
||||
```python
|
||||
from agent_framework.memory.long_term_memory import (
|
||||
LongTermMemoryManager,
|
||||
create_long_term_memory_manager,
|
||||
)
|
||||
from agent_framework.memory.long_term_models import LongTermMemoryItem
|
||||
from agent_framework.memory.long_term_store import (
|
||||
InMemoryLongTermMemoryStore,
|
||||
LongTermMemoryStore,
|
||||
SQLiteLongTermMemoryStore,
|
||||
create_long_term_memory_store,
|
||||
)
|
||||
```
|
||||
|
||||
### Atualização de settings.py
|
||||
|
||||
Adicione as configurações:
|
||||
|
||||
```python
|
||||
ENABLE_LONG_TERM_MEMORY: bool = False
|
||||
LONG_TERM_MEMORY_PROVIDER: str = "sqlite"
|
||||
LONG_TERM_MEMORY_SQLITE_PATH: str = "./data/agent_framework.db"
|
||||
LONG_TERM_MEMORY_TABLE: str = "agentfw_long_term_memory"
|
||||
LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS: int = 20
|
||||
LONG_TERM_MEMORY_MIN_CONFIDENCE: float = 0.70
|
||||
LONG_TERM_MEMORY_AUTO_EXTRACT: bool = True
|
||||
LONG_TERM_MEMORY_INJECT_CONTEXT: bool = True
|
||||
```
|
||||
|
||||
### Integração com AgentRuntime
|
||||
|
||||
O runtime deve:
|
||||
|
||||
1. verificar se a funcionalidade está habilitada;
|
||||
2. criar o manager quando necessário;
|
||||
3. recuperar os fatos pela identidade;
|
||||
4. preencher o estado;
|
||||
5. injetar o contexto no prompt.
|
||||
|
||||
Campos adicionados ao estado:
|
||||
|
||||
```python
|
||||
long_term_memories: list[dict]
|
||||
long_term_memory_context: str
|
||||
long_term_memory_write_result: dict
|
||||
```
|
||||
|
||||
### Inicialização no AgentWorkflow
|
||||
|
||||
O manager deve ser criado no `AgentWorkflow`:
|
||||
|
||||
```python
|
||||
self.long_term_memory_manager = create_long_term_memory_manager(
|
||||
settings,
|
||||
telemetry=telemetry,
|
||||
)
|
||||
```
|
||||
|
||||
### Inicialização correta dos agentes
|
||||
|
||||
O `long_term_memory_manager` não deve ser passado pelo `agent_kwargs` caso os construtores de `BillingAgent`, `ProductAgent`, `OrdersAgent` e `SupportAgent` não declarem esse parâmetro.
|
||||
|
||||
Esta inicialização causa erro:
|
||||
|
||||
```python
|
||||
agent_kwargs = {
|
||||
"telemetry": telemetry,
|
||||
"settings": settings,
|
||||
"memory": memory,
|
||||
"summary_memory": summary_memory,
|
||||
"long_term_memory_manager": self.long_term_memory_manager,
|
||||
}
|
||||
|
||||
self.billing = BillingAgent(llm, **agent_kwargs)
|
||||
```
|
||||
|
||||
Erro resultante:
|
||||
|
||||
```text
|
||||
TypeError: BillingAgent.__init__() got an unexpected keyword argument
|
||||
'long_term_memory_manager'
|
||||
```
|
||||
|
||||
A forma recomendada é criar os agentes com a assinatura já existente e injetar o manager como atributo após a inicialização:
|
||||
|
||||
```python
|
||||
agent_kwargs = {
|
||||
"telemetry": telemetry,
|
||||
"tool_router": getattr(self, "tool_router", None),
|
||||
"rag_service": self.rag_service,
|
||||
"cache": self.cache,
|
||||
"settings": settings,
|
||||
"observer": self.observer,
|
||||
"memory": memory,
|
||||
"summary_memory": summary_memory,
|
||||
}
|
||||
|
||||
self.billing = BillingAgent(llm, **agent_kwargs)
|
||||
self.product = ProductAgent(llm, **agent_kwargs)
|
||||
self.orders = OrdersAgent(llm, **agent_kwargs)
|
||||
self.support = SupportAgent(llm, **agent_kwargs)
|
||||
|
||||
for agent in (
|
||||
self.billing,
|
||||
self.product,
|
||||
self.orders,
|
||||
self.support,
|
||||
):
|
||||
agent.long_term_memory_manager = self.long_term_memory_manager
|
||||
```
|
||||
|
||||
Essa abordagem evita alterar os construtores de todos os agentes e mantém a funcionalidade encapsulada no framework.
|
||||
|
||||
### Configuração do LangGraph
|
||||
|
||||
Registre o nó:
|
||||
|
||||
```python
|
||||
builder.add_node(
|
||||
"persist_long_term_memory",
|
||||
self._node(
|
||||
"persist_long_term_memory",
|
||||
self.persist_long_term_memory,
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
Altere o fluxo:
|
||||
|
||||
```python
|
||||
builder.add_edge(
|
||||
"supervisor_review",
|
||||
"persist_long_term_memory",
|
||||
)
|
||||
builder.add_edge(
|
||||
"persist_long_term_memory",
|
||||
"persist",
|
||||
)
|
||||
```
|
||||
|
||||
Implemente o método:
|
||||
|
||||
```python
|
||||
async def persist_long_term_memory(
|
||||
self,
|
||||
state: AgentState,
|
||||
) -> dict[str, object]:
|
||||
result = await self.long_term_memory_manager.persist_turn(state)
|
||||
|
||||
return {
|
||||
"long_term_memory_write_result": result,
|
||||
}
|
||||
```
|
||||
|
||||
Fluxo final:
|
||||
|
||||
```text
|
||||
supervisor_review
|
||||
│
|
||||
▼
|
||||
persist_long_term_memory
|
||||
│
|
||||
▼
|
||||
persist
|
||||
```
|
||||
|
||||
### Variáveis de ambiente
|
||||
|
||||
```env
|
||||
ENABLE_LONG_TERM_MEMORY=true
|
||||
|
||||
LONG_TERM_MEMORY_PROVIDER=sqlite
|
||||
LONG_TERM_MEMORY_SQLITE_PATH=./data/agent_framework.db
|
||||
LONG_TERM_MEMORY_TABLE=agentfw_long_term_memory
|
||||
|
||||
LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS=20
|
||||
LONG_TERM_MEMORY_MIN_CONFIDENCE=0.70
|
||||
LONG_TERM_MEMORY_AUTO_EXTRACT=true
|
||||
LONG_TERM_MEMORY_INJECT_CONTEXT=true
|
||||
```
|
||||
|
||||
### Caminho do banco SQLite
|
||||
|
||||
O caminho relativo é resolvido a partir do diretório em que o backend é iniciado.
|
||||
|
||||
Para evitar que bancos diferentes sejam criados acidentalmente, prefira um caminho absoluto em ambientes de desenvolvimento:
|
||||
|
||||
```env
|
||||
LONG_TERM_MEMORY_SQLITE_PATH=/mnt/c/Asus_Projects/agent_platform_oci_long_term_memory/data/agent_framework.db
|
||||
```
|
||||
|
||||
Crie a pasta antes de iniciar:
|
||||
|
||||
```bash
|
||||
mkdir -p data
|
||||
```
|
||||
|
||||
### Como testar
|
||||
|
||||
### Teste 1 — Gravação
|
||||
|
||||
Envie:
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "default:telecom_contas:memory-session-a",
|
||||
"customer_key": "11999999999",
|
||||
"message": "Me chame de Cris. Minha linguagem preferida é Python e meu projeto atual se chama Atlas."
|
||||
}
|
||||
```
|
||||
|
||||
### Teste 2 — Recuperação em outra sessão
|
||||
|
||||
Utilize outro `session_id`, mantendo o mesmo `customer_key`:
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "default:telecom_contas:memory-session-b",
|
||||
"customer_key": "11999999999",
|
||||
"message": "O que você lembra sobre mim, minhas preferências e meu projeto?"
|
||||
}
|
||||
```
|
||||
|
||||
Resultado esperado:
|
||||
|
||||
```text
|
||||
Seu nome preferido é Cris.
|
||||
Sua linguagem preferida é Python.
|
||||
Seu projeto atual se chama Atlas.
|
||||
```
|
||||
|
||||
### Teste 3 — Isolamento
|
||||
|
||||
Utilize outro cliente:
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "default:telecom_contas:memory-session-c",
|
||||
"customer_key": "outro-cliente",
|
||||
"message": "Qual é meu nome preferido e qual é meu projeto atual?"
|
||||
}
|
||||
```
|
||||
|
||||
Os dados de `11999999999` não devem aparecer.
|
||||
|
||||
### Teste 4 — Reinicialização do frontend
|
||||
|
||||
Reinicie ou resete o frontend e confirme que ele continua enviando o mesmo `customer_key`.
|
||||
|
||||
A memória deve sobreviver à troca do `session_id`. O reset do frontend não apaga o SQLite.
|
||||
|
||||
### Teste 5 — Reinicialização do backend
|
||||
|
||||
Reinicie o Uvicorn e repita a consulta.
|
||||
|
||||
Com:
|
||||
|
||||
```env
|
||||
LONG_TERM_MEMORY_PROVIDER=sqlite
|
||||
```
|
||||
|
||||
a memória deve continuar disponível.
|
||||
|
||||
Com:
|
||||
|
||||
```env
|
||||
LONG_TERM_MEMORY_PROVIDER=memory
|
||||
```
|
||||
|
||||
a memória será perdida quando o processo for encerrado.
|
||||
|
||||
### Verificação direta no SQLite
|
||||
|
||||
Localize o banco:
|
||||
|
||||
```bash
|
||||
find . -name "agent_framework.db" -type f
|
||||
```
|
||||
|
||||
Abra:
|
||||
|
||||
```bash
|
||||
sqlite3 ./data/agent_framework.db
|
||||
```
|
||||
|
||||
Consulte:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
tenant_id,
|
||||
agent_id,
|
||||
customer_key,
|
||||
memory_type,
|
||||
memory_key,
|
||||
memory_value,
|
||||
confidence,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM agentfw_long_term_memory
|
||||
ORDER BY updated_at DESC;
|
||||
```
|
||||
|
||||
### Critérios de sucesso
|
||||
|
||||
A implementação está funcionando quando:
|
||||
|
||||
- a memória é recuperada com outro `session_id`;
|
||||
- o mesmo `customer_key` recupera os fatos anteriores;
|
||||
- outro `customer_key` não acessa esses fatos;
|
||||
- reiniciar o frontend não apaga a memória;
|
||||
- reiniciar o backend não apaga a memória quando o provider é SQLite;
|
||||
- o nó `persist_long_term_memory` é executado;
|
||||
- o prompt recebe `long_term_memory_context`.
|
||||
|
||||
### Boas práticas
|
||||
|
||||
- Persistir somente fatos duradouros.
|
||||
- Não armazenar a conversa completa como Long-Term Memory.
|
||||
- Isolar dados por `tenant_id`, `agent_id` e `customer_key`.
|
||||
- Não utilizar `session_id` como identidade permanente do usuário.
|
||||
- Persistir somente depois das validações finais.
|
||||
- Evitar armazenar resultados temporários de ferramentas.
|
||||
- Registrar telemetria de leitura, escrita, atualização e falha.
|
||||
- Definir políticas de retenção e exclusão.
|
||||
- Usar caminho absoluto para SQLite em ambientes com múltiplos diretórios de execução.
|
||||
- Migrar para um banco corporativo em ambientes de produção e alta disponibilidade.
|
||||
|
||||
### Limitações da implementação de referência
|
||||
|
||||
A implementação atual utiliza extração baseada em regras e SQLite como provider de referência.
|
||||
|
||||
Evoluções recomendadas:
|
||||
|
||||
- extração de fatos com LLM;
|
||||
- memória semântica com vetores;
|
||||
- memória episódica;
|
||||
- expiração e versionamento;
|
||||
- deduplicação semântica;
|
||||
- política de consentimento;
|
||||
- API de consulta e exclusão;
|
||||
- provider Oracle Autonomous Database;
|
||||
- criptografia e classificação de dados sensíveis.
|
||||
|
||||
### Checkpoint Enterprise no LangGraph
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/README_CHECKPOINT_ENTERPRISE.md`.
|
||||
|
||||
Esta versão adiciona quatro capacidades ao checkpointer do LangGraph usado pelo framework:
|
||||
|
||||
1. **Checkpoint Integrity**: cada checkpoint é salvo dentro de um envelope com `schema_version`, `checkpoint_id`, `payload_hash` SHA-256 e `created_at`. Na leitura, o hash é recalculado. Se o payload foi truncado, alterado ou corrompido, o checkpoint é ignorado no recovery.
|
||||
2. **Checkpoint Compaction**: checkpoints antigos são removidos automaticamente conforme a configuração `CHECKPOINT_COMPACT_EVERY` e `CHECKPOINT_KEEP_LAST`. Isso evita crescimento infinito da tabela `workflow_checkpoints`.
|
||||
3. **Resilient Checkpointer**: gravações e leituras usam retry com backoff e jitter. A camada resiliente funciona sobre memory, SQLite e Oracle/Autonomous Database.
|
||||
4. **Checkpoint Recovery**: ao recuperar o estado, o framework varre os últimos checkpoints e retorna o mais recente válido, pulando checkpoints corrompidos.
|
||||
|
||||
### Configuração
|
||||
|
||||
No `.env`:
|
||||
|
||||
```env
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=sqlite
|
||||
ENABLE_RESILIENT_CHECKPOINTER=true
|
||||
ENABLE_CHECKPOINT_INTEGRITY=true
|
||||
ENABLE_CHECKPOINT_COMPACTION=true
|
||||
CHECKPOINT_COMPACT_EVERY=50
|
||||
CHECKPOINT_KEEP_LAST=20
|
||||
CHECKPOINT_RECOVERY_SCAN_LIMIT=25
|
||||
CHECKPOINT_RETRY_MAX_ATTEMPTS=3
|
||||
CHECKPOINT_RETRY_BASE_DELAY_SECONDS=0.05
|
||||
CHECKPOINT_RETRY_MAX_DELAY_SECONDS=1.0
|
||||
CHECKPOINT_RETRY_JITTER_SECONDS=0.05
|
||||
```
|
||||
|
||||
Para produção com múltiplos pods, prefira:
|
||||
|
||||
```env
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=autonomous
|
||||
ADB_USER=...
|
||||
ADB_PASSWORD=...
|
||||
ADB_DSN=...
|
||||
ADB_WALLET_LOCATION=...
|
||||
ADB_TABLE_PREFIX=AGENTFW
|
||||
```
|
||||
|
||||
### Uso no LangGraph
|
||||
|
||||
```python
|
||||
from agent_framework.checkpoints import create_langgraph_checkpointer
|
||||
|
||||
checkpointer = create_langgraph_checkpointer(settings)
|
||||
graph = builder.compile(checkpointer=checkpointer)
|
||||
|
||||
config = {"configurable": {"thread_id": session_id}}
|
||||
result = graph.invoke(input_state, config=config)
|
||||
```
|
||||
|
||||
O `thread_id` continua sendo a chave de recuperação da conversa. Em ambiente com Load Balancer, qualquer pod consegue retomar a execução se usar o mesmo repositório persistente.
|
||||
|
||||
### Arquivos alterados
|
||||
|
||||
- `agent_framework/src/agent_framework/checkpoints/checkpoint_repository.py`
|
||||
- `agent_framework/src/agent_framework/checkpoints/langgraph_saver.py`
|
||||
- `agent_framework/src/agent_framework/checkpoints/__init__.py`
|
||||
- `agent_framework/src/agent_framework/config/settings.py`
|
||||
- `tests/unit/test_resilient_checkpointer.py`
|
||||
|
||||
### Observação importante
|
||||
|
||||
O provider `memory` agora também usa o `RepositoryCheckpointSaver` quando `ENABLE_RESILIENT_CHECKPOINTER=true`. Para voltar ao `MemorySaver` puro do LangGraph em testes locais, configure:
|
||||
|
||||
```env
|
||||
ENABLE_RESILIENT_CHECKPOINTER=false
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=memory
|
||||
```
|
||||
|
||||
### Arquivos de origem
|
||||
|
||||
Os arquivos abaixo foram consolidados neste manual:
|
||||
|
||||
- `Documentacao/Manual_Long_Term_Memory_PT.md`
|
||||
- `Documentacao/README_CHECKPOINT_ENTERPRISE.md`
|
||||
|
||||
### Regra de manutenção
|
||||
|
||||
Novas correções ou evoluções deste tema devem atualizar este documento consolidado. Release notes podem continuar existindo como histórico, mas não devem ser necessárias para compreender ou implementar a funcionalidade.
|
||||
118
docs/developer/pt/09_llm_rich_response_reasoning.md
Normal file
118
docs/developer/pt/09_llm_rich_response_reasoning.md
Normal file
@@ -0,0 +1,118 @@
|
||||
|
||||
### LLM Rich Response e reasoning_content
|
||||
|
||||
### Como usar este manual
|
||||
|
||||
Este é um **manual de referência especializado**. Ele não substitui o tutorial principal.
|
||||
|
||||
- Para criar um agente do início ao fim, use [`README.md`](../../../README.md).
|
||||
- Use este documento quando precisar implementar, aprofundar ou diagnosticar **`ainvoke_response()`, metadados de inferência e `reasoning_content` opcional**.
|
||||
- Os exemplos históricos consolidados aqui devem ser lidos à luz da API atual do framework.
|
||||
- Em caso de divergência, o código da versão e o `README.md` atual prevalecem.
|
||||
|
||||
### Relação com o tutorial principal
|
||||
|
||||
O `README.md` apresenta essa capacidade no fluxo normal de desenvolvimento. Este manual reúne detalhes que estavam distribuídos em `docs/`, `Documentacao/`, release notes, validações e guias especializados.
|
||||
|
||||
O objetivo aqui é responder **“como essa feature funciona em profundidade e como eu resolvo problemas nela?”**, sem transformar este arquivo em uma segunda cópia do tutorial principal.
|
||||
|
||||
### Escopo
|
||||
|
||||
`ainvoke_response()`, metadados de inferência e `reasoning_content` opcional.
|
||||
|
||||
### Conteúdo técnico consolidado
|
||||
|
||||
### LLM Rich Response e reasoning_content
|
||||
|
||||
Guia para usar a API opt-in de resposta estruturada do LLM sem quebrar o contrato legado de ainvoke(), incluindo reasoning_content, usage, model, provider, fallback e testes.
|
||||
|
||||
### Como usar este documento
|
||||
|
||||
Este é o documento consolidado de desenvolvimento para este assunto. Ele reúne arquitetura, configuração, exemplos, comportamento de runtime, compatibilidade, testes e troubleshooting que antes estavam distribuídos em vários arquivos. As seções de origem foram preservadas quando traziam detalhes técnicos distintos; notas de release foram incorporadas como comportamento atual ou histórico de correção.
|
||||
|
||||
### API rica de resposta LLM
|
||||
|
||||
> Conteúdo consolidado a partir de `docs/LLM_RICH_RESPONSE.md`.
|
||||
|
||||
### Objetivo
|
||||
|
||||
O framework mantém `ainvoke()` como API retrocompatível, retornando apenas `str`, e adiciona `ainvoke_response()` para consumidores que precisam de metadados adicionais da inferência, incluindo `reasoning_content` quando o modelo/provider/API o disponibilizar.
|
||||
|
||||
### APIs
|
||||
|
||||
### API legada — sem alteração
|
||||
|
||||
```python
|
||||
answer = await llm.ainvoke(messages)
|
||||
assert isinstance(answer, str)
|
||||
```
|
||||
|
||||
Nenhum agente existente precisa ser alterado.
|
||||
|
||||
### Nova API rica — opt-in
|
||||
|
||||
```python
|
||||
response = await llm.ainvoke_response(messages)
|
||||
|
||||
answer = response.content
|
||||
reasoning = response.reasoning_content
|
||||
usage = response.usage
|
||||
model = response.model
|
||||
provider = response.provider
|
||||
```
|
||||
|
||||
`reasoning_content` é `str | None`. `None` é o comportamento esperado quando o modelo, provider ou API não expõe reasoning textual.
|
||||
|
||||
### Backoffice
|
||||
|
||||
Um consumidor que antes fazia:
|
||||
|
||||
```python
|
||||
answer = await llm.ainvoke(messages)
|
||||
template = extract_response(answer)
|
||||
```
|
||||
|
||||
pode passar a fazer:
|
||||
|
||||
```python
|
||||
response = await llm.ainvoke_response(messages)
|
||||
template = extract_response(response.content)
|
||||
reasoning_content = response.reasoning_content
|
||||
```
|
||||
|
||||
A lógica que espera texto continua recebendo `response.content`; o reasoning fica separado e não contamina resposta, cache, memória, judges ou guardrails.
|
||||
|
||||
### Compatibilidade de providers customizados
|
||||
|
||||
`LLMProvider.ainvoke_response()` possui fallback. Um provider externo que implemente apenas `ainvoke()` continua funcionando e recebe automaticamente um `LLMResponse(content=<texto>)`, com `reasoning_content=None`.
|
||||
|
||||
Providers nativos (`mock`, OpenAI-compatible/OCI OpenAI e OCI SDK) implementam a resposta rica e tentam preservar reasoning quando presente.
|
||||
|
||||
### Garantias de compatibilidade
|
||||
|
||||
- `ainvoke()` continua retornando `str`.
|
||||
- Nenhum router, judge, RAG, memória, cache ou runtime existente foi migrado para a nova API.
|
||||
- `reasoning_content` nunca é fabricado pelo framework.
|
||||
- Ausência de reasoning não gera erro.
|
||||
- O output existente de telemetria continua sendo o conteúdo final, sem anexar reasoning automaticamente.
|
||||
|
||||
### Testes
|
||||
|
||||
Os testes específicos estão em `tests/unit/test_llm_rich_response.py` e verificam:
|
||||
|
||||
1. provider legado que só implementa `ainvoke()`;
|
||||
2. manutenção do retorno `str` em `ainvoke()`;
|
||||
3. retorno de `LLMResponse` em `ainvoke_response()`;
|
||||
4. reasoning via atributo direto;
|
||||
5. reasoning via `model_extra`;
|
||||
6. ausência de reasoning e extração no formato OCI SDK.
|
||||
|
||||
### Arquivos de origem
|
||||
|
||||
Os arquivos abaixo foram consolidados neste manual:
|
||||
|
||||
- `docs/LLM_RICH_RESPONSE.md`
|
||||
|
||||
### Regra de manutenção
|
||||
|
||||
Novas correções ou evoluções deste tema devem atualizar este documento consolidado. Release notes podem continuar existindo como histórico, mas não devem ser necessárias para compreender ou implementar a funcionalidade.
|
||||
356
docs/developer/pt/10_performance_cache_and_async_runtime.md
Normal file
356
docs/developer/pt/10_performance_cache_and_async_runtime.md
Normal file
@@ -0,0 +1,356 @@
|
||||
|
||||
### Performance, Cache e Runtime Assíncrono
|
||||
|
||||
### Como usar este manual
|
||||
|
||||
Este é um **manual de referência especializado**. Ele não substitui o tutorial principal.
|
||||
|
||||
- Para criar um agente do início ao fim, use [`README.md`](../../../README.md).
|
||||
- Use este documento quando precisar implementar, aprofundar ou diagnosticar **concorrência, cache, redução de chamadas LLM e correções cross-loop**.
|
||||
- Os exemplos históricos consolidados aqui devem ser lidos à luz da API atual do framework.
|
||||
- Em caso de divergência, o código da versão e o `README.md` atual prevalecem.
|
||||
|
||||
### Relação com o tutorial principal
|
||||
|
||||
O `README.md` apresenta essa capacidade no fluxo normal de desenvolvimento. Este manual reúne detalhes que estavam distribuídos em `docs/`, `Documentacao/`, release notes, validações e guias especializados.
|
||||
|
||||
O objetivo aqui é responder **“como essa feature funciona em profundidade e como eu resolvo problemas nela?”**, sem transformar este arquivo em uma segunda cópia do tutorial principal.
|
||||
|
||||
### Escopo
|
||||
|
||||
Concorrência, cache, redução de chamadas llm e correções cross-loop.
|
||||
|
||||
### Conteúdo técnico consolidado
|
||||
|
||||
### Performance, Cache, Concorrência e Runtime Assíncrono
|
||||
|
||||
Manual das otimizações no caminho crítico de MCP, RAG e Judges, redução de chamadas LLM, preempção determinística e correção de deadlock cross-loop no sequenciamento.
|
||||
|
||||
### Como usar este documento
|
||||
|
||||
Este é o documento consolidado de desenvolvimento para este assunto. Ele reúne arquitetura, configuração, exemplos, comportamento de runtime, compatibilidade, testes e troubleshooting que antes estavam distribuídos em vários arquivos. As seções de origem foram preservadas quando traziam detalhes técnicos distintos; notas de release foram incorporadas como comportamento atual ou histórico de correção.
|
||||
|
||||
### Otimizações MCP, RAG e Judges
|
||||
|
||||
> Conteúdo consolidado a partir de `docs/PERFORMANCE_OPTIMIZATIONS_MCP_JUDGES_RAG.md`.
|
||||
|
||||
- `mcp_tools` permanece allowlist; somente a consulta selecionada por `selection_keywords` é executada.
|
||||
- Extração `strategy: hybrid` tenta `pattern` regex antes do perfil LLM.
|
||||
- RAG é ignorado quando MCP bem-sucedido é suficiente, salvo perguntas de política/regra.
|
||||
- `mcp_results` é fornecido como evidência ao groundedness judge.
|
||||
- `judges.yaml` aceita `sample_rate` e `always_run_for_transactional`.
|
||||
- Consultas estruturadas simples podem retornar resposta determinística sem LLM do agente.
|
||||
|
||||
### Mudança de consulta para ação transacional
|
||||
|
||||
A route stickiness é preemptada quando uma keyword explícita configurada em `routing.yaml` identifica outra intent/agente. Assim, uma sessão em `retail_order_tracking` muda para `retail_support_exchange_return` ao receber pedidos como “devolver pedido”. Além disso, respostas diretas de tools read-only são bloqueadas quando a mensagem contém `selection_keywords` de qualquer tool transacional registrada.
|
||||
|
||||
As palavras de ação ficam em `config/tools.yaml`; o runtime não mantém aliases de domínio hardcoded.
|
||||
|
||||
|
||||
### Preempção determinística de mudança explícita de intent
|
||||
|
||||
A stickiness não chama um segundo LLM quando a mensagem contém uma mudança explícita que pode ser reconhecida deterministicamente. Keywords multi-token configuradas em `routing.yaml` aceitam até três tokens intermediários, preservando a ordem. Assim, `cancelar pedido` reconhece `quero cancelar meu pedido`, `cancelar o meu pedido` e `pode cancelar esse pedido`. Nesse caso a nova intent preempta a stickiness e o metadado `keyword_match_strategy=ordered_tokens` permite auditar a decisão. Mensagens sem sinal explícito continuam usando a route stickiness normalmente.
|
||||
|
||||
### Correção de deadlock cross-loop
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/FIX_DEADLOCK_SEQUENCE_CROSS_LOOP.md`.
|
||||
|
||||
### Problema
|
||||
|
||||
A API síncrona `agent_framework.observer.event()` podia ser chamada em uma worker thread sem event loop ativo. Nesse caso, a implementação anterior executava `asyncio.run(aevent(...))`, criando um novo event loop temporário. Ao mesmo tempo, `analytics/tim_sequence.py` compartilhava instâncias globais de `asyncio.Lock` (`_mongo_index_lock` e `_memory_lock`) entre chamadas que podiam vir de event loops diferentes.
|
||||
|
||||
Na primeira operação Mongo, `_ensure_mongo_ttl_index_once()` mantinha `_mongo_index_lock` durante a criação do índice TTL. A contenção por outro loop podia deixar a segunda chamada aguardando indefinidamente.
|
||||
|
||||
### Alterações aplicadas
|
||||
|
||||
1. `observer.py`
|
||||
- removido `asyncio.run()` do caminho síncrono de `event()`;
|
||||
- adicionado um event loop dedicado e reutilizável para chamadas síncronas;
|
||||
- submissão cross-thread feita com `asyncio.run_coroutine_threadsafe()`;
|
||||
- encerramento best-effort do loop no shutdown do processo.
|
||||
|
||||
2. `analytics/tim_sequence.py`
|
||||
- `_mongo_index_lock`: `asyncio.Lock` -> `threading.Lock`;
|
||||
- `_memory_lock`: `asyncio.Lock` -> `threading.Lock`;
|
||||
- inicialização do índice TTL movida para uma função síncrona protegida por lock de thread e chamada via `asyncio.to_thread()`;
|
||||
- o contador de fallback em memória usa uma seção crítica curta e thread-safe.
|
||||
|
||||
3. Testes
|
||||
- `tests/test_observer_cross_loop_deadlock_fix.py` valida:
|
||||
- múltiplas worker threads usando `event()` compartilham o mesmo loop síncrono do observer;
|
||||
- sequence em memória permanece monotônica entre event loops independentes;
|
||||
- criação do índice TTL ocorre apenas uma vez sob contenção cross-loop.
|
||||
|
||||
### Validação executada
|
||||
|
||||
```bash
|
||||
PYTHONPATH=libs/agent_framework/src pytest -q tests/test_observer_cross_loop_deadlock_fix.py
|
||||
```
|
||||
|
||||
Resultado: `3 passed`.
|
||||
|
||||
A suíte completa do repositório possui falhas preexistentes/independentes desta alteração, incluindo conflitos de coleta de arquivos `test_long_term_memory.py`, caminhos estáticos de template e testes de checkpoint/workflow. Esses itens não foram alterados por esta correção.
|
||||
|
||||
### Recursos operacionais de performance
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/README_MAX_OPERACIONAL.md`.
|
||||
|
||||
Esta versão adiciona os ajustes operacionais que faltavam para aproximar o framework do padrão FIRST em produção.
|
||||
|
||||
### Ajustes incluídos nesta versão
|
||||
|
||||
### 1. Langfuse Enterprise Adapter
|
||||
Novo módulo:
|
||||
|
||||
```text
|
||||
agent_framework/observability/langfuse_enterprise.py
|
||||
```
|
||||
|
||||
Inclui adaptador compatível com SDKs Langfuse v2/v3 para:
|
||||
|
||||
- atualização de trace;
|
||||
- score/avaliação de trace;
|
||||
- prompt registry quando suportado pelo SDK;
|
||||
- isolamento das diferenças de API do Langfuse.
|
||||
|
||||
### 2. Token e Cost Accounting persistente
|
||||
Novo pacote:
|
||||
|
||||
```text
|
||||
agent_framework/billing/
|
||||
```
|
||||
|
||||
Inclui:
|
||||
|
||||
- `UsageRecord`
|
||||
- `SQLiteUsageRepository`
|
||||
- `OracleUsageRepository`
|
||||
- `create_usage_repository(settings)`
|
||||
|
||||
O provider LLM agora registra automaticamente:
|
||||
|
||||
- `prompt_tokens`
|
||||
- `completion_tokens`
|
||||
- `cached_tokens`
|
||||
- `total_tokens`
|
||||
- `cost_usd`
|
||||
- `cost_brl`
|
||||
- `tenant_id`
|
||||
- `agent_id`
|
||||
- `session_id`
|
||||
- `message_id`
|
||||
|
||||
Novo endpoint:
|
||||
|
||||
```http
|
||||
GET /debug/usage
|
||||
GET /debug/usage?tenant_id=default
|
||||
GET /debug/usage?session_id=<id>
|
||||
```
|
||||
|
||||
### 3. RAG Service operacional
|
||||
Novo módulo:
|
||||
|
||||
```text
|
||||
agent_framework/rag/rag_service.py
|
||||
```
|
||||
|
||||
Inclui:
|
||||
|
||||
- `RagService.add_documents()`
|
||||
- `RagService.retrieve()`
|
||||
- `RagResult.as_prompt_context()`
|
||||
- telemetria de latência, quantidade de documentos, top scores e grafo.
|
||||
|
||||
### 4. Configuração nova
|
||||
Variável adicionada:
|
||||
|
||||
```env
|
||||
USAGE_REPOSITORY_PROVIDER=sqlite
|
||||
```
|
||||
|
||||
Valores:
|
||||
|
||||
```text
|
||||
sqlite
|
||||
oracle
|
||||
autonomous
|
||||
```
|
||||
|
||||
### 5. Compatibilidade operacional local
|
||||
Por padrão, a contabilização de uso usa SQLite mesmo que o restante esteja em memória. Assim é possível testar localmente sem Oracle.
|
||||
|
||||
### Teste rápido
|
||||
|
||||
```bash
|
||||
cd agent_template_backend
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
Teste uma mensagem:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/gateway/message \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"channel":"web","payload":{"text":"teste","user_id":"u1","session_id":"s1"}}'
|
||||
```
|
||||
|
||||
Verifique uso/custo:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/debug/usage
|
||||
```
|
||||
|
||||
### Para rodar com padrão mais próximo de produção
|
||||
|
||||
```env
|
||||
SESSION_REPOSITORY_PROVIDER=sqlite
|
||||
MEMORY_REPOSITORY_PROVIDER=sqlite
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=sqlite
|
||||
USAGE_REPOSITORY_PROVIDER=sqlite
|
||||
CACHE_BACKEND_PROVIDER=sqlite
|
||||
VECTOR_STORE_PROVIDER=sqlite
|
||||
ENABLE_LANGFUSE=true
|
||||
LANGFUSE_HOST=http://localhost:3000
|
||||
LANGFUSE_PUBLIC_KEY=...
|
||||
LANGFUSE_SECRET_KEY=...
|
||||
```
|
||||
|
||||
Para Autonomous Database:
|
||||
|
||||
```env
|
||||
SESSION_REPOSITORY_PROVIDER=oracle
|
||||
MEMORY_REPOSITORY_PROVIDER=oracle
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=oracle
|
||||
USAGE_REPOSITORY_PROVIDER=oracle
|
||||
CACHE_BACKEND_PROVIDER=oracle
|
||||
VECTOR_STORE_PROVIDER=oracle
|
||||
GRAPH_STORE_PROVIDER=oracle
|
||||
ADB_USER=...
|
||||
ADB_PASSWORD=...
|
||||
ADB_DSN=...
|
||||
ADB_WALLET_LOCATION=...
|
||||
ADB_TABLE_PREFIX=AGENTFW
|
||||
```
|
||||
|
||||
### Ajustes finais de cache, RAG e telemetria
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/README_FIRST_MAX_OPERATIONAL_FIXES.md`.
|
||||
|
||||
Esta versão corrige os gaps identificados na comparação contra o FIRST.
|
||||
|
||||
### Correções aplicadas
|
||||
|
||||
### 1. Checkpoint LangGraph operacional
|
||||
|
||||
O workflow não compila mais com `MemorySaver()` diretamente. Foi criado o adaptador:
|
||||
|
||||
```text
|
||||
agent_framework/checkpoints/langgraph_saver.py
|
||||
```
|
||||
|
||||
Ele conecta o LangGraph ao repository configurado do framework:
|
||||
|
||||
- `memory`
|
||||
- `sqlite`
|
||||
- `oracle` / `autonomous`
|
||||
|
||||
No workflow:
|
||||
|
||||
```python
|
||||
builder.compile(checkpointer=create_langgraph_checkpointer(self.settings))
|
||||
```
|
||||
|
||||
### 2. Telemetria LangGraph envolvendo a execução real
|
||||
|
||||
Foi adicionado wrapper de nó no workflow:
|
||||
|
||||
```python
|
||||
self._node("billing_agent", self.billing_agent)
|
||||
```
|
||||
|
||||
Assim o span/evento `langgraph.node.*` envolve a execução real do nó, não apenas um bloco vazio.
|
||||
|
||||
Eventos emitidos:
|
||||
|
||||
- `langgraph.node.started`
|
||||
- `langgraph.node.completed`
|
||||
- `langgraph.node.failed`
|
||||
- `langgraph.edge.selected`
|
||||
|
||||
### 3. RAG integrado aos agentes
|
||||
|
||||
Os agentes agora recebem `RagService` e usam o contexto recuperado no prompt:
|
||||
|
||||
- BillingAgent
|
||||
- ProductAgent
|
||||
- OrdersAgent
|
||||
- SupportAgent
|
||||
|
||||
O RAG usa:
|
||||
|
||||
- `VECTOR_STORE_PROVIDER=memory|sqlite|oracle|autonomous`
|
||||
- `GRAPH_STORE_PROVIDER=memory|oracle|autonomous`
|
||||
- `RAG_TOP_K`
|
||||
|
||||
### 4. Cache integrado ao runtime dos agentes
|
||||
|
||||
Criado mixin:
|
||||
|
||||
```text
|
||||
agent_template_backend/app/agents/runtime.py
|
||||
```
|
||||
|
||||
Ele adiciona:
|
||||
|
||||
- busca RAG padronizada;
|
||||
- chave de cache para chamada LLM;
|
||||
- hit/miss com telemetria;
|
||||
- cache distribuído via `create_cache(settings)`.
|
||||
|
||||
### 5. Testes unitários
|
||||
|
||||
Criada pasta:
|
||||
|
||||
```text
|
||||
tests/unit
|
||||
```
|
||||
|
||||
Cobertura inicial:
|
||||
|
||||
- cache;
|
||||
- SSE;
|
||||
- RAG;
|
||||
- checkpoint saver;
|
||||
- telemetria LangGraph;
|
||||
- runtime dos agentes;
|
||||
- verificação estática do workflow;
|
||||
- imports principais.
|
||||
|
||||
Validação local executada:
|
||||
|
||||
```text
|
||||
12 passed
|
||||
```
|
||||
|
||||
### Como testar
|
||||
|
||||
```bash
|
||||
cd projeto_agent_framework_first_ready
|
||||
pip install -r agent_template_backend/requirements.txt
|
||||
pytest -q tests/unit
|
||||
```
|
||||
|
||||
### Arquivos de origem
|
||||
|
||||
Os arquivos abaixo foram consolidados neste manual:
|
||||
|
||||
- `docs/PERFORMANCE_OPTIMIZATIONS_MCP_JUDGES_RAG.md`
|
||||
- `Documentacao/FIX_DEADLOCK_SEQUENCE_CROSS_LOOP.md`
|
||||
- `Documentacao/README_MAX_OPERACIONAL.md`
|
||||
- `Documentacao/README_FIRST_MAX_OPERATIONAL_FIXES.md`
|
||||
|
||||
### Regra de manutenção
|
||||
|
||||
Novas correções ou evoluções deste tema devem atualizar este documento consolidado. Release notes podem continuar existindo como histórico, mas não devem ser necessárias para compreender ou implementar a funcionalidade.
|
||||
@@ -0,0 +1,736 @@
|
||||
|
||||
### Observabilidade, Persistência e Prontidão Operacional
|
||||
|
||||
### Como usar este manual
|
||||
|
||||
Este é um **manual de referência especializado**. Ele não substitui o tutorial principal.
|
||||
|
||||
- Para criar um agente do início ao fim, use [`README.md`](../../../README.md).
|
||||
- Use este documento quando precisar implementar, aprofundar ou diagnosticar **telemetria, IC/NOC/GRL, correlação, sequência, persistência e diagnóstico operacional**.
|
||||
- Os exemplos históricos consolidados aqui devem ser lidos à luz da API atual do framework.
|
||||
- Em caso de divergência, o código da versão e o `README.md` atual prevalecem.
|
||||
|
||||
### Relação com o tutorial principal
|
||||
|
||||
O `README.md` apresenta essa capacidade no fluxo normal de desenvolvimento. Este manual reúne detalhes que estavam distribuídos em `docs/`, `Documentacao/`, release notes, validações e guias especializados.
|
||||
|
||||
O objetivo aqui é responder **“como essa feature funciona em profundidade e como eu resolvo problemas nela?”**, sem transformar este arquivo em uma segunda cópia do tutorial principal.
|
||||
|
||||
### Escopo
|
||||
|
||||
Telemetria, ic/noc/grl, correlação, sequência, persistência e diagnóstico operacional.
|
||||
|
||||
### Conteúdo técnico consolidado
|
||||
|
||||
### Observabilidade, Persistência e Prontidão Operacional
|
||||
|
||||
Guia consolidado das capacidades FIRST-ready: correlação ponta-a-ponta, Langfuse, OpenTelemetry, SSE observável, persistência Oracle, token/cost accounting, cache e telemetria LangGraph.
|
||||
|
||||
### Como usar este documento
|
||||
|
||||
Este é o documento consolidado de desenvolvimento para este assunto. Ele reúne arquitetura, configuração, exemplos, comportamento de runtime, compatibilidade, testes e troubleshooting que antes estavam distribuídos em vários arquivos. As seções de origem foram preservadas quando traziam detalhes técnicos distintos; notas de release foram incorporadas como comportamento atual ou histórico de correção.
|
||||
|
||||
### Base FIRST-ready e observabilidade
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/README_FIRST_READY.md`.
|
||||
|
||||
Esta versão mantém a arquitetura do `meu_projeto_agent_framework` e adiciona os padrões operacionais encontrados no projeto FIRST.
|
||||
|
||||
### Recursos adicionados
|
||||
|
||||
1. **SSE no padrão FIRST**
|
||||
- `GET /gateway/events/{session_id}` para stream `text/event-stream`.
|
||||
- `POST /gateway/message/sse` para processar mensagem emitindo eventos SSE.
|
||||
- Eventos: `connected`, `flow.start`, `session.upserted`, `message.received`, `workflow.started`, `workflow.completed`, `message.responded`, `flow.end`.
|
||||
- Keepalive configurável por `SSE_KEEPALIVE_SECONDS`.
|
||||
- Lock por sessão para evitar concorrência dentro da mesma conversa.
|
||||
- Replay de eventos via `Last-Event-ID` ou query param `last_event_id`.
|
||||
|
||||
2. **Persistência de sessão e mensagens**
|
||||
- Implementado provider `sqlite`, executável localmente.
|
||||
- `SESSION_REPOSITORY_PROVIDER=sqlite`.
|
||||
- `MEMORY_REPOSITORY_PROVIDER=sqlite`.
|
||||
- Tabelas locais: `agent_sessions`, `agent_messages`.
|
||||
- Idempotência por `message_id`.
|
||||
|
||||
3. **Checkpoint persistente**
|
||||
- Implementado provider `sqlite` para checkpoint final do workflow.
|
||||
- `CHECKPOINT_REPOSITORY_PROVIDER=sqlite`.
|
||||
- Endpoint de leitura: `GET /sessions/{session_id}/checkpoint`.
|
||||
|
||||
4. **Histórico de mensagens**
|
||||
- Endpoint: `GET /sessions/{session_id}/messages`.
|
||||
- Histórico usado como memória conversacional antes de chamar o LangGraph.
|
||||
|
||||
5. **Cache**
|
||||
- Novo módulo `agent_framework.cache.cache`.
|
||||
- Suporta cache local em memória e Redis se `ENABLE_REDIS_CACHE=true`.
|
||||
|
||||
6. **RAG / Vector Store**
|
||||
- `agent_framework.rag.vector_store` agora possui `InMemoryVectorStore`, `SQLiteVectorStore` e contrato `AutonomousVectorStore`.
|
||||
- A versão SQLite usa busca lexical local para desenvolvimento.
|
||||
- O contrato permite trocar por Oracle Vector Search sem alterar a camada de aplicação.
|
||||
|
||||
7. **Observabilidade**
|
||||
- Mantém Langfuse existente.
|
||||
- Acrescenta eventos de gateway/SSE/workflow com `session_id`, `agent_id`, `tenant_id`, `message_id`, rota e intenção.
|
||||
|
||||
### Arquitetura resultante
|
||||
|
||||
```text
|
||||
Browser
|
||||
|-- POST /gateway/message/sse
|
||||
|-- GET /gateway/events/{session_id}
|
||||
|
|
||||
FastAPI Template Backend
|
||||
|
|
||||
ChannelGateway
|
||||
|
|
||||
SessionRepository + MessageHistory + CheckpointRepository
|
||||
|
|
||||
LangGraph AgentWorkflow
|
||||
|
|
||||
Guardrails -> Router/Supervisor -> Agent -> Output Guardrails -> Judges
|
||||
|
|
||||
Telemetry / Langfuse / OCI Streaming
|
||||
```
|
||||
|
||||
### Como rodar localmente
|
||||
|
||||
```bash
|
||||
cd agent_template_backend
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
pip install -e ../agent_framework
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
Frontend:
|
||||
|
||||
```bash
|
||||
cd agent_frontend
|
||||
python -m http.server 3000
|
||||
```
|
||||
|
||||
Abra:
|
||||
|
||||
```text
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
### Variáveis principais
|
||||
|
||||
```env
|
||||
SESSION_REPOSITORY_PROVIDER=sqlite
|
||||
MEMORY_REPOSITORY_PROVIDER=sqlite
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=sqlite
|
||||
VECTOR_STORE_PROVIDER=sqlite
|
||||
SQLITE_DB_PATH=./data/agent_framework.db
|
||||
ENABLE_SSE=true
|
||||
SSE_KEEPALIVE_SECONDS=15
|
||||
ENABLE_MESSAGE_IDEMPOTENCY=true
|
||||
```
|
||||
|
||||
### Teste via curl
|
||||
|
||||
Mensagem normal:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/gateway/message \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"channel":"web","payload":{"text":"teste","message":"teste","session_id":"s1","user_id":"u1","message_id":"m1"}}'
|
||||
```
|
||||
|
||||
Mensagem com SSE:
|
||||
|
||||
```bash
|
||||
curl -N http://localhost:8000/gateway/events/s1
|
||||
```
|
||||
|
||||
Em outro terminal:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/gateway/message/sse \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"channel":"web","payload":{"text":"teste","message":"teste","session_id":"s1","user_id":"u1","message_id":"m2"}}'
|
||||
```
|
||||
|
||||
Histórico:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/sessions/s1/messages
|
||||
```
|
||||
|
||||
Checkpoint:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/sessions/s1/checkpoint
|
||||
```
|
||||
|
||||
### Observação importante
|
||||
|
||||
A versão adicionada é executável localmente com SQLite. As classes `AutonomousSessionRepository`, `DatabaseMessageHistory`, `AutonomousCheckpointRepository` e `AutonomousVectorStore` mantêm o contrato para Oracle Autonomous Database, mas nesta entrega usam SQLite como backend local para permitir rodar e testar sem infraestrutura Oracle.
|
||||
|
||||
### Evolução de Observabilidade no padrão FIRST
|
||||
|
||||
Esta versão adiciona uma camada corporativa de observabilidade ao framework, mantendo os componentes reutilizáveis dentro de `agent_framework`.
|
||||
|
||||
### Componentes adicionados
|
||||
|
||||
```text
|
||||
agent_framework/observability/
|
||||
├── context.py # ContextVar: request_id, session_id, user_id, tenant_id, agent_id, channel, ura_call_id, workflow_id, message_id
|
||||
├── telemetry.py # Facade central: span, event, generation, rag_event, cache_event, checkpoint_event
|
||||
├── event_bus.py # Event bus interno para plugar logs, SSE, OCI Streaming, Elastic, Phoenix etc.
|
||||
├── otel.py # OpenTelemetry opcional via OTLP
|
||||
├── workflow_events.py # workflow.started, node.started, node.completed, edge.selected, workflow.failed
|
||||
├── guardrail_events.py # guardrail.<CODE>.evaluated e guardrail.<CODE>.blocked
|
||||
├── judge_events.py # judge.<NAME>.evaluated
|
||||
├── streaming_events.py # sse.connected, sse.keepalive, sse.event.emitted
|
||||
└── decorators.py # decorator @traced para classes do framework
|
||||
```
|
||||
|
||||
### Correlação ponta-a-ponta
|
||||
|
||||
Cada chamada HTTP cria ou propaga `x-request-id` e o fluxo de mensagem vincula:
|
||||
|
||||
```text
|
||||
request_id → tenant_id → agent_id → session_id → user_id → channel → message_id → workflow_id
|
||||
```
|
||||
|
||||
O contexto usa `ContextVar`, portanto funciona em chamadas assíncronas, FastAPI, LangGraph e providers LLM.
|
||||
|
||||
### Política de auto-instrumentação OpenAI no Langfuse
|
||||
|
||||
A configuração oficial dos templates do framework é:
|
||||
|
||||
```env
|
||||
ENABLE_LANGFUSE=true
|
||||
ENABLE_LANGFUSE_OPENAI_AUTO_INSTRUMENTATION=false
|
||||
```
|
||||
|
||||
O `false` é intencional. O framework já registra as chamadas ao modelo usando `Telemetry.generation(...)` e mantém a generation dentro do trace da requisição. Habilitar simultaneamente `langfuse.openai` cria uma segunda camada de instrumentação e pode resultar em `OpenAI-generation` como trace raiz, observations duplicadas e dupla contabilização de tokens/custos.
|
||||
|
||||
Use `true` somente para capturar chamadas diretas ao SDK OpenAI/OpenAI-compatible que ocorram fora da telemetria do framework. Esse é um modo de compatibilidade/diagnóstico, não o padrão operacional. Todos os `.env.example` do repositório devem permanecer explicitamente com o valor `false`.
|
||||
|
||||
### Langfuse
|
||||
|
||||
Ative no `.env`:
|
||||
|
||||
```env
|
||||
ENABLE_LANGFUSE=true
|
||||
LANGFUSE_PUBLIC_KEY=pk-lf-...
|
||||
LANGFUSE_SECRET_KEY=sk-lf-...
|
||||
LANGFUSE_HOST=http://localhost:3000
|
||||
```
|
||||
|
||||
O framework registra:
|
||||
|
||||
```text
|
||||
Trace de conversa
|
||||
├── http.request
|
||||
├── agent.gateway_message
|
||||
├── workflow.langgraph.ainvoke
|
||||
├── workflow.input_guardrails
|
||||
│ └── guardrail.<CODE>.evaluated / blocked
|
||||
├── workflow.routing_decision
|
||||
├── workflow.agent.<agent>
|
||||
│ └── generation.<model>
|
||||
├── workflow.output_guardrails
|
||||
├── workflow.judge
|
||||
│ └── judge.<NAME>.evaluated
|
||||
├── workflow.supervisor_review
|
||||
├── workflow.persist
|
||||
└── sse.event.emitted / sse.keepalive
|
||||
```
|
||||
|
||||
### OpenTelemetry
|
||||
|
||||
Ative no `.env`:
|
||||
|
||||
```env
|
||||
ENABLE_OTEL=true
|
||||
OTEL_SERVICE_NAME=agent-framework-template
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318/v1/traces
|
||||
```
|
||||
|
||||
Com isso, os mesmos spans são exportados via OTLP para Elastic, Grafana Tempo, Jaeger, Collector ou outro backend compatível.
|
||||
|
||||
### SSE observável
|
||||
|
||||
O `SSEHub` agora registra eventos de:
|
||||
|
||||
- conexão aberta;
|
||||
- replay de eventos;
|
||||
- evento emitido;
|
||||
- keepalive;
|
||||
- lock por sessão no processamento de mensagem.
|
||||
|
||||
### Guardrails e Judges
|
||||
|
||||
Além dos eventos agregados (`guardrails.input.completed`, `judges.completed`), cada decisão individual gera telemetria própria:
|
||||
|
||||
```text
|
||||
guardrail.MSK.evaluated
|
||||
guardrail.OOS.blocked
|
||||
judge.response_quality.evaluated
|
||||
judge.groundedness.evaluated
|
||||
```
|
||||
|
||||
### Extensão para outros backends
|
||||
|
||||
A classe `Telemetry.event_bus` permite plugar novos handlers sem alterar o workflow. Exemplo:
|
||||
|
||||
```python
|
||||
async def enviar_para_elastic(event):
|
||||
...
|
||||
|
||||
telemetry.event_bus.subscribe(enviar_para_elastic)
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Evolução FIRST Enterprise Completa
|
||||
|
||||
Esta versão recebeu os componentes que faltavam para aproximar o framework do padrão operacional do projeto FIRST:
|
||||
|
||||
### Persistência Oracle Autonomous Database
|
||||
|
||||
Foram adicionados providers reais Oracle:
|
||||
|
||||
- `OracleSessionRepository`
|
||||
- `OracleMessageHistory`
|
||||
- `OracleCheckpointRepository`
|
||||
- `OracleCache`
|
||||
- `OracleVectorStore`
|
||||
- `OracleGraphStore`
|
||||
- `OracleStore`
|
||||
|
||||
Tabelas criadas automaticamente com prefixo configurável `ADB_TABLE_PREFIX`:
|
||||
|
||||
- `<PREFIX>_AGENT_SESSION`
|
||||
- `<PREFIX>_AGENT_MESSAGE`
|
||||
- `<PREFIX>_WORKFLOW_CHECKPOINT`
|
||||
- `<PREFIX>_WORKFLOW_CHECKPOINT_WRITE`
|
||||
- `<PREFIX>_WORKFLOW_CHECKPOINT_BLOB`
|
||||
- `<PREFIX>_SSE_EVENT`
|
||||
- `<PREFIX>_CACHE_ENTRY`
|
||||
- `<PREFIX>_RAG_DOCUMENT`
|
||||
- `<PREFIX>_GRAPH_EDGE`
|
||||
|
||||
### Configuração Oracle
|
||||
|
||||
```env
|
||||
SESSION_REPOSITORY_PROVIDER=oracle
|
||||
MEMORY_REPOSITORY_PROVIDER=oracle
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=oracle
|
||||
CACHE_BACKEND_PROVIDER=oracle
|
||||
VECTOR_STORE_PROVIDER=oracle
|
||||
GRAPH_STORE_PROVIDER=oracle
|
||||
SSE_STORE_PROVIDER=oracle
|
||||
|
||||
ADB_USER=ADMIN
|
||||
ADB_PASSWORD=***
|
||||
ADB_DSN=meu_adb_high
|
||||
ADB_WALLET_LOCATION=/path/wallet
|
||||
ADB_WALLET_PASSWORD=***
|
||||
ADB_TABLE_PREFIX=AGENTFW
|
||||
```
|
||||
|
||||
### SSE Enterprise
|
||||
|
||||
O SSE agora possui:
|
||||
|
||||
- lock por sessão (`SessionLockManager`)
|
||||
- keepalive configurável
|
||||
- replay por `Last-Event-ID`
|
||||
- persistência de eventos em SQLite ou Oracle
|
||||
- telemetria de conexão, replay, keepalive e desconexão
|
||||
|
||||
Endpoint:
|
||||
|
||||
```text
|
||||
GET /gateway/events/{session_id}?last_event_id=123
|
||||
```
|
||||
|
||||
### LangGraph Deep Telemetry
|
||||
|
||||
Foi adicionado `LangGraphDeepTelemetry` com eventos:
|
||||
|
||||
- `langgraph.node.started`
|
||||
- `langgraph.node.completed`
|
||||
- `langgraph.node.failed`
|
||||
- `langgraph.edge.selected`
|
||||
|
||||
Esses eventos são enviados para o Event Bus, Langfuse e OpenTelemetry quando habilitados.
|
||||
|
||||
### Token e Cost Accounting
|
||||
|
||||
Foi adicionado:
|
||||
|
||||
- `TokenUsageCollector`
|
||||
- `CostTracker`
|
||||
- cálculo de `prompt_tokens`, `completion_tokens`, `cached_tokens`, `total_tokens`
|
||||
- cálculo de `cost_usd` e `cost_brl`
|
||||
|
||||
Configuração opcional:
|
||||
|
||||
```env
|
||||
USD_BRL_RATE=5.0
|
||||
MODEL_PRICES_JSON={"openai.gpt-4.1":{"input_per_1m":"2.00","output_per_1m":"8.00"}}
|
||||
```
|
||||
|
||||
### Cache Enterprise
|
||||
|
||||
O cache agora é em cascata:
|
||||
|
||||
```text
|
||||
L1: InMemory
|
||||
L2: Redis, SQLite ou Oracle
|
||||
```
|
||||
|
||||
Configuração:
|
||||
|
||||
```env
|
||||
ENABLE_REDIS_CACHE=true
|
||||
REDIS_URL=redis://localhost:6379/0
|
||||
```
|
||||
|
||||
ou:
|
||||
|
||||
```env
|
||||
CACHE_BACKEND_PROVIDER=oracle
|
||||
```
|
||||
|
||||
### RAG Oracle 23ai
|
||||
|
||||
Foi adicionado `OracleVectorStore`, com suporte a coluna `VECTOR` e `VECTOR_DISTANCE()` quando um embedding provider for conectado.
|
||||
Sem embedding provider, mantém fallback lexical para desenvolvimento local.
|
||||
|
||||
Também foi adicionado `OracleGraphStore` com tabela de arestas, pronto para evoluir para PGQL/Property Graph.
|
||||
|
||||
### Langfuse
|
||||
|
||||
Cada chamada LLM agora gera `generation` com:
|
||||
|
||||
- input
|
||||
- output
|
||||
- model
|
||||
- provider
|
||||
- token usage
|
||||
- cost metadata
|
||||
|
||||
Além disso, spans de workflow, guardrails, judges, RAG, cache, checkpoint, SSE e LangGraph são publicados pelo mesmo Event Bus.
|
||||
|
||||
### Extensões Enterprise Plus
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/README_FIRST_ENTERPRISE_PLUS.md`.
|
||||
|
||||
Esta versão evolui o framework nos quatro blocos solicitados:
|
||||
|
||||
1. **Langfuse Enterprise completo**
|
||||
- `Telemetry.span()` com trace/session/user/metadata/tags.
|
||||
- `Telemetry.generation()` com `usage`, token/cost metadata e compatibilidade Langfuse v2/v3.
|
||||
- `Telemetry.score()` para judges/avaliações.
|
||||
- Eventos arbitrários são registrados como spans seguros para evitar `Unknown observation type` no Langfuse.
|
||||
|
||||
2. **Token/Cost Accounting completo**
|
||||
- `TokenUsageCollector` suporta `prompt_tokens`, `completion_tokens`, `cached_tokens`, `reasoning_tokens` e `total_tokens`.
|
||||
- Tabela de preços por modelo via `MODEL_PRICES_JSON`.
|
||||
- Conversão USD→BRL via `USD_BRL_RATE`.
|
||||
- Persistência em `UsageRepository` e endpoint `/debug/usage`.
|
||||
|
||||
3. **Redis distribuído**
|
||||
- `DistributedCache`: L1 memória + L2 Redis/SQLite/Oracle.
|
||||
- `RedisCache` com `redis.asyncio` quando disponível e fallback sync.
|
||||
- Namespace por `CACHE_KEY_PREFIX`.
|
||||
- Telemetria de cache hit/miss/set/delete.
|
||||
|
||||
4. **Oracle Vector + PGQL reais**
|
||||
- `OracleVectorStore` usa `VECTOR_DISTANCE(..., COSINE)` e `TO_VECTOR()` no Oracle 23ai.
|
||||
- Tentativa automática de criar vector index quando suportado.
|
||||
- `OracleGraphStore` usa tabelas `GRAPH_NODE` e `GRAPH_EDGE`.
|
||||
- Suporte a criação de Property Graph e consulta por `GRAPH_TABLE`/PGQL, com fallback SQL.
|
||||
|
||||
Também foi corrigido o problema de duplicação SSE por replay + fila live usando controle de `max_replayed_id` no `SSEHub.subscribe()`.
|
||||
|
||||
### Testes
|
||||
|
||||
```bash
|
||||
PYTHONPATH=agent_framework/src pytest -q tests/unit
|
||||
```
|
||||
|
||||
Resultado validado nesta geração:
|
||||
|
||||
```text
|
||||
17 passed
|
||||
```
|
||||
|
||||
### Segurança
|
||||
|
||||
Os arquivos `.env` foram higienizados para não conter chaves reais. Configure suas credenciais localmente antes de usar OCI/Langfuse.
|
||||
|
||||
### Delta para padrão FIRST
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/README_FIRST_ENTERPRISE_DELTA.md`.
|
||||
|
||||
Esta versão corrige as prioridades levantadas na comparação com o FIRST:
|
||||
|
||||
1. Oracle Session Repository real
|
||||
2. Oracle Message History real
|
||||
3. Oracle LangGraph Checkpoint Repository real
|
||||
4. LangGraph Deep Telemetry
|
||||
5. Token Accounting
|
||||
6. Cost Accounting
|
||||
7. Session Lock SSE
|
||||
8. Replay Buffer SSE
|
||||
9. KeepAlive SSE
|
||||
10. Recovery por Last-Event-ID
|
||||
11. Redis Provider e Distributed Cache
|
||||
12. Oracle Vector Provider
|
||||
13. Oracle Graph Provider
|
||||
14. RAG Telemetry
|
||||
15. Langfuse Generation Tracking
|
||||
16. OpenTelemetry/Event Bus compatível
|
||||
17. OCI Streaming Exporter preservado
|
||||
|
||||
A lógica de domínio continua genérica; o framework não copia regras específicas de cobrança do FIRST.
|
||||
|
||||
### Operação máxima e contabilização
|
||||
|
||||
> Conteúdo consolidado a partir de `Documentacao/README_MAX_OPERACIONAL.md`.
|
||||
|
||||
Esta versão adiciona os ajustes operacionais que faltavam para aproximar o framework do padrão FIRST em produção.
|
||||
|
||||
### Ajustes incluídos nesta versão
|
||||
|
||||
### 1. Langfuse Enterprise Adapter
|
||||
Novo módulo:
|
||||
|
||||
```text
|
||||
agent_framework/observability/langfuse_enterprise.py
|
||||
```
|
||||
|
||||
Inclui adaptador compatível com SDKs Langfuse v2/v3 para:
|
||||
|
||||
- atualização de trace;
|
||||
- score/avaliação de trace;
|
||||
- prompt registry quando suportado pelo SDK;
|
||||
- isolamento das diferenças de API do Langfuse.
|
||||
|
||||
### 2. Token e Cost Accounting persistente
|
||||
Novo pacote:
|
||||
|
||||
```text
|
||||
agent_framework/billing/
|
||||
```
|
||||
|
||||
Inclui:
|
||||
|
||||
- `UsageRecord`
|
||||
- `SQLiteUsageRepository`
|
||||
- `OracleUsageRepository`
|
||||
- `create_usage_repository(settings)`
|
||||
|
||||
O provider LLM agora registra automaticamente:
|
||||
|
||||
- `prompt_tokens`
|
||||
- `completion_tokens`
|
||||
- `cached_tokens`
|
||||
- `total_tokens`
|
||||
- `cost_usd`
|
||||
- `cost_brl`
|
||||
- `tenant_id`
|
||||
- `agent_id`
|
||||
- `session_id`
|
||||
- `message_id`
|
||||
|
||||
Novo endpoint:
|
||||
|
||||
```http
|
||||
GET /debug/usage
|
||||
GET /debug/usage?tenant_id=default
|
||||
GET /debug/usage?session_id=<id>
|
||||
```
|
||||
|
||||
### 3. RAG Service operacional
|
||||
Novo módulo:
|
||||
|
||||
```text
|
||||
agent_framework/rag/rag_service.py
|
||||
```
|
||||
|
||||
Inclui:
|
||||
|
||||
- `RagService.add_documents()`
|
||||
- `RagService.retrieve()`
|
||||
- `RagResult.as_prompt_context()`
|
||||
- telemetria de latência, quantidade de documentos, top scores e grafo.
|
||||
|
||||
### 4. Configuração nova
|
||||
Variável adicionada:
|
||||
|
||||
```env
|
||||
USAGE_REPOSITORY_PROVIDER=sqlite
|
||||
```
|
||||
|
||||
Valores:
|
||||
|
||||
```text
|
||||
sqlite
|
||||
oracle
|
||||
autonomous
|
||||
```
|
||||
|
||||
### 5. Compatibilidade operacional local
|
||||
Por padrão, a contabilização de uso usa SQLite mesmo que o restante esteja em memória. Assim é possível testar localmente sem Oracle.
|
||||
|
||||
### Teste rápido
|
||||
|
||||
```bash
|
||||
cd agent_template_backend
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
Teste uma mensagem:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/gateway/message \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"channel":"web","payload":{"text":"teste","user_id":"u1","session_id":"s1"}}'
|
||||
```
|
||||
|
||||
Verifique uso/custo:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/debug/usage
|
||||
```
|
||||
|
||||
### Para rodar com padrão mais próximo de produção
|
||||
|
||||
```env
|
||||
SESSION_REPOSITORY_PROVIDER=sqlite
|
||||
MEMORY_REPOSITORY_PROVIDER=sqlite
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=sqlite
|
||||
USAGE_REPOSITORY_PROVIDER=sqlite
|
||||
CACHE_BACKEND_PROVIDER=sqlite
|
||||
VECTOR_STORE_PROVIDER=sqlite
|
||||
ENABLE_LANGFUSE=true
|
||||
LANGFUSE_HOST=http://localhost:3000
|
||||
LANGFUSE_PUBLIC_KEY=...
|
||||
LANGFUSE_SECRET_KEY=...
|
||||
```
|
||||
|
||||
Para Autonomous Database:
|
||||
|
||||
```env
|
||||
SESSION_REPOSITORY_PROVIDER=oracle
|
||||
MEMORY_REPOSITORY_PROVIDER=oracle
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=oracle
|
||||
USAGE_REPOSITORY_PROVIDER=oracle
|
||||
CACHE_BACKEND_PROVIDER=oracle
|
||||
VECTOR_STORE_PROVIDER=oracle
|
||||
GRAPH_STORE_PROVIDER=oracle
|
||||
ADB_USER=...
|
||||
ADB_PASSWORD=...
|
||||
ADB_DSN=...
|
||||
ADB_WALLET_LOCATION=...
|
||||
ADB_TABLE_PREFIX=AGENTFW
|
||||
```
|
||||
|
||||
### Validação complementar do supervisor
|
||||
|
||||
> Conteúdo consolidado a partir de `docs/docs_GLOBAL_SUPERVISOR_VALIDATION.txt`.
|
||||
|
||||
VALIDAÇÃO - GLOBAL SUPERVISOR
|
||||
|
||||
Alterações implementadas:
|
||||
|
||||
1. Framework
|
||||
- agent_framework.global_supervisor.models
|
||||
- agent_framework.global_supervisor.config
|
||||
- agent_framework.global_supervisor.session_store
|
||||
- agent_framework.global_supervisor.router
|
||||
- agent_framework.global_supervisor.client
|
||||
|
||||
2. Novo serviço
|
||||
- agent_gateway/app/main.py
|
||||
- agent_gateway/app/settings.py
|
||||
- agent_gateway/config/backends.yaml
|
||||
- agent_gateway/README.md
|
||||
- agent_gateway/Dockerfile
|
||||
- agent_gateway/docs/ARQUITETURA_GLOBAL_SUPERVISOR.md
|
||||
|
||||
3. Docker Compose
|
||||
- serviço agent-gateway adicionado na porta 8010.
|
||||
|
||||
Validações executadas:
|
||||
|
||||
- python3 -m compileall -q agent_framework/src/agent_framework/global_supervisor agent_gateway/app
|
||||
Resultado: OK
|
||||
|
||||
- Smoke test do roteamento híbrido:
|
||||
Entrada 1: "Minha fatura veio alta" -> contas
|
||||
Entrada 2: "e esse valor?" na mesma session_id -> contas por active_backend
|
||||
Resultado: OK
|
||||
|
||||
- Smoke test de import do app FastAPI:
|
||||
from app.main import app, registry, router
|
||||
Resultado: OK
|
||||
|
||||
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:
|
||||
|
||||
- `Documentacao/README_FIRST_READY.md`
|
||||
- `Documentacao/README_FIRST_ENTERPRISE_PLUS.md`
|
||||
- `Documentacao/README_FIRST_ENTERPRISE_DELTA.md`
|
||||
- `Documentacao/README_MAX_OPERACIONAL.md`
|
||||
- `docs/docs_GLOBAL_SUPERVISOR_VALIDATION.txt`
|
||||
|
||||
### Regra de manutenção
|
||||
|
||||
Novas correções ou evoluções deste tema devem atualizar este documento consolidado. Release notes podem continuar existindo como histórico, mas não devem ser necessárias para compreender ou implementar a funcionalidade.
|
||||
@@ -0,0 +1,188 @@
|
||||
# 12 — Feedback de Guardrails de Entrada e Semântica de Turno Bloqueado
|
||||
|
||||
## Objetivo
|
||||
|
||||
Este documento descreve como o `AgentWorkflow`, implementado em `app/workflows/agent_graph.py`, deve tratar um turno interrompido por guardrail de entrada sem transformar toda interrupção em uma mensagem genérica de “regra de segurança”.
|
||||
|
||||
A regra central é separar três coisas:
|
||||
|
||||
1. **decisão técnica do guardrail**, usada pelo runtime e pela observabilidade;
|
||||
2. **mensagem pública ao usuário**, adequada ao tipo de bloqueio ou necessidade de esclarecimento;
|
||||
3. **estado do turno**, que não pode carregar routing, tools ou judges de um turno que foi interrompido antes dessas etapas.
|
||||
|
||||
## Fluxo esperado
|
||||
|
||||
```text
|
||||
mensagem do usuário
|
||||
↓
|
||||
input_guardrails
|
||||
↓
|
||||
allowed?
|
||||
├─ sim → routing → tools/agente → composição → output_guardrails
|
||||
│
|
||||
└─ não
|
||||
↓
|
||||
classificar tratamento público
|
||||
↓
|
||||
limpar estado de routing/tools/judges do turno
|
||||
↓
|
||||
construir mensagem pública segura
|
||||
↓
|
||||
output_guardrails
|
||||
↓
|
||||
persistência/resposta
|
||||
```
|
||||
|
||||
Um guardrail de entrada bloqueante deve ser decidido **antes de qualquer tool com efeito colateral**.
|
||||
|
||||
## `reason` interno não é a resposta ao usuário
|
||||
|
||||
O campo `reason` deve permanecer disponível para logs, traces, eventos e diagnóstico. Ele não deve ser exibido literalmente quando puder revelar mecanismo interno ou quando a frase técnica não for apropriada ao usuário final.
|
||||
|
||||
Exemplo:
|
||||
|
||||
```text
|
||||
COER.reason = "fala incompreensível ou negação ambígua na transcrição"
|
||||
```
|
||||
|
||||
A resposta pública pode ser:
|
||||
|
||||
```text
|
||||
"Não consegui entender sua última mensagem porque ela parece incompleta ou ambígua. Pode reformular ou completar o que você quis dizer?"
|
||||
```
|
||||
|
||||
## Tratamento por tipo de guardrail
|
||||
|
||||
O comportamento exato continua configurável, mas a semântica esperada é:
|
||||
|
||||
| Guardrail | Tratamento público recomendado |
|
||||
|---|---|
|
||||
| `COER` | solicitar esclarecimento/reformulação; não tratar ambiguidade como incidente de segurança |
|
||||
| `PINJ` | bloquear com mensagem segura sem explicar o mecanismo interno |
|
||||
| `DLEX_IN` | bloquear ou orientar reformulação sem expor dado interno/sensível |
|
||||
| `INPUT_SIZE` | solicitar redução da entrada |
|
||||
| `TOX` | aplicar a política configurada para conteúdo inadequado |
|
||||
| `CMP` | responder segundo a política de compliance |
|
||||
| desconhecido | usar fallback seguro e genérico |
|
||||
|
||||
## Limpeza do estado do turno bloqueado
|
||||
|
||||
Quando o input é bloqueado antes do routing, o estado final daquele turno não deve reutilizar dados residuais do turno anterior.
|
||||
|
||||
No mínimo, o workflow deve evitar apresentar como atuais:
|
||||
|
||||
```text
|
||||
route_decision
|
||||
mcp_tools
|
||||
mcp_results
|
||||
judge_results
|
||||
```
|
||||
|
||||
O metadata deve deixar explícito que o turno foi interrompido no estágio de input guardrails.
|
||||
|
||||
Isso evita um diagnóstico falso como:
|
||||
|
||||
```text
|
||||
route = blocked
|
||||
mcp_results = [tool executada]
|
||||
```
|
||||
|
||||
quando a tool na realidade pertence ao turno anterior.
|
||||
|
||||
## Mensagem pública também passa pelos guardrails de saída
|
||||
|
||||
Uma resposta criada em função de um bloqueio de entrada ainda é uma saída do agente. Portanto ela deve seguir o mesmo pipeline de validação de saída antes de chegar ao usuário.
|
||||
|
||||
Isso permite que `DLEX_OUT`, `PINJ`, `TOXOUT`, Output Supervisor e outras políticas removam ou sanitizem informação que não deva ser apresentada.
|
||||
|
||||
## COER, `expected_input` e `reprompt` em workflows pausados
|
||||
|
||||
Quando existe um `expected_input.semantic_classifier` ativo, o framework pode delegar a decisão semântica ao contrato do workflow em vez de tratar imediatamente uma entrada ambígua como bloqueio genérico de `COER`. Nesse modo, o guardrail pode aparecer como permitido/delegado, por exemplo com mecanismo `expected_input_semantic_classifier`.
|
||||
|
||||
O workflow continua responsável por diferenciar:
|
||||
|
||||
```text
|
||||
resposta válida e compreensível -> classe válida / option_action
|
||||
resposta sem significado suficiente -> unmatched_value -> reprompt
|
||||
```
|
||||
|
||||
Exemplo de configuração:
|
||||
|
||||
```yaml
|
||||
expected_input:
|
||||
allowed_values: [SIM, NAO, CONTINUAR]
|
||||
reprompt: "Não entendi. Essa explicação resolveu sua dúvida? Responda sim ou não."
|
||||
semantic_classifier:
|
||||
enabled: true
|
||||
unmatched_value: OUTRO
|
||||
option_actions:
|
||||
CONTINUAR:
|
||||
action: contextual_reentry
|
||||
```
|
||||
|
||||
Nesse contrato, `CONTINUAR` não significa "qualquer coisa que não seja SIM ou NAO". Ele representa uma fala **coerente e contextual** que continua o assunto. Uma entrada como `ano`, sem significado suficiente para a pergunta pendente, deve retornar o sentinela `OUTRO` e produzir o `reprompt`, mantendo o mesmo workflow pausado.
|
||||
|
||||
`OUTRO` não deve ser adicionado a `allowed_values` nem enviado ao runtime como valor de resume. A descrição completa do contrato está em [03 — Workflows Transacionais e Estado](./03_transaction_workflows_and_state.md#expected_inputsemantic_classifierunmatched_value-e-reprompt).
|
||||
|
||||
## Relação com `agent_graph.py`
|
||||
|
||||
Esta feature é responsabilidade da orquestração do template, porque define a precedência entre nós do grafo e o estado do turno.
|
||||
|
||||
Ao alterar `app/workflows/agent_graph.py`, preserve estas invariantes:
|
||||
|
||||
- `input_guardrails` antecede routing/tools;
|
||||
- um bloqueio de input não executa ação transacional depois do bloqueio;
|
||||
- resposta pública não é o `reason` bruto do guardrail;
|
||||
- estado residual de routing/tools/judges não sobrevive como resultado do turno bloqueado;
|
||||
- a resposta pública passa por `output_guardrails` antes da persistência/resposta.
|
||||
|
||||
A mesma semântica deve ser mantida nos templates oficiais e nas variantes equivalentes em `Tuning-Performance`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### O usuário recebe “Não consegui seguir com essa mensagem por regra de segurança” para uma frase apenas incompleta
|
||||
|
||||
Verifique:
|
||||
|
||||
1. qual guardrail retornou `allowed=false`;
|
||||
2. se `COER` está sendo tratado como esclarecimento e não como bloqueio genérico;
|
||||
3. se o caminho de bloqueio usa uma mensagem pública específica;
|
||||
4. se o fallback genérico está sendo usado somente quando não existe tratamento específico.
|
||||
|
||||
### O metadata mostra tool executada mesmo com `route=blocked`
|
||||
|
||||
Verifique se o ramo de bloqueio limpa o estado transitório do turno antes de retornar a resposta. Confirme também se a tool não foi executada no mesmo turno antes do guardrail de entrada.
|
||||
|
||||
### A mensagem de bloqueio expõe detalhes internos
|
||||
|
||||
Não use `reason` diretamente como texto público. Gere a mensagem pública e deixe o `reason` apenas em observabilidade.
|
||||
|
||||
### A resposta de bloqueio ignora guardrails de saída
|
||||
|
||||
Verifique a aresta do grafo. O fluxo esperado é:
|
||||
|
||||
```text
|
||||
input_guardrails bloqueou
|
||||
→ construir resposta pública
|
||||
→ output_guardrails
|
||||
→ persist
|
||||
```
|
||||
|
||||
não:
|
||||
|
||||
```text
|
||||
input_guardrails bloqueou
|
||||
→ persist
|
||||
```
|
||||
|
||||
## Testes de regressão recomendados
|
||||
|
||||
Cubra pelo menos:
|
||||
|
||||
- `COER=false` gera solicitação de esclarecimento, não mensagem genérica de segurança;
|
||||
- com `expected_input.semantic_classifier.unmatched_value`, entrada incoerente/sem significado suficiente aciona `reprompt` e mantém o workflow `PAUSED`;
|
||||
- uma entrada compreensível fora de SIM/NAO pode seguir `CONTINUAR -> contextual_reentry` sem ser confundida com uma entrada incoerente;
|
||||
- ramo bloqueado não conserva `mcp_results`/routing de turno anterior;
|
||||
- nenhuma tool transacional é executada depois de um bloqueio de input;
|
||||
- mensagem pública passa pelos guardrails de saída;
|
||||
- guardrail desconhecido ainda possui fallback seguro.
|
||||
183
docs/developer/pt/13_workflows_transacionais_avancados.md
Normal file
183
docs/developer/pt/13_workflows_transacionais_avancados.md
Normal file
@@ -0,0 +1,183 @@
|
||||
### 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.
|
||||
|
||||
### 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
|
||||
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`.
|
||||
122
docs/developer/pt/14_routing_parametros_e_estado_avancados.md
Normal file
122
docs/developer/pt/14_routing_parametros_e_estado_avancados.md
Normal 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`.
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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`.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
169
docs/developer/pt/INDEX_DEVELOPER_GUIDE.md
Normal file
169
docs/developer/pt/INDEX_DEVELOPER_GUIDE.md
Normal file
@@ -0,0 +1,169 @@
|
||||
|
||||
### Índice de Desenvolvimento — Agent Framework OCI
|
||||
|
||||
### Como usar esta documentação
|
||||
|
||||
A documentação possui três níveis claros:
|
||||
|
||||
1. **Tutorial principal:** [`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](./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.
|
||||
|
||||
Se você está começando um novo agente, comece pelo `README.md`.
|
||||
|
||||
Se algo não está funcionando, use **Buscar pelo problema** abaixo.
|
||||
|
||||
### Buscar pelo problema
|
||||
|
||||
| Problema / dúvida | O que normalmente está envolvido | Onde procurar |
|
||||
|---|---|---|
|
||||
| O framework não encontra o agente/intenção correta | routing, intents, threshold, modo determinístico/LLM | [Routing e Stickiness](./02_routing_stickiness_and_intent_shift.md) |
|
||||
| O agente fica preso no mesmo assunto e não troca de intent | route stickiness, intent shift, handoff | [Routing e Stickiness](./02_routing_stickiness_and_intent_shift.md) |
|
||||
| Uma resposta que deveria preencher parâmetro é interpretada como novo intent | precedência transacional, parameter extraction | [Workflows Transacionais](./03_transaction_workflows_and_state.md) |
|
||||
| A transação fica pedindo o mesmo parâmetro | estado transacional, extractor, schema | [Workflows Transacionais](./03_transaction_workflows_and_state.md) e [MCP/Tools](./04_mcp_integration_tools_and_policies.md) |
|
||||
| A confirmação “sim/não” não continua o fluxo | confirmation state, transaction state | [Workflows Transacionais](./03_transaction_workflows_and_state.md) |
|
||||
| Uma fala inválida durante um `expected_input` vira `CONTINUAR` em vez de pedir esclarecimento | `semantic_classifier.unmatched_value`, `reprompt`, `contextual_reentry`, COER delegado | [Workflows Transacionais](./03_transaction_workflows_and_state.md) e [Feedback de Guardrails de Entrada](./12_input_guardrail_feedback_and_blocked_turns.md) |
|
||||
| Uma transação encerrada reaparece | checkpoint antigo versus estado transacional ativo | [Workflows Transacionais](./03_transaction_workflows_and_state.md) e [LTM/Checkpoint](./08_long_term_memory_and_checkpoint.md) |
|
||||
| O sistema diz que executou algo, mas não existe evidência | MCP result, estado `COMPLETED`, judges transacionais | [Workflows Transacionais](./03_transaction_workflows_and_state.md) e [Guardrails/Judges](./06_guardrails_judges_and_transaction_evaluation.md) |
|
||||
| Uma tool não aparece ou não é encontrada | `tools.yaml`, catálogo MCP, discovery | [MCP/Tools](./04_mcp_integration_tools_and_policies.md) |
|
||||
| MCP Server não aparece no catálogo | registration, manifest/discovery, MCP Gateway | [MCP/Tools](./04_mcp_integration_tools_and_policies.md) e [Gateways](./05_agent_gateway_mcp_gateway_and_auth.md) |
|
||||
| Parâmetros enviados à tool estão errados | schema, mapping, BusinessContext, extractor | [MCP/Tools](./04_mcp_integration_tools_and_policies.md) |
|
||||
| Preciso chamar a mesma operação para vários produtos/pedidos/itens | contrato escalar ou coleção, pre-validation, confirmação única, resultado por item | [MCP Multi-item](../../MCP_MULTI_ITEM_DEVELOPER_GUIDE.md) e [MCP/Tools](./04_mcp_integration_tools_and_policies.md) |
|
||||
| Uma operação transacional executa sem confirmação | tool policy, `require_confirmation` | [MCP/Tools](./04_mcp_integration_tools_and_policies.md) |
|
||||
| Uma busca por nome exige correspondência exata demais | extração/mapeamento de parâmetros e lógica do agente | [MCP/Tools](./04_mcp_integration_tools_and_policies.md) |
|
||||
| Recebo 401 entre gateway/backend/MCP | Basic Auth, credenciais por hop | [Gateways e Auth](./05_agent_gateway_mcp_gateway_and_auth.md) |
|
||||
| Preciso decidir se algo pertence ao framework ou ao agente | boundary core/agente | [Arquitetura e Conceitos](./01_architecture_and_concepts.md) |
|
||||
| Guardrail específico de um agente está quebrando outro | extensibilidade, imports de domínio no core | [Guardrails e Judges](./06_guardrails_judges_and_transaction_evaluation.md) |
|
||||
| Uma frase incompleta recebe mensagem genérica de “regra de segurança” | feedback de input guardrail, `COER`, blocked-turn state | [Feedback de Guardrails de Entrada](./12_input_guardrail_feedback_and_blocked_turns.md) |
|
||||
| `route=blocked` aparece junto com tools/resultados de outro turno | limpeza de estado do turno bloqueado | [Feedback de Guardrails de Entrada](./12_input_guardrail_feedback_and_blocked_turns.md) |
|
||||
| Workflow conclui e gera protocolo, mas a resposta final vira mensagem de segurança | `expected_protocols`, `CMP`, `DLEX_OUT`, ordem de `output_guardrails` | [Guardrails e Judges](./06_guardrails_judges_and_transaction_evaluation.md) |
|
||||
| Judge não roda em uma transação | sampling, `always_run_for_transactional`, sinais transacionais | [Guardrails e Judges](./06_guardrails_judges_and_transaction_evaluation.md) |
|
||||
| Groundedness está avaliando sem contexto correto | RAG context, MCP evidence, judge inputs | [RAG/Grounding](./07_rag_business_context_and_grounding.md) |
|
||||
| RAG não encontra conteúdo | provider, ingestão, embeddings, configuração | [RAG/Grounding](./07_rag_business_context_and_grounding.md) |
|
||||
| Não sei se usar RAG, memória ou tool | separação de responsabilidades | [Arquitetura e Conceitos](./01_architecture_and_concepts.md) e [RAG/Grounding](./07_rag_business_context_and_grounding.md) |
|
||||
| Memória desaparece ao trocar de sessão | LTM versus conversation memory | [LTM e Checkpoint](./08_long_term_memory_and_checkpoint.md) |
|
||||
| Memória de um cliente/agente aparece em outro | identity key, tenant/agent/customer isolation | [LTM e Checkpoint](./08_long_term_memory_and_checkpoint.md) |
|
||||
| Preciso recuperar `reasoning_content` | `ainvoke_response()` | [LLM Rich Response](./09_llm_rich_response_reasoning.md) |
|
||||
| `reasoning_content` vem `None` | provider/model não expõe o campo | [LLM Rich Response](./09_llm_rich_response_reasoning.md) |
|
||||
| Há chamadas LLM desnecessárias | routing determinístico, concorrência, cache | [Performance](./10_performance_cache_and_async_runtime.md) |
|
||||
| Há deadlock ou espera entre event loops | cross-loop sequence/runtime | [Performance](./10_performance_cache_and_async_runtime.md) |
|
||||
| Logs/traces não correlacionam o mesmo agente | labels, IDs e mapeamento de observabilidade | [Observabilidade](./11_observability_persistence_and_operational_readiness.md) |
|
||||
| Sequence está interferindo no processamento | implementação assíncrona de sequência | [Observabilidade](./11_observability_persistence_and_operational_readiness.md) e [Performance](./10_performance_cache_and_async_runtime.md) |
|
||||
| Um exemplo antigo não compila | documentação histórica versus API atual | [Validação README x Código](./VALIDATION_README_ALIGNMENT.md) |
|
||||
| Preciso criar um agente novo do zero | fluxo completo | [`README.md`](../../../README.md) |
|
||||
| Preciso saber onde colocar uma nova feature | arquitetura e boundaries | [Arquitetura e Conceitos](./01_architecture_and_concepts.md) |
|
||||
|
||||
### Buscar pela funcionalidade
|
||||
|
||||
### [01 — Arquitetura e Conceitos](./01_architecture_and_concepts.md)
|
||||
|
||||
**O que é:** visão dos componentes, contratos e limites de responsabilidade.
|
||||
|
||||
**Use quando:** precisar entender a plataforma, decidir onde implementar algo ou evitar acoplamento entre core e agente.
|
||||
|
||||
### [02 — Routing, Route Stickiness e Intent Shift](./02_routing_stickiness_and_intent_shift.md)
|
||||
|
||||
**O que é:** referência completa de descoberta de agente/intent, stickiness, handoff e mudança de intenção.
|
||||
|
||||
**Use quando:** a mensagem cai no agente errado, não troca de intent ou perde continuidade.
|
||||
|
||||
### [03 — Workflows Transacionais e Estado](./03_transaction_workflows_and_state.md)
|
||||
|
||||
**O que é:** ciclo transacional multi-turno, estados, confirmação, pausa/retomada, `expected_input`, `semantic_classifier`, `unmatched_value`/`reprompt` e evidência operacional.
|
||||
|
||||
**Use quando:** há loops, confirmações incorretas, retomadas erradas, `CONTINUAR`/`contextual_reentry` indevido, `reprompt` ausente ou operações críticas.
|
||||
|
||||
### [04 — MCP, Tools, Policies e Extração de Parâmetros](./04_mcp_integration_tools_and_policies.md)
|
||||
|
||||
**O que é:** referência de tools, MCP Servers, mappings, policies, parameter extraction e chamadas multi-item.
|
||||
|
||||
**Use quando:** integração/execução de tool está incorreta, precisa ser criada ou deve processar listas de itens sem lógica paralela no agente.
|
||||
|
||||
### [05 — Agent Gateway, MCP Gateway e Autenticação](./05_agent_gateway_mcp_gateway_and_auth.md)
|
||||
|
||||
**O que é:** responsabilidades dos gateways, governança e autenticação entre componentes.
|
||||
|
||||
**Use quando:** houver problema de entrada, catálogo, autorização, 401 ou deployment dos gateways.
|
||||
|
||||
### [06 — Guardrails, Judges e Avaliação Transacional](./06_guardrails_judges_and_transaction_evaluation.md)
|
||||
|
||||
**O que é:** validações nativas/externas, judges, grounding e regras para turnos transacionais.
|
||||
|
||||
**Use quando:** uma validação bloqueia, não roda ou produz avaliação incorreta.
|
||||
|
||||
### [07 — RAG, BusinessContext e Grounding](./07_rag_business_context_and_grounding.md)
|
||||
|
||||
**O que é:** providers de RAG, contexto recuperado, BusinessContext e grounding.
|
||||
|
||||
**Use quando:** conhecimento recuperado não chega corretamente ao agente/judge.
|
||||
|
||||
### [08 — Long-Term Memory e Checkpoint](./08_long_term_memory_and_checkpoint.md)
|
||||
|
||||
**O que é:** memória durável, memória conversacional, identidade e snapshots de estado.
|
||||
|
||||
**Use quando:** contexto some, vaza ou workflow retoma do lugar errado.
|
||||
|
||||
### [09 — LLM Rich Response e reasoning_content](./09_llm_rich_response_reasoning.md)
|
||||
|
||||
**O que é:** resposta estruturada de inferência além do `str` retornado por `ainvoke()`.
|
||||
|
||||
**Use quando:** consumidores precisam de metadados, usage ou reasoning disponibilizado pelo provider.
|
||||
|
||||
### [10 — Performance, Cache e Runtime Assíncrono](./10_performance_cache_and_async_runtime.md)
|
||||
|
||||
**O que é:** otimizações de concorrência, cache, LLM e event loops.
|
||||
|
||||
**Use quando:** houver latência evitável, processamento serial ou deadlock.
|
||||
|
||||
### [11 — Observabilidade, Persistência e Prontidão Operacional](./11_observability_persistence_and_operational_readiness.md)
|
||||
|
||||
**O que é:** correlação, eventos, labels, sequence, persistência e diagnóstico.
|
||||
|
||||
**Use quando:** for necessário provar o caminho executado ou diagnosticar produção.
|
||||
|
||||
### [12 — Feedback de Guardrails de Entrada e Turnos Bloqueados](./12_input_guardrail_feedback_and_blocked_turns.md)
|
||||
|
||||
**O que é:** tratamento público de bloqueios de input, limpeza do estado do turno e validação da mensagem gerada pelos guardrails de saída.
|
||||
|
||||
**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 → workflows → idempotência → execução → testes → gateways → memória → RAG → operação`.
|
||||
|
||||
### Manutenção
|
||||
|
||||
Não crie outro tutorial paralelo ao `README.md`.
|
||||
|
||||
Ao evoluir uma feature:
|
||||
|
||||
- atualize o README somente se o fluxo normal de desenvolvimento mudou;
|
||||
- atualize o manual especializado com comportamento, configuração, exemplos e troubleshooting;
|
||||
- atualize SPECs se o contrato mudou;
|
||||
- mantenha release notes como histórico, não como única documentação atual.
|
||||
84
docs/developer/pt/VALIDATION_README_ALIGNMENT.md
Normal file
84
docs/developer/pt/VALIDATION_README_ALIGNMENT.md
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
### Validação de Alinhamento da Documentação
|
||||
|
||||
### Objetivo
|
||||
|
||||
Registrar como a documentação desta versão foi reorganizada e quais fontes devem ser usadas pelo desenvolvedor.
|
||||
|
||||
### Decisão estrutural
|
||||
|
||||
O `README.md` da raiz é o **único tutorial principal ponta a ponta**.
|
||||
|
||||
O antigo `01_architecture_and_agent_development.md` foi removido porque repetia grande parte do README, mas não todo ele. Isso criava ambiguidade: dois documentos aparentavam ensinar a mesma coisa, porém um era parcial.
|
||||
|
||||
A nova estrutura substitui esse arquivo por `01_architecture_and_concepts.md`, que contém apenas arquitetura, conceitos, responsabilidades e critérios de extensão.
|
||||
|
||||
### Validação de `README_old2.md`
|
||||
|
||||
`Documentacao/README_old2.md` permanece útil como histórico, mas não é fonte principal para desenvolvimento.
|
||||
|
||||
Foram encontradas evoluções posteriores no README atual e no código, incluindo:
|
||||
|
||||
- SPECs/SDDs;
|
||||
- configuração mais completa de `llm_profiles.yaml`;
|
||||
- Channel Gateway e contratos canônicos;
|
||||
- `memory` e `summary_memory` no ciclo atual do agente;
|
||||
- `prepare_memory_context()` e `build_messages()`;
|
||||
- `RuntimeContext`;
|
||||
- `normalize_tools_by_intent()`;
|
||||
- `build_tool_arguments()`;
|
||||
- `execute_tools_for_intent()`;
|
||||
- helpers de estado transacional;
|
||||
- respostas MCP diretas;
|
||||
- evolução de gateways, RAG, memória e políticas.
|
||||
|
||||
### Correção aplicada ao README principal
|
||||
|
||||
Foi corrigido no pacote gerado o typo:
|
||||
|
||||
```python
|
||||
from app.agents.financeiro_agent import FinanceirotAgent
|
||||
```
|
||||
|
||||
para:
|
||||
|
||||
```python
|
||||
from app.agents.financeiro_agent import FinanceiroAgent
|
||||
```
|
||||
|
||||
A classe correta é confirmada pelo código e pelo restante da documentação.
|
||||
|
||||
### APIs confirmadas na implementação atual
|
||||
|
||||
```python
|
||||
AgentRuntimeMixin.get_runtime_context()
|
||||
AgentRuntimeMixin.normalize_tools_by_intent()
|
||||
AgentRuntimeMixin.build_tool_arguments()
|
||||
AgentRuntimeMixin.execute_tools_for_intent()
|
||||
AgentRuntimeMixin.prepare_memory_context()
|
||||
AgentRuntimeMixin.build_messages()
|
||||
AgentRuntimeMixin.transaction_state_patch()
|
||||
AgentRuntimeMixin.transaction_clarification_message()
|
||||
AgentRuntimeMixin.transaction_confirmation_message()
|
||||
AgentRuntimeMixin.build_direct_mcp_answer()
|
||||
```
|
||||
|
||||
### Ordem de confiança
|
||||
|
||||
1. código da versão;
|
||||
2. README principal da mesma versão;
|
||||
3. SPECs/SDDs;
|
||||
4. manuais especializados;
|
||||
5. release notes;
|
||||
6. documentos `README_old*`.
|
||||
|
||||
### Regra de manutenção futura
|
||||
|
||||
Uma evolução de feature deve atualizar:
|
||||
|
||||
1. o README principal, **somente se alterar o caminho normal de desenvolvimento**;
|
||||
2. o manual especializado da feature, com detalhes técnicos, comportamento, configuração e troubleshooting;
|
||||
3. a SPEC, quando houver mudança de contrato;
|
||||
4. release note, quando for necessário registrar a mudança histórica.
|
||||
|
||||
Não crie um novo “manual principal” para uma feature. Não mantenha correções funcionais permanentemente apenas em release notes.
|
||||
Reference in New Issue
Block a user