schemas.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. """Agent 与双端前端之间的稳定结构化协议。"""
  2. from dataclasses import dataclass, field
  3. from typing import Annotated, Any, Literal
  4. from pydantic import BaseModel, ConfigDict, Field
  5. class StrictModel(BaseModel):
  6. """拒绝未声明字段,防止模型生成内容悄悄穿透协议。"""
  7. model_config = ConfigDict(extra="forbid")
  8. class ProductPlanView(StrictModel):
  9. id: str
  10. code: str
  11. name: str
  12. summary: str = ""
  13. premium_cents: int = Field(ge=0)
  14. coverage_amount_cents: int = Field(ge=0)
  15. min_age: int = Field(ge=0, le=120)
  16. max_age: int = Field(ge=0, le=120)
  17. class ProductView(StrictModel):
  18. product_id: str
  19. product_code: str
  20. name: str
  21. category: str
  22. summary: str
  23. product_version_id: str
  24. plans: list[ProductPlanView]
  25. class ProductRecommendationsBlock(StrictModel):
  26. type: Literal["product_recommendations"] = "product_recommendations"
  27. version: Literal["1.0"] = "1.0"
  28. title: str = "为你匹配的保障方案"
  29. items: list[ProductView] = Field(max_length=2)
  30. class QuoteView(StrictModel):
  31. eligible: bool
  32. quote_id: str | None = None
  33. product_id: str | None = None
  34. product_name: str
  35. plan_id: str | None = None
  36. plan_name: str
  37. premium_cents: int | None = None
  38. currency: str = "CNY"
  39. expires_at: str | None = None
  40. reason: str | None = None
  41. class QuoteBlock(StrictModel):
  42. type: Literal["quote"] = "quote"
  43. version: Literal["1.0"] = "1.0"
  44. title: str = "保费测算结果"
  45. quote: QuoteView
  46. class BusinessListBlock(StrictModel):
  47. type: Literal["business_list"] = "business_list"
  48. version: Literal["1.0"] = "1.0"
  49. title: str
  50. entity: Literal["order", "policy", "product", "attribution"]
  51. items: list[dict[str, Any]]
  52. total: int = Field(ge=0)
  53. class MetricItem(StrictModel):
  54. label: str
  55. value: int | float | str
  56. unit: str = ""
  57. trend: str | None = None
  58. class MetricsBlock(StrictModel):
  59. type: Literal["metrics"] = "metrics"
  60. version: Literal["1.0"] = "1.0"
  61. title: str
  62. items: list[MetricItem]
  63. UiBlock = Annotated[
  64. ProductRecommendationsBlock | QuoteBlock | BusinessListBlock | MetricsBlock,
  65. Field(discriminator="type"),
  66. ]
  67. class AgentAction(StrictModel):
  68. """前端可执行动作;类型和负载都由 Harness 控制。"""
  69. type: Literal[
  70. "open_enrollment",
  71. "open_orders",
  72. "open_policies",
  73. "open_admin_orders",
  74. "open_admin_policies",
  75. "open_attribution",
  76. "none",
  77. ]
  78. label: str
  79. payload: dict[str, Any] = Field(default_factory=dict)
  80. confirmation_required: bool = False
  81. class AgentStructuredOutput(StrictModel):
  82. """模型最终输出;业务数据必须来自 Tool,不允许模型自行填充。"""
  83. message: str = Field(min_length=1, max_length=1200)
  84. selected_product_codes: list[str] = Field(default_factory=list, max_length=2)
  85. suggested_action: AgentAction | None = None
  86. class HarnessToolResult(StrictModel):
  87. """单个 Tool 的统一结果包络。"""
  88. summary: str
  89. data: dict[str, Any] = Field(default_factory=dict)
  90. blocks: list[UiBlock] = Field(default_factory=list)
  91. actions: list[AgentAction] = Field(default_factory=list)
  92. @dataclass(frozen=True)
  93. class AgentReply:
  94. """Agent Kernel 对应用层返回的结果。
  95. ``cards`` 沿用第一版 H5 字段名,内容已经升级为受校验的 UI Block。
  96. """
  97. text: str
  98. cards: list[dict[str, Any]]
  99. trace_id: str | None
  100. trace_url: str | None = None
  101. actions: list[dict[str, Any]] = field(default_factory=list)
  102. invoked_tools: tuple[str, ...] = ()