feat(server): add workbench API, DSH planner worker and RAG pipeline

This commit is contained in:
yi
2026-09-16 19:06:20 +08:00
parent 72fc5b47c7
commit d4b5b3f43a
122 changed files with 31906 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
"""任务启动前准备度检查(交付报告 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"]