diff --git a/README.md b/README.md index 37427cf0..9fe147f4 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,7 @@ O framework deve concentrar os motores reutilizáveis: - Telemetria Langfuse/OpenTelemetry. - Analytics IC/NOC/GRL. - MCP Tool Router. +- Planejamento multi-intent e fila persistente de `pending_topics`. - Cache. - RAG genérico. @@ -230,6 +231,8 @@ Input Guardrails ↓ Enterprise Router ou Supervisor ↓ +Planejamento multi-intent / pending_topics + ↓ Agente especializado ↓ MCP Tool Router / RAG / Cache / LLM @@ -11486,6 +11489,8 @@ O conteúdo desta pasta deve ser tratado como uma extensão adicional do framewo | Um exemplo antigo não compila | documentação histórica versus API atual | [Validação README x Código](docs/developer/pt/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](docs/developer/pt/01_architecture_and_concepts.md) | +| Duas solicitações na mesma mensagem acionam apenas uma intent | decomposição multi-intent, `pending_topics`, allowlist de tools | [Planejamento Multi-Intent](docs/features/pt-BR/16_multi_intent_planning.md) | +| O agent secundário repete a transação ou confirmação primária | isolamento de `active_transaction`, `mcp_tools` e estado operacional | [Planejamento Multi-Intent](docs/features/pt-BR/16_multi_intent_planning.md) | ### 34. Funcionalidades Avançadas @@ -11561,9 +11566,31 @@ O conteúdo desta pasta deve ser tratado como uma extensão adicional do framewo **Use quando:** um `COER`/guardrail de entrada gera mensagem genérica, `route=blocked` carrega resultados antigos ou há dúvida sobre a precedência entre input guardrails, routing e tools. +### Guias de features implementadas + +Os manuais `01` a `12` acima organizam os assuntos por área de desenvolvimento. Os guias abaixo documentam cada feature executável individualmente: + +1. [Autenticação](docs/features/pt-BR/01_authentication.md) +2. [Workflow Transacional Determinístico](docs/features/pt-BR/02_deterministic_transactional_workflow.md) +3. [Composição por LLM Solicitada pelo Domínio](docs/features/pt-BR/03_domain_requested_llm_composition.md) +4. [RAG Solicitado pelo Domínio](docs/features/pt-BR/04_domain_requested_rag.md) +5. [Memória de Longo Prazo](docs/features/pt-BR/05_long_term_memory.md) +6. [Regressão Offline de Workflow](docs/features/pt-BR/06_offline_workflow_regression.md) +7. [Pause e Resume de Workflow](docs/features/pt-BR/07_pause_resume_workflow.md) +8. [Route Stickiness](docs/features/pt-BR/08_route_stickiness.md) +9. [Replay em Interrupções de Voz](docs/features/pt-BR/09_voice_interruption_replay.md) +10. [Recuperação de Erro em Workflow](docs/features/pt-BR/10_workflow_error_recovery.md) +11. [Clarificação](docs/features/pt-BR/11_clarification.md) +12. [Idempotência Durável](docs/features/pt-BR/12_durable_idempotency.md) +13. [Estados Transacionais Dinâmicos](docs/features/pt-BR/13_dynamic_transaction_states.md) +14. [Replay Após Finalização](docs/features/pt-BR/14_post_finalization_replay.md) +15. [Guardrails de Retrieval e Tools](docs/features/pt-BR/15_retrieval_tool_guardrails.md) +16. [Planejamento Multi-Intent e Pending Topics](docs/features/pt-BR/16_multi_intent_planning.md) + +O [índice de features em português](docs/features/pt-BR/README.md) deve ser atualizado sempre que um novo guia for adicionado. + ### 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`. - diff --git a/README_en.md b/README_en.md index 38df2bf4..b877ea44 100644 --- a/README_en.md +++ b/README_en.md @@ -126,7 +126,7 @@ Details the evaluation and certification framework, including evaluation archite Defines the platform operational readiness and SRE model, including managed components, health checks, readiness, SLOs, metrics, dashboards, alerts, runbooks, incident management, capacity planning, and production checklists. -### [IMPORTANT: Disclaimer Auth and Security](specs/Disclaimer%20Auth%20and%20Security_EN.md) +### [IMPORTANT: Disclaimer Auth and Security](specs/Disclaimer_Auth_and_Security_EN.md) Oracle Cloud Infrastructure Security Best Practice Recommendations. @@ -186,6 +186,7 @@ The framework should concentrate the reusable engines: - Langfuse/OpenTelemetry Telemetry. - Analytics IC/NOC/GRL. - MCP Tool Router. +- Multi-intent planning and the persistent `pending_topics` queue. - Cache. - Generic RAG. @@ -231,6 +232,8 @@ Input Guardrails ↓ Enterprise Router or Supervisor ↓ +Multi-intent planning / pending_topics +↓ Specialized agent ↓ MCP Tool Router / RAG / Cache / LLM @@ -11388,6 +11391,8 @@ The content of this folder should be treated as an additional framework extensio | Logs/traces do not correlate the same agent | labels, IDs, observability mapping | [Observability](docs/developer/en/11_observability_persistence_and_operational_readiness.md) | | Historical example no longer compiles | stale docs vs current API | [README Alignment Validation](docs/developer/en/VALIDATION_README_ALIGNMENT.md) | | Need to create a new agent from scratch | complete flow | [`README_en.md`](README_en.md) | +| Two requests in one message trigger only one intent | multi-intent decomposition, `pending_topics`, tool allowlist | [Multi-Intent Planning](docs/features/en/16_multi_intent_planning.md) | +| A secondary agent repeats the primary transaction or confirmation | isolation of `active_transaction`, `mcp_tools`, and operational state | [Multi-Intent Planning](docs/features/en/16_multi_intent_planning.md) | ### 34. Advanced Features @@ -11463,6 +11468,29 @@ The content of this folder should be treated as an additional framework extensio **Use it when:** `COER`/input guardrails generate generic messages, `route=blocked` carries stale results, or you need to reason about precedence between input guardrails, routing, and tools. +### Implemented feature guides + +The `01` through `12` manuals above organize documentation by development area. The guides below document each executable feature individually: + +1. [Authentication](docs/features/en/01_authentication.md) +2. [Deterministic Transactional Workflow](docs/features/en/02_deterministic_transactional_workflow.md) +3. [Domain-Requested LLM Composition](docs/features/en/03_domain_requested_llm_composition.md) +4. [Domain-Requested RAG](docs/features/en/04_domain_requested_rag.md) +5. [Long-Term Memory](docs/features/en/05_long_term_memory.md) +6. [Offline Workflow Regression](docs/features/en/06_offline_workflow_regression.md) +7. [Pause and Resume Workflow](docs/features/en/07_pause_resume_workflow.md) +8. [Route Stickiness](docs/features/en/08_route_stickiness.md) +9. [Voice Interruption Replay](docs/features/en/09_voice_interruption_replay.md) +10. [Workflow Error Recovery](docs/features/en/10_workflow_error_recovery.md) +11. [Clarification](docs/features/en/11_clarification.md) +12. [Durable Idempotency](docs/features/en/12_durable_idempotency.md) +13. [Dynamic Transaction States](docs/features/en/13_dynamic_transaction_states.md) +14. [Post-Finalization Replay](docs/features/en/14_post_finalization_replay.md) +15. [Retrieval and Tool Guardrails](docs/features/en/15_retrieval_tool_guardrails.md) +16. [Multi-Intent Planning and Pending Topics](docs/features/en/16_multi_intent_planning.md) + +The [English feature index](docs/features/en/README.md) must be updated whenever a new feature guide is added. + ### Main tutorial [`README_en.md`](README_en.md) remains the complete step-by-step guide. diff --git a/Tuning-Performance/Authentication/agent_template_backend_authentication/app/state.py b/Tuning-Performance/Authentication/agent_template_backend_authentication/app/state.py index 84d9f49e..6ec11159 100644 --- a/Tuning-Performance/Authentication/agent_template_backend_authentication/app/state.py +++ b/Tuning-Performance/Authentication/agent_template_backend_authentication/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Authentication/agent_template_backend_authentication/app/workflows/agent_graph.py b/Tuning-Performance/Authentication/agent_template_backend_authentication/app/workflows/agent_graph.py index ed17e04b..bb2d550a 100644 --- a/Tuning-Performance/Authentication/agent_template_backend_authentication/app/workflows/agent_graph.py +++ b/Tuning-Performance/Authentication/agent_template_backend_authentication/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -409,6 +410,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -547,14 +552,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -623,6 +642,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/state.py b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/state.py index 84d9f49e..6ec11159 100644 --- a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/state.py +++ b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflows/agent_graph.py index ed17e04b..bb2d550a 100644 --- a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -409,6 +410,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -547,14 +552,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -623,6 +642,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/state.py b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/state.py index fc680927..ef4f2c70 100644 --- a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/state.py +++ b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/workflows/agent_graph.py index fc292459..5c5f9a28 100644 --- a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -407,6 +408,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -545,14 +550,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -621,6 +640,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/state.py b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/state.py index 84d9f49e..6ec11159 100644 --- a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/state.py +++ b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/workflows/agent_graph.py index ed17e04b..bb2d550a 100644 --- a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -409,6 +410,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -547,14 +552,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -623,6 +642,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/state.py b/Tuning-Performance/Normal/templates/agent_template_backend/app/state.py index fc680927..ef4f2c70 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend/app/state.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/Normal/templates/agent_template_backend/app/workflows/agent_graph.py index fc292459..5c5f9a28 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -407,6 +408,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -545,14 +550,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -621,6 +640,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md b/Tuning-Performance/Normal/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md index a5508710..8cbc0077 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md +++ b/Tuning-Performance/Normal/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md @@ -4,7 +4,7 @@ Este template suporta confirmação transacional em duas camadas: primeiro um pa A configuração `router.transaction_confirmation.semantic_fallback` usa três classes: `SIM`, `NAO` e `CONTINUAR`. O prompt pode usar `{{ pending_prompt }}`, `{{ relevant_conversation_context }}`, `{{ user_input }}` e `{{ allowed_values }}`. O histórico injetado é apenas contexto de interpretação; não substitui validação de negócio ou evidência MCP. -Exemplo: após `Você confirma o cancelamento do serviço Tamboro Mensal?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. +Exemplo: após `Você confirma o cancelamento do serviço adicional selecionado?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. Entradas explícitas já suportadas continuam no caminho determinístico e não geram custo adicional de LLM. Em observabilidade, o fallback usa `transaction.confirmation.semantic_classifier` e o `route_decision.metadata` informa `transaction_confirmation_source: semantic`. diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/state.py b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/state.py index fc680927..ef4f2c70 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/state.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py index fc292459..5c5f9a28 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -407,6 +408,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -545,14 +550,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -621,6 +640,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md index a5508710..8cbc0077 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md +++ b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md @@ -4,7 +4,7 @@ Este template suporta confirmação transacional em duas camadas: primeiro um pa A configuração `router.transaction_confirmation.semantic_fallback` usa três classes: `SIM`, `NAO` e `CONTINUAR`. O prompt pode usar `{{ pending_prompt }}`, `{{ relevant_conversation_context }}`, `{{ user_input }}` e `{{ allowed_values }}`. O histórico injetado é apenas contexto de interpretação; não substitui validação de negócio ou evidência MCP. -Exemplo: após `Você confirma o cancelamento do serviço Tamboro Mensal?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. +Exemplo: após `Você confirma o cancelamento do serviço adicional selecionado?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. Entradas explícitas já suportadas continuam no caminho determinístico e não geram custo adicional de LLM. Em observabilidade, o fallback usa `transaction.confirmation.semantic_classifier` e o `route_decision.metadata` informa `transaction_confirmation_source: semantic`. diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/state.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/state.py index fc680927..ef4f2c70 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/state.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/workflows/agent_graph.py index fc292459..5c5f9a28 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -407,6 +408,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -545,14 +550,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -621,6 +640,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/state.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/state.py index fc680927..ef4f2c70 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/state.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py index fc292459..5c5f9a28 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -407,6 +408,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -545,14 +550,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -621,6 +640,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/state.py b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/state.py index 1d82e35c..de25c1e8 100644 --- a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/state.py +++ b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/state.py @@ -38,6 +38,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflows/agent_graph.py index e22cf447..b950fa4e 100644 --- a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -409,6 +410,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -547,14 +552,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -623,6 +642,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/state.py b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/state.py index 69555085..0b4da3e9 100644 --- a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/state.py +++ b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/state.py @@ -39,6 +39,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflows/agent_graph.py index e22cf447..b950fa4e 100644 --- a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -409,6 +410,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -547,14 +552,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -623,6 +642,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/docs/features/en/16_multi_intent_planning.md b/docs/features/en/16_multi_intent_planning.md new file mode 100644 index 00000000..fbc3c122 --- /dev/null +++ b/docs/features/en/16_multi_intent_planning.md @@ -0,0 +1,27 @@ +# Multi-Intent Planning + +`MultiIntentPlanner` recognizes independent requests in one utterance and +builds a plan constrained to the intents, agents, and tools declared in +`config/routing.yaml`. It does not execute operations or let the LLM invent a +sequence: the router selects the primary operation and the domain workflow +continues to own the transaction. + +## Configuration and execution + +No additional configuration is required. The planner uses the intents already +declared in `routing.yaml`, selects the single transactional operation from +`tool_policies.yaml`, and persists the remaining requests as `pending_topics`. + +Known read-only topics execute through their own agent. A second mutation stays +pending for its own confirmation, while an unknown clause becomes off-context. + +The router resolves clauses from registered intent keywords and routes the +transactional operation first. The graph drains read-only pending topics before +output supervision. During an open transaction, +`yes, and send me the invoice` confirms the transaction and records the +secondary request instead of causing an intent shift. + +For multiple side-effecting actions, implement a transactional coordinator +that persists the plan, confirms it, executes idempotently, and records partial +failure or compensation. See `tests/unit/test_multi_intent_planner.py`. No +special Tuning-Performance template is needed because the behavior is native. diff --git a/docs/features/en/README.md b/docs/features/en/README.md index 4cf22497..e74d579c 100644 --- a/docs/features/en/README.md +++ b/docs/features/en/README.md @@ -17,3 +17,4 @@ Documentation for the main `agent_framework_oci` features. - [Dynamic Transaction States](13_dynamic_transaction_states.md) - [Post Finalization Replay](14_post_finalization_replay.md) - [Retrieval / Tool Guardrails](15_retrieval_tool_guardrails.md) +- [Multi-Intent Planning](16_multi_intent_planning.md) diff --git a/docs/features/pt-BR/16_multi_intent_planning.md b/docs/features/pt-BR/16_multi_intent_planning.md new file mode 100644 index 00000000..92700152 --- /dev/null +++ b/docs/features/pt-BR/16_multi_intent_planning.md @@ -0,0 +1,46 @@ +# Planejamento Multi-Intent + +O `MultiIntentPlanner` reconhece solicitações independentes na mesma mensagem e +cria um plano limitado às intents, agents e tools declarados em +`config/routing.yaml`. O planejador não executa operações nem permite que o LLM +invente uma sequência: o roteador escolhe a operação primária e o workflow do +domínio continua responsável pela transação. + +## Configuração + +Não existe configuração adicional. O planejador usa as intents do `routing.yaml` +e lê `tool_policies.yaml` para priorizar automaticamente a única operação +transacional. Pedidos secundários são persistidos em `pending_topics`. + +- intent conhecida e read-only: executada pelo agent correspondente; +- intent conhecida e transacional: mantida pendente para confirmação própria; +- trecho sem capability correspondente: respondido como `off_context`. + +Consultas secundárias com `execute` podem ser executadas pelo agent correspondente +e consolidadas. Uma segunda mutação é marcada como `defer`, pois exige um +workflow transacional coordenador. + +## Fluxo de execução + +1. O `EnterpriseRouter` separa a entrada em cláusulas e resolve cada uma pelas + keywords das intents cadastradas. +2. `tool_policies.yaml` identifica a operação transacional e a torna primária. +3. A primeira operação é roteada normalmente para seu agent/workflow. +4. O `agent_graph` compõe as mensagens públicas antes do supervisor de saída. +5. Durante uma confirmação, uma frase como `sim e me manda a segunda via` + confirma a transação aberta e registra a solicitação secundária, sem causar + troca indevida de intent. + +## Operações mutáveis + +Para executar mais de uma ação com efeito colateral, implemente um workflow +transacional coordenador. Ele deve persistir o plano, pedir confirmação quando +necessário, executar cada passo com idempotência e registrar sucesso parcial, +falha e compensação. O planejador fornece o contrato; a política de negócio e a +orquestração permanecem no domínio. + +## Referência executável + +Veja os testes genéricos em `tests/unit/test_multi_intent_planner.py`. Como a +feature é padrão do runtime, não existe uma cópia especial do +`agent_template_backend` em `Tuning-Performance`. diff --git a/docs/features/pt-BR/README.md b/docs/features/pt-BR/README.md index 3654d7d7..41b7813d 100644 --- a/docs/features/pt-BR/README.md +++ b/docs/features/pt-BR/README.md @@ -17,3 +17,4 @@ Documentação das principais features do `agent_framework_oci`. - [Estados Transacionais Dinâmicos](13_dynamic_transaction_states.md) - [Replay Após Finalização](14_post_finalization_replay.md) - [Guardrails de Retrieval e Tools](15_retrieval_tool_guardrails.md) +- [Planejamento Multi-Intent](16_multi_intent_planning.md) diff --git a/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt b/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt index ddcd57f4..a54b9a88 100644 --- a/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt +++ b/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt @@ -201,6 +201,8 @@ src/agent_framework/routing/config_loader.py src/agent_framework/routing/continuity.py src/agent_framework/routing/enterprise_router.py src/agent_framework/routing/models.py +src/agent_framework/routing/multi_intent.py +src/agent_framework/routing/pending_topics.py src/agent_framework/runtime/__init__.py src/agent_framework/runtime/agent_runtime.py src/agent_framework/runtime/transaction_input.py diff --git a/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc index ce1e9f93..df6d8588 100644 Binary files a/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc index 807caa30..cb554852 100644 Binary files a/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc index 6f08ffe3..12003abf 100644 Binary files a/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc index 91e70cbd..b1c9b624 100644 Binary files a/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc index fb49d212..e898c3f0 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc index e981f801..e712d2f9 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc index 1d9aa3cb..7bbd7e29 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc index 765ccd7d..4babbb67 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc index c229aea4..ae1fbc0b 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc index 1283c107..d4109157 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc index 696a9cad..cd2fd538 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc index 777d08ad..52973d65 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc index 1de30648..96ac00f6 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc index 97d50e42..ca72ccc1 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc index d79f3db2..51cf17eb 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc index 94db8040..f1ebe313 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc index bb73a106..ec7116ff 100644 Binary files a/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc index dcb6ac63..7203cc94 100644 Binary files a/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc index 1ceb7715..a607903e 100644 Binary files a/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc b/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc index 6d3d8bfa..7ef20fe0 100644 Binary files a/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc index 7ad47ad8..d7acb1d5 100644 Binary files a/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc index 132c5a6e..49d22a1f 100644 Binary files a/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc index 24f958a5..92f7a896 100644 Binary files a/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc index 3c036972..22d70f4e 100644 Binary files a/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc index bbcd9dec..120a77d9 100644 Binary files a/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc index f9581836..c96aa636 100644 Binary files a/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc index f0d1c0a3..156fbc62 100644 Binary files a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc index 1de528d2..f8dc611e 100644 Binary files a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc index 5b1b72e5..8a9eeb09 100644 Binary files a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc index d4255401..30c732ec 100644 Binary files a/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc index 4cb1188f..8bb7929c 100644 Binary files a/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc b/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc index 2b8e8794..a3a2721b 100644 Binary files a/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc index d3caec6d..39c36761 100644 Binary files a/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc index 35c314ee..ea291b6b 100644 Binary files a/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc index 9d7ec1f1..25eb6a00 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc index 411bdd79..fa70a44e 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc index 83fc0fbe..436b6f35 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc index 46aba452..de3f3f0c 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc index 37a3be28..e250fed3 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc index 15525708..6f98d07d 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc index cba5b65b..6dbc0149 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc index 7e6aac25..60934f02 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc index 8dde16fe..2261f53b 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc index 9c73700d..be67b8ec 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc index 15d43753..9c9966ad 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc index e4a777ce..2445e18d 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc index 81567b4f..db39055e 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc index 744f80f5..1afa0019 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc index 6d79bde3..a580e392 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc index 4dfda6f5..a101e629 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc index 3865d2cc..49a250a3 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc index 84347c88..82e7e46d 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc index 0366ecf2..bbf2cb75 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc index 8435c799..54882cd3 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc index 9f11ca3c..738d967f 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc index 401fe787..c9af6e39 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc index 69494e73..367c823f 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc index c2e493eb..0988eff5 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc index 154eeca3..efbe6b8b 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc index 8030b6c4..84b19f8f 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc index 77fcc99b..d3784841 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc index 4fbb618a..fe8fd127 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc index ec16fea2..59f0fb27 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc index 67419e57..f489aff4 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc index d5c23d92..2ce265f6 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc index 60581198..31d97eeb 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc index 6414806b..c947a70e 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc index 288c058a..3ee4c559 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc index 1140c6aa..c4cce796 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc index 0e61c16a..2171e2e8 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc index 3299142b..383f5c7a 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc index 463c0bef..2340921b 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc index 8e6db06e..a6892a37 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc index c11fe045..04fcca85 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc index cc0ecfda..ef27b036 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc index c0af6cba..031c6db0 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc index 5ba6e9b7..05fe8ace 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc index 5131d4d0..ec9e2e53 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc index 65cc96c3..c944a36e 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc index 8e03ce6e..ad33f885 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc index 65e10062..a2804632 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc index e41a0782..34c982f5 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc index 116a2fef..18dbfb22 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc index 69fa2d6a..c5c716a1 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc index 02cfd33c..8e60226c 100644 Binary files a/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc index ebfe6268..15e00436 100644 Binary files a/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc index c7643ed8..a9e0d412 100644 Binary files a/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc index a6cfed47..667d8c2e 100644 Binary files a/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc index c1caea45..f2b28f37 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc index fbca2fb9..513c1ac6 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc index 2f95a868..f29e56b8 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc index 5d7c2438..37d75e89 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc index cf731d8f..4d82da4c 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc index e25ed611..1f63e39d 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc index 615c0091..f1dc7156 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc index 627aa3e0..852c9e00 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc index 3de4edef..bc32f101 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc index 3250734d..16fc593a 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc index 4f933a0a..c982ee80 100644 Binary files a/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc index 1cc72169..467a8907 100644 Binary files a/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc index a8df7241..37d1d0c0 100644 Binary files a/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc index 55667e21..31f23ae7 100644 Binary files a/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc index 51f486a6..97ff627d 100644 Binary files a/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/types.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/types.cpython-313.pyc index 5082409e..64babb18 100644 Binary files a/libs/agent_framework/src/agent_framework/llm/__pycache__/types.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/llm/__pycache__/types.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc index d4bfbb4b..72a2ad3e 100644 Binary files a/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc index e51ef90c..6575b47d 100644 Binary files a/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc index 2c1f9120..c29938b2 100644 Binary files a/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc index c2d673c6..3c77fe0b 100644 Binary files a/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc index 3703011a..d05e49d4 100644 Binary files a/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc index 78b78233..fbc73074 100644 Binary files a/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc index eb070c7e..aaf6786f 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc index 4d634cfb..43cef0b8 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc index a36e2634..b4da7475 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc index 2193260b..8af63eb2 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc index 5aa19260..43b94734 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc index 8d6318d3..ff0d9324 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc index eb0f8b41..ad97060a 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc index 8968f9f0..e0b5a670 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc index e60831de..4d602976 100644 Binary files a/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc b/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc index 0916ec78..470d0617 100644 Binary files a/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc b/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc index 68e68130..b8dec2db 100644 Binary files a/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc index e52f92a1..b91eb2a7 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/code_mapper.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/code_mapper.cpython-313.pyc index fdade174..221f2127 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/code_mapper.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/code_mapper.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc index f3181065..fd75cd4d 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc index a7a250c4..2c167ff0 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc index aa4a5022..b1947d21 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc index 10a61af9..27ca857f 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc index 47e173e2..840e6ba4 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc index a09c898d..9c70f7a9 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc index f6a537b0..e02f1ceb 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc index d117f5c2..0c967ffb 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc index 6ac05fd6..f953f75d 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc index c8a30ca5..78e79032 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc index 4961d2f5..54969b6e 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc index fa0d088f..310750e6 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc index 6e14600a..1403568a 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc index 676cc7de..65093788 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc index 8cfe8a50..0895aa48 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc index 199cb2d7..d37c458e 100644 Binary files a/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc b/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc index c62ed31a..df2d63e9 100644 Binary files a/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc index d4e59f69..f4385263 100644 Binary files a/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc index 99c6ac07..d82d14bf 100644 Binary files a/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc index 1d3f8d5d..6876be87 100644 Binary files a/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc index c23bb7da..f3d2b878 100644 Binary files a/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc b/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc index 68466170..2769ba9e 100644 Binary files a/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc index e6c302bf..9d0343e4 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc index fdf9cd31..a7defeb1 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc index 990e8dcd..5c39bc80 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc index c8a55a01..ed178722 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/kbdb_service.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/kbdb_service.cpython-313.pyc index 3933e8b0..ef04e269 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/kbdb_service.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/kbdb_service.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc index 46bd04b4..853b2c2e 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc index 5cc15726..bcb6f09d 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc index 2ba82514..f8fa52dd 100644 Binary files a/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc index 24a7ade3..216844ec 100644 Binary files a/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/routing/__init__.py b/libs/agent_framework/src/agent_framework/routing/__init__.py index 31af5729..663c91eb 100644 --- a/libs/agent_framework/src/agent_framework/routing/__init__.py +++ b/libs/agent_framework/src/agent_framework/routing/__init__.py @@ -1,9 +1,16 @@ from .models import IntentDefinition, RouteDecision, RouterStatePolicy from .enterprise_router import EnterpriseRouter +from .multi_intent import MultiIntentPlan, MultiIntentPlanner, PlannedIntent +from .pending_topics import PendingTopicDrain, drain_pending_topics __all__ = [ "IntentDefinition", "RouteDecision", "RouterStatePolicy", "EnterpriseRouter", + "PlannedIntent", + "MultiIntentPlan", + "MultiIntentPlanner", + "PendingTopicDrain", + "drain_pending_topics", ] diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc index a1747c6d..3c58cd4c 100644 Binary files a/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc index 2b8a88a4..7c458e2d 100644 Binary files a/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc index 8866cf66..f8213f79 100644 Binary files a/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc index aa65e5cb..1c415ff5 100644 Binary files a/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc index dffeae85..3829534f 100644 Binary files a/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/multi_intent.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/multi_intent.cpython-313.pyc new file mode 100644 index 00000000..f701a67c Binary files /dev/null and b/libs/agent_framework/src/agent_framework/routing/__pycache__/multi_intent.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/pending_topics.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/pending_topics.cpython-313.pyc new file mode 100644 index 00000000..8e3ce1fc Binary files /dev/null and b/libs/agent_framework/src/agent_framework/routing/__pycache__/pending_topics.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/routing/config_loader.py b/libs/agent_framework/src/agent_framework/routing/config_loader.py index 6ab4bd04..95a9740e 100644 --- a/libs/agent_framework/src/agent_framework/routing/config_loader.py +++ b/libs/agent_framework/src/agent_framework/routing/config_loader.py @@ -33,3 +33,10 @@ def load_state_policies(path: str) -> list[RouterStatePolicy]: def load_router_defaults(path: str) -> dict[str, Any]: data = load_routing_config(path) return data.get("router", {}) + + +def load_multi_intent_config(path: str) -> dict[str, Any]: + """Backward-compatible technical limits; no section is required.""" + data = load_routing_config(path) + value = data.get("multi_intent", {}) + return value if isinstance(value, dict) else {} diff --git a/libs/agent_framework/src/agent_framework/routing/enterprise_router.py b/libs/agent_framework/src/agent_framework/routing/enterprise_router.py index e622918f..ac060a6e 100644 --- a/libs/agent_framework/src/agent_framework/routing/enterprise_router.py +++ b/libs/agent_framework/src/agent_framework/routing/enterprise_router.py @@ -6,10 +6,12 @@ import re import unicodedata from typing import Any -from .config_loader import load_intents, load_router_defaults, load_state_policies +from .config_loader import load_intents, load_multi_intent_config, load_router_defaults, load_state_policies +from .multi_intent import MultiIntentPlanner from .continuity import SemanticRouteContinuity from .models import IntentDefinition, RouteDecision, RouterStatePolicy from agent_framework.llm.structured_output import parse_json_object +from agent_framework.mcp.tool_policy import ToolPolicyRegistry from agent_framework.runtime.transaction_parameters import extract_transaction_parameters, parse_transaction_confirmation from agent_framework.workflows.input_contract import ( expected_input_reprompt, @@ -44,6 +46,16 @@ class EnterpriseRouter: self.intents: list[IntentDefinition] = load_intents(self.config_path) self.state_policies: list[RouterStatePolicy] = load_state_policies(self.config_path) self.defaults = load_router_defaults(self.config_path) + tool_policies = ToolPolicyRegistry(getattr(settings, "TOOL_POLICIES_PATH", None)) + transactional_tools = { + name for name, policy in tool_policies.policies.items() + if policy.operation_type == "transactional" or policy.require_confirmation + } + self.multi_intent_planner = MultiIntentPlanner( + self.intents, + load_multi_intent_config(self.config_path), + transactional_tools=transactional_tools, + ) self.fallback_agent = self.defaults.get("fallback_agent", "billing_agent") self.intent_shift_threshold = float(self.defaults.get("confidence_threshold", 0.7)) self.transaction_confirmation = dict(self.defaults.get("transaction_confirmation") or {}) @@ -727,6 +739,27 @@ class EnterpriseRouter: # intent/agente ativos devem prevalecer sobre a route stickiness. Isso # evita manter um fluxo read-only (por exemplo, tracking) quando o usuário # muda para uma ação transacional (por exemplo, devolução). + planner = getattr(self, "multi_intent_planner", None) + multi_intent_plan = planner.plan(str(text)) if planner is not None else None + if multi_intent_plan is not None: + primary = multi_intent_plan.operations[0] + decision = RouteDecision( + route=primary.agent, + agent=primary.agent, + intent=primary.intent, + confidence=1.0, + reason="Plano multi-intent validado a partir de intents configuradas.", + method="keyword", + metadata={ + "multi_intent": True, + "multi_intent_plan": multi_intent_plan.model_dump(mode="json"), + }, + domain=primary.domain, + mcp_tools=primary.tools, + ) + await self._emit(decision, state) + return decision + keyword_candidate = self._route_by_keyword(text) active_agent = str(state.get("active_agent") or "").strip() previous = state.get("route_decision") or {} @@ -799,7 +832,13 @@ class EnterpriseRouter: """ tx_status = str(state.get("transaction_status") or "").strip().upper() if tx_status == "AWAITING_CONFIRMATION": - confirmation = parse_transaction_confirmation(text) + active = state.get("active_transaction") if isinstance(state.get("active_transaction"), dict) else {} + previous = state.get("route_decision") if isinstance(state.get("route_decision"), dict) else {} + owner_intent = str(active.get("started_from_intent") or previous.get("intent") or state.get("intent") or "") + compound_plan = self.multi_intent_planner.plan_confirmation_secondary( + text, primary_intent=owner_intent, primary_agent=state_decision.agent + ) + confirmation = "confirm" if compound_plan is not None else parse_transaction_confirmation(text) source = "deterministic" classifier_output = None raw_classifier = None @@ -828,6 +867,12 @@ class EnterpriseRouter: "transaction_confirmation_decision": confirmation, "transaction_confirmation_source": source, } + if compound_plan is not None: + state_decision.metadata.update({ + "multi_intent": True, + "multi_intent_confirmation": True, + "multi_intent_plan": compound_plan.model_dump(mode="json"), + }) if source == "semantic": state_decision.metadata.update({ "transaction_confirmation_classifier_output": classifier_output, @@ -1129,7 +1174,12 @@ class EnterpriseRouter: kw_normalized = kw.casefold() strategy = None # Exato primeiro para preservar o comportamento existente. - if kw_normalized in normalized: + keyword_tokens = self._keyword_tokens(kw) + text_tokens = self._keyword_tokens(text) + if ( + (len(keyword_tokens) == 1 and keyword_tokens[0] in text_tokens) + or (len(keyword_tokens) > 1 and kw_normalized in normalized) + ): strategy = "exact" elif self._ordered_keyword_match(kw, text): strategy = "ordered_tokens" diff --git a/libs/agent_framework/src/agent_framework/routing/multi_intent.py b/libs/agent_framework/src/agent_framework/routing/multi_intent.py new file mode 100644 index 00000000..27354e17 --- /dev/null +++ b/libs/agent_framework/src/agent_framework/routing/multi_intent.py @@ -0,0 +1,203 @@ +from __future__ import annotations + +import re +import unicodedata +import uuid +from typing import Any, Literal + +from pydantic import BaseModel, Field + +from .models import IntentDefinition + + +class PlannedIntent(BaseModel): + operation_id: str + intent: str + agent: str + domain: str | None = None + tools: list[str] = Field(default_factory=list) + source_text: str = "" + disposition: Literal["execute", "inform", "unsupported", "defer"] = "execute" + message: str | None = None + status: Literal["pending", "completed", "failed", "skipped"] = "pending" + + +class MultiIntentPlan(BaseModel): + plan_id: str + operations: list[PlannedIntent] + execution_mode: Literal["primary_with_secondary", "sequential"] = "primary_with_secondary" + status: Literal["planned", "running", "completed", "partially_completed", "failed"] = "planned" + + +class MultiIntentPlanner: + """Deterministic planner constrained by routing.yaml. + + The planner never invents agents or tools. It recognizes independently + configured intents in conjunction-separated clauses. Execution remains + owned by the agent/workflow runtime. + """ + + _SPLIT = re.compile(r"\s*(?:,|;|\be\b|\bmas\b|\btamb[eé]m\b)\s*", re.I) + _CONFIRM_WITH_REMAINDER = re.compile( + r"^\s*(?:sim|s|pode|pode\s+sim|isso\s+mesmo|claro|ok|confirmo)\s*(?:,|;|\be\b)\s*(.+)$", + re.I, + ) + + def __init__( + self, + intents: list[IntentDefinition], + config: dict[str, Any] | None = None, + *, + transactional_tools: set[str] | None = None, + ): + self.intents = [item for item in intents if item.enabled] + self.config = dict(config or {}) + # Multi-intent é capacidade padrão. O bloco opcional só permite limites + # técnicos/overrides; sua ausência nunca desabilita a detecção. + self.enabled = bool(self.config.get("enabled", True)) + self.max_operations = max(2, int(self.config.get("max_operations", 4))) + self.transactional_tools = set(transactional_tools or set()) + + @staticmethod + def _normalize(value: str) -> str: + text = unicodedata.normalize("NFKD", str(value or "")) + return "".join(ch for ch in text if not unicodedata.combining(ch)).casefold() + + def _intent_for_clause(self, clause: str) -> IntentDefinition | None: + normalized = self._normalize(clause) + clause_tokens = set(re.findall(r"[\w]+", normalized, flags=re.UNICODE)) + matches: list[tuple[int, int, IntentDefinition]] = [] + for intent in self.intents: + for keyword in intent.keywords: + token = self._normalize(keyword) + keyword_tokens = re.findall(r"[\w]+", token, flags=re.UNICODE) + matched = bool( + keyword_tokens + and ( + (len(keyword_tokens) == 1 and keyword_tokens[0] in clause_tokens) + or (len(keyword_tokens) > 1 and token in normalized) + ) + ) + if matched: + matches.append((intent.priority, -len(token), intent)) + if not matches: + return None + matches.sort(key=lambda item: (item[0], item[1])) + return matches[0][2] + + def _is_transactional(self, operation: PlannedIntent) -> bool: + return any(tool in self.transactional_tools for tool in operation.tools) + + @classmethod + def _looks_like_request(cls, clause: str) -> bool: + normalized = cls._normalize(clause) + return bool(re.search( + r"\b(quero|preciso|mandar|manda|mande|enviar|envia|envie|gerar|gera|gere|" + r"alterar|altera|altere|mudar|muda|mude|consultar|consulta|consulte|" + r"cancelar|cancela|cancele|explicar|explica|explique|mostrar|mostra|mostre|" + r"falar|comprar)\b", + normalized, + )) + + @staticmethod + def _off_context(clause: str, operation_id: str) -> PlannedIntent: + return PlannedIntent( + operation_id=operation_id, + intent="off_context", + agent="", + source_text=clause, + disposition="unsupported", + message=f"Sobre {clause}, não consigo ajudar nesta jornada.", + status="pending", + ) + + def plan(self, text: str) -> MultiIntentPlan | None: + if not self.enabled: + return None + clauses = [part.strip(" .") for part in self._SPLIT.split(str(text or "")) if part.strip(" .")] + operations: list[PlannedIntent] = [] + unknown_clauses: list[str] = [] + seen: set[str] = set() + for clause in clauses[: self.max_operations * 2]: + intent = self._intent_for_clause(clause) + if intent is None: + if self._looks_like_request(clause): + unknown_clauses.append(clause) + continue + if intent.name in seen: + continue + seen.add(intent.name) + operations.append(PlannedIntent( + operation_id=f"op-{len(operations) + 1}", intent=intent.name, + agent=intent.agent, domain=intent.domain, tools=list(intent.mcp_tools), + source_text=clause, + )) + if not operations or len(operations) + len(unknown_clauses) < 2: + return None + # Regra técnica geral: uma mutação que exige o contrato transacional é a + # operação primária. Não é necessário cadastrar cada par de intents. + transactional = [item for item in operations if self._is_transactional(item)] + if len(transactional) == 1: + primary_operation = transactional[0] + operations.sort(key=lambda item: item is not primary_operation) + for operation in operations[1:]: + if self._is_transactional(operation) and operation.disposition == "execute": + operation.disposition = "defer" + for clause in unknown_clauses[: max(0, self.max_operations - len(operations))]: + operations.append(self._off_context(clause, f"op-{len(operations) + 1}")) + return MultiIntentPlan( + plan_id=f"mip-{uuid.uuid4().hex}", operations=operations, + execution_mode="primary_with_secondary", + ) + + def plan_confirmation_secondary( + self, text: str, *, primary_intent: str, primary_agent: str + ) -> MultiIntentPlan | None: + """Recognize an explicit confirmation followed by another request. + + The confirmation remains authoritative for the open transaction. The + secondary request is recorded and governed by the same combination + policy instead of turning the whole utterance into an intent shift. + """ + if not self.enabled: + return None + match = self._CONFIRM_WITH_REMAINDER.match(str(text or "")) + if match is None: + return None + clause = match.group(1).strip() + secondary = self._intent_for_clause(clause) + primary = next((item for item in self.intents if item.name == primary_intent), None) + if secondary is not None and secondary.name == primary_intent: + return None + if secondary is None and not self._looks_like_request(clause): + return None + operations = [ + PlannedIntent( + operation_id="op-1", intent=primary_intent, agent=primary_agent, + domain=primary.domain if primary else None, + tools=list(primary.mcp_tools) if primary else [], + source_text="confirmation", status="pending", + ), + (PlannedIntent( + operation_id="op-2", intent=secondary.name, agent=secondary.agent, + domain=secondary.domain, tools=list(secondary.mcp_tools), source_text=clause, + ) if secondary is not None else self._off_context(clause, "op-2")), + ] + if self._is_transactional(operations[1]) and operations[1].disposition == "execute": + operations[1].disposition = "defer" + return MultiIntentPlan( + plan_id=f"mip-{uuid.uuid4().hex}", operations=operations, + execution_mode="primary_with_secondary", + ) + + @staticmethod + def public_messages(plan: dict[str, Any] | MultiIntentPlan | None) -> list[str]: + if plan is None: + return [] + data = plan.model_dump() if isinstance(plan, MultiIntentPlan) else plan + messages = [] + for operation in data.get("operations", []) or []: + message = str(operation.get("message") or "").strip() + if message and message not in messages: + messages.append(message) + return messages diff --git a/libs/agent_framework/src/agent_framework/routing/pending_topics.py b/libs/agent_framework/src/agent_framework/routing/pending_topics.py new file mode 100644 index 00000000..5de65c78 --- /dev/null +++ b/libs/agent_framework/src/agent_framework/routing/pending_topics.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Awaitable, Callable + +from .multi_intent import MultiIntentPlanner + +AgentHandler = Callable[[dict[str, Any]], Awaitable[dict[str, Any]]] + + +@dataclass(frozen=True) +class PendingTopicDrain: + answer: str + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] + + +async def drain_pending_topics( + state: dict[str, Any], + candidate: str, + handlers: dict[str, AgentHandler], +) -> PendingTopicDrain: + """Drain read-only topics and retain anything that cannot finish safely.""" + route = state.get("route_decision") if isinstance(state.get("route_decision"), dict) else {} + plan = route.get("metadata", {}).get("multi_intent_plan") + topics = list(state.get("pending_topics") or ( + plan.get("operations", [])[1:] if isinstance(plan, dict) else [] + )) + remaining: list[dict[str, Any]] = [] + handled = list(state.get("handled_topics") or []) + secondary_answers: list[str] = [] + for topic in topics: + disposition = topic.get("disposition") + if disposition == "defer": + remaining.append(topic) + continue + if disposition != "execute": + handled.append({**topic, "status": "completed"}) + continue + handler = handlers.get(str(topic.get("agent") or "")) + if handler is None: + remaining.append(topic) + continue + source_text = str(topic.get("source_text") or "").strip() + child = { + **state, + "user_text": source_text, + "message_text": source_text, + "sanitized_input": source_text, + "route": topic.get("agent"), + "active_agent": topic.get("agent"), + "intent": topic.get("intent"), + "domain": topic.get("domain"), + "mcp_tools": topic.get("tools") or [], + "route_decision": { + "route": topic.get("agent"), "agent": topic.get("agent"), + "intent": topic.get("intent"), "mcp_tools": topic.get("tools") or [], + "metadata": {"multi_intent_secondary": True}, + }, + "active_transaction": None, + "transaction_status": None, + "transaction_pre_validation": None, + "confirmation_required": False, + "confirmation_received": False, + "selected_tool_call": {}, + "pending_tool_call": {}, + "pending_tool_clarification": None, + "pending_domain_workflow": None, + "missing_parameters": [], + "tool_policy_result": None, + "mcp_results": [], + "available_mcp_tools": topic.get("tools") or [], + "next_state": None, + "workflow_id": None, + "route_bypassed": False, + } + try: + result = await handler(child) + except Exception: + remaining.append(topic) + continue + answer = str((result or {}).get("answer") or "").strip() + if not answer: + remaining.append(topic) + continue + secondary_answers.append(answer) + handled.append({**topic, "status": "completed"}) + + secondary_answers.extend(MultiIntentPlanner.public_messages(plan)) + unique = [] + for answer in secondary_answers: + if answer and answer.casefold() not in candidate.casefold() and answer not in unique: + unique.append(answer) + if unique: + secondary = "\n\n".join(unique) + candidate = ( + f"{secondary}\n\n{candidate.lstrip()}".strip() + if state.get("confirmation_required") + else f"{candidate.rstrip()}\n\n{secondary}".strip() + ) + return PendingTopicDrain(candidate, remaining, handled) diff --git a/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc index a35920c5..a0fbec33 100644 Binary files a/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc b/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc index 23e52040..5bb68f33 100644 Binary files a/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc b/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc index dce1b61d..417545db 100644 Binary files a/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc index abd21776..cd1f4cc4 100644 Binary files a/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc index 27bd0c7d..31ae8906 100644 Binary files a/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc index 0db928e0..65b6f01a 100644 Binary files a/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc index dcbefa0e..689cc2fc 100644 Binary files a/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc index def6ac65..ccefd30a 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc index 85652803..fb1dfac9 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/input_contract.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/input_contract.cpython-313.pyc index 1eb76230..078f3f48 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/input_contract.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/input_contract.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc index b806c324..a01d23fe 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc index 1180577c..950bd311 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc index 5ab0dd87..f5f2a86a 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc index f24728d9..5589ff24 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc index 15f1453a..c99df20e 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc differ diff --git a/scripts/sync_pending_topics_to_tuning.py b/scripts/sync_pending_topics_to_tuning.py new file mode 100644 index 00000000..9e6c49ae --- /dev/null +++ b/scripts/sync_pending_topics_to_tuning.py @@ -0,0 +1,109 @@ +"""Propaga o contrato nativo de pending_topics às cópias de Tuning-Performance.""" +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + + +def replace_once(text: str, old: str, new: str, path: Path) -> str: + if old not in text: + raise RuntimeError(f"Marcador não encontrado em {path}: {old[:60]!r}") + return text.replace(old, new, 1) + + +def update_state(path: Path) -> None: + text = path.read_text(encoding="utf-8") + if " pending_topics: list[dict[str, Any]]" in text: + return + text = replace_once( + text, + " supervisor_results: list[dict[str, Any]]\n", + " supervisor_results: list[dict[str, Any]]\n" + " pending_topics: list[dict[str, Any]]\n" + " handled_topics: list[dict[str, Any]]\n", + path, + ) + path.write_text(text, encoding="utf-8") + + +def update_graph(path: Path) -> None: + text = path.read_text(encoding="utf-8") + if "from agent_framework.routing.pending_topics import drain_pending_topics" in text: + return + text = replace_once( + text, + "from agent_framework.routing.enterprise_router import EnterpriseRouter\n", + "from agent_framework.routing.enterprise_router import EnterpriseRouter\n" + "from agent_framework.routing.pending_topics import drain_pending_topics\n", + path, + ) + text = replace_once( + text, + " } if decision.method == \"continuity\" else {},\n }\n\n async def billing_agent", + " } if decision.method == \"continuity\" else {},\n" + " \"pending_topics\": (\n" + " (decision.metadata or {}).get(\"multi_intent_plan\", {}).get(\"operations\", [])[1:]\n" + " if (decision.metadata or {}).get(\"multi_intent_plan\") else []\n" + " ),\n" + " }\n\n async def billing_agent", + path, + ) + marker = " async def output_supervisor(self, state):" + before, output = text.split(marker, 1) + old = """ if not bool(getattr(self.settings, \"ENABLE_OUTPUT_SUPERVISOR\", True)): + return { + \"output_guardrails_already_applied\": False, + \"supervisor_action\": \"disabled\", + \"supervisor_attempt\": int(state.get(\"supervisor_attempt\", 0)), + } + + candidate = state.get(\"answer\") or \"\" +""" + new = """ drained = await drain_pending_topics( + state, + str(state.get(\"answer\") or \"\"), + { + \"billing_agent\": self.billing.run, + \"product_agent\": self.product.run, + \"orders_agent\": self.orders.run, + \"support_agent\": self.support.run, + }, + ) + candidate = drained.answer + if not bool(getattr(self.settings, \"ENABLE_OUTPUT_SUPERVISOR\", True)): + return { + \"answer\": candidate, + \"final_answer\": candidate, + \"pending_topics\": drained.pending_topics, + \"handled_topics\": drained.handled_topics, + \"output_guardrails_already_applied\": False, + \"supervisor_action\": \"disabled\", + \"supervisor_attempt\": int(state.get(\"supervisor_attempt\", 0)), + } + +""" + output = replace_once(output, old, new, path) + output = replace_once( + output, + " \"output_guardrails_already_applied\": True,\n", + " \"output_guardrails_already_applied\": True,\n" + " \"pending_topics\": drained.pending_topics,\n" + " \"handled_topics\": drained.handled_topics,\n", + path, + ) + path.write_text(before + marker + output, encoding="utf-8") + + +def main() -> None: + tuning = ROOT / "Tuning-Performance" + states = sorted(tuning.glob("**/app/state.py")) + graphs = sorted(tuning.glob("**/app/workflows/agent_graph.py")) + for path in states: + update_state(path) + for path in graphs: + update_graph(path) + print(f"pending_topics sincronizado: {len(states)} states, {len(graphs)} graphs") + + +if __name__ == "__main__": + main() diff --git a/templates/agent_template_backend/app/state.py b/templates/agent_template_backend/app/state.py index 69555085..0b4da3e9 100644 --- a/templates/agent_template_backend/app/state.py +++ b/templates/agent_template_backend/app/state.py @@ -39,6 +39,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/templates/agent_template_backend/app/workflows/agent_graph.py b/templates/agent_template_backend/app/workflows/agent_graph.py index 99bf4c5f..7101db41 100644 --- a/templates/agent_template_backend/app/workflows/agent_graph.py +++ b/templates/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -409,6 +410,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -547,14 +552,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -623,6 +642,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md b/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md index a5508710..8cbc0077 100644 --- a/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md +++ b/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md @@ -4,7 +4,7 @@ Este template suporta confirmação transacional em duas camadas: primeiro um pa A configuração `router.transaction_confirmation.semantic_fallback` usa três classes: `SIM`, `NAO` e `CONTINUAR`. O prompt pode usar `{{ pending_prompt }}`, `{{ relevant_conversation_context }}`, `{{ user_input }}` e `{{ allowed_values }}`. O histórico injetado é apenas contexto de interpretação; não substitui validação de negócio ou evidência MCP. -Exemplo: após `Você confirma o cancelamento do serviço Tamboro Mensal?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. +Exemplo: após `Você confirma o cancelamento do serviço adicional selecionado?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. Entradas explícitas já suportadas continuam no caminho determinístico e não geram custo adicional de LLM. Em observabilidade, o fallback usa `transaction.confirmation.semantic_classifier` e o `route_decision.metadata` informa `transaction_confirmation_source: semantic`. diff --git a/templates/agent_template_backend_day_zero/app/state.py b/templates/agent_template_backend_day_zero/app/state.py index 7e3f280e..fb4a93bd 100644 --- a/templates/agent_template_backend_day_zero/app/state.py +++ b/templates/agent_template_backend_day_zero/app/state.py @@ -36,6 +36,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py b/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py index 2770df01..3df0833f 100644 --- a/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py +++ b/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -407,6 +408,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -545,14 +550,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -621,6 +640,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md b/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md index a5508710..8cbc0077 100644 --- a/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md +++ b/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md @@ -4,7 +4,7 @@ Este template suporta confirmação transacional em duas camadas: primeiro um pa A configuração `router.transaction_confirmation.semantic_fallback` usa três classes: `SIM`, `NAO` e `CONTINUAR`. O prompt pode usar `{{ pending_prompt }}`, `{{ relevant_conversation_context }}`, `{{ user_input }}` e `{{ allowed_values }}`. O histórico injetado é apenas contexto de interpretação; não substitui validação de negócio ou evidência MCP. -Exemplo: após `Você confirma o cancelamento do serviço Tamboro Mensal?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. +Exemplo: após `Você confirma o cancelamento do serviço adicional selecionado?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. Entradas explícitas já suportadas continuam no caminho determinístico e não geram custo adicional de LLM. Em observabilidade, o fallback usa `transaction.confirmation.semantic_classifier` e o `route_decision.metadata` informa `transaction_confirmation_source: semantic`. diff --git a/tests/test_route_stickiness_transaction_shift.py b/tests/test_route_stickiness_transaction_shift.py index 3ce6135e..099b1bc9 100644 --- a/tests/test_route_stickiness_transaction_shift.py +++ b/tests/test_route_stickiness_transaction_shift.py @@ -2,6 +2,20 @@ import pytest from types import SimpleNamespace from agent_framework.routing.enterprise_router import EnterpriseRouter + + +def test_single_word_keyword_requires_a_complete_token(): + from agent_framework.routing.models import IntentDefinition + + router = object.__new__(EnterpriseRouter) + router.intents = [ + IntentDefinition( + name="cancel", agent="orders_agent", priority=10, + keywords=["cancela"], examples=[], enabled=True, + ) + ] + assert router._route_by_keyword("cancela o pedido") is not None + assert router._route_by_keyword("sim, pode cancelar") is None from agent_framework.routing.models import RouteDecision diff --git a/tests/test_transaction_explicit_abandonment_and_structural_candidate.py b/tests/test_transaction_explicit_abandonment_and_structural_candidate.py index 259cfad6..9a2ea3cb 100644 --- a/tests/test_transaction_explicit_abandonment_and_structural_candidate.py +++ b/tests/test_transaction_explicit_abandonment_and_structural_candidate.py @@ -82,6 +82,11 @@ def _active_state(message): "started_from_intent": "contas_contestation", "parameter_schema": {"valor": {"type": "number"}}, }, + "history": [ + {"role": "user", "content": "não reconheço o Tamboro Mensal, quero tirar da fatura"}, + {"role": "assistant", "content": "Qual é o valor da cobrança que você deseja contestar?"}, + {"role": "user", "content": message}, + ], } diff --git a/tests/unit/test_multi_intent_planner.py b/tests/unit/test_multi_intent_planner.py new file mode 100644 index 00000000..0c720620 --- /dev/null +++ b/tests/unit/test_multi_intent_planner.py @@ -0,0 +1,66 @@ +from agent_framework.routing.models import IntentDefinition +from agent_framework.routing.multi_intent import MultiIntentPlanner + + +def _planner(): + return MultiIntentPlanner([ + IntentDefinition(name="cancel", agent="orders", priority=10, keywords=["cancelar pedido"], mcp_tools=["cancelar_pedido"]), + IntentDefinition(name="invoice", agent="billing", priority=20, keywords=["segunda via"], mcp_tools=["consultar_fatura"]), + ], { + "enabled": True, + }, transactional_tools={"cancelar_pedido"}) + + +def test_builds_validated_plan_and_keeps_primary_first(): + plan = _planner().plan("quero cancelar pedido e preciso da segunda via") + assert plan is not None + assert [item.intent for item in plan.operations] == ["cancel", "invoice"] + assert plan.operations[1].disposition == "execute" + assert plan.operations[1].tools == ["consultar_fatura"] + + +def test_confirmation_with_secondary_does_not_lose_confirmation(): + plan = _planner().plan_confirmation_secondary( + "sim e preciso da segunda via", primary_intent="cancel", primary_agent="orders" + ) + assert plan is not None + assert [item.intent for item in plan.operations] == ["cancel", "invoice"] + assert MultiIntentPlanner.public_messages(plan) == [] + + +def test_transactional_intent_is_primary_even_when_mentioned_second(): + plan = _planner().plan("preciso da segunda via e quero cancelar pedido") + assert plan is not None + assert [item.intent for item in plan.operations] == ["cancel", "invoice"] + + +def test_does_not_plan_single_intent_or_invent_unknown_intent(): + assert _planner().plan("quero cancelar pedido") is None + assert _planner().plan("quero cancelar pedido e saber o clima") is None + + +def test_known_primary_with_actionable_unknown_creates_off_context_topic(): + plan = _planner().plan("quero cancelar pedido e alterar meu CNPJ") + assert plan is not None + assert plan.operations[1].intent == "off_context" + assert plan.operations[1].disposition == "unsupported" + + +def test_confirmation_with_unknown_secondary_keeps_confirmation(): + plan = _planner().plan_confirmation_secondary( + "sim e altere meu CNPJ", primary_intent="cancel", primary_agent="orders" + ) + assert plan is not None + assert plan.operations[0].intent == "cancel" + assert plan.operations[1].intent == "off_context" + + +def test_single_word_keyword_does_not_match_a_longer_word_prefix(): + planner = MultiIntentPlanner([ + IntentDefinition(name="cancel", agent="orders", priority=10, keywords=["cancela"]), + IntentDefinition(name="invoice", agent="billing", priority=20, keywords=["segunda via"]), + ]) + plan = planner.plan("sim, pode cancelar e preciso da segunda via") + assert plan is not None + assert all(item.intent != "cancel" for item in plan.operations) + assert [item.intent for item in plan.operations] == ["invoice", "off_context"] diff --git a/tests/unit/test_pending_topics_isolation.py b/tests/unit/test_pending_topics_isolation.py new file mode 100644 index 00000000..e614cd7a --- /dev/null +++ b/tests/unit/test_pending_topics_isolation.py @@ -0,0 +1,53 @@ +import pytest + +from agent_framework.routing.pending_topics import drain_pending_topics + + +@pytest.mark.asyncio +async def test_secondary_topic_does_not_inherit_primary_transaction_state(): + captured = {} + + async def billing_agent(state): + captured.update(state) + return {"answer": "Aqui está a sua segunda via."} + + state = { + "answer": "Você confirma o cancelamento do Tamboro?", + "sanitized_input": "quero cancelar Tamboro e quero minha segunda via", + "confirmation_required": True, + "active_transaction": {"tool_name": "cancelar_vas_avulso"}, + "transaction_status": "AWAITING_CONFIRMATION", + "selected_tool_call": {"tool_name": "cancelar_vas_avulso"}, + "pending_tool_call": {"tool_name": "cancelar_vas_avulso"}, + "pending_domain_workflow": {"workflow_name": "cancelamento_vas_avulso"}, + "missing_parameters": ["subject"], + "next_state": "WAITING_CONTESTACAO_CONFIRMATION", + "mcp_results": [{"tool_name": "consultar_vas"}], + "pending_topics": [{ + "operation_id": "op-2", + "intent": "contas_invoice_query", + "agent": "faturas_agent", + "domain": "telecom_contas", + "tools": ["consultar_faturas"], + "source_text": "quero minha segunda via", + "disposition": "execute", + "status": "pending", + }], + } + + result = await drain_pending_topics( + state, state["answer"], {"faturas_agent": billing_agent} + ) + + assert captured["active_transaction"] is None + assert captured["selected_tool_call"] == {} + assert captured["pending_tool_call"] == {} + assert captured["pending_domain_workflow"] is None + assert captured["next_state"] is None + assert captured["sanitized_input"] == "quero minha segunda via" + assert captured["mcp_results"] == [] + assert captured["mcp_tools"] == ["consultar_faturas"] + assert captured["available_mcp_tools"] == ["consultar_faturas"] + assert captured["domain"] == "telecom_contas" + assert result.answer.startswith("Aqui está a sua segunda via.") + assert result.pending_topics == [] diff --git a/tests/unit/test_readme_feature_catalog.py b/tests/unit/test_readme_feature_catalog.py new file mode 100644 index 00000000..6a56b5d6 --- /dev/null +++ b/tests/unit/test_readme_feature_catalog.py @@ -0,0 +1,39 @@ +from pathlib import Path +import re +from urllib.parse import unquote + + +ROOT = Path(__file__).resolve().parents[2] + + +def _local_links(path: Path) -> list[str]: + text = path.read_text(encoding="utf-8") + return [ + target for target in re.findall(r"\[[^]]*\]\(([^)]+)\)", text) + if not target.startswith(("http://", "https://", "#", "mailto:")) + ] + + +def test_main_readmes_have_no_broken_local_links(): + for name in ("README.md", "README_en.md"): + readme = ROOT / name + for target in _local_links(readme): + relative = unquote(target.split("#", 1)[0]) + assert not relative or (readme.parent / relative).exists(), (name, target) + + +def test_every_feature_guide_is_listed_in_corresponding_main_readme(): + pairs = (("pt-BR", "README.md"), ("en", "README_en.md")) + for language, readme_name in pairs: + readme = (ROOT / readme_name).read_text(encoding="utf-8") + guides = sorted((ROOT / "docs" / "features" / language).glob("[0-9][0-9]_*.md")) + assert guides + for guide in guides: + expected = f"docs/features/{language}/{guide.name}" + assert expected in readme, (readme_name, expected) + + +def test_portuguese_and_english_feature_catalogs_are_symmetric(): + pt = {path.name for path in (ROOT / "docs" / "features" / "pt-BR").glob("*.md")} + en = {path.name for path in (ROOT / "docs" / "features" / "en").glob("*.md")} + assert pt == en diff --git a/tests/unit/test_tuning_pending_topics_sync.py b/tests/unit/test_tuning_pending_topics_sync.py new file mode 100644 index 00000000..23e29d46 --- /dev/null +++ b/tests/unit/test_tuning_pending_topics_sync.py @@ -0,0 +1,38 @@ +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] + + +def test_all_tuning_backend_graphs_support_pending_topics(): + graphs = sorted((ROOT / "Tuning-Performance").glob("**/app/workflows/agent_graph.py")) + assert graphs + for path in graphs: + source = path.read_text(encoding="utf-8") + assert "from agent_framework.routing.pending_topics import drain_pending_topics" in source, path + assert '"pending_topics": drained.pending_topics' in source, path + assert '"handled_topics": drained.handled_topics' in source, path + + +def test_all_tuning_backend_states_expose_pending_topics(): + states = sorted((ROOT / "Tuning-Performance").glob("**/app/state.py")) + assert states + for path in states: + source = path.read_text(encoding="utf-8") + assert "pending_topics: list[dict[str, Any]]" in source, path + assert "handled_topics: list[dict[str, Any]]" in source, path + + +def test_canonical_templates_delegate_drain_to_framework_helper(): + for template in ("agent_template_backend", "agent_template_backend_day_zero"): + graph = ROOT / "templates" / template / "app" / "workflows" / "agent_graph.py" + state = ROOT / "templates" / template / "app" / "state.py" + graph_source = graph.read_text(encoding="utf-8") + state_source = state.read_text(encoding="utf-8") + assert "from agent_framework.routing.pending_topics import drain_pending_topics" in graph_source + assert "drained = await drain_pending_topics(" in graph_source + assert '"pending_topics": drained.pending_topics' in graph_source + assert '"handled_topics": drained.handled_topics' in graph_source + assert "MultiIntentPlanner.public_messages" not in graph_source + assert "pending_topics: list[dict[str, Any]]" in state_source + assert "handled_topics: list[dict[str, Any]]" in state_source