mirror of
https://github.com/hoshikawa2/compass_backoffice.git
synced 2026-07-09 13:54:20 +00:00
bugfixes
This commit is contained in:
8
.env
8
.env
@@ -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
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
41
README_FRAMEWORK_NATIVE_MIGRATION_NOTES.md
Normal file
41
README_FRAMEWORK_NATIVE_MIGRATION_NOTES.md
Normal 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
|
||||||
|
```
|
||||||
54
app/main.py
54
app/main.py
@@ -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
|
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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
88
debug_events.jsonl
Normal 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"}}}
|
||||||
62
legacy_backend/mock_imdb_server.py
Normal file
62
legacy_backend/mock_imdb_server.py
Normal 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": []
|
||||||
|
}
|
||||||
@@ -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"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
47
mock_servers/mock_imdb_server.py
Normal file
47
mock_servers/mock_imdb_server.py
Normal 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"),
|
||||||
|
)
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
63
src/compat/framework_services.py
Normal file
63
src/compat/framework_services.py
Normal 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}
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user