Ajustes no agente de contas
This commit is contained in:
Binary file not shown.
@@ -1,6 +1,8 @@
|
|||||||
from app.agents.prompting import apply_agent_profile_prompt
|
from app.agents.prompting import apply_agent_profile_prompt
|
||||||
from app.agents.contas_prompting import load_domain_prompt
|
from app.agents.contas_prompting import load_domain_prompt
|
||||||
from app.agents.runtime import AgentRuntimeMixin
|
from app.agents.runtime import AgentRuntimeMixin
|
||||||
|
import re
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
|
|
||||||
class FaturasAgent(AgentRuntimeMixin):
|
class FaturasAgent(AgentRuntimeMixin):
|
||||||
@@ -31,6 +33,74 @@ class FaturasAgent(AgentRuntimeMixin):
|
|||||||
self.guardrail_pipeline = guardrail_pipeline
|
self.guardrail_pipeline = guardrail_pipeline
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _normalize_text(value):
|
||||||
|
text = unicodedata.normalize("NFKD", str(value or ""))
|
||||||
|
return "".join(ch for ch in text if not unicodedata.combining(ch)).lower().strip()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _is_live_internet_balance_request(cls, state):
|
||||||
|
text = cls._normalize_text(
|
||||||
|
state.get("sanitized_input") or state.get("user_text") or state.get("input") or ""
|
||||||
|
)
|
||||||
|
if not text:
|
||||||
|
return False
|
||||||
|
|
||||||
|
internet = bool(re.search(r"\b(internet|dados moveis|dados|franquia)\b", text))
|
||||||
|
live_balance = bool(
|
||||||
|
re.search(
|
||||||
|
r"\b(quanto.*(?:resta|restou|tenho|sobrou|disponivel)|"
|
||||||
|
r"saldo|restante|restam|ainda tenho|ainda posso usar|consumo atual)\b",
|
||||||
|
text,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return internet and live_balance
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _has_live_internet_balance_evidence(cls, tool_context):
|
||||||
|
# Não bloqueia uma integração futura que passe a devolver o saldo em tempo real.
|
||||||
|
evidence_keys = {
|
||||||
|
"saldo_internet",
|
||||||
|
"internet_balance",
|
||||||
|
"remaining_data",
|
||||||
|
"remaining_data_mb",
|
||||||
|
"remaining_data_gb",
|
||||||
|
"saldo_dados",
|
||||||
|
"franquia_disponivel",
|
||||||
|
"consumo_atual",
|
||||||
|
"current_usage",
|
||||||
|
}
|
||||||
|
|
||||||
|
def walk(value):
|
||||||
|
if isinstance(value, dict):
|
||||||
|
for key, nested in value.items():
|
||||||
|
if cls._normalize_text(key).replace(" ", "_") in evidence_keys and nested not in (None, "", [], {}):
|
||||||
|
return True
|
||||||
|
if walk(nested):
|
||||||
|
return True
|
||||||
|
elif isinstance(value, list):
|
||||||
|
return any(walk(item) for item in value)
|
||||||
|
return False
|
||||||
|
|
||||||
|
for item in tool_context or []:
|
||||||
|
if not isinstance(item, dict) or item.get("ok") is False:
|
||||||
|
continue
|
||||||
|
if walk(item.get("result")):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _live_internet_balance_guidance(cls, state, tool_context):
|
||||||
|
if not cls._is_live_internet_balance_request(state):
|
||||||
|
return None
|
||||||
|
if cls._has_live_internet_balance_evidence(tool_context):
|
||||||
|
return None
|
||||||
|
return (
|
||||||
|
"Não consigo consultar o saldo de internet em tempo real por aqui. "
|
||||||
|
"Para ver quanto ainda resta neste mês, consulte o app Meu TIM, "
|
||||||
|
"onde você acompanha o consumo atual da sua franquia."
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _handoff_patch_from_tool_context(tool_context):
|
def _handoff_patch_from_tool_context(tool_context):
|
||||||
for item in tool_context or []:
|
for item in tool_context or []:
|
||||||
@@ -93,6 +163,17 @@ class FaturasAgent(AgentRuntimeMixin):
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
live_balance_guidance = self._live_internet_balance_guidance(state, tool_context)
|
||||||
|
if live_balance_guidance:
|
||||||
|
return {
|
||||||
|
"answer": f"[FaturasAgent] {live_balance_guidance}",
|
||||||
|
"next_state": state.get("next_state") or "ACTIVE",
|
||||||
|
"mcp_results": tool_context,
|
||||||
|
"rag": {"enabled": False, "skipped": True, "reason": "live_internet_balance_not_available"},
|
||||||
|
**self.transaction_state_patch(state),
|
||||||
|
**handoff_patch,
|
||||||
|
}
|
||||||
|
|
||||||
direct_answer = self.build_direct_mcp_answer(state, tool_context, agent_label="FaturasAgent")
|
direct_answer = self.build_direct_mcp_answer(state, tool_context, agent_label="FaturasAgent")
|
||||||
if direct_answer:
|
if direct_answer:
|
||||||
return {
|
return {
|
||||||
|
|||||||
29
docs/FIX_SCENARIO_23_LIVE_INTERNET_BALANCE_20260831.md
Normal file
29
docs/FIX_SCENARIO_23_LIVE_INTERNET_BALANCE_20260831.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Correção do cenário 23 — saldo de internet em tempo real
|
||||||
|
|
||||||
|
## Problema
|
||||||
|
|
||||||
|
O FaturasAgent respondia que não havia informação nos "dados consultados" sobre o saldo de internet restante mesmo quando nenhuma tool de consumo em tempo real havia sido executada. Isso criava uma alegação de consulta sem evidência funcional.
|
||||||
|
|
||||||
|
## Solução
|
||||||
|
|
||||||
|
A correção foi mantida no agente, sem alteração do framework. Para perguntas explícitas de saldo/franquia restante em tempo real, o FaturasAgent:
|
||||||
|
|
||||||
|
1. verifica se alguma tool bem-sucedida trouxe evidência de saldo/consumo atual;
|
||||||
|
2. se houver evidência, deixa o fluxo normal responder com os dados reais;
|
||||||
|
3. se não houver evidência, não afirma que consultou dados e orienta o cliente ao Meu TIM.
|
||||||
|
|
||||||
|
Resposta de fallback:
|
||||||
|
|
||||||
|
> Não consigo consultar o saldo de internet em tempo real por aqui. Para ver quanto ainda resta neste mês, consulte o app Meu TIM, onde você acompanha o consumo atual da sua franquia.
|
||||||
|
|
||||||
|
A regra não intercepta perguntas genéricas sobre internet/plano e não bloqueia uma integração futura que passe a devolver saldo real.
|
||||||
|
|
||||||
|
## Arquivo alterado
|
||||||
|
|
||||||
|
- `app/agents/faturas_agent.py`
|
||||||
|
|
||||||
|
## Testes
|
||||||
|
|
||||||
|
- `tests/migration/test_scenario_23_live_internet_balance_guidance.py`
|
||||||
|
- 3/3 testes novos PASS
|
||||||
|
- regressão `tests/migration`: 821 PASS / 2 FAIL preexistentes
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,21 @@
|
|||||||
|
from app.agents.faturas_agent import FaturasAgent
|
||||||
|
|
||||||
|
|
||||||
|
def test_s23_live_balance_without_backend_uses_meu_tim_guidance():
|
||||||
|
state = {"user_text": "quanto de internet eu ainda tenho pra usar esse mês?"}
|
||||||
|
msg = FaturasAgent._live_internet_balance_guidance(state, [])
|
||||||
|
assert msg is not None
|
||||||
|
assert "Não consigo consultar o saldo de internet em tempo real por aqui" in msg
|
||||||
|
assert "Meu TIM" in msg
|
||||||
|
assert "dados consultados" not in msg
|
||||||
|
|
||||||
|
|
||||||
|
def test_s23_generic_internet_question_is_not_intercepted():
|
||||||
|
state = {"user_text": "como funciona a internet do meu plano?"}
|
||||||
|
assert FaturasAgent._live_internet_balance_guidance(state, []) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_s23_real_time_backend_evidence_takes_precedence():
|
||||||
|
state = {"user_text": "quanto de internet eu ainda tenho pra usar esse mês?"}
|
||||||
|
tools = [{"ok": True, "result": {"remaining_data_gb": 7.5}}]
|
||||||
|
assert FaturasAgent._live_internet_balance_guidance(state, tools) is None
|
||||||
Reference in New Issue
Block a user