尧图精选

Agent提示词工程化:从模板管理到编排的实战指南

🕒 发布时间:2026/10/2 10:48:07 📁 来源:尧图网络
最近几个做 Agent 项目的朋友不约而同来找我聊同一个问题框架换了好几个模型也上了最新的但智能体表现总是不稳定——有时候能按预期执行有时候答非所问工具调用乱成一团。我看了一圈他们的代码问题基本都不是出在模型能力上而是提示词这块太随意了。System Prompt 直接写在代码字符串里工具描述想到哪写到哪上下文快塞爆了也不知道裁剪。说白了就是没把提示词当工程来管更别提 Agent 场景下的编排了。这个选题其实也是我们系列写作的第七篇上一篇聊的是多轮会话与上下文管理这一篇聚焦两块一是提示词模板怎么管理才不失控二是 Agent 场景下提示词怎么编排才能真正发挥模型能力。这篇不会跟你扯太多玄学全部是能直接落到代码里的实践方法适合正在做智能体应用、已经在用 LangGraph / AutoGen / Coze 这类框架但觉得效果不满意的开发者参考。1. 提示词模板管理为什么值得做成一套工程体系很多人的项目里提示词是这么存的一个 Python 文件里十几个三引号字符串有的带f-string有的不带模型换一个就全局搜索替换加个需求得小心翼翼。这种搞法在前几个 Demo 里没什么问题但一旦你的应用开始服务真实用户模板数量上到几十上百个必然出事。提示词模板管理的本质是把“对话策略”和“业务逻辑”分开。你写代码负责的是流程、数据处理、工具调用但模型怎么理解任务、按什么格式输出、边界在哪里这些是策略。策略应该像配置文件一样独立维护而不是散落在代码里。这样产品想调一句人设风格、法务想加一条合规约束不需要开发写个需求单再改代码直接改模板内容就行。1.1 从“写一段prompt”到“管理一堆prompt”我先给你看一个典型的反例。某团队做一个客服 Agent最初只有一个 System Prompt写在主程序里system_prompt 你是XX公司的客服助手请根据知识库回答问题。后来加了订单查询、退款处理、投诉安抚又加了多语言支持、敏感话题拒答、营销话术规范。三个月后这个字符串变成了三千字中间穿插着十几个if分支去拼不同的指令片段任何一次改动都可能让另一条逻辑失效。这其实不是提示词的问题而是管理方式的问题。当你的提示词从一段演变成一套就需要给它设计结构拆分、命名、版本、渲染、引用。我在实践中比较推荐把提示词拆成三层专门整理了一套模板分层模型用表格表示就是这样层级定位典型内容变动频率基础层通用能力与安全底线角色定位、通用行为准则、敏感内容拒答策略极低业务层具体业务逻辑与知识边界业务流程说明、可选操作清单、特定场景话术中会话层单次会话相关上下文用户输入摘要、临时数据、当前步骤提示高分层的逻辑很直白基础层类似公司章程改一次要高层审批业务层类似部门制度随业务调整而变会话层类似每日待办清单每次会话都要刷新。写代码时你肯定会分层提示词为什么不分层不分层的问题在于当某个模板变成三千字之后你根本说不清楚是哪个模块导致的模型行为异常排查成本远超管理成本。1.2 模板分层与元数据设计落到具体实现上模板不只存文本还要存元数据。我设计的模板记录一般长这样{ template_id: order_refund, name: 订单退款处理, version: 2.3.1, layer: business, model_compat: [gpt-4o, claude-sonnet, qwen-max], variables: [user_name, order_id, refund_reason], tags: [电商, 客服, 售后], content: ...模板正文... }这段元数据里layer帮你定位改动影响面version保证模型行为可回退model_compat是踩坑换出来的经验——同一个模板在 GPT-4o 上表现很好换到另一个模型上可能因为格式要求不同而崩溃。variables字段则把模板依赖的外部数据显式声明渲染前就能做完整性校验而不是等模板输出乱码了才发现漏传变量。这类模板记录存成 JSON 文件或数据库表都可以只要保证读取逻辑统一。元数据不需要一开始就很复杂但template_id、version、content三件套必须有——没有版本号的模板管理就是耍流氓模型行为变了你连对比样本都拉不出来。1.3 变量、渲染与版本控制的落地细节模板渲染听起来很简单就是把变量插进去。实际做的时候坑不少。我在项目里用 Jinja2 做模板引擎主要看中三个能力变量缺失不静默、过滤器链、以及微调控制结构。from jinja2 import Environment, StrictUndefined, meta from jinja2 import FileSystemLoader env Environment( loaderFileSystemLoader(prompt_templates/), undefinedStrictUndefined, # 变量缺失直接抛异常不静默输出空串 autoescapeFalse, # 提示词里不要自动转义 HTML ) # 渲染前先检查模板引用的变量 def render_template(template_id: str, variables: dict) - str: source env.loader.get_source(env, f{template_id}.j2)[0] parsed env.parse(source) required_vars meta.find_undeclared_variables(parsed) missing required_vars - set(variables.keys()) if missing: raise ValueError(f模板 {template_id} 缺少变量: {missing}) tmpl env.get_template(f{template_id}.j2) return tmpl.render(**variables)StrictUndefined是血泪教训换来的。默认行为下Jinja2 遇到缺失变量会渲染成空字符串你的提示词可能变成“用户你好你的订单已”而代码完全不会报错。开启严格模式后这类错误在开发期就暴露。渲染前用meta.find_undeclared_variables做变量完整性校验相当于给提示词加了 TypeScript 的类型检查值得养成习惯。版本控制上我建议模板文件和代码走同一个 Git 仓库。这样每次改动模板都留下提交记录配合元数据里的version字段能随时用git diff看两个版本的行为差异。模板单独建目录不跟业务代码混在一起目录结构大致保持这样prompt_templates/ ├── base/ # 基础层 │ └── assistant_persona.j2 ├── business/ # 业务层 │ ├── order_refund.j2 │ ├── complaint_handling.j2 │ └── product_consult.j2 ├── session/ # 会话层 │ └── conversation_context.j2 └── test/ └── cases/ # 模板测试用例版本号建议用语义化版本主版本表示破坏性更新比如重写了整个角色设定次版本表示新增能力比如加了一条新的处理分支修订号表示小改动比如调整了一句措辞。这套规则不复杂但能让你出差错时快速定位是哪次改动引入了问题。2. Agent提示词编排不是拼积木是分层设计模板管理解决的是“存量怎么组织”的问题Agent 提示词编排解决的是“增量怎么生成”的问题。两者关系是模板是零件编排是装配。很多人都听过 Agent 大模型 规划 记忆 工具 这个公式但落到提示词层面怎么把这些模块按合理的次序与结构放进一次请求里是有一番讲究的。Agent 提示词编排和传统 Prompt Engineering 最大的区别在于传统 Prompt 大多是单轮一次性对话指向明确Agent 场景下模型需要理解自己“能做什么”“不能做什么”“按什么顺序做什么”“遇到异常怎么办”这就不是一段话能解决的了必须编排成一套相互配合的指令体系。2.1 Agent提示词的典型组成结构我每次设计 Agent 的 System Prompt脑子里都会过一个清单你可以把这个清单当作编排框架。标准结构大致如下角色与目标Agent 是谁本轮服务的核心目标是什么。比如“你是电商售后助理目标是尽快解决用户的售后问题同时控制退款率在合理范围”。行为准则面对用户、面对信息缺失、面对冲突时的处理原则。这部分是模型的“价值观底座”尤其要写清楚边界。比如“不得承诺超出政策范围的补偿”“不确定时先查规则再回答”。可用工具与触发条件列出 Agent 可以调用的工具每个工具在什么场景下触发不要一把梭全列出来要有选择逻辑。工作流程由步骤组成的执行链路比如先识别用户意图再查询订单再给出方案。复杂任务可以分组描述让模型按阶段推进。输出格式什么场景用自然语言回复什么场景输出结构化数据什么场景必须调用工具。格式要求写得越具体模型越不容易自由发挥。限制与兜底哪些话题不处理、连续失败怎么办、超时怎么办等异常分支的处理策略。这六个部分是松耦合的别把它们写成一整段连续文字。我在实际编排时会刻意在段落之间留出清晰的区域标识比如用标记性的段落标题模型对这种结构化的提示词理解更稳并能减少互相干扰。这是一个很实用的经验。2.2 工具描述与Few-shot示例的规范写法Agent 能不能正确调用工具一半取决于工具本身的实现另一半取决于工具描述写得怎么样。很多开发者的工具描述只有一句话“查询订单信息”模型并不知道这个工具接收什么参数、什么时候该用、返回什么结果于是经常出现误调用。我在项目里把工具描述规范为四个部分工具目标、输入参数说明、触发场景、输出说明。有一个专门整理的工具描述与功能配置的对照关系描述维度常见错误写法建议写法工具目标查询订单根据订单ID查询一笔订单的最新状态包含物流进度、退款状态、商品明细输入参数传入订单编号参数 order_id字符串必填例如 OD20250101001触发场景用户問订单情况时调用用户询问订单进度、物流位置、发货时间、退款到账情况时调用输出说明返回订单信息返回 JSONstatus(状态)、logistics(物流轨迹数组)、refund(退款金额/状态)工具描述写不到位模型就会“试探性调用”先猜一个参数格式失败了再猜一次反复出错。所以写工具描述时要站在模型的角度问自己一个问题如果我只看到这段描述我知道什么时候该用这个工具、参数怎么填吗如果答案是模糊的模型大概率也是模糊的。Few-shot 示例的写法同样重要但要注意精确度。示例不是写得越多越好通常每个工具 2-3 个典型调用示例足够了关键是覆盖易混淆场景。我做客服 Agent 时发现模型经常把“修改订单地址”和“取消订单”搞混于是给两个工具各加了一个对比例子用户说“我想改收货地址”不能调用 cancel_order 而要调用 update_order_address并保留原订单号信息。这种对比式示例比单纯堆功能示例有效得多。另外示例中的用户表述、工具调用、模型回复要完整配对不要只给工具调用片段那样模型学不到完整的决策链路。2.3 上下文窗口管理与记忆注入策略Agent 编排里最容易忽视的是上下文长度控制。你现在有模板、有示例、有工具描述、有历史对话摘要一股脑塞进去算一下 token 很可观。一旦执行 Agent 陷入长对话超出上下文限制模型要么忘记前面的指令要么直接报错。我的策略是把提示词分优先级用“三梯队”方式管理上下文空间。第一梯队是核心指令区系统提示词里最关键的段落角色、行为准则、输出格式保持在上下文前半部分模型对这部分关注度最高。第二梯队是工具与示例区只在执行到相关分支时才注入避免无关工具占空间。第三梯队是历史与临时数据区对历史消息做衰减式保留最近的保留原文较早的只保留摘要再早的直接丢弃。记忆注入也要讲策略不要把整段历史塞进 Prompt。我会对历史消息做摘要然后按“当前目标相关度”筛选注入。简单做法是维护一个滑动窗口最近 5 轮全量保留更早的每 3 轮压缩成一句摘要。这样 Agent 既保留长期上下文又不至于让上下文爆掉。这与长时记忆系统的思路一致在 Agent 架构里记忆不是照单全收而是有选择地调度。3. 实操一套可落地的模板管理与Agent编排方案理论铺垫完了接下来是完整的可落地实践。我会带你走一遍我最近做的一个“客服售后 Agent”的真实流程重点演示三部分模板工程基础实现、Agent 编排流程实现、测试评估方法。你不需要照抄我的业务直接把这个套路翻译到你的场景里即可。3.1 模板工程的基础实现第一步先把模板目录建好我在项目里用 Jinja2 作为引擎模板文件后缀统一用.j2做标识。先建一个基础人格模板{% raw %} 你是{{ company_name }}的智能售后助理你的名字叫{{ assistant_name }}。 ## 行为准则 - 以解决用户问题为首要目标态度友好但不卑不亢。 - 所有回复必须基于给定的知识库与订单数据不得编造信息。 - 涉及退款、补偿等敏感操作必须先列明依据政策再向用户说明。 - 如果用户情绪激动先安抚情绪再解决问题不要急于反驳。 - 不回答与售后无关的问题礼貌引导用户回到售后主题。 ## 输出要求 - 回复使用简体中文语气自然篇幅控制在 200 字以内。 - 如果信息不足明确告知用户需要补充什么信息。 - 如果必须调用工具先给出简短说明再等待工具结果不要凭空猜测。 {% endraw %}基础层的内容不涉及具体业务是 Agent 的“人格与底线”。在写业务层模板时我只关注具体售后类型的分支说明。比如售后分类模板里定义业务链路{% raw %} ## 售后分类处理 根据用户的描述将售后请求分类并按对应策略处理 1. 订单查询调用订单查询工具获取 {{ user_name }} 名下最新订单信息。 2. 退货退款确认订单状态与商品是否支持七天无理由退货。 3. 物流异常查询物流轨迹如有异常联系仓库处理。 4. 发票问题引导用户登录后台查看电子发票。 无论哪种分类最后都要给用户一个明确结论或下一步操作指引不能只说“您的反馈已记录”。 {% endraw %}模板渲染与校验封装到统一模块里这部分代码负责加载模板、检查变量、渲染、记录版本。后续 Agent 每次调用前先走一遍渲染流程保证拿到的 System Prompt 是完整且符合当前业务配置的。3.2 Agent编排流程的实现接下来把模板编排成真正的 Agent 请求。这里我用一个极简的编排函数演示不依赖任何框架方便你看清本质def build_agent_prompt(user_input: str, session_state: dict) - list: # 1. 渲染基础层 base_prompt render_template( base/assistant_persona, {company_name: 示例科技, assistant_name: 小A} ) # 2. 渲染业务层 intent classify_intent(user_input) # 示例简单分类函数 business_prompt render_template( fbusiness/{intent}, {user_name: session_state.get(user_name, 用户)} ) # 3. 组装工具描述只注入当前意图相关的工具 tools get_relevant_tools(intent) tool_prompt format_tool_descriptions(tools) # 4. 注入会话层历史摘要 当前用户输入 session_prompt build_session_context(session_state) # 5. 按固定顺序拼接 system_prompt \n\n.join([ base_prompt, business_prompt, tool_prompt, ## 会话上下文, session_prompt ]) return [ {role: system, content: system_prompt}, {role: user, content: user_input} ]这套编排有几个关键选择解释一下我的考虑。省掉框架依赖是为了减少干扰让你集中于 Prompt 组装逻辑换任何框架LangGraph、AutoGen、字节的 Coze 或自研框架时这套逻辑都能平移。按意图注入相关工具而不是把所有工具全塞进去能明显降低模型工具选择错误的概率。拼接顺序固定把基础层和业务层放在前面会话上下文放最后对模型注意力分配更友好。你可能会想为什么 System Prompt 里要带会话上下文因为这样在多轮时模型不必每次都靠前面的对话记录推断当前状态信息直接注入能减少遗忘。3.3 测试与评估如何判断编排出的提示词是好是坏提示词编排完怎么知道好还是不好只跑几个 happy path 远远不够我总结一套可执行的测试清单单轮正确率20-50 条测试用例覆盖典型用户问题看回复是否符合预期格式与答案。工具选择准确率专门构造“易混淆场景”看模型是否选对工具参数是否正确错误率高就回去调工具描述。多轮稳定性连续 10 轮以上对话看模型是否保持人格一致、不跑偏、不忘记最初约束。安全边界测试输入诱导性、越权性问题看模型是否坚守边界会不会被带偏。成本基线记录每次调用的 token 数排编前后对比防止为了效果无节制塞长文本。测试时可以把输入输出存成日志标注通过/失败原因这样每次调整模板后都能对比回归。我习惯维护一个测试用例集文件的独立仓库与模板库并排管理每次模板变更必须跑一遍全部用例确保没有破坏既有能力。这一步花不了多少时间但能让你在项目后期省下大把的“灵异 Bug 排查时间”。4. 常见问题与排查技巧实录所有方案在真实项目里都会踩坑这里把我遇到过的典型问题整理成一份排查速查表并附上定位思路与修复建议。这些都是平时文档里不太会写的但大概率是你迟早遇上的。4.1 变量冲突与花括号转义Jinja2 用{{ }}做变量插值但提示词里有时需要让模型输出 JSON 示例、花括号结构结果模板一渲染花括号被引擎吃掉或者报错。我第一次写模板时因为提示词里带 JSON 示例导致渲染全乱排查了很久才发现是花括号冲突。解决办法是给模板中需要原样输出的花括号部分用{% raw %}包起来或者使用{{ {{ }}转义。更省心的做法是预先约定模板中不直接写大段 JSON改用占位符描述例如写“输出 JSON 格式包含 status 与 message 字段”把具体格式在代码侧控制。日常建议对模板做自动化渲染测试把测试用例挂在 CI 上改模板后自动跑一遍所有渲染样例花括号冲突这类问题在开发期就能被杀掉。4.2 模型不兼容与模板漂移同一套模板在 A 模型上效果好换到 B 模型上效果差这我在实际项目中经历过多次。原因各有不同有的是因为模型对 Markdown 标题的敏感度不同有的是偏好不同的指令措辞有的是工具描述格式的接受度差异。元数据里的model_compat字段值得认真维护每次验证通过后顺手记录一下。“模板漂移”指的是模板内容在频繁修改中慢慢偏离最初设计失去一致性。避免方案是版本控制加定期审查。我每个季度会做一次模板大扫除把线上所有模板拉出来检查是否有死代码、重复段落、过时表述做一次合并整理。这就像代码重构短期看不到收益长期能保住可维护性。4.3 编排后的提示词过长或截断把模板、工具描述、Few-shot、历史摘要全拼起来很容易达到上下文上限。尤其是工具数量多、每个工具描述写很长的时候。我处理过的极端案例里一次请求的 System Prompt 超过了 12000 token模型开始把注意力分散到不重要的部分核心能力反而下降。解决思路是给每个模块设预算上限。我给一个参考配置表模块预算比例说明基础层角色与准则20%-25%强约束必须完整业务层逻辑20%-30%按当前任务动态裁剪工具描述20%-25%只注入相关工具Few-shot 示例15%-20%按需精简会话上下文10%-15%摘要化呈现这个比例不是死的但能提供一个总量控制思路。配置工具时一个工具的描述控制在 150-300 token 之间比较合适超过这个范围就要考虑是不是把逻辑写进代码而不是提示词。预算意识很重要Prompt 编排不是“越多越全越好”是“每个 token 都有目的”。4.4 工具描述失效与Agent循环失控最常见的反馈是模型明明有工具却不用或者调用了工具但结果不被正确采纳。排查时先看工具描述里是否写清楚了触发条件和输出说明很多时候模型不调用不是因为不会而是描述里没提到“什么情况下调用”。有一种比较隐蔽的情况模型在一个循环里反复调用同一工具拿不到结果也不换策略表现为“Agent 卡在某个状态”。解决方案是要在编排中注入尝试次数限制例如明确写“同一工具调用失败两次后停止调用并直接告知用户无法处理建议转人工”。这属于典型的 Agent 安全边界必须写进 System Prompt 的异常处理段落。我还习惯把工具调用的完整轨迹写进日志出现循环时可以直接回溯每一步定位是哪一步的决策分支没有设计好。Agent 的编排中控逻辑要兜底不要把所有控制权都交给模型。我在实际项目中的几条干货建议最后说几句经验之谈不算总结就是这几年来反复验证过的几个观点。第一句话提示词模板管理永远值得从一开始就做。项目再小只要你的 Agent 要长期维护模板拆分和版本控制的前期成本都不高但后期收益极大。不要等提示词膨胀到没法收拾才开始重构。第二句话Agent 提示词编排要把自己当“导演”而不是“编剧”。你的核心工作不是替模型写好每一句话而是规划好阶段性目标、工具边界、异常处理路径让模型在框架内有足够的发挥空间。一个编排良好的 Agent不是提示词写得花哨而是边界清晰、路径完整、兜底明确。第三句话所有技巧都要靠测试数据检验不要凭感觉优化。我们很容易陷入“调整措辞玄学”的循环一会儿觉得这句加得好一会儿又改回去。坚持维护测试集、对比版本差异才是可持续的优化方法也符合我们系统化处理智能体问题的整体思路。这套方法在实际项目里已经帮我扛过了好几次发版模板管理和编排逻辑分离之后产品和开发之间的协作也顺了很多。你可以先从最小的场景开始把一个单机 Demo 的提示词按模板重写一遍感受一下差异下一步再看怎么引入更复杂的编排框架。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →