diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..da7ea03 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/__pycache__/main.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..5b6ab51 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/__pycache__/main.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc new file mode 100644 index 0000000..77accaf Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/__pycache__/state.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/__pycache__/state.cpython-313.pyc new file mode 100644 index 0000000..8e74de7 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/__pycache__/state.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc new file mode 100644 index 0000000..cf67993 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc new file mode 100644 index 0000000..5c3f4a0 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc new file mode 100644 index 0000000..0e8cc29 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc new file mode 100644 index 0000000..00a5707 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc new file mode 100644 index 0000000..26cc262 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc new file mode 100644 index 0000000..f56e53d Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/billing_agent.py b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/billing_agent.py index aa60099..05254d1 100644 --- a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/billing_agent.py +++ b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/billing_agent.py @@ -95,7 +95,7 @@ class BillingAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em faturas. Responda com clareza, objetividade e sem sugerir ações não solicitadas. Use dados MCP quando disponíveis.", + "Você é um agente especialista em faturas.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade para responder somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente, como “contract_key”, “customer_key” ou “MSISDN”.\nPara consultas informativas de fatura, apresente somente dados de negócio necessários, como valor, vencimento, situação e itens cobrados.\nNão acrescente canais, telefones, códigos USSD, URLs, aplicativos, lojas, relatórios adicionais, procedimentos alternativos ou próximos passos que não tenham sido explicitamente retornados pela tool/RAG e solicitados pelo usuário.\nNão ofereça espontaneamente outras ações ou detalhamentos.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/orders_agent.py b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/orders_agent.py index f557bed..b793c26 100644 --- a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/orders_agent.py +++ b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/orders_agent.py @@ -95,7 +95,7 @@ class OrdersAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de pedidos de varejo. Use dados de tools quando disponíveis.", + "Você é um agente de pedidos de varejo.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso, alteração, troca, cancelamento ou qualquer mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/product_agent.py b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/product_agent.py index 34433f5..998dad6 100644 --- a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/product_agent.py +++ b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/product_agent.py @@ -95,7 +95,7 @@ class ProductAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em produtos, planos e serviços. Explique sem fazer oferta proativa e sem executar ações sem confirmação. Use dados MCP quando disponíveis.", + "Você é um agente especialista em produtos, planos e serviços.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nEm consultas meramente informativas, não exponha flags ou capacidades transacionais internas como can.cancel e não informe espontaneamente que algo pode ser cancelado, alterado, contratado, removido ou trocado. Só mencione capacidade transacional quando o usuário tiver solicitado essa ação.\nNão faça oferta proativa e não execute nem simule mutações sem a confirmação exigida pelo framework.\nNão acrescente canais, procedimentos ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/support_agent.py b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/support_agent.py index b4f0244..b15a0e4 100644 --- a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/support_agent.py +++ b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/agents/support_agent.py @@ -95,7 +95,7 @@ class SupportAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de suporte de varejo para troca, devolução e garantia.", + "Você é um agente de suporte de varejo para troca, devolução e garantia.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso nem simule troca, devolução, garantia ou outra mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..97bc580 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc new file mode 100644 index 0000000..823a1a3 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc new file mode 100644 index 0000000..a725202 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc new file mode 100644 index 0000000..19296b0 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc new file mode 100644 index 0000000..fce919c Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc new file mode 100644 index 0000000..bf35ba7 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..47fe127 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc new file mode 100644 index 0000000..05612ae Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..c39a0a8 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc index 009b7c1..79a8274 100644 Binary files a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/tool_renderers.py b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/tool_renderers.py index f77c47a..cb44202 100644 --- a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/tool_renderers.py +++ b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/tool_renderers.py @@ -13,7 +13,42 @@ def _money_brl(value: Any) -> str: def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: - return f"[{agent_label}] Fatura consultada: {result}." + """Renderiza somente campos de negócio seguros da fatura. + + Identificadores técnicos/PII presentes no payload MCP (por exemplo msisdn, + customer_id, document e business keys) não devem ser propagados ao usuário. + """ + lines = [f"[{agent_label}] Dados da sua fatura:"] + total = result.get("valor_total") + vencimento = result.get("vencimento") + status = result.get("status") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + if vencimento not in (None, ""): + lines.append(f"Vencimento: {vencimento}.") + if status not in (None, ""): + lines.append(f"Situação: {status}.") + + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if not isinstance(item, dict): + continue + description = item.get("descricao") or item.get("nome") + value = item.get("valor") + if description in (None, ""): + continue + if value is None: + rendered_items.append(str(description)) + else: + rendered_items.append(f"{description}: R$ {_money_brl(value)}") + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + + # Se não houver nenhum campo de negócio seguro além do cabeçalho, deixe a + # composição pela LLM/guardrails em vez de despejar o payload bruto. + return " ".join(lines) if len(lines) > 1 else None def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..8accd28 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc new file mode 100644 index 0000000..0b97098 Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc differ diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc new file mode 100644 index 0000000..192132d Binary files /dev/null and b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ 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 b8ed7bc..303bf51 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 @@ -494,6 +494,119 @@ class AgentWorkflow: "next_state": "SESSION_ENDED", } + @staticmethod + def _output_guardrail_context(state: dict) -> dict: + """Monta o contexto operacional do turno para os guardrails de saída. + + Mantém evidências/protocolos necessários aos rails, mas impede que uma + transação encerrada ou semanticamente interrompida governe o novo turno. + O histórico completo permanece no state/checkpoint para auditoria. + """ + ctx = dict(state.get("context", {}) or {}) + mcp_results = state.get("mcp_results") or [] + ctx["evidence"] = mcp_results or ctx.get("evidence") + ctx["tool_result"] = mcp_results or ctx.get("tool_result") + ctx["tool_executed"] = any(isinstance(r, dict) and r.get("ok") for r in mcp_results) + + history = list(state.get("history") or []) + current_user_text = str(state.get("user_text") or "").strip() + if current_user_text: + if ( + not history + or not isinstance(history[-1], dict) + or str(history[-1].get("content") or "") != current_user_text + or str(history[-1].get("role") or "") != "user" + ): + history.append({"role": "user", "content": current_user_text}) + + route_decision = state.get("route_decision") or {} + route_metadata = route_decision.get("metadata") if isinstance(route_decision, dict) else {} + route_metadata = route_metadata if isinstance(route_metadata, dict) else {} + pre_validation = state.get("transaction_pre_validation") or {} + pre_validation = pre_validation if isinstance(pre_validation, dict) else {} + tx_status = str( + state.get("transaction_status") or pre_validation.get("status") or "" + ).strip().upper() + terminal_tx = bool(pre_validation.get("terminal")) or tx_status in { + "COMPLETED", "FAILED", "CANCELLED", "BLOCKED", "OUT_OF_SCOPE" + } + semantic_intent_shift = ( + str(route_metadata.get("transaction_interruption") or "").strip().lower() + == "intent_shift" + ) + stickiness_intent_shift = bool(route_metadata.get("route_stickiness_preempted")) + should_isolate_history = semantic_intent_shift or (terminal_tx and stickiness_intent_shift) + + current_route = str( + state.get("route") + or (route_decision.get("route") if isinstance(route_decision, dict) else "") + or "" + ).strip() + current_intent = str( + state.get("intent") + or (route_decision.get("intent") if isinstance(route_decision, dict) else "") + or "" + ).strip() + ctx["current_user_message"] = current_user_text + ctx["current_route"] = current_route + ctx["current_intent"] = current_intent + + if should_isolate_history: + operational_history = ( + [{"role": "user", "content": current_user_text}] + if current_user_text else [] + ) + ctx["historical_transaction_ignored"] = True + ctx["historical_transaction_status"] = tx_status or ( + "INTERRUPTED" if semantic_intent_shift else "TERMINAL" + ) + if semantic_intent_shift: + ctx["historical_transaction_interruption"] = "intent_shift" + for stale_key in ( + "transaction_pre_validation", + "transaction_status", + "active_transaction", + "transaction", + ): + ctx.pop(stale_key, None) + else: + operational_history = history + + ctx["conversation_history"] = operational_history + ctx["history_texts"] = [ + str(item.get("content") or "") + for item in operational_history + if isinstance(item, dict) and item.get("content") not in (None, "") + ] + + protocols: list[str] = [] + seen: set[str] = set() + protocol_keys = { + "protocol_number", "protocolo_id", "interactionProtocol", + "protocolNumber", "finalizacao_protocol", + } + + def walk(value): + if isinstance(value, dict): + for key, item in value.items(): + if key in protocol_keys and item not in (None, ""): + text = str(item).strip() + if text and text not in seen: + seen.add(text) + protocols.append(text) + elif isinstance(item, (dict, list, tuple)): + walk(item) + elif isinstance(value, (list, tuple)): + for item in value: + walk(item) + + walk(mcp_results) + if protocols: + ctx["expected_protocols"] = protocols + ctx["requer_protocolo"] = True + ctx.setdefault("tipo_fluxo", "ajuste") + return ctx + async def output_supervisor(self, state): """Valida a resposta candidata com o OutputSupervisor corporativo. @@ -509,15 +622,15 @@ class AgentWorkflow: } candidate = state.get("answer") or "" - context = { - **(state.get("context") or {}), + context = self._output_guardrail_context(state) + context.update({ "tenant_id": state.get("tenant_id"), "agent_id": state.get("agent_id"), "session_id": state.get("conversation_key") or state.get("session_id"), "route": state.get("route"), "intent": state.get("intent"), "supervisor_attempt": int(state.get("supervisor_attempt", 0)), - } + }) async with self.telemetry.span( "workflow.output_supervisor", session_id=state.get("conversation_key") or state.get("session_id"), @@ -603,7 +716,7 @@ class AgentWorkflow: component="workflow.output_guardrails.start", ) final, decisions = await self.guardrails.run_output( - state["answer"], state.get("context", {}) + state["answer"], self._output_guardrail_context(state) ) for _decision in decisions: await self.guardrail_telemetry.evaluated("output", _decision) diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..2130345 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/__pycache__/main.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..005a6d8 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/__pycache__/main.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc new file mode 100644 index 0000000..2f42339 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/__pycache__/state.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/__pycache__/state.cpython-313.pyc new file mode 100644 index 0000000..8ee7f90 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/__pycache__/state.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc new file mode 100644 index 0000000..7214e11 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc new file mode 100644 index 0000000..f1bf0df Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc new file mode 100644 index 0000000..af63de6 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc new file mode 100644 index 0000000..bc4a814 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc new file mode 100644 index 0000000..52afdf3 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc new file mode 100644 index 0000000..a44cfd9 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/billing_agent.py b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/billing_agent.py index aa60099..05254d1 100644 --- a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/billing_agent.py +++ b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/billing_agent.py @@ -95,7 +95,7 @@ class BillingAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em faturas. Responda com clareza, objetividade e sem sugerir ações não solicitadas. Use dados MCP quando disponíveis.", + "Você é um agente especialista em faturas.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade para responder somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente, como “contract_key”, “customer_key” ou “MSISDN”.\nPara consultas informativas de fatura, apresente somente dados de negócio necessários, como valor, vencimento, situação e itens cobrados.\nNão acrescente canais, telefones, códigos USSD, URLs, aplicativos, lojas, relatórios adicionais, procedimentos alternativos ou próximos passos que não tenham sido explicitamente retornados pela tool/RAG e solicitados pelo usuário.\nNão ofereça espontaneamente outras ações ou detalhamentos.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/orders_agent.py b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/orders_agent.py index f557bed..b793c26 100644 --- a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/orders_agent.py +++ b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/orders_agent.py @@ -95,7 +95,7 @@ class OrdersAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de pedidos de varejo. Use dados de tools quando disponíveis.", + "Você é um agente de pedidos de varejo.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso, alteração, troca, cancelamento ou qualquer mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/product_agent.py b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/product_agent.py index 34433f5..998dad6 100644 --- a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/product_agent.py +++ b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/product_agent.py @@ -95,7 +95,7 @@ class ProductAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em produtos, planos e serviços. Explique sem fazer oferta proativa e sem executar ações sem confirmação. Use dados MCP quando disponíveis.", + "Você é um agente especialista em produtos, planos e serviços.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nEm consultas meramente informativas, não exponha flags ou capacidades transacionais internas como can.cancel e não informe espontaneamente que algo pode ser cancelado, alterado, contratado, removido ou trocado. Só mencione capacidade transacional quando o usuário tiver solicitado essa ação.\nNão faça oferta proativa e não execute nem simule mutações sem a confirmação exigida pelo framework.\nNão acrescente canais, procedimentos ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/support_agent.py b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/support_agent.py index b4f0244..b15a0e4 100644 --- a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/support_agent.py +++ b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/agents/support_agent.py @@ -95,7 +95,7 @@ class SupportAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de suporte de varejo para troca, devolução e garantia.", + "Você é um agente de suporte de varejo para troca, devolução e garantia.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso nem simule troca, devolução, garantia ou outra mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..e9246dd Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc new file mode 100644 index 0000000..5961c07 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc new file mode 100644 index 0000000..6674af3 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc new file mode 100644 index 0000000..37c8d76 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc new file mode 100644 index 0000000..3e4fe4b Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc new file mode 100644 index 0000000..a9c17ae Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/extensions/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/extensions/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..a1382b0 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/extensions/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/extensions/__pycache__/example_guardrails.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/extensions/__pycache__/example_guardrails.cpython-313.pyc new file mode 100644 index 0000000..4a27a18 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/extensions/__pycache__/example_guardrails.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/extensions/__pycache__/example_judges.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/extensions/__pycache__/example_judges.cpython-313.pyc new file mode 100644 index 0000000..d82ff08 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/extensions/__pycache__/example_judges.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..14323bf Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc new file mode 100644 index 0000000..15b7866 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..e34e541 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc new file mode 100644 index 0000000..2885953 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/presentation/tool_renderers.py b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/presentation/tool_renderers.py index f77c47a..cb44202 100644 --- a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/presentation/tool_renderers.py +++ b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/presentation/tool_renderers.py @@ -13,7 +13,42 @@ def _money_brl(value: Any) -> str: def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: - return f"[{agent_label}] Fatura consultada: {result}." + """Renderiza somente campos de negócio seguros da fatura. + + Identificadores técnicos/PII presentes no payload MCP (por exemplo msisdn, + customer_id, document e business keys) não devem ser propagados ao usuário. + """ + lines = [f"[{agent_label}] Dados da sua fatura:"] + total = result.get("valor_total") + vencimento = result.get("vencimento") + status = result.get("status") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + if vencimento not in (None, ""): + lines.append(f"Vencimento: {vencimento}.") + if status not in (None, ""): + lines.append(f"Situação: {status}.") + + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if not isinstance(item, dict): + continue + description = item.get("descricao") or item.get("nome") + value = item.get("valor") + if description in (None, ""): + continue + if value is None: + rendered_items.append(str(description)) + else: + rendered_items.append(f"{description}: R$ {_money_brl(value)}") + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + + # Se não houver nenhum campo de negócio seguro além do cabeçalho, deixe a + # composição pela LLM/guardrails em vez de despejar o payload bruto. + return " ".join(lines) if len(lines) > 1 else None def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc new file mode 100644 index 0000000..7e37046 Binary files /dev/null and b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ 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 0a12c4b..2cb86e1 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 @@ -492,6 +492,119 @@ class AgentWorkflow: "next_state": "SESSION_ENDED", } + @staticmethod + def _output_guardrail_context(state: dict) -> dict: + """Monta o contexto operacional do turno para os guardrails de saída. + + Mantém evidências/protocolos necessários aos rails, mas impede que uma + transação encerrada ou semanticamente interrompida governe o novo turno. + O histórico completo permanece no state/checkpoint para auditoria. + """ + ctx = dict(state.get("context", {}) or {}) + mcp_results = state.get("mcp_results") or [] + ctx["evidence"] = mcp_results or ctx.get("evidence") + ctx["tool_result"] = mcp_results or ctx.get("tool_result") + ctx["tool_executed"] = any(isinstance(r, dict) and r.get("ok") for r in mcp_results) + + history = list(state.get("history") or []) + current_user_text = str(state.get("user_text") or "").strip() + if current_user_text: + if ( + not history + or not isinstance(history[-1], dict) + or str(history[-1].get("content") or "") != current_user_text + or str(history[-1].get("role") or "") != "user" + ): + history.append({"role": "user", "content": current_user_text}) + + route_decision = state.get("route_decision") or {} + route_metadata = route_decision.get("metadata") if isinstance(route_decision, dict) else {} + route_metadata = route_metadata if isinstance(route_metadata, dict) else {} + pre_validation = state.get("transaction_pre_validation") or {} + pre_validation = pre_validation if isinstance(pre_validation, dict) else {} + tx_status = str( + state.get("transaction_status") or pre_validation.get("status") or "" + ).strip().upper() + terminal_tx = bool(pre_validation.get("terminal")) or tx_status in { + "COMPLETED", "FAILED", "CANCELLED", "BLOCKED", "OUT_OF_SCOPE" + } + semantic_intent_shift = ( + str(route_metadata.get("transaction_interruption") or "").strip().lower() + == "intent_shift" + ) + stickiness_intent_shift = bool(route_metadata.get("route_stickiness_preempted")) + should_isolate_history = semantic_intent_shift or (terminal_tx and stickiness_intent_shift) + + current_route = str( + state.get("route") + or (route_decision.get("route") if isinstance(route_decision, dict) else "") + or "" + ).strip() + current_intent = str( + state.get("intent") + or (route_decision.get("intent") if isinstance(route_decision, dict) else "") + or "" + ).strip() + ctx["current_user_message"] = current_user_text + ctx["current_route"] = current_route + ctx["current_intent"] = current_intent + + if should_isolate_history: + operational_history = ( + [{"role": "user", "content": current_user_text}] + if current_user_text else [] + ) + ctx["historical_transaction_ignored"] = True + ctx["historical_transaction_status"] = tx_status or ( + "INTERRUPTED" if semantic_intent_shift else "TERMINAL" + ) + if semantic_intent_shift: + ctx["historical_transaction_interruption"] = "intent_shift" + for stale_key in ( + "transaction_pre_validation", + "transaction_status", + "active_transaction", + "transaction", + ): + ctx.pop(stale_key, None) + else: + operational_history = history + + ctx["conversation_history"] = operational_history + ctx["history_texts"] = [ + str(item.get("content") or "") + for item in operational_history + if isinstance(item, dict) and item.get("content") not in (None, "") + ] + + protocols: list[str] = [] + seen: set[str] = set() + protocol_keys = { + "protocol_number", "protocolo_id", "interactionProtocol", + "protocolNumber", "finalizacao_protocol", + } + + def walk(value): + if isinstance(value, dict): + for key, item in value.items(): + if key in protocol_keys and item not in (None, ""): + text = str(item).strip() + if text and text not in seen: + seen.add(text) + protocols.append(text) + elif isinstance(item, (dict, list, tuple)): + walk(item) + elif isinstance(value, (list, tuple)): + for item in value: + walk(item) + + walk(mcp_results) + if protocols: + ctx["expected_protocols"] = protocols + ctx["requer_protocolo"] = True + ctx.setdefault("tipo_fluxo", "ajuste") + return ctx + async def output_supervisor(self, state): """Valida a resposta candidata com o OutputSupervisor corporativo. @@ -507,15 +620,15 @@ class AgentWorkflow: } candidate = state.get("answer") or "" - context = { - **(state.get("context") or {}), + context = self._output_guardrail_context(state) + context.update({ "tenant_id": state.get("tenant_id"), "agent_id": state.get("agent_id"), "session_id": state.get("conversation_key") or state.get("session_id"), "route": state.get("route"), "intent": state.get("intent"), "supervisor_attempt": int(state.get("supervisor_attempt", 0)), - } + }) async with self.telemetry.span( "workflow.output_supervisor", session_id=state.get("conversation_key") or state.get("session_id"), @@ -601,7 +714,7 @@ class AgentWorkflow: component="workflow.output_guardrails.start", ) final, decisions = await self.guardrails.run_output( - state["answer"], state.get("context", {}) + state["answer"], self._output_guardrail_context(state) ) for _decision in decisions: await self.guardrail_telemetry.evaluated("output", _decision) diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc index 884409e..823d284 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc index 7f097b7..baf5776 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc index d662183..71b1339 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc index e5055fe..7d103e6 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc index 0e9f1e1..abd23f2 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc index f05ef2c..3a5ff2f 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc index 2b36337..cfd8819 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc index 27c5dd6..0ab1a52 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc index 321fc08..1089291 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc index bcd0559..66abc54 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/billing_agent.py b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/billing_agent.py index aa60099..05254d1 100644 --- a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/billing_agent.py +++ b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/billing_agent.py @@ -95,7 +95,7 @@ class BillingAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em faturas. Responda com clareza, objetividade e sem sugerir ações não solicitadas. Use dados MCP quando disponíveis.", + "Você é um agente especialista em faturas.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade para responder somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente, como “contract_key”, “customer_key” ou “MSISDN”.\nPara consultas informativas de fatura, apresente somente dados de negócio necessários, como valor, vencimento, situação e itens cobrados.\nNão acrescente canais, telefones, códigos USSD, URLs, aplicativos, lojas, relatórios adicionais, procedimentos alternativos ou próximos passos que não tenham sido explicitamente retornados pela tool/RAG e solicitados pelo usuário.\nNão ofereça espontaneamente outras ações ou detalhamentos.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/orders_agent.py b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/orders_agent.py index f557bed..b793c26 100644 --- a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/orders_agent.py +++ b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/orders_agent.py @@ -95,7 +95,7 @@ class OrdersAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de pedidos de varejo. Use dados de tools quando disponíveis.", + "Você é um agente de pedidos de varejo.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso, alteração, troca, cancelamento ou qualquer mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/product_agent.py b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/product_agent.py index 34433f5..998dad6 100644 --- a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/product_agent.py +++ b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/product_agent.py @@ -95,7 +95,7 @@ class ProductAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em produtos, planos e serviços. Explique sem fazer oferta proativa e sem executar ações sem confirmação. Use dados MCP quando disponíveis.", + "Você é um agente especialista em produtos, planos e serviços.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nEm consultas meramente informativas, não exponha flags ou capacidades transacionais internas como can.cancel e não informe espontaneamente que algo pode ser cancelado, alterado, contratado, removido ou trocado. Só mencione capacidade transacional quando o usuário tiver solicitado essa ação.\nNão faça oferta proativa e não execute nem simule mutações sem a confirmação exigida pelo framework.\nNão acrescente canais, procedimentos ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/support_agent.py b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/support_agent.py index b4f0244..b15a0e4 100644 --- a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/support_agent.py +++ b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/support_agent.py @@ -95,7 +95,7 @@ class SupportAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de suporte de varejo para troca, devolução e garantia.", + "Você é um agente de suporte de varejo para troca, devolução e garantia.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso nem simule troca, devolução, garantia ou outra mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc index e011e2c..82f6849 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc index 417fdea..5ce2480 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc index 43c2841..636a41a 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc index 8684f23..0f860a4 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc index 19cee2f..4e5f533 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc index 5919f9d..063a9b9 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc index 60f4328..e06cfd2 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc index a01c7f1..70a3957 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..91dabf0 Binary files /dev/null and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc index 32cbdf8..2c6e20e 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/tool_renderers.py b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/tool_renderers.py index f77c47a..cb44202 100644 --- a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/tool_renderers.py +++ b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/tool_renderers.py @@ -13,7 +13,42 @@ def _money_brl(value: Any) -> str: def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: - return f"[{agent_label}] Fatura consultada: {result}." + """Renderiza somente campos de negócio seguros da fatura. + + Identificadores técnicos/PII presentes no payload MCP (por exemplo msisdn, + customer_id, document e business keys) não devem ser propagados ao usuário. + """ + lines = [f"[{agent_label}] Dados da sua fatura:"] + total = result.get("valor_total") + vencimento = result.get("vencimento") + status = result.get("status") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + if vencimento not in (None, ""): + lines.append(f"Vencimento: {vencimento}.") + if status not in (None, ""): + lines.append(f"Situação: {status}.") + + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if not isinstance(item, dict): + continue + description = item.get("descricao") or item.get("nome") + value = item.get("valor") + if description in (None, ""): + continue + if value is None: + rendered_items.append(str(description)) + else: + rendered_items.append(f"{description}: R$ {_money_brl(value)}") + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + + # Se não houver nenhum campo de negócio seguro além do cabeçalho, deixe a + # composição pela LLM/guardrails em vez de despejar o payload bruto. + return " ".join(lines) if len(lines) > 1 else None def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc index 17e9714..8adf49f 100644 Binary files a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc and b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ 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 b8ed7bc..303bf51 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 @@ -494,6 +494,119 @@ class AgentWorkflow: "next_state": "SESSION_ENDED", } + @staticmethod + def _output_guardrail_context(state: dict) -> dict: + """Monta o contexto operacional do turno para os guardrails de saída. + + Mantém evidências/protocolos necessários aos rails, mas impede que uma + transação encerrada ou semanticamente interrompida governe o novo turno. + O histórico completo permanece no state/checkpoint para auditoria. + """ + ctx = dict(state.get("context", {}) or {}) + mcp_results = state.get("mcp_results") or [] + ctx["evidence"] = mcp_results or ctx.get("evidence") + ctx["tool_result"] = mcp_results or ctx.get("tool_result") + ctx["tool_executed"] = any(isinstance(r, dict) and r.get("ok") for r in mcp_results) + + history = list(state.get("history") or []) + current_user_text = str(state.get("user_text") or "").strip() + if current_user_text: + if ( + not history + or not isinstance(history[-1], dict) + or str(history[-1].get("content") or "") != current_user_text + or str(history[-1].get("role") or "") != "user" + ): + history.append({"role": "user", "content": current_user_text}) + + route_decision = state.get("route_decision") or {} + route_metadata = route_decision.get("metadata") if isinstance(route_decision, dict) else {} + route_metadata = route_metadata if isinstance(route_metadata, dict) else {} + pre_validation = state.get("transaction_pre_validation") or {} + pre_validation = pre_validation if isinstance(pre_validation, dict) else {} + tx_status = str( + state.get("transaction_status") or pre_validation.get("status") or "" + ).strip().upper() + terminal_tx = bool(pre_validation.get("terminal")) or tx_status in { + "COMPLETED", "FAILED", "CANCELLED", "BLOCKED", "OUT_OF_SCOPE" + } + semantic_intent_shift = ( + str(route_metadata.get("transaction_interruption") or "").strip().lower() + == "intent_shift" + ) + stickiness_intent_shift = bool(route_metadata.get("route_stickiness_preempted")) + should_isolate_history = semantic_intent_shift or (terminal_tx and stickiness_intent_shift) + + current_route = str( + state.get("route") + or (route_decision.get("route") if isinstance(route_decision, dict) else "") + or "" + ).strip() + current_intent = str( + state.get("intent") + or (route_decision.get("intent") if isinstance(route_decision, dict) else "") + or "" + ).strip() + ctx["current_user_message"] = current_user_text + ctx["current_route"] = current_route + ctx["current_intent"] = current_intent + + if should_isolate_history: + operational_history = ( + [{"role": "user", "content": current_user_text}] + if current_user_text else [] + ) + ctx["historical_transaction_ignored"] = True + ctx["historical_transaction_status"] = tx_status or ( + "INTERRUPTED" if semantic_intent_shift else "TERMINAL" + ) + if semantic_intent_shift: + ctx["historical_transaction_interruption"] = "intent_shift" + for stale_key in ( + "transaction_pre_validation", + "transaction_status", + "active_transaction", + "transaction", + ): + ctx.pop(stale_key, None) + else: + operational_history = history + + ctx["conversation_history"] = operational_history + ctx["history_texts"] = [ + str(item.get("content") or "") + for item in operational_history + if isinstance(item, dict) and item.get("content") not in (None, "") + ] + + protocols: list[str] = [] + seen: set[str] = set() + protocol_keys = { + "protocol_number", "protocolo_id", "interactionProtocol", + "protocolNumber", "finalizacao_protocol", + } + + def walk(value): + if isinstance(value, dict): + for key, item in value.items(): + if key in protocol_keys and item not in (None, ""): + text = str(item).strip() + if text and text not in seen: + seen.add(text) + protocols.append(text) + elif isinstance(item, (dict, list, tuple)): + walk(item) + elif isinstance(value, (list, tuple)): + for item in value: + walk(item) + + walk(mcp_results) + if protocols: + ctx["expected_protocols"] = protocols + ctx["requer_protocolo"] = True + ctx.setdefault("tipo_fluxo", "ajuste") + return ctx + async def output_supervisor(self, state): """Valida a resposta candidata com o OutputSupervisor corporativo. @@ -509,15 +622,15 @@ class AgentWorkflow: } candidate = state.get("answer") or "" - context = { - **(state.get("context") or {}), + context = self._output_guardrail_context(state) + context.update({ "tenant_id": state.get("tenant_id"), "agent_id": state.get("agent_id"), "session_id": state.get("conversation_key") or state.get("session_id"), "route": state.get("route"), "intent": state.get("intent"), "supervisor_attempt": int(state.get("supervisor_attempt", 0)), - } + }) async with self.telemetry.span( "workflow.output_supervisor", session_id=state.get("conversation_key") or state.get("session_id"), @@ -603,7 +716,7 @@ class AgentWorkflow: component="workflow.output_guardrails.start", ) final, decisions = await self.guardrails.run_output( - state["answer"], state.get("context", {}) + state["answer"], self._output_guardrail_context(state) ) for _decision in decisions: await self.guardrail_telemetry.evaluated("output", _decision) diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..33197e5 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..b834e5a Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc new file mode 100644 index 0000000..3f0f59d Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc new file mode 100644 index 0000000..66a8831 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc new file mode 100644 index 0000000..dd8907f Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc new file mode 100644 index 0000000..d93873a Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc new file mode 100644 index 0000000..e6a45d1 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc new file mode 100644 index 0000000..da03ff1 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc new file mode 100644 index 0000000..e5072a2 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc new file mode 100644 index 0000000..e38f707 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/billing_agent.py b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/billing_agent.py index aa60099..05254d1 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/billing_agent.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/billing_agent.py @@ -95,7 +95,7 @@ class BillingAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em faturas. Responda com clareza, objetividade e sem sugerir ações não solicitadas. Use dados MCP quando disponíveis.", + "Você é um agente especialista em faturas.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade para responder somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente, como “contract_key”, “customer_key” ou “MSISDN”.\nPara consultas informativas de fatura, apresente somente dados de negócio necessários, como valor, vencimento, situação e itens cobrados.\nNão acrescente canais, telefones, códigos USSD, URLs, aplicativos, lojas, relatórios adicionais, procedimentos alternativos ou próximos passos que não tenham sido explicitamente retornados pela tool/RAG e solicitados pelo usuário.\nNão ofereça espontaneamente outras ações ou detalhamentos.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/orders_agent.py b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/orders_agent.py index f557bed..b793c26 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/orders_agent.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/orders_agent.py @@ -95,7 +95,7 @@ class OrdersAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de pedidos de varejo. Use dados de tools quando disponíveis.", + "Você é um agente de pedidos de varejo.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso, alteração, troca, cancelamento ou qualquer mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/product_agent.py b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/product_agent.py index 34433f5..998dad6 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/product_agent.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/product_agent.py @@ -95,7 +95,7 @@ class ProductAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em produtos, planos e serviços. Explique sem fazer oferta proativa e sem executar ações sem confirmação. Use dados MCP quando disponíveis.", + "Você é um agente especialista em produtos, planos e serviços.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nEm consultas meramente informativas, não exponha flags ou capacidades transacionais internas como can.cancel e não informe espontaneamente que algo pode ser cancelado, alterado, contratado, removido ou trocado. Só mencione capacidade transacional quando o usuário tiver solicitado essa ação.\nNão faça oferta proativa e não execute nem simule mutações sem a confirmação exigida pelo framework.\nNão acrescente canais, procedimentos ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/support_agent.py b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/support_agent.py index b4f0244..b15a0e4 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/support_agent.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/support_agent.py @@ -95,7 +95,7 @@ class SupportAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de suporte de varejo para troca, devolução e garantia.", + "Você é um agente de suporte de varejo para troca, devolução e garantia.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso nem simule troca, devolução, garantia ou outra mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..ef32d16 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc new file mode 100644 index 0000000..e21d30a Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc new file mode 100644 index 0000000..0449243 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc new file mode 100644 index 0000000..02ff434 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc new file mode 100644 index 0000000..502913f Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc new file mode 100644 index 0000000..84f7d61 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..be993d0 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc new file mode 100644 index 0000000..da0b163 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..d5802e0 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc index 23b52e1..970b809 100644 Binary files a/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc and b/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/tool_renderers.py b/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/tool_renderers.py index f77c47a..cb44202 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/tool_renderers.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/tool_renderers.py @@ -13,7 +13,42 @@ def _money_brl(value: Any) -> str: def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: - return f"[{agent_label}] Fatura consultada: {result}." + """Renderiza somente campos de negócio seguros da fatura. + + Identificadores técnicos/PII presentes no payload MCP (por exemplo msisdn, + customer_id, document e business keys) não devem ser propagados ao usuário. + """ + lines = [f"[{agent_label}] Dados da sua fatura:"] + total = result.get("valor_total") + vencimento = result.get("vencimento") + status = result.get("status") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + if vencimento not in (None, ""): + lines.append(f"Vencimento: {vencimento}.") + if status not in (None, ""): + lines.append(f"Situação: {status}.") + + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if not isinstance(item, dict): + continue + description = item.get("descricao") or item.get("nome") + value = item.get("valor") + if description in (None, ""): + continue + if value is None: + rendered_items.append(str(description)) + else: + rendered_items.append(f"{description}: R$ {_money_brl(value)}") + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + + # Se não houver nenhum campo de negócio seguro além do cabeçalho, deixe a + # composição pela LLM/guardrails em vez de despejar o payload bruto. + return " ".join(lines) if len(lines) > 1 else None def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc new file mode 100644 index 0000000..114625a Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ 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 0a12c4b..2cb86e1 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 @@ -492,6 +492,119 @@ class AgentWorkflow: "next_state": "SESSION_ENDED", } + @staticmethod + def _output_guardrail_context(state: dict) -> dict: + """Monta o contexto operacional do turno para os guardrails de saída. + + Mantém evidências/protocolos necessários aos rails, mas impede que uma + transação encerrada ou semanticamente interrompida governe o novo turno. + O histórico completo permanece no state/checkpoint para auditoria. + """ + ctx = dict(state.get("context", {}) or {}) + mcp_results = state.get("mcp_results") or [] + ctx["evidence"] = mcp_results or ctx.get("evidence") + ctx["tool_result"] = mcp_results or ctx.get("tool_result") + ctx["tool_executed"] = any(isinstance(r, dict) and r.get("ok") for r in mcp_results) + + history = list(state.get("history") or []) + current_user_text = str(state.get("user_text") or "").strip() + if current_user_text: + if ( + not history + or not isinstance(history[-1], dict) + or str(history[-1].get("content") or "") != current_user_text + or str(history[-1].get("role") or "") != "user" + ): + history.append({"role": "user", "content": current_user_text}) + + route_decision = state.get("route_decision") or {} + route_metadata = route_decision.get("metadata") if isinstance(route_decision, dict) else {} + route_metadata = route_metadata if isinstance(route_metadata, dict) else {} + pre_validation = state.get("transaction_pre_validation") or {} + pre_validation = pre_validation if isinstance(pre_validation, dict) else {} + tx_status = str( + state.get("transaction_status") or pre_validation.get("status") or "" + ).strip().upper() + terminal_tx = bool(pre_validation.get("terminal")) or tx_status in { + "COMPLETED", "FAILED", "CANCELLED", "BLOCKED", "OUT_OF_SCOPE" + } + semantic_intent_shift = ( + str(route_metadata.get("transaction_interruption") or "").strip().lower() + == "intent_shift" + ) + stickiness_intent_shift = bool(route_metadata.get("route_stickiness_preempted")) + should_isolate_history = semantic_intent_shift or (terminal_tx and stickiness_intent_shift) + + current_route = str( + state.get("route") + or (route_decision.get("route") if isinstance(route_decision, dict) else "") + or "" + ).strip() + current_intent = str( + state.get("intent") + or (route_decision.get("intent") if isinstance(route_decision, dict) else "") + or "" + ).strip() + ctx["current_user_message"] = current_user_text + ctx["current_route"] = current_route + ctx["current_intent"] = current_intent + + if should_isolate_history: + operational_history = ( + [{"role": "user", "content": current_user_text}] + if current_user_text else [] + ) + ctx["historical_transaction_ignored"] = True + ctx["historical_transaction_status"] = tx_status or ( + "INTERRUPTED" if semantic_intent_shift else "TERMINAL" + ) + if semantic_intent_shift: + ctx["historical_transaction_interruption"] = "intent_shift" + for stale_key in ( + "transaction_pre_validation", + "transaction_status", + "active_transaction", + "transaction", + ): + ctx.pop(stale_key, None) + else: + operational_history = history + + ctx["conversation_history"] = operational_history + ctx["history_texts"] = [ + str(item.get("content") or "") + for item in operational_history + if isinstance(item, dict) and item.get("content") not in (None, "") + ] + + protocols: list[str] = [] + seen: set[str] = set() + protocol_keys = { + "protocol_number", "protocolo_id", "interactionProtocol", + "protocolNumber", "finalizacao_protocol", + } + + def walk(value): + if isinstance(value, dict): + for key, item in value.items(): + if key in protocol_keys and item not in (None, ""): + text = str(item).strip() + if text and text not in seen: + seen.add(text) + protocols.append(text) + elif isinstance(item, (dict, list, tuple)): + walk(item) + elif isinstance(value, (list, tuple)): + for item in value: + walk(item) + + walk(mcp_results) + if protocols: + ctx["expected_protocols"] = protocols + ctx["requer_protocolo"] = True + ctx.setdefault("tipo_fluxo", "ajuste") + return ctx + async def output_supervisor(self, state): """Valida a resposta candidata com o OutputSupervisor corporativo. @@ -507,15 +620,15 @@ class AgentWorkflow: } candidate = state.get("answer") or "" - context = { - **(state.get("context") or {}), + context = self._output_guardrail_context(state) + context.update({ "tenant_id": state.get("tenant_id"), "agent_id": state.get("agent_id"), "session_id": state.get("conversation_key") or state.get("session_id"), "route": state.get("route"), "intent": state.get("intent"), "supervisor_attempt": int(state.get("supervisor_attempt", 0)), - } + }) async with self.telemetry.span( "workflow.output_supervisor", session_id=state.get("conversation_key") or state.get("session_id"), @@ -601,7 +714,7 @@ class AgentWorkflow: component="workflow.output_guardrails.start", ) final, decisions = await self.guardrails.run_output( - state["answer"], state.get("context", {}) + state["answer"], self._output_guardrail_context(state) ) for _decision in decisions: await self.guardrail_telemetry.evaluated("output", _decision) diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..f58a065 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..ffb3aac Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc new file mode 100644 index 0000000..2c08e83 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc new file mode 100644 index 0000000..633c69b Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc new file mode 100644 index 0000000..b054c86 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc new file mode 100644 index 0000000..710b318 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc new file mode 100644 index 0000000..a06a87f Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc new file mode 100644 index 0000000..71ad8ec Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc new file mode 100644 index 0000000..e10fbd8 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc new file mode 100644 index 0000000..1a49580 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/billing_agent.py b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/billing_agent.py index aa60099..05254d1 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/billing_agent.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/billing_agent.py @@ -95,7 +95,7 @@ class BillingAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em faturas. Responda com clareza, objetividade e sem sugerir ações não solicitadas. Use dados MCP quando disponíveis.", + "Você é um agente especialista em faturas.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade para responder somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente, como “contract_key”, “customer_key” ou “MSISDN”.\nPara consultas informativas de fatura, apresente somente dados de negócio necessários, como valor, vencimento, situação e itens cobrados.\nNão acrescente canais, telefones, códigos USSD, URLs, aplicativos, lojas, relatórios adicionais, procedimentos alternativos ou próximos passos que não tenham sido explicitamente retornados pela tool/RAG e solicitados pelo usuário.\nNão ofereça espontaneamente outras ações ou detalhamentos.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/orders_agent.py b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/orders_agent.py index f557bed..b793c26 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/orders_agent.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/orders_agent.py @@ -95,7 +95,7 @@ class OrdersAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de pedidos de varejo. Use dados de tools quando disponíveis.", + "Você é um agente de pedidos de varejo.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso, alteração, troca, cancelamento ou qualquer mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/product_agent.py b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/product_agent.py index 34433f5..998dad6 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/product_agent.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/product_agent.py @@ -95,7 +95,7 @@ class ProductAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em produtos, planos e serviços. Explique sem fazer oferta proativa e sem executar ações sem confirmação. Use dados MCP quando disponíveis.", + "Você é um agente especialista em produtos, planos e serviços.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nEm consultas meramente informativas, não exponha flags ou capacidades transacionais internas como can.cancel e não informe espontaneamente que algo pode ser cancelado, alterado, contratado, removido ou trocado. Só mencione capacidade transacional quando o usuário tiver solicitado essa ação.\nNão faça oferta proativa e não execute nem simule mutações sem a confirmação exigida pelo framework.\nNão acrescente canais, procedimentos ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/support_agent.py b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/support_agent.py index b4f0244..b15a0e4 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/support_agent.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/agents/support_agent.py @@ -95,7 +95,7 @@ class SupportAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de suporte de varejo para troca, devolução e garantia.", + "Você é um agente de suporte de varejo para troca, devolução e garantia.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso nem simule troca, devolução, garantia ou outra mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..42d368b Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc new file mode 100644 index 0000000..b99954d Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc new file mode 100644 index 0000000..3c19c45 Binary files /dev/null and b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ 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 0a12c4b..2cb86e1 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 @@ -492,6 +492,119 @@ class AgentWorkflow: "next_state": "SESSION_ENDED", } + @staticmethod + def _output_guardrail_context(state: dict) -> dict: + """Monta o contexto operacional do turno para os guardrails de saída. + + Mantém evidências/protocolos necessários aos rails, mas impede que uma + transação encerrada ou semanticamente interrompida governe o novo turno. + O histórico completo permanece no state/checkpoint para auditoria. + """ + ctx = dict(state.get("context", {}) or {}) + mcp_results = state.get("mcp_results") or [] + ctx["evidence"] = mcp_results or ctx.get("evidence") + ctx["tool_result"] = mcp_results or ctx.get("tool_result") + ctx["tool_executed"] = any(isinstance(r, dict) and r.get("ok") for r in mcp_results) + + history = list(state.get("history") or []) + current_user_text = str(state.get("user_text") or "").strip() + if current_user_text: + if ( + not history + or not isinstance(history[-1], dict) + or str(history[-1].get("content") or "") != current_user_text + or str(history[-1].get("role") or "") != "user" + ): + history.append({"role": "user", "content": current_user_text}) + + route_decision = state.get("route_decision") or {} + route_metadata = route_decision.get("metadata") if isinstance(route_decision, dict) else {} + route_metadata = route_metadata if isinstance(route_metadata, dict) else {} + pre_validation = state.get("transaction_pre_validation") or {} + pre_validation = pre_validation if isinstance(pre_validation, dict) else {} + tx_status = str( + state.get("transaction_status") or pre_validation.get("status") or "" + ).strip().upper() + terminal_tx = bool(pre_validation.get("terminal")) or tx_status in { + "COMPLETED", "FAILED", "CANCELLED", "BLOCKED", "OUT_OF_SCOPE" + } + semantic_intent_shift = ( + str(route_metadata.get("transaction_interruption") or "").strip().lower() + == "intent_shift" + ) + stickiness_intent_shift = bool(route_metadata.get("route_stickiness_preempted")) + should_isolate_history = semantic_intent_shift or (terminal_tx and stickiness_intent_shift) + + current_route = str( + state.get("route") + or (route_decision.get("route") if isinstance(route_decision, dict) else "") + or "" + ).strip() + current_intent = str( + state.get("intent") + or (route_decision.get("intent") if isinstance(route_decision, dict) else "") + or "" + ).strip() + ctx["current_user_message"] = current_user_text + ctx["current_route"] = current_route + ctx["current_intent"] = current_intent + + if should_isolate_history: + operational_history = ( + [{"role": "user", "content": current_user_text}] + if current_user_text else [] + ) + ctx["historical_transaction_ignored"] = True + ctx["historical_transaction_status"] = tx_status or ( + "INTERRUPTED" if semantic_intent_shift else "TERMINAL" + ) + if semantic_intent_shift: + ctx["historical_transaction_interruption"] = "intent_shift" + for stale_key in ( + "transaction_pre_validation", + "transaction_status", + "active_transaction", + "transaction", + ): + ctx.pop(stale_key, None) + else: + operational_history = history + + ctx["conversation_history"] = operational_history + ctx["history_texts"] = [ + str(item.get("content") or "") + for item in operational_history + if isinstance(item, dict) and item.get("content") not in (None, "") + ] + + protocols: list[str] = [] + seen: set[str] = set() + protocol_keys = { + "protocol_number", "protocolo_id", "interactionProtocol", + "protocolNumber", "finalizacao_protocol", + } + + def walk(value): + if isinstance(value, dict): + for key, item in value.items(): + if key in protocol_keys and item not in (None, ""): + text = str(item).strip() + if text and text not in seen: + seen.add(text) + protocols.append(text) + elif isinstance(item, (dict, list, tuple)): + walk(item) + elif isinstance(value, (list, tuple)): + for item in value: + walk(item) + + walk(mcp_results) + if protocols: + ctx["expected_protocols"] = protocols + ctx["requer_protocolo"] = True + ctx.setdefault("tipo_fluxo", "ajuste") + return ctx + async def output_supervisor(self, state): """Valida a resposta candidata com o OutputSupervisor corporativo. @@ -507,15 +620,15 @@ class AgentWorkflow: } candidate = state.get("answer") or "" - context = { - **(state.get("context") or {}), + context = self._output_guardrail_context(state) + context.update({ "tenant_id": state.get("tenant_id"), "agent_id": state.get("agent_id"), "session_id": state.get("conversation_key") or state.get("session_id"), "route": state.get("route"), "intent": state.get("intent"), "supervisor_attempt": int(state.get("supervisor_attempt", 0)), - } + }) async with self.telemetry.span( "workflow.output_supervisor", session_id=state.get("conversation_key") or state.get("session_id"), @@ -601,7 +714,7 @@ class AgentWorkflow: component="workflow.output_guardrails.start", ) final, decisions = await self.guardrails.run_output( - state["answer"], state.get("context", {}) + state["answer"], self._output_guardrail_context(state) ) for _decision in decisions: await self.guardrail_telemetry.evaluated("output", _decision) diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..f8eb541 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..dd7251b Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc new file mode 100644 index 0000000..2e10029 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc new file mode 100644 index 0000000..af7100e Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc new file mode 100644 index 0000000..f5a9d47 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc new file mode 100644 index 0000000..9fefbf7 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc new file mode 100644 index 0000000..7ebb5c3 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc new file mode 100644 index 0000000..e81fba3 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc new file mode 100644 index 0000000..0302053 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc new file mode 100644 index 0000000..956fee1 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/billing_agent.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/billing_agent.py index aa60099..05254d1 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/billing_agent.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/billing_agent.py @@ -95,7 +95,7 @@ class BillingAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em faturas. Responda com clareza, objetividade e sem sugerir ações não solicitadas. Use dados MCP quando disponíveis.", + "Você é um agente especialista em faturas.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade para responder somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente, como “contract_key”, “customer_key” ou “MSISDN”.\nPara consultas informativas de fatura, apresente somente dados de negócio necessários, como valor, vencimento, situação e itens cobrados.\nNão acrescente canais, telefones, códigos USSD, URLs, aplicativos, lojas, relatórios adicionais, procedimentos alternativos ou próximos passos que não tenham sido explicitamente retornados pela tool/RAG e solicitados pelo usuário.\nNão ofereça espontaneamente outras ações ou detalhamentos.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/orders_agent.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/orders_agent.py index f557bed..b793c26 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/orders_agent.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/orders_agent.py @@ -95,7 +95,7 @@ class OrdersAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de pedidos de varejo. Use dados de tools quando disponíveis.", + "Você é um agente de pedidos de varejo.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso, alteração, troca, cancelamento ou qualquer mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/product_agent.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/product_agent.py index 34433f5..998dad6 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/product_agent.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/product_agent.py @@ -95,7 +95,7 @@ class ProductAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em produtos, planos e serviços. Explique sem fazer oferta proativa e sem executar ações sem confirmação. Use dados MCP quando disponíveis.", + "Você é um agente especialista em produtos, planos e serviços.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nEm consultas meramente informativas, não exponha flags ou capacidades transacionais internas como can.cancel e não informe espontaneamente que algo pode ser cancelado, alterado, contratado, removido ou trocado. Só mencione capacidade transacional quando o usuário tiver solicitado essa ação.\nNão faça oferta proativa e não execute nem simule mutações sem a confirmação exigida pelo framework.\nNão acrescente canais, procedimentos ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/support_agent.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/support_agent.py index b4f0244..b15a0e4 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/support_agent.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/support_agent.py @@ -95,7 +95,7 @@ class SupportAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de suporte de varejo para troca, devolução e garantia.", + "Você é um agente de suporte de varejo para troca, devolução e garantia.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso nem simule troca, devolução, garantia ou outra mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..fda75ca Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc new file mode 100644 index 0000000..9d7cc49 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc new file mode 100644 index 0000000..e1fe21e Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc new file mode 100644 index 0000000..b69294d Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc new file mode 100644 index 0000000..85c1994 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc new file mode 100644 index 0000000..3a2600e Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..f201aed Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc new file mode 100644 index 0000000..7c9b983 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..ed945c8 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc index 567eb4d..8c88b04 100644 Binary files a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/tool_renderers.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/tool_renderers.py index f77c47a..cb44202 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/tool_renderers.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/tool_renderers.py @@ -13,7 +13,42 @@ def _money_brl(value: Any) -> str: def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: - return f"[{agent_label}] Fatura consultada: {result}." + """Renderiza somente campos de negócio seguros da fatura. + + Identificadores técnicos/PII presentes no payload MCP (por exemplo msisdn, + customer_id, document e business keys) não devem ser propagados ao usuário. + """ + lines = [f"[{agent_label}] Dados da sua fatura:"] + total = result.get("valor_total") + vencimento = result.get("vencimento") + status = result.get("status") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + if vencimento not in (None, ""): + lines.append(f"Vencimento: {vencimento}.") + if status not in (None, ""): + lines.append(f"Situação: {status}.") + + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if not isinstance(item, dict): + continue + description = item.get("descricao") or item.get("nome") + value = item.get("valor") + if description in (None, ""): + continue + if value is None: + rendered_items.append(str(description)) + else: + rendered_items.append(f"{description}: R$ {_money_brl(value)}") + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + + # Se não houver nenhum campo de negócio seguro além do cabeçalho, deixe a + # composição pela LLM/guardrails em vez de despejar o payload bruto. + return " ".join(lines) if len(lines) > 1 else None def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc new file mode 100644 index 0000000..91f49fc Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ 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 0a12c4b..2cb86e1 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 @@ -492,6 +492,119 @@ class AgentWorkflow: "next_state": "SESSION_ENDED", } + @staticmethod + def _output_guardrail_context(state: dict) -> dict: + """Monta o contexto operacional do turno para os guardrails de saída. + + Mantém evidências/protocolos necessários aos rails, mas impede que uma + transação encerrada ou semanticamente interrompida governe o novo turno. + O histórico completo permanece no state/checkpoint para auditoria. + """ + ctx = dict(state.get("context", {}) or {}) + mcp_results = state.get("mcp_results") or [] + ctx["evidence"] = mcp_results or ctx.get("evidence") + ctx["tool_result"] = mcp_results or ctx.get("tool_result") + ctx["tool_executed"] = any(isinstance(r, dict) and r.get("ok") for r in mcp_results) + + history = list(state.get("history") or []) + current_user_text = str(state.get("user_text") or "").strip() + if current_user_text: + if ( + not history + or not isinstance(history[-1], dict) + or str(history[-1].get("content") or "") != current_user_text + or str(history[-1].get("role") or "") != "user" + ): + history.append({"role": "user", "content": current_user_text}) + + route_decision = state.get("route_decision") or {} + route_metadata = route_decision.get("metadata") if isinstance(route_decision, dict) else {} + route_metadata = route_metadata if isinstance(route_metadata, dict) else {} + pre_validation = state.get("transaction_pre_validation") or {} + pre_validation = pre_validation if isinstance(pre_validation, dict) else {} + tx_status = str( + state.get("transaction_status") or pre_validation.get("status") or "" + ).strip().upper() + terminal_tx = bool(pre_validation.get("terminal")) or tx_status in { + "COMPLETED", "FAILED", "CANCELLED", "BLOCKED", "OUT_OF_SCOPE" + } + semantic_intent_shift = ( + str(route_metadata.get("transaction_interruption") or "").strip().lower() + == "intent_shift" + ) + stickiness_intent_shift = bool(route_metadata.get("route_stickiness_preempted")) + should_isolate_history = semantic_intent_shift or (terminal_tx and stickiness_intent_shift) + + current_route = str( + state.get("route") + or (route_decision.get("route") if isinstance(route_decision, dict) else "") + or "" + ).strip() + current_intent = str( + state.get("intent") + or (route_decision.get("intent") if isinstance(route_decision, dict) else "") + or "" + ).strip() + ctx["current_user_message"] = current_user_text + ctx["current_route"] = current_route + ctx["current_intent"] = current_intent + + if should_isolate_history: + operational_history = ( + [{"role": "user", "content": current_user_text}] + if current_user_text else [] + ) + ctx["historical_transaction_ignored"] = True + ctx["historical_transaction_status"] = tx_status or ( + "INTERRUPTED" if semantic_intent_shift else "TERMINAL" + ) + if semantic_intent_shift: + ctx["historical_transaction_interruption"] = "intent_shift" + for stale_key in ( + "transaction_pre_validation", + "transaction_status", + "active_transaction", + "transaction", + ): + ctx.pop(stale_key, None) + else: + operational_history = history + + ctx["conversation_history"] = operational_history + ctx["history_texts"] = [ + str(item.get("content") or "") + for item in operational_history + if isinstance(item, dict) and item.get("content") not in (None, "") + ] + + protocols: list[str] = [] + seen: set[str] = set() + protocol_keys = { + "protocol_number", "protocolo_id", "interactionProtocol", + "protocolNumber", "finalizacao_protocol", + } + + def walk(value): + if isinstance(value, dict): + for key, item in value.items(): + if key in protocol_keys and item not in (None, ""): + text = str(item).strip() + if text and text not in seen: + seen.add(text) + protocols.append(text) + elif isinstance(item, (dict, list, tuple)): + walk(item) + elif isinstance(value, (list, tuple)): + for item in value: + walk(item) + + walk(mcp_results) + if protocols: + ctx["expected_protocols"] = protocols + ctx["requer_protocolo"] = True + ctx.setdefault("tipo_fluxo", "ajuste") + return ctx + async def output_supervisor(self, state): """Valida a resposta candidata com o OutputSupervisor corporativo. @@ -507,15 +620,15 @@ class AgentWorkflow: } candidate = state.get("answer") or "" - context = { - **(state.get("context") or {}), + context = self._output_guardrail_context(state) + context.update({ "tenant_id": state.get("tenant_id"), "agent_id": state.get("agent_id"), "session_id": state.get("conversation_key") or state.get("session_id"), "route": state.get("route"), "intent": state.get("intent"), "supervisor_attempt": int(state.get("supervisor_attempt", 0)), - } + }) async with self.telemetry.span( "workflow.output_supervisor", session_id=state.get("conversation_key") or state.get("session_id"), @@ -601,7 +714,7 @@ class AgentWorkflow: component="workflow.output_guardrails.start", ) final, decisions = await self.guardrails.run_output( - state["answer"], state.get("context", {}) + state["answer"], self._output_guardrail_context(state) ) for _decision in decisions: await self.guardrail_telemetry.evaluated("output", _decision) diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..f0d4c0e Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..ca99d56 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc new file mode 100644 index 0000000..ffaf183 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc new file mode 100644 index 0000000..8a0915d Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc new file mode 100644 index 0000000..455b175 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc new file mode 100644 index 0000000..bb03d12 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc new file mode 100644 index 0000000..44aa6f5 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc new file mode 100644 index 0000000..7d14660 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc new file mode 100644 index 0000000..340ff14 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc new file mode 100644 index 0000000..c047c8d Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/billing_agent.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/billing_agent.py index aa60099..05254d1 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/billing_agent.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/billing_agent.py @@ -95,7 +95,7 @@ class BillingAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em faturas. Responda com clareza, objetividade e sem sugerir ações não solicitadas. Use dados MCP quando disponíveis.", + "Você é um agente especialista em faturas.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade para responder somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente, como “contract_key”, “customer_key” ou “MSISDN”.\nPara consultas informativas de fatura, apresente somente dados de negócio necessários, como valor, vencimento, situação e itens cobrados.\nNão acrescente canais, telefones, códigos USSD, URLs, aplicativos, lojas, relatórios adicionais, procedimentos alternativos ou próximos passos que não tenham sido explicitamente retornados pela tool/RAG e solicitados pelo usuário.\nNão ofereça espontaneamente outras ações ou detalhamentos.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/orders_agent.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/orders_agent.py index f557bed..b793c26 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/orders_agent.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/orders_agent.py @@ -95,7 +95,7 @@ class OrdersAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de pedidos de varejo. Use dados de tools quando disponíveis.", + "Você é um agente de pedidos de varejo.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso, alteração, troca, cancelamento ou qualquer mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/product_agent.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/product_agent.py index 34433f5..998dad6 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/product_agent.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/product_agent.py @@ -95,7 +95,7 @@ class ProductAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em produtos, planos e serviços. Explique sem fazer oferta proativa e sem executar ações sem confirmação. Use dados MCP quando disponíveis.", + "Você é um agente especialista em produtos, planos e serviços.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nEm consultas meramente informativas, não exponha flags ou capacidades transacionais internas como can.cancel e não informe espontaneamente que algo pode ser cancelado, alterado, contratado, removido ou trocado. Só mencione capacidade transacional quando o usuário tiver solicitado essa ação.\nNão faça oferta proativa e não execute nem simule mutações sem a confirmação exigida pelo framework.\nNão acrescente canais, procedimentos ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/support_agent.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/support_agent.py index b4f0244..b15a0e4 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/support_agent.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/agents/support_agent.py @@ -95,7 +95,7 @@ class SupportAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de suporte de varejo para troca, devolução e garantia.", + "Você é um agente de suporte de varejo para troca, devolução e garantia.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso nem simule troca, devolução, garantia ou outra mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..e757f93 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc new file mode 100644 index 0000000..65b0a28 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc new file mode 100644 index 0000000..d268a63 Binary files /dev/null and b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ 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 0a12c4b..2cb86e1 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 @@ -492,6 +492,119 @@ class AgentWorkflow: "next_state": "SESSION_ENDED", } + @staticmethod + def _output_guardrail_context(state: dict) -> dict: + """Monta o contexto operacional do turno para os guardrails de saída. + + Mantém evidências/protocolos necessários aos rails, mas impede que uma + transação encerrada ou semanticamente interrompida governe o novo turno. + O histórico completo permanece no state/checkpoint para auditoria. + """ + ctx = dict(state.get("context", {}) or {}) + mcp_results = state.get("mcp_results") or [] + ctx["evidence"] = mcp_results or ctx.get("evidence") + ctx["tool_result"] = mcp_results or ctx.get("tool_result") + ctx["tool_executed"] = any(isinstance(r, dict) and r.get("ok") for r in mcp_results) + + history = list(state.get("history") or []) + current_user_text = str(state.get("user_text") or "").strip() + if current_user_text: + if ( + not history + or not isinstance(history[-1], dict) + or str(history[-1].get("content") or "") != current_user_text + or str(history[-1].get("role") or "") != "user" + ): + history.append({"role": "user", "content": current_user_text}) + + route_decision = state.get("route_decision") or {} + route_metadata = route_decision.get("metadata") if isinstance(route_decision, dict) else {} + route_metadata = route_metadata if isinstance(route_metadata, dict) else {} + pre_validation = state.get("transaction_pre_validation") or {} + pre_validation = pre_validation if isinstance(pre_validation, dict) else {} + tx_status = str( + state.get("transaction_status") or pre_validation.get("status") or "" + ).strip().upper() + terminal_tx = bool(pre_validation.get("terminal")) or tx_status in { + "COMPLETED", "FAILED", "CANCELLED", "BLOCKED", "OUT_OF_SCOPE" + } + semantic_intent_shift = ( + str(route_metadata.get("transaction_interruption") or "").strip().lower() + == "intent_shift" + ) + stickiness_intent_shift = bool(route_metadata.get("route_stickiness_preempted")) + should_isolate_history = semantic_intent_shift or (terminal_tx and stickiness_intent_shift) + + current_route = str( + state.get("route") + or (route_decision.get("route") if isinstance(route_decision, dict) else "") + or "" + ).strip() + current_intent = str( + state.get("intent") + or (route_decision.get("intent") if isinstance(route_decision, dict) else "") + or "" + ).strip() + ctx["current_user_message"] = current_user_text + ctx["current_route"] = current_route + ctx["current_intent"] = current_intent + + if should_isolate_history: + operational_history = ( + [{"role": "user", "content": current_user_text}] + if current_user_text else [] + ) + ctx["historical_transaction_ignored"] = True + ctx["historical_transaction_status"] = tx_status or ( + "INTERRUPTED" if semantic_intent_shift else "TERMINAL" + ) + if semantic_intent_shift: + ctx["historical_transaction_interruption"] = "intent_shift" + for stale_key in ( + "transaction_pre_validation", + "transaction_status", + "active_transaction", + "transaction", + ): + ctx.pop(stale_key, None) + else: + operational_history = history + + ctx["conversation_history"] = operational_history + ctx["history_texts"] = [ + str(item.get("content") or "") + for item in operational_history + if isinstance(item, dict) and item.get("content") not in (None, "") + ] + + protocols: list[str] = [] + seen: set[str] = set() + protocol_keys = { + "protocol_number", "protocolo_id", "interactionProtocol", + "protocolNumber", "finalizacao_protocol", + } + + def walk(value): + if isinstance(value, dict): + for key, item in value.items(): + if key in protocol_keys and item not in (None, ""): + text = str(item).strip() + if text and text not in seen: + seen.add(text) + protocols.append(text) + elif isinstance(item, (dict, list, tuple)): + walk(item) + elif isinstance(value, (list, tuple)): + for item in value: + walk(item) + + walk(mcp_results) + if protocols: + ctx["expected_protocols"] = protocols + ctx["requer_protocolo"] = True + ctx.setdefault("tipo_fluxo", "ajuste") + return ctx + async def output_supervisor(self, state): """Valida a resposta candidata com o OutputSupervisor corporativo. @@ -507,15 +620,15 @@ class AgentWorkflow: } candidate = state.get("answer") or "" - context = { - **(state.get("context") or {}), + context = self._output_guardrail_context(state) + context.update({ "tenant_id": state.get("tenant_id"), "agent_id": state.get("agent_id"), "session_id": state.get("conversation_key") or state.get("session_id"), "route": state.get("route"), "intent": state.get("intent"), "supervisor_attempt": int(state.get("supervisor_attempt", 0)), - } + }) async with self.telemetry.span( "workflow.output_supervisor", session_id=state.get("conversation_key") or state.get("session_id"), @@ -601,7 +714,7 @@ class AgentWorkflow: component="workflow.output_guardrails.start", ) final, decisions = await self.guardrails.run_output( - state["answer"], state.get("context", {}) + state["answer"], self._output_guardrail_context(state) ) for _decision in decisions: await self.guardrail_telemetry.evaluated("output", _decision) diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..41099f9 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/__pycache__/main.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..7a89633 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/__pycache__/main.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc new file mode 100644 index 0000000..c84d77f Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/__pycache__/state.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/__pycache__/state.cpython-313.pyc new file mode 100644 index 0000000..9c38b48 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/__pycache__/state.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc new file mode 100644 index 0000000..b970a9f Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc new file mode 100644 index 0000000..9ca64f2 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc new file mode 100644 index 0000000..1ebd6c5 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc new file mode 100644 index 0000000..2a94b99 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc new file mode 100644 index 0000000..0014d62 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc new file mode 100644 index 0000000..1d1ee67 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/billing_agent.py b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/billing_agent.py index aa60099..05254d1 100644 --- a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/billing_agent.py +++ b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/billing_agent.py @@ -95,7 +95,7 @@ class BillingAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em faturas. Responda com clareza, objetividade e sem sugerir ações não solicitadas. Use dados MCP quando disponíveis.", + "Você é um agente especialista em faturas.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade para responder somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente, como “contract_key”, “customer_key” ou “MSISDN”.\nPara consultas informativas de fatura, apresente somente dados de negócio necessários, como valor, vencimento, situação e itens cobrados.\nNão acrescente canais, telefones, códigos USSD, URLs, aplicativos, lojas, relatórios adicionais, procedimentos alternativos ou próximos passos que não tenham sido explicitamente retornados pela tool/RAG e solicitados pelo usuário.\nNão ofereça espontaneamente outras ações ou detalhamentos.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/orders_agent.py b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/orders_agent.py index f557bed..b793c26 100644 --- a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/orders_agent.py +++ b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/orders_agent.py @@ -95,7 +95,7 @@ class OrdersAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de pedidos de varejo. Use dados de tools quando disponíveis.", + "Você é um agente de pedidos de varejo.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso, alteração, troca, cancelamento ou qualquer mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/product_agent.py b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/product_agent.py index 34433f5..998dad6 100644 --- a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/product_agent.py +++ b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/product_agent.py @@ -95,7 +95,7 @@ class ProductAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em produtos, planos e serviços. Explique sem fazer oferta proativa e sem executar ações sem confirmação. Use dados MCP quando disponíveis.", + "Você é um agente especialista em produtos, planos e serviços.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nEm consultas meramente informativas, não exponha flags ou capacidades transacionais internas como can.cancel e não informe espontaneamente que algo pode ser cancelado, alterado, contratado, removido ou trocado. Só mencione capacidade transacional quando o usuário tiver solicitado essa ação.\nNão faça oferta proativa e não execute nem simule mutações sem a confirmação exigida pelo framework.\nNão acrescente canais, procedimentos ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/support_agent.py b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/support_agent.py index b4f0244..b15a0e4 100644 --- a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/support_agent.py +++ b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/agents/support_agent.py @@ -95,7 +95,7 @@ class SupportAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de suporte de varejo para troca, devolução e garantia.", + "Você é um agente de suporte de varejo para troca, devolução e garantia.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso nem simule troca, devolução, garantia ou outra mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..deb38e5 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc new file mode 100644 index 0000000..505e62b Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc new file mode 100644 index 0000000..2fcb362 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc new file mode 100644 index 0000000..d78ba9c Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc new file mode 100644 index 0000000..50ae968 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc new file mode 100644 index 0000000..ffc9f3f Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..dd8600f Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc new file mode 100644 index 0000000..d6f43df Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..8048b8f Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc new file mode 100644 index 0000000..defbfd7 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/presentation/tool_renderers.py b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/presentation/tool_renderers.py index f77c47a..cb44202 100644 --- a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/presentation/tool_renderers.py +++ b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/presentation/tool_renderers.py @@ -13,7 +13,42 @@ def _money_brl(value: Any) -> str: def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: - return f"[{agent_label}] Fatura consultada: {result}." + """Renderiza somente campos de negócio seguros da fatura. + + Identificadores técnicos/PII presentes no payload MCP (por exemplo msisdn, + customer_id, document e business keys) não devem ser propagados ao usuário. + """ + lines = [f"[{agent_label}] Dados da sua fatura:"] + total = result.get("valor_total") + vencimento = result.get("vencimento") + status = result.get("status") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + if vencimento not in (None, ""): + lines.append(f"Vencimento: {vencimento}.") + if status not in (None, ""): + lines.append(f"Situação: {status}.") + + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if not isinstance(item, dict): + continue + description = item.get("descricao") or item.get("nome") + value = item.get("valor") + if description in (None, ""): + continue + if value is None: + rendered_items.append(str(description)) + else: + rendered_items.append(f"{description}: R$ {_money_brl(value)}") + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + + # Se não houver nenhum campo de negócio seguro além do cabeçalho, deixe a + # composição pela LLM/guardrails em vez de despejar o payload bruto. + return " ".join(lines) if len(lines) > 1 else None def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..50d3c24 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc new file mode 100644 index 0000000..40704f8 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc new file mode 100644 index 0000000..515e583 Binary files /dev/null and b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ 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 e6605a4..fef0791 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 @@ -494,6 +494,119 @@ class AgentWorkflow: "next_state": "SESSION_ENDED", } + @staticmethod + def _output_guardrail_context(state: dict) -> dict: + """Monta o contexto operacional do turno para os guardrails de saída. + + Mantém evidências/protocolos necessários aos rails, mas impede que uma + transação encerrada ou semanticamente interrompida governe o novo turno. + O histórico completo permanece no state/checkpoint para auditoria. + """ + ctx = dict(state.get("context", {}) or {}) + mcp_results = state.get("mcp_results") or [] + ctx["evidence"] = mcp_results or ctx.get("evidence") + ctx["tool_result"] = mcp_results or ctx.get("tool_result") + ctx["tool_executed"] = any(isinstance(r, dict) and r.get("ok") for r in mcp_results) + + history = list(state.get("history") or []) + current_user_text = str(state.get("user_text") or "").strip() + if current_user_text: + if ( + not history + or not isinstance(history[-1], dict) + or str(history[-1].get("content") or "") != current_user_text + or str(history[-1].get("role") or "") != "user" + ): + history.append({"role": "user", "content": current_user_text}) + + route_decision = state.get("route_decision") or {} + route_metadata = route_decision.get("metadata") if isinstance(route_decision, dict) else {} + route_metadata = route_metadata if isinstance(route_metadata, dict) else {} + pre_validation = state.get("transaction_pre_validation") or {} + pre_validation = pre_validation if isinstance(pre_validation, dict) else {} + tx_status = str( + state.get("transaction_status") or pre_validation.get("status") or "" + ).strip().upper() + terminal_tx = bool(pre_validation.get("terminal")) or tx_status in { + "COMPLETED", "FAILED", "CANCELLED", "BLOCKED", "OUT_OF_SCOPE" + } + semantic_intent_shift = ( + str(route_metadata.get("transaction_interruption") or "").strip().lower() + == "intent_shift" + ) + stickiness_intent_shift = bool(route_metadata.get("route_stickiness_preempted")) + should_isolate_history = semantic_intent_shift or (terminal_tx and stickiness_intent_shift) + + current_route = str( + state.get("route") + or (route_decision.get("route") if isinstance(route_decision, dict) else "") + or "" + ).strip() + current_intent = str( + state.get("intent") + or (route_decision.get("intent") if isinstance(route_decision, dict) else "") + or "" + ).strip() + ctx["current_user_message"] = current_user_text + ctx["current_route"] = current_route + ctx["current_intent"] = current_intent + + if should_isolate_history: + operational_history = ( + [{"role": "user", "content": current_user_text}] + if current_user_text else [] + ) + ctx["historical_transaction_ignored"] = True + ctx["historical_transaction_status"] = tx_status or ( + "INTERRUPTED" if semantic_intent_shift else "TERMINAL" + ) + if semantic_intent_shift: + ctx["historical_transaction_interruption"] = "intent_shift" + for stale_key in ( + "transaction_pre_validation", + "transaction_status", + "active_transaction", + "transaction", + ): + ctx.pop(stale_key, None) + else: + operational_history = history + + ctx["conversation_history"] = operational_history + ctx["history_texts"] = [ + str(item.get("content") or "") + for item in operational_history + if isinstance(item, dict) and item.get("content") not in (None, "") + ] + + protocols: list[str] = [] + seen: set[str] = set() + protocol_keys = { + "protocol_number", "protocolo_id", "interactionProtocol", + "protocolNumber", "finalizacao_protocol", + } + + def walk(value): + if isinstance(value, dict): + for key, item in value.items(): + if key in protocol_keys and item not in (None, ""): + text = str(item).strip() + if text and text not in seen: + seen.add(text) + protocols.append(text) + elif isinstance(item, (dict, list, tuple)): + walk(item) + elif isinstance(value, (list, tuple)): + for item in value: + walk(item) + + walk(mcp_results) + if protocols: + ctx["expected_protocols"] = protocols + ctx["requer_protocolo"] = True + ctx.setdefault("tipo_fluxo", "ajuste") + return ctx + async def output_supervisor(self, state): """Valida a resposta candidata com o OutputSupervisor corporativo. @@ -509,15 +622,15 @@ class AgentWorkflow: } candidate = state.get("answer") or "" - context = { - **(state.get("context") or {}), + context = self._output_guardrail_context(state) + context.update({ "tenant_id": state.get("tenant_id"), "agent_id": state.get("agent_id"), "session_id": state.get("conversation_key") or state.get("session_id"), "route": state.get("route"), "intent": state.get("intent"), "supervisor_attempt": int(state.get("supervisor_attempt", 0)), - } + }) async with self.telemetry.span( "workflow.output_supervisor", session_id=state.get("conversation_key") or state.get("session_id"), @@ -603,7 +716,7 @@ class AgentWorkflow: component="workflow.output_guardrails.start", ) final, decisions = await self.guardrails.run_output( - state["answer"], state.get("context", {}) + state["answer"], self._output_guardrail_context(state) ) for _decision in decisions: await self.guardrail_telemetry.evaluated("output", _decision) diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..f942810 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/__pycache__/main.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..886a86f Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/__pycache__/main.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc new file mode 100644 index 0000000..accca3b Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/__pycache__/state.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/__pycache__/state.cpython-313.pyc new file mode 100644 index 0000000..103d670 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/__pycache__/state.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc new file mode 100644 index 0000000..c09150b Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc new file mode 100644 index 0000000..2d784a4 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc new file mode 100644 index 0000000..5f7bfa1 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc new file mode 100644 index 0000000..942b240 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc new file mode 100644 index 0000000..c06c124 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc new file mode 100644 index 0000000..ba05a3e Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/billing_agent.py b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/billing_agent.py index aa60099..05254d1 100644 --- a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/billing_agent.py +++ b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/billing_agent.py @@ -95,7 +95,7 @@ class BillingAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em faturas. Responda com clareza, objetividade e sem sugerir ações não solicitadas. Use dados MCP quando disponíveis.", + "Você é um agente especialista em faturas.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade para responder somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente, como “contract_key”, “customer_key” ou “MSISDN”.\nPara consultas informativas de fatura, apresente somente dados de negócio necessários, como valor, vencimento, situação e itens cobrados.\nNão acrescente canais, telefones, códigos USSD, URLs, aplicativos, lojas, relatórios adicionais, procedimentos alternativos ou próximos passos que não tenham sido explicitamente retornados pela tool/RAG e solicitados pelo usuário.\nNão ofereça espontaneamente outras ações ou detalhamentos.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/orders_agent.py b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/orders_agent.py index f557bed..b793c26 100644 --- a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/orders_agent.py +++ b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/orders_agent.py @@ -95,7 +95,7 @@ class OrdersAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de pedidos de varejo. Use dados de tools quando disponíveis.", + "Você é um agente de pedidos de varejo.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso, alteração, troca, cancelamento ou qualquer mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/product_agent.py b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/product_agent.py index 34433f5..998dad6 100644 --- a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/product_agent.py +++ b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/product_agent.py @@ -95,7 +95,7 @@ class ProductAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em produtos, planos e serviços. Explique sem fazer oferta proativa e sem executar ações sem confirmação. Use dados MCP quando disponíveis.", + "Você é um agente especialista em produtos, planos e serviços.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nEm consultas meramente informativas, não exponha flags ou capacidades transacionais internas como can.cancel e não informe espontaneamente que algo pode ser cancelado, alterado, contratado, removido ou trocado. Só mencione capacidade transacional quando o usuário tiver solicitado essa ação.\nNão faça oferta proativa e não execute nem simule mutações sem a confirmação exigida pelo framework.\nNão acrescente canais, procedimentos ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/support_agent.py b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/support_agent.py index b4f0244..b15a0e4 100644 --- a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/support_agent.py +++ b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/agents/support_agent.py @@ -95,7 +95,7 @@ class SupportAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de suporte de varejo para troca, devolução e garantia.", + "Você é um agente de suporte de varejo para troca, devolução e garantia.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso nem simule troca, devolução, garantia ou outra mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..317ae4d Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc new file mode 100644 index 0000000..0cf9b02 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc new file mode 100644 index 0000000..479518d Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc new file mode 100644 index 0000000..2baa2d5 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc new file mode 100644 index 0000000..912d680 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc new file mode 100644 index 0000000..bc15baf Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..515f833 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc new file mode 100644 index 0000000..24d8196 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..15e8e71 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc new file mode 100644 index 0000000..fde7777 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/presentation/tool_renderers.py b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/presentation/tool_renderers.py index f77c47a..cb44202 100644 --- a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/presentation/tool_renderers.py +++ b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/presentation/tool_renderers.py @@ -13,7 +13,42 @@ def _money_brl(value: Any) -> str: def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: - return f"[{agent_label}] Fatura consultada: {result}." + """Renderiza somente campos de negócio seguros da fatura. + + Identificadores técnicos/PII presentes no payload MCP (por exemplo msisdn, + customer_id, document e business keys) não devem ser propagados ao usuário. + """ + lines = [f"[{agent_label}] Dados da sua fatura:"] + total = result.get("valor_total") + vencimento = result.get("vencimento") + status = result.get("status") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + if vencimento not in (None, ""): + lines.append(f"Vencimento: {vencimento}.") + if status not in (None, ""): + lines.append(f"Situação: {status}.") + + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if not isinstance(item, dict): + continue + description = item.get("descricao") or item.get("nome") + value = item.get("valor") + if description in (None, ""): + continue + if value is None: + rendered_items.append(str(description)) + else: + rendered_items.append(f"{description}: R$ {_money_brl(value)}") + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + + # Se não houver nenhum campo de negócio seguro além do cabeçalho, deixe a + # composição pela LLM/guardrails em vez de despejar o payload bruto. + return " ".join(lines) if len(lines) > 1 else None def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..6003863 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc new file mode 100644 index 0000000..e179f6d Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc differ diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc new file mode 100644 index 0000000..8604cd4 Binary files /dev/null and b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ 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 e6605a4..fef0791 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 @@ -494,6 +494,119 @@ class AgentWorkflow: "next_state": "SESSION_ENDED", } + @staticmethod + def _output_guardrail_context(state: dict) -> dict: + """Monta o contexto operacional do turno para os guardrails de saída. + + Mantém evidências/protocolos necessários aos rails, mas impede que uma + transação encerrada ou semanticamente interrompida governe o novo turno. + O histórico completo permanece no state/checkpoint para auditoria. + """ + ctx = dict(state.get("context", {}) or {}) + mcp_results = state.get("mcp_results") or [] + ctx["evidence"] = mcp_results or ctx.get("evidence") + ctx["tool_result"] = mcp_results or ctx.get("tool_result") + ctx["tool_executed"] = any(isinstance(r, dict) and r.get("ok") for r in mcp_results) + + history = list(state.get("history") or []) + current_user_text = str(state.get("user_text") or "").strip() + if current_user_text: + if ( + not history + or not isinstance(history[-1], dict) + or str(history[-1].get("content") or "") != current_user_text + or str(history[-1].get("role") or "") != "user" + ): + history.append({"role": "user", "content": current_user_text}) + + route_decision = state.get("route_decision") or {} + route_metadata = route_decision.get("metadata") if isinstance(route_decision, dict) else {} + route_metadata = route_metadata if isinstance(route_metadata, dict) else {} + pre_validation = state.get("transaction_pre_validation") or {} + pre_validation = pre_validation if isinstance(pre_validation, dict) else {} + tx_status = str( + state.get("transaction_status") or pre_validation.get("status") or "" + ).strip().upper() + terminal_tx = bool(pre_validation.get("terminal")) or tx_status in { + "COMPLETED", "FAILED", "CANCELLED", "BLOCKED", "OUT_OF_SCOPE" + } + semantic_intent_shift = ( + str(route_metadata.get("transaction_interruption") or "").strip().lower() + == "intent_shift" + ) + stickiness_intent_shift = bool(route_metadata.get("route_stickiness_preempted")) + should_isolate_history = semantic_intent_shift or (terminal_tx and stickiness_intent_shift) + + current_route = str( + state.get("route") + or (route_decision.get("route") if isinstance(route_decision, dict) else "") + or "" + ).strip() + current_intent = str( + state.get("intent") + or (route_decision.get("intent") if isinstance(route_decision, dict) else "") + or "" + ).strip() + ctx["current_user_message"] = current_user_text + ctx["current_route"] = current_route + ctx["current_intent"] = current_intent + + if should_isolate_history: + operational_history = ( + [{"role": "user", "content": current_user_text}] + if current_user_text else [] + ) + ctx["historical_transaction_ignored"] = True + ctx["historical_transaction_status"] = tx_status or ( + "INTERRUPTED" if semantic_intent_shift else "TERMINAL" + ) + if semantic_intent_shift: + ctx["historical_transaction_interruption"] = "intent_shift" + for stale_key in ( + "transaction_pre_validation", + "transaction_status", + "active_transaction", + "transaction", + ): + ctx.pop(stale_key, None) + else: + operational_history = history + + ctx["conversation_history"] = operational_history + ctx["history_texts"] = [ + str(item.get("content") or "") + for item in operational_history + if isinstance(item, dict) and item.get("content") not in (None, "") + ] + + protocols: list[str] = [] + seen: set[str] = set() + protocol_keys = { + "protocol_number", "protocolo_id", "interactionProtocol", + "protocolNumber", "finalizacao_protocol", + } + + def walk(value): + if isinstance(value, dict): + for key, item in value.items(): + if key in protocol_keys and item not in (None, ""): + text = str(item).strip() + if text and text not in seen: + seen.add(text) + protocols.append(text) + elif isinstance(item, (dict, list, tuple)): + walk(item) + elif isinstance(value, (list, tuple)): + for item in value: + walk(item) + + walk(mcp_results) + if protocols: + ctx["expected_protocols"] = protocols + ctx["requer_protocolo"] = True + ctx.setdefault("tipo_fluxo", "ajuste") + return ctx + async def output_supervisor(self, state): """Valida a resposta candidata com o OutputSupervisor corporativo. @@ -509,15 +622,15 @@ class AgentWorkflow: } candidate = state.get("answer") or "" - context = { - **(state.get("context") or {}), + context = self._output_guardrail_context(state) + context.update({ "tenant_id": state.get("tenant_id"), "agent_id": state.get("agent_id"), "session_id": state.get("conversation_key") or state.get("session_id"), "route": state.get("route"), "intent": state.get("intent"), "supervisor_attempt": int(state.get("supervisor_attempt", 0)), - } + }) async with self.telemetry.span( "workflow.output_supervisor", session_id=state.get("conversation_key") or state.get("session_id"), @@ -603,7 +716,7 @@ class AgentWorkflow: component="workflow.output_guardrails.start", ) final, decisions = await self.guardrails.run_output( - state["answer"], state.get("context", {}) + state["answer"], self._output_guardrail_context(state) ) for _decision in decisions: await self.guardrail_telemetry.evaluated("output", _decision) diff --git a/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc b/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc index f6a2b79..ee504c1 100644 Binary files a/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc and b/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc b/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc index 08f8cb6..521accb 100644 Binary files a/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc and b/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc index 8d7093d..0e8f586 100644 Binary files a/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc and b/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc b/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc index 0e7d19e..50e1f65 100644 Binary files a/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc and b/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc b/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc index 4e27b20..7a1b3b5 100644 Binary files a/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc and b/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc b/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc index 803273e..9283837 100644 Binary files a/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc and b/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc b/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc index 104fb20..cec4448 100644 Binary files a/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc and b/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc b/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc index eda48f5..f4cf172 100644 Binary files a/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc and b/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc b/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc index df7bf15..a2df5a5 100644 Binary files a/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc and b/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc b/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc index 7fbc59f..de82b0f 100644 Binary files a/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc and b/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/agents/billing_agent.py b/templates/agent_template_backend/app/agents/billing_agent.py index aa60099..05254d1 100644 --- a/templates/agent_template_backend/app/agents/billing_agent.py +++ b/templates/agent_template_backend/app/agents/billing_agent.py @@ -95,7 +95,7 @@ class BillingAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em faturas. Responda com clareza, objetividade e sem sugerir ações não solicitadas. Use dados MCP quando disponíveis.", + "Você é um agente especialista em faturas.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade para responder somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente, como “contract_key”, “customer_key” ou “MSISDN”.\nPara consultas informativas de fatura, apresente somente dados de negócio necessários, como valor, vencimento, situação e itens cobrados.\nNão acrescente canais, telefones, códigos USSD, URLs, aplicativos, lojas, relatórios adicionais, procedimentos alternativos ou próximos passos que não tenham sido explicitamente retornados pela tool/RAG e solicitados pelo usuário.\nNão ofereça espontaneamente outras ações ou detalhamentos.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/templates/agent_template_backend/app/agents/orders_agent.py b/templates/agent_template_backend/app/agents/orders_agent.py index f557bed..b793c26 100644 --- a/templates/agent_template_backend/app/agents/orders_agent.py +++ b/templates/agent_template_backend/app/agents/orders_agent.py @@ -95,7 +95,7 @@ class OrdersAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de pedidos de varejo. Use dados de tools quando disponíveis.", + "Você é um agente de pedidos de varejo.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso, alteração, troca, cancelamento ou qualquer mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/templates/agent_template_backend/app/agents/product_agent.py b/templates/agent_template_backend/app/agents/product_agent.py index 113bd8e..6c691d2 100644 --- a/templates/agent_template_backend/app/agents/product_agent.py +++ b/templates/agent_template_backend/app/agents/product_agent.py @@ -110,7 +110,7 @@ class ProductAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em produtos, planos e serviços. Explique sem fazer oferta proativa e sem executar ações sem confirmação. Use dados MCP quando disponíveis.", + "Você é um agente especialista em produtos, planos e serviços.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nEm consultas meramente informativas, não exponha flags ou capacidades transacionais internas como can.cancel e não informe espontaneamente que algo pode ser cancelado, alterado, contratado, removido ou trocado. Só mencione capacidade transacional quando o usuário tiver solicitado essa ação.\nNão faça oferta proativa e não execute nem simule mutações sem a confirmação exigida pelo framework.\nNão acrescente canais, procedimentos ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/templates/agent_template_backend/app/agents/support_agent.py b/templates/agent_template_backend/app/agents/support_agent.py index b4f0244..b15a0e4 100644 --- a/templates/agent_template_backend/app/agents/support_agent.py +++ b/templates/agent_template_backend/app/agents/support_agent.py @@ -95,7 +95,7 @@ class SupportAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de suporte de varejo para troca, devolução e garantia.", + "Você é um agente de suporte de varejo para troca, devolução e garantia.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso nem simule troca, devolução, garantia ou outra mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc b/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc index a6540ec..53216fc 100644 Binary files a/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc and b/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc b/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc index 8fc575e..5a02778 100644 Binary files a/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc and b/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc b/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc index 8253000..fa305ea 100644 Binary files a/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc and b/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc b/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc index 41a7208..761f5f7 100644 Binary files a/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc and b/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc b/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc index a8f3f88..4f9483c 100644 Binary files a/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc and b/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc b/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc index 3b7d6a8..ccfb755 100644 Binary files a/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc and b/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc b/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc index 9144c5b..288f277 100644 Binary files a/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc and b/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc index 9495cf2..5125182 100644 Binary files a/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc and b/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc b/templates/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..9191fad Binary files /dev/null and b/templates/agent_template_backend/app/presentation/__pycache__/__init__.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc index 32e74b9..69de758 100644 Binary files a/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc and b/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/presentation/tool_renderers.py b/templates/agent_template_backend/app/presentation/tool_renderers.py index f77c47a..cb44202 100644 --- a/templates/agent_template_backend/app/presentation/tool_renderers.py +++ b/templates/agent_template_backend/app/presentation/tool_renderers.py @@ -13,7 +13,42 @@ def _money_brl(value: Any) -> str: def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: - return f"[{agent_label}] Fatura consultada: {result}." + """Renderiza somente campos de negócio seguros da fatura. + + Identificadores técnicos/PII presentes no payload MCP (por exemplo msisdn, + customer_id, document e business keys) não devem ser propagados ao usuário. + """ + lines = [f"[{agent_label}] Dados da sua fatura:"] + total = result.get("valor_total") + vencimento = result.get("vencimento") + status = result.get("status") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + if vencimento not in (None, ""): + lines.append(f"Vencimento: {vencimento}.") + if status not in (None, ""): + lines.append(f"Situação: {status}.") + + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if not isinstance(item, dict): + continue + description = item.get("descricao") or item.get("nome") + value = item.get("valor") + if description in (None, ""): + continue + if value is None: + rendered_items.append(str(description)) + else: + rendered_items.append(f"{description}: R$ {_money_brl(value)}") + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + + # Se não houver nenhum campo de negócio seguro além do cabeçalho, deixe a + # composição pela LLM/guardrails em vez de despejar o payload bruto. + return " ".join(lines) if len(lines) > 1 else None def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: diff --git a/templates/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc b/templates/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc index d217376..3236364 100644 Binary files a/templates/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc and b/templates/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc b/templates/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc index 9ec9aa1..001fdff 100644 Binary files a/templates/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc and b/templates/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc index c5a3581..720b535 100644 Binary files a/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc and b/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ diff --git a/templates/agent_template_backend/app/workflows/agent_graph.py b/templates/agent_template_backend/app/workflows/agent_graph.py index e6605a4..fef0791 100644 --- a/templates/agent_template_backend/app/workflows/agent_graph.py +++ b/templates/agent_template_backend/app/workflows/agent_graph.py @@ -494,6 +494,119 @@ class AgentWorkflow: "next_state": "SESSION_ENDED", } + @staticmethod + def _output_guardrail_context(state: dict) -> dict: + """Monta o contexto operacional do turno para os guardrails de saída. + + Mantém evidências/protocolos necessários aos rails, mas impede que uma + transação encerrada ou semanticamente interrompida governe o novo turno. + O histórico completo permanece no state/checkpoint para auditoria. + """ + ctx = dict(state.get("context", {}) or {}) + mcp_results = state.get("mcp_results") or [] + ctx["evidence"] = mcp_results or ctx.get("evidence") + ctx["tool_result"] = mcp_results or ctx.get("tool_result") + ctx["tool_executed"] = any(isinstance(r, dict) and r.get("ok") for r in mcp_results) + + history = list(state.get("history") or []) + current_user_text = str(state.get("user_text") or "").strip() + if current_user_text: + if ( + not history + or not isinstance(history[-1], dict) + or str(history[-1].get("content") or "") != current_user_text + or str(history[-1].get("role") or "") != "user" + ): + history.append({"role": "user", "content": current_user_text}) + + route_decision = state.get("route_decision") or {} + route_metadata = route_decision.get("metadata") if isinstance(route_decision, dict) else {} + route_metadata = route_metadata if isinstance(route_metadata, dict) else {} + pre_validation = state.get("transaction_pre_validation") or {} + pre_validation = pre_validation if isinstance(pre_validation, dict) else {} + tx_status = str( + state.get("transaction_status") or pre_validation.get("status") or "" + ).strip().upper() + terminal_tx = bool(pre_validation.get("terminal")) or tx_status in { + "COMPLETED", "FAILED", "CANCELLED", "BLOCKED", "OUT_OF_SCOPE" + } + semantic_intent_shift = ( + str(route_metadata.get("transaction_interruption") or "").strip().lower() + == "intent_shift" + ) + stickiness_intent_shift = bool(route_metadata.get("route_stickiness_preempted")) + should_isolate_history = semantic_intent_shift or (terminal_tx and stickiness_intent_shift) + + current_route = str( + state.get("route") + or (route_decision.get("route") if isinstance(route_decision, dict) else "") + or "" + ).strip() + current_intent = str( + state.get("intent") + or (route_decision.get("intent") if isinstance(route_decision, dict) else "") + or "" + ).strip() + ctx["current_user_message"] = current_user_text + ctx["current_route"] = current_route + ctx["current_intent"] = current_intent + + if should_isolate_history: + operational_history = ( + [{"role": "user", "content": current_user_text}] + if current_user_text else [] + ) + ctx["historical_transaction_ignored"] = True + ctx["historical_transaction_status"] = tx_status or ( + "INTERRUPTED" if semantic_intent_shift else "TERMINAL" + ) + if semantic_intent_shift: + ctx["historical_transaction_interruption"] = "intent_shift" + for stale_key in ( + "transaction_pre_validation", + "transaction_status", + "active_transaction", + "transaction", + ): + ctx.pop(stale_key, None) + else: + operational_history = history + + ctx["conversation_history"] = operational_history + ctx["history_texts"] = [ + str(item.get("content") or "") + for item in operational_history + if isinstance(item, dict) and item.get("content") not in (None, "") + ] + + protocols: list[str] = [] + seen: set[str] = set() + protocol_keys = { + "protocol_number", "protocolo_id", "interactionProtocol", + "protocolNumber", "finalizacao_protocol", + } + + def walk(value): + if isinstance(value, dict): + for key, item in value.items(): + if key in protocol_keys and item not in (None, ""): + text = str(item).strip() + if text and text not in seen: + seen.add(text) + protocols.append(text) + elif isinstance(item, (dict, list, tuple)): + walk(item) + elif isinstance(value, (list, tuple)): + for item in value: + walk(item) + + walk(mcp_results) + if protocols: + ctx["expected_protocols"] = protocols + ctx["requer_protocolo"] = True + ctx.setdefault("tipo_fluxo", "ajuste") + return ctx + async def output_supervisor(self, state): """Valida a resposta candidata com o OutputSupervisor corporativo. @@ -509,15 +622,15 @@ class AgentWorkflow: } candidate = state.get("answer") or "" - context = { - **(state.get("context") or {}), + context = self._output_guardrail_context(state) + context.update({ "tenant_id": state.get("tenant_id"), "agent_id": state.get("agent_id"), "session_id": state.get("conversation_key") or state.get("session_id"), "route": state.get("route"), "intent": state.get("intent"), "supervisor_attempt": int(state.get("supervisor_attempt", 0)), - } + }) async with self.telemetry.span( "workflow.output_supervisor", session_id=state.get("conversation_key") or state.get("session_id"), @@ -603,7 +716,7 @@ class AgentWorkflow: component="workflow.output_guardrails.start", ) final, decisions = await self.guardrails.run_output( - state["answer"], state.get("context", {}) + state["answer"], self._output_guardrail_context(state) ) for _decision in decisions: await self.guardrail_telemetry.evaluated("output", _decision) diff --git a/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc b/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc index eafedee..6a9d209 100644 Binary files a/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc and b/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc b/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc index c13b42c..7e6bd82 100644 Binary files a/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc and b/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc differ diff --git a/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc index a8f0280..a18a16d 100644 Binary files a/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc and b/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ diff --git a/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc b/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc index f856d0c..4dc5fa7 100644 Binary files a/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc and b/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc differ diff --git a/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc b/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc index 406206d..fa947b3 100644 Binary files a/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc and b/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc differ diff --git a/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc b/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc index cbe2d5d..50916c3 100644 Binary files a/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc and b/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc differ diff --git a/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc b/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc index d394a5b..4e1d80e 100644 Binary files a/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc and b/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc differ diff --git a/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc b/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc index d96c6e4..ac2905a 100644 Binary files a/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc and b/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc b/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc index 0d39586..f1619c0 100644 Binary files a/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc and b/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc b/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc index 9dee705..fa42c73 100644 Binary files a/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc and b/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc differ diff --git a/templates/agent_template_backend_day_zero/app/agents/billing_agent.py b/templates/agent_template_backend_day_zero/app/agents/billing_agent.py index aa60099..05254d1 100644 --- a/templates/agent_template_backend_day_zero/app/agents/billing_agent.py +++ b/templates/agent_template_backend_day_zero/app/agents/billing_agent.py @@ -95,7 +95,7 @@ class BillingAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em faturas. Responda com clareza, objetividade e sem sugerir ações não solicitadas. Use dados MCP quando disponíveis.", + "Você é um agente especialista em faturas.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade para responder somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente, como “contract_key”, “customer_key” ou “MSISDN”.\nPara consultas informativas de fatura, apresente somente dados de negócio necessários, como valor, vencimento, situação e itens cobrados.\nNão acrescente canais, telefones, códigos USSD, URLs, aplicativos, lojas, relatórios adicionais, procedimentos alternativos ou próximos passos que não tenham sido explicitamente retornados pela tool/RAG e solicitados pelo usuário.\nNão ofereça espontaneamente outras ações ou detalhamentos.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/templates/agent_template_backend_day_zero/app/agents/orders_agent.py b/templates/agent_template_backend_day_zero/app/agents/orders_agent.py index f557bed..b793c26 100644 --- a/templates/agent_template_backend_day_zero/app/agents/orders_agent.py +++ b/templates/agent_template_backend_day_zero/app/agents/orders_agent.py @@ -95,7 +95,7 @@ class OrdersAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de pedidos de varejo. Use dados de tools quando disponíveis.", + "Você é um agente de pedidos de varejo.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso, alteração, troca, cancelamento ou qualquer mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/templates/agent_template_backend_day_zero/app/agents/product_agent.py b/templates/agent_template_backend_day_zero/app/agents/product_agent.py index 34433f5..998dad6 100644 --- a/templates/agent_template_backend_day_zero/app/agents/product_agent.py +++ b/templates/agent_template_backend_day_zero/app/agents/product_agent.py @@ -95,7 +95,7 @@ class ProductAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente especialista em produtos, planos e serviços. Explique sem fazer oferta proativa e sem executar ações sem confirmação. Use dados MCP quando disponíveis.", + "Você é um agente especialista em produtos, planos e serviços.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou MSISDN/telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nEm consultas meramente informativas, não exponha flags ou capacidades transacionais internas como can.cancel e não informe espontaneamente que algo pode ser cancelado, alterado, contratado, removido ou trocado. Só mencione capacidade transacional quando o usuário tiver solicitado essa ação.\nNão faça oferta proativa e não execute nem simule mutações sem a confirmação exigida pelo framework.\nNão acrescente canais, procedimentos ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não declare sucesso, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/templates/agent_template_backend_day_zero/app/agents/support_agent.py b/templates/agent_template_backend_day_zero/app/agents/support_agent.py index b4f0244..b15a0e4 100644 --- a/templates/agent_template_backend_day_zero/app/agents/support_agent.py +++ b/templates/agent_template_backend_day_zero/app/agents/support_agent.py @@ -95,7 +95,7 @@ class SupportAgent(AgentRuntimeMixin): state, system_prompt=apply_agent_profile_prompt( state, - "Você é um agente de suporte de varejo para troca, devolução e garantia.", + "Você é um agente de suporte de varejo para troca, devolução e garantia.\n\nUse dados de tools/MCP e RAG autorizados como fonte de verdade e responda somente à solicitação atual.\nNunca exponha identificadores técnicos ou de identidade presentes no estado, contexto ou MCP, incluindo customer_key, contract_key, account_key, resource_key, session_key, customer_id, document, message_id, ura_call_id ou telefone completo.\nNão transforme nomes internos de campos em rótulos para o cliente.\nNão declare sucesso nem simule troca, devolução, garantia ou outra mutação se a tool não tiver confirmado a execução.\nNão acrescente canais, procedimentos, ofertas ou próximos passos não solicitados.\nSe uma tool retornar BLOCKED, OUT_OF_SCOPE, NOT_ALLOWED, FAILED ou outro resultado terminal, explique somente o motivo retornado, não invente alternativa e encerre a resposta.", ), mcp_results=tool_context, rag_context=rag_context, diff --git a/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc b/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc index 359dee6..029d41d 100644 Binary files a/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc and b/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc index 76a03bb..2d327ac 100644 Binary files a/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc and b/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc index a39b585..09d9e25 100644 Binary files a/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc and b/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ 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 8612e4f..aa1eb63 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 @@ -492,6 +492,119 @@ class AgentWorkflow: "next_state": "SESSION_ENDED", } + @staticmethod + def _output_guardrail_context(state: dict) -> dict: + """Monta o contexto operacional do turno para os guardrails de saída. + + Mantém evidências/protocolos necessários aos rails, mas impede que uma + transação encerrada ou semanticamente interrompida governe o novo turno. + O histórico completo permanece no state/checkpoint para auditoria. + """ + ctx = dict(state.get("context", {}) or {}) + mcp_results = state.get("mcp_results") or [] + ctx["evidence"] = mcp_results or ctx.get("evidence") + ctx["tool_result"] = mcp_results or ctx.get("tool_result") + ctx["tool_executed"] = any(isinstance(r, dict) and r.get("ok") for r in mcp_results) + + history = list(state.get("history") or []) + current_user_text = str(state.get("user_text") or "").strip() + if current_user_text: + if ( + not history + or not isinstance(history[-1], dict) + or str(history[-1].get("content") or "") != current_user_text + or str(history[-1].get("role") or "") != "user" + ): + history.append({"role": "user", "content": current_user_text}) + + route_decision = state.get("route_decision") or {} + route_metadata = route_decision.get("metadata") if isinstance(route_decision, dict) else {} + route_metadata = route_metadata if isinstance(route_metadata, dict) else {} + pre_validation = state.get("transaction_pre_validation") or {} + pre_validation = pre_validation if isinstance(pre_validation, dict) else {} + tx_status = str( + state.get("transaction_status") or pre_validation.get("status") or "" + ).strip().upper() + terminal_tx = bool(pre_validation.get("terminal")) or tx_status in { + "COMPLETED", "FAILED", "CANCELLED", "BLOCKED", "OUT_OF_SCOPE" + } + semantic_intent_shift = ( + str(route_metadata.get("transaction_interruption") or "").strip().lower() + == "intent_shift" + ) + stickiness_intent_shift = bool(route_metadata.get("route_stickiness_preempted")) + should_isolate_history = semantic_intent_shift or (terminal_tx and stickiness_intent_shift) + + current_route = str( + state.get("route") + or (route_decision.get("route") if isinstance(route_decision, dict) else "") + or "" + ).strip() + current_intent = str( + state.get("intent") + or (route_decision.get("intent") if isinstance(route_decision, dict) else "") + or "" + ).strip() + ctx["current_user_message"] = current_user_text + ctx["current_route"] = current_route + ctx["current_intent"] = current_intent + + if should_isolate_history: + operational_history = ( + [{"role": "user", "content": current_user_text}] + if current_user_text else [] + ) + ctx["historical_transaction_ignored"] = True + ctx["historical_transaction_status"] = tx_status or ( + "INTERRUPTED" if semantic_intent_shift else "TERMINAL" + ) + if semantic_intent_shift: + ctx["historical_transaction_interruption"] = "intent_shift" + for stale_key in ( + "transaction_pre_validation", + "transaction_status", + "active_transaction", + "transaction", + ): + ctx.pop(stale_key, None) + else: + operational_history = history + + ctx["conversation_history"] = operational_history + ctx["history_texts"] = [ + str(item.get("content") or "") + for item in operational_history + if isinstance(item, dict) and item.get("content") not in (None, "") + ] + + protocols: list[str] = [] + seen: set[str] = set() + protocol_keys = { + "protocol_number", "protocolo_id", "interactionProtocol", + "protocolNumber", "finalizacao_protocol", + } + + def walk(value): + if isinstance(value, dict): + for key, item in value.items(): + if key in protocol_keys and item not in (None, ""): + text = str(item).strip() + if text and text not in seen: + seen.add(text) + protocols.append(text) + elif isinstance(item, (dict, list, tuple)): + walk(item) + elif isinstance(value, (list, tuple)): + for item in value: + walk(item) + + walk(mcp_results) + if protocols: + ctx["expected_protocols"] = protocols + ctx["requer_protocolo"] = True + ctx.setdefault("tipo_fluxo", "ajuste") + return ctx + async def output_supervisor(self, state): """Valida a resposta candidata com o OutputSupervisor corporativo. @@ -507,15 +620,15 @@ class AgentWorkflow: } candidate = state.get("answer") or "" - context = { - **(state.get("context") or {}), + context = self._output_guardrail_context(state) + context.update({ "tenant_id": state.get("tenant_id"), "agent_id": state.get("agent_id"), "session_id": state.get("conversation_key") or state.get("session_id"), "route": state.get("route"), "intent": state.get("intent"), "supervisor_attempt": int(state.get("supervisor_attempt", 0)), - } + }) async with self.telemetry.span( "workflow.output_supervisor", session_id=state.get("conversation_key") or state.get("session_id"), @@ -601,7 +714,7 @@ class AgentWorkflow: component="workflow.output_guardrails.start", ) final, decisions = await self.guardrails.run_output( - state["answer"], state.get("context", {}) + state["answer"], self._output_guardrail_context(state) ) for _decision in decisions: await self.guardrail_telemetry.evaluated("output", _decision) 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 17572f2..2e7e76e 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_agent_template_backend_safety_consistency.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_agent_template_backend_safety_consistency.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..203c1aa Binary files /dev/null and b/tests/__pycache__/test_agent_template_backend_safety_consistency.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/__pycache__/test_generic_tool_response_presentation.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_generic_tool_response_presentation.cpython-313-pytest-9.0.2.pyc index 4b2fda1..0e408dc 100644 Binary files a/tests/__pycache__/test_generic_tool_response_presentation.cpython-313-pytest-9.0.2.pyc and b/tests/__pycache__/test_generic_tool_response_presentation.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 a56a7c0..479cc36 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_transaction_confirmation_customer_facing.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_transaction_confirmation_customer_facing.cpython-313-pytest-9.0.2.pyc index 289bd4f..64c4baa 100644 Binary files a/tests/__pycache__/test_transaction_confirmation_customer_facing.cpython-313-pytest-9.0.2.pyc and b/tests/__pycache__/test_transaction_confirmation_customer_facing.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/__pycache__/test_transaction_state_router_interruption.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_transaction_state_router_interruption.cpython-313-pytest-9.0.2.pyc index 2bba895..89a9991 100644 Binary files a/tests/__pycache__/test_transaction_state_router_interruption.cpython-313-pytest-9.0.2.pyc and b/tests/__pycache__/test_transaction_state_router_interruption.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 6300ab3..25a6167 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_agent_template_backend_safety_consistency.py b/tests/test_agent_template_backend_safety_consistency.py new file mode 100644 index 0000000..9c2788a --- /dev/null +++ b/tests/test_agent_template_backend_safety_consistency.py @@ -0,0 +1,70 @@ +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + + +def _template_dirs(): + return sorted( + p + for p in ROOT.rglob("*") + if p.is_dir() and p.name in {"agent_template_backend", "agent_template_backend_day_zero"} + ) + + +def test_all_agent_template_backend_copies_have_safe_agent_prompts(): + targets = _template_dirs() + assert targets, "Nenhum template backend encontrado" + for base in targets: + checks = { + "billing_agent.py": ( + "Nunca exponha identificadores técnicos", + "Não acrescente canais", + "Se uma tool retornar BLOCKED", + ), + "product_agent.py": ( + "Nunca exponha identificadores técnicos", + "can.cancel", + "Se uma tool retornar BLOCKED", + ), + "orders_agent.py": ( + "Nunca exponha identificadores técnicos", + "Não declare sucesso", + "Se uma tool retornar BLOCKED", + ), + "support_agent.py": ( + "Nunca exponha identificadores técnicos", + "Não declare sucesso", + "Se uma tool retornar BLOCKED", + ), + } + for filename, required in checks.items(): + text = (base / "app" / "agents" / filename).read_text(encoding="utf-8") + for marker in required: + assert marker in text, f"{base.relative_to(ROOT)}/{filename} sem {marker!r}" + + +def test_all_agent_template_backend_copies_isolate_interrupted_transaction_context(): + for base in _template_dirs(): + graph = (base / "app" / "workflows" / "agent_graph.py").read_text(encoding="utf-8") + assert 'route_metadata.get("transaction_interruption")' in graph + assert '== "intent_shift"' in graph + assert 'ctx["historical_transaction_ignored"] = True' in graph + assert 'ctx["expected_protocols"] = protocols' in graph + assert 'self._output_guardrail_context(state)' in graph + + +def test_full_templates_do_not_dump_raw_invoice_payload(): + checked = 0 + for base in _template_dirs(): + renderer = base / "app" / "presentation" / "tool_renderers.py" + if not renderer.exists(): + continue + checked += 1 + text = renderer.read_text(encoding="utf-8") + assert 'Fatura consultada: {result}' not in text + assert 'result.get("valor_total")' in text + assert 'result.get("vencimento")' in text + assert 'result.get("status")' in text + assert 'result.get("itens")' in text + assert checked > 0