from __future__ import annotations import re from datetime import datetime, timezone from typing import Any, Literal from uuid import uuid4 from pydantic import BaseModel, Field from app.agents.schemas.skills import SkillShortlistEntry ExecutionMode = Literal["direct", "collaboration", "parallel", "delegated"] ParallelPreference = Literal["direct", "collaboration", "parallel"] class ParallelWorthiness(BaseModel): should_parallelize: bool = False score: float = 0.0 estimated_subtasks: int = 1 preferred_mode: ParallelPreference = "direct" reasons: list[str] = Field(default_factory=list) risk_flags: list[str] = Field(default_factory=list) class TaskNode(BaseModel): node_id: str title: str role: str | None = None goal: str | None = None depends_on: list[str] = Field(default_factory=list) execution_mode: Literal["serial", "parallel"] = "serial" expected_evidence: list[dict[str, Any]] = Field(default_factory=list) class TaskGraph(BaseModel): graph_id: str = Field(default_factory=lambda: str(uuid4())) nodes: list[TaskNode] = Field(default_factory=list) entry_node_ids: list[str] = Field(default_factory=list) max_parallelism: int = 1 rationale: str | None = None class SubTaskSpec(BaseModel): subtask_id: str parent_run_id: str title: str role: str goal: str context_slice: dict[str, Any] = Field(default_factory=dict) allowed_tools: list[str] = Field(default_factory=list) budget_tokens: int = 1200 budget_tool_calls: int = 2 expected_output_schema: dict[str, Any] = Field(default_factory=dict) expected_evidence: list[dict[str, Any]] = Field(default_factory=list) dependencies: list[str] = Field(default_factory=list) class SubTaskResult(BaseModel): subtask_id: str status: Literal["completed", "failed", "blocked"] summary: str | None = None evidence: list[dict[str, Any]] = Field(default_factory=list) output: dict[str, Any] = Field(default_factory=dict) class MergeReport(BaseModel): merge_id: str = Field(default_factory=lambda: str(uuid4())) status: Literal["merged", "conflicted", "fallback"] summary: str | None = None evidence_union: list[dict[str, Any]] = Field(default_factory=list) conflict_flags: list[str] = Field(default_factory=list) resolution_strategy: str | None = None resolved_summary: str | None = None fallback_used: bool = False class VerificationReport(BaseModel): status: Literal["passed", "failed", "skipped"] summary: str | None = None evidence: list[dict[str, Any]] = Field(default_factory=list) class ExecutionDecision(BaseModel): request_id: str = Field(default_factory=lambda: str(uuid4())) mode: ExecutionMode = "direct" reason: str complexity_score: float = 0.0 parallel_worthiness_score: float | None = None selected_roles: list[str] = Field(default_factory=list) class RuntimeRequestContext(BaseModel): request_id: str = Field(default_factory=lambda: str(uuid4())) session_id: str | None = None user_id: str conversation_id: str | None = None query_text: str | None = None raw_user_query: str | None = None recalled_memories: list[str] = Field(default_factory=list) retrospective_shortlist: list[dict[str, Any]] = Field(default_factory=list) recalled_retrospectives: list[dict[str, Any]] = Field(default_factory=list) skill_shortlist: list[SkillShortlistEntry] = Field(default_factory=list) shortlisted_skills: list[str] = Field(default_factory=list) parallel_worthiness: ParallelWorthiness = Field(default_factory=ParallelWorthiness) task_graph: TaskGraph | None = None recommended_runtime_mode: Literal["direct", "collaboration"] = "direct" execution_mode: Literal["direct", "collaboration"] | None = None current_agent_role: str | None = None conversation_state_ref: str | None = None assembly_metrics: dict[str, float] = Field(default_factory=dict) assembled_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) def assess_parallel_worthiness( query_text: str, *, retrospective_count: int = 0, skill_count: int = 0, ) -> ParallelWorthiness: normalized = (query_text or "").strip().lower() reasons: list[str] = [] score = 0.0 multi_step_markers = ("然后", "接着", "同时", "并且", "最后", "汇总", "对比", "分析", "research") artifact_markers = ("文档", "代码", "文件", "数据库", "论坛", "知识库", "计划") if any(marker in normalized for marker in multi_step_markers): score += 0.35 reasons.append("multi_step_request") if sum(1 for marker in artifact_markers if marker in normalized) >= 2: score += 0.25 reasons.append("multi_source_context") if len(re.findall(r"[,,、;;]", query_text or "")) >= 2: score += 0.15 reasons.append("compound_instruction") if retrospective_count > 0: score += 0.1 reasons.append("historical_support") if skill_count > 0: score += 0.1 reasons.append("skill_candidates_available") score = min(score, 1.0) should_parallelize = score >= 0.55 preferred_mode: ParallelPreference = "parallel" if should_parallelize else "direct" if not should_parallelize and score >= 0.3: preferred_mode = "collaboration" estimated_subtasks = 1 if preferred_mode == "parallel": estimated_subtasks = 3 if score >= 0.8 else 2 elif preferred_mode == "collaboration": estimated_subtasks = 2 return ParallelWorthiness( should_parallelize=should_parallelize, score=round(score, 3), estimated_subtasks=estimated_subtasks, preferred_mode=preferred_mode, reasons=reasons, ) def render_runtime_request_context_summary(context: RuntimeRequestContext) -> str: lines = ["【Runtime Request Context】"] lines.append(f"- 推荐运行模式: {context.recommended_runtime_mode}") lines.append( f"- 并行潜力: score={context.parallel_worthiness.score:.2f}, " f"preferred={context.parallel_worthiness.preferred_mode}, " f"estimated_subtasks={context.parallel_worthiness.estimated_subtasks}" ) if context.parallel_worthiness.reasons: lines.append(f"- 并行判断依据: {', '.join(context.parallel_worthiness.reasons)}") if context.assembly_metrics: total_ms = context.assembly_metrics.get("total_ms") if total_ms is not None: lines.append(f"- 上下文装配耗时: {total_ms:.1f} ms") if context.task_graph and context.task_graph.nodes: lines.append( f"- 任务图: nodes={len(context.task_graph.nodes)}, max_parallelism={context.task_graph.max_parallelism}" ) for node in context.task_graph.nodes[:4]: deps = f", deps={len(node.depends_on)}" if node.depends_on else "" lines.append(f" - [{node.execution_mode}] {node.title} ({node.role}{deps})") if context.retrospective_shortlist: lines.append("- 历史复盘命中:") for item in context.retrospective_shortlist[:3]: summary = (item.get("summary") or item.get("summary_text") or "").strip() task_type = item.get("task_type") or "unknown" lines.append(f" - [{task_type}] {summary[:160]}") if context.skill_shortlist: lines.append("- 技能候选:") for item in context.skill_shortlist[:3]: lines.append( f" - {item.skill_name} ({item.injection_mode}, score={item.score:.2f})" + (f": {item.rationale}" if item.rationale else "") ) if context.recalled_memories: lines.append("- 记忆上下文已装配,可在回答中按需引用。") return "\n".join(lines)