我以为我会写 Prompt,结果 Cursor Agent 给我整了个大活

上周五下午,我在赶一个小需求:把项目里的 parseDate 函数改成支持时区。

需求本身不复杂,我平时用 Cursor Chat 处理这类事情很顺手,于是顺手把同样的描述扔给了 Cursor SDK Agent:

"帮我优化 parseDate 函数,让它支持时区处理。"

然后我去倒了杯水。

回来之后,Agent 的执行日志已经滚了满屏。它不仅改了 parseDate,还顺手"优化"了整个 utils/date.ts,把三个相关的辅助函数重命名了,又因为函数签名变了,连带着改了七个调用方文件,最后发现有两个单测跑不过,于是又开始修单测……

我盯着那二十多条 File modified 记录,沉默了大概十秒钟。

这不是 Agent 的 bug。是我的描述方式出了问题——而且,是一种我之前完全没意识到的、根本性的思维模型错误。

---

坑一:你在写"指令",但 Agent 需要的是"终态"

先说清楚普通 Cursor Chat 的运作逻辑。

当你在 Chat 里输入"帮我把这个函数改成支持时区",模型的处理方式是:接收指令 → 生成回复 → 结束。单轮、即时、无状态。它不会主动去看其他文件,不会连续执行多步操作,它的"视野"就是你给它的上下文。

答错了?你看一眼,觉得不对,重新发一条消息,成本极低。

SDK Agent 的运作逻辑完全不同。

根据 Cursor 官方文档对 Agent mode 的定义,Agent 的设计目标是自主完成多步骤任务:它会读取文件、调用工具、执行命令、修改代码,然后根据结果决定下一步该做什么。整个过程可能持续十几轮,期间几乎不需要你介入。

这意味着什么?

它不需要你告诉它怎么做,它需要你告诉它做成什么样算完

你给它的不是操作步骤,而是一份验收标准

但大多数人——包括一个月前的我——习惯性地把 Prompt 思维带进来,写出来的是这样的东西:

❌ 指令式(错误)

先读取 utils/date.ts,然后找到 parseDate 函数,

再把它改成支持传入时区参数,用 date-fns-tz 库来处理。

看起来很详细,对吧?但问题在于,你把"怎么做"说得很清楚,却没说"做成什么样算完"。Agent 执行完你列的步骤之后,它会继续往下走——因为它觉得任务还没"完成",它会自己判断还有什么需要做。

正确的写法应该是:

✅ 目标式(正确)

parseDate 函数需要支持传入可选的时区参数(string 类型),

默认值为 'UTC'。改动范围仅限 utils/date.ts 这一个文件,

改动后所有现有单测必须通过,不新增外部依赖。

注意这两种写法的本质区别:前者描述的是过程,后者描述的是结果

Agent 是一个会自主决策的执行者,你给它过程,它会在过程之外继续发挥;你给它结果,它会以结果为锚点,自己规划路径。

---

坑二:没写边界条件,Agent 帮你"重构"了整个项目

理解了"目标式描述"之后,很多人会犯第二个错误:只说了目标,忘了说边界

这个错误比第一个更隐蔽,也更危险。

普通 Prompt 的容错成本很低。模型答错了,你不采纳就好了,最多浪费几秒钟。但 SDK Agent 不一样——它在执行,它在真实地修改你的代码库。等你发现它跑偏,可能已经是十五分钟后、二十个文件被改动之后了。

更关键的是:Agent 在"目标"和"允许范围"之间,会默认选择它认为最优的路径。

你说"重构用户模块,让代码更清晰",这是一个目标。但"允许范围"是什么?Agent 不知道。于是它会按照自己的判断来决定:可以改哪些文件?可以引入新的设计模式吗?可以拆分模块吗?可以改接口签名吗?

在它的逻辑里,只要能让代码"更清晰",这些都是合理的手段。

结果就是我开头描述的那个场景。

来看一组对比:

❌ 缺边界

重构用户模块,让代码更清晰,提高可维护性。

✅ 加边界

重构 src/user/profile.ts 中的函数命名风格,

统一改为动词开头的驼峰命名(如 getUserInfo → fetchUserProfile)。

只改这一个文件,不修改任何接口签名,不新增或删除依赖,

不影响其他模块对这些函数的调用方式。

边界条件的核心作用是:把 Agent 的决策空间从"无限"压缩到"可控"

没有边界,Agent 的自主性是你的敌人;有了边界,Agent 的自主性才是你的朋友。

---

两种思维模型的本质差异

把上面两个坑拆开来看,你会发现它们指向同一个根本问题:普通 Prompt 和 SDK Agent Task Description,是两种完全不同的思维模型。

| 维度 | 普通 Prompt(Chat 模式) | SDK Agent Task Description | | 描述对象 | 当前这一步要做什么 | 任务完成时的终态是什么 | | 执行模式 | 单轮、即时、你主导节奏 | 多轮、自主、Agent 主导节奏 | | 容错成本 | 极低(看一眼就知道对不对) | 极高(跑偏后可能改了几十个文件) | | 关键要素 | 清晰的指令 + 足够的上下文 | 目标终态 + 约束边界 + 验收标准 |

基于这个对比,我总结出了一个 SDK Agent Task Description 的写作公式:

目标终态 + 约束边界 + 验收标准(可选)
  • 目标终态:任务完成后,代码/功能/文件应该是什么状态?
  • 约束边界:哪些文件可以动?哪些不能动?允不允许新增依赖?接口签名能不能改?
  • 验收标准:怎么判断任务做完了?单测通过?特定函数可以被正确调用?

有一个检验方法特别好用:用"Definition of Done"思维来审视你写的 Task Description

在敏捷开发里,DoD(完成的定义)是一个团队约定的验收清单,用来判断一个 Story 是否真正完成。你可以把同样的思路用在 Task Description 上:写完之后,问自己一个问题——"如果 Agent 完全按照我写的这段话执行完,我能判断出任务是否做好了吗?"

如果答案是"不确定",说明你的描述还不够清晰。

---

实战模板:拿走即用的三个场景

下面是三个常见场景的完整 Task Description 模板,每一行都附了写法逻辑。

场景一:Bug 修复

## 任务:修复日期显示 Bug

目标终态

在 src/components/DateDisplay.tsx 中,

修复当传入的日期字符串为空字符串时组件崩溃的问题。

修复后,传入空字符串应显示占位符文本"--",不抛出异常。

约束边界
  • 只修改 DateDisplay.tsx 这一个文件
  • 不改变组件的 Props 接口定义
  • 不引入新的依赖
验收标准
  • 现有单测全部通过
  • 手动传入空字符串时,组件正常渲染,显示"--"
为什么这样写:明确了"崩溃"的复现条件(空字符串),明确了期望行为(显示"--"),明确了边界(只改一个文件、不改接口),最后给出了可操作的验收方式。Agent 不需要猜测任何东西。

---

场景二:功能新增

## 任务:为搜索框新增防抖功能

目标终态

为 src/components/SearchBar.tsx 的 onChange 事件添加防抖处理,

防抖延迟为 300ms。用户停止输入 300ms 后,才触发实际的搜索请求。

约束边界
  • 只修改 SearchBar.tsx
  • 可以使用 lodash.debounce(项目已有此依赖),不引入其他新依赖
  • 不改变组件对外暴露的 Props 接口
  • 不影响已有的单测结构(可以修改单测内容以适配新行为)
验收标准
  • 快速连续输入时,网络请求只在停止输入后触发一次
  • 组件卸载时,防抖定时器被正确清除(无内存泄漏)
为什么这样写:功能新增类任务最容易"发散",Agent 可能会顺手把整个输入处理逻辑都重构了。约束边界里明确"可以用 lodash.debounce",既给了 Agent 明确的工具选择,又堵死了它自己引入其他库的可能性。

---

场景三:代码重构

## 任务:统一 API 层的错误处理方式

目标终态

将 src/api/ 目录下所有文件中的 try-catch 错误处理,

统一改为使用已有的 handleApiError 工具函数(位于 src/utils/error.ts)。

约束边界
  • 只修改 src/api/ 目录下的文件
  • handleApiError 函数本身不做任何修改
  • 不改变任何 API 函数的签名和返回值类型
  • 不修改 src/api/ 以外的任何文件
验收标准
  • src/api/ 目录下不再有裸露的 console.error 调用
  • 所有现有单测通过
  • 错误处理逻辑与改动前行为一致(只是统一了写法)
为什么这样写:重构类任务是最危险的,因为"更好"的定义太模糊。这里用"只修改 src/api/ 目录"把范围钉死,用"不改变函数签名"堵死了接口破坏的可能,用"行为一致"明确了这是纯粹的形式重构,不是功能变更。

---

写好 Task Description,是在训练工程师思维

如果你认真对比了上面三个模板,会发现一件有趣的事:这些模板里的写法,其实和一份好的技术需求文档非常像。

目标终态对应需求描述,约束边界对应技术约束,验收标准对应测试用例。

这不是巧合。写好 Task Description 的本质,是把一个模糊的想法转化成可验收的任务说明——这正是工程师日常工作里最核心的能力之一:把产品经理说的"让它更好用"翻译成"在 A 场景下,操作 B,结果应该是 C"。

换句话说,你在训练 Agent 的同时,也在训练自己把需求说清楚的能力。这个能力,在和人类同事协作时同样有用。

---

💡 想直接测试这些 Task Description 模板的效果?

如果你需要稳定的 Claude / GPT API 访问渠道来调试 Agent 任务,推荐 [api.884819.xyz](https://api.884819.xyz)——支持主流模型,国内直连,按量计费,没有月租,国产模型(Deepseek / 通义千问等)完全免费。新用户注册即送体验 token。

把本文的三个模板复制进去,对比"❌错误写法"和"✅正确写法"的实际输出差异,是最快的学习方式。调试 Agent 任务描述时,成本可控很重要。

---

最后留一个问题给你思考:今天我们聊的,都是任务开始之前的问题——怎么写出一份好的 Task Description,让 Agent 不跑偏。

但还有一个同样容易翻车的环节:当 Agent 执行到一半"卡住",或者主动向你提问时,你该怎么回应?

如果你回答得不好,它可能会按照自己的理解继续走,然后把之前做的一半工作全部推翻重来。这个问题和 Task Description 一起,构成了 SDK Agent 协作的完整方法论。

下一篇我们聊这个。

---

本文由8848AI原创,转载请注明出处。关注8848AI,带你从零开始学AI。

#AI编程 #Cursor #Agent开发 #Prompt技巧 #AI工具 #工程师思维 #8848AI #SDK开发