# report_bioflow_dev2 —— P1 生信服务地基(开发 agent A2) > 任务书:docs/team/brief_bioflow_dev2_p1.md > 工作分支:feature/bioflow-conversation-orchestration(保持,不切换) > 纪律:不 commit/push/部署;不碰生产服务(8090/8091);服务层零 pydantic;零硬编码 ## 1. 交付概览 | 模块 | 说明 | |---|---| | bioflow_service/(新增,独立部署树) | 生信服务 8098:FastAPI + 独立 SQLite + docker 执行器 + 状态机 + 规划器 | | sdk/app/routers/bioflow_proxy.py(新增) | BFF 流式反代 + 报告登记端点 | | sdk/app/main.py(2 行改动) | BIOFLOW_ENABLED 开关注册反代路由 | | sdk/app/config.py(2 键) | bioflow_enabled(默认 false)/ bioflow_service_url | | docs/team/report_bioflow_dev2.md | 本报告 | ## 2. 模块清单(bioflow_service/app/) **明文白名单(编译管线保留 .py)** - `config.py` —— BIOFLOW_ 前缀配置(pydantic-settings):image_map_path(仓库内 bioflow_kernel/stage_image_map.json,按 __file__ 推导 repo 根)、data_root/ jobs_dir/db_path/archives_dir/flow_docs_dir(全部可覆盖,无写死宿主路径)、 concurrency=3、stage_timeout_sec、host_uid_env=BIOFLOW_HOST_UID(默认服务进程 os.getuid() 推导)、container_data_dir=/bioflow-data、upload 白名单、DOCGEN_LLM_* 沿用 docgen 直连模式;bba_env 属性从配置推导 BBA_PFAM_DIR/BBA_RAG_MAPPING_FILE。 - `schemas.py` —— pydantic 请求/响应模型(ChatStreamRequest/JobCreateRequest/ StageConfirmRequest/ArchiveCreateRequest 等 10 个;STAGE/JOB 状态 Literal)。 - `db.py` —— 独立 SQLite(WAL)三表 + 幂等迁移(CREATE IF NOT EXISTS + PRAGMA table_info 增量列):bio_jobs/bio_stages/bio_archives(字段与实施计划 §六对齐); bio_stages UNIQUE(job_id, stage),job 删除级联。 - `main.py` —— FastAPI 组装 + lifespan(init_db→init_context→recover→shutdown); health 端点 /api/bioflow/health(db 连通状态字段,FR-BIO-025 探活用)。 **服务层(全部 dataclass/dict/stdlib,无 pydantic,Cython 编译)** - `services/bioflow/kernel.py` —— Docker 执行器:读 A1 stage_image_map.json (75 阶段/8 镜像),按阶段→镜像拼 docker run(--user uid/三目录挂载/BIOFLOW_DATA 只读/-e BBA_*/--cpus/--memory/--rm + `python -B -m cli.run_{stage}`,list 无 shell); cpu/memory 白名单回落;超时 terminate→kill;日志落任务 logs/ + 尾 80 行截断; 跨环境 note 透传(编排层 P2 拆分依据)。 - `services/bioflow/runner.py` —— 任务状态机:created→await_inputs→running→ paused/done/failed/archived;阶段 waiting→await_files→waiting_confirm→queued→ running→done/failed/skipped;输入检查(FR-BIO-005)→确认门(FR-BIO-008,confirm `_confirmed` 旁路防二次确认门)→并发信号量(BIOFLOW_CONCURRENCY 默认 3, FIFO 排队)→docker→manifest 判成败(no_manifest/manifest_failed/timeout/ exit_code 四因);pause 停阶段边界、resume 不跳过确认、rerun/skip 状态校验; 重启恢复 recover():running 阶段按 manifest 判成败(有 success manifest→done 否则重跑)、done 不重跑(attempt 不变)、archived 跳过。 - `services/bioflow/catalog.py` —— 阶段目录(映射表驱动 + STAGE_INFO/STAGE_DEPENDENCIES 静态快照,不运行时反射);73 阶段(真实表 75 去重复校验)、基因家族链 21 阶段、 手写参数 schema(identify 11 参数含 default/source/help,source 标注 contract/user/runtime 供表单默认值来源可见);validate_chain 未知阶段拒绝 + auto_fill_prerequisites 前置补全(拓扑排序依赖在前)。 - `services/bioflow/planner.py` —— 路径规划:复用句式(「用 XXX 存档任务的方法…」) → archive 直接装载;否则知识库检索(vstore/flow.py FlowStore 本地实例混合检索, 失败降级 llm_only)+ LLM 候选(DOCGEN_LLM_* 直连,JSON 模式 + 修复重试)→ catalog 硬校验(未知阶段拒绝、前置补全);PLAN_LLM_TIMEOUT/PLAN_UNAVAILABLE 降级内置兜底链。 - `services/bioflow/manifests.py` —— manifest 解析校验(manifest_v1.schema 必需字段; 定位 final/{prefix}.{stage}.manifest.json,无兜底防跨阶段误配);跨阶段输入定位 (artifacts full_path 解析 + 存在性检查)。 - `services/bioflow/events.py` —— 事件总线(12 类事件全集 + job_recovered): 进程内 pub/sub(SSE 订阅)+ 内存环形缓冲 + events/{job_id}.jsonl 持久 (重启回放)+ snapshot 首帧 + replay(after_seq) 断线回放。 - `services/bioflow/llm.py` —— LLM 直连(docgen 同款:指数退避 + JSON 修复 + LLMTimeout/LLMUnavailable 分型)。 - `services/bioflow/params.py` —— 参数预填(契约文档参数解析 + BBA_* 默认值标注 + 存档参数预填)。 - `services/bioflow/reports.py` —— 报告登记(报告 HTML 定位/fig:// 图清单提取/ 登记载荷(job_id/标题/md/图清单)+ 内部 HTTP 调 BFF 反代端点,BFF 不可达降级)。 - `services/bioflow/archive.py` —— 存档打包(final/结果+logs/过程+events.jsonl 过程记录+input 原始文件;include_intermediate 开关含中间文件;zip 下载)。 - `services/bioflow/store.py` —— 三表 CRUD(upsert/list/get/delete;归档精确优先 模糊匹配、reused_count 计数)。 - `services/bioflow/intent.py` —— 意图分流(cases_bioflow_intent.md 冻结词典同表: export→edit→docgen→附件 docgen→bioflow_qa(FLOW_QA 命中即拦)→bioflow (动词+家族语境词/分析对象)→qa 漏判安全;复用方法名提取 3-20 字符; >2000 字符截断判定)。 **路由** - `routers/bioflow.py` —— health/catalog(?stage=/?chain=gene_family)/ chat/stream(SSE:意图分流→plan_ready 候选→心跳;plan seq 编码 data 载荷直出)/ ask(知识库引用块:source/title_path/path/cite/line/text)。 - `routers/biojobs.py` —— jobs 全套(POST 201/列表 rowid 倒序分页过滤/详情/删除 running 409);files(multipart 流式逐块写盘 + 扩展名白名单 + 大小上限 413 + server_path 登记限 data_root/任务目录白名单 403);stages confirm(参数校验 未知 422)/rerun(带参数→waiting_confirm)/skip/pause/resume/manifest(manifest 对象 + 日志尾 80 行);events SSE(snapshot 首帧 + 回放 + 实时订阅 + 任务终态 关闭 TC-BIO-API-081);report revise(最小接口:无图目标 422/绘图阶段定位)。 - `routers/bioarchives.py` —— archives 全套(存档 201 重名 409/列表/详情/删除 磁盘+行/run 复用/按名匹配 404 ARCHIVE_NOT_FOUND/download zip/report HTML); reports/register(归一化 + 图清单回填,BFF 反代落 doc_artifacts)。 **构建与运维** - `build.sh` —— 复制 tooling/compile/compile_sdk.sh 改白名单(明文:main.py/ config.py/schemas.py/db.py + __init__.py 规则;schemas= pydantic cyfunction 教训、 db= 断流崩溃根治教训);16 模块 Cython+GCC 全过(实测 /tmp 输出 16 .so, 编译树 import OK)。 - `restart.sh` —— 8098 重启(零硬编码:路径脚本位置推导 + 环境变量覆盖)。 - `tests/` —— pytest(-m bioflow_unit 127 用例 / -m bioflow_api 39 用例)。 ## 3. 自测结果(2026-09-06,服务器 venv 3.12.13) | 命令 | 结果 | |---|---| | `python -m pytest tests/ -q` | **166 passed**(15.7s,0 warnings) | | `python -m pytest tests/ -q -m bioflow_unit` | 127 passed | | `python -m pytest tests/ -q -m bioflow_api` | 39 passed | | build.sh → /tmp 编译 + 编译树 import | 16 模块 .so,import OK | | BFF BIOFLOW_ENABLED=false/true 双态 import | 均 OK(反代路由 2 条) | | 真实 stage_image_map.json 加载 | 75 阶段/8 镜像/identify 11 参数/命令拼装正确 | | /home/xzq 硬编码扫描(app+tests) | 0 处 | 全部 mock docker/LLM/vstore(tmpdir + fake 执行器),不连任何生产服务 (8090/8091/8094/8098 从未探测)。 ## 4. 已知限制(交 TL 裁决) 1. **SSE 传输层测试法**:本环境 fastapi TestClient 的 sync stream 包装与 httpx ASGITransport 对永不结束的 SSE 流均存在首帧不达/整流缓冲问题(dbg3/dbg6 最小 复现实证),SSE 用例改为端点直调迭代 body_iterator + 模块级共享 event loop (anyio portal 绑定首个 loop,跨 loop 死锁实证)。生产部署(uvicorn 真实网络 SSE)不受影响;如 TL 要求 TestClient 原生流测试,需另行排查 fastapi/anyio 版本组合。 2. **catalog 阶段数口径**:任务书口径 73;A1 交付 stage_image_map.json 实为 75 阶段(含 module=null 的 synteny_plot_revise 与 executable=false 的 msa/ enrichment)。kernel 对 module=null 阶段拼命令前置拦截(UnknownStageError), catalog 全量返回 75。以 A1 交付物为准,口径差异记录备查。 3. **gene_family 链为 21 阶段**(A1 family_chain 实测 21,任务书口径约 18); 以 stage_image_map.json family_chain 字段为唯一事实源。 4. **FlowStore 生产数据未入库**:vstore/data/flow_docs 目录当前不存在(A4 自测 在 tmpdir 完成;真实语料 106 文件/724 块需 TL 部署时跑 vstore/scripts/ingest_flow_docs.py 入库)。planner 已按本地实例对接(加载成功 零块时检索空 → LLM 候选照常,降级链完整);生产前入库即可获得 KB 增强 (llm_rag source)。 5. **chat/stream 是规划入口**:plan_ready 后保持心跳连接,任务创建走 POST /jobs(P1 契约);会话级任务事件经 /jobs/{id}/events 订阅。若 TL 要求 chat/stream 单流贯穿任务事件(A3 前端形态),需 P2 加会话级订阅联动。 6. **报告登记 BFF 侧 doc_artifacts 落库**:bioflow_proxy.register_report 字段 (job_id/session_id/标题/md_path/图清单)已按约定实现;图清单字段当前存于 请求体(doc_artifacts 表无 figures 列,如需持久化图清单需 TL 裁决加列——已 在 payload 透传中保留)。与 A3 report 模板的 fig:// 渲染协调点:报告 HTML 内 fig://id 引用清单由生信服务 reports.extract_figure_refs 提供。 7. **意图词典判定为启发式快照**:与 cases_bioflow_intent.md 冻结表逐条对齐 (40 用例表全过);家族名语境词规则(「SPL 会议」不命中)为词典实现注意第 1/4 条的工程化解读,最终同表双跑一致性以 TC-BIO-INTENT-047(L2 用例)验收。 8. **事件持久化目录**:events/{job_id}.jsonl 与任务目录分离(bioflow_data/ jobs/events/);归档时打包进档案根。任务删除不清理事件文件(留审计), 如需一并删除交 TL 裁决。 9. **镜像 tag 后缀**:docker_image_tag_suffix 默认 "dev"(A1 交付物 :dev); 发布流水线(P2 FR-BIO-032 compile_bioflow.sh)需按终态 tag 覆盖环境变量。 10. **pytest.ini python_files 陷阱**:本仓库 pytest 9.1.1 下 `python_files = ["test_*.py"]`(列表形式)会导致目录级收集 0 用例(单文件显式传参正常), 已用字符串形式 `python_files = test_*.py` 规避并实证;写报告备案防踩。 ## 5. 交付物清单 | 文件 | 说明 | |---|---| | bioflow_service/app/{config,schemas,db,main,context}.py | 应用骨架(4 明文 + context) | | bioflow_service/app/services/bioflow/*.py | 服务层 12 模块(零 pydantic) | | bioflow_service/app/routers/{bioflow,biojobs,bioarchives}.py | 端点三层 | | bioflow_service/build.sh / restart.sh | 编译白名单管线 / 重启脚本 | | bioflow_service/pytest.ini | markers + python_files(字符串形式陷阱规避) | | bioflow_service/tests/*.py | 10 文件 166 用例 | | sdk/app/routers/bioflow_proxy.py | BFF 流式反代 + 报告登记 | | sdk/app/main.py(+2 行)/ sdk/app/config.py(+2 键) | BFF 最小改动 | | docs/team/report_bioflow_dev2.md | 本报告 |