Preformu / core /kernel /orchestrator.py
Kevinshh's picture
feat: 意图保真(intent-fidelity) + 描述性梳理技能 + 相容性引擎升级; 修复转置宽表解析/CQA对账/澄清交互/功能切换串显; .gitignore 排除专利与机密Demo数据
0e6887b
Raw
History Blame Contribute Delete
5.56 kB
"""分析编排器 ``AnalysisOrchestrator``(intent-understanding-layer 任务 11)。
协调「理解 → 确认 → 分析」两阶段流程(design.md「4. AnalysisOrchestrator」):
- Phase A::meth:`build_task_sheet` 调用理解层产出《分析任务单》(不触达 ``compute``)。
- Phase B::meth:`run_confirmed` 接收**用户已确认**的任务单,经 :meth:`Router.route_from_sheet`
路由后,把任务单挂到 ``RawInput.extra["task_sheet"]`` 交由固定 ``Pipeline`` 执行。
合规保证(Property 5 / 需求 1.3、5.4、6.2):``compute`` 只能经 :meth:`run_confirmed`
触达,而后者要求传入一份非澄清态任务单;澄清态 / 路由失败时**短路返回**澄清说明,
绝不进入流水线。审计在确认执行时记录(需求 8.2 / 9.4)。
"""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, Any, Optional
from .pipeline import Pipeline
from .router import Router
from .skill_base import RawInput, ReportSections
from .task_sheet import AnalysisTaskSheet
from .understanding import UnderstandingLayer
if TYPE_CHECKING: # pragma: no cover - 仅类型检查
from .registry import SkillRegistry
from .services import Services
logger = logging.getLogger(__name__)
class AnalysisOrchestrator:
"""两阶段分析编排:理解(Phase A)→ 用户确认 → 分析(Phase B)。"""
def __init__(self, registry: "SkillRegistry", svc: "Services") -> None:
self.registry = registry
self.svc = svc
self.router = Router(registry, svc)
# ------------------------------------------------------------------
# Phase A:理解
# ------------------------------------------------------------------
def build_task_sheet(self, raw: RawInput) -> AnalysisTaskSheet:
"""运行理解层产出《分析任务单》(不进行任何分析数值计算,Property 4)。"""
return UnderstandingLayer(self.svc).understand(raw)
# ------------------------------------------------------------------
# Phase B:分析(仅在用户确认后调用)
# ------------------------------------------------------------------
def run_confirmed(
self,
sheet: AnalysisTaskSheet,
raw: RawInput,
*,
pipeline: Optional[Pipeline] = None,
user: str = "anonymous",
) -> ReportSections:
"""执行已确认任务单对应的分析。
前置:``sheet`` 必须为**非澄清态**(澄清态直接返回澄清说明,Property 5)。
路由失败(需用户选择)同样短路返回,不进入流水线。
"""
# 澄清态:不得进入流水线(需求 6.2 / Property 5)。
if sheet is None or sheet.needs_clarification():
return self._clarification_sections(sheet)
decision = self.router.route_from_sheet(sheet)
if decision.needs_user_choice or decision.skill is None:
return ReportSections(
sections={
"clarification": {
"reason": "需要用户选择分析功能",
"detail": decision.reason,
}
}
)
raw2 = self._attach_confirmed_payload(raw, sheet)
self._audit_confirm(user, decision.skill.meta.id, sheet)
pipeline = pipeline or Pipeline()
return pipeline.run(decision.skill, raw2, self.svc)
# ------------------------------------------------------------------
@staticmethod
def _attach_confirmed_payload(raw: RawInput, sheet: AnalysisTaskSheet) -> RawInput:
"""把已确认任务单写入 ``RawInput.extra["task_sheet"]``(不改契约)。"""
extra = dict(getattr(raw, "extra", None) or {})
extra["task_sheet"] = sheet.to_dict()
return RawInput(
smiles=getattr(raw, "smiles", "") or "",
excipient=getattr(raw, "excipient", "") or "",
goal=getattr(raw, "goal", "") or "",
files=list(getattr(raw, "files", None) or []),
extra=extra,
)
@staticmethod
def _clarification_sections(sheet: Optional[AnalysisTaskSheet]) -> ReportSections:
clar = getattr(sheet, "clarification", None) if sheet else None
return ReportSections(
sections={
"clarification": {
"reason_code": getattr(clar, "reason_code", "") if clar else "",
"message_key": getattr(clar, "message_key", "") if clar else "",
"options": list(getattr(clar, "options", []) or []) if clar else [],
}
}
)
def _audit_confirm(self, user: str, skill_id: str, sheet: AnalysisTaskSheet) -> None:
audit = getattr(self.svc, "audit", None)
recorder = getattr(audit, "record_analysis", None) or getattr(audit, "record_event", None)
if callable(recorder):
try:
recorder(user or "anonymous", skill_id, step="confirmed_task_sheet")
except TypeError:
try:
recorder("confirm", skill_id, sheet.intent.value)
except Exception: # noqa: BLE001
logger.debug("编排器确认审计失败(已忽略)。", exc_info=True)
except Exception: # noqa: BLE001 - 审计失败不影响主流程
logger.debug("编排器确认审计失败(已忽略)。", exc_info=True)
__all__ = ["AnalysisOrchestrator"]