前言

        你是否被通义灵码生成的 from .. import 折磨过?是否苦于它到处写 os.getenv,部署后才发现配置缺失?问题的根源不在 AI 能力不足,而在于你给它的“上下文”不够像一份合同。本文将拆解 AI 代码生成问题的本质,并给出契约式提示词的完整构成框架。

一、AI 编程为什么总出 Bug?

        AI 编程工具本质上是一个基于概率的 token 预测器。在没有任何强约束时,它会生成最泛化、最保险的代码,而那恰恰是生产环境最不想要的

三大典型毒瘤与成因

  1. 路径导入混乱(ModuleNotFoundError

    • 现象:生成的文件里出现 from ..models import X 或 from config import settings,一运行就报错。

    • 根因:AI 不掌握你的项目根目录位置,它会假设代码在某个包层级。相对导入是最容易匹配多种层级关系的“通用解”,但往往与你的真 实包结构冲突。

  2. 环境变量失控(os.getenv 散落各处)

    • 现象:数据库连接、API Key 等关键配置用 os.getenv("KEY", "默认值") 读取,生产环境未注入变量时悄然使用默认值,造成数据错乱或服务瘫痪。

    • 根因os.getenv 是 Python 读取环境变量的最简方式,不需要额外依赖。AI 倾向于选择“独立成段、无需导入其他模块”的写法,且带默认值的写法能让代码在本地跑通,它便视为“安全”。但生产环境的安全恰恰需要强制显式配置

  3. 结构命名随意(前后端字段不一致)

    • 现象:数据库字段叫 customer_phone,后端模型叫 phone,前端请求又用 tel

    • 根因:自然语言描述存在多义性,AI 在不同生成环节独立命名,没有统一的元数据锚点。

根因总结:AI 缺乏跨越生成片段的统一契约,每一个代码块都在自己的“合理”世界中运行,拼在一起就崩。


二、什么是契约式提示词?

        契约式提示词(Contract-Driven Prompting),就是把你对代码的所有隐性期望,明确转化为不可违反的硬性约束,写在提示词开头,并贯穿每次交互。

        它的本质是:在项目初始化阶段,为 AI 与你的协作建立一份宪法。之后每次生成,都在这部宪法下进行,AI 的自由度被压缩到业务逻辑本身,环境适配类 Bug 大幅减少。


三、契约的核心构成——哪些东西必须被“锁死”?

        一套完备的 AI 编程契约至少应包含以下 7 个维度的明文规定。不需要你写一大堆模板代码,只需要在每次输入时,清晰列出这些规则即可。

1. 项目路径锚定

  • 必须规定:项目根包名(如 app),绝对导入前缀(from app.xxx import yyy)。

  • 严禁行为:相对导入(from ..)、sys.path.append()、硬编码绝对路径。

  • 为什么有效:消除了所有路径猜测,统一模块引用语法。

2. 配置获取铁律

  • 必须规定:配置统一从 app.core.config 的 Settings 单例获取,代码中只能出现 settings.XXX

  • 严禁行为os.getenvos.environload_dotenv() 出现在业务逻辑中。

  • 为什么有效:配置项被中心化管理,生产环境可通过任何手段注入(K8s Secrets、Docker 变量),程序内部无感知。

3. 数据模型锁定

  • 必须规定:数据库表名、字段名、类型、约束以 SQLAlchemy 模型或 Pydantic 定义的方式完整给出;前后端共用的实体以 JSON Schema 或接口定义的形式固化。

  • 严禁行为:让 AI 自行推断字段或命名。

  • 为什么有效:数据是一切逻辑的骨架,骨架确定后,Service 和 API 层的命名、类型自然统一。

4. 函数签名契约

  • 必须规定:关键方法的函数名、参数列表、返回类型、异常类型必须预先指定。

  • 例如:不加“写一个创建用户的接口”,而要写“实现 async def create_user(user: UserCreate, db: AsyncSession) -> User,其中 User 为 Pydantic 模型 UserSchema,异常为 DuplicatedError”。

  • 为什么有效:AI 只能在给定的输入输出类型内填充实现,不会乱改契约。

5. API 路由与通信协议

  • 必须规定:URL 路径、HTTP 方法、请求/响应模型(response_model)、状态码。

  • 例如@router.post("/customers", response_model=CustomerResponse, status_code=201)

  • 为什么有效:前后端接口定义成为强制标准,不会出现“写了 GET 却用 POST 调”的尴尬。

6. 外部依赖调用规范

  • 必须规定:HTTP 调用用 httpx.AsyncClient,超时设为 10 秒;文件操作用 aiofiles;Word 解析用 python-docx 等。

  • 严禁行为:AI 自己“创造”库,或使用同步库阻塞事件循环。

  • 为什么有效:技术栈统一,避免出现混用 requests 和 httpx 的并发问题。

7. 错误处理与日志策略

  • 必须规定:业务异常继承 BusinessError,全局异常处理器捕获;日志记录使用 loguru 或标准 logging,错误不丢失上下文。

  • 为什么有效:异常和日志行为一致,不会半途遗漏关键错误信息。


四、契约的高效来源——不要手写,要“截取”

        好的契约不是从零构造,而是从已有确定性源头直接抽取。按效率排序的契约来源:

优先级 来源 操作方式
P0 数据库 DDL / ORM 模型 直接复制 CREATE TABLE 或模型类定义
P1 OpenAPI / Swagger 文档 喂给 AI 生成前后端,所有字段、路径已严格定义
P2 JSON Schema 用于复杂嵌套结构,生成类型定义,再锁死

推荐工作流

  1. 先让 AI 生成一份纯模型定义或接口 Schema(无任何业务逻辑)。

  2. 你审核、调整,直到完全符合业务需求。

  3. 将审核后的定义作为铁律嵌入后续所有提示词。

这样你就成了“契约审核官”,AI 只是执行者。


五、实战范例:一份极简的契约式提示词骨架

以下是精简到极限的契约描述(无冗余代码),适合放在每次对话开头或设为 IDE 自定义指令。

# 契约
- 导入: 统一使用 `from app.xxx import yyy`,禁止相对导入。
- 配置: `from app.core.config import settings`,禁止 `os.getenv`。
- 路径: 基于 `pathlib.Path(__file__).resolve().parent.parent`。
- 数据库: 异步 SQLAlchemy 2.0,模型表名 `snake_case` 复数,类名 `PascalCase` 单数。
- 前端: React 18 + TS + Tailwind,毛玻璃风格 `backdrop-blur-lg bg-white/20`。

# 本次任务
1. 模型: [直接粘贴已审核的 SQLAlchemy 模型代码]
2. Service 签名: `async def judge_customer(source: bytes, type: str) -> JudgementResult`
3. API: `POST /judge` 返回 `JudgementResult`
4. 逻辑: 用 `openpyxl` 解析 Excel,列映射“姓名→name”,调用 Dify API 需加 Bearer Token。

效果:基于这几行约束,AI 生成的代码不会在路径、配置、命名上出错,只剩下你真正需要 review 的业务逻辑。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐