任务型Agent:prompt工程实践
本文聚焦Prompt工程实践,从组成、组装、调优到上下文管理进行系统阐述。Prompt由角色定义、技能步骤、规则限制、输出格式等模块构成,建议采用Velocity(Java)或Jinja2(Python)模板引擎实现动态组装。调优经验强调:利用思维链(CoT)排查矛盾点,通过示例强化约束,控制输出顺序(先推理后结论),关键要求置于Prompt末尾。针对上下文膨胀问题,提出"选择上下文&q
市面上讲 prompt 的文章遍地都是,这里不再分享 prompt 通用技巧,而是结合工程简要讲一下 prompt 组成、组装、调优经验和上下文工程。
一、prompt 组成及示例
prompt 一般由预设角色、技能(复杂的任务需给出处理步骤)、限制(如严格遵守的规则等)、输出要求、示例、历史会话和用户输入等部分组成,示例如下:
你现在是任务规划专家,你需要根据最新的输入内容(用户输入、上一步执行结果等),严格按照以下思考步骤,更新执行计划(EP)当前节点的内容。
***** 严格遵守规则 *****
**【严格遵守】绝对不允许调整执行计划的结构,只能变更当前节点的属性
**【严格遵守】绝对不允许修改节点的status属性
** ...(省略)
***** 风控领域基础知识 *****
...(省略)
***** 可用的工具定义、描述、参数列表 *****
=== 工具组:tableQuery ===
==工具编码: submitTableQuery
* 工具描述: 查询表基础信息
* 参数列表:
tableName: 表名称,必填
* 输出结果说明: 表的基础信息,其中 tableName 表示表名称, columns 表示列列表信息。
...(省略)
***** 输出说明 *****
最终需要输出两部分指定格式的内容:
**第一部分是EP的更新说明,格式要求如下:使用\"@epupdatelog@\"作为起始符和结束符包裹更新信息,采用markdown格式,严格遵守以下格式:
@epupdatelog@
{更新内容说明}
@epupdatelog@
第二部分是最终更新后的EP内容,其中结构说明、EP节点属性说明、更新说明格式要求参考下面的描述,使用\"@EP@\"作为特殊符号包裹更新信息,严格遵守以下格式:
@EP@
*id=1
*graph=digraph G {
1 [label ="标题1" shape=record type="function"];
2[label =“标题2" shape=ellipse type="confirm
toolCode="”status="待用户输入中”toolInput=""toolOutput=""*,question=""];
status="待初始化”];
1->2;
@EP@
**EP结构说明**
...(省略)
***** 参考示例 *****
${caseList}
***** 当前执行计划详情 *****
${curEP}
***** 用户对话历史 *****
${history}
>>> 现在,让我们开始...
>>> 当前的用户输入:${input}
二、prompt 组装
1) velocity 语法
prompt 模板在工程实践上可采用 velocity 语法进行配置,以使用 org.apache.velocity 包代码示例:
public class VelocityUtil {
private static final VelocityEngine ve = new VelocityEngine();
//...
public static String merge(String template, Map<String, Object> params) {
StringWriter sw = new StringWriter();
ve.evaluate(new VelocityContext(params), sw, "", template);
return sw.toString();
}
}
// prompt 拼接过程
Map<String, Object> params = new HashMap();
params.put("caseList", queryCaseList());
//......
String prompt = VelocityUtil.merge(prompteTemplate, params);
注意:#### 是 velocity 的注释符号,模板中不能使用。
2)Jinja2 模板
Jinja2 是一个基于 Python 的现代且功能丰富的模板引擎,广泛用于生成动态 HTML、XML 或其他标记语言内容。与Velocity的核心区别如下:
-
Jinja2 更适合需要高灵活性、复杂逻辑的 Python 项目(如 Flask 应用),功能丰富且安全性强。
-
Velocity 适合强调逻辑简单性、严格遵循 MVC 分离的 Java 项目,语法简洁但功能相对受限。
即 Python 优先选 Jinja2,Java 生态选 Velocity;复杂模板选 Jinja2,轻量需求选 Velocity。示例如下:
{# 基础提示词模板 #}
{% block system_role %}你是一个{{ role }}。{% endblock %}
{% block user_query %}
用户问题:{{ question }}
{% if context %}附加上下文:{{ context }}{% endif %}
{% endblock %}
{% block requirements %}
生成要求:
- 语言:{{ language }}
- 长度限制:{{ max_length }}字
{% endblock %}
三、prompt 调优经验
-
利用好 COT,遇到 badcase 需要排查大模型 COT,是否存在反复思考(或确认)的点,若存在,则 prompt 中可能存在矛盾点或描述不清的点。
-
排查 prompt 中示例和规则部分是否存在不合理的逻辑。
-
prompt 优化到极致需要考虑是否由模型能力不足引起,在不更换模型的情况下可考虑增加 few-shot。若 few-shot 不起作用,可尝试单点 LLM 优化或者深度 workflow 模式。
-
开启深度思考模式,比如 Qwen 系列可通过标签 \think。
-
遇到大模型始终不遵守的规则,可采用 cot 方式在 prompt 中讲清楚思考规则。
-
通过示例对齐标准和强化推理,多次强调加强约束。
-
数字计算类的最好通过工具来解决。
-
prompt 的指令的输出若包含推理依据和结论时,先给出推理依据,再给出结论(顺序不要搞反)。
-
学会控制大模型,比如输出工具和参数时,只需要让大模型给出参数 key 对应的 value 就行(一般参数名称 key 是确定的),来减小大模型的不确定性。
-
输出要求放到 prompt 的最后可能会起到意想不到的效果。
有的 prompt 包含模块较多,实战时“用户历史会话”部分放到 prompt 的最后,因会话信息格式杂乱,经常会影响大模型的输出,尝试将“输出格式”放到 prompt 最后,把“用户历史会话”上移,效果好了很多。
-
有时 prompt 中描述的太复杂,解释的越清晰,会起反作用。另外精准的描述会减小大模型的思考时间。
如工具参数的复杂计算逻辑,大模型经常会出现幻觉。
-
大模型出现幻觉的常用解决方案:代码后处理进行工程兜底、few shot 中增加 COT
四、Context 工程
随着 Agent 接入的业务场景日益增多,Prompt 逐渐变得零散且复杂。例如,在信贷风控场景中,规则需根据不同的客群进行区分,因此在优化 Prompt 时,必须为不同客群配置相应的知识上下文。然而,诸如风控基础知识、工具调用等部分又是多个场景共用的,如何有效组织和复用成为挑战。与此同时,会话历史中往往包含模型输出、用户对话以及工具执行记录等大量信息,过长的上下文不仅增加 token 数量,还容易导致大模型产生幻觉。随着业务知识不断沉淀,Prompt 的复杂度将持续攀升。在此背景下,上下文工程(Context Engineering)为解决这一系列问题提供了系统性的优化思路。context 工程并非要取代 prompt 工程,而是一个更高阶、更侧重于系统设计的必要学科。文章《Context Engineering》针对“丰富”的上下文可能出现的问题提出了几种解决方案,如下图:
针对大模型经常出现幻觉上下文优化思路:
-
采用“Select Context”思路针对不同客群加载不同的知识和指令、预圈选可使用工具(只为特定任务获取最相关的工具)等。
-
采用“Compress Context”思路对工具执行结果进行摘要,同时过滤掉无用、错误的对话信息。
参考文章:
-
Context Engineering:Context Engineering
-
Context Engineering - What it is, and techniques to consider:Context Engineering - What it is, and techniques to consider — LlamaIndex - Build Knowledge Assistants over your Enterprise Data
更多推荐
所有评论(0)