Files

98 lines
4.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""任务启动前准备度检查(交付报告 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"]