This commit is contained in:
2026-06-13 09:45:18 -03:00
parent 89c23fb0ed
commit 471c0e21a9
18 changed files with 837 additions and 91 deletions

8
.env
View File

@@ -189,18 +189,18 @@ BACKOFFICE_MCP_BACKEND_TYPE=tim_clients
BACKOFFICE_MCP_FAIL_OPEN_ON_BACKEND_ERROR=true BACKOFFICE_MCP_FAIL_OPEN_ON_BACKEND_ERROR=true
# TIM/develop original - preencha no .env real, nunca versionar segredos. # TIM/develop original - preencha no .env real, nunca versionar segredos.
PMID_API_HOST=https://pmidfqa.internal.timbrasil.com.br/access/v1/info PMID_API_HOST=http://localhost:8011/access/v1/info
PMID_API_BASIC_TOKEN=Basic dGlteDpAckp1SkpAcFJPUiM= PMID_API_BASIC_TOKEN=Basic dGlteDpAckp1SkpAcFJPUiM=
PMID_API_TIMEOUT=10 PMID_API_TIMEOUT=10
PMID_API_CLIENT_ID=AIAGENTCR PMID_API_CLIENT_ID=AIAGENTCR
SIEBEL_API_HOST=https://pmidfqa.internal.timbrasil.com.br/customers/v1/backOfficeSRopening SIEBEL_API_HOST=http://localhost:8011/customers/v1/backOfficeSRopening
SIEBEL_API_TIMEOUT=10 SIEBEL_API_TIMEOUT=10
SIEBEL_API_USERNAME=aiagentcr SIEBEL_API_USERNAME=aiagentcr
SIEBEL_API_PASSWORD=AiAgentCR#FQA# SIEBEL_API_PASSWORD=AiAgentCR#FQA#
SIEBEL_API_CLIENT_ID=AIAGENTCR SIEBEL_API_CLIENT_ID=AIAGENTCR
SIEBEL_STATUS_API_HOST=https://pmidfqa.internal.timbrasil.com.br SIEBEL_STATUS_API_HOST=https://locahost:8011
SIEBEL_STATUS_API_ROUTE=/interactions/v1/statusServiceRequest SIEBEL_STATUS_API_ROUTE=/interactions/v1/statusServiceRequest
SIEBEL_PREPAGO_STATUS_API_HOST=https://pmidfqa.internal.timbrasil.com.br SIEBEL_PREPAGO_STATUS_API_HOST=https://localhost:8011
SIEBEL_PREPAGO_STATUS_API_ROUTE=/customers/v1/serviceRequest SIEBEL_PREPAGO_STATUS_API_ROUTE=/customers/v1/serviceRequest
VERIFY_SSL=false VERIFY_SSL=false

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -0,0 +1,41 @@
# Backoffice framework-native migration notes
This package reduces direct legacy dependencies in the active runtime.
Main changes:
- Domain nodes call framework services through `src.compat.framework_services`.
- IMDB enrichment uses MCP tool `consultar_imdb_cliente` first.
- Speech enrichment uses MCP tool `consultar_speech_analytics` first.
- TAIS KB enrichment uses MCP tool `consultar_tais_kb` first.
- Direct TIM clients are disabled by default and only available behind `BACKOFFICE_ALLOW_LEGACY_CLIENTS=true` for parity tests.
- Legacy model aliases such as `bo_gptoss20b_dev` are mapped to the framework model, defaulting to `openai.gpt-4.1`.
- Non-serializable progress producers are no longer stored inside LangGraph state, reducing checkpoint hash corruption risk.
- MCP server defaults to mock mode for local development.
Recommended local execution:
```bash
# Terminal 1: MCP mock server
uvicorn mcp_servers.backoffice_mcp_server.main:app --host 0.0.0.0 --port 8010 --reload
# Terminal 2: backend
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
```
Optional standalone IMDB mock, only if you intentionally enable legacy direct clients:
```bash
uvicorn mock_servers.mock_imdb_server:app --host 0.0.0.0 --port 8011 --reload
```
For a fully framework-native local test, keep:
```env
BACKOFFICE_ALLOW_LEGACY_CLIENTS=false
BACKOFFICE_MCP_USE_MOCK=true
BACKOFFICE_MCP_BACKEND_TYPE=mock
CLASSIFICATION_LLM_MODEL=openai.gpt-4.1
CLASSIFICATION_LARGE_LLM_MODEL=openai.gpt-4.1
TAIS_KB_LLM_MODEL=openai.gpt-4.1
```

View File

@@ -16,8 +16,10 @@ from agent_framework.config.settings import settings
from agent_framework.analytics.factory import create_analytics_publisher from agent_framework.analytics.factory import create_analytics_publisher
from agent_framework.observability.observer import AgentObserver from agent_framework.observability.observer import AgentObserver
from src.compat.framework_observer import configure as configure_global_observer from src.compat.framework_observer import configure as configure_global_observer
from src.compat.framework_services import configure as configure_framework_services
from agent_framework.llm.providers import create_llm from agent_framework.llm.providers import create_llm
from agent_framework.memory.message_history import create_memory from agent_framework.memory.message_history import create_memory
from agent_framework.memory.summary_memory import create_conversation_summary_memory
from agent_framework.mcp.tool_router import create_mcp_tool_router from agent_framework.mcp.tool_router import create_mcp_tool_router
from agent_framework.models.identity import AgentIdentity from agent_framework.models.identity import AgentIdentity
from agent_framework.identity import IdentityResolver, BusinessContext from agent_framework.identity import IdentityResolver, BusinessContext
@@ -49,10 +51,11 @@ telemetry = Telemetry(settings)
usage_repository = create_usage_repository(settings) usage_repository = create_usage_repository(settings)
llm = create_llm(settings, telemetry=telemetry, usage_repository=usage_repository) llm = create_llm(settings, telemetry=telemetry, usage_repository=usage_repository)
memory = create_memory(settings) memory = create_memory(settings)
summary_memory = create_conversation_summary_memory(settings, message_history=memory, llm=llm, telemetry=telemetry)
sessions = create_session_repository(settings) sessions = create_session_repository(settings)
checkpoints = create_checkpoint_repository(settings) checkpoints = create_checkpoint_repository(settings)
cache = create_cache(settings, telemetry=telemetry) cache = create_cache(settings, telemetry=telemetry)
gateway = ChannelGateway() gateway = ChannelGateway(input_mode=settings.FRAMEWORK_CHANNEL_INPUT_MODE)
analytics = create_analytics_publisher(settings) analytics = create_analytics_publisher(settings)
observer = AgentObserver(analytics=analytics) observer = AgentObserver(analytics=analytics)
configure_global_observer({ configure_global_observer({
@@ -61,10 +64,11 @@ configure_global_observer({
"topic_path": getattr(settings, "GCP_PUBSUB_TOPIC_PATH", None) or getattr(settings, "AGENT_PUBSUB_TOPIC", None), "topic_path": getattr(settings, "GCP_PUBSUB_TOPIC_PATH", None) or getattr(settings, "AGENT_PUBSUB_TOPIC", None),
}) })
tool_router = create_mcp_tool_router(settings, telemetry=telemetry) tool_router = create_mcp_tool_router(settings, telemetry=telemetry)
configure_framework_services(tool_router=tool_router)
identity_resolver = IdentityResolver.from_yaml(settings.IDENTITY_CONFIG_PATH) identity_resolver = IdentityResolver.from_yaml(settings.IDENTITY_CONFIG_PATH)
agent_profiles = AgentProfileRegistry(settings) agent_profiles = AgentProfileRegistry(settings)
sse_hub = SSEHub(settings, telemetry=telemetry) sse_hub = SSEHub(settings, telemetry=telemetry)
workflow = AgentWorkflow(llm, memory, telemetry, analytics, settings, observer=observer, tool_router=tool_router) workflow = AgentWorkflow(llm, memory, telemetry, analytics, settings, observer=observer, tool_router=tool_router, summary_memory=summary_memory)
backoffice_runtime = BackofficeNativeRuntime(settings=settings, telemetry=telemetry, analytics=analytics, observer=observer) backoffice_runtime = BackofficeNativeRuntime(settings=settings, telemetry=telemetry, analytics=analytics, observer=observer)
logger.info("LLM provider carregado: %s", llm.__class__.__name__) logger.info("LLM provider carregado: %s", llm.__class__.__name__)
@@ -501,6 +505,9 @@ async def shutdown():
# Elas chamam o BackofficeNativeRuntime, que compila os workflows com o motor # Elas chamam o BackofficeNativeRuntime, que compila os workflows com o motor
# do framework e aplica guardrails, judges, supervisor, checkpoint e telemetry. # do framework e aplica guardrails, judges, supervisor, checkpoint e telemetry.
from src.api.schemas.anatel_schemas import TicketRequestEvent
from src.api.schemas.anatel_response_emulator_schemas import EmulatorGenerateRequest, EmulatorFinalizeRequest
import asyncio import asyncio
from fastapi.exceptions import RequestValidationError from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
@@ -531,13 +538,15 @@ async def startup_backoffice_native_domain():
if getattr(original_settings, "ENABLE_OCI_STREAMING", False): if getattr(original_settings, "ENABLE_OCI_STREAMING", False):
from src.infrastructure.streaming.producer import OciProducer from src.infrastructure.streaming.producer import OciProducer
app.state.oci_producer = OciProducer(original_settings.OCI_RESPONSE_STREAM_OCID) app.state.oci_producer = OciProducer(original_settings.OCI_RESPONSE_STREAM_OCID)
logger.info("OCI producer de domínio inicializado; consumer legado não é iniciado") configure_framework_services(progress_producer=app.state.oci_producer)
logger.info("OCI producer de domínio inicializado via framework services; consumer legado não é iniciado")
else: else:
try: try:
from src.infrastructure.streaming.debug_producer import LocalDebugProducer from src.infrastructure.streaming.debug_producer import LocalDebugProducer
if getattr(original_settings, "DEBUG", False): if getattr(original_settings, "DEBUG", False):
app.state.oci_producer = LocalDebugProducer() app.state.oci_producer = LocalDebugProducer()
logger.info("LocalDebugProducer de domínio ativo") configure_framework_services(progress_producer=app.state.oci_producer)
logger.info("LocalDebugProducer de domínio ativo via framework services")
except Exception: except Exception:
pass pass
except Exception: except Exception:
@@ -621,37 +630,34 @@ async def _run_native_checklist(event, request: Request):
@app.post("/agent/process-ticket", status_code=status.HTTP_200_OK) @app.post("/agent/process-ticket", status_code=status.HTTP_200_OK)
async def native_process_ticket(request: Request, event: "TicketRequestEvent"): async def native_process_ticket(request: Request, event: TicketRequestEvent):
return await _run_native_checklist(event, request) return await _run_native_checklist(event, request)
@app.post("/agent/execute", status_code=status.HTTP_200_OK) @app.post("/agent/execute", status_code=status.HTTP_200_OK)
async def native_agent_execute(request: Request, event: "TicketRequestEvent"): async def native_agent_execute(request: Request, event: TicketRequestEvent):
return await _run_native_checklist(event, request) return await _run_native_checklist(event, request)
@app.post("/agent/process-and-stream", status_code=status.HTTP_200_OK) @app.post("/agent/process-and-stream", status_code=status.HTTP_200_OK)
async def native_process_and_stream(request: Request, event: "TicketRequestEvent"): async def native_process_and_stream(request: Request, event: TicketRequestEvent):
return await _run_native_checklist(event, request) return await _run_native_checklist(event, request)
@app.post("/agent/search-tais-kb", status_code=status.HTTP_200_OK) @app.post("/agent/search-tais-kb", status_code=status.HTTP_200_OK)
async def native_search_tais_kb(body: dict): async def native_search_tais_kb(body: dict):
"""Adapter REST para TAIS KB usando o service/cliente de domínio, não grafo legado.""" """Adapter REST framework-native para TAIS KB via MCP Router."""
try: from src.compat.framework_services import call_mcp_tool
from src.components.clients.tais_kb_client import TaisKbClient
query = body.get("query") or body.get("text") or body.get("complaint_description") or "" query = body.get("query") or body.get("text") or body.get("complaint_description") or ""
client = TaisKbClient() result = await call_mcp_tool(
if hasattr(client, "search"): "consultar_tais_kb",
result = await client.search(query=query, **{k: v for k, v in body.items() if k not in {"query", "text", "complaint_description"}}) {"query": query, "protocol_id": body.get("protocol_id") or body.get("complaintProtocol"), "customer_key": body.get("customer_key") or body.get("cpfCnpj")},
elif hasattr(client, "query"): business_context={"customer_key": body.get("customer_key") or body.get("cpfCnpj"), "interaction_key": body.get("protocol_id") or body.get("complaintProtocol")},
result = await client.query(query) original_context=body,
else: )
raise AttributeError("TAIS KB client has no search/query method") if not result.get("ok"):
await telemetry.event("backoffice.tais_kb.failed", {"error": result.get("error")}, kind="tool")
return {"framework_native": True, "result": result} return {"framework_native": True, "result": result}
except Exception as exc:
await telemetry.event("backoffice.tais_kb.failed", {"error": str(exc)}, kind="tool")
return {"framework_native": True, "result": [], "error": str(exc)}
async def _run_native_emulator(request: Request, event): async def _run_native_emulator(request: Request, event):
@@ -687,7 +693,7 @@ async def _run_native_emulator(request: Request, event):
@app.post("/case/{transaction_id}/response-emulator/generate", status_code=status.HTTP_200_OK) @app.post("/case/{transaction_id}/response-emulator/generate", status_code=status.HTTP_200_OK)
async def native_response_emulator_generate(transaction_id: str, request: Request, body: "EmulatorGenerateRequest"): async def native_response_emulator_generate(transaction_id: str, request: Request, body: EmulatorGenerateRequest):
from src.api.schemas.anatel_response_emulator_schemas import ResponseEmulatorRequestEvent, OperatorFeedback from src.api.schemas.anatel_response_emulator_schemas import ResponseEmulatorRequestEvent, OperatorFeedback
if body.transactionId != transaction_id: if body.transactionId != transaction_id:
raise HTTPException(status_code=400, detail="transactionId path/body mismatch") raise HTTPException(status_code=400, detail="transactionId path/body mismatch")
@@ -711,7 +717,7 @@ async def native_response_emulator_generate(transaction_id: str, request: Reques
@app.post("/case/{transaction_id}/response-emulator/finalize", status_code=status.HTTP_200_OK) @app.post("/case/{transaction_id}/response-emulator/finalize", status_code=status.HTTP_200_OK)
async def native_response_emulator_finalize(transaction_id: str, request: Request, body: "EmulatorFinalizeRequest"): async def native_response_emulator_finalize(transaction_id: str, request: Request, body: EmulatorFinalizeRequest):
from src.api.schemas.anatel_response_emulator_schemas import ResponseEmulatorRequestEvent from src.api.schemas.anatel_response_emulator_schemas import ResponseEmulatorRequestEvent
if body.transactionId != transaction_id: if body.transactionId != transaction_id:
raise HTTPException(status_code=400, detail="transactionId path/body mismatch") raise HTTPException(status_code=400, detail="transactionId path/body mismatch")
@@ -820,7 +826,3 @@ async def debug_backoffice_parity():
}, },
} }
# Late imports only for FastAPI annotation resolution. They are schemas, not
# workflow motors.
from src.api.schemas.anatel_schemas import TicketRequestEvent
from src.api.schemas.anatel_response_emulator_schemas import EmulatorGenerateRequest, EmulatorFinalizeRequest

View File

@@ -20,6 +20,7 @@ from app.agents.support_agent import SupportAgent
from app.agents.backoffice_agent import BackofficeAgent from app.agents.backoffice_agent import BackofficeAgent
from app.state import AgentState from app.state import AgentState
from agent_framework.rag.rag_service import RagService from agent_framework.rag.rag_service import RagService
from agent_framework.rag.embedding_provider import create_embedding_provider
from agent_framework.cache.cache import create_cache from agent_framework.cache.cache import create_cache
@@ -85,7 +86,7 @@ class AgentWorkflow:
Em ambos os modos, memória/checkpoint/session usam tenant_id:agent_id:session_id. Em ambos os modos, memória/checkpoint/session usam tenant_id:agent_id:session_id.
""" """
def __init__(self, llm, memory, telemetry, analytics, settings, observer: AgentObserver | None = None, tool_router=None): def __init__(self, llm, memory, telemetry, analytics, settings, observer: AgentObserver | None = None, tool_router=None, summary_memory=None):
self.llm = llm self.llm = llm
self.memory = memory self.memory = memory
self.telemetry = telemetry self.telemetry = telemetry
@@ -93,7 +94,7 @@ class AgentWorkflow:
self.observer = observer or AgentObserver(analytics=analytics) self.observer = observer or AgentObserver(analytics=analytics)
self.settings = settings self.settings = settings
self.tool_router = tool_router self.tool_router = tool_router
self.tool_router = tool_router self.summary_memory = summary_memory
self.guardrails = GuardrailPipeline( self.guardrails = GuardrailPipeline(
observer=self.observer, observer=self.observer,
enable_parallel=bool(getattr(settings, "ENABLE_PARALLEL_GUARDRAILS", True)), enable_parallel=bool(getattr(settings, "ENABLE_PARALLEL_GUARDRAILS", True)),
@@ -113,6 +114,7 @@ class AgentWorkflow:
self.judge_telemetry = JudgeTelemetry(telemetry) self.judge_telemetry = JudgeTelemetry(telemetry)
self.langgraph_telemetry = LangGraphDeepTelemetry(telemetry) self.langgraph_telemetry = LangGraphDeepTelemetry(telemetry)
self.cache = create_cache(settings) self.cache = create_cache(settings)
self.embedding_provider = create_embedding_provider(settings)
self.rag_service = RagService(settings, telemetry=telemetry) self.rag_service = RagService(settings, telemetry=telemetry)
self.router = EnterpriseRouter(settings, llm=llm, telemetry=telemetry) self.router = EnterpriseRouter(settings, llm=llm, telemetry=telemetry)
agent_kwargs = {"telemetry": telemetry, "tool_router": getattr(self, "tool_router", None), "rag_service": self.rag_service, "cache": self.cache, "settings": settings, "observer": self.observer} agent_kwargs = {"telemetry": telemetry, "tool_router": getattr(self, "tool_router", None), "rag_service": self.rag_service, "cache": self.cache, "settings": settings, "observer": self.observer}

View File

@@ -382,8 +382,9 @@ class BackofficeNativeRuntime:
return state return state
@staticmethod @staticmethod
def _after_input_guardrails(state: AgentState) -> str: def _after_input_guardrails(state):
return "blocked" if state.get("error", {}).get("type") == "InputGuardrailBlocked" else "continue" error = state.get("error") or {}
return "blocked" if error.get("type") == "InputGuardrailBlocked" else "continue"
async def _framework_output_supervisor(self, state: AgentState) -> AgentState: async def _framework_output_supervisor(self, state: AgentState) -> AgentState:
await self._safe_emit_grl( await self._safe_emit_grl(
@@ -544,7 +545,8 @@ class BackofficeNativeRuntime:
"guardrails_config_path": self.guardrails_config.get("_config_path"), "guardrails_config_path": self.guardrails_config.get("_config_path"),
"active_input_rails": [r.get("code") for r in self.guardrails_config.get("input", []) if r.get("enabled", True)], "active_input_rails": [r.get("code") for r in self.guardrails_config.get("input", []) if r.get("enabled", True)],
"active_output_rails": [r.get("code") for r in self.guardrails_config.get("output", []) if r.get("enabled", True)], "active_output_rails": [r.get("code") for r in self.guardrails_config.get("output", []) if r.get("enabled", True)],
"_oci_producer": getattr(app_state, "oci_producer", None) if app_state is not None else None, # Progress producer is kept in src.compat.framework_services, not in state,
# so checkpoints remain JSON-stable and integrity hashes do not change.
"_framework_ics_bridge_index": 0, "_framework_ics_bridge_index": 0,
}) })
try: try:

88
debug_events.jsonl Normal file
View File

@@ -0,0 +1,88 @@
{"ts": "2026-06-13T11:32:45.674888Z", "key": "man-9d2cd2df", "payload": {"transactionId": "man-9d2cd2df", "processing": {"status": "processing", "current_step": "fetching_ticket", "action": "await_response", "note": "[13/06/2026 08:32:45] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:32:45.674868Z"}}}
{"ts": "2026-06-13T11:32:45.680553Z", "key": "man-9d2cd2df", "payload": {"transactionId": "man-9d2cd2df", "processing": {"status": "processing", "current_step": "validation", "action": "await_response", "note": "[13/06/2026 08:32:45] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:32:45.680538Z"}}}
{"ts": "2026-06-13T11:32:45.698929Z", "key": "man-9d2cd2df", "payload": {"transactionId": "man-9d2cd2df", "processing": {"status": "processing", "current_step": "imdb_enrichment", "action": "await_response", "note": "[13/06/2026 08:32:45] Chamado recebido, iniciando processamento.\n[13/06/2026 08:32:45] Enriquecendo chamado no IMDB....", "timestamp": "2026-06-13T11:32:45.698905Z"}}}
{"ts": "2026-06-13T11:33:17.366395Z", "key": "man-9d2cd2df", "payload": {"transactionId": "man-9d2cd2df", "processing": {"status": "processing", "current_step": "imdb_enrichment_failed", "action": "await_response", "note": "[13/06/2026 08:32:45] Chamado recebido, iniciando processamento.\n[13/06/2026 08:32:45] Enriquecendo chamado no IMDB....\n[13/06/2026 08:33:17] Falha no enriquecimento IMDB.", "timestamp": "2026-06-13T11:33:17.366360Z"}}}
{"ts": "2026-06-13T11:41:02.192755Z", "key": "man-fc7947cd", "payload": {"transactionId": "man-fc7947cd", "processing": {"status": "processing", "current_step": "fetching_ticket", "action": "await_response", "note": "[13/06/2026 08:41:02] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:41:02.192708Z"}}}
{"ts": "2026-06-13T11:41:02.199314Z", "key": "man-fc7947cd", "payload": {"transactionId": "man-fc7947cd", "processing": {"status": "processing", "current_step": "validation", "action": "await_response", "note": "[13/06/2026 08:41:02] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:41:02.199292Z"}}}
{"ts": "2026-06-13T11:41:02.215260Z", "key": "man-fc7947cd", "payload": {"transactionId": "man-fc7947cd", "processing": {"status": "processing", "current_step": "imdb_enrichment", "action": "await_response", "note": "[13/06/2026 08:41:02] Chamado recebido, iniciando processamento.\n[13/06/2026 08:41:02] Enriquecendo chamado no IMDB....", "timestamp": "2026-06-13T11:41:02.215238Z"}}}
{"ts": "2026-06-13T11:41:04.138471Z", "key": "man-fc7947cd", "payload": {"transactionId": "man-fc7947cd", "processing": {"status": "processing", "current_step": "imdb_enrichment_failed", "action": "await_response", "note": "[13/06/2026 08:41:02] Chamado recebido, iniciando processamento.\n[13/06/2026 08:41:02] Enriquecendo chamado no IMDB....\n[13/06/2026 08:41:04] Falha no enriquecimento IMDB.", "timestamp": "2026-06-13T11:41:04.138439Z"}}}
{"ts": "2026-06-13T11:44:27.657570Z", "key": "man-f0a2d929", "payload": {"transactionId": "man-f0a2d929", "processing": {"status": "processing", "current_step": "fetching_ticket", "action": "await_response", "note": "[13/06/2026 08:44:27] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:44:27.657555Z"}}}
{"ts": "2026-06-13T11:44:27.663735Z", "key": "man-f0a2d929", "payload": {"transactionId": "man-f0a2d929", "processing": {"status": "processing", "current_step": "validation", "action": "await_response", "note": "[13/06/2026 08:44:27] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:44:27.663715Z"}}}
{"ts": "2026-06-13T11:44:27.679132Z", "key": "man-f0a2d929", "payload": {"transactionId": "man-f0a2d929", "processing": {"status": "processing", "current_step": "imdb_enrichment", "action": "await_response", "note": "[13/06/2026 08:44:27] Chamado recebido, iniciando processamento.\n[13/06/2026 08:44:27] Enriquecendo chamado no IMDB....", "timestamp": "2026-06-13T11:44:27.679114Z"}}}
{"ts": "2026-06-13T11:44:27.740484Z", "key": "man-f0a2d929", "payload": {"transactionId": "man-f0a2d929", "processing": {"status": "processing", "current_step": "imdb_enrichment_failed", "action": "await_response", "note": "[13/06/2026 08:44:27] Chamado recebido, iniciando processamento.\n[13/06/2026 08:44:27] Enriquecendo chamado no IMDB....\n[13/06/2026 08:44:27] Falha no enriquecimento IMDB.", "timestamp": "2026-06-13T11:44:27.740453Z"}}}
{"ts": "2026-06-13T11:50:35.859525Z", "key": "man-ec33447d", "payload": {"transactionId": "man-ec33447d", "processing": {"status": "processing", "current_step": "fetching_ticket", "action": "await_response", "note": "[13/06/2026 08:50:35] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:50:35.859509Z"}}}
{"ts": "2026-06-13T11:50:35.864610Z", "key": "man-ec33447d", "payload": {"transactionId": "man-ec33447d", "processing": {"status": "processing", "current_step": "validation", "action": "await_response", "note": "[13/06/2026 08:50:35] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:50:35.864586Z"}}}
{"ts": "2026-06-13T11:50:35.881261Z", "key": "man-ec33447d", "payload": {"transactionId": "man-ec33447d", "processing": {"status": "processing", "current_step": "imdb_enrichment", "action": "await_response", "note": "[13/06/2026 08:50:35] Chamado recebido, iniciando processamento.\n[13/06/2026 08:50:35] Enriquecendo chamado no IMDB....", "timestamp": "2026-06-13T11:50:35.881244Z"}}}
{"ts": "2026-06-13T11:50:35.994273Z", "key": "man-ec33447d", "payload": {"transactionId": "man-ec33447d", "processing": {"status": "processing", "current_step": "imdb_enrichment_failed", "action": "await_response", "note": "[13/06/2026 08:50:35] Chamado recebido, iniciando processamento.\n[13/06/2026 08:50:35] Enriquecendo chamado no IMDB....\n[13/06/2026 08:50:35] Falha no enriquecimento IMDB.", "timestamp": "2026-06-13T11:50:35.994243Z"}}}
{"ts": "2026-06-13T11:53:46.367591Z", "key": "man-d96fdcf2", "payload": {"transactionId": "man-d96fdcf2", "processing": {"status": "processing", "current_step": "fetching_ticket", "action": "await_response", "note": "[13/06/2026 08:53:46] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:53:46.367575Z"}}}
{"ts": "2026-06-13T11:53:46.372800Z", "key": "man-d96fdcf2", "payload": {"transactionId": "man-d96fdcf2", "processing": {"status": "processing", "current_step": "validation", "action": "await_response", "note": "[13/06/2026 08:53:46] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:53:46.372781Z"}}}
{"ts": "2026-06-13T11:53:46.388442Z", "key": "man-d96fdcf2", "payload": {"transactionId": "man-d96fdcf2", "processing": {"status": "processing", "current_step": "imdb_enrichment", "action": "await_response", "note": "[13/06/2026 08:53:46] Chamado recebido, iniciando processamento.\n[13/06/2026 08:53:46] Enriquecendo chamado no IMDB....", "timestamp": "2026-06-13T11:53:46.388424Z"}}}
{"ts": "2026-06-13T11:53:46.490280Z", "key": "man-d96fdcf2", "payload": {"transactionId": "man-d96fdcf2", "processing": {"status": "processing", "current_step": "imdb_enrichment_failed", "action": "await_response", "note": "[13/06/2026 08:53:46] Chamado recebido, iniciando processamento.\n[13/06/2026 08:53:46] Enriquecendo chamado no IMDB....\n[13/06/2026 08:53:46] Falha no enriquecimento IMDB.", "timestamp": "2026-06-13T11:53:46.490243Z"}}}
{"ts": "2026-06-13T11:55:33.542425Z", "key": "man-f1ce4dbd", "payload": {"transactionId": "man-f1ce4dbd", "processing": {"status": "processing", "current_step": "fetching_ticket", "action": "await_response", "note": "[13/06/2026 08:55:33] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:55:33.542411Z"}}}
{"ts": "2026-06-13T11:55:33.548005Z", "key": "man-f1ce4dbd", "payload": {"transactionId": "man-f1ce4dbd", "processing": {"status": "processing", "current_step": "validation", "action": "await_response", "note": "[13/06/2026 08:55:33] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:55:33.547992Z"}}}
{"ts": "2026-06-13T11:55:33.562185Z", "key": "man-f1ce4dbd", "payload": {"transactionId": "man-f1ce4dbd", "processing": {"status": "processing", "current_step": "imdb_enrichment", "action": "await_response", "note": "[13/06/2026 08:55:33] Chamado recebido, iniciando processamento.\n[13/06/2026 08:55:33] Enriquecendo chamado no IMDB....", "timestamp": "2026-06-13T11:55:33.562168Z"}}}
{"ts": "2026-06-13T11:55:33.631504Z", "key": "man-f1ce4dbd", "payload": {"transactionId": "man-f1ce4dbd", "processing": {"status": "processing", "current_step": "imdb_enrichment_failed", "action": "await_response", "note": "[13/06/2026 08:55:33] Chamado recebido, iniciando processamento.\n[13/06/2026 08:55:33] Enriquecendo chamado no IMDB....\n[13/06/2026 08:55:33] Falha no enriquecimento IMDB.", "timestamp": "2026-06-13T11:55:33.631473Z"}}}
{"ts": "2026-06-13T11:56:29.575175Z", "key": "man-7ab9f6d3", "payload": {"transactionId": "man-7ab9f6d3", "processing": {"status": "processing", "current_step": "fetching_ticket", "action": "await_response", "note": "[13/06/2026 08:56:29] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:56:29.575145Z"}}}
{"ts": "2026-06-13T11:56:29.580507Z", "key": "man-7ab9f6d3", "payload": {"transactionId": "man-7ab9f6d3", "processing": {"status": "processing", "current_step": "validation", "action": "await_response", "note": "[13/06/2026 08:56:29] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T11:56:29.580489Z"}}}
{"ts": "2026-06-13T11:56:29.595401Z", "key": "man-7ab9f6d3", "payload": {"transactionId": "man-7ab9f6d3", "processing": {"status": "processing", "current_step": "imdb_enrichment", "action": "await_response", "note": "[13/06/2026 08:56:29] Chamado recebido, iniciando processamento.\n[13/06/2026 08:56:29] Enriquecendo chamado no IMDB....", "timestamp": "2026-06-13T11:56:29.595384Z"}}}
{"ts": "2026-06-13T11:56:29.610519Z", "key": "man-7ab9f6d3", "payload": {"transactionId": "man-7ab9f6d3", "processing": {"status": "processing", "current_step": "identity_verification", "action": "await_response", "note": "[13/06/2026 08:56:29] Chamado recebido, iniciando processamento.\n[13/06/2026 08:56:29] Enriquecendo chamado no IMDB....\n[13/06/2026 08:56:29] Realizando verificação de CPF divergente....", "timestamp": "2026-06-13T11:56:29.610501Z"}}}
{"ts": "2026-06-13T11:56:29.616230Z", "key": "man-7ab9f6d3", "payload": {"transactionId": "man-7ab9f6d3", "processing": {"status": "processing", "current_step": "speech_enrichment", "action": "await_response", "note": "[13/06/2026 08:56:29] Chamado recebido, iniciando processamento.\n[13/06/2026 08:56:29] Enriquecendo chamado no IMDB....\n[13/06/2026 08:56:29] Realizando verificação de CPF divergente....\n[13/06/2026 08:56:29] Enriquecendo chamado via Speech Analytics.", "timestamp": "2026-06-13T11:56:29.616214Z"}}}
{"ts": "2026-06-13T11:56:40.080696Z", "key": "man-7ab9f6d3", "payload": {"transactionId": "man-7ab9f6d3", "processing": {"status": "processing", "current_step": "speech_enrichment_unavailable", "action": "await_response", "note": "[13/06/2026 08:56:29] Chamado recebido, iniciando processamento.\n[13/06/2026 08:56:29] Enriquecendo chamado no IMDB....\n[13/06/2026 08:56:29] Realizando verificação de CPF divergente....\n[13/06/2026 08:56:29] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 08:56:40] Enriquecimento via Speech Analytics - indisponível no momento.", "timestamp": "2026-06-13T11:56:40.080657Z"}}}
{"ts": "2026-06-13T11:56:40.087207Z", "key": "man-7ab9f6d3", "payload": {"transactionId": "man-7ab9f6d3", "processing": {"status": "processing", "current_step": "knowledge_base_enrichment", "action": "await_response", "note": "[13/06/2026 08:56:29] Chamado recebido, iniciando processamento.\n[13/06/2026 08:56:29] Enriquecendo chamado no IMDB....\n[13/06/2026 08:56:29] Realizando verificação de CPF divergente....\n[13/06/2026 08:56:29] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 08:56:40] Enriquecimento via Speech Analytics - indisponível no momento.\n[13/06/2026 08:56:40] Consultando procedimentos na base de conhecimento....", "timestamp": "2026-06-13T11:56:40.087185Z"}}}
{"ts": "2026-06-13T11:56:40.419935Z", "key": "man-7ab9f6d3", "payload": {"transactionId": "man-7ab9f6d3", "processing": {"status": "processing", "current_step": "knowledge_base_enrichment_unavailable", "action": "await_response", "note": "[13/06/2026 08:56:29] Chamado recebido, iniciando processamento.\n[13/06/2026 08:56:29] Enriquecendo chamado no IMDB....\n[13/06/2026 08:56:29] Realizando verificação de CPF divergente....\n[13/06/2026 08:56:29] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 08:56:40] Enriquecimento via Speech Analytics - indisponível no momento.\n[13/06/2026 08:56:40] Consultando procedimentos na base de conhecimento....\n[13/06/2026 08:56:40] Base de conhecimento consultada - indisponível no momento.", "timestamp": "2026-06-13T11:56:40.419905Z"}}}
{"ts": "2026-06-13T11:56:40.426948Z", "key": "man-7ab9f6d3", "payload": {"transactionId": "man-7ab9f6d3", "processing": {"status": "processing", "current_step": "canceling_analysis", "action": "await_response", "note": "[13/06/2026 08:56:29] Chamado recebido, iniciando processamento.\n[13/06/2026 08:56:29] Enriquecendo chamado no IMDB....\n[13/06/2026 08:56:29] Realizando verificação de CPF divergente....\n[13/06/2026 08:56:29] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 08:56:40] Enriquecimento via Speech Analytics - indisponível no momento.\n[13/06/2026 08:56:40] Consultando procedimentos na base de conhecimento....\n[13/06/2026 08:56:40] Base de conhecimento consultada - indisponível no momento.\n[13/06/2026 08:56:40] Realizando análise de cancelamento....", "timestamp": "2026-06-13T11:56:40.426919Z"}}}
{"ts": "2026-06-13T12:08:54.689233Z", "key": "man-4e2a87ec", "payload": {"transactionId": "man-4e2a87ec", "processing": {"status": "processing", "current_step": "fetching_ticket", "action": "await_response", "note": "[13/06/2026 09:08:54] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T12:08:54.689212Z"}}}
{"ts": "2026-06-13T12:08:54.694436Z", "key": "man-4e2a87ec", "payload": {"transactionId": "man-4e2a87ec", "processing": {"status": "processing", "current_step": "validation", "action": "await_response", "note": "[13/06/2026 09:08:54] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T12:08:54.694423Z"}}}
{"ts": "2026-06-13T12:08:54.708866Z", "key": "man-4e2a87ec", "payload": {"transactionId": "man-4e2a87ec", "processing": {"status": "processing", "current_step": "imdb_enrichment", "action": "await_response", "note": "[13/06/2026 09:08:54] Chamado recebido, iniciando processamento.\n[13/06/2026 09:08:54] Enriquecendo chamado no IMDB....", "timestamp": "2026-06-13T12:08:54.708850Z"}}}
{"ts": "2026-06-13T12:08:57.796719Z", "key": "man-4e2a87ec", "payload": {"transactionId": "man-4e2a87ec", "processing": {"status": "processing", "current_step": "identity_verification", "action": "await_response", "note": "[13/06/2026 09:08:54] Chamado recebido, iniciando processamento.\n[13/06/2026 09:08:54] Enriquecendo chamado no IMDB....\n[13/06/2026 09:08:57] Realizando verificação de CPF divergente....", "timestamp": "2026-06-13T12:08:57.796696Z"}}}
{"ts": "2026-06-13T12:08:57.801813Z", "key": "man-4e2a87ec", "payload": {"transactionId": "man-4e2a87ec", "processing": {"status": "processing", "current_step": "speech_enrichment", "action": "await_response", "note": "[13/06/2026 09:08:54] Chamado recebido, iniciando processamento.\n[13/06/2026 09:08:54] Enriquecendo chamado no IMDB....\n[13/06/2026 09:08:57] Realizando verificação de CPF divergente....\n[13/06/2026 09:08:57] Enriquecendo chamado via Speech Analytics.", "timestamp": "2026-06-13T12:08:57.801789Z"}}}
{"ts": "2026-06-13T12:08:57.843004Z", "key": "man-4e2a87ec", "payload": {"transactionId": "man-4e2a87ec", "processing": {"status": "processing", "current_step": "knowledge_base_enrichment", "action": "await_response", "note": "[13/06/2026 09:08:54] Chamado recebido, iniciando processamento.\n[13/06/2026 09:08:54] Enriquecendo chamado no IMDB....\n[13/06/2026 09:08:57] Realizando verificação de CPF divergente....\n[13/06/2026 09:08:57] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:08:57] Consultando procedimentos na base de conhecimento....", "timestamp": "2026-06-13T12:08:57.842983Z"}}}
{"ts": "2026-06-13T12:08:58.853369Z", "key": "man-4e2a87ec", "payload": {"transactionId": "man-4e2a87ec", "processing": {"status": "processing", "current_step": "canceling_analysis", "action": "await_response", "note": "[13/06/2026 09:08:54] Chamado recebido, iniciando processamento.\n[13/06/2026 09:08:54] Enriquecendo chamado no IMDB....\n[13/06/2026 09:08:57] Realizando verificação de CPF divergente....\n[13/06/2026 09:08:57] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:08:57] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:08:58] Realizando análise de cancelamento....", "timestamp": "2026-06-13T12:08:58.853349Z"}}}
{"ts": "2026-06-13T12:09:01.892018Z", "key": "man-4e2a87ec", "payload": {"transactionId": "man-4e2a87ec", "processing": {"status": "processing", "current_step": "tim_complaint_analysis", "action": "await_response", "note": "[13/06/2026 09:08:54] Chamado recebido, iniciando processamento.\n[13/06/2026 09:08:54] Enriquecendo chamado no IMDB....\n[13/06/2026 09:08:57] Realizando verificação de CPF divergente....\n[13/06/2026 09:08:57] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:08:57] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:08:58] Realizando análise de cancelamento....\n[13/06/2026 09:09:01] Realizando análise de reencaminhamento....", "timestamp": "2026-06-13T12:09:01.891993Z"}}}
{"ts": "2026-06-13T12:09:04.913265Z", "key": "man-4e2a87ec", "payload": {"transactionId": "man-4e2a87ec", "processing": {"status": "processing", "current_step": "reclassification_analysis", "action": "await_response", "note": "[13/06/2026 09:08:54] Chamado recebido, iniciando processamento.\n[13/06/2026 09:08:54] Enriquecendo chamado no IMDB....\n[13/06/2026 09:08:57] Realizando verificação de CPF divergente....\n[13/06/2026 09:08:57] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:08:57] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:08:58] Realizando análise de cancelamento....\n[13/06/2026 09:09:01] Realizando análise de reencaminhamento....\n[13/06/2026 09:09:04] Realizando análise de reclassificação do chamado....", "timestamp": "2026-06-13T12:09:04.913231Z"}}}
{"ts": "2026-06-13T12:09:04.918375Z", "key": "man-4e2a87ec", "payload": {"transactionId": "man-4e2a87ec", "processing": {"status": "processing", "current_step": "reclassification_analysis", "action": "await_response", "note": "[13/06/2026 09:08:54] Chamado recebido, iniciando processamento.\n[13/06/2026 09:08:54] Enriquecendo chamado no IMDB....\n[13/06/2026 09:08:57] Realizando verificação de CPF divergente....\n[13/06/2026 09:08:57] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:08:57] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:08:58] Realizando análise de cancelamento....\n[13/06/2026 09:09:01] Realizando análise de reencaminhamento....\n[13/06/2026 09:09:04] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:09:04] Realizando análise de reclassificação do chamado....", "timestamp": "2026-06-13T12:09:04.918353Z"}}}
{"ts": "2026-06-13T12:09:08.130779Z", "key": "man-4e2a87ec", "payload": {"transactionId": "man-4e2a87ec", "processing": {"status": "processing", "current_step": "siebel_sr_opening", "action": "await_response", "note": "[13/06/2026 09:08:54] Chamado recebido, iniciando processamento.\n[13/06/2026 09:08:54] Enriquecendo chamado no IMDB....\n[13/06/2026 09:08:57] Realizando verificação de CPF divergente....\n[13/06/2026 09:08:57] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:08:57] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:08:58] Realizando análise de cancelamento....\n[13/06/2026 09:09:01] Realizando análise de reencaminhamento....\n[13/06/2026 09:09:04] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:09:04] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:09:08] Abrindo chamado no Siebel....", "timestamp": "2026-06-13T12:09:08.130753Z"}}}
{"ts": "2026-06-13T12:13:11.097807Z", "key": "man-fc277693", "payload": {"transactionId": "man-fc277693", "processing": {"status": "processing", "current_step": "fetching_ticket", "action": "await_response", "note": "[13/06/2026 09:13:11] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T12:13:11.097787Z"}}}
{"ts": "2026-06-13T12:13:11.103424Z", "key": "man-fc277693", "payload": {"transactionId": "man-fc277693", "processing": {"status": "processing", "current_step": "validation", "action": "await_response", "note": "[13/06/2026 09:13:11] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T12:13:11.103408Z"}}}
{"ts": "2026-06-13T12:13:11.118197Z", "key": "man-fc277693", "payload": {"transactionId": "man-fc277693", "processing": {"status": "processing", "current_step": "imdb_enrichment", "action": "await_response", "note": "[13/06/2026 09:13:11] Chamado recebido, iniciando processamento.\n[13/06/2026 09:13:11] Enriquecendo chamado no IMDB....", "timestamp": "2026-06-13T12:13:11.118177Z"}}}
{"ts": "2026-06-13T12:13:11.153234Z", "key": "man-fc277693", "payload": {"transactionId": "man-fc277693", "processing": {"status": "processing", "current_step": "identity_verification", "action": "await_response", "note": "[13/06/2026 09:13:11] Chamado recebido, iniciando processamento.\n[13/06/2026 09:13:11] Enriquecendo chamado no IMDB....\n[13/06/2026 09:13:11] Realizando verificação de CPF divergente....", "timestamp": "2026-06-13T12:13:11.153215Z"}}}
{"ts": "2026-06-13T12:13:11.157888Z", "key": "man-fc277693", "payload": {"transactionId": "man-fc277693", "processing": {"status": "processing", "current_step": "speech_enrichment", "action": "await_response", "note": "[13/06/2026 09:13:11] Chamado recebido, iniciando processamento.\n[13/06/2026 09:13:11] Enriquecendo chamado no IMDB....\n[13/06/2026 09:13:11] Realizando verificação de CPF divergente....\n[13/06/2026 09:13:11] Enriquecendo chamado via Speech Analytics.", "timestamp": "2026-06-13T12:13:11.157870Z"}}}
{"ts": "2026-06-13T12:13:11.187313Z", "key": "man-fc277693", "payload": {"transactionId": "man-fc277693", "processing": {"status": "processing", "current_step": "knowledge_base_enrichment", "action": "await_response", "note": "[13/06/2026 09:13:11] Chamado recebido, iniciando processamento.\n[13/06/2026 09:13:11] Enriquecendo chamado no IMDB....\n[13/06/2026 09:13:11] Realizando verificação de CPF divergente....\n[13/06/2026 09:13:11] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:13:11] Consultando procedimentos na base de conhecimento....", "timestamp": "2026-06-13T12:13:11.187290Z"}}}
{"ts": "2026-06-13T12:13:11.202410Z", "key": "man-fc277693", "payload": {"transactionId": "man-fc277693", "processing": {"status": "processing", "current_step": "canceling_analysis", "action": "await_response", "note": "[13/06/2026 09:13:11] Chamado recebido, iniciando processamento.\n[13/06/2026 09:13:11] Enriquecendo chamado no IMDB....\n[13/06/2026 09:13:11] Realizando verificação de CPF divergente....\n[13/06/2026 09:13:11] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:13:11] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:13:11] Realizando análise de cancelamento....", "timestamp": "2026-06-13T12:13:11.202392Z"}}}
{"ts": "2026-06-13T12:13:14.804084Z", "key": "man-fc277693", "payload": {"transactionId": "man-fc277693", "processing": {"status": "processing", "current_step": "tim_complaint_analysis", "action": "await_response", "note": "[13/06/2026 09:13:11] Chamado recebido, iniciando processamento.\n[13/06/2026 09:13:11] Enriquecendo chamado no IMDB....\n[13/06/2026 09:13:11] Realizando verificação de CPF divergente....\n[13/06/2026 09:13:11] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:13:11] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:13:11] Realizando análise de cancelamento....\n[13/06/2026 09:13:14] Realizando análise de reencaminhamento....", "timestamp": "2026-06-13T12:13:14.804060Z"}}}
{"ts": "2026-06-13T12:13:17.682902Z", "key": "man-fc277693", "payload": {"transactionId": "man-fc277693", "processing": {"status": "processing", "current_step": "reclassification_analysis", "action": "await_response", "note": "[13/06/2026 09:13:11] Chamado recebido, iniciando processamento.\n[13/06/2026 09:13:11] Enriquecendo chamado no IMDB....\n[13/06/2026 09:13:11] Realizando verificação de CPF divergente....\n[13/06/2026 09:13:11] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:13:11] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:13:11] Realizando análise de cancelamento....\n[13/06/2026 09:13:14] Realizando análise de reencaminhamento....\n[13/06/2026 09:13:17] Realizando análise de reclassificação do chamado....", "timestamp": "2026-06-13T12:13:17.682881Z"}}}
{"ts": "2026-06-13T12:13:17.687706Z", "key": "man-fc277693", "payload": {"transactionId": "man-fc277693", "processing": {"status": "processing", "current_step": "reclassification_analysis", "action": "await_response", "note": "[13/06/2026 09:13:11] Chamado recebido, iniciando processamento.\n[13/06/2026 09:13:11] Enriquecendo chamado no IMDB....\n[13/06/2026 09:13:11] Realizando verificação de CPF divergente....\n[13/06/2026 09:13:11] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:13:11] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:13:11] Realizando análise de cancelamento....\n[13/06/2026 09:13:14] Realizando análise de reencaminhamento....\n[13/06/2026 09:13:17] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:13:17] Realizando análise de reclassificação do chamado....", "timestamp": "2026-06-13T12:13:17.687695Z"}}}
{"ts": "2026-06-13T12:13:21.951853Z", "key": "man-fc277693", "payload": {"transactionId": "man-fc277693", "processing": {"status": "processing", "current_step": "siebel_sr_opening", "action": "await_response", "note": "[13/06/2026 09:13:11] Chamado recebido, iniciando processamento.\n[13/06/2026 09:13:11] Enriquecendo chamado no IMDB....\n[13/06/2026 09:13:11] Realizando verificação de CPF divergente....\n[13/06/2026 09:13:11] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:13:11] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:13:11] Realizando análise de cancelamento....\n[13/06/2026 09:13:14] Realizando análise de reencaminhamento....\n[13/06/2026 09:13:17] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:13:17] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:13:21] Abrindo chamado no Siebel....", "timestamp": "2026-06-13T12:13:21.951832Z"}}}
{"ts": "2026-06-13T12:20:43.763052Z", "key": "man-2bdc878d", "payload": {"transactionId": "man-2bdc878d", "processing": {"status": "processing", "current_step": "fetching_ticket", "action": "await_response", "note": "[13/06/2026 09:20:43] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T12:20:43.763028Z"}}}
{"ts": "2026-06-13T12:20:43.769794Z", "key": "man-2bdc878d", "payload": {"transactionId": "man-2bdc878d", "processing": {"status": "processing", "current_step": "validation", "action": "await_response", "note": "[13/06/2026 09:20:43] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T12:20:43.769769Z"}}}
{"ts": "2026-06-13T12:20:43.786827Z", "key": "man-2bdc878d", "payload": {"transactionId": "man-2bdc878d", "processing": {"status": "processing", "current_step": "imdb_enrichment", "action": "await_response", "note": "[13/06/2026 09:20:43] Chamado recebido, iniciando processamento.\n[13/06/2026 09:20:43] Enriquecendo chamado no IMDB....", "timestamp": "2026-06-13T12:20:43.786804Z"}}}
{"ts": "2026-06-13T12:20:43.823957Z", "key": "man-2bdc878d", "payload": {"transactionId": "man-2bdc878d", "processing": {"status": "processing", "current_step": "identity_verification", "action": "await_response", "note": "[13/06/2026 09:20:43] Chamado recebido, iniciando processamento.\n[13/06/2026 09:20:43] Enriquecendo chamado no IMDB....\n[13/06/2026 09:20:43] Realizando verificação de CPF divergente....", "timestamp": "2026-06-13T12:20:43.823930Z"}}}
{"ts": "2026-06-13T12:20:43.831082Z", "key": "man-2bdc878d", "payload": {"transactionId": "man-2bdc878d", "processing": {"status": "processing", "current_step": "speech_enrichment", "action": "await_response", "note": "[13/06/2026 09:20:43] Chamado recebido, iniciando processamento.\n[13/06/2026 09:20:43] Enriquecendo chamado no IMDB....\n[13/06/2026 09:20:43] Realizando verificação de CPF divergente....\n[13/06/2026 09:20:43] Enriquecendo chamado via Speech Analytics.", "timestamp": "2026-06-13T12:20:43.831058Z"}}}
{"ts": "2026-06-13T12:20:43.858859Z", "key": "man-2bdc878d", "payload": {"transactionId": "man-2bdc878d", "processing": {"status": "processing", "current_step": "knowledge_base_enrichment", "action": "await_response", "note": "[13/06/2026 09:20:43] Chamado recebido, iniciando processamento.\n[13/06/2026 09:20:43] Enriquecendo chamado no IMDB....\n[13/06/2026 09:20:43] Realizando verificação de CPF divergente....\n[13/06/2026 09:20:43] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:20:43] Consultando procedimentos na base de conhecimento....", "timestamp": "2026-06-13T12:20:43.858837Z"}}}
{"ts": "2026-06-13T12:20:43.875902Z", "key": "man-2bdc878d", "payload": {"transactionId": "man-2bdc878d", "processing": {"status": "processing", "current_step": "canceling_analysis", "action": "await_response", "note": "[13/06/2026 09:20:43] Chamado recebido, iniciando processamento.\n[13/06/2026 09:20:43] Enriquecendo chamado no IMDB....\n[13/06/2026 09:20:43] Realizando verificação de CPF divergente....\n[13/06/2026 09:20:43] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:20:43] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:20:43] Realizando análise de cancelamento....", "timestamp": "2026-06-13T12:20:43.875883Z"}}}
{"ts": "2026-06-13T12:20:46.284964Z", "key": "man-2bdc878d", "payload": {"transactionId": "man-2bdc878d", "processing": {"status": "processing", "current_step": "tim_complaint_analysis", "action": "await_response", "note": "[13/06/2026 09:20:43] Chamado recebido, iniciando processamento.\n[13/06/2026 09:20:43] Enriquecendo chamado no IMDB....\n[13/06/2026 09:20:43] Realizando verificação de CPF divergente....\n[13/06/2026 09:20:43] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:20:43] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:20:43] Realizando análise de cancelamento....\n[13/06/2026 09:20:46] Realizando análise de reencaminhamento....", "timestamp": "2026-06-13T12:20:46.284944Z"}}}
{"ts": "2026-06-13T12:20:49.551574Z", "key": "man-2bdc878d", "payload": {"transactionId": "man-2bdc878d", "processing": {"status": "processing", "current_step": "reclassification_analysis", "action": "await_response", "note": "[13/06/2026 09:20:43] Chamado recebido, iniciando processamento.\n[13/06/2026 09:20:43] Enriquecendo chamado no IMDB....\n[13/06/2026 09:20:43] Realizando verificação de CPF divergente....\n[13/06/2026 09:20:43] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:20:43] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:20:43] Realizando análise de cancelamento....\n[13/06/2026 09:20:46] Realizando análise de reencaminhamento....\n[13/06/2026 09:20:49] Realizando análise de reclassificação do chamado....", "timestamp": "2026-06-13T12:20:49.551546Z"}}}
{"ts": "2026-06-13T12:20:49.556486Z", "key": "man-2bdc878d", "payload": {"transactionId": "man-2bdc878d", "processing": {"status": "processing", "current_step": "reclassification_analysis", "action": "await_response", "note": "[13/06/2026 09:20:43] Chamado recebido, iniciando processamento.\n[13/06/2026 09:20:43] Enriquecendo chamado no IMDB....\n[13/06/2026 09:20:43] Realizando verificação de CPF divergente....\n[13/06/2026 09:20:43] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:20:43] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:20:43] Realizando análise de cancelamento....\n[13/06/2026 09:20:46] Realizando análise de reencaminhamento....\n[13/06/2026 09:20:49] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:20:49] Realizando análise de reclassificação do chamado....", "timestamp": "2026-06-13T12:20:49.556471Z"}}}
{"ts": "2026-06-13T12:20:52.715592Z", "key": "man-2bdc878d", "payload": {"transactionId": "man-2bdc878d", "processing": {"status": "processing", "current_step": "siebel_sr_opening", "action": "await_response", "note": "[13/06/2026 09:20:43] Chamado recebido, iniciando processamento.\n[13/06/2026 09:20:43] Enriquecendo chamado no IMDB....\n[13/06/2026 09:20:43] Realizando verificação de CPF divergente....\n[13/06/2026 09:20:43] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:20:43] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:20:43] Realizando análise de cancelamento....\n[13/06/2026 09:20:46] Realizando análise de reencaminhamento....\n[13/06/2026 09:20:49] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:20:49] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:20:52] Abrindo chamado no Siebel....", "timestamp": "2026-06-13T12:20:52.715571Z"}}}
{"ts": "2026-06-13T12:25:24.860002Z", "key": "man-a2834a06", "payload": {"transactionId": "man-a2834a06", "processing": {"status": "processing", "current_step": "fetching_ticket", "action": "await_response", "note": "[13/06/2026 09:25:24] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T12:25:24.859983Z"}}}
{"ts": "2026-06-13T12:25:24.866078Z", "key": "man-a2834a06", "payload": {"transactionId": "man-a2834a06", "processing": {"status": "processing", "current_step": "validation", "action": "await_response", "note": "[13/06/2026 09:25:24] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T12:25:24.866061Z"}}}
{"ts": "2026-06-13T12:25:24.884790Z", "key": "man-a2834a06", "payload": {"transactionId": "man-a2834a06", "processing": {"status": "processing", "current_step": "imdb_enrichment", "action": "await_response", "note": "[13/06/2026 09:25:24] Chamado recebido, iniciando processamento.\n[13/06/2026 09:25:24] Enriquecendo chamado no IMDB....", "timestamp": "2026-06-13T12:25:24.884763Z"}}}
{"ts": "2026-06-13T12:25:25.756583Z", "key": "man-a2834a06", "payload": {"transactionId": "man-a2834a06", "processing": {"status": "processing", "current_step": "identity_verification", "action": "await_response", "note": "[13/06/2026 09:25:24] Chamado recebido, iniciando processamento.\n[13/06/2026 09:25:24] Enriquecendo chamado no IMDB....\n[13/06/2026 09:25:25] Realizando verificação de CPF divergente....", "timestamp": "2026-06-13T12:25:25.756562Z"}}}
{"ts": "2026-06-13T12:25:25.761120Z", "key": "man-a2834a06", "payload": {"transactionId": "man-a2834a06", "processing": {"status": "processing", "current_step": "speech_enrichment", "action": "await_response", "note": "[13/06/2026 09:25:24] Chamado recebido, iniciando processamento.\n[13/06/2026 09:25:24] Enriquecendo chamado no IMDB....\n[13/06/2026 09:25:25] Realizando verificação de CPF divergente....\n[13/06/2026 09:25:25] Enriquecendo chamado via Speech Analytics.", "timestamp": "2026-06-13T12:25:25.761103Z"}}}
{"ts": "2026-06-13T12:25:25.784248Z", "key": "man-a2834a06", "payload": {"transactionId": "man-a2834a06", "processing": {"status": "processing", "current_step": "knowledge_base_enrichment", "action": "await_response", "note": "[13/06/2026 09:25:24] Chamado recebido, iniciando processamento.\n[13/06/2026 09:25:24] Enriquecendo chamado no IMDB....\n[13/06/2026 09:25:25] Realizando verificação de CPF divergente....\n[13/06/2026 09:25:25] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:25:25] Consultando procedimentos na base de conhecimento....", "timestamp": "2026-06-13T12:25:25.784224Z"}}}
{"ts": "2026-06-13T12:25:26.120273Z", "key": "man-a2834a06", "payload": {"transactionId": "man-a2834a06", "processing": {"status": "processing", "current_step": "canceling_analysis", "action": "await_response", "note": "[13/06/2026 09:25:24] Chamado recebido, iniciando processamento.\n[13/06/2026 09:25:24] Enriquecendo chamado no IMDB....\n[13/06/2026 09:25:25] Realizando verificação de CPF divergente....\n[13/06/2026 09:25:25] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:25:25] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:25:26] Realizando análise de cancelamento....", "timestamp": "2026-06-13T12:25:26.120253Z"}}}
{"ts": "2026-06-13T12:25:29.759053Z", "key": "man-a2834a06", "payload": {"transactionId": "man-a2834a06", "processing": {"status": "processing", "current_step": "tim_complaint_analysis", "action": "await_response", "note": "[13/06/2026 09:25:24] Chamado recebido, iniciando processamento.\n[13/06/2026 09:25:24] Enriquecendo chamado no IMDB....\n[13/06/2026 09:25:25] Realizando verificação de CPF divergente....\n[13/06/2026 09:25:25] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:25:25] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:25:26] Realizando análise de cancelamento....\n[13/06/2026 09:25:29] Realizando análise de reencaminhamento....", "timestamp": "2026-06-13T12:25:29.759028Z"}}}
{"ts": "2026-06-13T12:25:33.162287Z", "key": "man-a2834a06", "payload": {"transactionId": "man-a2834a06", "processing": {"status": "processing", "current_step": "reclassification_analysis", "action": "await_response", "note": "[13/06/2026 09:25:24] Chamado recebido, iniciando processamento.\n[13/06/2026 09:25:24] Enriquecendo chamado no IMDB....\n[13/06/2026 09:25:25] Realizando verificação de CPF divergente....\n[13/06/2026 09:25:25] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:25:25] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:25:26] Realizando análise de cancelamento....\n[13/06/2026 09:25:29] Realizando análise de reencaminhamento....\n[13/06/2026 09:25:33] Realizando análise de reclassificação do chamado....", "timestamp": "2026-06-13T12:25:33.162261Z"}}}
{"ts": "2026-06-13T12:25:33.168352Z", "key": "man-a2834a06", "payload": {"transactionId": "man-a2834a06", "processing": {"status": "processing", "current_step": "reclassification_analysis", "action": "await_response", "note": "[13/06/2026 09:25:24] Chamado recebido, iniciando processamento.\n[13/06/2026 09:25:24] Enriquecendo chamado no IMDB....\n[13/06/2026 09:25:25] Realizando verificação de CPF divergente....\n[13/06/2026 09:25:25] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:25:25] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:25:26] Realizando análise de cancelamento....\n[13/06/2026 09:25:29] Realizando análise de reencaminhamento....\n[13/06/2026 09:25:33] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:25:33] Realizando análise de reclassificação do chamado....", "timestamp": "2026-06-13T12:25:33.168322Z"}}}
{"ts": "2026-06-13T12:25:36.521378Z", "key": "man-a2834a06", "payload": {"transactionId": "man-a2834a06", "processing": {"status": "processing", "current_step": "siebel_sr_opening", "action": "await_response", "note": "[13/06/2026 09:25:24] Chamado recebido, iniciando processamento.\n[13/06/2026 09:25:24] Enriquecendo chamado no IMDB....\n[13/06/2026 09:25:25] Realizando verificação de CPF divergente....\n[13/06/2026 09:25:25] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:25:25] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:25:26] Realizando análise de cancelamento....\n[13/06/2026 09:25:29] Realizando análise de reencaminhamento....\n[13/06/2026 09:25:33] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:25:33] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:25:36] Abrindo chamado no Siebel....", "timestamp": "2026-06-13T12:25:36.521356Z"}}}
{"ts": "2026-06-13T12:40:35.443891Z", "key": "man-23f416a8", "payload": {"transactionId": "man-23f416a8", "processing": {"status": "processing", "current_step": "fetching_ticket", "action": "await_response", "note": "[13/06/2026 09:40:35] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T12:40:35.443864Z"}}}
{"ts": "2026-06-13T12:40:35.450268Z", "key": "man-23f416a8", "payload": {"transactionId": "man-23f416a8", "processing": {"status": "processing", "current_step": "validation", "action": "await_response", "note": "[13/06/2026 09:40:35] Chamado recebido, iniciando processamento.", "timestamp": "2026-06-13T12:40:35.450249Z"}}}
{"ts": "2026-06-13T12:40:35.466267Z", "key": "man-23f416a8", "payload": {"transactionId": "man-23f416a8", "processing": {"status": "processing", "current_step": "imdb_enrichment", "action": "await_response", "note": "[13/06/2026 09:40:35] Chamado recebido, iniciando processamento.\n[13/06/2026 09:40:35] Enriquecendo chamado no IMDB....", "timestamp": "2026-06-13T12:40:35.466250Z"}}}
{"ts": "2026-06-13T12:40:36.228740Z", "key": "man-23f416a8", "payload": {"transactionId": "man-23f416a8", "processing": {"status": "processing", "current_step": "identity_verification", "action": "await_response", "note": "[13/06/2026 09:40:35] Chamado recebido, iniciando processamento.\n[13/06/2026 09:40:35] Enriquecendo chamado no IMDB....\n[13/06/2026 09:40:36] Realizando verificação de CPF divergente....", "timestamp": "2026-06-13T12:40:36.228714Z"}}}
{"ts": "2026-06-13T12:40:36.239433Z", "key": "man-23f416a8", "payload": {"transactionId": "man-23f416a8", "processing": {"status": "processing", "current_step": "speech_enrichment", "action": "await_response", "note": "[13/06/2026 09:40:35] Chamado recebido, iniciando processamento.\n[13/06/2026 09:40:35] Enriquecendo chamado no IMDB....\n[13/06/2026 09:40:36] Realizando verificação de CPF divergente....\n[13/06/2026 09:40:36] Enriquecendo chamado via Speech Analytics.", "timestamp": "2026-06-13T12:40:36.238738Z"}}}
{"ts": "2026-06-13T12:40:36.265009Z", "key": "man-23f416a8", "payload": {"transactionId": "man-23f416a8", "processing": {"status": "processing", "current_step": "knowledge_base_enrichment", "action": "await_response", "note": "[13/06/2026 09:40:35] Chamado recebido, iniciando processamento.\n[13/06/2026 09:40:35] Enriquecendo chamado no IMDB....\n[13/06/2026 09:40:36] Realizando verificação de CPF divergente....\n[13/06/2026 09:40:36] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:40:36] Consultando procedimentos na base de conhecimento....", "timestamp": "2026-06-13T12:40:36.264990Z"}}}
{"ts": "2026-06-13T12:40:36.535711Z", "key": "man-23f416a8", "payload": {"transactionId": "man-23f416a8", "processing": {"status": "processing", "current_step": "canceling_analysis", "action": "await_response", "note": "[13/06/2026 09:40:35] Chamado recebido, iniciando processamento.\n[13/06/2026 09:40:35] Enriquecendo chamado no IMDB....\n[13/06/2026 09:40:36] Realizando verificação de CPF divergente....\n[13/06/2026 09:40:36] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:40:36] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:40:36] Realizando análise de cancelamento....", "timestamp": "2026-06-13T12:40:36.535691Z"}}}
{"ts": "2026-06-13T12:40:39.572330Z", "key": "man-23f416a8", "payload": {"transactionId": "man-23f416a8", "processing": {"status": "processing", "current_step": "tim_complaint_analysis", "action": "await_response", "note": "[13/06/2026 09:40:35] Chamado recebido, iniciando processamento.\n[13/06/2026 09:40:35] Enriquecendo chamado no IMDB....\n[13/06/2026 09:40:36] Realizando verificação de CPF divergente....\n[13/06/2026 09:40:36] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:40:36] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:40:36] Realizando análise de cancelamento....\n[13/06/2026 09:40:39] Realizando análise de reencaminhamento....", "timestamp": "2026-06-13T12:40:39.572311Z"}}}
{"ts": "2026-06-13T12:40:43.190711Z", "key": "man-23f416a8", "payload": {"transactionId": "man-23f416a8", "processing": {"status": "processing", "current_step": "reclassification_analysis", "action": "await_response", "note": "[13/06/2026 09:40:35] Chamado recebido, iniciando processamento.\n[13/06/2026 09:40:35] Enriquecendo chamado no IMDB....\n[13/06/2026 09:40:36] Realizando verificação de CPF divergente....\n[13/06/2026 09:40:36] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:40:36] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:40:36] Realizando análise de cancelamento....\n[13/06/2026 09:40:39] Realizando análise de reencaminhamento....\n[13/06/2026 09:40:43] Realizando análise de reclassificação do chamado....", "timestamp": "2026-06-13T12:40:43.190690Z"}}}
{"ts": "2026-06-13T12:40:43.201384Z", "key": "man-23f416a8", "payload": {"transactionId": "man-23f416a8", "processing": {"status": "processing", "current_step": "reclassification_analysis", "action": "await_response", "note": "[13/06/2026 09:40:35] Chamado recebido, iniciando processamento.\n[13/06/2026 09:40:35] Enriquecendo chamado no IMDB....\n[13/06/2026 09:40:36] Realizando verificação de CPF divergente....\n[13/06/2026 09:40:36] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:40:36] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:40:36] Realizando análise de cancelamento....\n[13/06/2026 09:40:39] Realizando análise de reencaminhamento....\n[13/06/2026 09:40:43] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:40:43] Realizando análise de reclassificação do chamado....", "timestamp": "2026-06-13T12:40:43.201371Z"}}}
{"ts": "2026-06-13T12:40:47.059892Z", "key": "man-23f416a8", "payload": {"transactionId": "man-23f416a8", "processing": {"status": "processing", "current_step": "siebel_sr_opening", "action": "await_response", "note": "[13/06/2026 09:40:35] Chamado recebido, iniciando processamento.\n[13/06/2026 09:40:35] Enriquecendo chamado no IMDB....\n[13/06/2026 09:40:36] Realizando verificação de CPF divergente....\n[13/06/2026 09:40:36] Enriquecendo chamado via Speech Analytics.\n[13/06/2026 09:40:36] Consultando procedimentos na base de conhecimento....\n[13/06/2026 09:40:36] Realizando análise de cancelamento....\n[13/06/2026 09:40:39] Realizando análise de reencaminhamento....\n[13/06/2026 09:40:43] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:40:43] Realizando análise de reclassificação do chamado....\n[13/06/2026 09:40:47] Abrindo chamado no Siebel....", "timestamp": "2026-06-13T12:40:47.059872Z"}}}

View File

@@ -0,0 +1,62 @@
# /compass_backoffice/legacy_backend/
# uvicorn mock_imdb_server:app --port 8011
from fastapi import FastAPI, Request
app = FastAPI(title="Mock IMDB TIM")
@app.get("/access/v1/info")
async def get_access_info(request: Request):
return {
"cpfCnpj": request.query_params.get("cpfCnpj") or "06252533106",
"msisdn": request.query_params.get("msisdn") or "62981152324",
"plan": {
"Type": "POS_PAGO",
"name": "TIM Black Família 50GB"
},
"customer": {
"segment": "consumer",
"status": "active",
"contumazCustomer": False,
"odcCustomer": False
},
"contracts": [
{
"contractId": "mock-contract-001",
"status": "active",
"product": "Celular Pós-pago"
}
]
}
@app.get("/access/v1/info/{msisdn}")
async def get_access_info_by_msisdn(msisdn: str):
return {
"msisdn": msisdn,
"cpfCnpj": "06252533106",
"plan": {
"Type": "POS_PAGO",
"name": "TIM Black Família 50GB"
},
"customer": {
"segment": "consumer",
"status": "active",
"contumazCustomer": False,
"odcCustomer": False
}
}
@app.post("/customers/v1/backOfficeSRopening")
async def backoffice_sr_opening(payload: dict):
return {
"status": "success",
"crmProtocol": "SR-MOCK-123456",
"fieldsToUpdate": {
"status": "Aberto",
"reason": "Mock Siebel SR Opening"
},
"case_response": {
"message": "Chamado Siebel aberto com sucesso no mock"
},
"transitions": []
}

View File

@@ -35,9 +35,9 @@ class BackofficeMCPSettings(BaseSettings):
port: int = Field(default=8010, validation_alias=AliasChoices("BACKOFFICE_MCP_PORT", "MCP_PORT")) port: int = Field(default=8010, validation_alias=AliasChoices("BACKOFFICE_MCP_PORT", "MCP_PORT"))
log_level: str = Field(default="info", validation_alias=AliasChoices("BACKOFFICE_MCP_LOG_LEVEL", "LOG_LEVEL")) log_level: str = Field(default="info", validation_alias=AliasChoices("BACKOFFICE_MCP_LOG_LEVEL", "LOG_LEVEL"))
use_mock: bool = Field(default=False, validation_alias=AliasChoices("BACKOFFICE_MCP_USE_MOCK", "MCP_USE_MOCK", "USE_MOCK")) use_mock: bool = Field(default=True, validation_alias=AliasChoices("BACKOFFICE_MCP_USE_MOCK", "MCP_USE_MOCK", "USE_MOCK"))
backend_type: Literal["mock", "tim_clients", "rest", "oracle"] = Field( backend_type: Literal["mock", "tim_clients", "rest", "oracle"] = Field(
default="tim_clients", default="mock",
validation_alias=AliasChoices("BACKOFFICE_MCP_BACKEND_TYPE", "MCP_BACKEND_TYPE"), validation_alias=AliasChoices("BACKOFFICE_MCP_BACKEND_TYPE", "MCP_BACKEND_TYPE"),
) )

View File

@@ -0,0 +1,47 @@
from __future__ import annotations
from fastapi import FastAPI, Request
app = FastAPI(title="Mock IMDB TIM", version="1.0.0")
def _payload(msisdn: str | None = None, cpf_cnpj: str | None = None):
return {
"msisdn": msisdn or "62981152324",
"cpfCnpj": cpf_cnpj or "06252533106",
"socialSecNo": cpf_cnpj or "06252533106",
"plan": {
"Type": "POS_PAGO",
"name": "TIM Black Família 50GB",
"commercialName": "TIM Black Família 50GB",
},
"statusType": "ACTIVE",
"statusDescription": "Cliente ativo",
"customer": {
"segment": "consumer",
"status": "active",
"contumazCustomer": False,
"odcCustomer": False,
},
}
@app.get("/health")
async def health():
return {"status": "ok", "service": "mock_imdb"}
@app.get("/access/v1/info")
async def get_access_info(request: Request):
return _payload(
msisdn=request.query_params.get("msisdn"),
cpf_cnpj=request.query_params.get("cpfCnpj") or request.query_params.get("cpf_cnpj"),
)
@app.get("/access/v1/info/{msisdn}")
async def get_access_info_by_msisdn(msisdn: str, request: Request):
return _payload(
msisdn=msisdn,
cpf_cnpj=request.query_params.get("cpfCnpj") or request.query_params.get("cpf_cnpj"),
)

View File

@@ -2,13 +2,12 @@ import logging
from src.agent.state.agent_state import AgentState, set_error, update_state_metadata from src.agent.state.agent_state import AgentState, set_error, update_state_metadata
from src.agent.state.steps import GraphStep from src.agent.state.steps import GraphStep
from src.agent.state.step_helpers import set_current_step from src.agent.state.step_helpers import set_current_step
from src.components.clients.imdb_client import ImdbClient
from src.api.schemas.imdb_schemas import ImdbRequest from src.api.schemas.imdb_schemas import ImdbRequest
from src.core.config import settings from src.core.config import settings
from src.components.clients.exceptions.imdb_exceptions import ImdbClientError
from src.utils.observer import trace_node from src.utils.observer import trace_node
from src.utils.ics_collector import build_ic_payload from src.utils.ics_collector import build_ic_payload
from src.compat.framework_observer import event from src.compat.framework_observer import event
from src.compat.framework_services import call_mcp_tool
import json import json
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -18,8 +17,6 @@ async def imdb_enrich_ticket(state: AgentState) -> AgentState:
""" """
Fetches access data in the IMDB API to enrich call data. Fetches access data in the IMDB API to enrich call data.
""" """
client = ImdbClient()
await set_current_step(state, GraphStep.IMDB_ENRICHMENT) await set_current_step(state, GraphStep.IMDB_ENRICHMENT)
session_id = state.get("session_id", "") session_id = state.get("session_id", "")
@@ -74,11 +71,72 @@ async def imdb_enrich_ticket(state: AgentState) -> AgentState:
await set_current_step(state, GraphStep.IMDB_ENRICHMENT_FAILED) await set_current_step(state, GraphStep.IMDB_ENRICHMENT_FAILED)
return set_error(state, "ValidationError", "Missing required fields to fetch access data in the PMID API", step=GraphStep.IMDB_ENRICHMENT_FAILED) return set_error(state, "ValidationError", "Missing required fields to fetch access data in the PMID API", step=GraphStep.IMDB_ENRICHMENT_FAILED)
# Validate parameters via DTO before forwarding to the client # Framework-native path: use MCP tool instead of the legacy direct IMDB client.
logger.info("Fetching IMDB through framework MCP tool consultar_imdb_cliente")
mcp_response = await call_mcp_tool(
"consultar_imdb_cliente",
{
"customer_key": msisdn,
"contract_key": cpf_cnpj,
"session_key": session_id,
"document": cpf_cnpj,
},
business_context={"customer_key": msisdn, "contract_key": cpf_cnpj, "session_key": session_id},
original_context=context,
)
mcp_ok = bool(mcp_response.get("ok"))
mcp_result = mcp_response.get("result") if isinstance(mcp_response.get("result"), dict) else mcp_response
if mcp_ok or mcp_result.get("source", "").startswith("mock_"):
data = mcp_result.get("data") or mcp_result
context["imdb_access_data"] = {
"status_code": 200 if mcp_result.get("found", True) else 204,
"cpf_cnpj": cpf_cnpj,
"source": mcp_result.get("source", "framework_mcp"),
"mcp_tool": "consultar_imdb_cliente",
"plan": data.get("plan") or data.get("plano") or {"Type": "POS_PAGO", "name": "Mock Pós-pago"},
"statusType": data.get("statusType") or data.get("status_type") or "ACTIVE",
"statusDescription": data.get("statusDescription") or data.get("status_description") or "Cliente ativo",
"socialSecNo": data.get("socialSecNo") or data.get("cpfCnpj") or cpf_cnpj,
"raw": data,
}
event("AGA.011", {
"status": "Resultado IMDB: sucesso via MCP/framework",
"type": "INFO",
"session_id": session_id,
"tag": "AGA.011",
"call_id": session_id,
"origin": "MCP",
**build_ic_payload(state, "AGA.011", {"apiUrl": "mcp://consultar_imdb_cliente", "apiStatusCode": "200", "apiResponsePayload": json.dumps(context["imdb_access_data"], ensure_ascii=False), "latencyMs": "N/A"}),
}, metadata={"noc": True})
state = update_state_metadata(state, request_context=context)
await set_current_step(state, GraphStep.IMDB_ENRICHMENT_COMPLETED)
return state
# Legacy direct client is disabled by default. Enable only for controlled parity tests.
if not getattr(settings, "BACKOFFICE_ALLOW_LEGACY_CLIENTS", False):
logger.warning("IMDB MCP unavailable; continuing with controlled mock fallback. error=%s", mcp_response.get("error"))
context["imdb_access_data"] = {
"status_code": 200,
"cpf_cnpj": cpf_cnpj,
"source": "framework_local_fallback",
"mcp_tool": "consultar_imdb_cliente",
"plan": {"Type": "POS_PAGO", "name": "Mock Pós-pago"},
"statusType": "ACTIVE",
"statusDescription": "Cliente ativo - fallback local",
"socialSecNo": cpf_cnpj,
}
state = update_state_metadata(state, request_context=context)
await set_current_step(state, GraphStep.IMDB_ENRICHMENT_COMPLETED)
return state
# Legacy parity path. Avoid in framework-native local execution.
from src.components.clients.imdb_client import ImdbClient
from src.components.clients.exceptions.imdb_exceptions import ImdbClientError
client = ImdbClient()
imdb_request = ImdbRequest(msisdn=msisdn, client_id=client_id) imdb_request = ImdbRequest(msisdn=msisdn, client_id=client_id)
logger.info( logger.info(
"Fetching IMDB API") "Fetching legacy IMDB API")
try: try:
response, http_meta = await client.get_imdb_access_data_with_retry( response, http_meta = await client.get_imdb_access_data_with_retry(
msisdn=imdb_request.msisdn, msisdn=imdb_request.msisdn,

View File

@@ -3,12 +3,11 @@ import logging
from src.agent.state.agent_state import AgentState, update_state_metadata from src.agent.state.agent_state import AgentState, update_state_metadata
from src.agent.state.steps import GraphStep from src.agent.state.steps import GraphStep
from src.agent.state.step_helpers import set_current_step from src.agent.state.step_helpers import set_current_step
from src.components.clients.tais_kb_client import TaisKbClient, Product
from src.components.clients.exceptions.tais_kb_exceptions import TaisKbClientError
from src.core.config import settings from src.core.config import settings
from src.utils.observer import trace_node from src.utils.observer import trace_node
from src.utils.ics_collector import build_ic_payload, build_rag_telemetry_fields, build_noc_db_metadata from src.utils.ics_collector import build_ic_payload, build_rag_telemetry_fields, build_noc_db_metadata
from src.compat.framework_observer import event from src.compat.framework_observer import event
from src.compat.framework_services import call_mcp_tool
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -217,6 +216,62 @@ async def enrich_with_knowledge_base(state: AgentState) -> AgentState:
state = update_state_metadata(state, request_context=context) state = update_state_metadata(state, request_context=context)
return state return state
# Framework-native path: query KB via MCP/RAG abstraction instead of direct TAIS client.
mcp_response = await call_mcp_tool(
"consultar_tais_kb",
{
"query": query_text,
"protocol_id": (context.get("complaint") or {}).get("complaintProtocol"),
"customer_key": (context.get("customer") or {}).get("cpfCnpj") or (context.get("customer") or {}).get("msisdn"),
},
business_context={
"customer_key": (context.get("customer") or {}).get("cpfCnpj") or (context.get("customer") or {}).get("msisdn"),
"interaction_key": (context.get("complaint") or {}).get("complaintProtocol"),
},
original_context=context,
)
mcp_result = mcp_response.get("result") if isinstance(mcp_response.get("result"), dict) else mcp_response
if mcp_response.get("ok") or str(mcp_result.get("source", "")).startswith("mock_"):
raw_docs = mcp_result.get("documents") or ((mcp_result.get("result") or {}).get("documents") if isinstance(mcp_result.get("result"), dict) else []) or []
documents = [
{
"documentId": d.get("documentId") or d.get("id") or d.get("id_proc"),
"title": d.get("title") or d.get("title_proc"),
"chunk": d.get("chunk") or d.get("content") or d.get("text"),
"distance": d.get("distance") if d.get("distance") is not None else d.get("score"),
}
for d in raw_docs
]
relevant_documents = {"query": query_text, "documents": documents}
message = mcp_result.get("message") or mcp_result.get("summary")
if message:
relevant_documents["message"] = message
elif not documents:
relevant_documents = _not_found_payload(query_text)
context = dict(context)
context["relevant_documents"] = relevant_documents
state = update_state_metadata(state, request_context=context)
event("AGA.012", {
"status": f"Resultado da Base de Conhecimento: via MCP/framework — {len(documents)} documento(s)",
"type": "SUCCESS" if documents else "INFO",
"session_id": session_id,
"tag": "AGA.012",
"call_id": session_id,
"origin": "MCP",
**build_ic_payload(state, "AGA.012", build_rag_telemetry_fields(retrieved=documents, selected=documents)),
}, metadata={"noc": True})
return state
if not getattr(settings, "BACKOFFICE_ALLOW_LEGACY_CLIENTS", False):
context = dict(context)
context["relevant_documents"] = _unavailable_payload(query_text, error=mcp_response.get("error", "MCP unavailable"))
state = update_state_metadata(state, request_context=context)
await set_current_step(state, GraphStep.KNOWLEDGE_BASE_ENRICHMENT_UNAVAILABLE)
return state
from src.components.clients.tais_kb_client import TaisKbClient, Product
from src.components.clients.exceptions.tais_kb_exceptions import TaisKbClientError
try: try:
event("AGA.020", { event("AGA.020", {
"status": "Registro da busca do template: iniciando busca na Base de Conhecimento", "status": "Registro da busca do template: iniciando busca na Base de Conhecimento",

View File

@@ -144,9 +144,16 @@ async def open_siebel_sr(state: AgentState) -> AgentState:
motivo_extra = build_external_response(context, classification) motivo_extra = build_external_response(context, classification)
opened_at = context.get("complaint", {}).get("openedAt")
if hasattr(opened_at, "isoformat"):
opened_at_value = opened_at.isoformat()
else:
opened_at_value = opened_at
notas = SiebelSRRequest.build_notes( notas = SiebelSRRequest.build_notes(
type = context.get("siebel_action", "tratamento"), type = context.get("siebel_action", "tratamento"),
data = context.get("complaint", {}).get("openedAt").isoformat() if context.get("complaint", {}).get("openedAt") else None, data = opened_at_value,
protocolo_anatel = context.get("complaint", {}).get("complaintProtocol"), protocolo_anatel = context.get("complaint", {}).get("complaintProtocol"),
acao = context.get("complaint", {}).get("actionType"), acao = context.get("complaint", {}).get("actionType"),
cpf_cliente = cpf, cpf_cliente = cpf,

View File

@@ -1,11 +1,10 @@
import time import time
import logging import logging
import json import json
import re
from src.agent.state.agent_state import AgentState, update_state_metadata from src.agent.state.agent_state import AgentState, update_state_metadata
from src.agent.state.steps import GraphStep from src.agent.state.steps import GraphStep
from src.agent.state.step_helpers import set_current_step from src.agent.state.step_helpers import set_current_step
from src.components.clients.speech_analytics_client import SpeechAnalyticsClient
from src.components.clients.exceptions.speech_exceptions import SpeechClientError
from src.utils.observer import trace_node, score_current_trace from src.utils.observer import trace_node, score_current_trace
from src.utils.ics_collector import build_ic_payload, build_noc_api_metadata, build_noc_llm_metadata from src.utils.ics_collector import build_ic_payload, build_noc_api_metadata, build_noc_llm_metadata
from src.compat.framework_observer import event from src.compat.framework_observer import event
@@ -13,6 +12,7 @@ from src.providers.llm_provider import classification_llm, chat_llm_with_usage,
from src.core.config import settings from src.core.config import settings
from src.core.prompt_manager import get_prompt from src.core.prompt_manager import get_prompt
from src.agent.local_prompts.speech_history_analysis import speech_history_similarity_pt from src.agent.local_prompts.speech_history_analysis import speech_history_similarity_pt
from src.compat.framework_services import call_mcp_tool
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -42,9 +42,7 @@ _NULL_SPEECH_DATA = {
def _map_prediction_response(response: dict) -> dict: def _map_prediction_response(response: dict) -> dict:
""" """Map the raw Prediction API response to the internal speech_analytics schema."""
Map the raw Prediction API response to the internal speech_analytics schema.
"""
variables = response.get("variables", {}) variables = response.get("variables", {})
return { return {
"reclamacao_resumo": response.get("resume") or variables.get("resume"), "reclamacao_resumo": response.get("resume") or variables.get("resume"),
@@ -57,18 +55,8 @@ def _map_prediction_response(response: dict) -> dict:
} }
def _build_related_history( def _build_related_history(raw_history: list, current_complaint_id: str | None, llm_scores: list, threshold: int) -> list:
raw_history: list, """Merge LLM similarity scores with raw history items and filter by threshold."""
current_complaint_id: str | None,
llm_scores: list,
threshold: int,
) -> list:
"""
Merge LLM similarity scores with raw history items and filter by threshold.
Each returned item has: protocolo, data_reclamacao, similaridade_pct,
reasoning, plus the 7 detail keys (same as reclamacao_atual).
"""
if not raw_history or not llm_scores: if not raw_history or not llm_scores:
return [] return []
@@ -105,18 +93,13 @@ def _build_related_history(
def _build_analise_agente(related_items: list) -> str: def _build_analise_agente(related_items: list) -> str:
""" """Build the analise_agente text from related items."""
Build the analise_agente string from related items: one line per item with
'Protocolo X (Y%): <reasoning>'. Empty list -> negative spec message.
"""
if not related_items: if not related_items:
return _NO_SIMILAR_MESSAGE return _NO_SIMILAR_MESSAGE
lines = [] lines = []
for item in related_items: for item in related_items:
reasoning = (item.get("reasoning") or "").strip() reasoning = (item.get("reasoning") or "").strip() or ""
if not reasoning:
reasoning = ""
lines.append(f"Protocolo {item.get('protocolo')} ({item.get('similaridade_pct')}%): {reasoning}") lines.append(f"Protocolo {item.get('protocolo')} ({item.get('similaridade_pct')}%): {reasoning}")
return "\n".join(lines) return "\n".join(lines)
@@ -128,20 +111,31 @@ async def _score_history_with_llm(
session_id: str = "", session_id: str = "",
state: dict | None = None, state: dict | None = None,
) -> list: ) -> list:
""" """Score similarity between current complaint and historical complaints using the LLM."""
Calls the LLM to score similarity between the current complaint description
and each historical item. Returns the LLM-parsed JSON list. Items below
SPEECH_SIMILARITY_THRESHOLD will be dropped later by _build_related_history.
"""
if not current_complaint_description or not history_list: if not current_complaint_description or not history_list:
event("AGA.015", {
"status": "Houve acionamento do Agente Especializado (LLM): não — sem resumo atual ou histórico vazio",
"type": "INFO",
"session_id": session_id,
"tag": "AGA.015",
"call_id": session_id,
"origin": "LLM",
**build_ic_payload({"session_id": session_id}, "AGA.015"),
})
return [] return []
clean_history = [ clean_history = [item for item in history_list if str(item.get("protocolo")) != str(current_id or "")]
item for item in history_list
if str(item.get("protocolo")) != str(current_id or "")
]
if not clean_history: if not clean_history:
logger.info("No historical items left after deduplication.") logger.info("No historical items left after deduplication.")
event("AGA.015", {
"status": "Houve acionamento do Agente Especializado (LLM): não — histórico vazio após deduplicação",
"type": "INFO",
"session_id": session_id,
"tag": "AGA.015",
"call_id": session_id,
"origin": "LLM",
**build_ic_payload({"session_id": session_id}, "AGA.015"),
})
return [] return []
llm = classification_llm llm = classification_llm
@@ -163,6 +157,305 @@ async def _score_history_with_llm(
history_json=json.dumps(history_payload, ensure_ascii=False), history_json=json.dumps(history_payload, ensure_ascii=False),
) )
logger.info(f"Calling LLM for history similarity filtering. Items: {len(clean_history)}") logger.info("Calling LLM for history similarity filtering. Items: %s", len(clean_history))
event("AGA.014", {
"status": "Houve acionamento do Agente Especializado (LLM): sim — filtragem de histórico Speech",
"type": "INFO",
"session_id": session_id,
"tag": "AGA.014",
"call_id": session_id,
"origin": "LLM",
**build_ic_payload({"session_id": session_id}, "AGA.014"),
})
# LLM generation telemetry is recorded by agent_framework.llm.providers. try:
_t0 = time.perf_counter()
llm_resp = chat_llm_with_usage(llm, message)
content = llm_resp.content
json_match = re.search(r"```(?:json)?\s*(\[.*?\])\s*```", content, re.DOTALL)
if json_match:
content = json_match.group(1)
else:
json_match = re.search(r"(\[.*?\])", content, re.DOTALL)
if json_match:
content = json_match.group(1)
scores = json.loads(content)
if not isinstance(scores, list):
raise ValueError(f"LLM returned non-list: {type(scores).__name__}")
score_current_trace(name="speech_history_similarity_valid", value=1.0)
return scores
except Exception as e:
logger.error("Error during LLM history scoring: %s", e, exc_info=True)
score_current_trace(name="speech_history_similarity_valid", value=0.0, comment=str(e))
if state is not None:
event("NOC.004", {
"status": f"LLM error during history similarity analysis: {e}",
"type": "FAILURE",
**build_noc_llm_metadata(
state,
"NOC.004",
latency_ms=int((time.perf_counter() - _t0) * 1000) if "_t0" in locals() else 0,
llm_endpoint=LLM_ENDPOINT,
model_name=str(getattr(llm, "eligibleModel_name", "unknown")),
),
}, metadata={"noc": True})
return []
@trace_node
async def enrich_with_speech(state: AgentState) -> AgentState:
"""Enrich the ticket with Speech Analytics NLP insights and historical similarity."""
logger.info("Running speech enrichment node.")
await set_current_step(state, GraphStep.SPEECH_ENRICHMENT)
session_id = state.get("session_id", "")
context = state.get("metadata", {}).get("request_context", {})
if "speech_analytics" in context:
logger.info("Speech analytics data already exists in context (cached). Skipping API call.")
return state
complaint = context.get("complaint", {})
reclamacao_id: str | None = complaint.get("complaintProtocol")
raw_text: str | None = complaint.get("description")
customer = context.get("customer", {})
cpf_cnpj: str | None = customer.get("cpfCnpj")
msisdn: str | None = customer.get("msisdn")
# Framework-native path: call Speech through MCP/fallback instead of direct legacy clients.
mcp_response = await call_mcp_tool(
"consultar_speech_analytics",
{
"protocol_id": reclamacao_id,
"customer_key": cpf_cnpj or msisdn,
"interaction_key": reclamacao_id,
"document": cpf_cnpj,
},
business_context={"customer_key": cpf_cnpj or msisdn, "interaction_key": reclamacao_id},
original_context=context,
)
mcp_result = mcp_response.get("result") if isinstance(mcp_response.get("result"), dict) else mcp_response
if mcp_response.get("ok") or str(mcp_result.get("source", "")).startswith("mock_"):
summary = mcp_result.get("summary") or (raw_text or "Reclamação recebida para análise.")
speech_data = {
"reclamacao_resumo": summary,
"causa_raiz": mcp_result.get("root_cause") or "Cobrança/contato indevido",
"descortesia_cliente": mcp_result.get("rude_customer") or "False",
"motivo_reclamacao": mcp_result.get("reason") or complaint.get("motive"),
"submotivo_reclamacao": mcp_result.get("subreason") or complaint.get("modality"),
"sentimento_cliente": mcp_result.get("sentiment") or "Negativo",
"solucao_proposta_cliente": mcp_result.get("proposed_solution") or "Analisar cobrança e cessar contatos indevidos",
"historico_bruto": mcp_result.get("events") or [],
"historico_relacionado": mcp_result.get("related_history") or [],
"analise_agente": mcp_result.get("agent_analysis") or (mcp_result.get("message") or _NO_SIMILAR_MESSAGE),
"source": mcp_result.get("source", "framework_mcp"),
"mcp_tool": "consultar_speech_analytics",
}
context = dict(context)
context["speech_analytics"] = speech_data
state = update_state_metadata(state, request_context=context)
event("AGA.034", {
"status": "Resultado Speech: sucesso via MCP/framework",
"type": "SUCCESS",
"session_id": session_id,
"tag": "AGA.034",
"call_id": session_id,
"origin": "MCP",
**build_ic_payload(state, "AGA.034"),
}, metadata={"noc": True})
return state
if not getattr(settings, "BACKOFFICE_ALLOW_LEGACY_CLIENTS", False):
speech_data = dict(_NULL_SPEECH_DATA)
speech_data.update({
"reclamacao_resumo": raw_text or "Indisponível SPEECH para Reclamação Atual - realizar consulta manualmente",
"historico_bruto": [],
"historico_relacionado": [],
"analise_agente": _HISTORY_UNAVAILABLE_MESSAGE,
"source": "framework_local_fallback",
})
context = dict(context)
context["speech_analytics"] = speech_data
state = update_state_metadata(state, request_context=context)
await set_current_step(state, GraphStep.SPEECH_ENRICHMENT_UNAVAILABLE)
return state
from src.components.clients.speech_analytics_client import SpeechAnalyticsClient
from src.components.clients.exceptions.speech_exceptions import SpeechClientError
speech_data: dict = dict(_NULL_SPEECH_DATA)
history_unavailable = False
if not reclamacao_id or not raw_text:
logger.warning(
"Speech enrichment skipped prediction: missing complaintProtocol or description. "
"reclamacao_id=%s, raw_text_present=%s",
bool(reclamacao_id), bool(raw_text)
)
event("AGA.010", {
"status": "Resultado consulta Speech: ignorada — complaintProtocol ou description ausente",
"type": "INFO",
"session_id": session_id,
"tag": "AGA.010",
"call_id": session_id,
"origin": "AGENT",
**build_ic_payload(state, "AGA.010"),
}, metadata={"noc": True})
else:
try:
client = SpeechAnalyticsClient()
response, http_meta = await client.get_prediction(
reclamacao_id=reclamacao_id,
raw_text=raw_text,
customer_segment="",
)
speech_data = _map_prediction_response(response)
if not speech_data.get("reclamacao_resumo"):
speech_data["reclamacao_resumo"] = "Reclamação Atual não foi encontrada no SPEECH"
event("NOC.002", {
"status": "Invalid API response",
"type": "WARNING",
**build_noc_api_metadata(
state,
"NOC.002",
retry_count=http_meta.get("retry_count", 0),
latency_ms=http_meta["latency_ms"],
api_url=http_meta["url"],
status_code=http_meta["status_code"],
),
}, metadata={"noc": True})
logger.info("Speech Analytics prediction retrieved successfully. reclamacao_id=%s", reclamacao_id)
event("AGA.010", {
"status": "Resultado consulta Speech: sucesso",
"type": "SUCCESS",
"session_id": session_id,
"tag": "AGA.010",
"call_id": session_id,
"origin": "AGENT",
**build_ic_payload(state, "AGA.010", {
"apiUrl": http_meta["url"],
"apiStatusCode": http_meta["status_code"],
"apiResponsePayload": http_meta["response_text"],
"latencyMs": http_meta["latency_ms"],
}),
}, metadata={"noc": True})
except SpeechClientError as client_exc:
logger.warning("Speech Analytics unavailable — continuing without prediction. Error: %s", client_exc)
speech_data["reclamacao_resumo"] = "Indisponível SPEECH para Reclamação Atual - realizar consulta manualmente"
await set_current_step(state, GraphStep.SPEECH_ENRICHMENT_UNAVAILABLE)
api_fields = {
"apiUrl": getattr(client_exc, "url", None) or "N/A",
"apiStatusCode": getattr(client_exc, "status_code", None) if getattr(client_exc, "status_code", None) is not None else "N/A",
"apiResponsePayload": getattr(client_exc, "response_text", None) if getattr(client_exc, "response_text", None) is not None else "N/A",
"latencyMs": getattr(client_exc, "latency_ms", None) if getattr(client_exc, "latency_ms", None) is not None else "N/A",
}
event("AGA.035", {
"status": f"Erro na consulta Speech (Reclamação Atual): SpeechClientError: {str(client_exc)}",
"type": "FAILURE",
"session_id": session_id,
"tag": "AGA.035",
"call_id": session_id,
"origin": "AGENT",
**build_ic_payload(state, "AGA.035", api_fields),
}, metadata={"noc": True})
historico: list = []
if not cpf_cnpj:
logger.warning("Speech enrichment skipped history fetch: cpfCnpj absent.", extra={"session_id": session_id})
event("AGA.034", {
"status": "Resultado consulta histórico Speech: ignorada — cpfCnpj ausente",
"type": "INFO",
"session_id": session_id,
"tag": "AGA.034",
"call_id": session_id,
"origin": "AGENT",
**build_ic_payload(state, "AGA.034"),
}, metadata={"noc": True})
else:
try:
client = SpeechAnalyticsClient()
historico, http_meta = await client.get_history(cpf_cnpj, msisdn)
logger.info("Speech Analytics history retrieved successfully. %s items.", len(historico), extra={"session_id": session_id})
event("AGA.034", {
"status": f"Resultado consulta histórico Speech: sucesso — {len(historico)} item(ns)",
"type": "SUCCESS",
"session_id": session_id,
"tag": "AGA.034",
"call_id": session_id,
"origin": "AGENT",
**build_ic_payload(state, "AGA.034", {
"apiUrl": http_meta["url"],
"apiStatusCode": http_meta["status_code"],
"apiResponsePayload": http_meta["response_text"],
"latencyMs": http_meta["latency_ms"],
}),
}, metadata={"noc": True})
if not historico:
event("NOC.002", {
"status": "Invalid API response",
"type": "WARNING",
**build_noc_api_metadata(
state,
"NOC.002",
retry_count=http_meta.get("retry_count", 0),
latency_ms=http_meta["latency_ms"],
api_url=http_meta["url"],
status_code=http_meta["status_code"],
),
}, metadata={"noc": True})
except Exception as exc:
logger.warning("Failed to fetch speech history: %s", exc)
await set_current_step(state, GraphStep.SPEECH_ENRICHMENT_UNAVAILABLE)
api_fields = {}
if isinstance(exc, SpeechClientError):
api_fields = {
"apiUrl": getattr(exc, "url", None) or "N/A",
"apiStatusCode": getattr(exc, "status_code", None) if getattr(exc, "status_code", None) is not None else "N/A",
"apiResponsePayload": getattr(exc, "response_text", None) if getattr(exc, "response_text", None) is not None else "N/A",
"latencyMs": getattr(exc, "latency_ms", None) if getattr(exc, "latency_ms", None) is not None else "N/A",
}
event("AGA.035", {
"status": f"Falha ao buscar histórico Speech: {str(exc)}",
"type": "FAILURE",
"session_id": session_id,
"tag": "AGA.035",
"call_id": session_id,
"origin": "AGENT",
**build_ic_payload(state, "AGA.035", api_fields),
}, metadata={"noc": True})
history_unavailable = True
historico_relacionado: list = []
if historico and raw_text:
scores = await _score_history_with_llm(
current_id=reclamacao_id,
current_complaint_description=raw_text,
history_list=historico,
session_id=session_id,
state=state,
)
historico_relacionado = _build_related_history(
raw_history=historico,
current_complaint_id=reclamacao_id,
llm_scores=scores,
threshold=settings.SPEECH_SIMILARITY_THRESHOLD,
)
def _strip_to_detail_keys(h_list: list) -> list:
return [{k: item.get(k) for k in _HISTORY_DETAIL_KEYS} for item in h_list]
speech_data["historico_bruto"] = _strip_to_detail_keys(historico)
speech_data["historico_relacionado"] = historico_relacionado
speech_data["analise_agente"] = _HISTORY_UNAVAILABLE_MESSAGE if history_unavailable else _build_analise_agente(historico_relacionado)
context = dict(context)
context["speech_analytics"] = speech_data
state = update_state_metadata(state, request_context=context)
logger.info("Speech enrichment node completed successfully. State metadata and current step updated: %s", state.get("current_step"))
return state

View File

@@ -25,6 +25,7 @@ from src.agent.state.agent_state import AgentState
from src.agent.state.step_notes import STEP_NOTES from src.agent.state.step_notes import STEP_NOTES
from src.agent.state.steps import GraphStep from src.agent.state.steps import GraphStep
from src.api.schemas.anatel_schemas import ProgressEvent, ProgressProcessing from src.api.schemas.anatel_schemas import ProgressEvent, ProgressProcessing
from src.compat.framework_services import get_progress_producer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -68,7 +69,7 @@ async def set_current_step(state: AgentState, step: GraphStep) -> None:
return return
metadata = state.get("metadata") or {} metadata = state.get("metadata") or {}
producer = metadata.get("_oci_producer") producer = metadata.get("_oci_producer") or get_progress_producer()
transaction_id = metadata.get("transaction_id") transaction_id = metadata.get("transaction_id")
if producer is None or not transaction_id: if producer is None or not transaction_id:
return return

View File

@@ -0,0 +1,63 @@
"""Framework service bridge for migrated backoffice domain nodes.
This module is intentionally tiny and dependency-light. It lets old domain nodes
call framework-owned services (MCP router and progress producer) without storing
non-serializable objects inside LangGraph state/checkpoints.
"""
from __future__ import annotations
from typing import Any
import logging
logger = logging.getLogger(__name__)
_tool_router: Any = None
_progress_producer: Any = None
def configure(*, tool_router: Any | None = None, progress_producer: Any | None = None) -> None:
global _tool_router, _progress_producer
if tool_router is not None:
_tool_router = tool_router
if progress_producer is not None:
_progress_producer = progress_producer
def get_tool_router() -> Any | None:
return _tool_router
def get_progress_producer() -> Any | None:
return _progress_producer
async def call_mcp_tool(
tool_name: str,
arguments: dict[str, Any] | None = None,
*,
business_context: dict[str, Any] | None = None,
original_context: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Call a framework MCP tool and normalize the result to a dict.
Returns {"ok": False, "error": ...} when the router is not configured or the
tool fails, so domain nodes can decide whether to fail-open or fail-closed.
"""
router = get_tool_router()
if router is None or not getattr(router, "enabled", False):
return {"ok": False, "error": "framework MCP router not configured", "tool": tool_name}
try:
result = await router.call(
tool_name,
arguments or {},
business_context=business_context or {},
original_context=original_context or arguments or {},
)
if hasattr(result, "model_dump"):
return result.model_dump(mode="json")
if isinstance(result, dict):
return result
return {"ok": True, "result": result, "tool": tool_name}
except Exception as exc: # noqa: BLE001
logger.warning("framework MCP call failed tool=%s error=%s", tool_name, exc, exc_info=True)
return {"ok": False, "error": str(exc), "tool": tool_name}

View File

@@ -69,14 +69,14 @@ class Settings(BaseSettings):
LLM_MAX_TOKENS: Optional[int] = Field(default=None, gt=0, description="Max tokens") LLM_MAX_TOKENS: Optional[int] = Field(default=None, gt=0, description="Max tokens")
# Classification LLM settings # Classification LLM settings
CLASSIFICATION_LLM_MODEL: str = Field(default="bo_gptoss20b_dev", description="Model name for classification node") CLASSIFICATION_LLM_MODEL: str = Field(default="openai.gpt-4.1", description="Model name for classification node")
CLASSIFICATION_LLM_TEMPERATURE: float = Field(default=0.3, ge=0.0, le=2.0, description="Temperature for classification node") CLASSIFICATION_LLM_TEMPERATURE: float = Field(default=0.3, ge=0.0, le=2.0, description="Temperature for classification node")
CLASSIFICATION_LLM_MAX_TOKENS: int = Field(default=1024, gt=0, description="Max tokens for classification node") CLASSIFICATION_LLM_MAX_TOKENS: int = Field(default=1024, gt=0, description="Max tokens for classification node")
CLASSIFICATION_LLM_TOP_P: float = Field(default=0.8, description="Top P for classification node") CLASSIFICATION_LLM_TOP_P: float = Field(default=0.8, description="Top P for classification node")
CLASSIFICATION_LLM_TOP_K: float = Field(default=250, description="Top K for classification node") CLASSIFICATION_LLM_TOP_K: float = Field(default=250, description="Top K for classification node")
# Large Classification LLM settings (for complex reasoning/canceling) # Large Classification LLM settings (for complex reasoning/canceling)
CLASSIFICATION_LARGE_LLM_MODEL: str = Field(default="bo_gptoss120b_dev", description="Large model name for critical classification steps") CLASSIFICATION_LARGE_LLM_MODEL: str = Field(default="openai.gpt-4.1", description="Large model name for critical classification steps")
CLASSIFICATION_LARGE_LLM_TEMPERATURE: float = Field(default=0.3, ge=0.0, le=2.0, description="Temperature for large classification node") CLASSIFICATION_LARGE_LLM_TEMPERATURE: float = Field(default=0.3, ge=0.0, le=2.0, description="Temperature for large classification node")
CLASSIFICATION_LARGE_LLM_MAX_TOKENS: int = Field(default=1024, gt=0, description="Max tokens for large classification node") CLASSIFICATION_LARGE_LLM_MAX_TOKENS: int = Field(default=1024, gt=0, description="Max tokens for large classification node")
CLASSIFICATION_LARGE_LLM_TOP_P: float = Field(default=0.8, description="Top P for large classification node") CLASSIFICATION_LARGE_LLM_TOP_P: float = Field(default=0.8, description="Top P for large classification node")
@@ -201,6 +201,9 @@ class Settings(BaseSettings):
# Anatel Dictionary settings # Anatel Dictionary settings
USE_FULL_ANATEL_DICT: bool = Field(default=False, description="Whether to use the full Anatel motives dictionary instead of filtering by service") USE_FULL_ANATEL_DICT: bool = Field(default=False, description="Whether to use the full Anatel motives dictionary instead of filtering by service")
# Migration control. Default False: domain nodes must use framework services/MCP/fallbacks.
BACKOFFICE_ALLOW_LEGACY_CLIENTS: bool = Field(default=False, description="Allow direct legacy TIM clients as a parity escape hatch. Keep False for framework-native execution.")
# Speech Analytics API settings # Speech Analytics API settings
SPEECH_PREDICTION_BASE_URL: Optional[str] = Field(default=None, description="Base URL of the Speech Prediction API gateway (OAuth2 + prediction endpoint)") SPEECH_PREDICTION_BASE_URL: Optional[str] = Field(default=None, description="Base URL of the Speech Prediction API gateway (OAuth2 + prediction endpoint)")
SPEECH_PREDICTION_CLIENT_ID: Optional[str] = Field(default=None, description="Client ID for the Speech Prediction OAuth2 client_credentials flow") SPEECH_PREDICTION_CLIENT_ID: Optional[str] = Field(default=None, description="Client ID for the Speech Prediction OAuth2 client_credentials flow")
@@ -230,7 +233,7 @@ class Settings(BaseSettings):
TAIS_TABLE_CHUNKS: str = Field(default="CHUNKS_CHAR_COHERE_3", description="Oracle table containing TAIS chunks + embeddings") TAIS_TABLE_CHUNKS: str = Field(default="CHUNKS_CHAR_COHERE_3", description="Oracle table containing TAIS chunks + embeddings")
TAIS_TABLE_FILES: str = Field(default="files_oci", description="Oracle table containing TAIS raw documents") TAIS_TABLE_FILES: str = Field(default="files_oci", description="Oracle table containing TAIS raw documents")
TAIS_TOP_K: int = Field(default=3, gt=0, description="Number of unique documents returned by TAIS KB search") TAIS_TOP_K: int = Field(default=3, gt=0, description="Number of unique documents returned by TAIS KB search")
TAIS_KB_LLM_MODEL: str = Field(default="bo_gptoss20b_dev", description="Modelo LLM para pós-processamento da KB (bo_gptoss20b_dev ou bo_gptoss120b_dev)") TAIS_KB_LLM_MODEL: str = Field(default="openai.gpt-4.1", description="Modelo LLM framework para pós-processamento da KB")
TAIS_KB_LLM_TEMPERATURE: float = Field(default=0.3, ge=0.0, le=2.0) TAIS_KB_LLM_TEMPERATURE: float = Field(default=0.3, ge=0.0, le=2.0)
TAIS_KB_LLM_MAX_TOKENS: int = Field(default=4096, gt=0, description="Tokens de saída — manter alto para respostas completas") TAIS_KB_LLM_MAX_TOKENS: int = Field(default=4096, gt=0, description="Tokens de saída — manter alto para respostas completas")
TAIS_KB_LLM_TOP_P: float = Field(default=0.9) TAIS_KB_LLM_TOP_P: float = Field(default=0.9)

View File

@@ -71,7 +71,7 @@ class _SettingsProxy:
# OpenAI-compatible implementation. # OpenAI-compatible implementation.
if self.LLM_PROVIDER == "oci": if self.LLM_PROVIDER == "oci":
self.LLM_PROVIDER = "oci_openai" self.LLM_PROVIDER = "oci_openai"
self.OCI_GENAI_MODEL = str(llm.eligibleModel_name) self.OCI_GENAI_MODEL = _framework_model_name(llm.eligibleModel_name)
self.LLM_TEMPERATURE = float(llm.temperature) self.LLM_TEMPERATURE = float(llm.temperature)
self.LLM_MAX_TOKENS = int(llm.max_tokens) self.LLM_MAX_TOKENS = int(llm.max_tokens)
@@ -81,8 +81,24 @@ class _SettingsProxy:
LLM_ENDPOINT: str = getattr(fw_settings, "OCI_GENAI_BASE_URL", "") LLM_ENDPOINT: str = getattr(fw_settings, "OCI_GENAI_BASE_URL", "")
def _framework_model_name(candidate: str | None = None) -> str:
"""Resolve any legacy backoffice model alias to the framework model.
Old develop configs used internal model aliases. In the migrated project those aliases must never be sent to OCI/OpenAI directly.
"""
model = str(candidate or "").strip()
if not model or model.startswith("bo_") or "gptoss" in model.lower():
model = (
getattr(fw_settings, "OCI_GENAI_MODEL", None)
or getattr(fw_settings, "LLM_MODEL", None)
or getattr(settings, "LLM_MODEL", None)
or "openai.gpt-4.1"
)
return str(model)
classification_llm = BackofficeLLMDescriptor( classification_llm = BackofficeLLMDescriptor(
eligibleModel_name=settings.CLASSIFICATION_LLM_MODEL, eligibleModel_name=_framework_model_name(settings.CLASSIFICATION_LLM_MODEL),
temperature=settings.CLASSIFICATION_LLM_TEMPERATURE, temperature=settings.CLASSIFICATION_LLM_TEMPERATURE,
max_tokens=settings.CLASSIFICATION_LLM_MAX_TOKENS, max_tokens=settings.CLASSIFICATION_LLM_MAX_TOKENS,
top_p=settings.CLASSIFICATION_LLM_TOP_P, top_p=settings.CLASSIFICATION_LLM_TOP_P,
@@ -90,7 +106,7 @@ classification_llm = BackofficeLLMDescriptor(
) )
classification_large_llm = BackofficeLLMDescriptor( classification_large_llm = BackofficeLLMDescriptor(
eligibleModel_name=settings.CLASSIFICATION_LARGE_LLM_MODEL, eligibleModel_name=_framework_model_name(settings.CLASSIFICATION_LARGE_LLM_MODEL),
temperature=settings.CLASSIFICATION_LARGE_LLM_TEMPERATURE, temperature=settings.CLASSIFICATION_LARGE_LLM_TEMPERATURE,
max_tokens=settings.CLASSIFICATION_LARGE_LLM_MAX_TOKENS, max_tokens=settings.CLASSIFICATION_LARGE_LLM_MAX_TOKENS,
top_p=settings.CLASSIFICATION_LARGE_LLM_TOP_P, top_p=settings.CLASSIFICATION_LARGE_LLM_TOP_P,
@@ -98,7 +114,7 @@ classification_large_llm = BackofficeLLMDescriptor(
) )
tais_kb_llm = BackofficeLLMDescriptor( tais_kb_llm = BackofficeLLMDescriptor(
eligibleModel_name=settings.TAIS_KB_LLM_MODEL, eligibleModel_name=_framework_model_name(settings.TAIS_KB_LLM_MODEL),
temperature=settings.TAIS_KB_LLM_TEMPERATURE, temperature=settings.TAIS_KB_LLM_TEMPERATURE,
max_tokens=settings.TAIS_KB_LLM_MAX_TOKENS, max_tokens=settings.TAIS_KB_LLM_MAX_TOKENS,
top_p=settings.TAIS_KB_LLM_TOP_P, top_p=settings.TAIS_KB_LLM_TOP_P,