摘要
2026年,大模型应用开发已经从“能不能跑通”的阶段,进入“能不能稳定跑”的深水区。本文基于多个生产级 AI Agent 项目的实战经验,系统复盘提示词工程、工具调用与多轮对话逻辑设计三大核心模块的工程化方法。文章首先剖析大模型落地中“概率模型与确定性业务冲突”的根本矛盾,然后从 Prompt 的结构化契约设计、Function Calling 的可靠集成模式、多轮对话的状态管理策略三个维度展开,最后给出从原型到生产的可落地建议。核心观点是:大模型应用的稳定性不取决于模型有多强,而取决于工程控制体系有多完整。
关键词:提示词工程;工具调用;多轮对话;AI Agent;工程化实践
一、引言:从 Demo 惊艳到上线翻车的教训
几乎每个做 AI Agent 的团队都经历过这样的落差:本地演示时效果惊艳,小范围内测勉强可用,一旦上线生产环境,各类“玄学故障”扎堆出现——相同输入输出不一致、多轮任务越跑越偏、工具调用随机失败且问题无法复现。
问题的根源往往不在模型能力本身,而在于我们是否用工程手段管理好了大模型特有的“反直觉特性”。大语言模型基于 Transformer 自回归生成机制,天生具备三个反工程直觉的特性:输出非确定性、原生无状态性、注意力资源衰减。传统软件“输入确定则输出确定”的工程假设,在大模型面前彻底失效。
本文从三个最核心的工程模块——提示词工程、工具调用、多轮对话逻辑——出发,复盘实战中的设计决策与踩坑经验,试图回答一个具体问题:如何用工程控制体系,把非确定、无状态、注意力有限的大模型,改造成可稳定、可控制、可复现的生产级组件。
二、提示词工程:从“说话技巧”到“接口契约”
2.1 核心理念转变:Prompt 是调用接口,不是聊天记录
很多开发者对提示词的理解停留在“把需求说清楚”的层面。这在单轮简单任务中够用,但在生产级 Agent 中远远不够。从工程视角来看,Prompt 本质是大模型的调用接口契约(Function Contract),和传统 API 接口定义、OpenAPI 规范逻辑一致。这个认知转变至关重要:你不是在“和 AI 聊天”,你是在调用一个概率性的计算组件,必须像定义 API 入参出参一样定义 Prompt。
传统 API 接口:输入{user_id: 123},输出{name: "张三", age: 28},确定性执行。
大模型“接口”:输入一段文本,输出一段文本,非确定性生成。要让这个“接口”可靠,Prompt 必须承担起类型定义、约束校验、输出格式规范的全部职责。
2.2 结构化 Prompt 的实战框架
经过多个项目的迭代,我们沉淀出的生产级 Prompt 模板遵循“角色定位 + 核心任务 + 约束规则 + 输出格式”的四段式结构。以一个企业内部知识库助手为例,其 Prompt 的核心部分如下:
你是一个企业内部知识库助手,专门帮助员工查找和理解公司内部文档。
你的工作准则
- 只根据提供的参考资料回答问题,不要使用你自己的知识。
- 如果参考资料中没有相关信息,请明确说“根据现有资料,我无法找到关于这个问题的信息”,并建议用户联系相关部门或换个关键词搜索。
- 回答时请标注信息来源,格式如【资料1】,方便用户追溯原文。
- 对于操作类问题,请按步骤清晰地列出;对于概念类问题,先给出简明定义再展开解释。
- 如果不同资料中的信息有冲突,请指出差异并说明各自的适用场景。
这个设计中有几个关键决策值得展开:
“不知道”的显式声明。大模型最大的风险之一是幻觉——编造看似合理但完全错误的信息。如果不显式要求模型在信息不足时说“不知道”,它会倾向于“接话”而非“承认不会”。我们的经验是,在准则中必须用加粗、编号等方式强化这一条,并且在 Few-shot 示例中提供正例和反例。
来源标注的强制要求。这不仅是为了可追溯,更是一种“认知负荷”设计:当模型被要求标注来源时,它被迫在参考资料和输出之间建立映射关系,这能显著降低幻觉率。
动态指令分层。初期我们试图把所有要求塞进一个 Prompt,结果模型反而“顾了这个忘了那个”。后来的做法是将指令分为核心指令(任何场景必须包含)和场景指令(根据问题类型动态追加)。操作类问题追加步骤格式要求,概念解释类问题追加“先定义再展开”的指令,故障排查类追加“排查顺序”的指令。这种分层设计比“万能 Prompt”的效果稳定得多。
2.3 Few-shot 与思维链:什么时候用,什么时候不用
Few-shot 提示的核心不是“放几个例子”,而是示例的策略设计。我们的经验是:3 到 5 个示例是性价比最高的区间,超过 8 个示例后边际收益急剧下降,反而会因上下文过长稀释模型注意力。更重要的是示例的“难度梯度”——以一个简单案例开头建立模式认知,中间放一个边界案例展示处理规范,最后以一个复杂案例锚定质量上限。
思维链(CoT)提示在推理任务中效果显著。“让我们一步步思考”这句简单的零样本提示之所以有效,是因为它激活了模型在预训练中大量接触的“分步解题”模式。但 CoT 并非万能,我们踩过的坑包括:
- 过度推理:在情感分析等简单任务上强制 CoT,模型生成冗长的推理链,既浪费 Token 又可能引入新错误。
- 推理幻觉:中间步骤看似合理但事实错误,后续推理基于错误前提层层放大。
- 格式漂移:在创意写作任务上强制 CoT,输出变得僵硬、不自然。
我们的决策规则是:分类、抽取、格式转换类任务不用 CoT;数学计算、逻辑推理、多步规划类任务必须用 CoT;创意生成类任务慎用 CoT。
2.4 提示词的工程化管理
把 Prompt 当作代码来管理,是团队协作的底线要求。我们维护了一个Prompt 资产库,每个业务场景的 Prompt 进行版本管理,记录每次修改的原因和效果指标变化。当模型版本升级或切换供应商时,通过批量测试快速评估 Prompt 的兼容性,而不是靠“感觉好像变差了”来判断。
一个实用的做法是建立Prompt 回归测试集:从真实业务日志中抽取 50-100 个代表性 Query,覆盖正常路径和边界情况,每次 Prompt 修改后跑一遍自动化评估。虽然大模型的非确定性让“断言式测试”不完全适用,但用 LLM-as-Judge 的方式做相对评估——比较新旧 Prompt 在同一批 Query 上的表现——已经足够指导工程决策。
三、工具调用:让大模型“动手”而不只是“动嘴”
3.1 Function Calling 的本质:结构化意图表达
工具调用(Function Calling)是大模型从“聊天机器人”进化为“Agent”的关键能力。其核心机制是:模型不直接执行业务逻辑,而是返回一个结构化的函数调用请求(包含函数名和 JSON 格式的参数),应用程序执行该函数,再将结果传回模型,模型基于实际执行结果生成最终回复。
这个设计的关键在于执行权的分离:模型负责“决定做什么”,代码负责“实际执行”。这解决了纯 LLM 方案的最大风险——让概率模型直接操作业务系统,等于让一个可能“看走眼”的实习生直接点击生产数据库的删除按钮。
3.2 函数定义的工程准则
我们在实践中总结了几条函数定义的“铁律”,每一条都对应着真实的踩坑经历。
准则一:不要让模型填它不需要知道的参数。
如果系统已经知道order_id(比如从上一轮对话中提取),就不要把它放在函数的参数列表里。OpenAI 的官方最佳实践明确指出:“不要让你已经知道的参数交给模型来填”。错误示范:submit_refund(order_id, reason),如果order_id已经通过上下文获取,函数应该设计为submit_refund(reason),由代码在调用时注入order_id。
准则二:合并总是顺序调用的函数。
如果query_location()之后必然调用mark_location(),就把标记逻辑合并进查询函数。每次工具调用都是一次模型推理的往返,减少不必要的往返就是降低延迟和出错概率。
准则三:用枚举和对象结构防止非法状态。
不要设计toggle_light(on: bool, off: bool)这种允许传on=true, off=true的接口。用枚举类型toggle_light(state: "on"|"off")让非法状态在类型层面就无法表达。
准则四:通过“实习生测试”。
把函数定义和描述给一个不了解你系统的实习生看,他能不能正确使用?如果不能,他问了什么问题?把这些问题的答案补进 Prompt 里。
3.3 工具数量与 Token 成本的权衡
一个容易被忽视的工程约束是:函数定义本身就消耗 Token。每个可调用函数的名称、描述、参数 schema 都会被注入到系统消息中,按输入 Token 计费。我们曾经在一个项目中定义了 40 多个工具函数,系统提示词的开销高达每次调用 3000+ Token 的“固定成本”。
经验法则是:单次对话初始可用的函数数量控制在 20 个以内。如果工具生态很大,使用“工具搜索”机制延迟加载——让模型先搜索相关工具,再将其加入上下文。另一种策略是按场景动态加载工具集:客服场景只加载订单、退货相关工具,运维场景只加载服务器、日志相关工具。
3.4 工具执行失败的恢复策略
生产环境中工具调用失败是常态而非例外。我们的降级策略分三层:
第一层:模型层重试。将错误信息作为 Tool Message 返回,让模型理解失败原因并尝试修复参数后重试。适用于参数格式错误、缺少必要字段等可恢复错误。
第二层:代码层容错。对于超时或临时性故障,代码层自动重试 2-3 次,使用指数退避策略。
第三层:用户层降级。当工具连续失败或返回不可恢复错误时,Agent 应该告知用户当前无法完成该操作,并提供替代方案(如“请稍后重试”或“请联系人工客服”)。绝不能让 Agent 在工具失败后“编造”一个成功的结果。
四、多轮对话逻辑设计:状态管理的工程化
4.1 核心问题:大模型天生“失忆”
大模型的每一次 API 调用都是独立冷启动,没有内置的长期记忆与状态留存能力。你问完“我叫张三”再问“我叫什么”,模型答不上来不是因为“笨”,而是因为两轮调用之间没有任何状态传承——除非你在第二次调用时把“我叫张三”这段历史重新传给它。
多轮对话的工程本质,就是用外部系统维护一个“消息列表”,每次调用时把相关历史全量或选择性传入。LangChain 等框架将这个过程抽象为 Message 对象:SystemMessage(系统指令)、HumanMessage(用户输入)、AIMessage(模型回复)、ToolMessage(工具结果)。每条消息都有 role 和 content,按时间顺序组成对话历史。
4.2 上下文管理的三种策略
策略一:全量保留(适合短对话)。每次将所有历史消息传入。优点是信息无损,缺点是 Token 成本随轮次线性增长。经验阈值:对话轮次在 10 轮以内、总 Token 不超过模型窗口 30% 时可用。
策略二:滑动窗口 + 摘要(适合中等长度对话)。保留最近 K 轮原文,更早的历史通过 LLM 压缩为摘要。摘要中必须保留的关键信息包括:用户的核心目标、已确认的关键实体(订单号、人名、日期)、已完成的步骤和未完成的步骤。
策略三:基于索引的检索(适合长对话)。OrbitCorrect 协议提出了一种“Rally 索引”方案:每轮对话分配一个递增索引号,用户可以通过索引引用历史轮次(“总结第 5 轮到第 8 轮的内容”),系统也能基于索引精准检索相关历史。这种方案的优势在于将“上下文选择权”部分交给了用户,避免了自动摘要的“信息丢失不可控”问题。
我们在生产环境中的选择是:滑动窗口 + 结构化摘要。最近 5 轮保留完整原文,更早的历史压缩为结构化摘要(固定包含“用户目标”“已确认信息”“已完成操作”“待处理事项”四个字段)。这样既控制了 Token 成本,又保证了关键状态信息不丢失。
4.3 多轮对话中的“偏离”与“回归”
多轮对话最容易出问题的地方是任务执行过程中的话题偏移。用户在第 3 轮问了一个与主任务无关的问题,模型回答完后可能“忘记”了原本的主任务。
解决方案是引入任务状态追踪。我们在 System Prompt 中维护一个隐藏的“任务上下文”区块:
【当前任务状态】
- 主任务:帮用户完成退货流程
- 已完成步骤:1. 确认订单存在 2. 验证退货时效
- 待处理步骤:3. 确认退货原因 4. 生成退货单
- 用户附加信息:订单号 ORD-12345,商品为蓝牙耳机
每轮对话后,由代码逻辑或轻量级模型调用更新这个状态区块。当用户提出无关问题时,模型正常回答,但状态区块始终保留在上下文中,确保主任务不被遗忘。
4.4 工具调用在多轮对话中的编排
多轮对话中工具调用的编排比单轮复杂得多。考虑这样一个场景:
用户:“帮我看看我的订单到哪了” → Agent 调用query_order(order_id)→ 返回“已发货” → 用户:“那帮我退了吧” → Agent 需要调用initiate_return(order_id),但order_id来自上一轮的工具调用结果,不在用户输入中。
这个场景的工程实现要点是:工具调用的结果必须作为 Tool Message 完整保留在对话历史中,且其关联的tool_call_id必须与 AIMessage 中的调用 ID 匹配。这样在下一轮中,模型才能从历史中找到order_id并用于新工具调用。
一个常见的 bug 是:开发者在代码中消费了工具结果但没有将其正确回传为 Tool Message,导致模型在下一轮“看不到”之前获取的数据,从而重复调用工具或凭空猜测参数。
五、从原型到生产的工程化建议
5.1 可观测性:给 Agent 装上“黑匣子”
生产环境的 Agent 出错时,你不能只看到“最终回复是错的”,你需要知道它为什么错。由于 Agent 包含多轮推理和多次工具调用,传统日志完全失效。
必须建立的监控维度包括:
- Trace ID 贯穿全链路:每个用户请求生成唯一 ID,记录从输入到输出的完整生命周期。
- 细粒度步骤追踪:每次模型调用的输入 Prompt、输出 Token、耗时;每次工具调用的参数、返回值、执行时间。
- 费用监控:实时统计每个 Trace 的 Token 消耗,设置异常告警,防止无限循环导致的成本失控。
- 质量指标:意图识别准确率、工具调用成功率、用户满意度反馈。
5.2 模型选型:不是越大越好
我们在一个客服项目中的真实教训:初期用 72B 模型跑推理,延迟 6-8 秒,用户等得想骂人。后来换用 7B 模型 + LoRA 微调,延迟降到 2 秒以内,准确率反而从 88% 提升到 93.7%。
选型的核心原则是先定约束再选模型:延迟要求是什么?成本预算多少?准确率底线在哪里?在满足约束的候选模型中选最合适的,而不是选“最强”的。必须用真实业务数据做评估,通用 benchmark 上的差距在业务场景中可能完全不成立。
5.3 建立“概率性思维”
最后,也是最根本的一条:做 AI 应用开发,必须接受“没有 100% 正确”这个前提。传统软件中,一个函数要么正确要么有 bug;AI 应用中,一个 Agent 可能在 95% 的情况下表现良好,5% 的情况下出现各种奇怪行为。
工程手段的目标不是消除这 5%,而是让这 5% 可预测、可恢复、可解释。通过在 Prompt 中强化“不知道”的声明、在工具调用中设置容错和降级、在多轮对话中维护显式状态、在系统层面建立全链路可观测,我们可以把不可控的概率问题,转化为可管理的工程问题。
六、结语
回顾过去两年在 AI Agent 工程化上的探索,最大的收获不是某个具体的技巧,而是一个认知框架:大模型不是“更聪明的传统软件组件”,它是一个全新的计算范式,需要一套与之匹配的工程方法论。
提示词工程解决的是“如何精确表达意图”的问题,工具调用解决的是“如何可靠连接现实”的问题,多轮对话逻辑解决的是“如何维持任务连续性”的问题。三者共同构成了 AI Agent 从 Demo 走向生产的基本工程能力。
模型本身会持续进化,今天需要复杂 Prompt 才能完成的任务,明天可能一行指令就够了。但工程控制体系的价值不会消失——只要大模型仍然是概率性、无状态、注意力有限的,我们就需要工程手段来驾驭它。