Files
eightbf-analysis-go/docs/服务端开发设计要求.md
gozerosandCursor 0c45b86d67 chore: 初始化 eightbf-analysis-go(对齐 Java 会商作业服务)
Go 实现 /api/v1/analysis 契约、Demo/Live Worker、DeerFlow SSE;含设计文档与冒烟脚本。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-03 00:13:46 +08:00

18 KiB
Raw Permalink Blame History

eightbf-analysis 服务端开发设计要求

适用范围:本仓库(8BF「多岗位会商」后台作业服务,Spring Boot 2.0.9 + JDK8)。
目标:关浏览器后会商仍能跑完;状态以服务端为准;产品前端只经 /analysis-api 访问本服务。
配套:根 README.md、内网迁移.md、源码包注释(package-info / 各层类注释)。

本文是开发约束与设计验收标准,不是空泛愿景。改功能前先对照;评审以本文为准。


1. 定位与边界

1.1 本服务负责

职责 说明
会商作业生命周期 创建 / 查询 / 取消 / 续跑 / 热更新 / 注入 / 结果
服务端执行 Demo 假进度或 Live 真模型链条;关页不断
状态与流式内容落库 status、plan、dialogues、result 等以 MySQL 为准
对接 DeerFlow 登录、建线程、消费 SSE;失败策略由本服务决定
岗位可见性投影 按账号裁剪对话后再返回前端

1.2 本服务不负责

  • 产品登录 / SSO / 账号体系本体(只认请求头里的账号与角色)
  • 岗位会商「DeerFlow joinStream」那条链路(岗位侧保持原样,不经本服务挂起)
  • 浏览器直连 DeerFlow roundtable-jobs 跑 8BF(禁止作为正式路径)
  • Fusion 其它 API(/zl_consumer、/deerflow-api 等)

1.3 硬原则(不可破)

  1. 状态权威在服务端:前端轮询展示;刷新必须带草稿里的 jobId,禁止用「task 下最新一场」冒充重连。
  2. 一开一场:每次「开始研讨」新建作业;同一 taskId 可并存多场;不自动取消旧场。
  3. 浏览器可走,作业不能丢:取消只靠 cancel_flag + Worker 协作停;不要假设 SSE 连着用户标签页。
  4. 失败不当成功:模型不可用、席位空输出、流无终帧、自动换模 → 整场 error;禁止刷完链条再标 done 解锁前端下一阶段。
  5. 热更新以库为准:人工干预 / 模型 / inject 写在库里;Runner 每席与 flush 前从库合并,禁止内存态盖掉用户 PATCH。

2. 总体架构

浏览器 → Nginx /analysis-api → eightbf-analysis (:18097)
                                    ├─ web(HTTP + DTO)
                                    ├─ service(编排 / 鉴权投影 / 落库 / DemoWorker)
                                    ├─ worker(LiveWorker 调度 + LiveChainRunner 执行)
                                    ├─ deerflow(Auth / Client / SSE)
                                    └─ MySQL eba_analysis_job
                                         ↑
                                    DeerFlow Gateway(仅服务端访问)

2.1 分层要求

包 允许 禁止
web 参数校验、HTTP 状态码、组 Caller、调 service 直接 JDBC;直接调 DeerFlow;写业务状态机细节
service 作业编排、权限、仓储、Demo 推进 拼 DeerFlow SSE;复制一套状态机与 Runner 打架
worker 真模型调度与链条;读写作业检查点 暴露新 HTTP;绕过 Repository 改库
deerflow HTTP/SSE、token、模型校验、流式折算 知道「作业业务规则」以外的前端约定;改作业 status
domain 实体、状态常量 Spring 注解、IO

依赖方向只能向内:web → service → domain;worker → service/domain + deerflow。禁止 deerflow 依赖 web。

2.2 运行模式

配置 行为
analysis.mode=live + eba.worker.demo-enabled=false 正式/联调默认:LiveWorker 领 queued,LiveChainRunner 调 DeerFlow
analysis.mode=demo + eba.worker.demo-enabled=true 仅演示假发言;不得当内网验收真模型

两模式互斥清晰:禁止「一边假进度一边又去打 DeerFlow」。


3. 领域与状态机

3.1 作业(AnalysisJob)

一场作业 = 服务端上的一场研讨录像带。关键袋子:

字段 用途 开发约束
id 主键;草稿必须存 新建生成;禁止按 task 覆盖
status / phase 生命周期 / 细阶段 状态字符串只用 JobStatus 常量
plan 检查点与开关 至少:seatIndex、manualIntervention、pendingInject、awaitReason、seedMessage
dialogues 气泡流式展示 增量追加;终帧勿无脑整段覆盖丢掉 <think>
threadIds DeerFlow 线程 有则续跑跳过 init
access 岗位账号限制 结构与前端约定一致
progress / result 进度文案 / 终态汇总 error 时清掉误导性 summary / 共识

3.2 状态流转

queued → running → awaiting_input ⇄ queued(resume)
                 → done
                 → error
                 → cancelled
状态 含义 允许的写操作
queued 等 Worker 领取 cancel;inject;patch plan
running 执行中 cancel;inject;patch plan
awaiting_input 澄清或人工卡点 resume;cancel;inject;patch plan(关干预可自动放行)
done / error / cancelled 终态 禁止 resume / inject / 改计划

实现要求:

  • 取消:先 cancel_flag=1,Runner/Demo 每拍检查;再落 cancelled。
  • 续跑:仅 awaiting_input → queued;检查点用 plan.seatIndex,勿重新从 0 开场除非产品明确要求。
  • 人工干预默认安全:manualIntervention 缺省视为 true(宁可多卡一次)。

3.3 真模型链条语义

顺序固定:

  1. 校验席位 ≥ 2、模型在 DeerFlow 列表中(未知模型 拒绝,禁止静默换模)。
  2. 无 threadIds 则 init;有则续跑。
  3. 对每个席位:总控派活 → 该席输出 →(若开人工干预且非末席)进入 awaiting_input。
  4. 全席成功后总控收口;交付不全或对话已有 error 气泡 → 不得 done。

一席失败立刻 error 停场,禁止「继续跑完再汇总错误」。


4. API 设计要求

4.1 约定

  • 前缀:/api/v1/analysis
  • 包体:ApiResponse<T> → { code, message, data };成功 code=0
  • HTTP:201 创建;404 找不到;403 无权限;400 状态/参数不允许
  • 身份头:X-Account-Id、X-Analysis-Role(admin / task_owner / position_owner / viewer)
  • 探活:GET /health 不鉴权

4.2 接口清单(保持稳定)

方法 路径 说明
POST /jobs 新建;返回投影后快照
GET /jobs/{id} 轮询 / 重连主路径
GET /jobs?taskId=&latest= 兼容查询;正式重连勿依赖此接口替代 jobId
POST /jobs/{id}/cancel 仅责任人/创建人/管理员
POST /jobs/{id}/resume 澄清回答或人工「继续」
POST /jobs/{id}/plan 热更新人工干预 / 模型
POST /jobs/{id}/inject 运行中补充;空文本 400
GET /jobs/{id}/result 裁剪字段的结果视图
GET /health 探活

4.3 兼容与演进

  • 字段名尽量与前端 mapJob 一致,少做二次映射。
  • 新增字段:只增不改语义;废弃字段先双写/文档标明,再删。
  • 破坏性变更(状态字符串、必填头、错误码语义)必须同步改 code-zk 客户端与本文,并在 PR 说明迁移步骤。

5. 鉴权与投影

配置:analysis.auth.enabled(本机可 false;内网建议 true)。

角色 读 resume/inject/改 plan cancel
admin / task_owner / 创建人 / access 任务责任人 全量 是 是
position_owner(绑定了席位) 本席 + system/user/leader 是 否(除非兼责任人)
无账号且强制鉴权 否 否 否

投影规则(即使本机关强制鉴权,声明了 position_owner 也要裁剪,便于验收):

  • 岗位账号:只见 system / user / leader + 自己 agentId 的 special 气泡。
  • result.seatDeliveries 同步裁剪。

新接口若返回作业快照,必须走 JobAccessService.projectForCaller,禁止裸库对象出站。


6. 持久化要求

  • 表:eba_analysis_job(见 schema.sql);大字段 JSON 文本列。
  • 正式库:MySQL utf8mb4;H2 仅应急联调。
  • 写库路径统一经 JobRepository;流式 flush 可节流(约 ≥150ms),但取消旗、状态跃迁必须及时。
  • 索引:task_id / status / owner_account_id 保持;新增查询条件先补索引再上线。
  • 启动执行 schema.sql 允许 continue-on-error(重复索引);禁止靠静默失败掩盖真实 DDL 错误而不验收。

7. DeerFlow 集成要求

  1. 仅服务端持有 Gateway 账号;口令走环境变量,不写死进正式配置提交物。
  2. Token 复用并提前刷新;并发登录要有同步,避免打爆登录接口。
  3. assertModelAvailable:指定模型不在列表 → 开跑前失败;禁止依赖 Gateway「自动换模」。
  4. SSE:识别 event:error、model_switch、无终帧;一律映射为 invalidDelivery / 业务失败。
  5. 流式展示:reasoning_content 与正文一并折成 <think>…</think> + 正文,保证前端两边都能流。
  6. 超时:stream-idle-timeout-ms 可配;取消检查嵌在读循环里。
  7. 本服务不得把 DeerFlow 内部错误原文无差别甩给用户;要带「当前模型」提示,便于换模重开。

8. Worker 与并发

  • LiveWorker:FIFO 领 queued;先占坑改 running 再异步执行;默认并发上限 2(可配置化时须有文档)。
  • 单机进程内线程池即可;多实例部署前必须先设计「领取互斥」(乐观锁 / FOR UPDATE / 外部队列),否则禁止双活抢同一作业。
  • DemoWorker 与 LiveWorker 开关清晰;生产关闭 demo。

9. 配置与环境

项 要求
端口 默认 18097;经 Nginx /analysis-api 反代
数据源 EBA_DB_* 覆盖;正式禁用默认弱口令
DeerFlow EBA_DF_* 覆盖
鉴权 内网打开 analysis.auth.enabled=true
密钥 不进 Git;参考 docs/env.sample

变更配置项必须更新:application.yml 注释、env.sample、内网迁移文档。


10. 编码与注释规范

  • 语言:JDK 8;禁止使用仅高版本语法(除非整体升级并改构建)。
  • 注释:新代码须详细中文注释(为什么 / 约定 / 边界),禁止只复述变量名;有用旧注释不得顺手删。
  • 异常:业务用明确文案;SecurityException → 403;IllegalStateException / IllegalArgumentException → 400。
  • 日志:见 §11 日志与收集;关键作业必须带 jobId=;禁止打 token、密码、完整用户隐私对话到 INFO(必要时 DEBUG 且脱敏)。
  • 测试:至少覆盖状态不允许的操作、权限拒绝、模型校验失败、取消中断;无单测的改动须在 PR 写清手工验收步骤。

11. 日志与收集

本节是运维可落地的约束:先保证「能查、能关联、不泄密」,再谈集中平台。

11.1 目标

目标 说明
排障 任意一场会商能用 jobId(必要时再加 requestId / taskId)串起创建→领取→DeerFlow→卡点→结束
审计 谁 cancel / resume / inject / 改 plan 可追溯(带账号头,不带口令)
采集 日志形态稳定,便于本机落盘或内网 Filebeat/Vector/rsyslog 采集
安全 密钥与大段会商正文默认不上日志;完整内容以库表为准

11.2 输出与落盘(应用侧)

  1. 默认打 stdout/stderr(systemd / Docker / 进程托管都能直接收)。
  2. 正式环境建议同时挂 滚动文件(便于采集 agent 扫目录),例如:
    • 路径:/var/log/eightbf-analysis/app.log(可用环境变量覆盖)
    • 滚动:按天或按大小(如 100MB × 14 份);禁止无限单文件撑爆磁盘
  3. 使用标准 Logback(Spring Boot 默认即可);自定义 logback-spring.xml 时须文档化路径与滚动策略。
  4. 日志级别:
    • 生产默认:com.eightbf.analysis=INFO,框架 WARN
    • 排障临时:单 logger 升 DEBUG,用完改回;禁止长期全局 DEBUG

11.3 格式(方便采集与检索)

每条业务日志尽量同构,文本键值即可(先不强制 JSON,内网解析成本更低):

2026-11-02 23:40:01.123 INFO  [...] c.e.a.worker.LiveWorker - 开始真模型会商 jobId=job_xxx requestId=req_yyy

推荐固定键(有则必带):

键 何时
jobId= 一切与作业相关的日志
requestId= 创建、与前端对账
taskId= 仅当本次操作明确带了 task(勿刷屏)
accountId= cancel / resume / inject / patchPlan(来自请求头)
phase= / status= 状态跃迁、暂停等待
model= 模型校验失败、开跑、换模拒绝

禁止:无键的散文导致无法 grep;同一事件重复刷屏(流式 delta 不要每字打日志)。

11.4 必打事件(最低集合)

级别 事件
INFO 作业创建;LiveWorker 领取并开始;进入 awaiting_input(澄清/人工);resume 重新入队;会商 done;cancelled
INFO DeerFlow 登录成功(只打过期秒数,不打 token);init 完成(席位数)
WARN Worker/Runner 未捕获异常;DeerFlow HTTP≥400;模型不可用;流无终帧 / model_switch 拒绝;取消后仍异常退出
ERROR 进程级故障(数据源不可用、反复登录失败等)

HTTP 访问明细优先靠 Nginx access log(/analysis-api);应用内不必再镜像每一条轮询 GET(会淹没真正故障)。

11.5 脱敏与禁止项

禁止写入日志:

  • DeerFlow / DB 密码、Bearer token、Cookie
  • 完整 dialogues / 模型长回复 / inject 全文(库里已有;日志最多截断摘要 ≤200 字且仅 WARN/ERROR)
  • 身份证等敏感 PII(本服务一般无;若 payload 带入勿原样打印)

允许: 错误摘要、模型名、agentId、席位序号、HTTP 状态码、耗时毫秒。

11.6 采集与留存(运维侧)

按内网条件选一种,应用不绑定某一家商业 APM:

eightbf-analysis
    → stdout 和/或 /var/log/eightbf-analysis/*.log
        → Filebeat / Vector / rsyslog
            → 内网 Loki / ELK / 现有日志平台
Nginx /analysis-api access/error
    → 同上(务必带上游 upstream 耗时,便于区分网关与本服务)

要求:

  1. 采集配置与本服务同主机交付说明写进内网迁移或运维手册(路径、用户、权限)。
  2. 应用日志与 Nginx 日志时间对齐(统一时区 Asia/Shanghai)。
  3. 留存:建议热数据 ≥14 天,冷存按等保/单位规定;磁盘告警纳入主机巡检。
  4. 检索演练:上线后用一场真实 jobId 能在平台搜到「创建→开始→结束/失败」完整链,算采集验收通过。

11.7 与业务库的分工

数据 放哪
会商正文、气泡、检查点 MySQL(权威)
何时开始/停/失败、对接 DeerFlow 是否成功 日志(排障)
进度百分比给用户看 progress 字段,不是日志

不要把日志当第二份 dialogues 备份。

11.8 可观测(日志之外,可排期)

最低仍须保留:GET /api/v1/analysis/health。

建议后续:作业耗时、DeerFlow 错误码计数、queued 堆积告警(指标/告警通道另定,不替代日志)。

内网功能验收仍见 内网迁移.md。


12. 变更流程(本仓)

  1. 先方案:非琐碎改动先说清影响面(状态机 / API / 前端约定 / DeerFlow),用户批准后再写代码。
  2. 范围克制:只改任务所需;不顺手重构无关层。
  3. 联调:本服务 + code-zk /analysis-api;sim:build 后走一遍开研讨 / 干预 / 刷新。
  4. 提交:仅用户要求时 commit/push;勿夹带密钥与 target/、data/。
  5. 文档同步:行为变了必须改本文相关条款 + README/内网迁移(若涉及部署)。

13. 验收检查表(开发自测)

  • 新建两场同 taskId,互不覆盖;刷新只跟各自 jobId
  • live 模式关浏览器后作业仍推进或停在明确卡点
  • 取消后 SSE/推进停止,状态 cancelled
  • 人工干预开:每席(末席前)进入 awaiting_input;关且在卡点时自动续跑
  • inject 后下一席派活带上补充;气泡可见用户原文
  • 未知模型开跑失败,不进入 done
  • position_owner 看不到其它席 special 气泡;不能 cancel
  • 失败场无「可解锁下一阶段」的假 summary / 100% 共识
  • 一场作业可用 jobId 在日志中串起创建→开始→结束/失败;无 token/密码泄露
  • 正式环境日志有滚动策略;采集路径写进运维说明并能检索
  • 注释与本文冲突时:以代码行为为准并回修文档

14. 反例(明确禁止)

  • 浏览器直连 DeerFlow 跑 8BF 正式会商
  • 用 GET ?taskId= 代替草稿 jobId 做刷新重连主路径
  • 信任 DeerFlow 自动换模并当成功
  • flush 时用旧内存 manualIntervention/model 盖掉库里用户热改
  • 终态仍允许 inject/resume 而不报错
  • 多实例无互斥双活抢作业
  • 把演示 DemoWorker 开进生产当真模型
  • 把 token / 密码 / 完整 dialogues 打进 INFO;流式 delta 逐字打日志
  • 只写控制台、无滚动、无采集说明就当「生产可运维」

文档版本与仓库代码同步维护;重大行为变更请改本节并在提交说明中引用。