初始化脚本


文档摘要

初始化脚本 本节摘要:每个冷启动的会话都在交税——Agent 读同样的文件、重试同样的探针、重新发现同样的路径。一个初始化脚本一次性付清这笔税,把答案写进状态。诊断很直接:打开一个会话,Agent 猜 Python 版本、猜测试命令、列五次仓库根找入口、试 import 一个没装的包、问用户配置文件在哪——等它做出第一次真实编辑,一万 token 已花在本该是一个脚本的准备工作上。修法是一个初始化脚本,在 Agent 做任何事之前跑,写一份 让 Agent 启动时读。

初始化脚本

本节摘要:每个冷启动的会话都在交——Agent 读同样的文件、重试同样的探针、重新发现同样的路径。一个初始化脚本一次性付清这笔税,把答案写进状态。诊断很直接:打开一个会话,Agent 猜 Python 版本、猜测试命令、列五次仓库根找入口、试 import 一个没装的包、问用户配置文件在哪——等它做出第一次真实编辑,一万 token 已花在本该是一个脚本的准备工作上。修法是一个初始化脚本,在 Agent 做任何事之前跑,写一份 init_report.json 让 Agent 启动时读。本节定义脚本该探查什么(运行时版本、依赖可用性、测试命令、仓库路径、环境变量、状态与板的新鲜度、上次已知良好提交),以及三条铁律:失败要响、要快、要集中在一个地方(探针失败即停并上报人,没有「Agent 会自己搞定」);幂等(连跑两次,第二次除时间戳外是 no-op,这让它能接进 CI、钩子、pre-task 斜杠命令);无网络、无 LLM、热路径无意外(探针是确定性管道,调 LLM 分类失败或查外部服务许可证的不是探针是工作流,干跑超 3 秒即工作台坏味道)。本节还讲清初始化 vs 启动规则的区分:规则(第 33 节)描述「行动前什么必须为真」,初始化是建立「这些规则能否被检查」的脚本——规则无初始化变「小心点」,初始化无规则变「精致的失败」。读完本节,你能为项目写一个确定性、幂等、失败即停的初始化脚本。

对应原课程:Phase 14 · Lesson 35 · initialization-scripts(原英文 phases/14-agent-engineering/35-initialization-scripts/docs/en.md)。前置:第 32 节(最小工作台)、第 34 节(仓库记忆)。

学习目标

阅读完本节,你应当能够:

  1. 识别 Agent 每会话不该重做的工作。
  2. 构建一个确定性探查运行时、依赖、仓库健康的初始化脚本
  3. 持久化探查结果,让 Agent 读它而非重跑检查。
  4. 初始化失败时失败要响、要快、要集中在一个地方
  5. 区分初始化脚本启动规则

一、问题与直觉

打开一个会话。Agent 猜 Python 版本。猜测试命令。列五次仓库根找入口。试 import 一个没装的包。问用户配置文件在哪。等它做出一次真实编辑,一万 token 已花在本来该是一个脚本的准备工作上

修法是一个初始化脚本,在 Agent 做任何事之前跑,写一份 Agent 启动时读的 init_report.json

脚本探查什么

探针 为何重要
运行时版本 错的 Python 或 Node 版本意味着静默的错版本 bug
依赖可用性 一个缺包,后来抓的成本是现在抓的十倍
测试命令 Agent 必须知道怎么验证;命令缺了,工作台就坏了
仓库路径 硬编码路径会漂移;解析一次并钉住
环境变量 OPENAI_API_KEY 是失败面,不是运行时谜题
状态与板的新鲜度 崩溃会话留下的过期状态是 footgun
上次已知良好提交 会话结束时交接 diff 的锚点

失败要响、要快、要集中在一个地方

探针失败意味着停并上报人。没有「Agent 会自己搞定」。初始化的全部意义,就是在工作台坏了时拒绝启动

幂等

连跑两次。第二次除新鲜时间戳外应是 no-op。幂等性让你能把脚本接进 CI、钩子、或一个 pre-task 斜杠命令。

初始化 vs 启动规则

规则(第 33 节)描述行动前什么必须为真。初始化是建立这些规则能否被检查的脚本。规则无初始化变「小心点」。初始化无规则变「精致的失败」。两者配套才有意义。

二、从零实现

原课程 code/main.py 实现 init_agent.py:

  • 五个探针:Python 版本、经 importlib.util.find_spec 列依赖、测试命令可解析性、必填环境变量、状态文件新鲜度。
  • 每探针返回 (name, status, detail)
  • 脚本写带完整探针集的 init_report.json,任一 block 严重度探针失败即非零退出。

Step 1:探针接口 + 实现

@dataclass class Probe: name: str; status: str; detail: str # status: ok / warn / block def probe_python(): v = ".".join(map(str, sys.version_info[:3])) return Probe("python", "ok" if sys.version_info >= (3,10) else "block", v) def probe_deps(packages): missing = [p for p in packages if importlib.util.find_spec(p) is None] return Probe("deps", "block" if missing else "ok", f"missing={missing}") def probe_test_cmd(cmd): ok = shutil.which(cmd.split()[0]) is not None return Probe("test_cmd", "ok" if ok else "block", cmd) def probe_env(keys): missing = [k for k in keys if not os.getenv(k)] return Probe("env", "block" if missing else "ok", f"missing={missing}") def probe_state_freshness(path, max_age_hours=24): age = time.time() - os.path.getmtime(path) return Probe("state_fresh", "warn" if age > max_age_hours*3600 else "ok", f"{age/3600:.1f}h")

Step 2:聚合 + 失败即停

def init(): probes = [probe_python(), probe_deps(REQUIRED), probe_test_cmd("pytest"), probe_env(["OPENAI_API_KEY"]), probe_state_freshness("agent_state.json")] report = {"timestamp": now(), "probes": [asdict(p) for p in probes]} atomic_write("init_report.json", json.dumps(report, indent=2)) blocks = [p for p in probes if p.status == "block"] if blocks: print("INIT FAILED:"); [print(f" - {p.name}: {p.detail}") for p in blocks] sys.exit(1) # 失败要响,非零退出 return report

运行 python3 code/main.py 会打印探针表、写 init_report.json,happy path 退出零,或有失败探针列表时非零退出。

生产模式(把仪式变成有用的脚本)

三条把有用初始化与仪式分开的模式。

上次已知良好提交锚定。 把当前提交对一份在上次成功合并时写的 LKG 文件做 diff。若 diff 超预算(默认 50 文件),拒绝启动,要人批准新基线。这是 Cloudflare AI 代码评审用来给审查者 Agent 定范围的:每次评审会话锚定同一上次已知良好,从不跨会话累积漂移。

带 TTL 的锁文件。 第一次成功探查后写 prereqs.lock。后续运行在 N 小时内(默认 24h)信任锁,跳过昂贵探针。初始化脚本先读锁;若新鲜且依赖清单哈希匹配,短路。同 Docker 层缓存的模式:幂等探查 + 内容哈希 = 跳过。

热路径无网络、无 LLM、无意外。 初始化探针是确定性管道。调 LLM 分类失败、或打外部服务查许可证的探针,不是探针,是工作流。干跑里一个探针超 3 秒,当作工作台坏味道,要么移出初始化、要么缓存其结果。

💡 设计要点:初始化脚本是「付一次税,而非每会话付」的工程纪律。它的价值不在「跑通」,而在把不确定性前置消除——Agent 不再猜版本、猜命令、猜路径,它读一份确定性的报告。这与第 31 节「触发器」原语(会话启动触发器调初始化)、第 34 节「状态播种」一脉相承:初始化是会话启动触发器的函数体。

三、框架对比

运行时 初始化如何接
Claude Code 钩子 pre-task 钩子调初始化脚本,失败即拒启动 Agent
GitHub Actions setup-agent job 跑初始化脚本;Agent job 依赖它
Docker entrypoint Agent 容器 exec Agent 运行时前跑初始化脚本;失败时日志浮现

初始化脚本可移植,因为它不调特定框架。Bash、Make、或 tasks 文件都能包它。

四、可复用产物

原课程 outputs/skill-init-script.md:采访项目,把它的准备工作分进探针,产出项目特定的 init_agent.py 加一个在任何 Agent 步骤前跑它的 CI 工作流。

五、练习

  1. (Easy) 加一个探针,把当前提交对上次已知良好做 diff,改超 50 文件即拒启动。
  2. (Medium) 让脚本写 prereqs.lock,锁超 7 天即拒启动。
  3. (Medium)--fix 标志,自动装缺的开发依赖,但绝不无审批改运行时依赖。
  4. (Hard) 把探针从硬编码函数迁到 YAML 注册表。论证取舍。
  5. (Hard) 每探针加时间预算。跑超 3 秒的探针是工作台坏味道。

本节要点回顾

  1. 冷启动交税:Agent 每会话重读文件、重试探针、重发现路径;初始化脚本一次付清,写进状态。
  2. 七类探针:运行时版本、依赖可用性、测试命令、仓库路径、环境变量、状态新鲜度、上次已知良好提交。
  3. 失败要响要快要集中:探针失败即停上报人,没有「Agent 自己搞定」;工作台坏时拒绝启动。
  4. 幂等:连跑两次第二次 no-op(除时间戳),让能接 CI/钩子/斜杠命令。
  5. 无网络无 LLM 热路径无意外:探针是确定性管道;调 LLM/外部服务的是工作流;干跑超 3 秒即坏味道。
  6. 初始化 vs 启动规则:规则描述「行动前什么为真」,初始化建立「规则能否被检查」;配套才有意义。
  7. 上次已知良好锚定:对 LKG diff,超预算(50 文件)拒启动要人批;Cloudflare 评审用此定范围。
  8. TTL 锁文件:首次成功写 prereqs.lock,24h 内信任跳过昂贵探针;同 Docker 层缓存。
  9. 可移植:不调特定框架,Bash/Make/tasks 文件都能包。
  10. 付一次税而非每会话付:把不确定性前置消除,Agent 读确定性报告而非猜。

下一节,我们做实「范围」这一面——范围契约:如何用允许/禁用文件 glob、验收标准、审批边界,把「只动该动的文件」从愿望变成验证门能强制的硬约束。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U