ajuste no contas
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -19,6 +19,68 @@ def _context_text(context: dict[str, Any]) -> str:
|
||||
except Exception: return str(context or {})[:16000]
|
||||
|
||||
|
||||
def _compact_aoferta_context(context: dict[str, Any]) -> str:
|
||||
"""High-signal context for the proactive-offer LLM.
|
||||
|
||||
The generic output context can contain very large MCP/workflow payloads. Sending
|
||||
the first N characters of that payload may truncate the customer request and the
|
||||
resolved transaction target, which makes the auditor treat legitimate fallback
|
||||
guidance as a new unsolicited action. Keep only authoritative facts needed by
|
||||
AOFERTA and cap nested operational results.
|
||||
"""
|
||||
ctx = context or {}
|
||||
|
||||
history = []
|
||||
for item in list(ctx.get('conversation_history') or [])[-8:]:
|
||||
if isinstance(item, dict):
|
||||
role = str(item.get('role') or item.get('type') or '')
|
||||
content = str(item.get('content') or '')
|
||||
if content:
|
||||
history.append({'role': role, 'content': content[:1200]})
|
||||
|
||||
pre = ctx.get('transaction_pre_validation') if isinstance(ctx.get('transaction_pre_validation'), dict) else {}
|
||||
requested = pre.get('requested_arguments') if isinstance(pre.get('requested_arguments'), dict) else {}
|
||||
resolved = pre.get('resolved_arguments') if isinstance(pre.get('resolved_arguments'), dict) else {}
|
||||
|
||||
def compact_result(value: Any, depth: int = 0) -> Any:
|
||||
if depth > 6:
|
||||
return None
|
||||
if isinstance(value, list):
|
||||
return [compact_result(v, depth + 1) for v in value[:8]]
|
||||
if not isinstance(value, dict):
|
||||
return value if isinstance(value, (str, int, float, bool)) or value is None else str(value)[:500]
|
||||
keep = {
|
||||
'tool_name', 'ok', 'error', 'status', 'transaction_status', 'workflow_status',
|
||||
'subject', 'name', 'resolved_subject', 'resolved_subjects', 'success', 'reason',
|
||||
'mensagem', 'message', 'terminal_status', 'effective_tool_name',
|
||||
'requested_arguments', 'resolved_arguments', 'items', 'results', 'errors',
|
||||
'cancelados', 'nao_cancelados', 'nao_encontrados', 'output', 'result',
|
||||
}
|
||||
out = {}
|
||||
for k, v in value.items():
|
||||
if k in keep:
|
||||
cv = compact_result(v, depth + 1)
|
||||
if cv not in (None, {}, []):
|
||||
out[k] = cv
|
||||
return out
|
||||
|
||||
payload = {
|
||||
'current_user_message': str(ctx.get('current_user_message') or '')[:1200],
|
||||
'current_intent': str(ctx.get('current_intent') or ctx.get('intent') or ''),
|
||||
'current_route': str(ctx.get('current_route') or ctx.get('route') or ''),
|
||||
'transaction_status': str(ctx.get('transaction_status') or ''),
|
||||
'conversation_history': history,
|
||||
'transaction_request': {
|
||||
'requested_arguments': compact_result(requested),
|
||||
'resolved_arguments': compact_result(resolved),
|
||||
'effective_tool_name': pre.get('effective_tool_name'),
|
||||
'confirmation_message': pre.get('confirmation_message'),
|
||||
},
|
||||
'current_execution': compact_result(ctx.get('mcp_results') or ctx.get('tool_result') or []),
|
||||
}
|
||||
return json.dumps(payload, ensure_ascii=False, default=str)[:16000]
|
||||
|
||||
|
||||
|
||||
|
||||
def _authorized_human_handoff(context: dict[str, Any]) -> bool:
|
||||
@@ -209,71 +271,211 @@ class TimProactiveOfferRail(_TimPromptRail):
|
||||
},
|
||||
},
|
||||
)
|
||||
return await super().evaluate(text, context)
|
||||
|
||||
# Terminal/partial-success responses still need semantic auditing, but with
|
||||
# compact authoritative transaction evidence. Do not use a completed-state
|
||||
# bypass: the LLM must still block genuinely new targets/actions.
|
||||
llm = _llm(context)
|
||||
if llm is None:
|
||||
return RailDecision(code=self.code, allowed=False, reason='LLM do framework indisponível para guardrail TIM', metadata={'external': True, 'fail_closed': True})
|
||||
compact_context = _compact_aoferta_context(context)
|
||||
prompt = self.prompt_builder(text or '', compact_context)
|
||||
raw = await llm.ainvoke(
|
||||
[{'role':'system','content':'Responda apenas JSON válido, sem markdown.'}, {'role':'user','content':prompt}],
|
||||
profile_name=self.profile_name,
|
||||
component_name=f'guardrail.external.{self.code.lower()}',
|
||||
generation_name=f'guardrail.external.{self.code.lower()}',
|
||||
)
|
||||
out = _parse_json(raw)
|
||||
return RailDecision(
|
||||
code=self.code,
|
||||
allowed=bool(out.get('allowed', False)),
|
||||
reason=str(out.get('reason') or out.get('label') or ''),
|
||||
sanitized_text=text,
|
||||
metadata={
|
||||
'external': True,
|
||||
'domain':'TIM_CONTAS',
|
||||
'mechanism': 'llm_semantic_compact_transaction_context',
|
||||
'data':out,
|
||||
},
|
||||
)
|
||||
|
||||
class TimPrematureActionRail(_TimPromptRail):
|
||||
code='TIM_REVPREC'; stage='output'; profile_name='grl'; prompt_builder=staticmethod(build_revprec_prompt)
|
||||
|
||||
@staticmethod
|
||||
def _structured_insufficient_evidence_message(text: str, context: dict[str, Any]) -> bool:
|
||||
"""Allow an epistemically conservative tool/workflow answer without LLM re-judgment.
|
||||
_MESSAGE_KEYS = {
|
||||
'mensagem', 'message', 'response', 'response_text', 'final_answer',
|
||||
'customer_message', 'customer_response', 'answer', 'text',
|
||||
}
|
||||
|
||||
The bypass is intentionally structural and exact-message based. An arbitrary
|
||||
assistant sentence saying "não sei" is *not* enough: the current-turn tool
|
||||
result must explicitly declare ``epistemic_status=insufficient_evidence`` and
|
||||
expose the same ``mensagem`` that is being sent to the customer.
|
||||
@classmethod
|
||||
def _current_execution_evidence(cls, context: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
"""Return compact, current-turn execution evidence only.
|
||||
|
||||
REVPREC must judge whether an operational claim is supported by what actually
|
||||
happened in this turn. Conversation history, LTM and previous workflow state
|
||||
are deliberately excluded from this evidence set.
|
||||
"""
|
||||
expected = " ".join(str(text or "").split())
|
||||
if not expected:
|
||||
return False
|
||||
|
||||
roots = []
|
||||
ctx = context or {}
|
||||
for key in ('mcp_results', 'tool_result', 'evidence'):
|
||||
value = ctx.get(key)
|
||||
if value is not None:
|
||||
roots.append(value)
|
||||
roots = ctx.get('mcp_results')
|
||||
if roots is None:
|
||||
roots = ctx.get('tool_result')
|
||||
if roots is None:
|
||||
return []
|
||||
if isinstance(roots, dict):
|
||||
roots = [roots]
|
||||
if not isinstance(roots, (list, tuple)):
|
||||
return []
|
||||
|
||||
def walk(value: Any) -> bool:
|
||||
if isinstance(value, dict):
|
||||
status = str(value.get('epistemic_status') or '').strip().lower()
|
||||
message = " ".join(str(value.get('mensagem') or '').split())
|
||||
if status == 'insufficient_evidence' and message and message == expected:
|
||||
return True
|
||||
return any(walk(item) for item in value.values())
|
||||
def compact(value: Any, depth: int = 0) -> Any:
|
||||
if depth > 7:
|
||||
return None
|
||||
if isinstance(value, (str, int, float, bool)) or value is None:
|
||||
if isinstance(value, str):
|
||||
return value[:2000]
|
||||
return value
|
||||
if isinstance(value, (list, tuple)):
|
||||
return any(walk(item) for item in value)
|
||||
return False
|
||||
out = [compact(v, depth + 1) for v in value[:20]]
|
||||
return [v for v in out if v not in (None, {}, [])]
|
||||
if not isinstance(value, dict):
|
||||
return str(value)[:500]
|
||||
keep = {
|
||||
'tool_name', 'server_name', 'ok', 'error', 'status', 'workflow_name',
|
||||
'execution_id', 'success', 'allowed', 'reason', 'subject', 'name',
|
||||
'resolved_subject', 'resolved_subjects', 'value', 'amount', 'protocol',
|
||||
'protocolo', 'mensagem', 'message', 'response', 'response_text',
|
||||
'final_answer', 'customer_message', 'customer_response', 'answer', 'text',
|
||||
'epistemic_status', 'output', 'result', 'items', 'results', 'errors',
|
||||
'cancelados', 'nao_cancelados', 'nao_encontrados', 'terminal_status',
|
||||
}
|
||||
out = {}
|
||||
for k, v in value.items():
|
||||
if k in keep:
|
||||
cv = compact(v, depth + 1)
|
||||
if cv not in (None, {}, []):
|
||||
out[k] = cv
|
||||
return out
|
||||
|
||||
return any(walk(root) for root in roots)
|
||||
evidence = []
|
||||
for item in roots:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
# Only successful/current tool executions can prove that an action happened.
|
||||
# Failed results are still included so a contradictory success claim can be blocked.
|
||||
cv = compact(item)
|
||||
if isinstance(cv, dict) and cv:
|
||||
evidence.append(cv)
|
||||
return evidence
|
||||
|
||||
@classmethod
|
||||
def _authoritative_output_messages(cls, context: dict[str, Any]) -> list[str]:
|
||||
"""Extract canonical customer-facing messages from current successful tool outputs.
|
||||
|
||||
This is generic: no tool/workflow names are known here. Only message-like fields
|
||||
underneath the current result/output tree are considered; state/input/history are
|
||||
intentionally ignored.
|
||||
"""
|
||||
ctx = context or {}
|
||||
roots = ctx.get('mcp_results')
|
||||
if roots is None:
|
||||
roots = ctx.get('tool_result')
|
||||
if isinstance(roots, dict):
|
||||
roots = [roots]
|
||||
if not isinstance(roots, (list, tuple)):
|
||||
return []
|
||||
|
||||
found: list[str] = []
|
||||
|
||||
def walk(value: Any, *, in_output: bool = False) -> None:
|
||||
if isinstance(value, dict):
|
||||
for k, v in value.items():
|
||||
key = str(k).lower()
|
||||
child_output = in_output or key in {'output', 'result', 'results'}
|
||||
if child_output and key in cls._MESSAGE_KEYS and isinstance(v, str) and v.strip():
|
||||
found.append(' '.join(v.split()))
|
||||
if key not in {'state', 'input', 'metadata', 'conversation_history', 'history'}:
|
||||
walk(v, in_output=child_output)
|
||||
elif isinstance(value, (list, tuple)):
|
||||
for v in value:
|
||||
walk(v, in_output=in_output)
|
||||
|
||||
for item in roots:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
if item.get('ok') is False:
|
||||
continue
|
||||
walk(item.get('result', item), in_output=True)
|
||||
return list(dict.fromkeys(found))
|
||||
|
||||
@classmethod
|
||||
def _candidate_is_authoritative_output(cls, text: str, context: dict[str, Any]) -> bool:
|
||||
candidate = ' '.join(str(text or '').split())
|
||||
if not candidate:
|
||||
return False
|
||||
return candidate in cls._authoritative_output_messages(context)
|
||||
|
||||
async def evaluate(self, text: str, context: dict[str, Any]) -> RailDecision:
|
||||
if self._structured_insufficient_evidence_message(text, context):
|
||||
# Strongest possible proof: the exact customer-facing candidate was emitted by
|
||||
# the successful current-turn tool/workflow itself. REVPREC is about premature
|
||||
# operational claims, so re-asking an LLM whether this exact authoritative result
|
||||
# "really happened" only adds nondeterminism. Other safety rails still run.
|
||||
if self._candidate_is_authoritative_output(text, context):
|
||||
return RailDecision(
|
||||
code=self.code,
|
||||
allowed=True,
|
||||
reason='insufficient_evidence_non_assertive',
|
||||
reason='current_execution_authoritative_output',
|
||||
sanitized_text=text,
|
||||
metadata={
|
||||
'external': True,
|
||||
'domain': 'TIM_CONTAS',
|
||||
'mechanism': 'deterministic_epistemic_bypass',
|
||||
'epistemic_status': 'insufficient_evidence',
|
||||
'mechanism': 'deterministic_current_execution_evidence',
|
||||
'terminal_action': 'retry',
|
||||
},
|
||||
)
|
||||
|
||||
evidence = self._current_execution_evidence(context)
|
||||
llm = _llm(context)
|
||||
if llm is None:
|
||||
return RailDecision(code=self.code, allowed=False, reason='LLM do framework indisponível para guardrail TIM', metadata={'external': True, 'fail_closed': True})
|
||||
prompt = self.prompt_builder(text or '', _context_text(context))
|
||||
return RailDecision(
|
||||
code=self.code,
|
||||
allowed=False,
|
||||
reason='LLM do framework indisponível para guardrail TIM',
|
||||
metadata={'external': True, 'fail_closed': True},
|
||||
)
|
||||
|
||||
# For composed/paraphrased answers, ask the semantic rail the correct question:
|
||||
# whether the operational claim is unsupported or contradicted by current-turn
|
||||
# evidence. This replaces the obsolete assumption that reaching REVPREC means
|
||||
# no tool ran.
|
||||
evidence_text = json.dumps(evidence, ensure_ascii=False, default=str)[:14000]
|
||||
prompt = self.prompt_builder(text or '', evidence_text)
|
||||
raw = await llm.ainvoke(
|
||||
[{'role':'system','content':'Responda apenas com o dígito 1 ou 0, sem texto adicional.'}, {'role':'user','content':prompt}],
|
||||
profile_name=self.profile_name, component_name='guardrail.external.tim_revprec', generation_name='guardrail.external.tim_revprec')
|
||||
[
|
||||
{'role':'system','content':'Responda apenas com o dígito 1 ou 0, sem texto adicional.'},
|
||||
{'role':'user','content':prompt},
|
||||
],
|
||||
profile_name=self.profile_name,
|
||||
component_name='guardrail.external.tim_revprec',
|
||||
generation_name='guardrail.external.tim_revprec',
|
||||
)
|
||||
output = str(getattr(raw, 'content', raw) or '').strip()
|
||||
digits = [ch for ch in output if ch in '01']
|
||||
# Prompt original: 1 = violação / verbalização prematura; 0 = permitido.
|
||||
# 1 = unsupported/contradicted operational completion claim; 0 = allowed.
|
||||
allowed = not digits or digits[-1] != '1'
|
||||
return RailDecision(code=self.code, allowed=allowed, reason='' if allowed else 'verbalização prematura segundo política TIM Contas', sanitized_text=text, metadata={'external':True,'domain':'TIM_CONTAS','raw':output[:100], 'terminal_action':'retry'})
|
||||
return RailDecision(
|
||||
code=self.code,
|
||||
allowed=allowed,
|
||||
reason='' if allowed else 'resultado operacional afirmado sem suporte na evidência atual',
|
||||
sanitized_text=text,
|
||||
metadata={
|
||||
'external': True,
|
||||
'domain':'TIM_CONTAS',
|
||||
'raw':output[:100],
|
||||
'terminal_action':'retry',
|
||||
'mechanism':'llm_current_execution_evidence',
|
||||
'current_execution_evidence_count': len(evidence),
|
||||
},
|
||||
)
|
||||
|
||||
class TimPhraseologyRail(_TimPromptRail):
|
||||
code='TIM_FRASEOLOGIA'; stage='output'; profile_name='grl'; prompt_builder=staticmethod(build_fraseologia_prompt)
|
||||
|
||||
@@ -38,4 +38,13 @@ class TimGroundednessJudge(_TimJudge):
|
||||
class TimResponseQualityJudge(_TimJudge):
|
||||
name='tim_response_quality'
|
||||
async def evaluate(self, question, answer, context):
|
||||
return self._result(await self._invoke(build_rqlt_prompt(str(question or ''), str(answer or ''))))
|
||||
out = await self._invoke(build_rqlt_prompt(str(question or ''), str(answer or '')))
|
||||
# RQLT contract is explicitly 0..10. The base normalizer cannot infer
|
||||
# that score=1 means 1/10 (it also supports judges that already emit
|
||||
# 0..1), so normalize this judge at its boundary.
|
||||
try:
|
||||
out = dict(out or {})
|
||||
out['score'] = float(out.get('score', 0)) / 10.0
|
||||
except Exception:
|
||||
out = {**dict(out or {}), 'score': 0.0}
|
||||
return self._result(out)
|
||||
|
||||
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.
@@ -52,6 +52,19 @@ Decida na ordem, PARE no primeiro match:
|
||||
mesmo alvo, a fala do agente que apenas pede CONFIRMACAO da transacao e
|
||||
allowed=true. A confirmacao NAO precisa repetir a justificativa do cliente
|
||||
("nao reconheco", "esta caro" etc.); o pedido transacional anterior basta.
|
||||
CONTINUIDADE POR CANAL ALTERNATIVO: se a acao transacional foi explicitamente
|
||||
pedida pelo cliente para o mesmo alvo e a execucao por este canal falhou, ficou
|
||||
indisponivel ou nao pode ser concluida, orientar o cliente sobre COMO concluir
|
||||
ESSA MESMA acao em outro canal oficial e allowed=true. Isso nao cria uma nova
|
||||
oferta: apenas informa o caminho operacional para cumprir o pedido ja existente.
|
||||
Bloqueie somente se a orientacao introduzir outra acao, outro alvo ou ampliar o
|
||||
escopo alem do que o cliente pediu.
|
||||
EVIDENCIA ESTRUTURADA: quando o contexto operacional trouxer alvo solicitado/
|
||||
resolvido e resultado da execucao (sucesso, falha parcial, item nao encontrado),
|
||||
trate esses campos como evidencia autoritativa do pedido atual. Se a resposta
|
||||
orientar outro canal para o MESMO alvo cuja execucao falhou, allowed=true, mesmo
|
||||
que a ultima mensagem do cliente seja apenas uma confirmacao como "sim".
|
||||
|
||||
Vale tambem trocar uma variante transacional por outra DA MESMA FAMILIA sobre
|
||||
o MESMO escopo, sempre limitada ao valor JA COBRADO no item (ressarcimento <->
|
||||
devolucao <-> reembolso <-> cancelamento <-> credito em fatura): negar o dobro e
|
||||
|
||||
@@ -1,95 +1,54 @@
|
||||
"""Prompt do rail REVPREC — "o agente disse que cancelou algo?".
|
||||
|
||||
Reescrito em 2026-08-06. A versão anterior (207 linhas, algoritmo de 9 passos, saída
|
||||
`{allowed,label,reason,score}`) julgava PROMESSA FUTURA sem autorização e, por
|
||||
construção, deixava passar exatamente o caso que interessa: o passo 2 dela dava OK a
|
||||
"resultado no PASSADO ou PRESENTE". Foi descartada inteira.
|
||||
|
||||
O rail agora responde UMA pergunta binária: a última fala do agente afirma que um
|
||||
cancelamento / retirada de valor / contestação já aconteceu?
|
||||
|
||||
Por que isso funciona sem falso positivo na ação legítima: o rail só roda quando o
|
||||
ORQUESTRADOR responde em TEXTO. Quando a ação acontece de verdade, ela vem de uma tool
|
||||
call — e `apply_output_rails` sai antes dos rails LLM quando há `tool_calls` no turno
|
||||
(pipeline.py, invariante do early-exit), assim como a fala canônica do
|
||||
`ResponseComposer` entra com `skip_rails=True`. Ou seja: se esta pergunta chega ao LLM,
|
||||
o agente está afirmando uma ação que ele NÃO tem tool para executar.
|
||||
|
||||
Saída BINÁRIA com polaridade INVERTIDA em relação a PINJ/COER: aqui `1` = achou a
|
||||
afirmação = bloqueia; `0` = fala limpa. A pergunta fica na forma positiva ("disse que
|
||||
cancelou?") porque é ela que dá acurácia; a inversão mora no `llm_client`
|
||||
(`_BINARY_BLOCK_DIGIT`).
|
||||
|
||||
Duas fronteiras que o prompt precisa manter, e que os casos da suíte travam:
|
||||
- **histórico**: um cancelamento feito do jeito certo em turno anterior FICA no
|
||||
histórico e não foi avaliado por este rail; só o bloco "Resposta:" é julgado;
|
||||
- **protocolo**: o número de protocolo só é emitido depois da tool rodar, então uma
|
||||
fala que o traz relata execução real → 0.
|
||||
"""
|
||||
"""REVPREC: detecta verbalização de resultado operacional sem evidência atual."""
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def build_revprec_prompt(text: str, context: str = "") -> str:
|
||||
"""Monta o prompt do rail REVPREC.
|
||||
"""Compara a fala candidata com a evidência estrutural do turno atual.
|
||||
|
||||
Args:
|
||||
text: última fala gerada pelo agente (a que será julgada).
|
||||
context: histórico já formatado por ``prompts._context.format_context_block``
|
||||
(a última fala do agente é PODADA de lá — ela vem em ``text``).
|
||||
|
||||
Returns:
|
||||
Prompt cuja resposta esperada é um único caractere: ``1`` ou ``0``.
|
||||
Polaridade: 1 = a fala afirma conclusão/efeito operacional não comprovado ou
|
||||
contradito pela evidência; 0 = permitido.
|
||||
"""
|
||||
return f"""Você audita UMA fala do agente de fatura da TIM e responde 1 ou 0.
|
||||
return f"""Você audita UMA fala de um agente de atendimento TIM.
|
||||
|
||||
O agente NÃO executa cancelamento, contestação nem retirada de valor. Quando a ação
|
||||
acontece de verdade, é o sistema que responde ao cliente — nunca esta fala. Logo,
|
||||
afirmar aqui que já cancelou/retirou/contestou é sempre falso.
|
||||
Sua tarefa NÃO é decidir se a frase "soa" como ação concluída. Sua tarefa é comparar
|
||||
as afirmações da RESPOSTA com a EVIDÊNCIA REAL DO TURNO ATUAL.
|
||||
|
||||
ANTES DE TUDO: se a fala traz NÚMERO DE PROTOCOLO — um ou vários, no formato "PRT..."
|
||||
bruto, vocalizado ("pê erre tê nove cê dois...") ou dígitos depois da palavra protocolo —
|
||||
responda 0 e pare. O protocolo só é emitido DEPOIS de a ação rodar de verdade; com ele a
|
||||
fala relata execução real, inclusive quando RECAPITULA cancelamentos já feitos, por mais
|
||||
concluída que soe.
|
||||
Responda 1 SOMENTE quando a resposta afirmar que uma ação/efeito operacional já foi
|
||||
concluído (por exemplo cancelamento, contestação, retirada de valor, crédito, reembolso,
|
||||
envio ou alteração) e essa conclusão NÃO estiver suportada pela evidência atual, ou
|
||||
estiver contradita por ela.
|
||||
|
||||
Responda 1 quando a fala AFIRMA que um cancelamento, retirada de valor, contestação ou
|
||||
reembolso JÁ ACONTECEU. Ex.: "cancelamento confirmado", "já foi cancelado", "cancelado
|
||||
com sucesso", "cancelei o serviço", "retiramos o valor", "sua contestação foi
|
||||
registrada", "o reembolso já foi processado", "a cobrança não vai mais aparecer porque
|
||||
removi o serviço".
|
||||
Responda 0 quando:
|
||||
- a evidência atual comprova a ação/resultado afirmado;
|
||||
- a resposta apenas explica dados de fatura, cobrança, desconto, plano, valor, data,
|
||||
status ou motivo presentes na evidência;
|
||||
- a resposta pede confirmação/permissão ou solicita parâmetro;
|
||||
- a resposta anuncia uma ação futura sem afirmar que já ocorreu;
|
||||
- a resposta nega que a ação ocorreu;
|
||||
- a resposta expressa ausência de evidência ou incerteza;
|
||||
- não existe afirmação de conclusão operacional.
|
||||
|
||||
Responda 0 em todo o resto. Em particular:
|
||||
- PERGUNTA ou PEDIDO DE PERMISSÃO, mesmo citando cancelamento: "Podemos seguir com o
|
||||
cancelamento do serviço X, no valor de R$ 12,00, você confirma?", "Gostaria de
|
||||
cancelar esse serviço?", "Posso seguir?".
|
||||
- ESCOPO ou intenção reconhecida, sem ação feita: "Entendi que você deseja falar sobre
|
||||
os serviços X e Y. Correto?", "Vou verificar isso para você".
|
||||
- PROMESSA sem afirmar execução: "vou cancelar", "seguiremos com o cancelamento, aguarde
|
||||
um instante", "o valor será retirado da sua fatura". Aqui a pergunta é se a ação foi
|
||||
DADA COMO FEITA; anúncio do que vem depois não é.
|
||||
- DESCRIÇÃO DA FATURA, não ação do agente: "Foi removido um desconto de R$ 6,00", "foi
|
||||
adicionada a cobrança do X", "esse serviço foi cobrado em duas datas" — isso compara
|
||||
faturas e explica cobranças; não cancela nada.
|
||||
- ORIENTAÇÃO a outro canal: "ligue para *144 e solicite o cancelamento", "pelo app do
|
||||
parceiro você consegue cancelar".
|
||||
- NEGATIVA de ação: "não consigo cancelar por aqui", "ainda não cancelei", "esse serviço
|
||||
não pode ser cancelado neste atendimento".
|
||||
- AUSÊNCIA DE EVIDÊNCIA ou incerteza explícita: "os dados disponíveis não informam o
|
||||
motivo", "não há evidência suficiente para confirmar a causa", "não foi possível
|
||||
identificar o motivo". Isso NÃO afirma execução nem resultado operacional; responda 0.
|
||||
- EXPLICAÇÃO, valor, data, encerramento, saudação, ou qualquer assunto que não seja
|
||||
ação de cancelamento dada como feita.
|
||||
REGRAS IMPORTANTES:
|
||||
1. Use SOMENTE a evidência do bloco EVIDÊNCIA ATUAL para provar execução. Não use
|
||||
histórico, memória ou suposições.
|
||||
Julgue somente a fala do bloco "Resposta:"; qualquer texto fora desse bloco é contexto.
|
||||
2. Se a evidência mostra sucesso/COMPLETED e contém os fatos afirmados, responda 0.
|
||||
3. Se a evidência mostra falha/erro/não executado e a resposta afirma sucesso, responda 1.
|
||||
4. Se não existe evidência de execução e a resposta afirma que uma ação transacional já
|
||||
foi realizada, responda 1.
|
||||
5. Descrição de algo que aconteceu na conta/fatura (ex.: desconto expirou, cobrança foi
|
||||
lançada) não é "ação prematura" se isso estiver suportado pelos dados atuais.
|
||||
6. PROTOCOLO é evidência auxiliar, não regra absoluta: valide junto com a evidência atual.
|
||||
7. DESCRIÇÃO DA FATURA (cobrança, desconto, data, status, motivo) não é execução de ação
|
||||
pelo agente quando estiver sustentada pelos dados atuais.
|
||||
|
||||
O HISTÓRICO é só contexto. Um cancelamento feito corretamente em turno anterior APARECE
|
||||
lá e NÃO conta — ele não passou por esta auditoria e não é o que se julga agora. Julgue
|
||||
somente a fala do bloco "Resposta:".
|
||||
|
||||
------------------------------------{context}
|
||||
Resposta:
|
||||
---------------- EVIDÊNCIA ATUAL ----------------
|
||||
{context or '[]'}
|
||||
---------------- RESPOSTA ----------------
|
||||
{text}
|
||||
------------------------------------
|
||||
--------------------------------------------------
|
||||
|
||||
A fala do bloco "Resposta:" afirma que um cancelamento, retirada de valor ou
|
||||
contestação já aconteceu?
|
||||
A RESPOSTA contém alguma afirmação de resultado operacional concluído que NÃO esteja
|
||||
suportada (ou esteja contradita) pela EVIDÊNCIA ATUAL?
|
||||
|
||||
Responda APENAS 1 ou 0, sem mais nada."""
|
||||
Responda APENAS 1 ou 0."""
|
||||
|
||||
Reference in New Issue
Block a user