diff --git a/Documentacao/RELEASE_NOTES_GENERIC_DETERMINISTIC_INTENT_SHIFT_V15.md b/Documentacao/RELEASE_NOTES_GENERIC_DETERMINISTIC_INTENT_SHIFT_V15.md new file mode 100644 index 0000000..114c236 --- /dev/null +++ b/Documentacao/RELEASE_NOTES_GENERIC_DETERMINISTIC_INTENT_SHIFT_V15.md @@ -0,0 +1,47 @@ +# Release Notes — Generic Deterministic Intent Shift v15 + +## Problema corrigido + +A Route Stickiness podia preservar a intent anterior quando a nova mensagem correspondia a uma intent configurada no `routing.yaml`, mas a frase do usuário omitia conectores curtos presentes na keyword configurada. + +Exemplo real de configuração: + +- keyword: `qual é o meu plano` +- mensagem: `qual o meu plano` + +A classificação determinística não reconhecia a nova intent e a continuity acabava mantendo a intent anterior. + +## Correção + +O `EnterpriseRouter` continua usando, nesta ordem: + +1. match exato; +2. sequência completa de tokens com palavras inseridas (`ordered_tokens`); +3. sequência de tokens informativos tolerando a omissão de conectores curtos presentes na keyword (`ordered_content_tokens`). + +A terceira estratégia ignora, somente no lado da keyword, tokens de até dois caracteres e exige pelo menos dois tokens informativos. Não há nomes de intents, agentes, domínios ou verbos de negócio hardcoded. + +Assim, a solução é dirigida integralmente pelas intents carregadas do `routing.yaml` da aplicação. + +## Precedência sobre Route Stickiness + +Quando o candidato determinístico encontrado é diferente da intent ativa, ele preempta a stickiness e retorna: + +- `route_stickiness_preempted: true` +- `previous_agent` +- `previous_intent` +- `keyword_match_strategy` + +A continuity LLM não é chamada nesse caminho. + +## Casos cobertos + +### Mesmo agente, nova intent + +`retail_order_tracking` -> `quero cancelar meu pedido` -> `retail_order_cancel` + +### Mesmo agente, tools diferentes + +`contas_invoice_query` -> `qual o meu plano` -> `contas_plan_information` + +Mesmo que ambas as intents usem `faturas_agent`, as tools mudam de `consultar_faturas` para `consultar_plano`. 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 e9b075c..3cfa30e 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__/idempotency.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc index 87c7aba..932d7d8 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/cache/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc index 8cbc662..6b482c0 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 3677ec3..0384507 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/config/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc index f725ca2..b0c36ce 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__/settings.cpython-313.pyc b/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc index 7e29e89..9cfcddf 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 e193ad1..702723a 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 93e5440..cfc5b65 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/identity/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc index cd32f41..2035238 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 ccfe18b..c6cc243 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 c9bdda7..5ea4a98 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 acdea74..5bb3484 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/mcp/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc index 40b0f17..f1679da 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 136d8a0..fac6612 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 5c29ab3..eef176c 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 00f0f2c..bf15109 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 f46fe3f..0282027 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 29cc0c8..478d318 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 df7429d..ab60666 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__/message_history.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc index 10f3f23..9a56d2a 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 8081522..0b13722 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 75fbc18..1a22aab 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 5317aec..539fe00 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__/session.cpython-313.pyc b/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc index d46aa28..352c5f2 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/persistence/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc index 6f2a63c..da76b88 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__/sqlite_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc index f94aa79..db4bea7 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/routing/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc index 5dfaf2f..2263e9b 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 8c435b9..e510f87 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 4abc2dc..f614063 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 ec81931..67d963b 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 68426e9..f4724fd 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/enterprise_router.py b/libs/agent_framework/src/agent_framework/routing/enterprise_router.py index 0b57ae7..73a9eb2 100644 --- a/libs/agent_framework/src/agent_framework/routing/enterprise_router.py +++ b/libs/agent_framework/src/agent_framework/routing/enterprise_router.py @@ -181,6 +181,45 @@ class EnterpriseRouter: pos = found return True + @classmethod + def _ordered_content_keyword_match(cls, keyword: str, text: str, *, max_gap: int = 4) -> bool: + """Match determinístico tolerante à omissão de conectores curtos. + + Alguns ``routing.yaml`` usam frases naturais como ``qual é o meu plano``. + A mesma intenção pode chegar como ``qual o meu plano``. O matcher legado + falhava porque exigia também o token ``e`` (resultado da normalização de + ``é``). Aqui tokens de até dois caracteres são tratados como conectores + opcionais *apenas no lado da keyword*. Os tokens informativos continuam + obrigatórios, em ordem e próximos entre si. + + A heurística é propositalmente linguística-neutra e não contém nomes de + intents, agentes, domínios ou listas de verbos de negócio. Assim funciona + com qualquer configuração carregada pelo ``routing.yaml`` sem LLM extra. + """ + wanted_all = cls._keyword_tokens(keyword) + actual = cls._keyword_tokens(text) + if len(wanted_all) < 2 or not actual: + return False + + wanted = [token for token in wanted_all if len(token) > 2] + # Exigimos pelo menos dois tokens informativos para não transformar + # keywords curtas em matches amplos demais. + if len(wanted) < 2 or len(wanted) == len(wanted_all): + return False + + pos = -1 + for token in wanted: + found = None + upper = min(len(actual), pos + max_gap + 2) + for idx in range(pos + 1, upper): + if actual[idx] == token: + found = idx + break + if found is None: + return False + pos = found + return True + def _route_by_keyword(self, text: str) -> RouteDecision | None: normalized = text.casefold() matches: list[tuple[int, int, int, IntentDefinition, str, str]] = [] @@ -195,10 +234,17 @@ class EnterpriseRouter: strategy = "exact" elif self._ordered_keyword_match(kw, text): strategy = "ordered_tokens" + elif self._ordered_content_keyword_match(kw, text): + strategy = "ordered_content_tokens" if strategy: - # menor priority vence; exact vence fuzzy; keyword maior desempata - strategy_rank = 0 if strategy == "exact" else 1 + # menor priority vence; estratégias mais estritas vencem as relaxadas; + # keyword maior desempata dentro da mesma prioridade/estratégia. + strategy_rank = { + "exact": 0, + "ordered_tokens": 1, + "ordered_content_tokens": 2, + }[strategy] matches.append((intent.priority, strategy_rank, -len(kw), intent, kw, strategy)) if not matches: return None @@ -208,11 +254,19 @@ class EnterpriseRouter: route=intent.agent, agent=intent.agent, intent=intent.name, - confidence=0.85 if strategy == "exact" else 0.82, + confidence={ + "exact": 0.85, + "ordered_tokens": 0.82, + "ordered_content_tokens": 0.80, + }[strategy], reason=( f"Keyword '{kw}' correspondeu à intent '{intent.name}'." if strategy == "exact" - else f"Sequência de tokens da keyword '{kw}' correspondeu à intent '{intent.name}'." + else ( + f"Sequência de tokens da keyword '{kw}' correspondeu à intent '{intent.name}'." + if strategy == "ordered_tokens" + else f"Tokens informativos da keyword '{kw}' corresponderam à intent '{intent.name}'." + ) ), method="keyword", metadata={"matched_keyword": kw, "keyword_match_strategy": strategy}, 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 e38d1fb..eaad4f7 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 6ac1eb5..6f9eb51 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/workflows/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc index 8a90d5e..a3a6732 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 90cd525..6a94d35 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__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc index bef74f6..fa5b08c 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 32218f8..3c97c82 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 f1bf091..8dc468d 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 45e15b4..bf31e71 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 d574549..3f5f26a 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/templates/agent_template_backend/.env b/templates/agent_template_backend/.env deleted file mode 100644 index 4556734..0000000 --- a/templates/agent_template_backend/.env +++ /dev/null @@ -1,207 +0,0 @@ -############################################################################### -# AI AGENT PLATFORM - CONFIGURAÇÃO ÚNICA -# Este arquivo é lido por Pydantic Settings no framework e no backend template. -############################################################################### - -APP_NAME=ai-agent-template -APP_ENV=local -LOG_LEVEL=INFO -API_HOST=0.0.0.0 -API_PORT=8000 -CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173 - -############################################################################### -# LLM - OCI Generative AI como provider principal -############################################################################### -# Opções: mock, oci_openai, oci_sdk, openai_compatible -LLM_PROVIDER=oci_sdk -LLM_TEMPERATURE=0.2 -LLM_MAX_TOKENS=2048 -LLM_TIMEOUT_SECONDS=120 - -# OCI OpenAI-compatible endpoint -OCI_GENAI_BASE_URL=https://inference.generativeai.us-chicago-1.oci.oraclecloud.com -OCI_GENAI_MODEL=openai.gpt-4.1 -OCI_GENAI_API_KEY=sk-ph3FgX6iP3fxAQCXb9IpPIDTadkeeYAWntUWhzcWysIM6zsS -OCI_GENAI_PROJECT_OCID= - -#OCI_GENAI_BASE_URL=https://pegruagntaiatenddev.pe.inference.generativeai.sa-saopaulo-1.oci.oraclecloud.com -#OCI_GENAI_MODEL=openai.gpt-4.1 -#OCI_GENAI_API_KEY= -#OCI_GENAI_PROJECT_OCID= - - -# OCI_AUTH_MODE=config_file|instance_principal|resource_principal -OCI_AUTH_MODE=config_file -# OCI SDK / signer / profiles -OCI_CONFIG_FILE=~/.oci/config -OCI_PROFILE=LATINOAMERICA-Chicago -OCI_COMPARTMENT_ID=ocid1.compartment.oc1..aaaaaaaaexpiw4a7dio64mkfv2t273s2hgdl6mgfvvyv7tycalnjlvpvfl3q -OCI_REGION=us-chicago-1 - -############################################################################### -# Persistência -############################################################################### -# Opções: memory, autonomous, mongodb -SESSION_REPOSITORY_PROVIDER=autonomous -MEMORY_REPOSITORY_PROVIDER=autonomous -CHECKPOINT_REPOSITORY_PROVIDER=autonomous - -# Autonomous Database -ADB_USER=admin -ADB_PASSWORD=Moniquinha19721972 -ADB_DSN=oradb23ai_high -ADB_WALLET_LOCATION=/mnt/d/Dropbox/ORACLE/LatinoAmerica/Wallet_ORADB23ai -ADB_WALLET_PASSWORD=Moniquinha1972 -ADB_TABLE_PREFIX=AGENTFW - -# MongoDB - também pode representar Autonomous usando API compatível com Mongo, se habilitada no ambiente -MONGODB_URI=mongodb://mongo:mongopassword@localhost:27017 -MONGODB_DATABASE=agent_platform - -# Redis -REDIS_URL=redis://localhost:6379/0 -ENABLE_REDIS_CACHE=false - -############################################################################### -# RAG / Vector / Graph -############################################################################### -VECTOR_STORE_PROVIDER=autonomous -GRAPH_STORE_PROVIDER=autonomous -RAG_TOP_K=5 -EMBEDDING_PROVIDER=oci -OCI_EMBEDDING_MODEL=cohere.embed-multilingual-v3.0 -RAG_FILE_GLOBS=*.md,*.txt,*.yaml,*.yml,*.json - -############################################################################### -# Observabilidade -############################################################################### -ENABLE_LANGFUSE=true - # Opcional: verbose, compact -LANGFUSE_TRACE_MODE=compact -# Nome customizado do trace pai, ex.: backoffice.checklist.workflow ou backoffice.emulador.workflow -LANGFUSE_COMPACT_VISIBLE_EVENT_PREFIXES=AGA.,NOC., IC. -LANGFUSE_COMPACT_SUPPRESSED_PREFIXES=llm.chat_completion -LANGFUSE_IGNORE_HEALTHCHECKS=true -LANGFUSE_IGNORED_PATHS=/health,/ready,/metrics -LANGFUSE_PUBLIC_KEY=pk-lf-4a1e3921-5158-4fd3-a16d-7a77549fb312 -LANGFUSE_SECRET_KEY=sk-lf-efc6fd59-c5ec-4858-b6ec-4aa129734915 -LANGFUSE_HOST=http://localhost:3005 -ENABLE_OTEL=false -OTEL_EXPORTER_OTLP_ENDPOINT= -OTEL_SERVICE_NAME=ai-agent-template -ENABLE_LANGFUSE_OPENAI_AUTO_INSTRUMENTATION=true -ENABLE_LANGFUSE_ANALYTICS_PUBLISHER=false - -############################################################################### -# Analytics / Observer corporativo -############################################################################### -# Quando true, AgentObserver publica eventos IC.*, NOC.* e GRL.* nos providers abaixo. -ENABLE_ANALYTICS=false -# Providers aceitos: oci_streaming,pubsub,noop -ANALYTICS_PROVIDERS=oci_streaming -# Compatibilidade FIRST/TIM: pode informar AGENT_PUBSUB_TOPIC diretamente. -AGENT_PUBSUB_TOPIC= -GCP_PUBSUB_TOPIC_PATH= -GCP_PROJECT_ID= -GCP_PUBSUB_TOPIC= -GCP_PUBSUB_TIMEOUT_SECONDS=30 -# Credencial GCP segue padrão Google: -# GOOGLE_APPLICATION_CREDENTIALS=/secrets/gcp-service-account.json - -############################################################################### -# OCI Streaming -############################################################################### -ENABLE_OCI_STREAMING=false -OCI_STREAM_ENDPOINT= -OCI_STREAM_OCID= -OCI_STREAM_PARTITION_KEY=agent-events - -############################################################################### -# Guardrails, Judges, Supervisor -############################################################################### -ENABLE_INPUT_GUARDRAILS=true -ENABLE_OUTPUT_GUARDRAILS=true -ENABLE_JUDGES=true -ENABLE_SUPERVISOR=true -ENABLE_OUTPUT_SUPERVISOR=true -ENABLE_PARALLEL_GUARDRAILS=true -GUARDRAILS_FAIL_FAST=true -OUTPUT_SUPERVISOR_MAX_RETRIES=3 -GUARDRAILS_CONFIG_PATH=./config/guardrails.yaml -JUDGES_CONFIG_PATH=./config/judges.yaml -PROMPT_POLICY_PATH=./config/prompt_policy.yaml - -############################################################################### -# Gateway de canais -############################################################################### -DEFAULT_CHANNEL=web -# embedded = backend may parse simple/native channel payloads. -# external = backend only accepts GatewayRequest normalized by an external Channel Gateway. -FRAMEWORK_CHANNEL_INPUT_MODE=embedded -ENABLE_VOICE_ADAPTER=true -ENABLE_WHATSAPP_ADAPTER=true -ENABLE_TEXT_ADAPTER=true - -################################################# -# ENTERPRISE ROUTING -################################################# -# Arquivo YAML com intents, keywords, políticas de estado e fallback. -ROUTING_CONFIG_PATH=./config/routing.yaml -# true = usa LLM para classificar quando keywords/estado não resolverem. -# Em produção, costuma ser útil; em desenvolvimento, false evita custo e latência. -ENABLE_LLM_ROUTER=true - -# Semantic route stickiness (optional). -# Uses a lightweight LLM profile to decide only CONTINUE vs ROUTE. -# There are no regexes or deterministic language rules. -ENABLE_ROUTE_STICKINESS=true -ROUTE_STICKINESS_LLM_PROFILE=route_continuity -ROUTE_STICKINESS_CONFIDENCE_THRESHOLD=0.90 -ROUTE_STICKINESS_HISTORY_TURNS=2 -ROUTE_STICKINESS_MAX_TOKENS=80 -HUMAN_HANDOFF_MESSAGE=Vou encaminhar seu atendimento para uma pessoa. -END_SESSION_MESSAGE=Atendimento encerrado. Obrigado pelo contato. - -############################################################################### -# MCP / Tools -############################################################################### -ENABLE_MCP_TOOLS=true -MCP_SERVERS_CONFIG_PATH=./config/mcp_servers.yaml -TOOLS_CONFIG_PATH=./config/tools.yaml -MCP_TOOL_TIMEOUT_SECONDS=30 - -# router = EnterpriseRouter seleciona um agente; supervisor = pode acionar múltiplos agentes -ROUTING_MODE=router - -# Usage/cost accounting -USAGE_REPOSITORY_PROVIDER=autonomous -IDENTITY_CONFIG_PATH=./config/identity.yaml -MCP_PARAMETER_MAPPING_PATH=./config/mcp_parameter_mapping.yaml - -# ----------------------------------------------------------------------------- -# ConversationSummaryMemory / compressão de contexto conversacional -# ----------------------------------------------------------------------------- -ENABLE_CONVERSATION_SUMMARY_MEMORY=true -MEMORY_CONTEXT_STRATEGY=summary -MEMORY_HISTORY_LIMIT=80 -MEMORY_RECENT_MESSAGES_LIMIT=8 -MEMORY_SUMMARY_TRIGGER_MESSAGES=20 -MEMORY_MAX_SUMMARY_CHARS=6000 -MEMORY_SUMMARY_USE_LLM=true -MEMORY_INJECT_RECENT_MESSAGES=true -MEMORY_INJECT_SUMMARY=true - -############################################################################### -# LONG-TERM MEMORY -############################################################################### -ENABLE_LONG_TERM_MEMORY=true -LONG_TERM_MEMORY_PROVIDER=sqlite -LONG_TERM_MEMORY_SQLITE_PATH=./data/agent_framework.db -LONG_TERM_MEMORY_TABLE=agentfw_long_term_memory -# For Autonomous/Oracle, defaults to ${ADB_TABLE_PREFIX}_LONG_TERM_MEMORY -# LONG_TERM_MEMORY_ORACLE_TABLE=AGENTFW_LONG_TERM_MEMORY -LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS=20 -LONG_TERM_MEMORY_MIN_CONFIDENCE=0.70 -LONG_TERM_MEMORY_AUTO_EXTRACT=true -LONG_TERM_MEMORY_INJECT_CONTEXT=true diff --git a/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc index 8a69da5..ef585a7 100644 Binary files a/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc and b/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/__pycache__/test_route_stickiness_transaction_shift.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_route_stickiness_transaction_shift.cpython-313-pytest-9.0.2.pyc index d7f4107..aebaf3a 100644 Binary files a/tests/__pycache__/test_route_stickiness_transaction_shift.cpython-313-pytest-9.0.2.pyc and b/tests/__pycache__/test_route_stickiness_transaction_shift.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/__pycache__/test_transactional_tool_flow.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_transactional_tool_flow.cpython-313-pytest-9.0.2.pyc index 499b13b..8850608 100644 Binary files a/tests/__pycache__/test_transactional_tool_flow.cpython-313-pytest-9.0.2.pyc and b/tests/__pycache__/test_transactional_tool_flow.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/test_route_stickiness_transaction_shift.py b/tests/test_route_stickiness_transaction_shift.py index febad44..261c677 100644 --- a/tests/test_route_stickiness_transaction_shift.py +++ b/tests/test_route_stickiness_transaction_shift.py @@ -158,3 +158,83 @@ def test_ordered_keyword_match_is_conservative(): assert EnterpriseRouter._ordered_keyword_match("cancelar pedido", "quero cancelar meu pedido") is True assert EnterpriseRouter._ordered_keyword_match("cancelar pedido", "quero pedido e talvez cancelar depois") is False assert EnterpriseRouter._ordered_keyword_match("pedido", "meu pedido") is False + + +def test_ordered_content_keyword_match_tolerates_omitted_short_connectors(): + assert EnterpriseRouter._ordered_content_keyword_match( + "qual é o meu plano", "qual o meu plano" + ) is True + assert EnterpriseRouter._ordered_content_keyword_match( + "qual é o meu plano", "quero ver minha fatura" + ) is False + + +@pytest.mark.asyncio +async def test_loaded_domain_style_plan_shift_preempts_continuity_without_llm(): + """Regression: invoice -> plan must work from configured keywords, even same agent. + + This mirrors applications such as Contas where two intents may be handled by + the same agent but require different MCP tools. The routing core must not + know application-specific intent or agent names. + """ + from agent_framework.routing.models import IntentDefinition + + class _Continuity: + async def evaluate(self, state, *, intents): + raise AssertionError("continuity LLM must not run for explicit configured intent shift") + + router = object.__new__(EnterpriseRouter) + router.state_policies = [] + router.intents = [ + IntentDefinition( + name="domain_plan_information", + domain="customer_domain", + agent="customer_agent", + description="informações de plano", + priority=142, + mcp_tools=["consultar_plano"], + keywords=["qual é o meu plano", "plano contratado"], + examples=[], + enabled=True, + ), + IntentDefinition( + name="domain_invoice_query", + domain="customer_domain", + agent="customer_agent", + description="consulta de fatura", + priority=140, + mcp_tools=["consultar_faturas"], + keywords=["quero minha fatura", "fatura atual"], + examples=[], + enabled=True, + ), + ] + router.continuity = _Continuity() + router.enable_llm_router = False + router.llm = None + router.telemetry = None + router.fallback_agent = "customer_agent" + + message = "qual o meu plano" + decision = await router.route({ + "user_text": message, + "sanitized_input": message, + "active_agent": "customer_agent", + "intent": "domain_invoice_query", + "route_decision": { + "route": "customer_agent", + "agent": "customer_agent", + "intent": "domain_invoice_query", + "domain": "customer_domain", + "mcp_tools": ["consultar_faturas"], + }, + "context": {"session": {}}, + }) + + assert decision.intent == "domain_plan_information" + assert decision.agent == "customer_agent" + assert decision.mcp_tools == ["consultar_plano"] + assert decision.method == "keyword" + assert decision.metadata["route_stickiness_preempted"] is True + assert decision.metadata["previous_intent"] == "domain_invoice_query" + assert decision.metadata["keyword_match_strategy"] == "ordered_content_tokens" diff --git a/tests/unit/__pycache__/test_semantic_route_stickiness.cpython-313-pytest-9.0.2.pyc b/tests/unit/__pycache__/test_semantic_route_stickiness.cpython-313-pytest-9.0.2.pyc index 6ee1938..88a14b6 100644 Binary files a/tests/unit/__pycache__/test_semantic_route_stickiness.cpython-313-pytest-9.0.2.pyc and b/tests/unit/__pycache__/test_semantic_route_stickiness.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/unit/__pycache__/test_transactional_workflows.cpython-313-pytest-9.0.2.pyc b/tests/unit/__pycache__/test_transactional_workflows.cpython-313-pytest-9.0.2.pyc index 7415b27..09c30aa 100644 Binary files a/tests/unit/__pycache__/test_transactional_workflows.cpython-313-pytest-9.0.2.pyc and b/tests/unit/__pycache__/test_transactional_workflows.cpython-313-pytest-9.0.2.pyc differ