| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- import json
- from datetime import UTC, datetime
- import pytest
- from zbt.commands.seed import build_seed_manifest
- from zbt.core.errors import AppError
- from zbt.domains.agent.tools import build_agent_tool_registry
- from zbt.domains.catalog.repository import InMemoryCatalogRepository
- from zbt.domains.catalog.service import ProductCatalogService
- from zbt.domains.enrollment.repository import InMemoryEnrollmentRepository
- from zbt.domains.enrollment.service import EnrollmentService
- from zbt.domains.identity.models import AdminUser, H5User
- from zbt.harness.policy import HarnessPolicyEngine, ToolPolicy
- from zbt.harness.registries import PersonaRegistry, PromptRegistry, SkillRegistry
- from zbt.harness.schemas import ProductRecommendationsBlock
- from zbt.harness.tooling import ToolExecutionContext
- NOW = datetime(2026, 7, 26, 8, tzinfo=UTC)
- def test_prompt_skill_and_persona_registries_form_a_closed_contract() -> None:
- prompts = PromptRegistry()
- skills = SkillRegistry()
- personas = PersonaRegistry()
- assert prompts.ids() == ("customer", "operation")
- assert skills.ids() == (
- "enrollment-guidance",
- "insurance-consultation",
- "operation-insights",
- )
- customer = personas.get("customer")
- operation = personas.get("operation")
- assert customer.prompt_id == "customer"
- assert "calculate_insurance_quote" in customer.tool_names
- assert operation.prompt_id == "operation"
- assert "get_attribution_performance" in operation.tool_names
- for skill_id in customer.skill_ids + operation.skill_ids:
- skill = skills.get(skill_id)
- assert skill.instructions
- assert skill.source.name == "SKILL.md"
- def test_operation_persona_cannot_authorize_a_draft_tool() -> None:
- policy = HarnessPolicyEngine()
- with pytest.raises(AppError) as error:
- policy.authorize(
- persona="operation",
- allowed_by_persona=("calculate_insurance_quote",),
- tool_name="calculate_insurance_quote",
- policy=ToolPolicy(personas=("operation",), effect="draft"),
- permissions=("*",),
- )
- assert error.value.code == "AGENT_OPERATION_READ_ONLY"
- def test_customer_tools_use_real_catalog_and_deterministic_quote_service() -> None:
- catalog_repository = InMemoryCatalogRepository()
- manifest = build_seed_manifest()
- for product in manifest.products:
- catalog_repository.save_product(product)
- for version in manifest.versions:
- catalog_repository.save_version(version)
- catalog = ProductCatalogService(catalog_repository, lambda: NOW)
- enrollment = EnrollmentService(
- InMemoryEnrollmentRepository(),
- catalog,
- lambda: NOW,
- "x" * 32,
- )
- registry = build_agent_tool_registry(catalog, enrollment)
- user = H5User(
- id="01H5USER000000000000000001",
- mobile="18800000001",
- mobile_masked="188****0001",
- display_name="体验用户",
- status="ACTIVE",
- created_at=NOW,
- )
- context = ToolExecutionContext(persona="customer", h5_user=user)
- tools = registry.build(
- names=("list_available_products", "calculate_insurance_quote"),
- context=context,
- policy_engine=HarnessPolicyEngine(),
- )
- by_name = {tool.name: tool for tool in tools}
- products = json.loads(by_name["list_available_products"].invoke({"category": "MEDICAL"}))
- quote = json.loads(
- by_name["calculate_insurance_quote"].invoke(
- {
- "product_code": "MED-SENIOR",
- "plan_code": "SENIOR_STANDARD",
- "age": 65,
- "region_code": "510100",
- "occupation_code": "GENERAL",
- "relationship": "PARENT",
- }
- )
- )
- assert products["data"]["total"] == 3
- assert quote["data"]["premium_cents"] == 19900
- assert quote["blocks"][0]["type"] == "quote"
- assert quote["actions"][0]["type"] == "open_enrollment"
- assert [execution.name for execution in context.executions] == [
- "list_available_products",
- "calculate_insurance_quote",
- ]
- def test_product_recommendation_contract_accepts_enriched_plan_data() -> None:
- catalog_repository = InMemoryCatalogRepository()
- manifest = build_seed_manifest()
- for product in manifest.products:
- catalog_repository.save_product(product)
- for version in manifest.versions:
- catalog_repository.save_version(version)
- catalog = ProductCatalogService(catalog_repository, lambda: NOW)
- enrollment = EnrollmentService(
- InMemoryEnrollmentRepository(),
- catalog,
- lambda: NOW,
- "x" * 32,
- )
- registry = build_agent_tool_registry(catalog, enrollment)
- context = ToolExecutionContext(
- persona="customer",
- h5_user=H5User(
- id="01H5USER000000000000000001",
- mobile="18800000001",
- mobile_masked="188****0001",
- display_name="体验用户",
- status="ACTIVE",
- created_at=NOW,
- ),
- )
- tool = registry.build(
- names=("list_available_products",),
- context=context,
- policy_engine=HarnessPolicyEngine(),
- )[0]
- products = json.loads(tool.invoke({"category": "MEDICAL"}))
- product = next(
- item
- for item in products["data"]["items"]
- if item["product_code"] == "MED-SENIOR"
- )
- recommendation = ProductRecommendationsBlock(
- items=[
- {
- "product_id": product["product_id"],
- "product_code": product["product_code"],
- "name": product["name"],
- "category": product["category"],
- "summary": product["summary"],
- "product_version_id": product["product_version_id"],
- "plans": product["plans"],
- }
- ],
- )
- plan = recommendation.items[0].plans[0]
- assert plan.premium_cents == 19900
- assert plan.coverage_amount_cents == 100_000_000
- assert (plan.min_age, plan.max_age) == (50, 75)
- def test_operation_tool_permissions_filter_unavailable_tools() -> None:
- catalog_repository = InMemoryCatalogRepository()
- catalog = ProductCatalogService(catalog_repository, lambda: NOW)
- enrollment = EnrollmentService(
- InMemoryEnrollmentRepository(),
- catalog,
- lambda: NOW,
- "x" * 32,
- )
- registry = build_agent_tool_registry(catalog, enrollment)
- admin = AdminUser(
- id="01ADMIN0000000000000000001",
- username="sales_a",
- password_hash="unused",
- display_name="业务员A",
- status="ACTIVE",
- roles=("SALESPERSON",),
- permissions=("dashboard:read", "attribution:read"),
- data_scope="SELF",
- )
- tools = registry.build(
- names=PersonaRegistry().get("operation").tool_names,
- context=ToolExecutionContext(persona="operation", admin_user=admin),
- policy_engine=HarnessPolicyEngine(),
- )
- assert {tool.name for tool in tools} == {
- "get_operation_overview",
- "get_attribution_performance",
- "list_available_products",
- }
|