LOG · 日志

theatron

DATE 日期
2026-10-23
SOURCE 源码
开发/theatron.md
READ 阅读
~6 min

Theatron

给带教教师和练判断的学生,一个把训练场景演出来、把评分留在人手里的系统;M0/M1/M2 已跑通,M3 和换代收尾没做完。

状态:可用但未定版(v3 换代期,单部署单租户);入口:本地运行 bun run dev → :3100;仓库:fire-disposal/theatron

本质

让 LLM 扮演场景角色、把评分留给教师与量具的护理培训推演系统。

选型

基于 Bun 的单体 SSR 应用,前端 React Router + React + Tailwind,后端同进程 Hono,数据 PostgreSQL + Drizzle,部署单容器。

  • 运行时
    • Bun 1.4
  • 前端
    • React 19
    • React Router 8
      • react-router-hono-server
    • Vite 8
    • Tailwind CSS 4
      • shadcn/ui
      • Radix UI
    • lucide-react
  • 后端
    • Hono 4
      • zod 4
      • jose
  • 数据
    • PostgreSQL 15
      • Drizzle ORM
        • drizzle-kit
  • 外部服务
    • DeepSeek
    • 火山引擎 SeedTTS
  • 工具链
    • TypeScript 5
    • Biome
  • 部署
    • Docker
    • Docker Compose
    • Nginx
    • GitHub Actions

功能

  • M0 垂直切片:内容 → 会话 → 评分 → 记录/复核,共用同一套 session/score_run
  • M1:分组、作业、复核改分、任务级多标准评分(选量具建批次 → 队列逐条评 → α/β 对照)
  • M2:director:model 的隔离扮演 + 世界裁定,裁定者写不了台词、扮演者改不了世界,边界由 strict 工具 schema 保证
  • 内容与量具:cases/ 9 个场景(急性心梗、分诊隐性出血、双床优先级、凌晨支付故障等),instruments/ 4 份量具(临床决策、沟通、问病史、情境推理)
  • 教学面:场景可见性双门(已上架 + 命分组白名单,都没设=看不见)、/go/<slug> 直达、作业导出、班级配额闸门
  • 诊断面:/api/ops/report(模型闸门在途、评分队列深度、备份分项年龄)、错误检索、export:trace 一行一步决策导出
  • 验证:74 个测试文件走 bun test,无浏览器 e2e;bun run smoke 打真实构建产物,60 秒内查健康、鉴权、开局、上传、结束
  • 未完成:M3 历史数据导入与旧库转只读(未开始,导入干跑也没做);TTS 真实供应商闭环(缺凭据未验);首屏 124 KB gzip(目标 ≤60 KB)

启动流程

# 前置:本机 Postgres
cp .env.example .env            # 填 DATABASE_URL / JWT_SECRET / DEEPSEEK_API_KEY
createdb -h localhost -U postgres theatron_dev

bun install
bun run setup                   # migrate + cases:sync + seed,一步到位
bun run dev                     # http://localhost:3100
bun run build && bun run start  # 生产形态,同一端口
bun run check                   # typegen + tsc
bun test                        # 单测
bun run build && bun run smoke  # 打真实构建产物再冒烟
bun run score <sessionId> --instrument <slug> --replay   # 换标准重放

bun run seed 会建演示账号,口令写死在 scripts/seed.ts;生产环境里它见到默认口令会直接拒绝执行,首个教师走 bun run bootstrap:teacher 并显式给口令。

边界与已知问题

  • 单实例假设写在实现里:评分 worker 进程内每秒轮询串行消费、图片只存本地磁盘、DB 客户端不加连接池、compose 只有一个 app 容器。横向扩得先改这四处。
  • 三处双源还在:内容(宿主 shared/cases 文件 vs 库)、量具(instruments/*.md vs 库)、评分实现(两套机制无法互证)。TODO 的「结束换代脆弱期」四条一条没勾。
  • 宿主 shared/cases 是运行时输入,却不在任何备份数据集里:容器重建能保住(bind mount),备份恢复演练覆盖不到。
  • 一键登录 /login?u=&p= 明文留在浏览器历史和 nginx access log,只能用于一次性演示账号;/go/<slug> 的安全靠 startSession 那道闸,别绕过它开局。
  • .env.example 里没有 DEEPSEEK_API_KEY,README 却要求填;VOLC_TTS_SPEAKER 示例值与代码兜底值不一致。Bun 只在环境缺该变量时才读 .env,shell 残留的旧 DATABASE_URL 会静默生效(现象是「关系不存在」),启动那行 [db] 库名@主机 用来对账。
  • 遗留物:scene/turn.ts(director:none 的确定性实现)只剩测试在消费;uploads 路由注释还写着「登录切片没落地」。
  • Apache-2.0;/terms、/license 页已挂,但「不承诺稳定过渡」不等于可以无声丢数据,交接范围要在试用前写清。

文档索引