1. 为什么提示词是AI Agent的第一道门槛
很多人刚接触AI Agent的时候,脑子里想的都是“我要搭一个能自动帮我干活的智能体”,然后一头扎进框架选型、工具调用、记忆管理这些听起来很硬核的环节。结果跑起来发现,Agent确实能调工具了,但调得驴唇不对马嘴;确实能多轮对话了,但三轮之后就开始胡言乱语。问题出在哪?十有八九,出在提示词上。
我刚开始做Agent的时候也踩过这个坑。当时用了一个挺流行的Agent框架,把工具描述写得自认为很清楚,系统提示词也洋洋洒洒写了一大段,结果Agent要么该调工具的时候不调,要么不该调的时候乱调,要么调了之后拿到结果不知道怎么处理。后来我把整个链路拆开逐段排查,发现根因几乎全在提示词的设计上——工具描述有歧义、系统提示词缺少边界约束、输出格式没有明确约定。改完提示词之后,同样的框架、同样的工具、同样的模型,成功率直接从六成出头拉到了九成以上。
这就是为什么我把提示词和提示工程放在这个系列的第二篇来讲。第一篇我们聊了AI Agent的整体架构和核心组件,但架构是骨架,提示词才是神经。没有好的提示词,再漂亮的架构也跑不出预期的效果。这一篇我会把提示工程在Agent场景下的核心方法论拆开讲透,从最基础的角色设定到复杂的多步推理链,从工具描述的设计到输出格式的约束,全部配上可以直接抄作业的模板和我在实际项目中踩过的坑。
不管你是刚入门想搞清楚提示词到底该怎么写,还是已经搭过几个Agent但效果总差那么一口气,这篇内容应该都能帮你把思路理顺。我尽量不堆术语,用实际案例和对比来把每个设计决策背后的逻辑讲清楚。
2. 提示工程的核心思路与Agent场景的特殊性
2.1 从“聊天”到“干活”:提示词的角色转变
普通对话场景下的提示词和Agent场景下的提示词,本质上是两种东西。你跟聊天机器人说“帮我写一首诗”,它写出来就行,写得好不好是另一回事,但任务边界很清楚。Agent场景完全不一样——你说“帮我查一下明天北京的天气然后决定要不要带伞”,Agent需要理解意图、选择工具、调用接口、解析结果、做出判断、给出建议,这一连串动作全部由提示词来驱动。
这意味着Agent的提示词必须承担比聊天提示词多得多的职责。我习惯把Agent提示词分成四个层次来理解:
- 身份层:定义Agent是谁、具备什么能力、行为边界在哪
- 任务层:描述当前要完成什么任务、有哪些约束条件
- 工具层:说明有哪些工具可用、什么情况下该用哪个
- 格式层:约定输出的结构、字段、格式要求
这四个层次缺一不可。我见过很多新手只写了身份层和任务层,工具层就扔了一句“你可以使用以下工具”,格式层完全没写,结果Agent的输出格式每次都不一样,下游解析代码天天报错。
2.2 为什么“说清楚”比“说得多”重要
一个常见的误区是觉得提示词写得越长越好,把所有可能的情况都罗列进去。实际上,过长的提示词会带来两个问题:一是模型注意力被稀释,关键信息淹没在大量文本里;二是维护成本急剧上升,改一个地方可能影响其他部分。
我做过一个对比测试,同一个Agent任务,一个版本的系统提示词写了1200字,另一个版本精简到400字但结构更清晰,其他条件完全一致。结果精简版的工具调用准确率反而高了将近15个百分点。原因很简单:400字的版本每条指令都直击要害,模型不需要在大量文本中“找重点”。
所以提示词设计的核心原则不是“多写”,而是“写准”。每一条指令都要有明确的目的,每一个约束都要能解决一个具体的问题。写完之后逐句问自己:这句话删掉会怎样?如果删掉之后Agent的行为没有变化,那这句话就是冗余的。
2.3 Agent提示工程的三个关键差异点
跟传统的提示工程相比,Agent场景有三个特别需要注意的地方。
第一个是状态感知。Agent是多轮执行的,每一轮的状态可能不同。提示词需要让模型知道当前处于什么阶段、上一步做了什么、下一步该做什么。这就需要在提示词中动态注入上下文信息,而不是写一段静态的文本。
第二个是工具边界。Agent能调用的工具是有限的,提示词必须明确告诉模型哪些事情能做、哪些不能做。我见过一个案例,Agent被要求查询内部数据库,但提示词里没有明确说“只能查询不能修改”,结果模型自己“脑补”了一个写入操作,差点造成数据污染。
第三个是错误恢复。Agent调用工具失败是常态,提示词需要告诉模型失败之后该怎么办——是重试、换工具、还是直接告诉用户做不到。没有这层设计,模型遇到错误要么卡死,要么编造一个结果糊弄过去。
3. 核心细节解析:提示词的六个关键模块
3.1 角色设定:给Agent一个清晰的“人设”
角色设定不是写一句“你是一个有用的助手”就完事了。好的角色设定应该包含三个要素:专业背景、行为风格、能力边界。
专业背景决定了模型调用哪些知识。比如“你是一个资深数据分析师”和“你是一个客服专员”,面对同一个问题给出的回答角度完全不同。行为风格决定了输出的语气和详细程度,“简洁直接”和“耐心解释”出来的结果差异很大。能力边界则明确告诉模型什么能做、什么不能做,这是防止幻觉的第一道防线。
我常用的角色设定模板是这样的:
你是一个[专业角色],擅长[核心能力]。 你的工作方式是[行为风格]。 你只能处理[能力范围]相关的问题,对于超出范围的问题,你应该[兜底行为]。举个例子,一个电商客服Agent的角色设定:
你是一个电商平台的售后客服专员,擅长处理退换货、物流查询、订单修改等售后问题。 你的工作方式是先安抚用户情绪,再给出明确的解决方案,语气友好但不过度热情。 你只能处理售后相关的问题,对于售前咨询、商品推荐等问题,你应该引导用户联系售前客服。这个模板看起来简单,但每句话都有明确的目的。第一句让模型知道自己的知识范围,第二句控制输出风格,第三句防止模型越界回答。
3.2 任务描述:把“做什么”拆到不能再拆
任务描述最容易犯的错误是太笼统。比如“帮我处理用户的退款请求”,这句话对模型来说信息量几乎为零。什么叫“处理”?是审核?是执行?是记录?不同的理解会导致完全不同的行为。
好的任务描述应该把流程拆解到每一步都可执行的程度。我习惯用“输入-处理-输出”的框架来组织:
输入:用户提交的退款申请,包含订单号、退款原因、退款金额。 处理步骤: 1. 验证订单号是否存在且状态为“已支付” 2. 检查退款原因是否在允许退款的范围内 3. 如果退款金额超过500元,标记为“需人工审核” 4. 如果满足自动退款条件,调用退款接口 输出:返回处理结果,包含处理状态、处理说明、后续操作建议。这种拆解方式的好处是每一步都有明确的判断条件,模型不需要“猜”你的意图。而且当某一步出错时,你能快速定位是哪个环节的描述有问题。
3.3 工具描述:让模型知道“什么时候用哪个”
工具描述是Agent提示词中最容易被低估的部分。很多人写工具描述就是一句话:“查询天气的工具”。这种描述对模型来说几乎没用,因为模型不知道什么情况下该用它、输入格式是什么、返回结果怎么解读。
我总结了一个工具描述的四要素模板:
- 功能说明:这个工具是干什么的
- 使用场景:什么情况下应该调用这个工具
- 输入参数:需要提供哪些参数、格式是什么
- 返回说明:返回什么结果、如何解读
以天气查询工具为例:
工具名称:get_weather 功能说明:查询指定城市指定日期的天气信息。 使用场景:当用户询问天气相关问题时调用,包括温度、降水、风力等。 输入参数: - city(必填):城市名称,中文,如“北京” - date(必填):日期,格式为YYYY-MM-DD 返回说明:返回JSON格式的天气数据,包含temperature(温度)、condition(天气状况)、precipitation(降水量)、wind(风力)四个字段。对比一下“查询天气的工具”和上面这段描述,模型在后者面前的表现会好很多。因为每个字段都有明确的格式要求,模型不需要猜测参数该怎么传。
还有一个容易忽略的点是工具之间的优先级。当多个工具都能完成类似功能时,提示词需要明确告诉模型优先用哪个。比如同时有“精确查询工具”和“模糊搜索工具”,就要说明“当用户提供了明确的关键词时优先使用精确查询工具”。
3.4 输出格式:用Schema约束模型的“自由发挥”
Agent的输出通常需要被下游代码解析,所以格式的稳定性至关重要。我见过太多项目因为模型输出格式不稳定,导致解析代码写了一堆兼容逻辑,最后还是经常报错。
解决这个问题最有效的方式是在提示词中直接给出输出Schema,并且明确说明“必须严格按照此格式输出”。比如:
输出格式要求: { "status": "success | failed | need_review", "message": "处理结果的文字说明", "data": { "order_id": "订单号", "refund_amount": "退款金额(数字)" }, "next_action": "建议的下一步操作" }这里有几个细节需要注意。第一,枚举值要写清楚,比如status只能是success、failed、need_review三个值之一,不能是其他。第二,字段类型要明确,refund_amount是数字不是字符串。第三,对于可能为空的字段,要说明是返回null还是空字符串。
实测下来,给出明确Schema之后,格式错误率能从30%以上降到5%以下。如果配合few-shot示例,还能进一步降低。
3.5 约束条件:告诉模型“什么不能做”
约束条件是提示词中最容易被忽略但最重要的部分。模型天然倾向于“帮忙”,如果你不明确禁止某些行为,它很可能会做出你不期望的操作。
常见的约束条件包括:
- 数据约束:不能编造不存在的数据,如果查不到就如实说明
- 操作约束:不能执行删除、修改等破坏性操作,除非用户明确确认
- 范围约束:不能回答与当前任务无关的问题
- 格式约束:不能输出Schema之外的字段
写约束条件时有一个技巧:用“如果...则...”的句式比单纯说“不要...”更有效。比如“如果查询不到数据,则返回status为failed并说明原因”比“不要编造数据”更容易被模型执行。
3.6 上下文注入:让Agent“记住”之前发生了什么
Agent的多轮执行特性决定了提示词不能是静态的。每一轮都需要把之前的执行结果注入到提示词中,让模型知道当前的状态。
上下文注入的内容通常包括:
- 用户的历史消息
- 之前调用工具的结果
- 当前执行到了哪一步
- 已经尝试过哪些方案
注入的方式我推荐用结构化格式,而不是简单拼接。比如:
当前对话历史: 用户:帮我查一下订单12345的状态 Agent:已调用query_order工具,返回结果:{"status": "已发货", "logistics": "顺丰 SF123456"} 用户:那大概什么时候能到? Agent:需要调用estimate_delivery工具来估算送达时间。这种结构化的上下文比纯文本拼接更清晰,模型更容易理解当前的状态和下一步该做什么。
4. 实操过程:从零搭建一个Agent提示词体系
4.1 第一步:明确Agent的能力边界
在写任何提示词之前,先拿出一张纸,把Agent能做的事情和不能做的事情列清楚。这一步看起来简单,但实际做的时候你会发现很多模糊地带。
比如一个“会议安排助手”Agent,能做的事情包括:查询参会人空闲时间、创建会议、修改会议时间、取消会议。不能做的事情包括:代替参会人确认参会、修改其他人的日历权限、处理跨时区会议的复杂规则。
把这些边界写清楚之后,后面的提示词设计就有了明确的框架。我通常会把能力边界整理成一个表格:
| 能力类型 | 具体能力 | 限制条件 |
|---|---|---|
| 查询类 | 查询空闲时间 | 只能查询未来7天 |
| 创建类 | 创建会议 | 需要所有参会人确认 |
| 修改类 | 修改会议时间 | 只能修改自己创建的会议 |
| 删除类 | 取消会议 | 需要二次确认 |
这张表不仅是提示词的依据,也是后续测试用例的来源。
4.2 第二步:设计系统提示词的整体结构
系统提示词的结构我推荐用“分块+标记”的方式,每个模块用明确的分隔符隔开。这样模型在解析时能快速定位到不同部分,也方便后续维护。
一个完整的系统提示词结构如下:
[角色设定] 你是一个会议安排助手... [能力范围] 你可以执行以下操作: 1. 查询参会人空闲时间 2. 创建会议 ... [工具列表] 工具1:query_availability 功能:... 参数:... ... [输出格式] 所有输出必须遵循以下JSON格式: ... [约束条件] 1. 如果参会人超过5人,必须提示用户确认 2. 如果会议时间在非工作时间,必须提示用户 ... [上下文] 当前用户信息:... 当前时间:... 历史操作记录:...这种结构的好处是每个模块职责清晰,修改时不会互相影响。比如要增加一个新工具,只需要在工具列表里加一段,不用动其他部分。
4.3 第三步:编写工具描述并测试调用准确率
工具描述写完之后,不要急着集成到Agent里,先单独测试模型的工具选择准确率。方法很简单:准备一组测试用例,每个用例描述一个场景,看模型是否能选对工具、传对参数。
我常用的测试用例格式:
| 测试场景 | 期望调用的工具 | 期望参数 |
|---|---|---|
| 用户问“明天下午3点大家有空吗” | query_availability | date=明天, time=15:00 |
| 用户说“帮我约个会” | 需要追问具体时间 | 无 |
| 用户说“取消刚才的会议” | cancel_meeting | meeting_id=最近创建的会议 |
如果某个场景的调用准确率低于90%,就需要回去检查工具描述是不是有歧义。常见的歧义来源包括:工具名称太相似、使用场景描述重叠、参数说明不清晰。
4.4 第四步:用Few-shot示例校准输出格式
即使给出了详细的Schema,模型有时候还是会“自由发挥”。这时候Few-shot示例就派上用场了。在提示词中加入2-3个完整的输入输出示例,能显著提升格式的稳定性。
示例的选择有讲究。不要选太简单的,也不要选太复杂的。最好选那种“典型但有代表性”的场景。比如:
示例1: 输入:用户说“帮我查一下明天下午2点张三有没有空” 输出: { "status": "success", "action": "query_availability", "params": {"person": "张三", "date": "2026-01-15", "time": "14:00"}, "message": "正在查询张三明天下午2点的空闲情况" } 示例2: 输入:用户说“帮我约张三和李四下周三上午10点开个会” 输出: { "status": "need_confirm", "action": "create_meeting", "params": {"attendees": ["张三", "李四"], "date": "2026-01-21", "time": "10:00"}, "message": "即将创建会议,参会人:张三、李四,时间:下周三上午10点,请确认" }这两个示例覆盖了“直接执行”和“需要确认”两种典型场景,模型看完之后对输出格式的理解会清晰很多。
4.5 第五步:构建上下文管理机制
Agent执行过程中,上下文会不断增长。如果不加管理,很快就会超出模型的上下文窗口。我通常采用“滑动窗口+摘要”的策略:
- 保留最近3轮完整对话
- 更早的对话压缩成摘要
- 工具调用结果只保留关键字段
具体实现时,可以在每轮执行结束后,用一段简短的文字总结当前状态,然后把这个总结作为下一轮的上下文注入。比如:
[历史摘要] 用户最初要求安排一个会议,参会人为张三和李四,时间偏好是下周三上午。 已查询张三空闲时间:下周三上午有空。 已查询李四空闲时间:下周三上午10点之前没空,10点之后有空。 当前状态:等待用户确认是否将会议时间调整为下周三上午10点。这种摘要方式既保留了关键信息,又控制了上下文长度。
4.6 第六步:设计错误处理和兜底逻辑
Agent调用工具失败是常态,提示词必须包含完整的错误处理逻辑。我通常会在提示词中定义几种常见的错误类型和对应的处理方式:
| 错误类型 | 处理方式 | 输出示例 |
|---|---|---|
| 参数缺失 | 追问用户补充 | “请提供会议的具体时间” |
| 工具调用失败 | 重试一次,仍失败则告知用户 | “查询失败,请稍后重试” |
| 结果为空 | 如实告知,不编造 | “没有找到符合条件的记录” |
| 超出能力范围 | 引导用户 | “这个问题我处理不了,建议您联系...” |
这些处理逻辑要写进系统提示词里,让模型在遇到对应情况时知道该怎么做。
5. 常见问题与排查技巧实录
5.1 模型不调用工具,直接编造答案
这是最常见的问题之一。用户问“明天天气怎么样”,模型不调用天气查询工具,直接编了一个“明天晴,25度”的回答。
排查思路:先检查工具描述中的“使用场景”是否足够明确。如果只写了“查询天气”,模型可能觉得“我知道天气知识,不需要调用工具”。改成“当用户询问具体日期的天气时,必须调用此工具获取实时数据,不能依赖模型自身知识”之后,问题通常能解决。
如果改了描述还是不行,可以在约束条件中加一条硬性规定:“所有涉及实时数据的问题,必须先调用工具获取数据,禁止直接回答。”
5.2 工具调用参数格式错误
模型传的参数格式不对,比如日期传了“明天”而不是“2026-01-15”,或者数字传成了字符串。
这个问题的根因通常是参数说明不够具体。把参数说明改成“日期格式必须为YYYY-MM-DD,如2026-01-15”之后,大部分情况都能解决。如果还有问题,可以在Few-shot示例中专门加一个参数格式的示例。
还有一个技巧是在工具描述中加一句“参数格式错误会导致调用失败”,给模型一个“后果提示”,实测能提升参数准确率。
5.3 多轮对话后模型“忘记”了之前的约定
对话轮次多了之后,模型可能会忘记最初设定的角色或约束。比如一开始说好了“输出必须用JSON格式”,聊了五六轮之后突然变成纯文本了。
这个问题的主要原因是上下文太长,早期的系统提示词被“淹没”了。解决方法有两个:一是每轮都把关键约束重新注入到上下文中;二是控制对话轮次,超过一定轮次后主动总结并重置上下文。
我通常会在每轮的用户消息前面加一段“提醒”,把最关键的2-3条约束再强调一遍。虽然看起来有点冗余,但效果很稳定。
5.4 模型输出格式不稳定,解析代码频繁报错
即使给了Schema,模型有时候还是会多输出一个字段,或者把嵌套结构拍平了。
除了加Few-shot示例之外,还有一个实用的技巧:在Schema后面加一句“输出必须严格符合上述JSON结构,不得添加任何额外字段”。另外,在解析代码中做好容错处理,比如忽略未知字段、对缺失字段给默认值,这样即使格式有小偏差也不会导致整个流程崩溃。
5.5 工具调用陷入死循环
Agent反复调用同一个工具,每次都得到相同的结果,但就是不往下走。
这种情况通常是因为提示词中没有定义“什么时候算完成”。解决方法是加一个明确的终止条件,比如“如果连续两次调用同一工具得到相同结果,则停止调用并告知用户”。
另外,在上下文注入时,要把“已经调用过的工具和结果”明确标出来,让模型知道这个工具已经试过了。
5.6 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 | 解决方式 |
|---|---|---|---|
| 不调用工具直接回答 | 工具描述使用场景不明确 | 检查工具描述的“使用场景”部分 | 明确“必须调用”的场景 |
| 参数格式错误 | 参数说明不够具体 | 检查参数格式说明 | 给出具体格式示例 |
| 多轮后忘记约束 | 上下文过长 | 检查上下文长度 | 每轮重新注入关键约束 |
| 输出格式不稳定 | 缺少Few-shot示例 | 检查是否有格式示例 | 添加2-3个Few-shot示例 |
| 工具调用死循环 | 缺少终止条件 | 检查是否有终止逻辑 | 添加“连续相同结果则停止”规则 |
| 越权操作 | 约束条件不完整 | 检查约束条件列表 | 补充“禁止...”类约束 |
6. 进阶技巧:让提示词从“能用”到“好用”
6.1 思维链在Agent场景下的正确用法
思维链(Chain of Thought)在Agent场景下特别有用,但用法和普通问答不一样。普通问答中,思维链是让模型“把推理过程写出来”;Agent场景中,思维链是让模型“先规划再执行”。
我通常会在提示词中加一段“执行前思考”的要求:
在执行任何操作之前,先进行以下思考: 1. 用户的核心需求是什么? 2. 需要调用哪些工具?按什么顺序调用? 3. 每个工具需要什么参数?参数是否齐全? 4. 如果工具调用失败,备选方案是什么? 思考完成后,再开始执行。这段要求能让模型的执行更有条理,减少“想到哪做到哪”的情况。实测下来,加了这段之后,多步任务的完成率能提升20%以上。
6.2 用“角色扮演+场景模拟”提升复杂任务表现
对于一些复杂的Agent任务,单纯描述流程效果有限。这时候可以用“角色扮演+场景模拟”的方式,让模型先“进入角色”再执行。
比如一个代码审查Agent,可以在提示词中这样写:
你现在是一位有10年经验的资深代码审查员。你正在审查一个即将上线的生产环境代码。 你的审查风格是:先看整体架构,再看关键逻辑,最后看边界条件。 你特别关注:空指针异常、资源泄漏、并发安全问题。这种角色扮演能让模型调用更专业的“知识模式”,审查的深度和广度都会明显提升。
6.3 提示词的版本管理和A/B测试
提示词不是写完就完了,需要持续迭代。我建议从第一天就建立版本管理机制,每次修改都记录改了什么、为什么改、效果如何。
A/B测试的方法很简单:准备一组固定的测试用例,用两个版本的提示词分别跑一遍,对比成功率、格式准确率、平均执行步数等指标。我通常会用表格记录:
| 版本 | 成功率 | 格式准确率 | 平均步数 | 主要改动 |
|---|---|---|---|---|
| v1.0 | 72% | 85% | 4.2 | 初始版本 |
| v1.1 | 85% | 92% | 3.8 | 优化工具描述 |
| v1.2 | 91% | 96% | 3.5 | 添加Few-shot示例 |
这种数据驱动的迭代方式比“凭感觉改”高效得多。
6.4 提示词压缩:在效果和成本之间找平衡
提示词越长,Token消耗越大,成本越高。当Agent的调用量上来之后,提示词压缩就变得很重要。
压缩的原则是“删冗余、留关键”。具体操作时,我会把提示词中的每一句话都过一遍,问三个问题:这句话删掉会影响效果吗?这句话能合并到其他部分吗?这句话能用更短的表达吗?
一个实用的技巧是用“表格化”代替“段落化”。比如工具描述用表格来写,比用段落描述更紧凑,模型理解起来也更清晰。
6.5 多Agent场景下的提示词分工
当系统中有多个Agent协作时,每个Agent的提示词需要明确分工。我通常会把提示词分成“公共部分”和“专属部分”:
- 公共部分:角色设定、输出格式、通用约束
- 专属部分:具体任务描述、专属工具列表、特殊约束
这样设计的好处是修改公共部分时所有Agent同步生效,修改专属部分时不影响其他Agent。
7. 我在实际项目中的几点体会
提示工程这件事,说到底是一个“翻译”工作——把人类的意图翻译成模型能精确执行的语言。翻译得好不好,不取决于你用了多少高级技巧,而取决于你对模型行为的理解有多深。
我刚开始做Agent的时候,总觉得提示词写得越详细越好,恨不得把每个可能的分支都写进去。后来发现,模型不是执行代码的机器,它更像一个“理解意图的助手”。你给它太多细节,它反而会迷失在细节里;你给它清晰的框架和边界,它反而能发挥出更好的水平。
另一个体会是,提示词的优化永远没有“完成”的时候。模型在更新,业务在变化,用户在成长,提示词也需要持续迭代。我现在的习惯是每周花半小时回顾一下Agent的执行日志,看看有没有新的失败模式,然后针对性地调整提示词。这个习惯坚持了半年多,Agent的成功率从最初的六成出头稳定到了九成五以上。
最后分享一个我觉得特别有用的技巧:当你觉得提示词已经改无可改的时候,试着把它读给一个完全不懂技术的朋友听。如果他能听懂并准确复述出Agent该做什么,那说明你的提示词写清楚了;如果他自己都听糊涂了,那模型大概率也会糊涂。这个“外行测试法”帮我发现了很多自己意识不到的表述问题。