分类
更多信息

如何构建你的智能体:11 项提示工程技术,打造更优 AI 智能体

引言

提示工程(Prompt Engineering)已成为现代软件开发中杠杆率最高的技能之一。你提供给智能体(Agent)的提示(Prompt),塑造了它的规划方式、工具使用方式,并决定了它是构建还是破坏你的工作流程。细微的改动——比如增加一行上下文说明、澄清一个约束条件、调整指令顺序——往往能在准确性和可靠性方面带来超乎想象的提升。本文提炼了我们在 Augment Code 公司经过实战检验的策略,用于构建行为如同训练有素的团队成员、而非产生幻觉的“氛围编码工具”(hallucinating vibe coding tools)的自主智能体。

文中的示例侧重于编码智能体,但所述技术普遍适用。

什么是提示工程?

智能体的提示包含所有作为输入提供给模型的内容。这包括多种组件:

  • 系统提示(System Prompt)
  • 工具定义(Tool Definitions)
  • 工具输出(Tool Outputs)
  • 用户指令(User Instructions)
  • 模型自身在先前轮次中的输出(The model’s own outputs from previous turns)

提示工程是一门艺术,旨在通过提供更好的提示来提升模型在特定任务上的表现。提示的所有部分都可能通过提示工程得到改进。例如:

  • 系统提示可以包含通用指令,引导模型采用不同的响应风格或自主级别。
  • 工具定义可以向模型解释在何种情况下应使用或不应使用某个工具。
  • 工具输出可以告知模型错误情况。
  • 用户指令可以在呈现给模型之前进行重写(提示增强,Prompt Enhancement)。
  • 先前的模型输出可以被压缩或截断以节省 token,从而使更长的对话历史能放入上下文窗口。截断方式对质量至关重要。

如何理解模型

模型具有(人工)智能。提示模型更接近于与人交谈,而非编程计算机。模型构建的世界观完全基于提示中的内容。这种世界观越完整、越一致,模型的结果就越好。

模型向我们呈现一个自然语言界面,这不同于开发者使用的编程语言界面。将语言模型(LM)界面视为一个独立但真实的抽象层是很有用的。这个界面可用于呈现理想路径的结果,也可用于发出错误警报、通知变更等——本质上就是与模型进行沟通。

示例:

如果模型错误地调用了工具,请不要在你的智能体代码中抛出异常。相反,返回一个工具结果,解释错误所在:工具调用缺少必需参数 xyz。模型将恢复并重试。

如何评估提示

通常很难自动评估提示,除非目标是让模型执行非常具体的任务。尝试构思能多方位测试提示的场景,并努力找出提示更改可能导致性能退步(regressions)的示例。关于这些评估原则的实际应用,可参考这些相同的提示工程技术如何助力 Augment Code 在 SWE-bench 上获得开源项目第一名的具体案例。

提示工程技巧

遵循这些技巧,你将解锁通用人工智能(AGI)。

  1. 优先关注上下文
    提示工程中最重要的因素是为模型提供尽可能最佳的上下文:即用户提供的信息(而非我们提供的提示文本)。这是模型执行任务的主要信号依据。
    当前模型擅长在大型提示中找出相关的有用上下文信息,因此当有疑问时,倾向于提供更多信息,如果这能增加上下文包含有用相关信息的可能性。
    关于一个提示应问的第一个问题是——它是否包含所有相关信息?可能性有多大?回答这个问题并非总是易事。示例:在为模型提供长命令输出而需进行截断时,截断方法很重要。通常,截断长文本涉及截断后缀。然而,对于命令输出,有用的信息更可能出现在前缀和后缀,而非中间部分。例如,崩溃的堆栈跟踪通常出现在后缀中。因此,为了最大化模型获得最相关上下文的可能性,截断命令输出的中间部分比截断后缀更好。
  2. 呈现完整的世界图景
    通过解释模型所处的运行环境,并提供可能有助于其良好表现的细节,帮助模型进入正确状态。例如,如果你希望模型扮演软件开发者角色,就在系统提示中告知它。向其解释它可以访问哪些资源,以及应如何使用它们。示例:在 Augment 智能体开发的早期,系统提示中引入了以下两行,显著提升了其性能:
    你是一名 AI 助手,可以访问开发者的代码库。
    你可以使用提供的工具读取和写入代码库。
  3. 保持提示组件间的一致性
    确保提示的所有组件(系统提示、工具定义等)以及底层工具定义保持一致。示例:
    • 系统提示包含一行:当前目录是 $CWD
    • execute_command 工具(允许智能体执行 shell 命令)包含一个可选的 cwd 参数。一致性意味着该参数的默认值应为 $CWD。这可以在工具定义中指明。如果没有指明,模型很可能会默认如此。
    • read_file 工具接受一个 path 参数(要读取的文件路径)。如果提供的是相对路径,应解释为相对于 $CWD
    💡注意:避免让模型感到意外。 模型很容易被混淆。如果模型很可能期望某个工具调用产生特定结果,请确保要么提供该结果,要么在工具结果中解释偏差。例如,如果工具定义承诺返回特定长度的输出,那么要么返回该长度的输出,要么在答案前加上说明:请求长度为 N 的输出,但因...原因,现返回长度为 K 的输出。示例:如果提示包含可能在会话期间改变的状态(例如当前时间),不要将其包含在系统提示或工具定义中。
    相反,在下一个用户消息中告知模型该变更。这保持了提示的内部一致性:模型可以看到每个轮次的状态是什么。
  4. 使模型与用户视角对齐
    考虑用户的视角,并尝试使模型与该视角对齐。示例: 当用户在集成开发环境(IDE)中工作时,可以向模型呈现 IDE 状态的详细视图,聚焦于用户最可能关心或在其指令中引用的元素。有助于对齐模型的潜在信息示例:
    • 用户的当前时间和时区
    • 用户的当前位置
    • 用户的活动历史
    包含 IDE 状态的基本系统提示示例:用户在 IDE 中工作。当前 IDE 状态:
    文件 foo.py 已打开。
    IDE 类型为 VSCode。描述 IDE 状态的更详细系统提示示例:用户在 IDE 中工作。当前 IDE 状态:
    IDE 类型为 VSCode。
    当前打开的文件是 foo.py。
    屏幕上可见第 134 行至第 179 行。
    以下是当前可见文本,光标位置由 <CURSOR> 标示:python134 def bar(): 135 print(“hell<CURSOR>o”) … 179 # TODO 实现此功能没有选中的文本。
    有 14 个打开的标签页。按最近访问顺序排列如下:
    foo.py
    bar.py ...
    xyz.py💡注意: 这并非暗示其中一个提示必然优于另一个。详细提示的潜在缺点是模型可能开始过度关注 IDE 状态,而这并不总是用户意图的最佳信号。
  5. 力求详尽
    详尽的提示对模型有益。无需担心提示长度。当前的上下文长度很长且会持续增加:通过编写更长的提示,你无法显著影响提示的 token 预算。一个成功且详尽的提示示例,用于教导模型如何使用 Graphite(一种版本控制工具):
    ## 使用 Graphite 进行版本控制
    我们在 git 之上使用 Graphite 进行版本控制。Graphite 帮助管理 git 分支和 PR(Pull Request)。
    Graphite 维护 PR 栈(stacks of PRs):对某个 PR 的更改会自动触发栈中更高层 PR 的变基(rebase),
    节省大量手动操作。以下各节描述了如何使用 Graphite 和 GitHub 执行常见的版本控制工作流。
    如果用户要求你执行此类工作流,请遵循以下指南。### 禁止操作
    不要使用 git commitgit pullgit push。这些命令都被以下以 gt 开头的 Graphite 命令所取代。### 创建 PR(及分支)
    要创建 PR,请执行以下操作:
    - 使用 git status 查看哪些文件被更改,哪些是新增文件
    - 使用 git add 暂存(stage)相关文件
    - 使用 gt create USERNAME-BRANCHNAME -m PRDESCRIPTION 创建分支,其中:
    USERNAME 可获取(参见其他说明)
    BRANCHNAME 是你想出的一个好分支名
    PRDESCRIPTION 是你想出的一个好 PR 描述
    - 这可能会因预提交(pre-commit)问题而失败。有时预提交会自行修复问题。检查 git status 看是否有文件被修改。
    如果有,用git add添加它们。如果没有,请自行修复问题并用git add添加。然后重复gt create 命令尝试再次创建 PR。
    - 运行 gt submit 在 GitHub 上创建 PR(如果只是创建分支,则跳过此步骤)。
    - 如果 gh (GitHub CLI) 可用,请使用它设置 PR 描述。
    注意:运行 gt create 前别忘了添加文件,否则你会卡住!### 更新 PR
    要更新 PR,请执行以下操作:
    - 使用 git status 查看哪些文件被更改,哪些是新增文件
    - 使用 git add 暂存相关文件
    - 使用 gt modify 提交更改(无需提供消息)
    - 这可能会因预提交问题而失败。处理方式同上(创建 PR 步骤)。
    - 使用 gt submit 推送更改
    - 如果你还需要更新 PR 描述,请使用 gh(如果未安装,请告知用户但不要强求更新 PR 描述)### 从主分支(main)拉取更改
    要使本地仓库与主分支同步,请执行以下操作:
    - 使用 git status 确保工作目录是干净的
    - 使用 gt sync 拉取更改并进行变基
    - 遵循指令。如果出现冲突,询问用户是否希望解决。如果是,则遵循 gt sync 显示的指令。### 其他 Graphite 命令
    要查找其他命令,请运行 gt –help
  6. 避免过度拟合特定示例
    模型是强大的模式匹配器,会紧紧抓住提示中的细节。提供具体操作示例是一把双刃剑:它是引导模型走向正确方向的简单方法,但也存在模型过度拟合这些示例而在其他情况下性能下降的风险。务必进行实验,并包含可能暴露过度拟合问题的示例。
    相比之下,告诉模型不要做什么是安全的(尽管并非总是有效)。
  7. 考虑工具调用的局限性
    工具调用在多个方面受到限制:
    • 如果模型在训练中接触过类似工具,或者指令与工具之间的联系清晰,模型通常能选择正确的工具。但在许多情况下,即使有最好的提示,它们也可能无法选择正确的工具。
    • 如果提供多个功能相似的工具,不能期望模型在任何给定情况下都能选择正确的工具。例如,当面对一个简单工具和一个复杂工具都能完成类似任务时,Claude 通常会选择简单工具。
    • 模型经常以错误的方式调用工具,违反工具定义中的契约:参数类型可能错误、参数范围可能错误、可能缺少必需参数等。最佳实践是验证输入,并在失败时返回一个解释错误的工具输出。模型通常能恢复。
    示例:给模型一个 edit_file 工具来编辑文件的某个区域。
    给模型一个剪贴板工具,模型可以剪切、复制和粘贴大量代码。告诉模型在移动大量代码时使用此工具。
    指示模型将类 Foo 从 foo.py 移动到 bar.py。Sonnet 3.5 通常仍会选择使用 edit_file
  8. 威胁和激发同理心有时确实奏效
    告诉模型诸如“必须正确执行此操作,否则你将面临财务损失”之类的话,有时确实有助于提高性能。友好地请求或“大声呵斥”模型则很少有效。
  9. 注意提示缓存
    尽可能构建你的提示,使其能在会话期间被追加内容,以避免提示缓存失效(invalidate)。示例:如果提示包含可能在会话期间改变的状态(例如当前时间),不要将其包含在系统提示或工具定义中,因为一旦它们改变,大部分提示缓存将失效。
    相反,在下一个用户消息中告知模型该变更。
  10. 模型更关注提示开头或(尤其是)结尾的信息
    模型对指令的关注程度似乎是:用户消息 → 输入开头 → 中间某处。如果某些内容很重要,考虑将其添加到用户消息中。(这是一个快照,优先级可能会随着模型训练的演变而改变。)
  11. 警惕提示瓶颈
    通过直接提示所能实现的提升存在极限。提示工程会进入收益递减区域,需要引入其他技术。

结论

掌握提示工程,与其说是技巧,不如说是严谨的沟通之道:为智能体提供完整、一致的上下文;像对待不可信的同事一样验证其行为;并通过经验不断迭代。当你将提示视为代码库的一部分——进行版本控制、审查和测试时——你就能解锁那些能放大你影响力、而非倍增你烦恼的智能体。

阅读原文:https://www.augmentcode.com/blog/how-to-build-your-agent-11-prompting-techniques-for-better-ai-agents