原本只是准备开始日常工作,结果云端环境执行不了命令。文字对话还能继续,想发一张截图说明情况,附件也没能传过去。

这时最让人困惑的,是几个界面状态似乎互相矛盾:环境标着正在运行,连接却显示离线;设置提示已经保存,环境发布又没有完成。继续重试之前,得先回答两个问题:哪些东西已经保住了,怎样才算恢复成功?

这次故障没有查明平台内部的根因。工作接回来以后,我给 Daywise(日有所见)做了一个项目级技能 recover-environment,把配置、恢复步骤和验收方法一起保存到仓库。

下面既复盘发生过什么,也具体讲下一次怎样让 AI 按这个技能接管。对于使用云端 AI 工作环境的个人用户,这些步骤可以作为整理自己项目恢复方法的参考。

先看实际能做什么,再看状态标签

当时附件请求中出现了这样的错误片段:

409 conflict
environment_offline: Environment is not connected.

这里需要把两行结合起来看。environment_offline 说明这次请求遇到了环境未连接的问题,不能由此把所有 HTTP 409 都解释成环境离线。

命令无法执行、连接离线和附件请求失败,已经足够说明当前工作链路不可用。但它们没有告诉我们,平台内部究竟是哪一步出了问题:启动、恢复、发布,还是连接状态的登记?没有足够的内部日志,就不能给出确定解释。

同样,故障出现在调整配置之后,也不能证明是某个新增配置造成的。时间上的先后值得记录,因果关系仍需要另外的证据。

因此,排查的第一个目标是确认影响范围和恢复条件。重新连接、重试发布可以有边界地尝试;如果没有新信息,反复做同一件事,很难让判断更可靠。

恢复之前,先确认哪些状态能找回

这次能够转向替代环境,一个重要前提是:程序、已确认的业务记录和技能说明已经保存在私有 Git 仓库,历史产物也有版本化归档。

Git 可以理解为文件的版本记录。内容成功推送到远端以后,不必等旧环境重新连上,才有机会取回它们。但它保存的范围有边界。

状态能否从原有版本记录恢复需要留意什么
已提交并成功推送的程序、规则和业务记录可以作为恢复基础核对所需版本,保护本地独有修改
已校验并归档的历史产物可按归档机制取回检查来源、清单和文件校验值
只存在旧实例里的未推送修改、原始附件不能仅靠重新拉取仓库找回旧实例不可达时,不能承诺已经保住
虚拟环境、浏览器登录态、运行中的进程不会随代码自动恢复依赖尽量重建,登录与进程另行处理

这里还容易混淆两件事:数据的恢复点 和 环境配置是否生效。

成功推送的文件,不依赖云环境发布成功才保存。反过来,设置页面提示已经保存,也不代表新配置已经应用到当前环境。把这两种状态分别记清楚,才能判断是需要找回数据,还是需要重新建立运行条件。

重新拉取项目,可以恢复一部分持久状态,并不能一比一复制整台旧机器。

先把工作接回来,再补齐恢复方法

早期进行过重新连接和发布重试,但现有材料没有证明这些动作修复了旧实例。随后采用了替代环境路径:保留旧环境,从同一私有项目的最新代码继续,重建依赖和必要的网络设置。

当次替代环境恢复,留下了两类独立证据。一类是命令能够执行,相关检查通过;另一类是用户实际上传了一张图片,新环境中的对话确实收到了它。后者发生以后,才确认当次图片通道已经恢复。

两类检查不能互相代替。终端正常,不意味着客户端附件一定能传;某个上传域名能够访问,也不等于真实上传链路已经走通。

旧实例没有在这次自动操作中被删除。先让工作能够接管,再决定如何处理旧环境,保留了检查旧状态的余地。

不过,恢复过程中也暴露出另一个问题:代码和数据虽然已经有版本,运行条件仍有一部分散落在环境设置与旧对话里。换一个实例后,还得重新拼出工具版本、网络目的地、普通变量和验收步骤。

这说明灾备不只需要备份文件,也需要把“怎样重新工作”保存下来。

我给 Daywise 做了一个 recover-environment

recover-environment 的入口是 .agents/skills/recover-environment/SKILL.md。它适用于环境离线、启动或发布失败,以及需要用替代环境接管的情况。AGENTS.md 中也加入了入口,让新对话能找到这套方法。

实际落地的不只是一段提示词,而是几份相互配合的文件:

.agents/skills/recover-environment/SKILL.md  # 故障判断、恢复顺序与停止条件
environment/manifest.json                  # 非敏感环境配置清单
environment/install.sh                     # 检查运行时、安装固定版本依赖
environment/start.md                       # 普通启动说明
environment/recovery-prompt.txt            # 可以交给新 AI 窗口的恢复提示词
scripts/environment_recovery.py            # 导出配置、执行恢复验收
docs/环境灾后重建.md                         # 恢复流程与演练记录

Skill 负责告诉助手什么时候做什么,脚本负责完成能机械检查的部分。例如先保护现有修改,再核对代码版本,之后安装依赖、运行验收,最后等待实际图片上传测试。

它不会给客户端自动增加按钮,也没有创建、强制重启或发布云实例的能力。旧环境完全无法执行命令时,仍需要我先从具备环境管理功能的客户端准备替代环境,再让新窗口读取仓库中的技能。

配置清单具体保存什么

下面节选 environment/manifest.json 中的实际字段。运行时、变量名和普通变量值保留;八个网络目的地以占位符展示,不是可直接使用的域名配置:

{
  "runtime": {
    "python": "3.12",
    "node_major": 24,
    "venv": ".venv-market",
    "platform": "Linux"
  },
  "network": {
    "mode": "restricted",
    "presets": ["package_managers"],
    "allowed_domains": [
      "api.<git-host>",
      "<macro-data-host>",
      "<fund-data-host>",
      "query1.<market-data-host>",
      "query2.<market-data-host>",
      "<skill-search-host>",
      "uploads.<git-host>",
      "www.<exchange-host>"
    ]
  },
  "environment_variables": [
    {"name": "NODE_USE_ENV_PROXY", "suggested_value": "1"}
  ],
  "development_secret_requirements": [],
  "actions_only_secret_names": [
    "OPENAI_API_KEY",
    "SMTP_HOST",
    "SMTP_USERNAME",
    "SMTP_PASSWORD",
    "SMTP_PORT",
    "MAIL_FROM"
  ]
}

这些字段把几个容易遗忘的条件写明了:当前安装脚本要求 Python 3.12、Node 24,并使用 .venv-market 隔离依赖;网络保留 package_managers 预设,自定义域名按项目实际需要补齐,而不是每次凭旧聊天回忆。

NODE_USE_ENV_PROXY=1 是普通变量,用来让相应 Node 运行时使用环境提供的代理设置。平台提供的 HTTP_PROXY、HTTPS_PROXY 及证书信任配置继续继承,不把代理地址或认证值复制进清单。这不是所有 Node 版本与平台都适用的万能开关,其他项目仍需核对自己的运行条件。

actions_only_secret_names 只记录生产自动化所用的密钥或配置名称。当前恢复配置的 development_secret_requirements 是空列表:重建这个开发环境,不需要把 GitHub Actions 中的 OPENAI_API_KEY、SMTP 等值搬过来。运行恢复验收也不需要先生成一份付费报告或发一封测试邮件。

安装与验收各做什么

environment/install.sh 检查运行时,按需创建 .venv-market,根据 requirements-market.txt 安装固定版本依赖,再运行依赖一致性检查。它不重新锁定依赖,也不要求复制旧虚拟环境。

普通启动说明放在 environment/start.md。这个项目没有必须重新启动的后台服务;普通启动检查可以运行 verify,灾后接管则明确使用完整和在线验收:

cd /workspace/daywise
bash environment/install.sh
.venv-market/bin/python scripts/environment_recovery.py verify --full --online

--full 才会运行完整测试套件,--online 才会检查外部读取和归档同步。只运行默认的 verify,不能当成这两个范围都已经验收。

需要整理环境配置草稿时,还可以运行:

python3 scripts/environment_recovery.py config

它输出安装脚本、启动说明、允许域名和普通变量等配置字段,不会自动应用或发布环境,也不是客户端“一键导入完整环境”的保证。仓库选择、包管理器预设和环境访问范围仍需核对;已有配置要先读出再合并,不能拿一份新清单覆盖其他设置。

下一次,怎样给 AI 窗口下指令

我把恢复提示词保存在 environment/recovery-prompt.txt,这样换窗口以后,不必再从一大段旧聊天里拼步骤。实际使用时可以分成两种情况。

旧窗口已经连不上环境

这时先把工作范围说清楚。下面是根据这套技能整理的可复制提示词,不代表旧窗口一定仍能访问仓库:

当前云环境出现 environment_offline,命令或图片请求无法完成。
请先报告本次实际连接状态和命令执行结果,保留旧环境,不删除或覆盖文件。

如果还能读取 Daywise 仓库,请读取:
AGENTS.md
.agents/skills/recover-environment/SKILL.md
environment/manifest.json

按技能判断能继续到哪一步。若无法读取仓库或执行命令,请明确说明,
停止依赖这些能力的步骤,不循环重试,也不要凭记忆补造配置或诊断结论。

请说明需要我在具备环境管理功能的客户端完成哪些准备,
让替代环境使用同一项目与资源仓库的最新 main。
不要编造手机端按钮位置;新建、发布以及是否弃用旧环境由我操作。
不要重放旧记录、触发生产任务或开始新的业务。

目的不是让聊天强行修好一台离线机器,而是让助手说明已经确认什么、卡在哪里,以及我需要接手哪一步。

替代环境就绪后,交给新窗口执行

在新窗口中,可以使用下面这段提示词。路径与命令对应 Daywise 当前的实现;换成自己的项目时,需要替换仓库定位和配套脚本:

请使用 Daywise 最新 main 中的 $recover-environment 执行灾后重建。
若技能没有出现在菜单,直接读取 .agents/skills/recover-environment/SKILL.md。

先读取 AGENTS.md、README.md、environment/manifest.json、
docs/环境灾后重建.md 和上述技能文件。

1. 使用现有 /workspace/daywise、/workspace/gallery checkout。
   检查并保护已暂存、未暂存、未跟踪及必要的本地文件,
   通过既有 Git 授权检查最新 main 和 report-archive。
   仅在干净且可快进时同步;有本地独有提交或分叉时保留并报告,不覆盖。

2. 按 manifest 核对运行时与配置(本文版本为 Python 3.12、Node 24)。
   保留 package_managers 预设和已有域名,按 manifest 合并必要域名;
   普通变量 NODE_USE_ENV_PROXY=1,继承平台代理和证书信任,
   不复制 GitHub Actions 的密钥。配置草稿是否已发布要分开报告。

3. 从 /workspace/daywise 执行:
   bash environment/install.sh
   .venv-market/bin/python scripts/environment_recovery.py verify --full --online

4. 查看本次命令新生成的 outputs/recovery-*/acceptance.json。
   报告当前 Git 提交、实际测试数与跳过数、core_passed、online_passed、
   tracked_files_unchanged、归档结果及免费数据缺项。
   命令非零或检查失败时报告未完成,不能拿旧验收文件或旧缓存充数。

5. 脚本验收与新对话收图分开确认。
   我发送无敏感内容的测试图片后,只确认本次是否收到,不把它录入业务。

不 reset、不 force push、不删除旧环境,不重放历史记录或旧待办,
不刷新估值、不运行付费报告、不发邮件、不下单、不触发生产工作流。
完成后等待我明确决定是否继续业务,不沿用旧对话的日期或进度。

即使 $ 菜单没有显示技能,直接读取 SKILL.md 仍然是明确的操作入口。文件已在仓库中,不代表客户端已经自动注册或热加载;同样,提示词也不能代替新环境中的真实验收。

一次 132 秒的演练,证明了什么

实现推送以后,项目做了一次受控演练:在当前宿主机的独立临时目录,从远端全新克隆项目和资源仓库,创建新的虚拟环境,安装固定版本依赖。没有复制旧虚拟环境或旧产物目录。

这轮演练记录的结果是:

  • 从克隆、安装到完整测试和在线验收,用时 132 秒,约 2 分 12 秒。
  • 完整套件 120 项测试通过,0 项跳过。
  • 关键持久化文件逐一比对一致,没有重放业务记录。
  • 再次运行安装脚本成功,受版本管理的文件没有发生变化。
  • 相应代码与技术验收记录的远端无模型预检通过,没有为验证恢复而重新触发生产业务。

这给出的证据是:在这轮演练的条件下,可以从远端文件和仓库中的步骤,重新建立项目工作能力。

132 秒不是完整云端恢复时间。 演练没有计入云实例创建、环境发布、人工配置或新对话收图的时间。它仍然使用同一台宿主机提供的 Python、Node、系统代理、CA 信任和可用包缓存。

因此,要把前后两件事分开:前面那次替代环境,确实通过命令和真实收图确认了当次恢复;后面这轮干净克隆演练,验证的是仓库内恢复步骤的可重复性,没有另外创建并发布一台云实例,也没有再到另一新对话测试图片。

本次没有测量从故障到完整接管的总时长,也没有账单测算,不能据此给出费用或服务可用性的保证。更不能证明平台整体不可用时,替代实例仍然能够启动。

把“恢复成功”拆成几层证据

恢复验收应该能暴露问题,而不是只给出一个好看的成功提示。

verify 每次都会创建独立的 outputs/recovery-*/acceptance.json,终端会打印本次文件的具体位置。要读取这一次输出的文件,不能随便找一份旧的成功记录。

对于不熟悉脚本的使用者,可以让 AI 逐项解释这些真实字段:

  • core_passed:本地运行时、配置、依赖、目录写入等核心检查是否通过;使用 --full 时还包括完整测试。
  • full_tests_requested、online_requested:本次是否要求完整测试、在线检查。它们记录请求范围,本身不是通过证明。
  • online_passed:本次外部读取、归档与路由等检查是否通过;未请求在线检查时,不能声称已验证。
  • tracked_files_unchanged:受版本管理的文件前后校验值是否一致,恢复过程有没有改写它们。
  • online_checks.free_sources 内的 quality_counts:免费数据按质量状态统计的数量,不能用连接成功代替质量合格。
  • 同一项内的 unavailable_records:哪些数据仍不可用,需要保留未知。
  • image_upload:脚本写为 manual_check_required,等待新对话实际收图。
  • passed:脚本范围内的汇总结果,不包含客户端真实图片上传验收。

其中有一个需要特别留意的组合:脚本即使显示 passed=true,image_upload 仍然可以是 manual_check_required。这并不矛盾,只是说明脚本能检查的部分通过了,用户操作还没有完成验证。

online_checks.uploads_route 也只表示上传域名的只读 HTTPS 路由探测结果,不证明拥有上传权限,更不能替代手机端发图。最后仍需要我在新对话发一张无敏感信息的测试图片,让窗口确认收到。

这次检查中,就出现过外部连接成功,但部分返回数据没有通过原有质量检查的情况。恢复工具把“路由可用”和“数据缺项”分别记录,继续保留未知值,没有为了全部变绿而降低标准。

对使用者来说,这种回执更有用:能连接但数据不全,和完全无法连接,需要采取的下一步不同。把它们压成一句“恢复成功”,反而会掩盖仍然存在的问题。

这组技能仍然需要人做什么

准备替代环境、核对访问范围、审阅并发布环境配置,以及最后决定旧环境是否弃用,仍然需要使用者完成。技能把步骤和判断依据保存下来,并没有替代这些决定。

配置变化也要留下记录:更新 manifest.json、安装脚本和启动说明,检查后再将同一版本应用到环境。只改设置页面、不更新仓库,下次仍然需要重新找上下文。

这套办法依赖远端仓库和所需服务仍能访问。Git 托管不可用、仓库被删除,或者更大范围的平台故障,都不在这次演练已经解决的范围内。

这次值得留下来的经验,是把项目需要的状态尽量从某一台实例里分离出来:文件有恢复点,运行条件有清单,接管步骤有说明,成功与失败都有实际证据。

这些材料不能保证以后不再故障,却能让下一次恢复少依赖记忆,也更容易判断:工作究竟接回了哪一部分,还有哪些部分需要继续验证。