feat(backend): add ontology and orchestrator API endpoints
New endpoints: - server/src/app/api/v1/endpoints/ontology.py: ontology API - server/src/app/api/v1/endpoints/orchestrator.py: orchestrator API New schemas: - server/src/app/schemas/ontology.py: ontology data schemas - server/src/app/schemas/orchestrator.py: orchestrator data schemas - server/src/app/schemas/user_agent.py: user agent data schemas New services: - server/src/app/services/ontology.py: ontology business logic - server/src/app/services/orchestrator.py: orchestrator business logic - server/src/app/services/runtime_chat.py: runtime chat service - server/src/app/services/user_agent.py: user agent service New tests: - server/tests/test_ontology_service.py - server/tests/test_orchestrator_service.py - server/tests/test_user_agent_service.py
This commit is contained in:
116
server/src/app/schemas/ontology.py
Normal file
116
server/src/app/schemas/ontology.py
Normal file
@@ -0,0 +1,116 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
OntologyScenario = Literal[
|
||||
"expense",
|
||||
"accounts_receivable",
|
||||
"accounts_payable",
|
||||
"knowledge",
|
||||
"unknown",
|
||||
]
|
||||
OntologyIntent = Literal["query", "explain", "compare", "risk_check", "draft", "operate"]
|
||||
OntologyPermissionLevel = Literal["read", "draft_write", "approval_required", "forbidden"]
|
||||
OntologyParseStrategy = Literal["llm_primary", "rule_fallback"]
|
||||
|
||||
|
||||
class OntologyEntity(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
type: str = Field(description="业务对象类型,例如 employee / customer / vendor。")
|
||||
value: str = Field(description="从原始问题中提取的对象值。")
|
||||
normalized_value: str = Field(description="标准化后的对象值。")
|
||||
role: str = Field(default="target", description="对象角色,例如 target / filter / threshold。")
|
||||
confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="字段级置信度。")
|
||||
|
||||
|
||||
class OntologyTimeRange(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
raw: str = Field(default="", description="命中的原始时间表达。")
|
||||
start_date: str | None = Field(default=None, description="ISO 格式起始日期。")
|
||||
end_date: str | None = Field(default=None, description="ISO 格式结束日期。")
|
||||
granularity: str | None = Field(
|
||||
default=None,
|
||||
description="day / week / month / quarter / year。",
|
||||
)
|
||||
|
||||
|
||||
class OntologyMetric(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
name: str = Field(description="指标名,例如 amount / count / overdue。")
|
||||
aggregation: str | None = Field(default=None, description="sum / count / max 等聚合口径。")
|
||||
unit: str | None = Field(default=None, description="金额、数量等单位。")
|
||||
sort: str | None = Field(default=None, description="asc / desc 排序方向。")
|
||||
top_n: int | None = Field(default=None, ge=1, description="Top N 口径。")
|
||||
|
||||
|
||||
class OntologyConstraint(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
field: str = Field(description="约束字段,例如 department / status / amount。")
|
||||
operator: str = Field(description="操作符,例如 = / > / < / desc。")
|
||||
value: str | int | float | bool = Field(description="约束值。")
|
||||
currency: str | None = Field(default=None, description="金额类约束使用的币种。")
|
||||
|
||||
|
||||
class OntologyPermission(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
level: OntologyPermissionLevel = Field(default="read", description="动作权限等级。")
|
||||
allowed: bool = Field(default=True, description="是否可直接执行当前动作。")
|
||||
reason: str = Field(default="", description="权限判断原因。")
|
||||
|
||||
|
||||
class OntologyFieldError(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
field: str = Field(description="发生问题的字段。")
|
||||
code: str = Field(description="错误码。")
|
||||
message: str = Field(description="面向前端展示的说明。")
|
||||
|
||||
|
||||
class OntologyParseRequest(BaseModel):
|
||||
query: str = Field(min_length=1, description="自然语言问题。")
|
||||
user_id: str | None = Field(default=None, description="当前请求用户 ID。")
|
||||
context_json: dict[str, Any] = Field(
|
||||
default_factory=dict,
|
||||
description="用户上下文,例如角色、部门、是否管理员。",
|
||||
)
|
||||
|
||||
|
||||
class OntologyParseResult(BaseModel):
|
||||
scenario: OntologyScenario = Field(default="unknown", description="业务场景。")
|
||||
intent: OntologyIntent = Field(default="query", description="用户意图。")
|
||||
entities: list[OntologyEntity] = Field(default_factory=list, description="业务对象列表。")
|
||||
time_range: OntologyTimeRange = Field(
|
||||
default_factory=OntologyTimeRange,
|
||||
description="时间范围。",
|
||||
)
|
||||
metrics: list[OntologyMetric] = Field(default_factory=list, description="指标解析结果。")
|
||||
constraints: list[OntologyConstraint] = Field(
|
||||
default_factory=list,
|
||||
description="过滤、阈值、排序等约束。",
|
||||
)
|
||||
risk_flags: list[str] = Field(default_factory=list, description="风险信号列表。")
|
||||
permission: OntologyPermission = Field(
|
||||
default_factory=OntologyPermission,
|
||||
description="权限结果。",
|
||||
)
|
||||
confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="整体置信度。")
|
||||
missing_slots: list[str] = Field(default_factory=list, description="继续处理所缺少的关键槽位。")
|
||||
ambiguity: list[str] = Field(default_factory=list, description="当前识别中的潜在歧义。")
|
||||
parse_strategy: OntologyParseStrategy = Field(
|
||||
default="rule_fallback",
|
||||
description="本次语义解析使用的主策略。",
|
||||
)
|
||||
clarification_required: bool = Field(default=False, description="是否需要追问。")
|
||||
clarification_question: str | None = Field(default=None, description="推荐追问问题。")
|
||||
run_id: str = Field(description="关联的 AgentRun.run_id。")
|
||||
field_errors: list[OntologyFieldError] = Field(
|
||||
default_factory=list,
|
||||
description="字段级错误或提示。",
|
||||
)
|
||||
46
server/src/app/schemas/orchestrator.py
Normal file
46
server/src/app/schemas/orchestrator.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
OrchestratorSource = Literal["user_message", "schedule", "system_event"]
|
||||
OrchestratorAgent = Literal["user_agent", "hermes"]
|
||||
OrchestratorStatus = Literal["succeeded", "blocked", "failed"]
|
||||
|
||||
|
||||
class OrchestratorRequest(BaseModel):
|
||||
source: OrchestratorSource = Field(description="请求来源。")
|
||||
user_id: str | None = Field(default=None, description="当前用户 ID,任务触发可为空。")
|
||||
message: str | None = Field(default=None, description="用户消息或任务描述。")
|
||||
task_id: str | None = Field(default=None, description="任务资产 ID,schedule 触发时优先使用。")
|
||||
context_json: dict[str, Any] = Field(
|
||||
default_factory=dict,
|
||||
description="用户上下文、测试开关或调用方附加信息。",
|
||||
)
|
||||
|
||||
|
||||
class OrchestratorTraceSummary(BaseModel):
|
||||
scenario: str = Field(description="语义场景。")
|
||||
intent: str = Field(description="语义意图。")
|
||||
tool_count: int = Field(default=0, ge=0, description="工具调用总数。")
|
||||
failed_tool_count: int = Field(default=0, ge=0, description="失败工具调用数量。")
|
||||
selected_capability_codes: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="本次路由命中的能力编码。",
|
||||
)
|
||||
degraded: bool = Field(default=False, description="是否发生降级。")
|
||||
|
||||
|
||||
class OrchestratorResponse(BaseModel):
|
||||
run_id: str = Field(description="本次运行的唯一 run_id。")
|
||||
selected_agent: OrchestratorAgent | None = Field(
|
||||
default=None,
|
||||
description="最终路由到的下游 Agent。",
|
||||
)
|
||||
route_reason: str = Field(description="路由原因摘要。")
|
||||
permission_level: str = Field(description="权限级别。")
|
||||
status: OrchestratorStatus = Field(description="最终运行状态。")
|
||||
result: dict[str, Any] = Field(default_factory=dict, description="对前端可直接展示的最小结果。")
|
||||
requires_confirmation: bool = Field(default=False, description="是否需要用户或管理员确认。")
|
||||
trace_summary: OrchestratorTraceSummary = Field(description="简化后的 Trace 摘要。")
|
||||
58
server/src/app/schemas/user_agent.py
Normal file
58
server/src/app/schemas/user_agent.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.schemas.ontology import OntologyParseResult
|
||||
|
||||
UserAgentCitationType = Literal["rule", "knowledge"]
|
||||
|
||||
|
||||
class UserAgentCitation(BaseModel):
|
||||
source_type: UserAgentCitationType = Field(description="引用来源类型。")
|
||||
code: str = Field(description="来源编码。")
|
||||
title: str = Field(description="来源标题。")
|
||||
version: str | None = Field(default=None, description="引用版本。")
|
||||
updated_at: str | None = Field(default=None, description="来源更新时间。")
|
||||
excerpt: str | None = Field(default=None, description="面向用户展示的引用摘要。")
|
||||
|
||||
|
||||
class UserAgentSuggestedAction(BaseModel):
|
||||
label: str = Field(description="建议动作文案。")
|
||||
action_type: str = Field(description="动作类型,例如 open_detail / create_draft。")
|
||||
description: str = Field(default="", description="动作说明。")
|
||||
|
||||
|
||||
class UserAgentDraftPayload(BaseModel):
|
||||
draft_type: str = Field(description="草稿类型。")
|
||||
title: str = Field(description="草稿标题。")
|
||||
body: str = Field(description="草稿正文。")
|
||||
confirmation_required: bool = Field(default=True, description="是否需要人工确认。")
|
||||
|
||||
|
||||
class UserAgentRequest(BaseModel):
|
||||
run_id: str = Field(description="关联的 AgentRun.run_id。")
|
||||
user_id: str | None = Field(default=None, description="当前请求用户 ID。")
|
||||
message: str = Field(description="原始用户问题。")
|
||||
ontology: OntologyParseResult = Field(description="语义解析结果。")
|
||||
context_json: dict[str, Any] = Field(default_factory=dict, description="附加上下文。")
|
||||
tool_payload: dict[str, Any] = Field(default_factory=dict, description="工具返回的原始结果。")
|
||||
selected_capability_codes: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="本次命中的能力编码。",
|
||||
)
|
||||
degraded: bool = Field(default=False, description="当前是否发生降级。")
|
||||
requires_confirmation: bool = Field(default=False, description="是否要求确认。")
|
||||
|
||||
|
||||
class UserAgentResponse(BaseModel):
|
||||
answer: str = Field(description="面向用户展示的自然语言回答。")
|
||||
citations: list[UserAgentCitation] = Field(default_factory=list, description="规则或知识引用。")
|
||||
suggested_actions: list[UserAgentSuggestedAction] = Field(
|
||||
default_factory=list,
|
||||
description="建议的下一步动作。",
|
||||
)
|
||||
draft_payload: UserAgentDraftPayload | None = Field(default=None, description="可选草稿内容。")
|
||||
risk_flags: list[str] = Field(default_factory=list, description="本次回答关联的风险标签。")
|
||||
requires_confirmation: bool = Field(default=False, description="是否需要人工确认。")
|
||||
Reference in New Issue
Block a user