Ajustes conforme relatorio de testes 2026-08-27
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
|
||||
### 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.
|
||||
|
||||
### 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)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,693 @@
|
||||
|
||||
### 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/`
|
||||
|
||||
### 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`.
|
||||
|
||||
### 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.
|
||||
@@ -0,0 +1,826 @@
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
### 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.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,292 @@
|
||||
|
||||
### 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
|
||||
|
||||
> Conteúdo consolidado a partir de `docs/EXTERNAL_GUARDRAILS_JUDGES.md`.
|
||||
|
||||
`agent_framework_oci` supports agent-owned guardrails and judges without importing domain code into the core.
|
||||
|
||||
```yaml
|
||||
output:
|
||||
- code: ACME_POLICY
|
||||
type: external
|
||||
class: app.extensions.guardrails:AcmePolicyRail
|
||||
```
|
||||
|
||||
```yaml
|
||||
judges:
|
||||
- name: acme_quality
|
||||
type: external
|
||||
class: app.extensions.judges:AcmeQualityJudge
|
||||
threshold: 0.7
|
||||
```
|
||||
|
||||
Native entries remain unchanged. External synchronous `evaluate()` methods execute in worker threads via `asyncio.to_thread`; asynchronous methods execute concurrently on the framework event loop. Judges run concurrently with `asyncio.gather`, preserving YAML result order. Agent plugins should reuse the LLM supplied by the framework rather than instantiate a separate provider.
|
||||
|
||||
The core must not reference a concrete agent package, company, product, telecom identifier or domain-specific policy. Domain-specific variants belong to the agent and should receive distinct public codes/names.
|
||||
|
||||
### Compatibility rule
|
||||
Domain policies must not be replaced by cosmetically generic text inside the core while losing the original policy. The generic core implementation and the agent-specific implementation may coexist; the embedding agent explicitly selects its own code/name in YAML.
|
||||
|
||||
Legacy business validators should migrate to the agent domain. A temporary compatibility shim is acceptable for old imports, but new application code must import the agent-owned implementation.
|
||||
|
||||
### 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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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,685 @@
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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,157 @@
|
||||
# 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.
|
||||
|
||||
## 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;
|
||||
- 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.
|
||||
143
agent_framework_oci/docs/developer/pt/INDEX_DEVELOPER_GUIDE.md
Normal file
143
agent_framework_oci/docs/developer/pt/INDEX_DEVELOPER_GUIDE.md
Normal file
@@ -0,0 +1,143 @@
|
||||
|
||||
### Í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 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) |
|
||||
| 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 e evidência operacional.
|
||||
|
||||
**Use quando:** há loops, confirmações incorretas, retomadas erradas 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 e parameter extraction.
|
||||
|
||||
**Use quando:** integração/execução de tool está incorreta ou precisa ser criada.
|
||||
|
||||
### [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.
|
||||
|
||||
### Tutorial principal
|
||||
|
||||
[`README.md`](../../../README.md) continua sendo a referência para o passo a passo completo:
|
||||
|
||||
`arquitetura → configuração → criação do agente → registro → estado → routing → tools → MCP → identidade → execução → testes → gateways → memória → RAG`.
|
||||
|
||||
### 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.
|
||||
@@ -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