Separate the Goal from the Constraints Instead of a One-Liner Requirement
A one-line requirement is behind most failures: "optimize this code" invites the tool to improvise. The mental model is to split the prompt into goal (the final behavior to achieve) + constraints (invariants, what is off-limits, which tests must pass) + acceptance (what counts as done).
Constraints are your main hallucination defense: state clearly "introduce no new dependencies", "only touch src/foo.ts", "keep the existing function signature" and "must not break existing tests". The more concrete the constraints, the less the tool overreaches with refactors.
Acceptance criteria matter too — "pnpm typecheck returns 0, eslint clean, all related tests green" — giving the agent a checkable endpoint rather than "looks fixed".
## 目标
把 orders 列表改为服务端分页,保留现有 API 返回结构。
## 约束
- 只改 server/ 与对应的接口层文件,不碰前端展示
- 不引入新依赖;复用已有 SQLhelper
- 保持现有函数签名与错误码
## 验收
- pnpm typecheck 返回 0
- 相关接口测试全绿Feed Enough Context, but Not the Whole Repo
An agent can only judge on what you feed it; too little context makes it start guessing. But pasting thousands of lines washes out attention and raises hallucination. The trick is "index plus key snippets": tell it the project layout, the data-flow entry points, and the path and rough responsibility of the file you want changed.
Fix a "project quick-view", such as a one-page CONTEXT.md covering: directory purpose, build command, test entry, and where common type definitions live. Refer to it on every task; it measurably cuts the odds of the agent landing in the wrong file.
If domain knowledge matters (cache strategy, auth flow), pull it out explicitly instead of letting the model search for it — search-style recall is more uncontrollable.
# CONTEXT.md(团队维护的单页速览)
## 目录
- src/api/ 请求层,签服务用 http-client
- src/domain/ 业务规则,含 order 状态机
## 命令
- test: pnpm vitest run
- typecheck: pnpm run typecheck
下发任务时开头引用:
"先读 CONTEXT.md 了解结构,再实现 X。"Constrain Behavior with Test-First Instructions, Not Verbal Emphasis
To make agent-written code stand up, the most effective instruction is not "please write carefully" but baking verification in: "first write unit tests for this function, then implement until the tests pass". Test-first constrains both behavioral boundaries and acceptance criteria at once.
Give the exact test entry and the expected assertion style. In a Nuxt/Vite project using vitest, say organize with describe/it and prefer expect(..).toEqual. That keeps the produced tests stylistically uniform and easier to maintain later.
For large tasks, have the agent plan first: an implementation plan (which files to create/modify, dependency order), and only execute after you approve. The plan is an alignment step in advance, far cheaper than reworking mid-flight.
"先为 src/domain/discount.ts 的 applyDiscount 写单测
(describe/it,断言用 toEqual),当前先跑出失败;
再实现到全绿。最后贴出测试输出。"Give a Wrong-versus-Fixed Contrast to Train Accurately Rather Than by Trial
When you want to steer it away from a common code smell, abstract phrasing transfers poorly while a concrete counter-example lands. Feed both a wrong and a fixed snippet, clearly marking which is bad and why; the agent then tells the boundary apart better.
This also works for style constraints: give it a real sample of "this project’s accepted practice" as an anchor to follow, rather than the vague "keep the style consistent".
An annotated example usually beats a whole page of abstract rules. The reason is an agent reconstructs your preferences from concrete instances, rather than guessing at them.
# 错误示范:在回调里同步读环境变量,导致测试切不到值
const onReady = () => doLogic(process.env.FLAG === "1");
# 修复示范:把 flag 作为参数注入,便于测试与替换
const onReady = (flag: boolean) => doLogic(flag);
# 指令:"避免示范 A 的写法,采用示范 B 的模式。"Use Acceptance Self-Check and Minimal Change as the Delivery Gate
Delivery is not about lines changed but about passing acceptance. A standard delivery instruction should include the commands the agent runs itself — typecheck, lint, test — and demand it paste the raw output verbatim, so it cannot self-soothe with "should be fine".
Also hold the minimal-change line hard: if the diff shows unrelated refactors, suspiciously deleted code, or broad formatting reshuffles, have it revert and redo with tighter scope. Requiring a one-sentence explanation per change naturally flushes out motion-for-motion’s-sake.
The final gate is a change note/release note: ask it to add one line on "why changed, which modules affected, whether docs need updating". This thread runs through the whole task, ensuring it is not an isolated edit.
"完成后按验收跑一遍:pnpm typecheck、pnpm lint、pnpm test,
贴原始输出。逐条列改动 + 一句原因;
确认 diff 最小化,剔除无关重构。
补一行:影响模块 + 是否需要更新文档。"