Prompt 不是咒语,而是上下文设计

很多 Prompt 教程会给出一长串模板,比如“你是某某专家”“一步一步思考”“如果你做得好我会奖励你”。这些写法有时会改善输出,但如果只盯着措辞,很容易走偏。

在真实应用里,Prompt 的核心不是咒语,而是上下文设计。

上下文设计要回答几个工程问题:模型需要知道什么?哪些信息最重要?哪些信息不能出现?用户当前处在什么任务状态?输出会被谁消费?失败时怎么兜底?

为什么说 Prompt 是上下文设计

LLM 每次生成都依赖你提交的上下文。它不会天然知道你的产品状态、用户权限、业务规则、页面数据和历史操作。

如果一个客服助手回答错了,不一定是模型“不会客服”。可能是你没有给它订单状态、售后规则、用户所在国家、退款期限,或者没有告诉它哪些问题必须转人工。

如果一个代码助手改错了组件,也不一定是模型“不懂 React”。可能是你只贴了一个组件,没有贴类型定义、调用方、设计约束和报错信息。

Prompt 设计的本质,就是把一次任务所需的上下文组织成模型能理解、能遵守、能输出给下游系统使用的形式。

前端类比:页面状态决定组件行为

在前端里,同一个组件会因为上下文不同而表现不同。

<CheckoutButton
  disabled={!cart.items.length}
  loading={isSubmitting}
  userRole={currentUser.role}
  region={currentUser.region}
/>

按钮能不能点击,不只取决于按钮文字,还取决于购物车、提交状态、用户角色和地区规则。

LLM 也是一样。用户说“帮我写一个回复”,这个请求本身不够。模型至少需要知道:

  • 回复给谁。
  • 当前沟通场景是什么。
  • 语气应该正式还是轻松。
  • 是否有必须提到的信息。
  • 是否有不能承诺的内容。
  • 输出是给人直接看,还是给系统继续处理。

Prompt 不是把一句命令写得更有气势,而是补齐这些上下文。

上下文的五个层次

在工程实践中,可以把 Prompt 上下文分成五层。

第一层是系统边界。它定义模型在产品里扮演什么角色,能做什么,不能做什么。

第二层是任务目标。它说明这次请求要完成的具体工作。

第三层是业务知识。它包括规则、术语、数据、文档片段、权限和当前页面状态。

第四层是用户输入。它是用户这次真正提出的问题或意图。

第五层是输出协议。它规定输出格式、字段、语言、长度、是否需要引用来源。

一个稳定的 Prompt 往往不是靠某个句子,而是靠这五层信息的顺序和边界。

一个反例:上下文缺失

假设我们要做一个前端错误解释助手。最粗糙的 Prompt 可能是:

解释这个错误:Cannot read properties of undefined (reading 'map')

模型可以给出通用解释,但它不知道错误发生在哪个组件,也不知道数据来自哪里,更不知道项目约定。

更好的 Prompt 会补齐上下文:

你是一个帮助前端工程师定位问题的助手。

任务:
解释下面的 React 运行时错误,并给出排查步骤。

项目上下文:
- 使用 React + TypeScript。
- 接口数据由 React Query 获取。
- 列表组件约定接收数组,不能接收 null。

错误信息:
Cannot read properties of undefined (reading 'map')

相关代码:
function ProjectList({ projects }: { projects?: Project[] }) {
  return projects.map((project) => <div key={project.id}>{project.name}</div>);
}

输出要求:
- 先说明最可能原因。
- 再给出最小修改方案。
- 最后给出如何避免同类问题。

这个 Prompt 并没有使用神秘技巧,只是把必要上下文交代清楚。

上下文不是越多越好

上下文设计的难点之一,是控制信息密度。

很多人遇到模型答不好,会继续往 Prompt 里塞内容。结果 Prompt 变成一锅材料:产品介绍、接口文档、用户历史、调试日志、旧规则、新规则、临时说明全部混在一起。

这会产生几个问题。

第一,成本增加。更多 token 意味着更高价格和更长延迟。

第二,噪声增加。模型可能关注到不重要的信息,忽略真正关键的约束。

第三,冲突增加。旧规则和新规则同时出现,模型可能无法判断优先级。

第四,维护困难。没人知道某一段 Prompt 到底还需不需要。

好的上下文设计不是“尽量多”,而是“刚好够”。

给上下文建立优先级

真实产品里的上下文经常会冲突。例如系统规则要求“不能泄露内部字段”,用户却要求“把完整订单对象打印出来”。这时模型需要知道优先级。

可以在 Prompt 中明确分层:

优先级规则:
1. 安全和权限规则优先于用户请求。
2. 业务规则优先于表达风格。
3. 输出格式优先于补充解释。
4. 如果用户请求和权限冲突,说明无法处理,并给出可替代操作。

这类似前端里的状态优先级。比如 loading 状态下按钮应该禁用,即使表单校验通过也不能提交。Prompt 也需要这样的规则。

把页面状态变成 Prompt 上下文

前端接入 LLM 时,常见场景是把当前页面状态传给模型。例如一个项目管理工具里,用户点击“帮我总结当前迭代风险”。

不要把整个前端 store 原样塞给模型。更好的方式是先做上下文选择和裁剪。

type Sprint = {
  name: string;
  startDate: string;
  endDate: string;
  tasks: Array<{
    title: string;
    status: 'todo' | 'doing' | 'blocked' | 'done';
    assignee: string;
    updatedAt: string;
  }>;
};

function buildSprintContext(sprint: Sprint) {
  const blockedTasks = sprint.tasks.filter((task) => task.status === 'blocked');
  const doingTasks = sprint.tasks.filter((task) => task.status === 'doing');

  return {
    sprintName: sprint.name,
    dateRange: `${sprint.startDate}${sprint.endDate}`,
    blockedTasks: blockedTasks.map((task) => ({
      title: task.title,
      assignee: task.assignee,
      updatedAt: task.updatedAt,
    })),
    doingTaskCount: doingTasks.length,
    doneTaskCount: sprint.tasks.filter((task) => task.status === 'done').length,
  };
}

然后把裁剪后的上下文放进 Prompt:

const context = buildSprintContext(sprint);

const prompt = `
你是一个帮助前端团队做迭代复盘的项目助手。

任务:
根据当前迭代状态,总结主要风险和建议动作。

当前迭代上下文:
${JSON.stringify(context, null, 2)}

输出要求:
- 输出 3 条以内风险。
- 每条风险包含:风险、依据、建议动作。
- 不要编造上下文中没有出现的任务。
`.trim();

这里的关键不是 Prompt 写得多漂亮,而是先把产品状态变成适合模型消费的上下文。

上下文设计和信息架构

Prompt 上下文其实是一种信息架构。你需要决定哪些内容进入模型,按什么顺序进入,如何标注来源,如何处理冲突。

一个常见顺序是:

  1. 角色和边界。
  2. 任务目标。
  3. 业务规则。
  4. 输入数据。
  5. 输出格式。

这个顺序有助于模型先理解“自己在什么系统里”,再理解“这次要做什么”。

如果任务需要引用资料,最好给资料加标签:

[产品规则]
试用用户不能导出完整报表,只能导出最近 7 天摘要。

[当前用户]
计划类型:trial
所在页面:报表详情页

[用户请求]
帮我导出这个月的完整报表。

标签能降低上下文混乱,让模型更容易区分规则、数据和请求。

常见工程实践

第一,先定义任务类型,再写 Prompt。

不要把所有 AI 功能都塞进一个通用助手。总结、改写、分类、代码解释、JSON 抽取是不同任务,应有不同 Prompt 结构。

第二,先裁剪上下文,再提交模型。

前端状态、接口数据和文档内容都应该经过筛选。只传和任务有关的内容。

第三,把不可违反的规则放在稳定层。

权限、安全、隐私、品牌边界等规则不要依赖用户输入临时拼接,应该由系统或服务端统一注入。

第四,输出要面向下游。

如果结果给用户阅读,就优化可读性。如果结果给程序解析,就优先使用 JSON schema、字段说明和失败策略。

第五,记录 Prompt 和输出。

调试 LLM 应用时,需要知道当时到底给了模型什么上下文。没有日志,就很难复盘问题。

常见误区

第一个误区:以为换个“专家角色”就能解决问题。

角色有用,但角色不能替代业务数据和任务边界。模型不知道订单规则时,扮演“资深客服专家”也可能答错。

第二个误区:把所有历史消息都传进去。

多轮对话不是无限追加历史。旧消息会增加成本,也可能干扰当前任务。需要摘要、裁剪和状态化。

第三个误区:把前端状态原样传给模型。

完整 store 里可能有敏感字段、无关字段和内部实现细节。应该构造专门的模型上下文对象。

第四个误区:只优化 Prompt 文案,不优化数据入口。

如果用户输入本身不清楚,页面没有收集必要信息,再好的 Prompt 也只能猜。

第五个误区:没有处理冲突。

业务规则、用户要求和输出格式可能互相冲突。Prompt 里应该明确优先级和拒绝策略。

小结

Prompt 不是咒语,而是上下文设计。它的价值不在于某些神秘句式,而在于让模型在正确的信息、边界和任务协议中工作。

对前端工程师来说,最重要的转变是:不要只问“这句 Prompt 怎么写”,而要问“这次模型请求需要哪些状态、数据、规则和输出约束”。

当你开始像设计页面状态、接口参数和组件边界一样设计 Prompt,LLM 功能才会从演示效果走向可维护的产品能力。