PMFlow/backend/app/services/task_breakdown.py

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",
]