让 AI 从 .env.example 和 Dockerfile 抽配置,生成的部署文档才不靠模型瞎猜默认值

很多团队用 AI 写部署文档时都踩过同一个坑:模型不知道你项目里实际有哪些环境变量,于是开始编造 REDIS_URLJWT_SECRET 这类看起来合理但根本不存在的配置。解法很简单——别让模型空手写,先把 .env.example、Dockerfile、docker-compose.yml 这些真实配置文件喂给它,让它从源码里抽取,而不是从训练记忆里猜。

我最近在一个 Go 服务项目里完整跑了一遍这个流程,生成的部署文档准确率从「大概能看」提升到「可以直接交给运维执行」。这篇文章把这个方法拆开讲清楚。

核心思路:把配置抽取和文档生成拆成两步

直接让 AI「读项目然后写部署文档」的效果不稳定,因为模型会同时处理太多信息:代码结构、依赖关系、配置项、部署步骤。更可靠的做法是两步走:先让 AI 只做一件事——从配置文件中抽取出结构化的环境变量清单,然后再基于这份清单生成文档。

第一步的输出应该是这样的结构化数据:

{
  "variables": [
    {
      "name": "DATABASE_URL",
      "required": true,
      "default": null,
      "description": "PostgreSQL connection string",
      "source": ".env.example:12"
    },
    {
      "name": "LOG_LEVEL",
      "required": false,
      "default": "info",
      "description": "Logging level",
      "source": "Dockerfile:8"
    }
  ]
}

有了这份清单,第二步让模型写文档时就有了硬约束——它只能使用清单里的变量,不能凭空添加。我在 prompt 里加了一句:「如果文档中需要引用环境变量,只能使用上述清单中的变量名,不得新增或改名。」效果立竿见影。

第一步实操:从 .env.example 抽取环境变量

先看一个典型的 .env.example 文件:

# Server
PORT=8080
HOST=0.0.0.0

# Database
DATABASE_URL=postgres://user:password@localhost:5432/mydb?sslmode=disable
DB_MAX_CONNS=20
DB_IDLE_TIMEOUT=30s

# Redis (optional)
REDIS_ADDR=localhost:6379
REDIS_PASSWORD=
REDIS_DB=0

# Auth
JWT_SECRET=
JWT_EXPIRY=24h

给 AI 的 prompt 要明确三点:一是识别注释行并作为变量描述,二是保留默认值(空值也要标记),三是标记哪些变量明显是必须填写的(比如 JWT_SECRET 后面没有默认值)。我用的 prompt 大致这样:

从以下 .env.example 文件内容中抽取所有环境变量,输出 JSON 格式。
规则:
1. 每行 KEY=VALUE 是一个变量,KEY 为变量名
2. 注释行(以 # 开头)如果紧邻变量上方,作为该变量的描述
3. VALUE 为空字符串时,default 字段设为 null,required 设为 true
4. VALUE 非空时,default 为该值,required 设为 false
5. 保留变量在文件中的行号,记录到 source 字段

这里有个细节:要求模型输出行号(source 字段),是为了后续人工复核时能快速定位。别小看这个,当模型抽错或者漏抽时,行号能帮你 10 秒内发现问题。

第二步实操:从 Dockerfile 抽取构建时配置

Dockerfile 里的配置分两类:ENV 声明的运行时环境变量,和 ARG 声明的构建参数。两者都要抽,但在文档里要分开处理。

FROM golang:1.22-alpine AS builder

ARG VERSION=dev
ARG BUILD_TIME=unknown

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build \
    -ldflags="-X main.Version=${VERSION} -X main.BuildTime=${BUILD_TIME}" \
    -o /app/server .

FROM alpine:3.19

ENV PORT=8080 \
    LOG_LEVEL=info \
    TZ=Asia/Shanghai

RUN apk add --no-cache tzdata ca-certificates

COPY --from=builder /app/server /usr/local/bin/server

EXPOSE 8080

ENTRYPOINT ["server"]

给 AI 的 prompt 需要额外说明:

从以下 Dockerfile 中抽取配置项,分为两类:
1. ENV 指令:运行时环境变量,记录默认值
2. ARG 指令:构建参数,标记为 build_arg: true
输出 JSON,格式与之前一致,source 字段记录行号。

实际测试中,GPT-4o 和 Claude 3.5 Sonnet 都能准确区分 ENVARG,但有一个常见错误:把 EXPOSE 8080 也当成配置项抽出来。需要在 prompt 里明确排除 EXPOSEWORKDIRCOPYRUN 等指令,只关注 ENVARG

第三步:合并去重,处理冲突

两个来源抽取出的变量可能有重叠。比如 .env.example 里有 PORT=8080,Dockerfile 里也有 ENV PORT=8080。合并时的规则要提前定好:

  1. 同名变量,如果默认值一致,合并为一条,source 记录两个来源
  2. 同名变量,如果默认值不一致,以 .env.example 为准(因为它是面向部署者的配置入口),但在 description 里标注 Dockerfile 中的差异
  3. 只在 Dockerfile 中出现的变量,保留,标记 source: "Dockerfile"

这个合并逻辑我直接写在了 prompt 里让模型执行,对于十几个变量的项目,模型处理得很干净。但如果你的项目有 50+ 个变量,建议写个小脚本做合并,不要让模型做这种机械操作——模型在大量重复性合并任务上容易出错。

第四步:基于清单生成部署文档

拿到最终的变量清单后,生成文档的 prompt 核心约束就一句话:

以下是该项目的完整环境变量清单。生成部署文档时:
1. 只能使用清单中的变量名,不得新增、修改或遗漏任何变量
2. 每个变量的默认值必须与清单一致
3. 标记为 required 的变量,文档中必须明确说明「部署者必须自行设置」
4. 标记为 build_arg 的变量,放在「构建参数」小节,不要混入运行时环境变量

这一步生成的文档质量取决于清单质量。清单准,文档就准;清单有遗漏,文档就会漏配置——但至少不会瞎编。

实际效果对比:不用这个方法时,模型生成的文档里出现了 REDIS_URL(项目里实际叫 REDIS_ADDR)、DB_HOST/DB_PORT(项目里用的是一个完整的 DATABASE_URL),还漏掉了 TZ 这个在 Dockerfile 里定义的变量。用了清单约束后,这些错误全部消失。

进阶:引入 docker-compose.yml 和 CI 配置

如果你的项目还有 docker-compose.yml、Kubernetes manifests 或 CI 配置文件,这些也是配置项的重要来源。docker-compose.yml 里可能有 environment 段、.env 文件引用、secrets 声明。CI 配置里可能有部署环境特定的变量。

把这些也纳入抽取范围,prompt 里加一条来源标记规则即可。比如:

source 字段取值规则:
- .env.example 中的变量 → ".env.example"
- Dockerfile 中的变量 → "Dockerfile"
- docker-compose.yml 中的变量 → "docker-compose.yml"
- 多个来源都有 → 用逗号连接,如 ".env.example,Dockerfile"

我最近处理的一个项目,配置分散在 4 个文件里,最终清单有 37 个变量。模型一次抽取全部成功,但前提是每个文件的 prompt 都写清楚了该文件特有的语法规则(比如 YAML 的 environment 段格式、${VAR:-default} 语法)。

常见问题

为什么不直接让 AI 读整个项目然后生成文档?

因为模型处理大量文件时注意力分散,容易凭训练记忆补全它认为「应该有」的配置。把抽取和生成分开,每一步的任务边界清晰,出错概率大幅降低。而且中间产物(变量清单)可以人工检查,发现问题及时修正再继续。

清单抽取这一步,模型漏掉变量怎么办?

在 prompt 里加一条强制规则:「输出完成后,请自查:是否覆盖了输入文件中的每一行配置声明?如有遗漏,重新输出完整清单。」实测这条规则能减少大约 80% 的遗漏。另外,如果你用 Claude,可以开启 extended thinking,对长文件的抽取准确率有明显提升。

配置文件里用了变量嵌套,比如 DATABASE_URL=${DB_HOST}:${DB_PORT}/mydb,怎么处理?

这种情况下要让模型在 description 里说明这个变量的实际组成部分,并把 DB_HOSTDB_PORT 也作为独立变量列出(如果它们在文件里有定义的话)。prompt 里加一条:「如果变量的值中包含对其他变量的引用(${VAR} 语法),将被引用变量也加入清单,并在描述中标注引用关系。」

这个方法适合多大体量的项目?

变量数在 50 以内的项目,直接让模型处理完全没问题。超过 50 个变量,或者配置分散在 10 个以上文件的项目,建议写脚本做初步抽取(正则匹配 KEY=VALUE 模式),再让模型做语义理解和描述生成。模型擅长理解,不擅长穷举——这是目前 LLM 的普遍特性。