
"帮我做一个餐厅账单分摊计算器,算算每个人该付多少。"这句话听起来很清楚,细想起来却有不少问题:大家平摊,还是各自付自己点的菜?小费算在账单里了吗?如果总额不能平均分,应该显示什么?
我们和 Claude Code 一起梳理需求:您说明任务并作出决定,智能体把决定记录到文件里,您再检查记录。先从一句简短的请求开始,逐步整理出一份可执行的 TASK_SPEC。
"做得像样点"不算任务
想象一下:您只想快速分摊账单,智能体却加上了注册功能和餐厅记录。为了保存账单,它又需要一个服务器。您原本只想在浏览器里打开一个文件,现在还得研究怎么启动它、再删掉多余内容。每项新增功能单独看都挺有用,可最后的结果已经偏离了您的想法。
在编程工作中,没说清楚的地方可能变成实际的文件修改。事情往往会这样发展:
- 含糊的请求
- 智能体自行补全条件
- 出现多余的修改
- 您再来分析和返工
这张图展示了一个遗漏的决定如何带来额外工作。智能体可能会追问,不过最好在修改前就发现不明确的地方。最初的表述就可以先划定方向和边界:
还不明确:
帮我做一个餐厅账单分摊计算器,算算每个人该付多少。
更具体一些:
只需要一个页面:输入账单总额和人数,
显示每个人应付的金额,大家平摊。
不做账户、账单历史,也不按菜品分别结算。第二种说法还没有回答所有问题,但已经排除了根据第一种请求可能做出的几种完全不同的产品。接下来,智能体可以帮您找出剩下的空白,并记录您的决定。
把任务写进一个文件
TASK_SPEC 用来记录一项任务的条件:需要什么结果、工作到哪里为止,以及您要如何验证。通常会把它保存在项目旁边的 Markdown 文件 TASK_SPEC.md 中。这个名称并不代表某种特殊格式,也没有对应的专用命令。条件也可以直接发消息说明,但文件更方便反复查看和更新。
不必手动编写 TASK_SPEC。更方便的做法是和智能体一起整理,并检查它如何记录您的决定。您可以用平常的话描述任务、回答问题,也可以要求调整表述。智能体负责维护文件,您负责确认其中的意思。
- 您说明想得到什么结果,并讨论不明确的地方;
- 智能体提出几个选项,您选择合适的方案;
- 智能体把决定写进 TASK_SPEC,并展示新增或修改的片段;
- 您在文件中检查这段内容:意思准确、没有多余条件,也不与之前的记录冲突。发现问题就要求修改,再检查一次。
每作出一项决定后,检查受影响的几行和相关条件就够了。开始实现前,请重新阅读整个文件:单独看每条记录都可能没问题,放在一起却可能互相矛盾。智能体说"我更新了规格",并不能说明文件里具体写了什么。
CLAUDE.md 的用途不同。"项目中的所有标签都使用中文"是项目通用规则;"这项任务要添加小费计算"则是具体工作的条件。前者适合写入 CLAUDE.md,后者适合写入 TASK_SPEC。
文档的长度取决于任务。如果只需修正现有页面上的一个错别字,这样的任务说明就够了:
修正账单分摊页面上的按钮标签:
"记算"改为"计算"。其他内容保持不变。
确认新标签可见,而且按钮仍能正常计算。这是一份简短的 TASK_SPEC,有时也称为 tiny spec。几乎没有什么需要讨论。新计算器却有几种可能的行为,两行说明就不够了。值得记录的是那些会影响结果的决定;标题的数量本身并不能保证说明完整。
从结果开始
假设项目还不存在。几个朋友想在浏览器中打开一个页面,输入账单金额,查看每个人该付多少。我们让智能体先起草一份说明:
为账单分摊页面创建 TASK_SPEC.md。
项目尚不存在。输入账单金额和人数,
点击"计算"后显示每个人应付的金额。
最终需要一个无需安装即可在浏览器中打开的 index.html。
记录已确认的条件,未知之处标成问题。
展示记录下来的文本。暂时不要编写页面代码。
下面是文件开头部分的教学示例:
## 背景
几个朋友在餐厅用餐,想快速分摊账单。
项目尚不存在,工作目录为空。
## 预期行为
用户输入账单金额和人数。
点击"计算"后显示每位参与者应付的金额。
工作成果:一个 index.html 页面,
无需安装即可在浏览器中打开。在编辑器中打开 TASK_SPEC.md,对照请求检查记录:金额和人数两个输入项、操作以及每位参与者的结果都应写明。如果智能体加上姓名输入或强制注册,那就是未经确认的条件。请它从文件中删除这些内容,并检查修改。
现在已经明确谁会使用页面,以及会得到什么结果。这里还提到了产物,也就是智能体工作的具体成果:页面文件。其他任务的产物可能是错误分析,或是不含代码的修改计划。最好直接说明需要哪一种。
注意,我们还没有选择 JavaScript 函数,也没有规定如何存储参与者。这些属于实现细节。任务说明更应记录用户能看到的内容:输入、操作和结果。
明确任务边界
现在来明确第一版做到哪里为止。请智能体记录已经确认的边界,并展示更新后的部分:
## SCOPE
一个页面:输入账单金额和人数,
平分总额,并显示每个人应付的金额。
## NON-GOALS
不做账户和账单历史:无需保存数据。
不按菜品分摊:第一版采用平分。
实现限制:所有样式和脚本都放在一个 HTML 文件中,
不使用服务器或外部库。
页面应可通过双击打开。检查这段内容时,不仅要看排除项是否列出来:平分必须仍在任务范围内,按菜品分摊则必须在范围之外。如果智能体把两者弄反了,就在开始制作页面前修正文档。
Scope 描述工作范围。像这里一样,可以通过页面和使用场景来说明。知道准确路径时,写出来会有帮助,但一开始不必列文件。如果您要修改现有项目,却不知道目标页面在哪里,可以先让 Claude Code 只查找、不修改,并解释为什么选中该文件。然后再把路径补进任务说明。允许有未知项,但不要凭空编造。
Non-goals 是有意排除的内容。"不保存账单历史"比"别做多余的事"更能说明边界。简短写明原因对您也有帮助:一周后回来看,就知道当时考虑过数据保存,并决定不做。
实现限制回答的是另一个问题:哪些实现方式可以接受。账户是多余功能,服务器则是我们在这里排除的一种实现方式。在较短的文档里,可以把这些条件放在同一个部分,并明确标注为限制。
任务边界也不能妨碍目标实现。如果之后需要让多人在不同设备上使用共享账户和账单历史,就得重新考虑"一个本地 HTML 文件"这项要求。不能一边要求新功能,一边保留让它无法实现的限制。
忘记的一分钱
目前看起来已经足够明确。先在写代码前检验一下说明:三个人一起用餐,账单是 1000。每个人会看到多少?
333.33 + 333.33 + 333.33 = 999.99还差一分钱。可以把它加给第一位或最后一位,也可以让用户选择。"大家平摊"这句话并没有解决这个问题。这需要您作出决定,而不是考验您对 JavaScript 的了解。
我们为页面选定以下规则:每个人的金额最多相差一分钱;剩余的一分钱依次分给列表中的前几位。这样,1000 由三人分摊时,结果是 333.34、333.33、333.33;账单为 1000.01 时,有两分钱需要分配,结果是 333.34、333.34、333.33。所有金额都分完了,各份金额之和也与账单相符。
现在请智能体把选定的规则写进文档:
把舍入规则写入 TASK_SPEC.md 的预期行为部分。
展示修改过的行。暂时不要改代码。检查文件中是否出现了类似这样的记录:
各份金额之和必须正好等于总额。
各份金额最多相差 0.01。
剩余的一分钱依次分给排在前面的参与者;
页面要说明这条规则。特别留意"依次分给排在前面的参与者"这句话。如果智能体写成"把所有余款都给第一位",决定就变了:余下两分钱时,第一位会比其他人多付 0.02。请它修正这一行,再检查文件。要核对意思,不必要求逐字一致。
这就是把数字代入任务说明的用处。智能体选定方案并据此写代码之前,我们就发现了遗漏的决定。到目前为止,只改了几行。
让 Claude 提出问题
有些遗漏您未必能自己发现。把草稿交给智能体,请它专门帮您梳理任务说明:
阅读 @TASK_SPEC.md,帮我找出尚未明确的条件。
每次只问一个问题,并为每个问题提供一个建议选项。
在我回答之前,不要把你的建议当作已确认的决定。
每确认一项决定,就更新 TASK_SPEC.md,
并展示修改后的片段,方便我检查。
暂时不要写代码。下面是一段对话示例。您实际会话中的问题可能不同,重点是观察回答如何改变文档。
Claude:"小费包含在账单里,还是另行添加?我建议单独设置一个百分比输入框,默认值为 0。"
您:"单独计算。按账单金额计算百分比,把小费加上去后再一起分摊。页面上显示小费金额和总额。"
Claude:"如果算出的小费带有不足一分钱的零头,该怎么舍入?我建议舍入到最近的一分钱,正好半分钱时向上舍入。"
您:"可以。先舍入小费金额,再加到账单上。然后按我们定的规则分摊总额。"
Claude:"输入值有哪些要求?我建议账单金额必须为正数,可以用小数点或逗号;人数必须是大于等于 1 的整数;小费百分比必须是非负整数。输入有误时,在对应字段旁显示提示。"
您:"可以。金额最多保留两位小数,千位之间不能有空格。如果字段为空或数值无效,就隐藏结果,即使之前已经算过也一样。"
我们不只明确了小费。现在也确定了可接受的输入、舍入顺序和出错时的行为。把回答写入后,再检查文件中更新的部分:
小费:单独输入非负整数百分比,初始值为 0。
按账单金额计算小费,并舍入到最近的一分钱;
正好半分钱时向上舍入。
先把舍入后的小费加到账单,再分摊总额。
显示小费金额、总额和每个人应付的金额。
输入为空或无效时隐藏结果,
并在对应字段旁显示提示。检查顺序:先舍入小费,再加到账单,最后分摊总额。同样的词换个顺序,就会变成不同的要求。也请智能体更新 scope:第一版新增了小费输入框和总额计算。确认它真的改了这两个部分,而不只是聊天里口头答应。
请智能体如实记录:"小费如何处理尚未决定。"您可以讨论选项,也可以明确把这项功能排除在第一版之外。智能体提出的建议可以帮助您作出选择,但它本身并不会自动变成您的要求。
结果与验证方式
"各份金额之和等于总额"是结果要求。"输入 1000、3 人和 0% 小费,点击按钮,看到 333.34、333.33 和 333.33"则是一项验证。它明确了要执行的操作以及预期观察到的结果。
请智能体现在就把几项这样的验证写入文件,也就是在实现之前。预期数字由您自己核对。否则很容易接受实际结果,再不知不觉地按照已经做好的页面调整预期。
| 输入与操作 | 预期结果 |
|---|---|
| 1000;3 人;0%。点击"计算" | 总额 1000.00;各份金额 333.34 / 333.33 / 333.33 |
| 把账单改为 1000.01,再次计算 | 总额 1000.01;各份金额 333.34 / 333.34 / 333.33 |
| 1000;3 人;10% | 小费 100.00;总额 1100.00;各份金额 366.67 / 366.67 / 366.66 |
| 10.05;3 人;10% | 小费 1.01;总额 11.06;各份金额 3.69 / 3.69 / 3.68 |
| 1000;1 人;0% | 一份金额:1000.00 |
| 成功计算后清空金额再计算;然后恢复金额并输入 0 人 | 两种情况下都不显示分摊金额,无效字段旁显示提示 |
10.05 这一行用于验证操作顺序:10% 是 1.005,舍入后为 1.01。如果在其他步骤舍入,总额可能不同。为了验证输入格式,再用逗号输入一次:10,05 应得到相同结果。另外,试试负数金额、非整数人数、负数或小数百分比,以及空的小费字段:都不应执行计算。
每项重要条件都应有对应的观察结果。"页面打开了"并不能证明金额正确,就像链接地址正确也不能证明目标页面已经加载。请验证这项任务真正关心的行为。
整理 TASK_SPEC
智能体在讨论过程中把决定逐项记进了文件。现在请通读全文:回答是否都已记录,验证是否符合要求,有没有遗留的旧条件。这就是本例最终的文档。它有六个主要部分:背景、预期行为、scope、non-goals、验证计划和安全启动。标题方便快速定位,内容则来自我们对任务的梳理。
# TASK_SPEC.md
## 背景
几个朋友想分摊餐厅账单。
项目尚不存在;除了 TASK_SPEC.md,工作目录为空。
## 预期行为
工作成果:一个可双击打开的 index.html,
以及一份简短的验证说明。
页面有三个输入框和一个"计算"按钮:
- 账单金额大于 0,最多保留两位小数;
可用小数点或逗号,不使用千位分隔符;
- 人数为大于等于 1 的整数;
- 小费百分比为非负整数,初始值为 0。
按账单金额计算小费,舍入到最近的一分钱;
正好半分钱时向上舍入。
先把舍入后的小费加到账单,再分摊总额。
显示小费、总额和带编号的各人应付金额。
各份金额之和等于总额,彼此相差不超过 0.01。
余款依次分给排在前面的参与者;
页面要说明这条规则。
输入为空或无效时隐藏结果,
并在无效字段旁显示提示。
## SCOPE
一个页面:三个输入框、计算、提示和金额展示。
按上述余款分配规则平分总额。
## NON-GOALS
不需要账户和账单历史:不保存数据。
不按菜品分摊:这是计算器的另一种版本。
限制:样式和脚本都放在 index.html 中,
不使用服务器或外部库,以便在本地打开。
## 验证计划
双击打开 index.html,点击"计算":
- 1000 / 3 人 / 0% → 333.34;333.33;333.33;
- 1000.01 / 3 / 0% → 333.34;333.34;333.33;
- 1000 / 3 / 10% → 小费 100.00,总额 1100.00,
各份金额 366.67;366.67;366.66;
- 10.05 / 3 / 10% → 小费 1.01,总额 11.06,
各份金额 3.69;3.69;3.68;输入 10,05 时结果相同;
- 1000 / 1 / 0% → 一份金额 1000.00。
每种情况都要将各份金额相加,并与总额核对。
检查人数为零或小数、负数金额、负数或小数百分比、
金额超过两位小数,以及逐一清空每个字段:
无效字段旁显示提示并隐藏结果,包括计算成功之后。
检查金额编号以及余款分配规则的说明。
## 安全启动
阅读任务说明并查看工作目录内容。
如果里面已有项目文件,先停下来确认任务;
不要覆盖或删除现有文件。
先提问并制定计划,不写代码,然后等待批准。
如果实现需要修改 scope 之外的内容,
先确认任务说明,再进行修改。
文档变长,是因为我们补充了实际需要的决定。它没有逐步教人如何编写 JavaScript,但说明了小费应该怎么处理,以及如何检查每一分钱。您的任务可能不需要写得这么详细。
编写自己的任务说明时,可以按这份简短清单逐项检查:
| 部分 | 可以问自己什么 |
|---|---|
| 背景 | 目前是什么情况?为什么需要这项任务? |
| 预期行为 | 要实现什么功能?最终会得到什么? |
| SCOPE | 要修改哪个使用场景和项目部分? |
| NON-GOALS | 排除了什么?有哪些限制需要保留? |
| 验证计划 | 要执行什么操作?预期看到什么? |
| 安全启动 | 修改前需要查明并确认什么? |
条件发生变化时
现在来做个小练习。用户看过任务说明后说:"百分比不方便,我想直接输入小费金额,比如 100。" TASK_SPEC 中哪些部分需要更新?先试着说出来,再看下面的分析。
在预期行为中,要把百分比输入框改成小费金额输入框:金额不能为负,可以使用小数点或逗号,最多保留两位小数。初始值仍为 0。删除百分比公式和计算小费时的舍入规则:直接把输入的小费金额加到账单上。scope 中要修改计算方式;验证计划中要更新输入值和无效输入场景。总额分摊方式和 non-goals 保持不变。
验证"账单 1000、小费 100、三人分摊"仍会得到熟悉的 366.67、366.67、366.66。但只看这几个数字,无法区分新版本和旧版的 10% 小费。再加一项不同的测试:账单 500,加固定小费 100,两人分摊。每人应付 300;旧版按 10% 计算则是每人 275。这项验证能发现规则是否变了。
请智能体更新预期行为、scope 和验证项,然后展示所有修改过的片段。打开文件,确认旧的百分比公式已删除,新增的验证与金额输入相符。不要同时保留旧公式和"现在改为输入金额"这样的补充说明:任务说明只能得出一种解释。上面的主示例仍采用百分比;这里讨论的是对它的另一种独立修改。
与 Claude Code 配合使用
逐项检查修改后,在开始实现前重新阅读整个 TASK_SPEC。确认目标、边界和验证项描述的是同一项任务。然后明确把文件交给智能体:
阅读 @TASK_SPEC.md。先检查是否存在矛盾,
或仍需确认的条件。
给出简短计划,并列出将要创建或修改的文件。
计划获得批准前不要修改代码。这样,在实现开始前就已经有东西可以检查了。如果计划里出现了用于保存账单的服务器,请让智能体修改计划:账单历史已被排除,而且页面必须能在本地打开。如果计划合适,就批准实现。完成后打开生成的文件,按照记录的场景逐项验证,包括成功计算后输入无效值的情况。
Non-goals 仍然只是文字条件,并不会从技术上阻止修改。因此,检查计划和验证结果都很重要。智能体说"完成了"可以帮助您了解它做了什么,但不能代替您核对预期数值。
任务结束后,如果您把 TASK_SPEC 保留在项目中,请将任务标记为已关闭。工作还在继续时,则要及时更新文件里的决定。让智能体帮您拟定措辞并记录内容;是否正确,仍以文档本身为准:讨论后检查修改片段,实现前通读全文。
GO TO FULL VERSION