sidecar崩溃后前端怎么续命 重启策略与状态保留
chayuan-desktop 桌面单机版的前端是 React 跑在 Tauri webview 里,后端是 Python sidecar 跑在另一个进程。两个进程通过本地 HTTP 通信。sidecar 这一边万一崩了,前端要怎么活下来不让用户重开应用,是 免费开源的AI软件 做得稳的关键之一。这一篇讲 chayuan-desktop 的崩溃处理策略。
先看 sidecar 崩溃的几种典型原因。第一种是模型权重文件被破坏,加载时抛异常;第二种是 SQLite 文件锁冲突,长事务死锁;第三种是依赖的第三方服务(比如外部 Milvus、外部 LLM 厂商)连接失败导致内部异常未捕获;第四种是用户上传一份特殊格式文件,解析逻辑没覆盖该格式触发异常;第五种是 OOM,单机版内存吃光导致 OS 杀进程;第六种是被外部杀毒软件干掉。
第一道防线在 sidecar 内部。FastAPI 的全局异常处理 hook 会捕获所有未处理异常,转成 5xx 响应返回给客户端,避免进程整体崩溃。常见的业务异常会被识别出来转成具体的错误码,比如 RAG 入库失败、模型调用失败、KB 不存在等,前端能据此给用户友好提示。
第二道防线在 Tauri 主进程。如果 sidecar 真的退出了,Tauri 心跳监控会在几秒内发现,启动重启逻辑。重启之前先检查上次崩溃原因(看 server.log 最后几行),如果是 OOM 或 segfault 这种系统级问题,先告知用户,再询问是否重启。一般业务异常会自动重启不打扰用户。
重启过程中前端怎么处理。React 应用接到 fetch 请求失败,识别出是 sidecar 不可用,进入降级模式:把当前对话框的输入禁用,顶部显示一个状态条 后端正在恢复,请稍候;同时启动一个轮询,每秒检查 /health 是否回来。sidecar 重启完成后状态条变绿,输入恢复,正在打字的内容不会丢。
状态保留是这一步的关键。前端的核心状态在两个地方:当前对话的消息列表(包括已发送和正在流式接收的)、用户的输入草稿。这两份状态都在 React 应用内存里,崩溃恢复时不会清。流式接收中的回答如果中途断掉,会标记为 中断,给用户一个 重试 按钮,重新发起同一条请求即可。
数据层的状态保留。sidecar 崩溃时正在写的对话历史可能有部分丢失。SQLite 默认开了 WAL 模式,写入是先写日志再 checkpoint,崩溃后启动会自动 recover,已 commit 的写入不丢。未 commit 的(比如流式回答还没结束)会丢,但前端已经在内存里有副本,下次发送时会重新落库。
外部依赖的崩溃隔离。模型供应商失败、外部向量库失败、外部 SQL 数据库失败,这些都不应该让 sidecar 整体崩溃。chayuan-desktop 在每个 adapter 内部做了超时和 try-catch,单一外部源失败只会让对应的查询返回 error,整个流程其他源继续正常工作。比如多 KB 查询里某个外部 Milvus 挂了,其他 sqlite-vec 库的结果照样返回。
文件锁冲突的特别处理。OneDrive、坚果云这种同步软件会偶尔锁住 SQLite 文件触发崩溃。chayuan-desktop 检测到这种特定异常会重试三次,仍失败的话给用户一个提示 数据目录被外部进程锁定,建议把 CHAYUAN_ROOT 移到不被同步的目录。
崩溃日志的可观测性。每次崩溃都会写一段 crash 日志到 CHAYUAN_ROOT/logs/crash-YYYYMMDD-HHMMSS.log,包括 traceback、当时的请求上下文、内存快照。这个文件用户可以贴给社区或者发反馈。日志默认保留最近 10 份,超过自动清理。
WPS AI 插件 chayuan-wps 在 sidecar 崩溃时同样会失效。加载项侧也实现了类似的降级模式:发起请求失败后显示 后端连接中断 状态,等 sidecar 恢复后自动重连。这样在 WPS 文字里不会因为后端崩溃而中断写作流程。
国产化支持下的特殊场景。麒麟 UOS 上偶尔出现 sidecar 因为系统休眠唤醒后端口绑定失败的情况,对应处理是唤醒后做一次端口探测重启。loongarch64 平台上 PyInstaller 二进制启动慢,重启时间需要更长容忍度。
崩溃恢复机制不是设计出来给用户看的,而是用户压根不应该感知它的存在。chayuan-desktop 在这件事上的目标是:sidecar 偶尔崩了,用户继续打字,几秒之后又能正常工作,对话历史不丢,模型钥匙不丢。这才是 免费开源的AI软件 桌面单机版应有的稳态。