Commit Graph

119 Commits

Author SHA1 Message Date
caoqianming 1ac9aa1ba0 fix(files): prevent silent upload overwrites 2026-08-12 14:13:42 +08:00
caoqianming 6b37ce141f perf(storage): optimize database hot paths 2026-08-12 13:54:40 +08:00
caoqianming 4932f00e90 feat(artifacts): add lifecycle tracking and trash 2026-08-12 12:44:28 +08:00
caoqianming 2b4316ed25 refactor(external-systems)!: 统一为通用 API provider
移除 factory_mes preset 和兼容包装,新增存量 definition 一次性迁移脚本,并让 OpenAPI 参数直接遵循规格约束。

BREAKING CHANGE: 管理 API 不再接受 factory_mes provider,部署新版前必须先执行 definition 迁移脚本。
2026-08-10 17:26:39 +08:00
caoqianming 44b7df6f20 feat(external-systems): add generic MCP connector 2026-08-10 16:04:51 +08:00
caoqianming 2187c7f0d0 fix(preview): restore continuous document scrolling 2026-08-10 09:16:01 +08:00
caoqianming 8d741e4141 fix(preview): support document previews in app webviews 2026-08-10 08:55:44 +08:00
caoqianming 9032c5b517 fix(external-systems): preserve credentials on definition updates 2026-08-07 11:25:29 +08:00
caoqianming 0e4bd0456a feat(external-systems): 重构连接治理与运行态缓存 2026-08-07 10:12:37 +08:00
caoqianming 5430379d76 fix(context): 按模型协议治理历史推理上下文 2026-08-06 16:43:20 +08:00
caoqianming 02e7d2cb14 feat(files): create folders from file panel 2026-08-06 15:04:15 +08:00
caoqianming 6437e06503 fix(ppt): follow selected conversation model 2026-08-06 13:35:12 +08:00
caoqianming 7e544827c0 feat(files): toggle task hidden directories 2026-08-06 12:05:36 +08:00
caoqianming 41cd7858a1 feat(external-systems): add persistent result exports 2026-08-06 11:10:06 +08:00
caoqianming e81005435e fix(external-systems): preserve complete OpenAPI responses 2026-08-06 10:32:31 +08:00
caoqianming bce896a21f docs(design): document OpenWorker action governance 2026-08-06 08:40:19 +08:00
caoqianming 80fbc7ab4a feat(external-systems): support generic OpenAPI integrations 2026-08-05 17:18:29 +08:00
caoqianming af0ad934bb feat(external-systems): guide MES aggregate queries 2026-08-05 15:01:43 +08:00
caoqianming cb9a39f33d feat(external-systems): add managed MES connections 2026-08-04 16:53:58 +08:00
caoqianming cee4eb4c14 feat(eval): add reproducible technical benchmark 2026-08-03 16:10:38 +08:00
caoqianming bc45753f77 feat(models): unify thinking controls 2026-08-03 14:36:50 +08:00
caoqianming 57655984dd feat(web): add task-scoped artifact references 2026-08-03 13:49:20 +08:00
caoqianming afdcf46577 feat(tasks): streamline task creation and embedded entry 2026-07-31 14:31:50 +08:00
caoqianming 3c022494f5 fix(web): retitle cleared conversations 2026-07-31 13:00:34 +08:00
caoqianming 59c41d22df docs(scheduler): record deferred task retention design 2026-07-31 11:31:58 +08:00
caoqianming cfc1d17af7 优化工具失败观测与重复保护 2026-07-29 09:29:44 +08:00
caoqianming 6f2840a5d0 统一运行生命周期入口 2026-07-28 15:34:42 +08:00
caoqianming 7cfeebfa89 增强消息运行可靠性 2026-07-27 17:50:31 +08:00
caoqianming 70683b7d09 增强知识库文件写入一致性 2026-07-27 16:37:56 +08:00
caoqianming c3a6f7deee Add workspace-first new conversation flow 2026-07-27 12:28:56 +08:00
caoqianming 85c918dc2f Add safe conversational workspace rename 2026-07-27 11:23:20 +08:00
caoqianming e0aa8130af fix(kb): 零库时注入冷启动契约,修「放进知识库」被误写进记忆
根因:kb_block 零库返回空串,agent 不知道 KB 机制存在,用户点名"知识库"
被就近理解成记忆写进 .memory/(真实用户事故)。与 memory 空契约常驻同一课。

- core/kb.py: 新增 _KB_COLD_START(建库步骤 + INDEX 行格式 + KB/记忆分工,
  ~百 token);kb_block 零库分支改注该契约,有库分支不变
- tests/test_kb_block.py: 锁两分支(冷启动文案/路径展示/全量 INDEX/空库)
- DESIGN.md §3.8: "有库才注入"取舍更新为冷启动注入 + 事故记录

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 15:31:50 +08:00
caoqianming 1332139c9e 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>
2026-07-23 14:29:19 +08:00
caoqianming 6e455d37be fix(kb): 注入契约改主动查阅——相关即先读无需点名,去掉 document_search 路由
- _KB_CONTRACT 第一条换成主动性规矩:INDEX 条目与问题相关就先 read 正文再答,
  用户无需说"从我的知识库里查";此前契约只写"怎么用"没写"何时主动用",
  flash 容易把 INDEX 当背景略过
- 去掉契约里的 document_search 路由条(契约只管 .kb 自己怎么用,路由靠各自
  工具描述自然分流,少一层耦合)
- DESIGN §3.8 两处描述同步收窄

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 15:43:01 +08:00
caoqianming c6232d8325 feat(kb): 个人知识库——.kb/ 纯文件机制+/v1/kb* API+前端 modal+注入契约(bump 0.59.0)
- core/kb.py: 状态/视图层(INDEX 单行格式 parse/format、已入库判据=INDEX 有条目、
  删单篇连带原件与索引行、kb_block 注入——有库才注)
- core/kb_ingest.py: 入库管线 markitdown→扫描件 OCR 兜底(方舟)→flash 摘要(失败降级
  不阻塞)→追加 INDEX;编排 to_thread+per-(user,库) 锁,幂等可续跑
- 0022 迁移: usage_events.task_id 放宽可 NULL(kb 入库无 task 上下文);
  记账溯源 kind="kb_ingest"/"vision" + units {"kb","source"}
- web/app.py: /v1/kb* 8 端点(列/建/删库、详情带进度、上传即入库、手动 ingest、看/删单篇),
  不设 HTTP 检索端点(agent 走 fs 工具)
- 前端: kb.js 两栏 modal(上传 XHR/进度轮询/删除确认);rail 左下新增知识库入口,
  四按钮改图标+小字两行布局
- agent_builder 在 memory_block 后注 kb_block(docker/host 路径换算同 .memory);
  documents skill「何时不用」补自建资料路由行
- DESIGN §3.8 机制小节 / CHANGELOG 0.59.0 / PROGRESS

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 15:20:43 +08:00
caoqianming a1db5756aa feat(tools): 扫描件 PDF 直读 read_document——方舟文档理解 base64 内联(bump 0.58.55)
markitdown 只抽文本层,扫描件(老标准/检测报告/红头指南)转出为空=死路。
探针(scripts/probe_ark_doc.py)验证方舟 chat file 内容块直读 PDF 后落地:

- tools/read_document.py:seed_2_lite 同 variant 同 key,记账走
  record_vision_usage;体积/页数双闸(30MB/100 页,pdfminer 软探页数)+
  finish_reason=length 截断提示;多页 OCR save_md 全文落盘只返 1500 字预览
  (防上下文爆),save_md 与读取侧同款三形态路径解析(相对/宿主绝对/容器
  /workspace)+ user_root 边界
- tools/image_ref.py:抽 load_pdf_as_data_url(复用 resolve_in_root;
  data:application/pdf;base64, 前缀为方舟硬校验)
- config/media/doubao.yaml:vision 段加 max_pdf_mb / max_pdf_pages /
  doc_request_timeout_s(probe 实测口径:~1300 token/页、100 页全覆盖、
  17MB 内联可用、单页栅格化 3600 万像素硬限)
- core/agent_builder.py:注册(跟 look_at_image 同 vision 段走)+ 系统提示
  _MEDIA_READDOC_SEG(何时调/何时不调,防对有文本层 PDF 重复花钱)
- 六 skill(paper/patent/standard/proposal/rebuttal/ppt)摄取段加扫描件
  兜底一行(markitdown 转出空/近空 → read_document OCR)
- 冒烟 scripts/smoke_read_document.py 全过(3 页 ¥0.0066,表格→md 表、
  公式→LaTeX,usage_events 记账验证)

选型对比(不接 MinerU 等外部解析 API=新增第三方数据面;不选本地 OCR=
过度投资;不选 file_url/file_id=免认证公网直链/TOS 依赖)见 DESIGN §8.13。
host 侧工具,无需重建沙箱镜像,pull + 重启 web 生效。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 16:57:23 +08:00
caoqianming fb214de960 fix(context): 窗口体量估算实测校准——50%压缩/85%折叠/占用环换 token 实测口径(bump 0.58.52)
diag 实测(scripts/diag_context_pressure.py 留仓):折叠机制有效、零撞硬上限,
但静态 CHARS_PER_TOKEN=2.5 对中文密集窗口低估近一倍——名义 85% 折叠线实际
~155% reliable 才触发(task 9a863424 冲到 40.7 万 tokens);代码密集反向虚高
(fe2d8b73 估 1.2M 实际 616k)。

修法是信号校准、不加新机制:
- context.py: estimate_window_tokens(provider 实报 usage 覆盖窗口主体,仅实测
  点后尾巴按 2.5 估)+ calibrated_chars_per_token(比值夹 [1.0,4.0] 带宽)
- session.py: last_measured_usage() 取窗口内最后一条实报 usage 的 assistant 行
  (best-effort 绝不抛;idx→内存 pos 映射校验 role)
- context_fold.py: maybe_fold 触发判定换 token 实测口径
- loop.py: 压缩门槛与 context_limit_chars(前端环)用校准比值,逐轮以
  sent_chars/prompt_tokens 刷新;校准态挪类属性兜默认,任何异常回退 2.5

已知残余:折叠后 run 在首次 chat 完成前崩掉会多折一次(摘要偏保守,原文全在
DB),不为此加持久化状态。run 中途折叠/超限自愈按 §5 无信号不实施继续搁置。

真实生产 task 只读验证映射与校准(drift 0.95x/0.51x/0.86x),286 测试全绿。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 12:49:02 +08:00
caoqianming 5cf92ec71a feat(rendering): 通用报告走平台渲染器 report profile + 基座引导,少让模型手撸 python-docx(bump 0.58.35)
接 0.58.34(语法预检=止血),这条根治那一头。关键发现:平台早有成熟 md→docx(+pdf)
渲染器 rendering/(bind-mount /sandbox/rendering:ro,任何 run_python 可达,与 skill
无关),标题/正文/列表/表格/图片图题/mermaid/化学式下标/目录/house 字体全有,
paper/proposal/brief 三 profile 在用。烧 token 的验收报告没走它、在裸手撸 python-docx,
唯一原因是没引导把"随手写 Word 报告"指向它。故不造轮子,只两件小事:

1. rendering/docx_manuscript.py 加 report profile(复用全套渲染;差异=报告不是申报书:
   目录默认无[--toc 才带]/章节不强制分页/页边距 2.5-2.5/列表含"第X章节"不含"条"),
   render.py --profile 加 report 选项 + 路由。
2. core/agent_builder.py 基座 prompt 加软引导:出 Word 报告→正文写成 sections/*.md
   调 render.py --profile report,别手撸 python-docx 内联中文。

取舍红线(与用户对齐):渲染器是首选加法不是替代——绝不硬拦 run_python 生成文档,
确需精细定制版式/改写已有 docx/处理 xlsx 等渲染器覆盖不了的照样自由写代码;软引导
让位于更具体的 skill render 指引(proposal/paper)。刻意不把渲染器指路塞进语法预检
hint(会 overload 只该管语法的检查、误导合法手撸),预检保持只诊断语法。三层叠加:
渲染器(根治)> 预检(0.58.34)> RepeatGuard err-streak(0.58.33),run_python 能力不削。

测试 tests/test_rendering.py(四 profile 端到端 + report 目录可选)+ CLI e2e
(render.py --profile report 出 37KB docx)全绿;py_compile 校三文件无语法错。
DESIGN §8.6 记该 profile + 并存红线。additive,不碰对外 API/DB 契约。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 09:32:02 +08:00
caoqianming 7500b3b134 chore: 撤回 §8.11 delegate,只留验证过的批量工具(bump 0.58.31)
0.58.30 曾整体落地 delegate(隔离检索子循环),现 revert + force-push 抹除。
机制没错、对标 Claude Code subagent 也成立,但触发信号没坐实:自评回看
diag_search_args.py,motivating 案子 document_search 122 次呈"一批批不同材料
体系并行搜"形态,更像批量扇出而非结果驱动探索——若属实正解应与 mp_search 同为
批量工具 document_search_batch,delegate 是过度设计;叠加子循环 transcript 不落盘
(诊断驱动的功能反不可诊断)、20 轮上限对 122 次偏低、强制 flash 对难检索降质等
未验证坑。

重建前置收紧:先用 diag 确认某检索 task 的 query 是真探索(依赖前序结果、无法
一次列全)而非可枚举批量,是批量走批量工具、只有真探索才 delegate。批量工具
(0.58.28)验证过、保留。PROGRESS/DESIGN §8.11 记该教训免日后盲目重建。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 14:58:21 +08:00
caoqianming 2066947061 fix(web): 用户停止后补持久「已停止」提示,cancel 落一等终态(bump 0.58.26)
点停止中断回复后对话底部什么都不剩:live-append 的「已停止」badge 被收尾
loadMessages 整屏重建冲掉(同 2a1bc25d 案)。补齐 cancel 终态,与 error 卡对称:

- 后端: agent.run 取消路径 return "[cancelled]",_run_agent_bg 据此收尾写
  run_status="cancelled"(否则 idle)。run_status 是 Text 列,加新值零 migration。
- 前端: renderPersistedRunError → renderPersistedRunTerminal,同时认 error/cancelled;
  cancelled 渲居中 muted 虚线「已停止 · 你中断了上次运行」(良性态,不用错误卡样式),
  刷新/切任务仍在,下次起新 run 覆盖清掉。删掉会被冲掉的 live badge 及死 CSS。
- 兼容: run_status 加新值纯增量,蓝绿旧实例读到 cancelled 当良性态优雅退化不崩;
  全部 run_status 消费点(409 门/reaper/SSE is_active/cancel 端点/clear)已核对。
- 同步 DESIGN §7.4 / models.py 注释 / filter allowlist / CHANGELOG / PROGRESS。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:38:54 +08:00
caoqianming c655ce0a76 feat(skill): 已选国际旗舰模型时豁免 skill 定向模型切换(bump 0.58.25)
ppt 等内置 skill 会 pin glm.pro52,但用户若已主动选了国际旗舰模型
(unifyllm 网关族:Claude/GPT/Gemini)不应被切走。_skill_model_switch
在算出目标 profile 后加判据:current profile 的 family==unifyllm → 返回
None 不切。定向 pin 本意是给较弱的国产默认模型托底产物质量,已在旗舰
模型上的用户尊重其选择、不降级。判据在 DB 写入前(纯字符串检查,无副作用),
其余取舍(只信内置 skill / 跳档位门控 / 不自动切回)不变。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:22:02 +08:00
caoqianming 73d07c9560 fix(sandbox): init.sh 放行容器自身 loopback,mermaid 渲染真·终案(bump 0.58.10)
90 天 67 次 timeout 真凶:127.0.0.0/8 DROP 掐死 puppeteer 连 chromium 的
127.0.0.1 DevTools WebSocket(SYN DROP -> TCP 重试 ~127s,恒定 2m15s 超时
+ [object Object] + 零 CPU)。容器 127.0.0.1 是自己 netns 的 lo、到不了宿主,
DROP 无安全收益纯误伤。apply_blocklist 首条 -o lo ACCEPT(v6 同款),
127.0.0.11:53 豁免收编,127/8 与 ::1 DROP 降为 belt-and-suspenders。

服务器全款复刻容器实测:放行前小图也挂 2m14s;放行后小图 3.9s、
真实大图 -w 2800 -s 2 仅 5.2s(1 vCPU 渲大图慢亦为误判)。
之前 probe 全因 --entrypoint bash 绕过 init.sh 无 iptables 而全绿误判。

生效:镜像重建(init.sh COPY 层秒过)+ 回收存量容器。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 10:40:48 +08:00
caoqianming daf0db854a feat(procs): 后台进程 bg proc + 对话锁/完成通知 + seedance 续查(bump 0.56.0)
- shell/run_python 加 background=true(模型判断,>~1min 走后台):进程 detach 独立运行,
  不受工具超时/服务重启/蓝绿切换影响;新 check_process 工具查状态/日志尾部/终止
- 状态协议纯文件(<user_root>/.zcbot_procs/<task>/<proc>/,exit_code 出现即终态,
  无 DB 无队列组件):host=stdlib wrapper detach(限时+杀树+日志截尾 10MB);
  docker=专用容器 zcbot-proc-*(同款硬化+iptables,product=proc 与 sandbox
  reaper/shutdown_all 生命周期解耦);web lifespan 每小时 sweep(7d TTL+孤儿容器)
- web:GET /v1/procs(用户级)+ POST /v1/tasks/{id}/procs/{pid}/kill;前端
  [Background] 工具卡活化(spinner/跳秒/停止,历史重渲恢复)+ 完成 toast(跨 task
  可跳转)+ 对话锁(bgproc 运行期 composer 发送→停止/Enter 拦截,先锁后放消
  刷新时序窗口;润色/语音与 streaming 期一致可用)
- seedance:resume_task_id 续查(轮询超时/中断后取回结果,跳过提交不重复计费),
  fast/pro poll_timeout 统一 1200s;取消/超时文案引导续查
- core/llm.py:litellm timeout 显式化(默认 600s 不变,ZCBOT_LLM_TIMEOUT_S 可调)
- DESIGN 新增 §8.12(含边界:不做 job 链/自动续跑/不引 Celery);RUN/PROGRESS/
  CHANGELOG 同步;scripts/test_bgproc_manual.py 手工验证;unittest 全量 201 过

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:47:11 +08:00
caoqianming 8aa6bed79c docs(models): 清理 deepseek-chat/reasoner 下线残留描述(bump 0.54.2)
旧模型 2026-07 已下线:删 deepseek_v4.yaml 里"可退回 deepseek-chat"的
误导注释(照做会打不通),DESIGN 附录迁移提醒改为已完成陈述。
全库 grep 确认代码/配置本体无旧 model id 引用。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 13:28:26 +08:00
caoqianming 66a515aa3d feat(context): §8.8 Phase 2 长会话中段折叠摘要 + 头部压缩指示环(bump 0.54.1)
- 折叠(0.54.0):run 起点窗口体量达 reliable_context×85% → core/context_fold.py
  把中段折叠为固定模板结构化摘要(路径/ID/数值原文保留),写 tasks.context_summary
  (migration 0021)+ 推进 context_base_idx;Session.load 注入仅内存「前情摘要」
  (_n_head/_base_idx 维护 idx 映射);摘要调用复用会话 prepare 后前缀 + 末尾追加
  指令,与上一轮 chat 缓存字节一致近全程 hit;失败零阻塞(warn+跳过);「新话题」
  硬重置 / web 清空对话顺带清摘要,gap 软重置保留
- 前端(0.54.0):新 SSE context_fold 事件(start 状态行提示 / done 流内分隔条);
  补 warn 事件渲染 —— 此前 loop 熔断/重复拦截提示在 web 端被静默丢弃
- 压缩指示环(0.54.1):头部金额撤下明面(挪 hover tooltip),换 SVG donut
  (绿<50% 未压缩 / 琥珀 50-85% 压缩区 / 红≥85% 达折叠阈值)+ 已整理×N;
  GET /v1/tasks/{id} 详情新增 context_window_chars/limit_chars/pressure/folds
  (additive,列表端点不加);折叠计费 kind="context_fold"(record_chat_usage
  加 kind 参数,折叠次数 = count 该 kind,零 migration);llm_start 事件补
  context_limit_chars,run 中实时刷环
- DESIGN:§8.11 新增「最小子循环 delegate」预留(上下文隔离而非多 agent 编排,
  按 diag 数据触发,无信号不实施);§1/§6 "不做 subagent" 收窄为编排型;
  §8.8 Phase 2 标  沉淀六条取舍;§7.2 SSE 清单补 warn/context_fold
- tests/test_context_fold.py 12 项(切点/映射/apply_fold/maybe_fold 打桩),
  全量 198 过;本地 DB 已升 0021

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 12:39:31 +08:00
caoqianming 09db2c9c93 feat(ppt): 验收改纯代码几何质检,撤除渲图 vision 验收闭环(bump 0.50.0)
真实会话复盘:沙箱 chromium "找到但渲染崩"无回退,硬验收门+死路逼模型
pip install cairosvg 手写渲染;look_at_image 逐页 26-42s 纯烧 token。

- pptx_cli.py:删导出渲图验收门(_acceptance_problems)+ --allow-unreviewed;
  几何硬错误质检门(check13/14 越界/压字/错位)原样保留并继续在导出边界复跑
- 删 accept_pages.py 及 acceptance.json 登记机制
- svg_preview.py 保留为手动工具:chromium 渲染失败按页自动回退 cairosvg
  (原来只在"完全没浏览器"时才回退,找到但崩=死路)
- SKILL.md:阶段五简化为 finalize 单步;渲图预览零提及(提示面不给渲染
  入口);强调导出唯一入口 svg_to_pptx;Geometry warning 改"回源核对坐标"
- DESIGN §8.10 记录取舍推翻(0.36.0 渲图验收闭环);PROGRESS/SKILL_LIST 同步

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 20:41:28 +08:00
caoqianming 463e282afd feat(web): run 长耗时可视化——reasoning 事件流 + 阶段跳秒指示(bump 0.43.0)
修"前端一直静止显示思考中被当成卡死":

- core/loop.py:新增 SSE reasoning{delta} 事件——thinking 模型分钟级推理
  原先 delta.reasoning_content 整段丢弃,前端零反馈;流式/非流式兜底两路都发
- chat.js:reasoning 渲染成灰色可折叠"思考过程"卡(直播态随流展开、正文
  开始折叠;历史消息里持久化的 reasoning_content 同款渲染)
- chat.js:活跃状态指示——TTFT/推理期占位段每秒跳"思考中/深度思考中 · Ns"
  (data-status + CSS attr),工具卡运行中 spinner + 跳秒、tool_result 定格
  为执行耗时;每轮 llm_start 重建占位段,工具轮之后不再空窗
- DESIGN §7.2 SSE 事件清单补 reasoning

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 09:02:31 +08:00
caoqianming 8b482e7a30 feat(broker): 外置 Redis pub/sub——蓝绿跨实例 SSE/cancel 可达(bump 0.42.0)
用户量上升重评落地(DESIGN §7.0):web/broker.py 拆 LocalRunBroker(原进程内
实现,默认)/ RedisRunBroker 同接口双实现,ZCBOT_REDIS_URL env 开关。

- event:PUBLISH zcbot:ev:<tid> + 单条 async pubsub reader 路由本地订阅 queue
  (Redis 只管跨进程一跳,fan-out 最后一公里仍在进程内)
- done:SETEX 60s key + publish 双通道;订阅先挂 channel 再查 key,晚到不漏
- cancel:SETEX/GET/DEL key,loop chunk 间 poll,跨实例可达——蓝绿切换窗口
  「刷新丢直播 / 停止失灵」两个边缘消失;稳态双实例分流只差 nginx 配置
- 容错:启动 ping 不通 fail-fast(同 sandbox init 纪律);运行中失败 10s 节流
  log + 降级丢帧,run 不受影响;reader 1s 退避重连并重挂活跃 channel
- lifespan finally 加 broker.shutdown();sinks.py 解开对旧类名的 import
- tests/test_broker_redis.py 9 例(fakeredis:跨实例 emit/cancel、晚订阅 done、
  降级不抛、fail-fast);requirements 加 redis/fakeredis
- RUN.md:env 段 + 蓝绿节 + bootstrap(apt install redis-server)+ 故障兜底 3 行

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:51:39 +08:00
caoqianming bb6fd11dbb docs: 规模设定重评——broker 外置 Redis 由搁置转推荐实施(bump 0.41.2)
用户量已非个位数,DESIGN §7.0/§8.4 与 RUN.md 蓝绿节的"当前规模不值得上
Redis"设定过时:①部署时总有 in-flight run,蓝绿窗口边缘常态化;②稳态双实例
分流是最近的扩容手段,broker 外置是硬前提。选型排除 PG LISTEN/NOTIFY(token
级 delta 耦主库)与 nginx sticky(upstream 变更时 hash 重排恰在部署窗失效)。
实施待确认,方案要点已记 DESIGN §7.0。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:28:17 +08:00
caoqianming 1d62670366 feat(deploy): 蓝绿双实例无感部署落地——零 503 窗口 + SSE 不断流(bump 0.41.0)
- deploy/update_bluegreen.sh:复用 update.sh --prepare-only 做准备 → 起空闲色
  → healthz 验 instance → nginx upstream 切流 → 轮询 DB 等旧色 run 自然清空
  (DRAIN_WAIT_SECONDS 默 600s,期间旧 SSE 继续直播)→ stop 旧色 → 残留
  running 行标 error → enable 新色/disable 旧色;--switch 半途恢复调流
- deploy/zcbot@.service 模板 unit + deploy/instances/{blue,green}.env +
  deploy/nginx/zcbot.conf.example(upstream 单行文件由脚本改写)
- tasks.run_owner(migration 0020):起 run 写 ZCBOT_INSTANCE,启动 reaper
  只收自己色的孤儿,不误杀另一实例在跑的 run
- sandbox 容器名/label 带实例色,shutdown_all 只清自己色,并存期互不清扫
- 微信 ClawBot 长轮询 PG advisory lock 选主(detach 连接持锁 + 30s keepalive,
  断连即让位),蓝绿并存不抢消息;定时任务 claim 本就 SKIP LOCKED 免改
- /healthz 加 instance 字段;update.sh 加 --prepare-only
- 单实例部署(ZCBOT_INSTANCE 未设)行为全部不变;RUN/DESIGN/PROGRESS 同步

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:03:10 +08:00