mirror of
https://github.com/hoshikawa2/agent_platform_oci.git
synced 2026-11-06 03:14:12 +00:00
new feature: Multi-Intent
This commit is contained in:
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.
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.
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.
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.
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.
Binary file not shown.
Binary file not shown.
@@ -40,6 +40,20 @@ def test_transactional_intent_is_primary_even_when_mentioned_second():
|
||||
assert [item.intent for item in plan.operations] == ["cancel", "invoice"]
|
||||
|
||||
|
||||
def test_sentence_boundary_does_not_leak_previous_intent_into_knowledge_query():
|
||||
planner = MultiIntentPlanner([
|
||||
IntentDefinition(name="invoice", agent="billing", priority=20, keywords=["fatura"]),
|
||||
IntentDefinition(name="knowledge", agent="support", priority=30, keywords=["tarifação"]),
|
||||
])
|
||||
plan = planner.plan(
|
||||
"quero ver minha fatura e o valor. depois gostaria de saber como funciona a tarifação do plano"
|
||||
)
|
||||
assert plan is not None
|
||||
knowledge = next(item for item in plan.operations if item.intent == "knowledge")
|
||||
assert knowledge.source_text == "depois gostaria de saber como funciona a tarifação do plano"
|
||||
assert not knowledge.source_text.startswith("valor")
|
||||
|
||||
|
||||
def test_does_not_plan_single_intent_or_invent_unknown_intent():
|
||||
assert _planner().plan("quero cancelar pedido") is None
|
||||
assert _planner().plan("quero cancelar pedido e saber o clima") is None
|
||||
|
||||
@@ -12,6 +12,8 @@ async def test_secondary_topic_does_not_inherit_primary_transaction_state():
|
||||
return {
|
||||
"answer": "Aqui está a sua segunda via.",
|
||||
"mcp_results": [{"tool_name": "consultar_faturas", "ok": True}],
|
||||
"rag": {"provider": "kbdb", "attempted": True, "status": "executed", "document_count": 1},
|
||||
"rag_context": "Documento recuperado da base de conhecimento.",
|
||||
}
|
||||
|
||||
state = {
|
||||
@@ -59,3 +61,5 @@ async def test_secondary_topic_does_not_inherit_primary_transaction_state():
|
||||
{"tool_name": "consultar_vas"},
|
||||
{"tool_name": "consultar_faturas", "ok": True},
|
||||
]
|
||||
assert result.rag_results[0]["metadata"]["provider"] == "kbdb"
|
||||
assert result.rag_results[0]["context"] == "Documento recuperado da base de conhecimento."
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from types import SimpleNamespace
|
||||
from contextlib import contextmanager
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -16,6 +17,7 @@ def _settings(**overrides):
|
||||
KBDB_NODE_MAX_RELATED=8, KBDB_GRAPH_CROSS_REF=False,
|
||||
KBDB_MAX_CROSS_REF_HOPS=1, KBDB_DOCUMENT_TYPE="customer_safe",
|
||||
KBDB_METADATA_JSON=None, KBDB_MIN_SCORE=None,
|
||||
KBDB_IDENTIFY_DOCUMENT=True, KBDB_STORE_QUERY=True, KBDB_IDENTIFY_TOP_N=3,
|
||||
)
|
||||
data.update(overrides)
|
||||
return SimpleNamespace(**data)
|
||||
@@ -115,3 +117,69 @@ def test_kbdb_does_not_fallback_to_framework_adb_credentials():
|
||||
)
|
||||
with pytest.raises(RuntimeError, match="KBDB_DB_USER"):
|
||||
KbdbRagService(settings)
|
||||
|
||||
|
||||
def test_kbdb_discovers_number_flags_and_clob_output(monkeypatch):
|
||||
import sys
|
||||
from agent_framework.rag.kbdb_service import KbdbRagService
|
||||
|
||||
captured = {}
|
||||
|
||||
class Var:
|
||||
def __init__(self, value=None):
|
||||
self.value = value
|
||||
|
||||
def getvalue(self):
|
||||
return self.value
|
||||
|
||||
class Cursor:
|
||||
def execute(self, statement):
|
||||
captured["signature_query"] = statement
|
||||
|
||||
def fetchall(self):
|
||||
names = [
|
||||
("P_SEARCH_TYPE", "VARCHAR2"), ("P_QUERY", "VARCHAR2"),
|
||||
("P_TOP_K", "NUMBER"), ("P_NODE_EXPANSION", "NUMBER"),
|
||||
("P_NODE_MAX_RELATED", "NUMBER"), ("P_DOCUMENT_TYPE", "VARCHAR2"),
|
||||
("P_METADATA", "CLOB"), ("P_IDENTIFY_DOCUMENT", "NUMBER"),
|
||||
("P_STORE_QUERY", "NUMBER"), ("P_IDENTIFY_TOP_N", "NUMBER"),
|
||||
("P_RESULT", "CLOB"),
|
||||
]
|
||||
return [
|
||||
(name, index, "OUT" if index == 11 else "IN", data_type, None)
|
||||
for index, (name, data_type) in enumerate(names, start=1)
|
||||
]
|
||||
|
||||
def var(self, data_type, size=None):
|
||||
captured["out_type"] = data_type
|
||||
return Var('{"units": [], "seeds": []}')
|
||||
|
||||
def callproc(self, name, binds):
|
||||
captured["procedure"] = name
|
||||
captured["binds"] = binds
|
||||
|
||||
class Connection:
|
||||
def cursor(self):
|
||||
return Cursor()
|
||||
|
||||
class FakeOracleDb:
|
||||
DB_TYPE_JSON = "JSON"
|
||||
DB_TYPE_CLOB = "CLOB"
|
||||
DB_TYPE_VARCHAR = "VARCHAR"
|
||||
|
||||
service = KbdbRagService(_settings())
|
||||
|
||||
@contextmanager
|
||||
def connect():
|
||||
yield Connection()
|
||||
|
||||
monkeypatch.setitem(sys.modules, "oracledb", FakeOracleDb)
|
||||
monkeypatch.setattr(service, "_connect", connect)
|
||||
|
||||
result = service._search_sync("como funciona?", 5)
|
||||
|
||||
assert result == {"units": [], "seeds": []}
|
||||
assert captured["procedure"] == "PKG_KB_SERVING.search_knowledge_base"
|
||||
assert captured["binds"][3] == 1
|
||||
assert captured["binds"][7:10] == [1, 1, 3]
|
||||
assert captured["out_type"] == "CLOB"
|
||||
|
||||
@@ -13,7 +13,8 @@ def test_all_tuning_backend_graphs_support_pending_topics():
|
||||
assert '"pending_topics": drained.pending_topics' in source, path
|
||||
assert '"handled_topics": drained.handled_topics' in source, path
|
||||
assert '"mcp_results": drained.mcp_results' in source, path
|
||||
assert '"evidence": drained.mcp_results' in source, path
|
||||
assert '"rag_results": drained.rag_results' in source, path
|
||||
assert 'list(drained.mcp_results) + list(drained.rag_results)' in source, path
|
||||
|
||||
|
||||
def test_all_tuning_backend_states_expose_pending_topics():
|
||||
@@ -23,6 +24,7 @@ def test_all_tuning_backend_states_expose_pending_topics():
|
||||
source = path.read_text(encoding="utf-8")
|
||||
assert "pending_topics: list[dict[str, Any]]" in source, path
|
||||
assert "handled_topics: list[dict[str, Any]]" in source, path
|
||||
assert "rag_results: list[dict[str, Any]]" in source, path
|
||||
|
||||
|
||||
def test_canonical_templates_delegate_drain_to_framework_helper():
|
||||
@@ -36,7 +38,53 @@ def test_canonical_templates_delegate_drain_to_framework_helper():
|
||||
assert '"pending_topics": drained.pending_topics' in graph_source
|
||||
assert '"handled_topics": drained.handled_topics' in graph_source
|
||||
assert '"mcp_results": drained.mcp_results' in graph_source
|
||||
assert '"evidence": drained.mcp_results' in graph_source
|
||||
assert '"rag_results": drained.rag_results' in graph_source
|
||||
assert 'list(drained.mcp_results) + list(drained.rag_results)' in graph_source
|
||||
assert "MultiIntentPlanner.public_messages" not in graph_source
|
||||
assert "pending_topics: list[dict[str, Any]]" in state_source
|
||||
assert "handled_topics: list[dict[str, Any]]" in state_source
|
||||
assert "rag_results: list[dict[str, Any]]" in state_source
|
||||
|
||||
|
||||
def test_all_rag_agents_expose_metadata_and_context_as_state_evidence():
|
||||
roots = [ROOT / "templates", ROOT / "Tuning-Performance"]
|
||||
agents = sorted(
|
||||
path
|
||||
for root in roots
|
||||
for path in root.glob("**/app/agents/*.py")
|
||||
if '"rag": rag_metadata' in path.read_text(encoding="utf-8")
|
||||
)
|
||||
assert agents
|
||||
for path in agents:
|
||||
source = path.read_text(encoding="utf-8")
|
||||
assert '"rag_context": rag_context' in source, path
|
||||
assert '"rag_results": [{' in source, path
|
||||
|
||||
|
||||
def test_templates_and_tuning_expose_rag_results_in_api_and_telemetry():
|
||||
roots = [ROOT / "templates", ROOT / "Tuning-Performance"]
|
||||
mains = sorted(path for root in roots for path in root.glob("**/app/main.py"))
|
||||
graphs = sorted(path for root in roots for path in root.glob("**/app/workflows/agent_graph.py"))
|
||||
assert mains and graphs
|
||||
for path in mains:
|
||||
assert '"rag_results": result.get("rag_results")' in path.read_text(encoding="utf-8"), path
|
||||
for path in graphs:
|
||||
assert '"rag_results": state.get("rag_results", [])' in path.read_text(encoding="utf-8"), path
|
||||
|
||||
|
||||
def test_all_tuning_envs_expose_complete_kbdb_configuration():
|
||||
envs = sorted((ROOT / "Tuning-Performance").glob("**/.env.example"))
|
||||
assert envs
|
||||
required = (
|
||||
"RAG_PROVIDER=standard",
|
||||
"KBDB_DB_USER=",
|
||||
"KBDB_DB_DSN=",
|
||||
"KBDB_IDENTIFY_DOCUMENT=true",
|
||||
"KBDB_STORE_QUERY=true",
|
||||
"KBDB_IDENTIFY_TOP_N=3",
|
||||
"KBDB_GROUNDED_ONLY=true",
|
||||
)
|
||||
for path in envs:
|
||||
source = path.read_text(encoding="utf-8")
|
||||
for setting in required:
|
||||
assert setting in source, (path, setting)
|
||||
|
||||
Reference in New Issue
Block a user