mirror of
https://github.com/hoshikawa2/agent_platform_oci.git
synced 2026-11-05 19:04:13 +00:00
289 lines
9.4 KiB
Python
289 lines
9.4 KiB
Python
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from agent_framework.rag.rag_service import RagResult
|
|
from agent_framework.rag.vector_store import VectorDocument
|
|
from agent_framework.runtime.agent_runtime import AgentRuntimeMixin
|
|
|
|
|
|
class DummyRag:
|
|
def __init__(self):
|
|
self.calls = []
|
|
|
|
async def retrieve(self, query, *, namespace="default", graph_node=None, rewrite=False, k=None):
|
|
self.calls.append((query, namespace, graph_node, rewrite))
|
|
return RagResult(
|
|
query=query,
|
|
documents=[VectorDocument(id="kb-1", content="Tarifação documentada", metadata={}, score=0.9)],
|
|
graph_neighbors=[],
|
|
latency_ms=3,
|
|
metadata={"provider": "kbdb", "confidence": "high", "low_confidence": False},
|
|
)
|
|
|
|
|
|
class Runtime(AgentRuntimeMixin):
|
|
pass
|
|
|
|
|
|
def _runtime(**settings):
|
|
rt = Runtime()
|
|
base = dict(
|
|
RAG_PROVIDER="kbdb",
|
|
SKIP_RAG_WHEN_MCP_SUFFICIENT=True,
|
|
ENABLE_RAG_QUERY_REWRITE=False,
|
|
ENABLE_RAG_CONTEXT_COMPRESSION=False,
|
|
RAG_GROUNDED_ONLY=False,
|
|
KBDB_GROUNDED_ONLY=True,
|
|
LONG_TERM_MEMORY_INJECT_CONTEXT=False,
|
|
)
|
|
base.update(settings)
|
|
rt.settings = SimpleNamespace(**base)
|
|
rt.rag_service = DummyRag()
|
|
rt.guardrail_pipeline = None
|
|
return rt
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_successful_mcp_does_not_skip_rag_without_explicit_sufficiency():
|
|
rt = _runtime()
|
|
state = {
|
|
"agent_id": "telecom_contas",
|
|
"user_text": "Como funciona a tarifação do plano Infinity Pós?",
|
|
"sanitized_input": "Como funciona a tarifação do plano Infinity Pós?",
|
|
"mcp_results": [{"ok": True, "tool_name": "qualquer_tool", "result": {"plano": "Controle 50GB"}}],
|
|
}
|
|
|
|
context, metadata = await rt._retrieve_rag_context(state)
|
|
|
|
assert rt.rag_service.calls
|
|
assert "Tarifação documentada" in context
|
|
assert metadata["provider"] == "kbdb"
|
|
assert metadata["status"] == "executed"
|
|
assert metadata["document_count"] == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_rag_skips_only_when_mcp_explicitly_declares_sufficiency():
|
|
rt = _runtime()
|
|
state = {
|
|
"agent_id": "telecom_contas",
|
|
"user_text": "qual é meu plano?",
|
|
"sanitized_input": "qual é meu plano?",
|
|
"mcp_results": [{"ok": True, "result": {"plano": "Controle 50GB", "rag_sufficient": True}}],
|
|
}
|
|
|
|
context, metadata = await rt._retrieve_rag_context(state)
|
|
|
|
assert context == ""
|
|
assert not rt.rag_service.calls
|
|
assert metadata["reason"] == "mcp_explicitly_sufficient"
|
|
|
|
|
|
def test_kbdb_build_messages_injects_grounding_policy():
|
|
rt = _runtime()
|
|
state = {
|
|
"user_text": "Como funciona?",
|
|
"sanitized_input": "Como funciona?",
|
|
"context": {},
|
|
"business_context": {},
|
|
}
|
|
messages = rt.build_messages(
|
|
state,
|
|
system_prompt="system",
|
|
mcp_results=[{"ok": True, "result": {"plano": "Controle"}}],
|
|
rag_context="",
|
|
rag_metadata={"provider": "kbdb", "enabled": True, "status": "empty", "document_count": 0},
|
|
)
|
|
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"
|