V2 开发:多 Agent 协作(主管/评审/辩论) + 自动评估体系 + 模板市场 + 企业版(SSO/RBAC/审计/合规)

- agents.py: supervisor/review/debate 三模式编排引擎,步骤流水全记录
- eval.py: eval 数据集 + LLM-as-Judge 评分 + 任务产出自动沉淀 + Worker 排行榜
- templates.py: 任务/项目/团队三类模板,占位符渲染(JSON安全转义),一键应用
- enterprise.py: 用户体系/RBAC、OIDC+LDAP SSO、审计日志、数据导出/保留期/PII脱敏
- db.py: V2 表结构 + 迁移 + 启动恢复(stale running→failed)
- app.py: V2 API 路由 + 登录升级(账号体系)+ 审计钩子
- 前端: 新增多Agent协作/自动评估/模板市场/企业版 4 页面
- Dockerfile: 企业版私有化部署
This commit is contained in:
2026-08-12 12:21:17 +08:00
parent 29874e6ca9
commit 9a95b71631
11 changed files with 2626 additions and 47 deletions
+182
View File
@@ -134,6 +134,136 @@ CREATE TABLE IF NOT EXISTS settings (
);
CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project_id);
-- ===================================================================
-- V2 表结构:多 Agent 协作 / 自动评估 / 模板市场 / 企业版
-- ===================================================================
CREATE TABLE IF NOT EXISTS agent_runs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
mode TEXT NOT NULL, -- supervisor / review / debate
title TEXT DEFAULT '',
topic TEXT DEFAULT '', -- 输入主题 / 任务
context TEXT DEFAULT '', -- 附加上下文(知识库/约束)
worker_ids TEXT DEFAULT '[]', -- JSON: 参与协作的 worker id 列表
params TEXT DEFAULT '{}', -- JSON: rounds/阈值/立场等
status TEXT DEFAULT 'running', -- running/done/failed/cancelled
result TEXT DEFAULT '', -- 最终产出
summary TEXT DEFAULT '', -- 过程摘要(评审意见/共识等)
error TEXT DEFAULT '',
total_tokens INTEGER DEFAULT 0,
cost REAL DEFAULT 0,
created_at INTEGER,
finished_at INTEGER
);
CREATE TABLE IF NOT EXISTS agent_steps (
id INTEGER PRIMARY KEY AUTOINCREMENT,
run_id INTEGER NOT NULL,
role TEXT DEFAULT '', -- supervisor/worker/reviewer/judge/debater
worker_id INTEGER,
seq INTEGER DEFAULT 0,
stage TEXT DEFAULT '', -- plan/delegate/produce/critique/revise/synthesize/verdict
content TEXT DEFAULT '',
tokens INTEGER DEFAULT 0,
cost REAL DEFAULT 0,
created_at INTEGER
);
CREATE TABLE IF NOT EXISTS eval_datasets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT DEFAULT '',
rubric TEXT DEFAULT '', -- 评分标准(LLM-as-judge
tags TEXT DEFAULT '[]',
is_builtin INTEGER DEFAULT 0,
created_at INTEGER,
updated_at INTEGER
);
CREATE TABLE IF NOT EXISTS eval_cases (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dataset_id INTEGER NOT NULL,
input TEXT DEFAULT '',
expected TEXT DEFAULT '',
tags TEXT DEFAULT '[]',
created_at INTEGER
);
CREATE TABLE IF NOT EXISTS eval_runs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dataset_id INTEGER NOT NULL,
worker_id INTEGER NOT NULL,
status TEXT DEFAULT 'running', -- running/done/failed/cancelled
score REAL DEFAULT 0, -- 平均分 0-100
total_tokens INTEGER DEFAULT 0,
cost REAL DEFAULT 0,
cases_total INTEGER DEFAULT 0,
cases_done INTEGER DEFAULT 0,
created_at INTEGER,
finished_at INTEGER
);
CREATE TABLE IF NOT EXISTS eval_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
run_id INTEGER NOT NULL,
case_id INTEGER NOT NULL,
worker_id INTEGER,
output TEXT DEFAULT '',
score REAL DEFAULT 0,
judgment TEXT DEFAULT '',
latency_ms INTEGER DEFAULT 0,
cost REAL DEFAULT 0,
created_at INTEGER
);
CREATE TABLE IF NOT EXISTS templates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL, -- task / project / team
name TEXT NOT NULL,
description TEXT DEFAULT '',
content TEXT DEFAULT '{}', -- JSON
tags TEXT DEFAULT '[]',
author TEXT DEFAULT 'system',
is_builtin INTEGER DEFAULT 0,
usage_count INTEGER DEFAULT 0,
created_at INTEGER,
updated_at INTEGER
);
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password_hash TEXT DEFAULT '',
display_name TEXT DEFAULT '',
role TEXT DEFAULT 'member', -- admin / member / auditor
source TEXT DEFAULT 'local', -- local / oidc / ldap
status TEXT DEFAULT 'active', -- active / disabled
last_login_at INTEGER,
created_at INTEGER
);
CREATE TABLE IF NOT EXISTS audit_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
actor TEXT DEFAULT '', -- 用户名 / token 名 / system
action TEXT DEFAULT '', -- 如 task.create / worker.update / agent.run
target TEXT DEFAULT '',
detail TEXT DEFAULT '',
ip TEXT DEFAULT '',
user_agent TEXT DEFAULT '',
created_at INTEGER
);
CREATE TABLE IF NOT EXISTS enterprise_settings (
key TEXT PRIMARY KEY,
value TEXT DEFAULT ''
);
CREATE INDEX IF NOT EXISTS idx_agent_steps_run ON agent_steps(run_id);
CREATE INDEX IF NOT EXISTS idx_agent_runs_status ON agent_runs(status);
CREATE INDEX IF NOT EXISTS idx_eval_cases_ds ON eval_cases(dataset_id);
CREATE INDEX IF NOT EXISTS idx_eval_runs_ds ON eval_runs(dataset_id);
CREATE INDEX IF NOT EXISTS idx_eval_results_run ON eval_results(run_id);
CREATE INDEX IF NOT EXISTS idx_audit_time ON audit_logs(created_at);
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
CREATE INDEX IF NOT EXISTS idx_logs_task ON task_logs(task_id);
CREATE INDEX IF NOT EXISTS idx_cost_task ON cost_records(task_id);
@@ -156,6 +286,42 @@ def _migrate():
conn.close()
# ---------------------------------------------------------------------------
# V2 迁移:旧库升级(幂等)
# ---------------------------------------------------------------------------
def migrate_v2():
"""老数据库升级:V2 表由 SCHEMA 中的 CREATE TABLE IF NOT EXISTS 保证存在;
此处处理老表缺列 / 默认数据(管理员账号、内置模板)。"""
conn = get_conn()
# users 表首次出现时注入默认管理员
c = conn.execute('SELECT COUNT(*) c FROM users').fetchone()['c']
if c == 0:
conn.execute(
"INSERT INTO users (username, password_hash, display_name, role, source, status, created_at) "
"VALUES ('admin', ?, '管理员', 'admin', 'local', 'active', ?)",
(_hash_password('admin123'), int(time.time())))
conn.commit()
conn.close()
def _hash_password(pwd):
import hashlib
return 'sha256$' + hashlib.sha256(pwd.encode('utf-8')).hexdigest()
def verify_password(pwd, pwd_hash):
if not pwd_hash:
return False
if pwd_hash.startswith('sha256$'):
import hashlib
return hashlib.sha256(pwd.encode('utf-8')).hexdigest() == pwd_hash.split('$', 1)[1]
return pwd == pwd_hash # 兼容明文
def hash_password(pwd):
return _hash_password(pwd)
def get_conn():
conn = sqlite3.connect(DB_PATH, timeout=30)
conn.row_factory = sqlite3.Row
@@ -170,6 +336,22 @@ def init_db():
conn.commit()
conn.close()
_migrate()
migrate_v2()
def recover_stale_runs():
"""启动恢复:进程重启后,把遗留的 running 状态标记为 failed(线程已随进程消亡)。
覆盖:V1 任务 / V2 协作运行 / V2 评估运行。"""
now_ts = now()
n1 = w('UPDATE tasks SET status="failed", error="服务重启,执行中断", finished_at=? '
'WHERE status="running"', (now_ts,))
n2 = w('UPDATE agent_runs SET status="failed", error="服务重启,协作中断", finished_at=? '
'WHERE status="running"', (now_ts,))
n3 = w('UPDATE eval_runs SET status="failed", finished_at=? WHERE status="running"', (now_ts,))
if n1 or n2 or n3:
import logging
logging.warning(f'recover_stale_runs: tasks={n1 or 0} agent_runs={n2 or 0} eval_runs={n3 or 0}')
return (n1 or 0, n2 or 0, n3 or 0)
def q(sql, args=(), one=False):