84 lines
2.6 KiB
Python
84 lines
2.6 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from ..schemas import TaskBreakdownBusinessObject, TaskBreakdownPayload
|
|
from .task_persistence_core import SkillTagResolution, resolve_skill_tag_ids_by_names
|
|
from .task_persistence_core.shared import AsyncSession
|
|
|
|
|
|
def normalize_task_breakdown_payload(raw_payload: Any) -> TaskBreakdownPayload:
|
|
"""将模型结构化输出归一为任务拆解载荷。"""
|
|
|
|
if isinstance(raw_payload, TaskBreakdownPayload):
|
|
return raw_payload
|
|
if not isinstance(raw_payload, dict):
|
|
raise ValueError("task_breakdown_payload must be a JSON object")
|
|
return TaskBreakdownPayload.model_validate(raw_payload)
|
|
|
|
|
|
def build_task_breakdown_ai_payload(payload: TaskBreakdownPayload) -> dict[str, Any]:
|
|
"""将结构化拆解载荷保存为 JSON 负载。"""
|
|
|
|
return payload.model_dump(mode="json")
|
|
|
|
|
|
async def resolve_task_breakdown_skill_tag_ids(
|
|
db: AsyncSession,
|
|
*,
|
|
tenant_id: int,
|
|
task: TaskBreakdownBusinessObject,
|
|
) -> tuple[list[int], SkillTagResolution]:
|
|
"""将任务里的技能名称和已有 skill_tag_id 合并成最终 ID 列表。"""
|
|
|
|
resolution = await resolve_skill_tag_ids_by_names(
|
|
db,
|
|
tenant_id=tenant_id,
|
|
skill_names=task.required_skill_names,
|
|
)
|
|
merged_ids: list[int] = []
|
|
seen: set[int] = set()
|
|
for raw in list(task.required_skill_tag_ids or []) + list(resolution.matched_ids or []):
|
|
try:
|
|
skill_id = int(raw)
|
|
except (TypeError, ValueError):
|
|
continue
|
|
if skill_id <= 0 or skill_id in seen:
|
|
continue
|
|
seen.add(skill_id)
|
|
merged_ids.append(skill_id)
|
|
return merged_ids, resolution
|
|
|
|
|
|
def should_auto_assign_breakdown_task(
|
|
*,
|
|
task: TaskBreakdownBusinessObject,
|
|
top_match_score: float | None,
|
|
top_workload_rate: float | None,
|
|
confidence_threshold: float = 0.85,
|
|
match_score_threshold: float = 75.0,
|
|
workload_threshold: float = 70.0,
|
|
) -> bool:
|
|
"""判断 AI 拆解任务是否可自动分配负责人。"""
|
|
|
|
confidence = float(task.confidence or 0.0)
|
|
if confidence < confidence_threshold:
|
|
return False
|
|
if str(task.risk_level or "").strip().lower() not in {"low"}:
|
|
return False
|
|
if top_match_score is None or top_workload_rate is None:
|
|
return False
|
|
if top_match_score < match_score_threshold:
|
|
return False
|
|
if top_workload_rate >= workload_threshold:
|
|
return False
|
|
return True
|
|
|
|
|
|
__all__ = [
|
|
"normalize_task_breakdown_payload",
|
|
"build_task_breakdown_ai_payload",
|
|
"resolve_task_breakdown_skill_tag_ids",
|
|
"should_auto_assign_breakdown_task",
|
|
]
|