Three Consumers, One Interface

中文翻译即将推出

Three Consumers, One Interface

Back in 2024 I wrote about a unified CLI interface for dev machine and CI agent, and about migrating our build scripts to pyinvoke. The problem I was solving then was developer experience. The same invoke deploy --env=staging should run on a laptop and on a build agent, so that a failing pipeline can be reproduced locally instead of by pushing small tweaks and hoping one of them works.

Since then I have been handing a fair amount of work to AI coding agents. It turns out that interface has a third consumer. In this post I’ll go through what changed in practice, and why the CLI investment paid off in a way I did not plan for.

Discoverability

An agent starts every session with no context about our codebase, our conventions, or how anything gets deployed. A new engineer is in the same position on day one; the difference is that the agent starts over every time. Both will otherwise dig through README.md, piece shell commands together from documentation, and guess.

invoke -l gives the whole capability surface in one command:

invoke test                    #Run unit tests
invoke package --version=X     #Build and tag a Docker image
invoke publish --version=X     #Push the image to the registry
invoke deploy --env=X          #Deploy to the target environment
invoke smoke-test --env=X      #Run post-deployment validation
invoke peek-a-order            #Quick problem diagnosis.

Each task has a description, and the parameters are explicit. There’s no documentation to read and no scripts to grep through.

Compare this with a codebase where the tasks live in bash scripts, inline pipeline YAML, and a README that hasn’t been touched for two quarters. An agent can work with that, but it has to explore, infer and guess first. Every delegation starts with an exploration phase, and that phase burns context before any useful work happens.

Where project knowledge lives

Knowledge about how a project works tends to end up in one of three places.

In people’s heads. e.g. the engineer who knows which --flag to add when deploying to prod on a Friday, or the workaround for the flaky integration test. It is lost when they leave, and inaccessible to everyone else in the meantime.

In UI-oriented tools. When we were setting up API testing, the obvious candidates were Postman and httpie. Postman is a GUI, so an agent can not open it; httpie commands live in someone’s shell history or notes. Neither shows up in invoke -l; Neither can be called from CI.

In versioned CLI tasks. We went with pyinvoke, and the API tests became invoke smoke-test --env=staging.

Two things work together in the third option. The tasks live in the repo and are versioned with the code, so when the API changes the task changes in the same commit. And because they are CLI, every consumer can reach them: the engineer, CI, and an agent. A Postman collection drifts; an invoke task co-evolves.

That is the kind of knowledge an agent can act on. Not general knowledge about API testing, but our team’s specific way of doing it, current as of the last commit.

Delegating a deployment

Before, handing a deployment over meant explaining the whole sequence: build the Docker image, push it, update the manifest and apply it, and check the smoke tests pass.

Now it is: run invoke package --version=1.4.2, then invoke publish, then invoke deploy --env=staging, then invoke smoke-test --env=staging.

The second version is barely a prompt. It is a sequence of well defined operations the agent can execute, observe and report on; the context is already encoded in the tasks.

Extracting a skill

Once that sequence repeats often enough, and for routine deployments it does, it is worth extracting it as a project level agent skill.

# .agents/skills/deploy/SKILL.md
Deploy a service version to an environment.
Use when asked to release or deploy a version.
Steps: invoke package -> invoke publish -> invoke deploy -> invoke smoke-test.

The instruction then collapses to “deploy 1.4.2 to staging”. The agent calls the skill, the skill runs the sequence, and I get a summary back. The CLI provides the raw material; The skill is the recipe.

Progressive disclosure

The encapsulation works for both consumers because a CLI discloses itself progressively.

  • invoke -l shows what exists, one line per task, with no implementation detail.
  • invoke deploy --help shows the parameters, flags and expected values. You only load this once you have decided to run that particular task.
  • The code inside the task is implementation detail. Neither a human nor an agent needs to read it to use the task.

Agent skills follow the same pattern, one layer up.

CLI:
  invoke -l              what exists          (always available)
  invoke deploy --help   how to use it        (load on demand)
  invoke deploy --env=X  execution

Skills:
  skill descriptions     what exists          (always in context)
  SKILL.md content       how to orchestrate   (load when relevant)
  tool/CLI calls         execution

The constraint being managed is the same in both cases. Too much detail too early crowds out the actual task; this applies to an engineer orienting on a new project and to an agent with a finite context window.

Day to day

A few things that changed in practice.

Onboarding. Instead of a 15 step wiki page for environment setup, one line in AGENT.md: run invoke -l to see what this project can do. The same line works for a new engineer.

Reproducing a CI failure. The build goes red. Previously: read the pipeline logs, work out which script failed, reconstruct the environment by hand, iterate. Now I tell the agent that the deploy step failed in CI and ask it to reproduce it locally. Because invoke deploy --env=staging runs the same code in both places, it can start narrowing down the failure straight away.

Routine releases. “Package, publish and deploy 1.5.0 to dev, run smoke tests and tell me if they pass.” The agent runs the sequence and reports the result; I review it and decide whether to promote.

Diagnosis. invoke peek-a-order takes a snapshot of system state. “Run peek-a-order and summarise what looks unusual.” The agent comes back with structured observations, and I don’t have to remember which commands to run or in which order.

Conclusion

If you are working out where to start with AI adoption, look at your CLI before you look at models, agent tools or prompt techniques. Can an agent run invoke -l on your project and understand what it can do? Can it run a deployment without you explaining each step? If not, that is the work, and it pays off for the engineers on the team as well as for the agents.

三个消费者,一个接口

2024 年我写过开发机与 CI agent 的统一 CLI 接口,以及把构建脚本迁移到 pyinvoke。当时我要解决的是开发体验的问题。同一个 invoke deploy --env=staging,在笔记本上和在 build agent 上都应该能跑,这样流水线失败的时候可以在本地复现,而不是一点点改了往上推,指望其中某一次碰巧能过。

从那以后,我把不少工作交给了 AI coding agent。结果发现这个接口还有第三个消费者。这篇文章讲一下实际发生的变化,以及当初在 CLI 上的投入为什么以我没预料到的方式产生了回报。

可发现性

agent 每次会话开始时,对我们的代码库、规范、以及东西是怎么部署的都一无所知。新工程师第一天也是同样的处境;区别在于 agent 每次都要重来一遍。否则两者都会去翻 README.md,从文档里拼凑 shell 命令,然后靠猜。

invoke -l 一条命令就给出全部的能力范围:

invoke test                    运行单元测试
invoke package --version=X     构建并打标签 Docker 镜像
invoke publish --version=X     推送镜像到镜像仓库
invoke deploy --env=X          部署到目标环境
invoke smoke-test --env=X      运行部署后验证
invoke peek-a-order            快速问题诊断

每个任务都有描述,参数是显式的。不需要读文档,也不需要 grep 脚本。

对比一下另一种代码库:任务散落在 bash 脚本、pipeline YAML、以及两个季度没动过的 README 里。agent 也能应付,但它必须先探索、推断、猜测。每一次委托都要从探索阶段开始,而这个阶段在任何有用的工作发生之前就把上下文烧掉了。

项目知识存放在哪里

关于项目怎么运作的知识,通常会落在三个地方之一。

在人的脑子里。e.g. 知道周五往 prod 部署要加哪个 --flag 的工程师,或者绕过不稳定集成测试的办法。人一走就没了,在此之前对其他人也不可及。

在 UI 类工具里。我们搭 API 测试的时候,明显的候选是 Postman 和 httpie。Postman 是 GUI,agent 打不开它;httpie 命令活在某个人的 shell 历史或者笔记里。两者都不会出现在 invoke -l 里;两者也都没法从 CI 调用。

在有版本的 CLI 任务里。我们选了 pyinvoke,API 测试变成了 invoke smoke-test --env=staging

第三种做法里有两件事同时起作用。任务住在仓库里,和代码一起版本化,所以 API 变了,任务在同一个 commit 里跟着变。而且因为是 CLI,每一个消费者都能访问它:工程师、CI、以及 agent。Postman 集合会漂移;invoke 任务会跟着代码一起演进。

这才是 agent 能直接拿来用的知识。不是关于 API 测试的通用知识,而是我们团队具体的做法,精确到最近一次提交。

委托一次部署

以前,把部署交出去意味着要把整个流程解释一遍:构建 Docker 镜像,推送,更新 manifest 并 apply,再检查冒烟测试有没有过。

现在是:运行 invoke package --version=1.4.2,然后 invoke publish,然后 invoke deploy --env=staging,然后 invoke smoke-test --env=staging

第二种几乎算不上是 prompt。它是一串定义明确的操作,agent 可以执行、观察并汇报;上下文已经编码在任务里了。

提取成 skill

等这个序列重复得足够频繁,常规部署一定会,就值得把它提取成一个项目级的 agent skill。

# .agents/skills/deploy/SKILL.md
将服务的某个版本部署到指定环境。
当被要求发布或部署版本时使用。
步骤:invoke package -> invoke publish -> invoke deploy -> invoke smoke-test。

指令于是压缩成「把 1.4.2 部署到 staging」。agent 调用 skill,skill 执行序列,我收到一份汇报。CLI 提供原材料;skill 是菜谱。

渐进披露

这种封装对两类消费者都有效,是因为 CLI 把自己渐进地暴露出来。

  • invoke -l 显示有什么,每个任务一行,没有实现细节。
  • invoke deploy --help 显示参数、标志和期望值。只有当你决定要跑某个具体任务时才加载这一层。
  • 任务内部的代码是实现细节。人和 agent 都不需要读它才能使用这个任务。

agent skill 遵循同样的模式,在上面再加一层。

CLI:
  invoke -l              有什么            (始终可用)
  invoke deploy --help   怎么用            (按需加载)
  invoke deploy --env=X  执行

Skills:
  skill 描述列表          有什么            (始终在上下文中)
  SKILL.md 内容           怎么编排          (相关时加载)
  工具调用/CLI 调用        执行

两种情况下被管理的约束是同一个。太早引入太多细节会把真正的任务挤掉;这对在新项目上摸索的工程师成立,对上下文窗口有限的 agent 也成立。

日常

实际变化的几件事。

入职。不再是 15 步的 wiki 环境配置页,而是 AGENT.md 里的一行:运行 invoke -l 看这个项目能做什么。同一行对新工程师也管用。

复现 CI 失败。构建变红了。以前:读流水线日志,弄清楚哪个脚本失败了,手动重建环境,然后迭代。现在我告诉 agent 部署步骤在 CI 里失败了,让它在本地复现。因为 invoke deploy --env=staging 在两边跑的是同一份代码,它可以立刻开始缩小故障范围。

常规发布。「把 1.5.0 打包、发布、部署到 dev,跑冒烟测试,告诉我过没过。」agent 执行序列并汇报结果;我看一下,决定要不要往上晋升。

诊断。invoke peek-a-order 给系统状态拍个快照。「跑 peek-a-order,总结一下有什么不对劲。」agent 返回结构化的观察,我不用去记该跑哪些命令、按什么顺序。

结论

如果你在想 AI 落地该从哪里开始,先看你的 CLI,再去看用哪个模型、买哪个 agent 工具、怎么写 prompt。agent 能对你的项目运行 invoke -l 并理解它能做什么吗?它能在你不逐步解释的情况下跑完一次部署吗?如果不能,那就是要做的工作,而且回报不只属于 agent,也属于团队里的工程师。

Share this post: 分享这篇文章:

Comments 评论