docs: 更新协议文档,删除废弃计划文档

- 更新 http-error-codes, user-points-chat-data-protocol
- 更新 divination-run-protocol, profile-protocol
- 删除废弃的后端和前端设计计划文档
This commit is contained in:
qzl
2026-04-08 17:23:02 +08:00
parent 49fc9a116f
commit e80a82bef4
57 changed files with 4117 additions and 2269 deletions
-14
View File
@@ -11,18 +11,10 @@ from schemas.agent.runtime_models import (
ToolAgentOutput,
ToolStatus,
WorkerAgentOutputLite,
WorkerAgentOutputRich,
resolve_worker_output_model,
)
from schemas.agent.system_agent import AgentType, SystemAgentLLMConfig
from schemas.agent.visibility import SystemVisibilityBit, VisibilityMask, bit_mask
from schemas.agent.ui_hints import (
UiHintAction,
UiHintIntent,
UiHintSection,
UiHintStatus,
UiHintsPayload,
)
__all__ = [
"AgentType",
@@ -35,14 +27,8 @@ __all__ = [
"SystemVisibilityBit",
"ToolAgentOutput",
"ToolStatus",
"UiHintAction",
"UiHintIntent",
"UiHintSection",
"UiHintStatus",
"UiHintsPayload",
"VisibilityMask",
"WorkerAgentOutputLite",
"WorkerAgentOutputRich",
"bit_mask",
"parse_forwarded_props_client_time",
"parse_forwarded_props_runtime_mode",
@@ -64,6 +64,7 @@ class ClientTimeContext(BaseModel):
class RuntimeMode(str, Enum):
CHAT = "chat"
FOLLOW_UP = "follow_up"
class ForwardedPropsPayload(BaseModel):
+15 -6
View File
@@ -5,7 +5,6 @@ from typing import Any, Literal
from pydantic import BaseModel, ConfigDict, Field
from schemas.agent.ui_hints import UiHintsPayload
from schemas.domain.divination import DerivedDivinationData
@@ -53,18 +52,28 @@ class WorkerAgentOutputLite(BaseModel):
error: ErrorInfo | None = None
class WorkerAgentOutputRich(WorkerAgentOutputLite):
ui_hints: UiHintsPayload | None = None
class FollowUpOutput(BaseModel):
model_config = ConfigDict(extra="forbid")
status: RunStatus = RunStatus.SUCCESS
answer: str = Field(min_length=1, max_length=4000)
error: ErrorInfo | None = None
class AgentOutput(WorkerAgentOutputRich):
class AgentOutput(WorkerAgentOutputLite):
model_config = ConfigDict(extra="forbid")
divination_derived: DerivedDivinationData | None = None
WorkerAgentOutput = WorkerAgentOutputLite | WorkerAgentOutputRich
WorkerAgentOutput = WorkerAgentOutputLite
RuntimeAgentOutput = AgentOutput | FollowUpOutput
def resolve_worker_output_model() -> type[WorkerAgentOutputLite]:
def resolve_worker_output_model(
*, runtime_mode: str
) -> type[WorkerAgentOutputLite | FollowUpOutput]:
normalized = runtime_mode.strip().lower()
if normalized == "follow_up":
return FollowUpOutput
return WorkerAgentOutputLite
-349
View File
@@ -1,349 +0,0 @@
"""
UiHints - 描述性 UI 提示
设计原则:
- 描述性而非渲染性: 告诉编译器“要展示什么”,而不是“如何渲染”
- 最小化 token: 保持字段简洁
- 可编译: 可机械转换为 UiSchemaRenderer
- 尽量无损: hints 中的主要内容字段应尽量被保留到 renderer 中
Version: 2.1
"""
from __future__ import annotations
from enum import Enum
import re
from typing import Any, ClassVar, Literal
from pydantic import BaseModel, ConfigDict, Field
from pydantic import field_validator
_NAVIGATION_PATH_PATTERN = re.compile(r"^/[A-Za-z0-9/_-]*$")
_NAVIGATION_PARAM_KEY_PATTERN = re.compile(r"^[A-Za-z][A-Za-z0-9_]{0,31}$")
_MAX_NAVIGATION_PARAMS = 8
# ============================================================
# Enums
# ============================================================
class UiHintStatus(str, Enum):
INFO = "info"
SUCCESS = "success"
WARNING = "warning"
ERROR = "error"
PENDING = "pending"
class UiHintIntent(str, Enum):
"""主要展示意图(弱提示,不应决定字段生死)"""
MESSAGE = "message" # 普通消息/说明
DATA = "data" # 数据/结果摘要
LIST = "list" # 列表为主
STATUS = "status" # 状态结果为主
FORM = "form" # 结构化内容(当前不表示真实输入表单)
MIXED = "mixed" # 混合内容
class UiHintActionStyle(str, Enum):
PRIMARY = "primary"
SECONDARY = "secondary"
GHOST = "ghost"
DANGER = "danger"
class UiHintTextFormat(str, Enum):
PLAIN = "plain"
MARKDOWN = "markdown"
class UiHintActionType(str, Enum):
NAVIGATION = "navigation"
URL = "url"
EVENT = "event"
TOOL = "tool"
COPY = "copy"
PAYLOAD = "payload"
class UiHintIconSource(str, Enum):
ICON = "icon"
EMOJI = "emoji"
URL = "url"
# ============================================================
# Base Config
# ============================================================
class UiHintBaseModel(BaseModel):
model_config: ClassVar[ConfigDict] = ConfigDict(
extra="forbid",
populate_by_name=True,
)
# ============================================================
# Action Targets
# ============================================================
class UiHintActionNavigation(UiHintBaseModel):
type: Literal["navigation"]
path: str = Field(..., description="Internal route path.")
params: dict[str, Any] | None = Field(default=None, description="Route params.")
@field_validator("path")
@classmethod
def validate_navigation_path(cls, value: str) -> str:
path = value.strip()
if not path:
raise ValueError("navigation path must not be empty")
if len(path) > 256:
raise ValueError("navigation path is too long")
if path.startswith("//") or "://" in path:
raise ValueError("navigation path must be internal")
if "?" in path or "#" in path:
raise ValueError("navigation path must not contain query or fragment")
if ":" in path:
raise ValueError("navigation path must be concrete without placeholders")
if _NAVIGATION_PATH_PATTERN.fullmatch(path) is None:
raise ValueError("navigation path contains unsupported characters")
return path
@field_validator("params")
@classmethod
def validate_navigation_params(
cls, value: dict[str, Any] | None
) -> dict[str, Any] | None:
if value is None:
return None
if len(value) > _MAX_NAVIGATION_PARAMS:
raise ValueError("navigation params exceed limit")
normalized: dict[str, Any] = {}
for key, param_value in value.items():
if _NAVIGATION_PARAM_KEY_PATTERN.fullmatch(key) is None:
raise ValueError("navigation param key is invalid")
if isinstance(param_value, (str, int, float, bool)):
normalized[key] = param_value
continue
raise ValueError("navigation params must be scalar")
return normalized
class UiHintActionUrl(UiHintBaseModel):
type: Literal["url"]
url: str = Field(..., description="External URL.")
target: Literal["_self", "_blank"] | None = Field(default=None)
class UiHintActionEvent(UiHintBaseModel):
type: Literal["event"]
event: str = Field(..., description="Frontend event name.")
payload: dict[str, Any] | None = Field(default=None)
class UiHintActionTool(UiHintBaseModel):
type: Literal["tool"]
tool_id: str = Field(alias="toolId", description="Tool identifier.")
params: dict[str, Any] | None = Field(default=None)
class UiHintActionCopy(UiHintBaseModel):
type: Literal["copy"]
content: str = Field(..., description="Content to copy.")
success_message: str | None = Field(alias="successMessage", default=None)
class UiHintActionPayload(UiHintBaseModel):
type: Literal["payload"]
payload: dict[str, Any] = Field(..., description="Structured payload.")
submit_to: str | None = Field(alias="submitTo", default=None)
UiHintActionTarget = (
UiHintActionNavigation
| UiHintActionUrl
| UiHintActionEvent
| UiHintActionTool
| UiHintActionCopy
| UiHintActionPayload
)
class UiHintAction(UiHintBaseModel):
label: str = Field(..., description="Button label.")
style: UiHintActionStyle | None = Field(default=None, description="Button style.")
disabled: bool = Field(default=False, description="Disabled state.")
action: UiHintActionTarget = Field(..., description="Action to execute.")
# ============================================================
# Small Descriptive Models
# ============================================================
class UiHintIcon(UiHintBaseModel):
source: UiHintIconSource = Field(default=UiHintIconSource.ICON)
value: str = Field(..., description="Icon identifier / emoji / url.")
color: str | None = Field(default=None)
size: int | None = Field(default=None)
class UiHintKvItem(UiHintBaseModel):
key: str = Field(..., description="Key identifier.")
label: str | None = Field(default=None, description="Display label.")
value: Any = Field(default=None, description="Value.")
copyable: bool = Field(default=False, description="Allow copy.")
class UiHintListItem(UiHintBaseModel):
id: str | None = Field(default=None)
title: str = Field(..., description="Item title.")
subtitle: str | None = Field(default=None)
description: str | None = Field(default=None)
icon: UiHintIcon | None = Field(default=None)
status: UiHintStatus | None = Field(default=None)
actions: list[UiHintAction] = Field(default_factory=list)
@field_validator("status", mode="before")
@classmethod
def normalize_status(cls, value: object) -> object:
if value is None:
return None
if isinstance(value, dict):
status_type = value.get("type")
if isinstance(status_type, str):
return status_type
status_value = value.get("status")
if isinstance(status_value, str):
return status_value
return value
class UiHintSection(UiHintBaseModel):
title: str | None = Field(default=None, description="Section title.")
description: str | None = Field(default=None, description="Section description.")
icon: UiHintIcon | None = Field(default=None, description="Section icon.")
content: str | None = Field(default=None, description="Main text content.")
content_format: UiHintTextFormat = Field(
default=UiHintTextFormat.PLAIN,
alias="contentFormat",
description="Section content text format.",
)
items: list[UiHintKvItem] = Field(default_factory=list, description="KV items.")
list_items: list[UiHintListItem] = Field(
default_factory=list,
alias="listItems",
description="List items.",
)
actions: list[UiHintAction] = Field(default_factory=list, description="Actions.")
# ============================================================
# Root Payload
# ============================================================
class UiHintsPayload(UiHintBaseModel):
"""
描述性 UI 提示
设计目标:
- agent 输出尽可能短
- 不表达布局细节
- 编译器负责转换为完整 UiSchemaRenderer
"""
model_config: ClassVar[ConfigDict] = ConfigDict(
extra="forbid",
populate_by_name=True,
json_schema_extra={
"examples": [
{
"intent": "status",
"status": "success",
"title": "日程已创建",
"body": "本次创建已成功完成。",
"items": [
{"key": "title", "label": "主题", "value": "Q1 规划会议"},
{"key": "time", "label": "时间", "value": "2026-03-15 14:00"},
],
"actions": [
{
"label": "查看详情",
"style": "primary",
"action": {
"type": "navigation",
"path": "/calendar/evt_123",
},
},
{
"label": "删除",
"style": "danger",
"action": {
"type": "tool",
"toolId": "calendar.delete",
"params": {"eventId": "evt_123"},
},
},
],
}
]
},
)
version: str = Field(default="2.1")
intent: UiHintIntent = Field(
default=UiHintIntent.MESSAGE,
description="Primary display intent.",
)
status: UiHintStatus = Field(
default=UiHintStatus.INFO,
description="Overall status.",
)
title: str | None = Field(default=None, description="Top-level title.")
description: str | None = Field(default=None, description="Top-level description.")
body: str | None = Field(default=None, description="Top-level main body text.")
body_format: UiHintTextFormat = Field(
default=UiHintTextFormat.PLAIN,
alias="bodyFormat",
description="Body text format.",
)
items: list[UiHintKvItem] = Field(
default_factory=list,
description="Top-level key-value items.",
)
list_items: list[UiHintListItem] = Field(
default_factory=list,
alias="listItems",
description="Top-level list items.",
)
sections: list[UiHintSection] = Field(
default_factory=list,
description="Grouped sections.",
)
actions: list[UiHintAction] = Field(
default_factory=list,
description="Top-level actions.",
)
icon: UiHintIcon | None = Field(
default=None,
description="Top-level icon.",
)
meta: dict[str, Any] = Field(
default_factory=dict,
description="Extra meta, e.g. requestId/toolId/traceId/userId.",
)
+3 -2
View File
@@ -6,7 +6,7 @@ from typing import Any, ClassVar
from uuid import UUID
from pydantic import BaseModel, ConfigDict, Field
from schemas.agent.runtime_models import AgentOutput
from schemas.agent.runtime_models import AgentOutput, FollowUpOutput
from ..agent import AgentType, ToolAgentOutput
@@ -25,7 +25,7 @@ class AgentChatMessageMetadata(BaseModel):
agent_type: AgentType | None = None
user_message_attachments: list[UserMessageAttachment] | None = None
tool_agent_output: ToolAgentOutput | None = None
agent_output: AgentOutput | None = None
agent_output: AgentOutput | FollowUpOutput | None = None
class AgentChatMessage(BaseModel):
@@ -34,6 +34,7 @@ class AgentChatMessage(BaseModel):
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
id: UUID
session_id: UUID
seq: int
role: str
content: str