编程提示词就是一份需求说明。模型没见过你的项目,不知道你用的是哪个版本的语言,也没法问你输入为空时该怎么办。提示词没写的地方,它都会用训练数据中最常见的选择来填补,而最常见的选择往往不是你要的。下面的提示词能让模型少猜一些东西。
先写需求说明,再写代码
下面的提示词要求写一个小的 Python 函数。提示词的每个部分都回答了一个问题,如果不写,模型就会替你回答。把这些部分一个一个关掉,想象一下没有它们的回复会是什么样:没有约束,你可能会拿到一个第三方库;没有上下文,模型只能猜什么算有效输入;没有格式要求,你可能拿不到测试。
这个函数按顺序匹配三个可省略的部分,并拒绝三个部分都为空的匹配。
import re
_PATTERN = re.compile(r"(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?")
def parse_duration(text: str) -> int:
match = _PATTERN.fullmatch(text)
if not match or not any(match.groups()):
raise ValueError(f"invalid duration: {text!r}")
hours, minutes, seconds = (int(g) if g else 0 for g in match.groups())
return hours * 3600 + minutes * 60 + seconds
import pytest
from duration import parse_duration
@pytest.mark.parametrize("text, expected", [
("1h30m", 5400), ("45m", 2700), ("2h", 7200), ("90s", 90),
])
def test_valid(text, expected):
assert parse_duration(text) == expected
@pytest.mark.parametrize("text", ["", "1m1h", "1.5h"])
def test_invalid(text):
with pytest.raises(ValueError):
parse_duration(text)
这个提示词里有四个细节承担了大部分工作:
- 带答案的示例。 “1h30m 返回 5400”是一个测试,模型可以拿它来检验自己的代码,同时也消除了对单位的任何疑问。
- 语言版本和允许使用的库。 不写的话,你可能会拿到一个你没安装的库,或者比你的解释器更新的语法。
- 什么算无效,以及无效时该怎么办。 没人要求的话,模型很容易漏掉错误处理。
- 答案里附带测试。 测试把“看起来没问题”变成了可以运行的东西。如果某个测试失败,你可以把失败信息贴回去,这比一句“不能用”好得多。
any(match.groups()) 这个检查也值得注意:单看这个模式,它能匹配空字符串,因为每个部分都是可选的。正是提示词中关于空字符串的那一行,让这种情况出现在了代码和测试里。
写明版本、技术栈和已有的东西
模型倾向于使用训练数据中最常见的写法。对 JavaScript 来说,这可能意味着在使用 ES 模块的项目里写出 CommonJS 的 require;对 Python 来说,可能意味着用了后来已经改变的库 API(Pydantic 1 和 2 就是常见的例子);对任何更新很快的框架来说,可能意味着用的是两个大版本之前的写法。通常一行话就能解决:“Node 22,ES 模块,不用 TypeScript”或者“React 19,函数组件,普通的 CSS 模块”。
在已有项目上添加功能时,模型需要看到新代码会涉及的部分。粘贴它必须调用的函数签名、它会收到的数据结构,以及一个能体现你的代码约定的现有文件。“使用下面文件中的 db.query(sql, params) 辅助函数”能换来与项目契合的代码;不写这句,你可能会得到一段从零写起的新数据库连接。与这次改动无关的文件就不要放进来,因为每一行无关的内容,模型都可能试图拿去复用。
每次只要一小步
在“vibe coding”(通过向 AI 描述来开发应用)中,最常见的失败是一次就要整个应用。模型必须在一个回复里选好框架、数据库、文件夹结构和十几个功能,而一个回复很少能装下全部能运行的代码,所以常常得到的只是一个大纲。对比下面两个标签页。
下面是一个使用 React、Node.js + Express 和 MongoDB 的全栈待办事项应用。
项目结构
todo-app/
client/ (React front end)
server/ (Express API, auth, reminders)
server/index.js
const express = require("express");
const mongoose = require("mongoose");
const app = express();
app.use(express.json());
// ... auth routes, todo routes and the reminder scheduler go here
身份验证可以用 jsonwebtoken 加上 JWT,提醒功能可以用 node-cron 这样的定时任务库。如果需要我把其中某个部分补全,告诉我就行。
第一个回复不算错,但它只是一个骨架:它替你选了三种技术,又把真正要做的工作留在了注释里。第二个回复短到可以读完,打开文件就能运行,还给了你一个可以用来做第 2 步的基础(“现在把列表保存到 localStorage,让它在刷新后还在”)。每一步都足够小,出问题时你就知道是哪处改动导致的。
这就是手动进行的提示词链:一次请求的输出成为下一次请求的起点。每做新的一步,都把文件的当前版本粘贴进去,这样模型修改的是你手上真实的代码,而不是它记忆中自己写过的那份。
大改动之前先要计划
只要比一个函数大,就先要计划,再要代码:“列出你要修改的文件,以及每处修改做什么。先不要写代码。”计划读起来快,改起来也快。如果它提出要加一个你不想要的依赖,或者漏掉了你知道会涉及的文件,你用一句话就能纠正,而不必在三百行代码里才发现问题。
检查返回的结果
生成的代码出错的方式有几种是可以预见的,每一种都有对应的提示词习惯可以防住:
- 编造的 API。 模型可能调用一个并不存在的函数或导入一个并不存在的包,只因为这个名字听起来很合理。安装不熟悉的导入之前先查一查;AI 幻觉解释了为什么会这样。
- 被忽略的边界情况。 代码在正常情况下能跑,遇到空列表就崩溃。在提示词里列出边界情况并要求写测试,是成本最低的解决办法。
- 悄悄的改动。 当你要求修复一个长文件时,模型可能还会顺手重命名一些东西,或重新组织你没提到的代码。加上一句“只改必要的地方,并列出你做的每一处改动”。
如果代码能运行但行为不对,就换成调试用的提示词:调试提示词讲了该粘贴些什么。合并任何重要的代码之前,再用代码审查提示词过一遍,能发现写代码的提示词没想到要问的问题。
常见问题
用 ChatGPT 或 Claude 写代码,最好的提示词是什么?
没有一个万能的提示词。管用的提示词读起来像一份简短的需求说明:语言和版本、代码接收什么、返回什么、两三个示例输入及其输出、边界情况,以及代码不能使用的东西。最后加一句“同时为这些情况写测试”,你就有办法检验答案,而不是只能相信它。
vibe coding 提示词是什么?
“Vibe coding”指的是主要靠向 AI 描述你想要的东西来开发软件,并直接接受它写出的代码,往往不仔细阅读。能让 vibe coding 项目一直保持可用的提示词都很小:每次请求只做一个功能,清楚说明已经有哪些东西,并要求先运行或测试结果再继续。一次要求做完一大堆,正是这类项目最容易出问题的地方。
要不要告诉 AI 使用哪个版本的编程语言?
要。语言和库在不同版本之间会变化,不写明的话,模型会采用训练数据中最常见的写法,而它可能比你的环境更旧。写明版本(“Python 3.12”“React 19,使用函数组件”“Node 22,ES 模块”)可以避免回答用到你根本没有的 API。
AI 写的代码可以信任吗?
把它当作新同事写的代码来对待:大概率八九不离十,有时却错得看上去很对。运行它,用你关心的边界情况测试它,并仔细阅读涉及金钱、安全或用户数据的部分。模型还可能编造不存在的函数或包,所以安装任何东西之前,先查一查不熟悉的导入。
为什么项目变大以后,AI 生成的代码就容易出问题?
模型只能看到对话里的内容。项目变大后,它看不到你没给它看的文件,只能靠猜来补上名称、结构和之前的决定。粘贴相关的文件,说明项目遵循的约定,并让每次请求只做一处改动。