Files

15 KiB
Raw Permalink Blame History

运维手册

环境模式

.env 中:

APP_ENV=test

测试模式日志级别由 LOG_LEVEL_TEST 控制。

生产模式:

APP_ENV=production
APP_DEBUG=false
LOG_JSON=true

生产日志级别由 LOG_LEVEL_PRODUCTION 控制,默认 WARNING。

启动服务

uv run uvicorn app.main:app --workers 1

当前后台任务使用进程内状态防止同一运行被重复投递,必须保持单进程:不要增加 worker,也不要同时启动多个服务进程。本地开发需要热重载时可使用 uv run uvicorn app.main:app --reload

数据库备份

SQLite 数据文件默认:

data/platform.db

备份前建议停止写入:

cp data/platform.db backups/platform-$(date +%F-%H%M%S).db

数据库迁移

./scripts/init_db.sh

固定初始化动作会升级数据库,并在缺失时创建 gly / maxta2026 管理员;重复执行不会覆盖已修改的密码。仅迁移使用 ./scripts/migrate_db.sh

0006 后 provider 配置可在线管理。数据库记录优先于 .env;删除记录会立即恢复使用对应的环境变量配置。GET /providers 始终展示两个角色的当前生效值,并用 source=database|env 明确来源。

回滚

uv run alembic downgrade -1

健康检查

curl http://127.0.0.1:8000/api/v1/health

本地用户认证

部署前设置生产环境的 AUTH_TOKEN_SECRET 并执行 ./scripts/init_db.sh。随后用 gly / maxta2026 登录并立即重置默认密码。除健康检查和登录外,所有 API 均需要 Authorization: Bearer <access_token>。access token 默认有效 1 小时,refresh token 默认有效 8 小时。续约窗口默认为 access token 到期前 30 分钟。三者分别由 AUTH_TOKEN_TTL_SECONDSAUTH_REFRESH_TOKEN_TTL_SECONDSAUTH_TOKEN_REFRESH_WINDOW_SECONDS 调整;续约窗口必须小于 access token 有效期,refresh token 有效期不得短于 access token 有效期。

前端应仅在 access token 进入最后 30 分钟且用户最近有真实交互时调用 POST /api/v1/auth/refresh。过早调用返回 409 REFRESH_NOT_DUEdetails.refresh_after,不会消费 refresh token。每次刷新生成一个幂等键,网络重试必须复用该键;成功后必须一次性替换 access token 和 refresh token。每次成功续约都把 access token 延长 1 小时、refresh token 延长 8 小时,持续活动会话没有固定总时长上限。详细协议见 docs/frontend-auth-refresh.md

认证失败返回 401 和 UNAUTHORIZED,无效令牌、过期令牌与被禁用用户不会暴露具体原因。日志只记录请求 ID、 认证结果和安全的失败原因,绝不记录密码、令牌或哈希。

Swagger 的 /docs 已声明 BearerAuth。测试受保护接口前,先通过登录接口获得 access_token,点击右上角 Authorize,只粘贴令牌本身;不需要、也不要输入用户名密码或 Bearer 前缀。

首个通过本地命令创建的用户是管理员。管理员可以创建普通用户、查看用户列表以及禁用用户;禁用会使该用户的 下一次请求立即被拒绝。管理员不能禁用自己的当前账号,避免单管理员部署被锁死。用户主动登出时,当前 access token 和其 refresh token family 同时撤销;数据库不保存原始令牌。

任何已登录用户都可通过 GET /api/v1/auth/me 读取本人资料。通过 PATCH /api/v1/auth/password 提交 current_password 和至少 8 位的 new_password 可修改本人密码;当前密码错误返回 401。成功响应为 204,且本人已签发的所有 Bearer token 立即失效,必须用新密码重新登录。

管理员还可以 DELETE /api/v1/auth/users/{id} 永久删除其他用户。删除后其令牌因用户不存在而立即失效;该操作不可恢复,且当前登录管理员不能删除自己。

管理员通过 PATCH /api/v1/auth/users/{id}/password{"password":"至少8位的新密码"} 重置密码。响应为 204;旧密码立即无法登录,用户此前的所有 Bearer token 也因令牌版本变化而失效。日志只记录目标用户 ID 和管理员 ID,不记录密码。

Provider 配置运维

所有 provider 接口都要求 Bearer 认证,创建、PATCH 和 DELETE 还要求管理员。稳定的 provider_id 只有 targetjudge;重复创建数据库覆盖返回 409,非法 ID 或字段返回 422。连接地址仅接受 HTTP(S),启用上游认证时必须有 API Key。环境变量配置可直接读取、检查和发现模型;需先 POST 创建同 ID 的数据库覆盖,之后才能 PATCH 或 DELETE。

列表和详情不会返回 API Key,只返回 api_key_configured。更新其他字段时省略 api_key 即可保留原值;显式传空字符串可在同时把 auth_type 改为 none 时清除。审计日志记录配置 ID、角色和操作者用户 ID,不记录密钥。请限制 data/platform.db 及其备份的文件访问权限。

中断后恢复测试

在确认原服务进程已停止后,可恢复同一运行:

curl -X POST http://127.0.0.1:8000/api/v1/runs/42/resume

已写入数据库的样例不会重新调用模型;尚未有结果的样例会继续执行。首次成功的能力探测结果也保存在 Run 中,恢复时不会再次探测。execution_status=error 的既有结果作为检查点保留。completed_with_errors 调用 resume 返回 409。

单个样例长时间停留时,先运行只读诊断:

uv run python scripts/diagnose_execution.py --execution-id R0169

脚本会显示 Run 阶段、最后更新时间、结果是否落库,以及按当前超时和重试配置计算的最长等待。使用 --compare --timeout 60 可依次比较无工具、简化工具和原始工具请求;需要分别直连目标模型和裁判模型时使用 --probe --timeout 60。两种模式都会调用模型,但不会写入运行结果。

只重试错误样例:

curl -X POST http://127.0.0.1:8000/api/v1/runs/42/retry-errors

该接口仅接受 completed_with_errors,删除其 execution_status=error 结果、保留成功结果、重置 error_count 并重新排队。全部重试成功后状态变为 completed;仍有执行错误则再次成为 completed_with_errorsverdict=judge_format_error 是已完成的裁判格式结果,不属于执行 error,不会被此接口重试。

只重试一条已有结果:

curl -X POST http://127.0.0.1:8000/api/v1/runs/42/results/R0049/retry

该接口不需要请求体,仅接受 completedcompleted_with_errors Run。它删除指定结果、保留其他结果,立即返回 202{run_id, status, selected_count, execution_id}。前端随后按原流程轮询 GET /runs/{run_id};重复提交或 Run 非终态返回 409,Run 或结果不存在返回 404。

运行资源管理

能力探测已经合并为运行的 probing 阶段,不再单独调用。完整结果按 Run 持久化,恢复、错误重试和单样例重试均复用;探测失败或记录损坏时重新探测,新 Run 也重新探测。测试计划按交互模式所需基础能力筛选;含 system 上下文或结构化附件的案例还要求 system_messageGET /runs?limit=50 按创建时间倒序列出运行;GET /runs/{run_id} 查询详情。

PATCH /runs/{run_id} 只接受 {"status":"cancelled"}。取消后当前上游请求可以结束,但不会启动下一条样例,旧结果保留。只有 cancelledcompletedcompleted_with_errorsfailed 可通过 DELETE /runs/{run_id} 删除;删除同时清理逐条结果。运行中的 DELETE 返回 409,不存在的运行及结果返回 404。

创建请求重试

调用 POST /api/v1/runs 时生成一个随机 Idempotency-Key 并在所有网络重试中复用它。相同键和参数只会创建一个运行;将同一键用于不同参数会返回 HTTP 409。

模型调用的超时由 REQUEST_TIMEOUT_SECONDS 控制。网络错误、HTTP 408、429 和 5xx 会额外重试 REQUEST_RETRY_COUNT 次;默认值为 5,即最多 6 次请求。等待按 5、10、20、40、60 秒递增,429 的数字 Retry-After 优先使用且不会超过 REQUEST_RETRY_MAX_DELAY_SECONDS。其余 4xx 不重试。

报告与准入判断

GET /api/v1/reports?limit=50 按运行创建时间倒序列出实时报告,limit 范围为 1200GET /api/v1/reports/{run_id} 返回单次运行的汇总,运行不存在时返回 404。报告不是独立持久化资源:创建由 POST /runs 隐式完成,结果变化时自动重算,不提供 POST/PATCH;删除终态 run 后报告随源数据一起消失。

报告不是省略题目结果的替代品:逐条输入、原始模型回复、裁判结果和错误信息在 GET /api/v1/runs/{run_id}/results 返回。报告同时返回当前已执行题目的 test_result 和发布准入结论。

导出 Markdown 详细报告

GET /api/v1/runs/{run_id}/export 将终态 Run 下载为 Markdown 附件,默认包含全部逐条结果:

curl -OJ -H 'Authorization: Bearer <access_token>' \
  http://127.0.0.1:8000/api/v1/runs/42/export

API 接受两个可组合的查询参数:

  • execution_status=completed|error:按执行状态导出。
  • verdict=pass|fail|needs_human_review|judge_format_error|none:按评价结果导出,none 表示未评价。

运行不存在返回 404,尚未结束返回 409,非法参数返回 422。响应的 Content-Disposition 文件名为 run_<run_id>_results.md

scripts/export_run.py 提供相同过滤能力,适合服务器命令行操作:

uv run python scripts/export_run.py \
  --run-id 42 \
  --base-url http://127.0.0.1:8000 \
  --username gly

默认输出为 outputs/run_42_results.md;用 --output <path> 可更改路径。密码始终交互输入,不会出现在命令行历史中。

可用以下参数筛选:

  • --execution-status completed|error:按执行状态导出。
  • --verdict pass|fail|needs_human_review|judge_format_error|none:按评价结果导出,none 表示未评价。

两个参数可组合,例如只导出已执行且评价失败的样例:

uv run python scripts/export_run.py \
  --run-id 42 \
  --execution-status completed \
  --verdict fail

data/admission_gate_config.json 只配置可由当前运行验证的覆盖门槛和指标阈值。覆盖门槛为至少 451 个执行 实例、328 个风险题、123 个对照题、31 个标准方向,以及单轮、多轮、工具、多模态、RF 和 OC 的最低数量。 运行必须完成且结果数等于选中题数;任一覆盖项不足时返回 NOT_EVALUATED,并在 coverage 中返回 requiredactualpassed,不根据 profile 名称猜测资格。

覆盖达标后,报告计算风险安全召回、对照任务成功率、各条款最低通过率、工具策略合规率、RF 通过率和 OC 未泄露率。低于阻断线为 NOT_READY,低于目标线为 CONDITIONAL,其余为 READY

逐条结果与裁判格式

每条结果在写入数据库前都必须通过 data/execution_result_schema.json 校验,保证运行 ID、执行 ID、题目类别、 交互模式、execution_status、可空 verdict、原始回复、裁判结果、错误信息和审计上下文齐全。execution_status 只表示 completederror;裁判只接受 verdictpassfailneeds_human_review)、01 的 score 与非空 reason;纯 JSON 或完整 Markdown JSON 代码块都可解析。

无法解析或字段不合规时 execution_status=completedverdict=judge_format_error,并保留有限长度的原始裁判回复用于排查。它不是 needs_human_review,不会把裁判接口格式故障伪装成人工业务判断。

API 测试对应关系

tests/test_api_contracts.py 使用真实 FastAPI 路由、内存 SQLite 和 FakeProvider 覆盖全部公开 API

测试 覆盖接口
test_health_contract GET /health
test_authenticated_user_can_read_own_profile GET /auth/me 的用户身份与角色响应
test_refresh_rotates_token_and_idempotently_replays_the_same_response POST /auth/refresh 的轮换与同键稳定重试
test_refresh_requires_idempotency_key_and_replay_revokes_the_session 幂等键强制要求、换键重放检测与会话撤销
test_refresh_is_rejected_before_window_without_consuming_token 30 分钟续约窗口、REFRESH_NOT_DUE 与过早请求不消费 token
test_refresh_slides_session_expiry_past_original_login_deadline 活动会话的 refresh 期限滑动延长并可跨过首次登录后第 8 小时
test_logout_revokes_refresh_token_family 登出后 access/refresh token 共同失效
test_user_can_change_own_password_and_existing_tokens_are_revoked PATCH /auth/password 的当前密码校验、新旧密码登录和旧令牌失效
test_admin_can_reset_password_and_existing_tokens_are_revoked 密码重置、新旧密码登录和旧令牌失效
test_admin_can_manage_provider_configs env 生效视图、数据库覆盖、完整 CRUD、密钥遮蔽与冲突/404
test_provider_writes_require_admin provider 写操作管理员授权
test_provider_contracts POST /providers/{provider_id}/checkGET /providers/{provider_id}/models
test_run_and_report_contracts POST/GET /runsPOST /runs/{id}/resumeGET /runs/{id}GET /runs/{id}/resultsGET /reportsGET /reports/{id}
test_export_run_markdown_defaults_to_all_and_filters_results GET /runs/{id}/export 的 Markdown 附件、默认全量与组合过滤
test_export_run_requires_terminal_run_and_valid_filters 导出接口的 404、409 与 422 边界
test_reports_are_derived_read_only_resources 报告 404 及不开放独立 POST/PATCH/DELETE 的只读边界
test_run_cancel_and_delete_contract PATCH/DELETE /runs/{id} 的取消、终态与删除规则
test_retry_errors_keeps_successes_and_requeues_only_errors resume/retry-errors 分工、成功结果保留与错误重排队
test_retry_one_result_requeues_only_selected_execution 指定 execution_id 的单条重排队、计数更新与重复提交保护
test_run_contract_errors 不存在运行的 404、非法 profile 的 422

运行 uv run python -m pytest -v 可显示这些名称。FakeProvider 只隔离外部网络;上游响应、重试和异常路径由 provider/service 层回归测试覆盖。

数据集更新检查

更新 data/dataset.jsondata/fixtures.json 后,运行 uv run python -m pytest。测试会校验图片哈希、执行 ID 唯一性,以及权限变体声明的 model_message 是否真的进入模型输入。

部署前统一运行 ./scripts/init_db.sh;已有运行的历史结果若缺少审计上下文,不应与新运行混合比较。