设想一下:您让智能体在一个已经写好的浏览器游戏里下一局棋。它走了几步,描述一下局面,然后就停下来了。棋局还没结束。您打一句"继续"。它又走一步棋,又停下来等您说话。到某个时候会发现,这里最枯燥的活儿,其实是您在干。

Goal 是一个带完成条件的目标,您用命令 /goal 交给智能体。比如:"双方轮流走,直到页面显示棋局结束为止"。每完成一轮工作,就有一个单独的模型根据对话判断目标是否达成。如果还没达成,工作会继续,不需要您再说一句"继续"。这样您就有机会等到结果出来,当然障碍或错误仍然可能提前让工作停下。
我们先从一个现成的游戏条件讲起,再为搜索、表单和移动端页面各写一个目标。每一个都有明确的结果和验证方式。顺便讲讲怎样让智能体替您写条件,以及怎样区分真正的完成和没有结果的停止。
/goal 让什么继续下去
围绕这个游戏例子的对话里,其实涉及两套不同的机制,很容易混为一谈。一个在浏览器里执行动作:点击、输入走法、读取页面显示的内容。另一个什么都不点 - 它读整段对话,判断要不要继续工作。
第一个是 Playwright,一个预先通过 MCP 接入的浏览器工具。第二个由命令 /goal 启动。Playwright 负责移动棋子,/goal 负责判断该不该停 - 这是两件不同的事,哪怕表面上看好像是同一回事。
| 谁在行动 | 发生了什么 |
|---|---|
| 智能体通过 Playwright | 在浏览器里执行下一个动作,得到一个观察结果 - 页面实际显示的内容 |
/goal 的评估者 | 智能体完成一轮工作之后,读取对话,判断:条件还没达成、已经达成,还是根本不可能达成 |
有一个细节很重要:评估不是在每次鼠标点击之后发生,而是在智能体完成一轮工作之后 - 粗略地说,是它的一整轮回复之后。在这一轮里,智能体完全可以连续做好几个动作:走一步棋,读结果,再对结果作出反应。/goal 是在这之后才介入的,那时候已经有东西可以评估了。
/goal 不会添加 Playwright,也不会添加任何其他工具 - 它只是盯着已经在进行的工作。它也不会取消常规的权限确认:如果设置要求某个动作先经过确认,智能体照样会问,不管有没有设定目标。
先把游戏准备就绪
在设定条件之前,示例本身得处于可用状态:页面能通过一个已知地址打开,游戏所需的工具已经接入(还是那个 Playwright),页面上还得有一个能看出棋局是否结束的标记。
在我们的例子里,这是一个 data 属性:棋局还在进行时是 data-game-over="false",结束之后是 data-game-over="true"。这个约定由写游戏页面的人定下来 - 智能体不会自己发明,只是读取它。
验证起来很简单:让智能体打开页面,读一下这个属性的值。对于刚开始的一局,它会读到 false,这很正常,开局就该是这样。
如果页面上根本没有这个属性,那设定目标还为时过早。条件只能建立在真正能读到的东西上,所以得先把这个标记补上,而不是临时给它想一个替代品。既然示例已经配置好了,就不要在对局过程中改动判定标准,也不要为了让演示快点结束而给游戏另外加一个计时器。条件从头到尾都应该保持一致。
启动一个现成的目标
接下来是具体的命令。我们这个例子的条件表述大致是这样:
/goal 通过页面界面轮流走双方的棋。
每走一步之后读取 data-game-over,并展示页面上看到的内容。
当 data-game-over 变成 true 时,棋局判定为结束。
如果 40 步之后还没有出现这个结果,停下来,描述卡在了哪里。
不要修改游戏代码。把这段表述拆开看看。同样的几个部分,您自己写条件时也用得上:
- "通过页面界面...走棋" - 一个可衡量的动作,智能体真的能在对话里展示出来;
- "当 data-game-over 变成 true 时,棋局判定为结束" - 这里唯一算作真正结果的东西。评估者要在对话里找的正是它;
- "每走一步之后读取...并展示" - 评估者正是靠这个事实才能核实点什么。没有这一句,它就无从判断;
- "如果 40 步之后还没有出现这个结果,停下来,描述" - 专门针对 true 始终没出现这种情况的说明。它不是成功的一部分,而是一种没有结果也能结束工作的方式:限定了目标的规模,但不能替代结果本身。而且这仍然是文字里的一条指令,不是硬性的程序计时器 - 它同样要通过对话来评估;
- "不要修改游戏代码" - 智能体追求结果的过程中不应该发生的事。
最终会得到什么,事先并不知道 - 具体谁赢、棋局多长,甚至能不能结束,都没法保证。但智能体应该展示什么是清楚的:读到的游戏状态,以及它继续或停止的理由。
怎样写出自己的条件
从"任务完成的标志是……"这句话开始。如果后半句是"一切都变好了"这种说法,智能体就得自己猜您到底想说什么。把主观评价换成能看见的行为:"清空搜索之后,所有卡片重新可见"。写几个检查点也可以,只要它们描述的是同一个结果,比如修好的搜索。
- 说清结果。"修好搜索"只给了方向,而"按名称能搜到卡片,清空后返回完整列表"才划定了终点。
- 写明验证方式。写清楚打开什么、输入什么、期待什么样的回应。要求在对话里展示实际结果,好让评估者能读到它。
- 限定改动范围。如果只需要改样式,就明说。如果智能体是在验证一个已经写好的游戏,就禁止为了凑结果而改它的代码。
- 预设不成功时的停止方式。比如,经过三次带验证的修复尝试之后,智能体应该描述剩下的问题。这是工作的边界,不是又一种宣布任务完成的办法。
启动之前,先确认智能体能访问所需的页面、文件和工具。条件里的地址和元素名称都应该对应您自己的项目。文字里的尝试次数上限能帮您管住工作节奏,但它不是硬性的时间或花费限制。
注意一下回退的箭头。如果结果还没达成,智能体会得到下一轮工作,可以先修问题,再重新验证一遍。但走出这个循环不总是意味着成功:障碍或事先约定的上限,需要单独说明。这张图展示的是条件的运行逻辑;尝试次数的上限,智能体和评估者都是通过对话来追踪的。
面向您自己任务的三个例子
下面是针对几个已有页面的示范条件。启动之前,把地址、数据和期望行为换成您自己的。这些是条件的例子,不是完成工作的报告。
修好目录里的搜索
假设本地目录里有 "Atlas" 和 "Orion" 两张卡片,但清空搜索之后列表却是空的。"把搜索改好"这种要求,完全没有说清楚需要的行为。这里更有用的是三个具体场景:
/goal 修好 http://localhost:3000/catalog 上目录里的搜索。
完成的标准是:搜索 "atlas" 能找到卡片 "Atlas",
搜索 "zzzz-no-match" 显示"未找到任何结果",
清空输入框会返回全部原始卡片。
通过 Playwright 验证全部三种情况,并在对话里展示
搜索词、期望结果和实际看到的卡片
或提示信息。最后一次修改之后,把三项验证全部重新做一遍。
只允许修改搜索逻辑,不要改动目录数据和外观。
如果经过三次带验证的修复尝试问题仍然存在,
停下来,列出还有哪些场景没有通过。这里"全部"这个词很关键:只找到一张卡片,并不能证明搜索已经修好了。目录数据是固定的,所以不能靠改卡片名字来凑出想要的结果。而最后一次修改之后要重新验证全部三项,是因为修好清空功能也可能顺带把搜索本身弄坏。
只验证表单,不做修改
目标的结果也可以是一份观察清单。假设需要验证一个已经写好的注册表单,而修复的事您想另外讨论。这时候,即便发现了错误,目标也应该在验证完所有场景之后就算完成。
/goal 通过 Playwright 验证 http://localhost:3000/signup
上的表单,不要修改项目文件。
验证两种情况,每种都从空表单开始:
1. 不填任何字段,点击"注册"。
期望出现必填字段的提示,注册不会发生。
2. 在 email 字段输入 "alex",其余必填字段填入
合法的示例值,然后点击按钮。
期望出现 email 格式错误,注册不会发生。
完成的标准是对话里对两种情况都有报告:
输入了什么、期望是什么、页面实际显示了什么、是否有出入。
不要用合法的 email 提交表单,也不要创建账号。
如果页面或元素打不开,停止验证,
说明哪种情况没有验证完,以及原因。注意这里的区别:"验证表单"和"让表单能用"是两件不同的事。这里,只要智能体确实验证了两种情况并展示了结果,表单本身的错误并不妨碍完成报告。但打不开的页面会妨碍:靠猜测代替观察,无法结束目标。
让页面适配手机
"把手机端做得好看点"这种说法留给智能体的自由度太大了。选定具体的页面、窗口尺寸,以及必须保持可用的元素。
/goal 修好 http://localhost:3000 页面的样式,
适配 390x844 和 1440x900 两种窗口尺寸。
完成的标准是:两种尺寸下都没有横向滚动条,
标题和"开始"按钮完全可见、不互相遮挡,
点击"开始"仍然能打开游戏区域。
最后一次修改之后,通过 Playwright 验证两种尺寸。
展示截图、点击的结果,以及测量到的
页面根元素的 scrollWidth 和 clientWidth:
scrollWidth 不应超过 clientWidth。
只允许修改 CSS。不要隐藏内容或裁剪它
来换取没有滚动条。保留游戏的文字和行为。
如果经过三次带验证的修改尝试条件仍未满足,
停下来,展示剩下的差异。这里的尺寸指浏览器窗口的宽度和高度,单位是像素。scrollWidth 表示的是内容的宽度,包括超出可视区域的部分,clientWidth 是可用的宽度。两者对比能帮您发现内容溢出,但单靠它并不能证明文字没被裁剪、按钮依然可点。所以条件里还加了截图和点击这一项。两种尺寸同样不能证明所有设备上都好用 - 它们只是划定了这一次任务的范围。
让智能体替您准备 goal
条件不必从零开始写。在 Claude Code Best Practice 这份汇总里,收录了社区的两条建议:让智能体根据项目上下文提出候选目标,以及让它读完对话记录和文件之后自己写出条件。下面是把这两个思路用到一个小任务上的做法。
如果您还没想清楚该把什么交给一次长时间的工作,可以先发一条普通消息,不带 /goal:
读一下当前的对话记录和与这个任务相关的项目文件。
提出三个有价值的目标,每一个都要能落到可验证的
结果上。为每一个说明结果、验证方式、
改动的边界,以及现在还缺什么才能启动。
以我们已经商定的内容为准。目标先不要启动,
文件先不要改动。如果意图不清楚,就提问。从中选一个目标。工作时间长本身并不是优点:修好一个搜索功能,比几个小时说不清楚的"改进"更有价值。如果任务已经清楚了,可以跳过上面这一步,直接让它写条件草稿:
根据我们的对话记录、项目文件和文档,
为选定的任务写出 /goal 的文本。现在什么都不要执行。
写明可观察的结果、怎样验证它,以及要在对话里展示什么。
加上改动的边界,和没有成功时的停止条件。
不要编造地址、命令和就绪标志:
去项目里找到它们,或者向我确认。
返回一份完整的文本,另外单独列出还不清楚的地方。发送草稿之前先核对一下意思。结果是不是您想要的那个?提到的页面真的存在吗?智能体是不是顺带允许了改数据或者加功能?如果最后还留着不清楚的地方,先把它们弄清楚。真正该启动的是已经确认过的文本:命令 /goal 一旦发出就立刻开始工作。
查看状态,随时停下工作
目标激活期间,可以随时跟踪它的进度:不带参数的命令 /goal 会展示当前状态 - 条件本身、已经过去多少时间和多少轮,以及评估者返回的最近一次理由。
提前停掉目标也可以 - 用命令 /goal clear。哪怕棋局还没结束,它也会把条件撤掉。
这里已确认的结果只有一种:条件已经达成,并且能从评估者在对话里的回应看出来。用命令 /goal clear 撤掉目标也是一个明确的结局,但那是您决定停下来,而不是工作已经完成的证据。目标自己停下来的其他方式,都不能确认结果。
如果评估者判定条件根本无法达成,或者条件里写的上限被触发了 - 目标同样会自己停下来,不需要您下命令。错误的情况要复杂一些:只有遇到需要手动解决的错误(例如额度用完了),目标才会自己撤掉。临时故障之后,目标仍然保持激活,等待重试或您的消息;如果智能体连续几轮都没有进展,Claude Code 会把控制权交还给您,同样不会撤掉目标。这些都和"棋局已经下完"不是一回事:上面要么是评估者确认了停止,要么是您自己决定撤掉目标,而这里两者都不是。不要为了凑一个看起来顺利的状态,事后放宽条件:如果工作卡住了,更明智的做法是用命令 /goal clear 撤掉目标,直接说明卡在了哪里,而不是把条件改到刚好能通过为止。
评估模型能拿到什么
/goal 的评估者看到的不是页面本身,而是对话:智能体写下的内容,以及它从浏览器观察结果里复制过来的内容。评估者自己没有工具 - 它不会重新打开页面,也不会自己执行命令,只能读已经积累下来的文字。
由此能得到一个简单的推论。智能体说一句"棋局结束了",这句话本身对评估者和对您都没有任何证明力。真正有证明力的,是智能体确实从页面上读到的结果 - 也就是 data-game-over 的那个值。前者是一份自述,后者是一次观察,两者是不同的东西,哪怕听起来很像。
也值得了解这个标志本身的局限。data-game-over 传达的是某一个具体应用的状态 - 页面作者放进去的东西。它并不能说明整局棋是不是完全按国际象棋规则下完、没有出过一次错。就像没有横向滚动,也不能证明按钮真的能点。选标志的时候,要对准您真正想要的那个结果,不要给它附加多余的含义。
这对日常工作意味着什么
对自己的任务,也可以走同样这条简短的路线:
- 明确的结果和可用的验证方式;
- 由命令
/goal设定、带有改动边界和停止条件的目标; - 随时可以查看的状态 - 不带参数的
/goal; - 有观察结果和验证结果支撑的结论;
- 如果工作卡住或者条件本身不合适,就用命令
/goal clear手动撤掉目标。
让工作自动继续下去,和决定接下来该做什么,是两件不同的事。前者可以在一次会话内交给 /goal。后者 - 判断到底得到了什么、撤掉没完成的目标、设定下一个任务 - 始终还是要靠您自己。
评估者的确切行为和状态的展示格式,可能随 Claude Code 版本略有不同。但根据对话而不是直接访问页面来评估的这个基本原则不会变。
GO TO FULL VERSION