更新日志

每次版本都写清“改了什么、为什么改、怎么验证”;本页与 /api/changelog 同源,直接读仓库根 CHANGELOG.md,不手抄、不二次编辑。

当前版本 v0.4.5 · 发布于 2026-11-06 · JSON
v0.3.6 2026-10-24

新增

  • KB 告警外部出口正式接线。DANAO_KB_ALERT_FORWARD_URL 已配置(企业微信群机器人),danao-kb-alert-forward.timer 每 2 分钟检查并转发。此前 configured:false 状态解除,状态文件现在报 {"configured":true,"ok":true,"channel":"wecom"}。
  • deploy/kb/kb_alert_forward.py --check:发送一条链路连通性测试消息后退出,不读写游标、不消费告警,用于运维自证「现在通不通」。

修复

  • 报文适配(_adapt_payload)。此前把 kb-alerts.jsonl 的原始行直接当请求体发出,而 IM 群机器人不接受自定义 JSON。实测企微对该形态返回HTTP 200 + {"errcode":40008,"errmsg":"invalid message type"}。现按目标域名翻译:企微/钉钉 → {"msgtype":"text","text":{"content":...}},飞书 → {"msg_type":"text","content":{"text":...}},未知域名原样发送(兼容自建接收端)。正文由 _format_text 渲染为可读文本,并按 UTF-8 字节截断至 1900 字节(企微上限 2048)。
  • 成功判据改看业务码(_verify)。IM 机器人在参数错误、key 失效时一律返回 HTTP 200,只在响应体给非零 errcode。此前仅凭 status_code < 300 判定成功,会让「key 失效」显示为「发送成功」并推进游标,把告警永久丢掉——这是 0.3.5「游标吞失败」缺陷的更深一层。现在 errcode != 0(企微/钉钉)或 code != 0(飞书)即判失败、不推进游标、退出码 1。
  • 状态文件新增 channel(wecom/dingtalk/feishu/generic)与 last_error 字段,失败时可定位原因且不回显 URL。

变更

  • tests/test_kb_alert_forward_semantics.py 由 5 用例扩至 13 用例,覆盖状态说谎、接线不成立、四种报文形状、字节截断、--check 不触游标。变异验证:换回 0.3.5 实现10 failed / 3 passed,恢复新版 13 passed。

说明

  • 历史遗留:kb-alerts.jsonl 前 3 条告警在旧实现下被 errcode 40008 误判为成功、游标已推进至 3,这 3 条不会补发(分别是一条 kb.test 测试条目与两条健康分告警)。如需补发,只能手工重置 /data/danao/kb-alerts.offset 后重跑——会向群推送历史告警,易被误认为当前故障,故未执行。
  • 钉钉若启用「加签」安全模式,需在 URL 上追加 timestamp + sign,当前实现未包含该步骤。
v0.3.5 2026-10-24

一句话:修掉三处「状态说谎」——告警转发通道从未接线却报 ok: true、发送失败仍推进游标导致告警永久丢失、webhook 地址(含机器人 key)被打印进 journal。同时补齐 0.3.4 里「守卫会写 kb-alerts.jsonl」这句当时并不成立的说法。

修复:告警链路的静默失真(deploy/kb/kb_alert_forward.py)

    补齐:守卫的告警出口(tools/check_fd_watermark.py)

    • ALERT 时除写日志外,追加一行到 --jsonl(默认 /data/danao/kb-alerts.jsonl),接入既有告警文件。0.3.4 的 CHANGELOG 曾写「守卫会把异常写进本地日志与 kb-alerts.jsonl」,当时脚本并没有写 jsonl 的实现——本版补齐,使文档与实现一致。
    • 同一组告警在 ALERT_REPEAT_SECONDS(1 小时)内只写一次,避免每 10 分钟刷一条重复行(按告警内容签名去重,签名与时间戳存 state 文件)。

    测试

      仍然存在(需人工决策)

      • /etc/danao/kb.env 与 /etc/youxi/alert.conf 都没有任何生效的 webhook 键,服务器上多套告警脚本都是「可选 webhook」且都没配。因此本版之后,告警的真实状态从「静默显示健康」变成「如实显示 degraded + pending 计数」,但仍然没有人会收到推送——要真正送达,需要提供一个 webhook 地址。
      v0.3.4 2026-10-24

      一句话:给 fd 事故装上可判定的回归守卫——新增连接水位采样工具并装配为每 10 分钟的 cron,同时把「告警往哪里送」这件事查清:转发通道从未配置,状态文件却报 ok: true。

      新增:fd / 连接水位守卫(0.3.3 事故的回归哨兵)

      • 工具 tools/check_fd_watermark.py:采样 /proc/<pid>/fd、指向 danao.db 的连接数、线程数与 Max open files。三条判据任一成立即 rc=1:① 连接数 ≥ max(64, 线程数 × 2)(事故形态是 500 连接 / 118 线程 ≈ 4.2×,正常 37 / 162 ≈ 0.23×);② fd 总数 ≥ 4096(正常约 100,留 40 倍余量);③ 与上次采样相比 fd 增长 ≥ 200(趋势判据,抓缓慢泄漏;只在同一 PID 内比较,避免重启前后串比)。
      • 为什么废掉「fd 使用率 > 70%」:0.3.3 把 LimitNOFILE 提到 65536,70% = 45,824 个 fd,正常水位要涨 458 倍才触发——这是一条永不可能触发的死判据。判据的价值在于能否分辨故障。
      • rc=2 是一等公民:服务未运行、/proc 读不到等情况返回 2 而非 0。「测不到」与「一切正常」塌成同一件事,就是静默失效。
      • 装配:scripts/install_evolution_cron.py 新增 _fd_watermark_line(),把守卫纳入既有# DANAO-BRAIN-EVOLUTION 单入口块(每 10 分钟,--quiet,异常才写 logs/fd_watermark.log)。走 installer 而非手改 crontab,避免下次安装被覆盖成两份。

      验证

      • 工具矩阵 8/8:正常 rc=0(fd 102/65536、conns 37、threads 162)、--json 输出、--quiet 静默、服务名不存在时 rc=2;三条判据各自变异(阈值压到 1 / 0)均按预期 ALERT + rc=1。
      • 装配安全性用集合差证明:安装前后块外业务行 106 → 106,丢失 0、新增 0 (其他项目 cron 全部保留)。副作用是块从文件中段移到末尾并多一个空行——installer 既有行为,故按行号定位块的做法不可靠,只能按标记。
      • env -i 干净环境下执行成功,证明不依赖 PATH 与登录环境。

      已知限制(未在本版解决)

      • 告警没有外部出口:/data/danao/kb-alert-forward.status 为{"configured": false, "ok": true, ...} —— /etc/danao/kb.env 里没有DANAO_KB_ALERT_FORWARD_URL,转发服务每 2 分钟空跑一次即退出。因此守卫当前只能把异常写进本地日志与 kb-alerts.jsonl(kb-alerts.jsonl 里已积压一条从未送出的 健康分偏低:36 告警)。
      • ok: true 与 configured: false 同时出现,属 fail-open 语义:未接上的守卫会伪装成已生效。该语义在 deploy/kb/kb_alert_forward.py,本次未改动(需先确认 webhook 目标)。

      版本号同时出现在 /healthz、/api/version 与控制台状态页;四处读的是同一个常量(app/core/version.py)。