docs: bump 0.59.1 + PROGRESS 汇总 15 commit 重构 + DESIGN §2 架构树同步

- core/__init__.py 0.59.0 → 0.59.1(patch:全部内部重构/测试/修复,对外行为零变化)
- PROGRESS:07-23 条目(七项重构 + 2 bug + 测试 286→351 + 测试库基建)、
  文件清单刷新(loop/llm_transport/tool_registry/storage 三分/web routers 布局)、
  最后更新行
- DESIGN §2:架构树补 llm_transport/tool_registry/storage 三分/web 拆分后布局
  (实施后与代码偏离,按「同步改回」条款修正);CHANGELOG 按「纯内部重构/修复
  不记」规则不动

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-07-23 14:29:19 +08:00
parent bcd2b90942
commit 1332139c9e
3 changed files with 26 additions and 15 deletions

View File

@ -21,7 +21,9 @@ zcbot/
├── core/ ├── core/
│ ├── capabilities.py # ModelCapabilities,从 yaml 加载 │ ├── capabilities.py # ModelCapabilities,从 yaml 加载
│ ├── llm.py # LiteLLM 封装,按 capabilities 自动启 features │ ├── llm.py # LiteLLM 封装,按 capabilities 自动启 features
│ ├── loop.py # ReAct 主循环 + 协作式 cancel │ ├── loop.py # ReAct 主循环 + 协作式 cancel(控制流)
│ ├── llm_transport.py # wire 层健壮性:畸形/吐空检测+留痕+非流式降级重试(自 loop 析出)
│ ├── tool_registry.py # 声明式工具注册表((组名,gate,factory);§7.5 #7 代码强制)
│ ├── probe.py # 真实探测对账 yaml 声称的能力 │ ├── probe.py # 真实探测对账 yaml 声称的能力
│ ├── session.py # 消息列表 + meta,落 PG │ ├── session.py # 消息列表 + meta,落 PG
│ ├── skills.py # SkillRegistry(渐进披露,多来源) │ ├── skills.py # SkillRegistry(渐进披露,多来源)
@ -29,18 +31,20 @@ zcbot/
│ ├── memory.py # per-user .memory/ 双层记忆 │ ├── memory.py # per-user .memory/ 双层记忆
│ ├── shortcuts.py # 快捷指令(入口层确定性展开) │ ├── shortcuts.py # 快捷指令(入口层确定性展开)
│ ├── paths.py # task_dir db form 归一 │ ├── paths.py # task_dir db form 归一
│ ├── storage/ # SQLAlchemy 2.x ORM │ ├── storage/ # SQLAlchemy 2.x ORM;usage(计费写)/telemetry(失败埋点)/usage_report(聚合读)三分
│ ├── scheduler.py # 定时任务(§8.5) │ ├── scheduler.py # 定时任务服务层(§8.5;执行引擎在 web/scheduler_runner)
│ ├── wechat/ # 渠道:ilink / wecom / service / inbound(§8.7) │ ├── wechat/ # 渠道:ilink / wecom / service / inbound(§8.7)
│ ├── sandbox/ + executor*.py # Executor ABC + Docker per-user 容器池(§7.5) │ ├── sandbox/ + executor*.py # Executor ABC + Docker per-user 容器池(§7.5)
│ └── agent_builder.py # 装配 lib:build_agent / system prompt │ └── agent_builder.py # 装配 lib:build_agent / system prompt
├── tools/ # fs / shell / run_python / skill / 媒体 / 检索 / host-side 域工具 ├── tools/ # fs / shell / run_python / skill / 媒体(共享原语 media_common)/ 检索 / host-side 域工具
├── skills/<name>/ # SKILL.md + references / scripts / assets ├── skills/<name>/ # SKILL.md + references / scripts / assets
├── rendering/ # 平台渲染层 md→docx/pdf(§8.6) ├── rendering/ # 平台渲染层 md→docx/pdf(§8.6;块收集器/inline 切分单一事实源在 common)
├── prompts/system/general_v1.md ├── prompts/system/general_v1.md
├── config/{agent.yaml, models/*.yaml, media/*.yaml} ├── config/{agent.yaml, models/*.yaml, media/*.yaml}
├── workspace/users/<user_id>/{.memory/, .skills/, <working_dir>/} ├── workspace/users/<user_id>/{.memory/, .skills/, <working_dir>/}
├── web/{app.py, auth.py, admin.py, broker.py, sinks.py, static/} ├── web/ # app.py=工厂+lifespan 编排;routers/*(11 路由模块,register 范式);
│ # background/scheduler_runner/wechat_runner(后台协程);runs(BG worker)
│ # + auth/admin/broker/sinks/common/schemas/model_gate/userfiles/static/
├── db/migrations/ # alembic ├── db/migrations/ # alembic
└── main.py # 入口:web / db / probe / user └── main.py # 入口:web / db / probe / user
``` ```

View File

@ -2,7 +2,7 @@
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9` > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`
最后更新:2026-07-21(扫描件 PDF 直读 read_document:方舟文档理解 base64 内联,markitdown 死路补位,bump 0.58.55) 最后更新:2026-07-23(架构审查落地:15 commit 内部重构大扫除 + 测试 286→351 + 测试库基建,bump 0.59.1)
--- ---
@ -23,6 +23,7 @@
### 2026-07 ### 2026-07
- **07-23 / 0.59.1 / 架构审查落地:15 commit 内部重构大扫除(对外行为零变化)**:全库审查(web/核心循环/工具渲染/数据层四路并行探查)后按优先级逐项执行。①**web 拆分**——app.py 3972→197 行:73 路由迁 11 个 `web/routers/*`(register 范式同 admin.py),lifespan 后台协程按域析出(`background`/`scheduler_runner`/`wechat_runner`),schemas/model_gate/runs/userfiles/common 各归位;②**计费收口**——units JSONB 读侧唯一出口 `core/storage/usage_report.py`(web 层清零 cast),写侧拆 `telemetry.py`(四类 cost=0 留痕与真计费分家,kind 常量成 loop↔toolfail 契约单一事实源);③**loop 拆分**——传输健壮性层(畸形/吐空检测、留痕、非流式降级重试)析出 `core/llm_transport.py`(loop 1158→812),`_execute_tool_call` 148 行拆 4 个正交方法,测试打桩缝(`_collect_stream_once`/`_nonstream_once`)原样保留;④**工具注册声明式化**——`core/tool_registry.py` (组名,gate,factory) 表收敛四种 env-gate 写法(§7.5 #7 获代码强制),32 工具挂载逐名比对不变;⑤**媒体工具去重**——五工具同构(配额闸/落盘命名/记账兜底/超时重试/递归找 URL)收 `tools/media_common.py`,净减 122 行,banner/轮询等刻意差异不动;⑥**渲染统一**——块收集器(fence 闭合/表格/blockquote/段落并合)与 brief inline 切分下沉 `rendering/common`,**golden 基线**(四 profile 渲富 fixture、document.xml 逐字节对账,`tests/golden/`)零 diff 验收,分派状态机(in_refs 等)留 profile;⑦**测试 286→351**——scheduler 服务层、usage 口径、media 原语、wecom 加解密(自造密文覆盖失败路径)、路由两套(无 DB 面:11 router 鉴权门 19 端点 + kb/skills/memory/files FS;DB 面:tasks CRUD 全链 + files 顶层目录 DB-aware 四分支 + upload);测试库基建 = docker PG@5433 + `ZCBOT_TEST_DB_URL` 显式门控(RUN.md 一键命令)。**修 2 个真 bug**:restore 恢复真软删过的任务必 500(server-side onupdate 列 flush 后 expired → DetachedInstanceError,幂等路径掩盖多时,路由测试抓出);DB 测试曾沿 .env 连到生产隧道(127.0.0.1:6012)——插入的到点 job 被生产 green 调度守护真跑,门控收紧为只认 ZCBOT_TEST_DB_URL + connect_timeout=5 兜挂库,产物已全量清理。附带:scripts 50→29(已结案诊断/backfill 归档 `scripts/archive/`)、`tools/output.py`(输出处理自 base.py 析出)、export_docx 复用 rendering.common 字体助手(rendering 保持无 core 依赖的沙箱可用性,修正审查"迁 rendering"建议)。
- **07-22 / 0.59.0 / 个人知识库(纯文件 .kb/ 机制,照记忆范式)**:方案 07-22 对齐后落地(DESIGN §3.8)。**core**:`core/kb.py`(状态/视图层:库名与文件名校验、INDEX 单行格式 `- [标题](docs/x.md)|来源 sources/x.pdf摘要关键词…` 的 parse/format、"已入库判据 = INDEX 有条目"派生 pending、删单篇连带原件与 INDEX 行、`kb_block` 注入)+ `core/kb_ingest.py`(入库管线:markitdown Python API → 扫描件 PDF 文本近零走方舟 OCR 兜底(§8.13 同通道)→ flash 单次 chat 写标题/摘要/关键词(失败降级不阻塞)→ 追加 INDEX;编排 create_task+to_thread+per-(user,库) 锁,进度内存态供轮询)。**记账**:0022 迁移放宽 `usage_events.task_id` 可 NULL(kb 入库无 task 上下文),溯源 = kind="kb_ingest"/"vision" + units `{"kb","source"}`(record_chat/vision_usage 签名同步放宽)。**API**:`/v1/kb*` 8 端点(列/建/删库、详情带入库进度、上传即入库、手动 ingest、看/删单篇),不设 HTTP 检索端点。**agent 侧零新工具**:agent_builder 在 memory_block 后注 `kb_block`(有库才注,docker/host 路径换算同 .memory);documents skill「何时不用」补自建资料路由行。**前端**:`kb.js` 两栏 modal(建/删库、上传 XHR、入库进度 3s 轮询、看/删单篇),rail 底部四按钮改图标+小字两行布局(用户 07-22 定)。冒烟:core 层 28 项全过(真实 markitdown + flash 摘要),TestClient API 15 项全过(usage 行实测 task_id=NULL + units 溯源),受影响存量测试(usage_accounting / system_prompt_paths)绿。 - **07-22 / 0.59.0 / 个人知识库(纯文件 .kb/ 机制,照记忆范式)**:方案 07-22 对齐后落地(DESIGN §3.8)。**core**:`core/kb.py`(状态/视图层:库名与文件名校验、INDEX 单行格式 `- [标题](docs/x.md)|来源 sources/x.pdf摘要关键词…` 的 parse/format、"已入库判据 = INDEX 有条目"派生 pending、删单篇连带原件与 INDEX 行、`kb_block` 注入)+ `core/kb_ingest.py`(入库管线:markitdown Python API → 扫描件 PDF 文本近零走方舟 OCR 兜底(§8.13 同通道)→ flash 单次 chat 写标题/摘要/关键词(失败降级不阻塞)→ 追加 INDEX;编排 create_task+to_thread+per-(user,库) 锁,进度内存态供轮询)。**记账**:0022 迁移放宽 `usage_events.task_id` 可 NULL(kb 入库无 task 上下文),溯源 = kind="kb_ingest"/"vision" + units `{"kb","source"}`(record_chat/vision_usage 签名同步放宽)。**API**:`/v1/kb*` 8 端点(列/建/删库、详情带入库进度、上传即入库、手动 ingest、看/删单篇),不设 HTTP 检索端点。**agent 侧零新工具**:agent_builder 在 memory_block 后注 `kb_block`(有库才注,docker/host 路径换算同 .memory);documents skill「何时不用」补自建资料路由行。**前端**:`kb.js` 两栏 modal(建/删库、上传 XHR、入库进度 3s 轮询、看/删单篇),rail 底部四按钮改图标+小字两行布局(用户 07-22 定)。冒烟:core 层 28 项全过(真实 markitdown + flash 摘要),TestClient API 15 项全过(usage 行实测 task_id=NULL + units 溯源),受影响存量测试(usage_accounting / system_prompt_paths)绿。
- **07-21 / 0.58.55 / 扫描件 PDF 直读(read_document,方舟文档理解)**:markitdown 只抽文本层,扫描件(老标准/检测报告/红头指南)转出为空=死路。探针(`scripts/probe_ark_doc.py`)验证方舟 chat file 内容块直读 PDF:格式 `{"type":"file","file":{filename,file_data}}` + `data:application/pdf;base64,` 前缀、base64 内联 17MB 可用(免 TOS)、单页栅格化 3600 万像素硬限(PIL 存 PDF 需标对 dpi)、~1300 输入 token/页(约 1 厘/页)、100 页魔术串全覆盖。落地 `tools/read_document.py`(seed_2_lite 同 variant 同 key,记账走 record_vision_usage):体积/页数双闸(30MB/100 页,pdfminer 软探页数免白付撞上下文)+ `finish_reason=length` 截断提示 + **多页 OCR `save_md` 全文落盘只返 1500 字预览**(防上下文爆);`image_ref.py` 抽 `load_pdf_as_data_url` 复用三形态路径解析与 user_root 边界。agent_builder 注册 + 系统提示 `_MEDIA_READDOC_SEG`(何时调/何时不调防重复花钱);六 skill(paper/patent/standard/proposal/rebuttal/ppt)摄取段加扫描件兜底一行。冒烟 `scripts/smoke_read_document.py` 全过(3 页 ¥0.0066,表格→md 表、公式→LaTeX)。选型对比(外部解析 API=新增第三方数据面 / 本地 OCR=过度投资)见 DESIGN §8.13。host 侧工具,**无需重建沙箱镜像**。 - **07-21 / 0.58.55 / 扫描件 PDF 直读(read_document,方舟文档理解)**:markitdown 只抽文本层,扫描件(老标准/检测报告/红头指南)转出为空=死路。探针(`scripts/probe_ark_doc.py`)验证方舟 chat file 内容块直读 PDF:格式 `{"type":"file","file":{filename,file_data}}` + `data:application/pdf;base64,` 前缀、base64 内联 17MB 可用(免 TOS)、单页栅格化 3600 万像素硬限(PIL 存 PDF 需标对 dpi)、~1300 输入 token/页(约 1 厘/页)、100 页魔术串全覆盖。落地 `tools/read_document.py`(seed_2_lite 同 variant 同 key,记账走 record_vision_usage):体积/页数双闸(30MB/100 页,pdfminer 软探页数免白付撞上下文)+ `finish_reason=length` 截断提示 + **多页 OCR `save_md` 全文落盘只返 1500 字预览**(防上下文爆);`image_ref.py` 抽 `load_pdf_as_data_url` 复用三形态路径解析与 user_root 边界。agent_builder 注册 + 系统提示 `_MEDIA_READDOC_SEG`(何时调/何时不调防重复花钱);六 skill(paper/patent/standard/proposal/rebuttal/ppt)摄取段加扫描件兜底一行。冒烟 `scripts/smoke_read_document.py` 全过(3 页 ¥0.0066,表格→md 表、公式→LaTeX)。选型对比(外部解析 API=新增第三方数据面 / 本地 OCR=过度投资)见 DESIGN §8.13。host 侧工具,**无需重建沙箱镜像**。
- **07-21 / 0.58.55 / 长对话点目录圆点首次跳不到位修复**:根因链=loadMessagesAround 后 renderMessages 尾部无条件滚底钉到窗口末尾,底部 sentinel 入视口立刻触发 loadNewerMessages 整窗重渲染删掉平滑滚动目标。修:renderMessages 加 stickBottom 参数(三个调窗口路径传 false);jumpToMessage 重建窗口后瞬时定位(auto)不留动画窗口期;`_msgScrollObserver` 在 `_outlineJumpLock` 期间不补载、解锁时对 sentinel 重投交叉状态。 - **07-21 / 0.58.55 / 长对话点目录圆点首次跳不到位修复**:根因链=loadMessagesAround 后 renderMessages 尾部无条件滚底钉到窗口末尾,底部 sentinel 入视口立刻触发 loadNewerMessages 整窗重渲染删掉平滑滚动目标。修:renderMessages 加 stickBottom 参数(三个调窗口路径传 false);jumpToMessage 重建窗口后瞬时定位(auto)不留动画窗口期;`_msgScrollObserver` 在 `_outlineJumpLock` 期间不补载、解锁时对 sentinel 重投交叉状态。
@ -218,7 +219,9 @@
``` ```
core/capabilities.py 75 ← 模型档案增加 CNY/Mtok 计费兜底字段 core/capabilities.py 75 ← 模型档案增加 CNY/Mtok 计费兜底字段
core/llm.py 151 ← litellm 离线 cost map env + chat_stream(stream + include_usage) core/llm.py 151 ← litellm 离线 cost map env + chat_stream(stream + include_usage)
core/loop.py 300 ← sink.emit + _stream_llm(chunk 间 poll cancel + emit delta)+ _RepeatGuard + usage cache 明细 core/loop.py 812 ← ReAct 主循环:_RepeatGuard/stall 熔断/热切编排(传输健壮性层已析出)
core/llm_transport.py 438 ← wire 层健壮性:畸形/吐空检测+留痕+非流式降级重试(07-23 自 loop 析出)
core/tool_registry.py 242 ← 声明式工具注册表((组名,gate,factory);secret 工具 env-gate 代码强制)
core/context.py 95 ← LLM 调用前压缩旧 tool / load_skill 消息(带压力门槛),保 tool_call 协议字段 core/context.py 95 ← LLM 调用前压缩旧 tool / load_skill 消息(带压力门槛),保 tool_call 协议字段
core/sinks.py 101 core/sinks.py 101
core/paths.py 50 ← task_dir db form 归一 core/paths.py 50 ← task_dir db form 归一
@ -230,22 +233,26 @@ core/memory.py 81 ← per-user `.memory/` dotfile
core/kb.py ~260 ← per-user `.kb/` 个人知识库(状态/视图/kb_block 注入) core/kb.py ~260 ← per-user `.kb/` 个人知识库(状态/视图/kb_block 注入)
core/kb_ingest.py ~280 ← kb 入库管线(markitdown→OCR 兜底→flash 摘要→INDEX) core/kb_ingest.py ~280 ← kb 入库管线(markitdown→OCR 兜底→flash 摘要→INDEX)
core/export_docx.py 383 core/export_docx.py 383
core/storage/{__init__,engine,models,usage,utils}.py ← 4 表(0004-0007 演进);record_chat/image_usage core/storage/{engine,models,usage,telemetry,usage_report,utils}.py ← 计费写/失败埋点/聚合读三分(07-23)
core/ark_client.py 105 ← 火山方舟 HTTP 客户端 core/ark_client.py 105 ← 火山方舟 HTTP 客户端
core/asr_xfyun.py 170 ← 讯飞语音听写 IAT wss 客户端(整段 PCM→文本;web 语音输入用,diag: scripts/diag_asr.py) core/asr_xfyun.py 170 ← 讯飞语音听写 IAT wss 客户端(整段 PCM→文本;web 语音输入用,diag: scripts/diag_asr.py)
core/asr_lfasr.py 250 ← 讯飞录音文件转写 LFASR 客户端(异步订单 + 说话人分离;transcribe_audio 工具底座,diag: scripts/diag_lfasr.py) core/asr_lfasr.py 250 ← 讯飞录音文件转写 LFASR 客户端(异步订单 + 说话人分离;transcribe_audio 工具底座,diag: scripts/diag_lfasr.py)
core/agent_builder.py 340 ← 装配 lib(有 ARK_API_KEY 才挂 SeedreamTool);build_skill_registry 装两来源 core/agent_builder.py 649 ← 装配 lib:build_agent/system prompt(工具注册块已迁 tool_registry)
core/executor.py / sandbox/{network,pool}.py / executor_docker.py ← Executor ABC + Docker per-user 容器池 core/executor.py / sandbox/{network,pool}.py / executor_docker.py ← Executor ABC + Docker per-user 容器池
tools/{base,fs,shell,run_python,skill_tool,skill_authoring,seedream,seedance,look_at_image,read_document,image_ref,web_search,web_fetch,documents,materials_project,transcribe_audio}.py ← read_document=扫描件 PDF OCR(方舟文档理解);image_ref=图/PDF 路径解析+base64 共享 tools/{base,output,fs,shell,run_python,skill_tool,skill_authoring,media_common,seedream,seedance,gpt_image,look_at_image,read_document,image_ref,web_search,web_fetch,documents,materials_project,transcribe_audio}.py ← media_common=媒体五工具共享原语;output=输出压缩/超时结果(自 base 析出)
main.py ~210 ← 入口:web / db / probe / user / sandbox check main.py ~210 ← 入口:web / db / probe / user / sandbox check
db/migrations/versions/ 0001-0022 db/migrations/versions/ 0001-0022
web/app.py ~1360 ← /v1 JSON API + user_id 隔离 + run lock + cancel + files + pptx 预览 + skills(列表/正文/删) web/app.py 197 ← 工厂 + lifespan 编排(07-23 拆分;路由在 routers/,协程在 background 等)
web/routers/*.py 11 个 ← misc/models/authroutes/wechat/kb/schedules/skills_memory/files/asr/tasks/messages
web/{background,scheduler_runner,wechat_runner}.py ← lifespan 后台协程按域析出
web/{runs,common,schemas,model_gate,userfiles}.py ← BG worker/共享 helper/请求体/档位门控/路径安全
web/auth.py ~190 ← 邮箱密码 + platform_key → JWT web/auth.py ~190 ← 邮箱密码 + platform_key → JWT
web/broker.py / sinks.py / pptx_render.py web/broker.py / sinks.py / pptx_render.py / admin.py
web/static/dev.html + js/*.js ← dev SPA 零构建 ES module(main.js 入口;skills.js=技能 modal;kb.js=知识库 modal) web/static/dev.html + js/*.js ← dev SPA 零构建 ES module(main.js 入口;skills.js=技能 modal;kb.js=知识库 modal)
web/static/vendor/ ~1 MB ← jszip / docx-preview / xlsx web/static/vendor/ ~1 MB ← jszip / docx-preview / xlsx
tests/ 351 项 ← golden/rendering 基线 + 路由两套(nodb/db)+ DB 套件(ZCBOT_TEST_DB_URL 门控)
───────────────────────────────── ─────────────────────────────────
Python 合计 ~3400 行(+ dev SPA + vendor 1MB);加 skills 脚本 + 配置,总仓库约 3800 行 Python 合计 ~5.5 万行(core+web+tools+rendering+tests+scripts;+ dev SPA + vendor 1MB)
``` ```
--- ---

View File

@ -1,3 +1,3 @@
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
# 改版本只动这一行。 # 改版本只动这一行。
__version__ = "0.59.0" __version__ = "0.59.1"