98 lines
4.5 KiB
Python
98 lines
4.5 KiB
Python
"""任务启动前准备度检查(交付报告 P1-1,Hermes 融合增强)。
|
||||
|
|
|
|||
|
|
运行前一次性检查:模型 Provider、生产 Agent、知识库索引、可用知识片段、
|
|||
|
|
QC/Composer Agent、点数余额;UI 在启动前显示阻断原因,避免开跑后中途失败。
|
|||
|
|
"""
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
from sqlmodel import Session, select
|
|||
|
|
|
|||
|
|
from .models import Agent, Campaign, KnowledgeBase, Segment
|
|||
|
|
from .billing import point_state
|
|||
|
|
from .agents.default_composer import resolve_composer_agent
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _provider_ready(session: Session, agent: Agent) -> tuple[bool, str]:
|
|||
|
|
"""Agent 的模型链路是否可用(显式配置或全局默认)。"""
|
|||
|
|
try:
|
|||
|
|
from .agents.coordinator import _provider_model
|
|||
|
|
_provider_model(session, agent)
|
|||
|
|
return True, ""
|
|||
|
|
except Exception as exc:
|
|||
|
|
return False, f"Agent「{agent.name}」模型不可用:{str(exc)[:140]}"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def readiness_report(session: Session, campaign: Campaign) -> dict:
|
|||
|
|
"""返回 {ready, blockers[], warnings[]},供 API 与任务页直接展示。"""
|
|||
|
|
blockers: list[str] = []
|
|||
|
|
warnings: list[str] = []
|
|||
|
|
|
|||
|
|
segments = session.exec(select(Segment).where(Segment.campaign_id == campaign.id)).all()
|
|||
|
|
if not segments:
|
|||
|
|
blockers.append("任务还没有内容段——拆解失败或导图为空。请重新创建任务并粘贴导图文本(或用图片 OCR)")
|
|||
|
|
return {"ready": False, "blockers": blockers, "warnings": warnings}
|
|||
|
|
|
|||
|
|
# 1. 每段必须有可用的生产 Agent + 模型链路
|
|||
|
|
agent_ids = {seg.agent_id for seg in segments if seg.agent_id}
|
|||
|
|
if not agent_ids:
|
|||
|
|
blockers.append("内容段没有挂接任何生产 Agent")
|
|||
|
|
for agent_id in sorted(agent_ids):
|
|||
|
|
agent = session.get(Agent, agent_id)
|
|||
|
|
if not agent:
|
|||
|
|
blockers.append(f"内容段引用的 Agent #{agent_id} 不存在")
|
|||
|
|
continue
|
|||
|
|
if agent.status != "active":
|
|||
|
|
blockers.append(f"Agent「{agent.name}」已停用,请先在 Agent 管理中启用")
|
|||
|
|
continue
|
|||
|
|
ok, reason = _provider_ready(session, agent)
|
|||
|
|
if not ok:
|
|||
|
|
blockers.append(reason)
|
|||
|
|
|
|||
|
|
# 2. 生产 Agent 的知识库:未索引的警告;完全没挂接的阻断
|
|||
|
|
for agent_id in sorted(agent_ids):
|
|||
|
|
agent = session.get(Agent, agent_id)
|
|||
|
|
if not agent or agent.status != "active" or agent.role != "production":
|
|||
|
|
continue
|
|||
|
|
kb_ids = list(agent.kb_ids or [])
|
|||
|
|
if not kb_ids:
|
|||
|
|
blockers.append(f"生产 Agent「{agent.name}」未挂接知识库,无法保证引用可溯源")
|
|||
|
|
continue
|
|||
|
|
for kb_id in kb_ids:
|
|||
|
|
kb = session.get(KnowledgeBase, kb_id)
|
|||
|
|
if not kb:
|
|||
|
|
blockers.append(f"Agent「{agent.name}」挂接的知识库 #{kb_id} 已被删除")
|
|||
|
|
elif kb.index_status not in ("ready", "empty"):
|
|||
|
|
warnings.append(f"知识库「{kb.name}」索引状态为 {kb.index_status},检索可能为空")
|
|||
|
|
|
|||
|
|
# 3. QC 必须存在且启用;组合 Agent 是系统内置阶段,不属于用户资源。
|
|||
|
|
qc_rows = session.exec(select(Agent).where(Agent.role == "qc")).all()
|
|||
|
|
if not qc_rows:
|
|||
|
|
blockers.append("缺少质检 Agent,流水线无法完成质检")
|
|||
|
|
elif not any(r.status == "active" for r in qc_rows):
|
|||
|
|
blockers.append("质检 Agent 全部处于停用状态")
|
|||
|
|
composer = resolve_composer_agent(session)
|
|||
|
|
composer_ok, composer_reason = _provider_ready(session, composer)
|
|||
|
|
if not composer_ok:
|
|||
|
|
blockers.append(f"系统组合 Agent 模型不可用:{composer_reason.split(':', 1)[-1]}")
|
|||
|
|
|
|||
|
|
# 4. 点数余额:至少要能撑过第一轮模型调用
|
|||
|
|
owner_id = int(campaign.owner_user_id or 0)
|
|||
|
|
org_id = int(campaign.organization_id or 1)
|
|||
|
|
if owner_id:
|
|||
|
|
points = point_state(session, user_id=owner_id, organization_id=org_id)
|
|||
|
|
per_token = int(points.get("points_per_token") or 10)
|
|||
|
|
balance = int(points.get("points") or 0)
|
|||
|
|
token_eq = int(points.get("token_equivalent") or 0)
|
|||
|
|
if balance < per_token:
|
|||
|
|
blockers.append(
|
|||
|
|
f"点数余额不足({balance} 点数,至少需要 {per_token} 点数),"
|
|||
|
|
"请联系管理员在用量与账单中增加点数")
|
|||
|
|
elif token_eq < 2000:
|
|||
|
|
warnings.append(
|
|||
|
|
f"点数偏低(约 {token_eq} Token),长任务可能中途暂停")
|
|||
|
|
|
|||
|
|
return {"ready": not blockers, "blockers": blockers, "warnings": warnings}
|
|||
|
|
|
|||
|
|
|
|||
|
|
__all__ = ["readiness_report"]
|