mirror of
https://github.com/hoshikawa2/agent_platform_oci.git
synced 2026-11-06 03:14:12 +00:00
adding ABANDON in multi-intent/intent-shift
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tests/unit/__pycache__/test_cache.cpython-313-pytest-9.0.2.pyc
Normal file
BIN
tests/unit/__pycache__/test_cache.cpython-313-pytest-9.0.2.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tests/unit/__pycache__/test_rag.cpython-313-pytest-9.0.2.pyc
Normal file
BIN
tests/unit/__pycache__/test_rag.cpython-313-pytest-9.0.2.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tests/unit/__pycache__/test_sse.cpython-313-pytest-9.0.2.pyc
Normal file
BIN
tests/unit/__pycache__/test_sse.cpython-313-pytest-9.0.2.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -115,3 +115,208 @@ async def test_deferred_transaction_is_promoted_after_primary_completes():
|
||||
assert result.state_patch["pending_tool_call"]["tool_name"] == "cancelar_pedido"
|
||||
assert result.agent_responses[-1]["primary"] is True
|
||||
assert result.agent_responses[-1]["agent"] == "orders_agent"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stale_agent_responses_do_not_leak_into_new_turn_projection():
|
||||
state = {
|
||||
"answer": "Lista atual de serviços.",
|
||||
"route": "vas_agent",
|
||||
"active_agent": "vas_agent",
|
||||
"intent": "contas_vas_information",
|
||||
"agent_responses": [
|
||||
{"agent": "contestacao_agent", "answer": "Você confirma Tamboro e Paramount+?", "primary": True, "status": "completed"},
|
||||
{"operation_id": "op-2", "agent": "faturas_agent", "answer": "Resposta antiga de fatura.", "primary": False, "status": "completed"},
|
||||
],
|
||||
"pending_topics": [],
|
||||
}
|
||||
result = await drain_pending_topics(state, state["answer"], {})
|
||||
assert [r["answer"] for r in result.agent_responses] == ["Lista atual de serviços."]
|
||||
assert result.agent_responses[0]["agent"] == "vas_agent"
|
||||
assert result.agent_responses[0]["primary"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_completed_secondary_result_is_kept_as_durable_operation_result():
|
||||
async def billing_agent(state):
|
||||
return {
|
||||
"answer": "Fatura consultada.",
|
||||
"mcp_results": [{"tool_name": "consultar_faturas", "ok": True, "invoice_id": "INV-1"}],
|
||||
}
|
||||
state = {
|
||||
"answer": "Cancelamento concluÃdo.",
|
||||
"route": "contestacao_agent",
|
||||
"active_agent": "contestacao_agent",
|
||||
"intent": "contas_vas_cancel",
|
||||
"operation_results": {
|
||||
"op-1": {"operation_id": "op-1", "status": "completed", "answer": "Cancelamento concluÃdo."}
|
||||
},
|
||||
"pending_topics": [{
|
||||
"operation_id": "op-2", "intent": "contas_invoice_query", "agent": "faturas_agent",
|
||||
"domain": "telecom_contas", "tools": ["consultar_faturas"],
|
||||
"source_text": "consulte minha fatura", "disposition": "execute", "status": "pending",
|
||||
}],
|
||||
}
|
||||
result = await drain_pending_topics(state, state["answer"], {"faturas_agent": billing_agent})
|
||||
assert result.operation_results["op-1"]["answer"] == "Cancelamento concluÃdo."
|
||||
assert result.operation_results["op-2"]["status"] == "completed"
|
||||
assert result.operation_results["op-2"]["mcp_results"][0]["invoice_id"] == "INV-1"
|
||||
assert [r["answer"] for r in result.agent_responses] == ["Cancelamento concluÃdo.", "Fatura consultada."]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_primary_operation_is_not_promoted_while_waiting_confirmation():
|
||||
state = {
|
||||
"answer": "Você confirma o cancelamento?",
|
||||
"route": "contestacao_agent",
|
||||
"active_agent": "contestacao_agent",
|
||||
"intent": "contas_vas_cancel",
|
||||
"transaction_status": "AWAITING_CONFIRMATION",
|
||||
"confirmation_required": True,
|
||||
"pending_tool_call": {"tool_name": "cancelar_vas_avulso", "arguments": {"subject": "Tamboro"}},
|
||||
"multi_intent_plan": {
|
||||
"plan_id": "mip-1",
|
||||
"operations": [
|
||||
{"operation_id": "op-1", "intent": "contas_vas_cancel", "agent": "contestacao_agent", "domain": "telecom_contas", "tools": ["cancelar_vas_avulso"], "source_text": "cancele tamboro"},
|
||||
],
|
||||
},
|
||||
"operation_results": {},
|
||||
"pending_topics": [],
|
||||
}
|
||||
result = await drain_pending_topics(state, state["answer"], {})
|
||||
assert "op-1" not in result.operation_results
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_terminal_primary_operation_is_promoted_and_keeps_previous_results():
|
||||
state = {
|
||||
"answer": "Tamboro cancelado; Paramount+ não encontrado.",
|
||||
"route": "contestacao_agent",
|
||||
"active_agent": "contestacao_agent",
|
||||
"intent": "state:WAITING_CONTESTACAO_CONFIRMATION",
|
||||
"transaction_status": "COMPLETED",
|
||||
"multi_intent_plan": {
|
||||
"plan_id": "mip-1",
|
||||
"operations": [
|
||||
{"operation_id": "op-1", "intent": "contas_vas_cancel", "agent": "contestacao_agent", "domain": "telecom_contas", "tools": ["cancelar_vas_avulso"], "source_text": "cancele tamboro e paramount"},
|
||||
{"operation_id": "op-2", "intent": "contas_invoice_query", "agent": "faturas_agent", "domain": "telecom_contas", "tools": ["consultar_faturas"], "source_text": "consulte minha fatura"},
|
||||
],
|
||||
},
|
||||
"operation_results": {
|
||||
"op-2": {"operation_id": "op-2", "status": "completed", "answer": "Fatura consultada."}
|
||||
},
|
||||
"mcp_results": [
|
||||
{
|
||||
"tool_name": "cancelar_vas_avulso",
|
||||
"ok": True,
|
||||
"result": {
|
||||
"status": "COMPLETED",
|
||||
"output": {
|
||||
"cancelar_vas_avulso": {
|
||||
"results": [
|
||||
{"subject": "Tamboro Mensal", "success": True, "protocol": "123"},
|
||||
{"subject": "Paramount+", "success": False, "reason": "service_not_found"},
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
],
|
||||
"transaction_evidence": [{"transaction_id": "tx-1", "status": "COMPLETED"}],
|
||||
"pending_topics": [],
|
||||
}
|
||||
result = await drain_pending_topics(state, state["answer"], {})
|
||||
assert result.operation_results["op-2"]["answer"] == "Fatura consultada."
|
||||
primary = result.operation_results["op-1"]
|
||||
assert primary["plan_id"] == "mip-1"
|
||||
assert primary["status"] == "completed"
|
||||
assert primary["mcp_results"][0]["result"]["output"]["cancelar_vas_avulso"]["results"][1]["reason"] == "service_not_found"
|
||||
assert primary["transaction_evidence"][0]["transaction_id"] == "tx-1"
|
||||
assert [item["answer"] for item in result.agent_responses] == ["Tamboro cancelado; Paramount+ não encontrado."]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_persisted_plan_does_not_rehydrate_completed_secondary_topics_on_next_turn():
|
||||
calls = {"faturas": 0, "suporte": 0}
|
||||
|
||||
async def billing_agent(state):
|
||||
calls["faturas"] += 1
|
||||
return {"answer": "Fatura executada novamente - ERRO."}
|
||||
|
||||
async def support_agent(state):
|
||||
calls["suporte"] += 1
|
||||
return {"answer": "RAG executado novamente - ERRO."}
|
||||
|
||||
plan = {
|
||||
"plan_id": "mip-continue-1",
|
||||
"operations": [
|
||||
{"operation_id": "op-1", "intent": "contas_vas_cancel", "agent": "contestacao_agent", "domain": "telecom_contas", "tools": ["cancelar_vas_avulso"], "source_text": "cancele tamboro"},
|
||||
{"operation_id": "op-2", "intent": "contas_invoice_query", "agent": "faturas_agent", "domain": "telecom_contas", "tools": ["consultar_faturas"], "source_text": "consulte minha fatura", "disposition": "execute", "status": "pending"},
|
||||
{"operation_id": "op-3", "intent": "contas_knowledge", "agent": "suporte_contas_agent", "domain": "telecom_contas", "tools": [], "source_text": "explique infinity pos", "disposition": "execute", "status": "pending"},
|
||||
],
|
||||
}
|
||||
state = {
|
||||
"answer": "Cancelamento concluído.",
|
||||
"route": "contestacao_agent",
|
||||
"active_agent": "contestacao_agent",
|
||||
"intent": "state:WAITING_CONTESTACAO_CONFIRMATION",
|
||||
"transaction_status": "COMPLETED",
|
||||
"route_decision": {"route": "contestacao_agent", "intent": "state:WAITING_CONTESTACAO_CONFIRMATION", "metadata": {}},
|
||||
"multi_intent_plan": plan,
|
||||
# Empty is authoritative: op-2/op-3 were already drained on turn 1.
|
||||
"pending_topics": [],
|
||||
"operation_results": {
|
||||
"op-2": {"operation_id": "op-2", "plan_id": "mip-continue-1", "agent": "faturas_agent", "intent": "contas_invoice_query", "domain": "telecom_contas", "source_text": "consulte minha fatura", "status": "completed", "answer": "Fatura consultada no turno anterior."},
|
||||
"op-3": {"operation_id": "op-3", "plan_id": "mip-continue-1", "agent": "suporte_contas_agent", "intent": "contas_knowledge", "domain": "telecom_contas", "source_text": "explique infinity pos", "status": "completed", "answer": "Infinity explicado no turno anterior."},
|
||||
},
|
||||
"mcp_results": [{"tool_name": "cancelar_vas_avulso", "ok": True, "result": {"status": "COMPLETED"}}],
|
||||
}
|
||||
|
||||
result = await drain_pending_topics(
|
||||
state,
|
||||
state["answer"],
|
||||
{"faturas_agent": billing_agent, "suporte_contas_agent": support_agent},
|
||||
)
|
||||
|
||||
assert calls == {"faturas": 0, "suporte": 0}
|
||||
assert [item["answer"] for item in result.agent_responses] == ["Cancelamento concluído."]
|
||||
assert result.operation_results["op-2"]["answer"] == "Fatura consultada no turno anterior."
|
||||
assert result.operation_results["op-3"]["answer"] == "Infinity explicado no turno anterior."
|
||||
assert result.operation_results["op-1"]["status"] == "completed"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_completed_queued_topic_is_idempotently_skipped_but_result_remains_available_to_processing():
|
||||
calls = 0
|
||||
|
||||
async def billing_agent(state):
|
||||
nonlocal calls
|
||||
calls += 1
|
||||
return {"answer": "não deveria executar"}
|
||||
|
||||
plan = {
|
||||
"plan_id": "mip-idempotent-1",
|
||||
"operations": [
|
||||
{"operation_id": "op-1", "intent": "primary", "agent": "primary_agent", "domain": "test", "tools": [], "source_text": "principal"},
|
||||
{"operation_id": "op-2", "intent": "contas_invoice_query", "agent": "faturas_agent", "domain": "telecom_contas", "tools": ["consultar_faturas"], "source_text": "consulte minha fatura", "disposition": "execute", "status": "pending"},
|
||||
],
|
||||
}
|
||||
existing = {
|
||||
"operation_id": "op-2", "plan_id": "mip-idempotent-1", "agent": "faturas_agent",
|
||||
"intent": "contas_invoice_query", "domain": "telecom_contas", "source_text": "consulte minha fatura",
|
||||
"status": "completed", "answer": "Fatura já consultada.",
|
||||
"mcp_results": [{"result": {"invoice_id": "INV-1"}}],
|
||||
}
|
||||
state = {
|
||||
"answer": "Resposta atual.",
|
||||
"route": "primary_agent", "active_agent": "primary_agent", "intent": "primary",
|
||||
"multi_intent_plan": plan,
|
||||
"pending_topics": [dict(plan["operations"][1])],
|
||||
"operation_results": {"op-2": existing},
|
||||
}
|
||||
|
||||
result = await drain_pending_topics(state, state["answer"], {"faturas_agent": billing_agent})
|
||||
|
||||
assert calls == 0
|
||||
assert result.operation_results["op-2"]["mcp_results"][0]["result"]["invoice_id"] == "INV-1"
|
||||
assert [item["answer"] for item in result.agent_responses] == ["Resposta atual."]
|
||||
assert result.pending_topics == []
|
||||
|
||||
@@ -98,3 +98,191 @@ def test_kbdb_build_messages_injects_grounding_policy():
|
||||
user = next(m["content"] for m in messages if m["role"] == "user")
|
||||
assert "Política de grounding obrigatória" in user
|
||||
assert "Não complete lacunas usando conhecimento paramétrico" in user
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("workflow_status", ["COMPLETED", "FAILED"])
|
||||
async def test_rag_skips_when_all_turn_workflows_are_terminal(workflow_status):
|
||||
rt = _runtime()
|
||||
state = {
|
||||
"agent_id": "telecom_contas",
|
||||
"user_text": "sim",
|
||||
"sanitized_input": "sim",
|
||||
"mcp_results": [
|
||||
{
|
||||
"ok": True,
|
||||
"tool_name": "cancelar_vas_avulso",
|
||||
"result": {
|
||||
"execution_id": "wf-1",
|
||||
"workflow_name": "cancelamento_vas_avulso",
|
||||
"status": workflow_status,
|
||||
"output": {"answer": "resultado operacional"},
|
||||
},
|
||||
"metadata": {
|
||||
"workflow_name": "cancelamento_vas_avulso",
|
||||
"workflow_status": workflow_status,
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
context, metadata = await rt._retrieve_rag_context(state)
|
||||
|
||||
assert context == ""
|
||||
assert not rt.rag_service.calls
|
||||
assert metadata["status"] == "skipped"
|
||||
assert metadata["reason"] == "all_turn_workflows_terminal"
|
||||
assert metadata["workflow_statuses"] == [workflow_status]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rag_still_runs_when_any_turn_workflow_is_not_terminal():
|
||||
rt = _runtime()
|
||||
state = {
|
||||
"agent_id": "telecom_contas",
|
||||
"user_text": "sim",
|
||||
"sanitized_input": "sim",
|
||||
"mcp_results": [
|
||||
{
|
||||
"ok": True,
|
||||
"result": {"execution_id": "wf-1", "workflow_name": "w1", "status": "COMPLETED"},
|
||||
"metadata": {"workflow_status": "COMPLETED"},
|
||||
},
|
||||
{
|
||||
"ok": True,
|
||||
"result": {"execution_id": "wf-2", "workflow_name": "w2", "status": "RUNNING"},
|
||||
"metadata": {"workflow_status": "RUNNING"},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
context, metadata = await rt._retrieve_rag_context(state)
|
||||
|
||||
assert rt.rag_service.calls
|
||||
assert "Tarifação documentada" in context
|
||||
assert metadata["status"] == "executed"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_explicit_requires_rag_overrides_terminal_workflow_skip():
|
||||
rt = _runtime()
|
||||
state = {
|
||||
"agent_id": "telecom_contas",
|
||||
"user_text": "sim",
|
||||
"sanitized_input": "sim",
|
||||
"mcp_results": [
|
||||
{
|
||||
"ok": True,
|
||||
"result": {
|
||||
"execution_id": "wf-1",
|
||||
"workflow_name": "w1",
|
||||
"status": "COMPLETED",
|
||||
"requires_rag": True,
|
||||
"rag_query": "explicar regra de negócio",
|
||||
},
|
||||
"metadata": {"workflow_status": "COMPLETED"},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
context, metadata = await rt._retrieve_rag_context(state)
|
||||
|
||||
assert rt.rag_service.calls
|
||||
assert rt.rag_service.calls[0][0] == "explicar regra de negócio"
|
||||
assert "Tarifação documentada" in context
|
||||
assert metadata["required_by_tool"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_terminal_workflow_does_not_skip_rag_when_operation_is_pending():
|
||||
rt = _runtime()
|
||||
state = {
|
||||
"agent_id": "telecom_contas",
|
||||
"user_text": "sim",
|
||||
"sanitized_input": "sim",
|
||||
"pending_topics": [
|
||||
{"operation_id": "op-2", "intent": "invoice_query", "status": "pending"}
|
||||
],
|
||||
"operation_results": {},
|
||||
"mcp_results": [
|
||||
{
|
||||
"ok": True,
|
||||
"result": {"execution_id": "wf-1", "workflow_name": "w1", "status": "COMPLETED"},
|
||||
"metadata": {"workflow_status": "COMPLETED"},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
context, metadata = await rt._retrieve_rag_context(state)
|
||||
|
||||
assert rt.rag_service.calls
|
||||
assert "Tarifação documentada" in context
|
||||
assert metadata["status"] == "executed"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_terminal_workflow_does_not_skip_rag_when_knowledge_operation_is_unresolved():
|
||||
rt = _runtime()
|
||||
state = {
|
||||
"agent_id": "telecom_contas",
|
||||
"user_text": "sim",
|
||||
"sanitized_input": "sim",
|
||||
"multi_intent_plan": {
|
||||
"plan_id": "mip-1",
|
||||
"operations": [
|
||||
{"operation_id": "op-1", "intent": "cancel", "status": "completed"},
|
||||
{"operation_id": "op-2", "intent": "account_knowledge", "status": "pending"},
|
||||
],
|
||||
},
|
||||
# op-1 está resolvida, op-2 ainda não possui resultado terminal.
|
||||
"operation_results": {"op-1": {"status": "completed"}},
|
||||
"mcp_results": [
|
||||
{
|
||||
"ok": True,
|
||||
"result": {"execution_id": "wf-1", "workflow_name": "w1", "status": "COMPLETED"},
|
||||
"metadata": {"workflow_status": "COMPLETED"},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
context, metadata = await rt._retrieve_rag_context(state)
|
||||
|
||||
assert rt.rag_service.calls
|
||||
assert "Tarifação documentada" in context
|
||||
assert metadata["status"] == "executed"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_completed_operation_results_prevent_stale_plan_from_counting_as_pending():
|
||||
rt = _runtime()
|
||||
state = {
|
||||
"agent_id": "telecom_contas",
|
||||
"user_text": "sim",
|
||||
"sanitized_input": "sim",
|
||||
"multi_intent_plan": {
|
||||
"plan_id": "mip-1",
|
||||
"operations": [
|
||||
{"operation_id": "op-1", "intent": "cancel", "status": "pending"},
|
||||
{"operation_id": "op-2", "intent": "invoice_query", "status": "pending"},
|
||||
{"operation_id": "op-3", "intent": "account_knowledge", "status": "pending"},
|
||||
],
|
||||
},
|
||||
"operation_results": {
|
||||
"op-1": {"status": "completed"},
|
||||
"op-2": {"status": "completed"},
|
||||
"op-3": {"status": "completed"},
|
||||
},
|
||||
"mcp_results": [
|
||||
{
|
||||
"ok": True,
|
||||
"result": {"execution_id": "wf-1", "workflow_name": "w1", "status": "COMPLETED"},
|
||||
"metadata": {"workflow_status": "COMPLETED"},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
context, metadata = await rt._retrieve_rag_context(state)
|
||||
|
||||
assert context == ""
|
||||
assert not rt.rag_service.calls
|
||||
assert metadata["status"] == "skipped"
|
||||
assert metadata["reason"] == "all_turn_workflows_terminal"
|
||||
|
||||
Reference in New Issue
Block a user