【0-1的agent进阶篇】Prompt 与上下文工程

🔥个人主页:代码不加冰(欢迎来访)

🎬作者简介:java后端学习者

❄️个人专栏:LeetCode刷题日记 ,苍穹外卖日记SSM框架深入JavaWeb

命运的结局尽可永在,不屈的挑战却不可须臾或缺!


引言

在大多数后端工程师的认知里,Prompt 是一段自然语言,写得好不好全凭语感。这种理解在做单轮问答、简单摘要之类的任务时基本够用,但一旦系统演进为 Agent ------ 需要调用工具、维护多轮状态、产出结构化结果、并在信息不全时做出合理判断 ------ 这套凭感觉写的方法就会迅速失效。

问题的根源在于:Prompt 从来不是一段静态文本,它是 Agent 系统运行时的协议。协议这个词很关键------协议意味着有明确的输入输出契约、有版本、有测试、有回归 。如果把 Prompt 仅仅当作接口说明文档,团队会在复杂任务里反复撞到同一堵墙:输出不稳定、模型在边界不清时自行脑补。而真正能解决这堵墙的,不是找到某个更好的措辞,而是建立一套系统性的上下文工程方法论。

本文尝试把这套方法论讲清楚:Prompt 作为协议究竟要定义什么,MCP又是什么协议、上下文工程包含哪些组成部分、常见的 Agent 质量问题根因在哪里,以及如何用工程化的手段(而不是直觉)去迭代和验证 Prompt。


一、Prompt 作为运行时协议:它必须定义的四件事

把 Prompt 当作协议来看待,意味着它在运行时必须清晰回答四个问题。任何一个问题回答得模糊,都会在下游放大成不可控的行为。

1. 角色(Role):模型此刻是谁

同一个模型,在不同任务阶段可能需要扮演完全不同的角色------有时是"严格按schema 输出的解析器",有时是"需要多步推理的规划者",有时是"只能引用检索结果、不能自由发挥的问答助手"。角色定义模糊,模型就会在"该有多大自由度"这件事上自行判断,而这种判断往往和工程期望不一致。

2. 信息边界(Scope):允许使用哪些信息

模型可以使用的信息来源通常包括:用户输入、系统规则、工具返回结果、历史对话状态、检索片段。

Prompt 必须明确划定边界------哪些信息是权威的(比如检索结果),哪些只是参考(比如历史对话中用户的猜测),哪些是绝对不能超越的约束(比如系统规则)。边界不清晰的典型后果,就是模型把用户一句模糊的话当成了事实依据,或者把工具返回的过期数据当成了当前状态。

3. 输出契约(Contract):结果必须满足什么结构

这是最容易被后端工程师理解、但也最容易被做得不彻底的一环。

输出契约不只是"返回 JSON",还包括:字段是否必填、枚举值范围、错误信息的格式、以及当任务无法完成时应该返回什么样的"失败态"结构,而不是让模型在自由文本里解释。契约不完整,程序侧就要额外写大量"猜测式"的解析兜底逻辑,这本质上是把 Prompt 该承担的工程责任转嫁给了下游代码。

4. 失败协议(Failure Mode):不确定时如何失败

这是最常被忽略、却最能决定系统可靠性的一环。几乎所有 Prompt 工程的问题------模型编造不存在的字段、编造工具没有返回的数据、在权限不足时假装完成了任务------根源于 Prompt 没有明确规定"遇到不确定该怎么办"。一个成熟的 Prompt 协议,必须像设计 API 的错误码一样,为"信息不足""权限不足""工具调用失败"这几类典型失败场景,分别规定清晰、可被程序识别的输出方式。


补充:

MCP 是什么层面的协议

MCP(Model Context Protocol)是 Anthropic 提出的一套客户端---服务端接口标准,基于 JSON-RPC 2.0。它标准化的是这样一件事:模型/宿主应用怎么去发现、连接、调用外部的工具和数据源。

MCP 服务端对外暴露三种核心原语:

  • Tools :模型可以调用的、有副作用的函数(查数据库、发消息、跑代码),带 JSON Schema 定义的输入输出,还有 、 这类标注。readOnlyHint``destructiveHint
  • Resources :只读数据(文件、DB 记录、文档),供模型读取作为上下文,可以带 、、 这类元数据。audience``priority``lastModified
  • Prompts:服务端预先写好的、可复用的提示词模板,供客户端在特定工作流里直接调用。

它解决的核心痛点是工程上的 M×N 问题:以前 M 个应用要各自对接 N 个工具,需要写 M×N 套胶水代码;有了 MCP,工具方只需按协议实现一次服务端,任何兼容 MCP 的客户端都能直接接入,变成 M+N。这是一个传输/接口层 的协议------它规定的是"怎么把工具和数据递给模型",不规定"模型该怎么用这些东西做决策"。

和之前讲的 Prompt 协议是什么关系

之前文章里说 Prompt 是"运行时协议",讲的是语义/行为层的东西:角色是什么、信息边界在哪、输出契约是什么、不确定时怎么失败。这一层无论工具是不是走 MCP 接入,都必须由工程师自己设计------MCP 不会替你回答这些问题。

但两者在实际系统里是紧密咬合的,具体体现在几个点上:

1. MCP 的 Tool annotations 呼应了"工具描述要区分读写风险"这个问题。 之前提到,工具说明如果不区分只读和有副作用,模型容易把高风险的写操作当普通查询调用。MCP 规范里专门加了 / 这类标注来解决这件事------但规范同时明确写了:这些标注来自服务端,客户端不能仅凭这个标注做安全决策 ,因为它本质上是"未经信任的提示"。也就是说,协议提供了标准化的风险标注格式,但真正的风险判断和人工确认逻辑,还是要工程侧自己在上下文装配和交互设计里做,协议本身不能替你兜底

2. MCP 的 Resource annotations 呼应了检索片段缺少来源这个问题。MCP 给 Resource 定义了 、 这些元数据字段,理论上能让模型判断一条信息的新鲜度和权威性。但现实是,很多客户端目前只实现了基础的 list/read,没有完整利用这些标注------协议定义了理想状态,生态实现经常滞后。这提醒工程师:不能因为"我用了 MCP"就默认来源问题已经解决,仍然要在上下文工程层面检查这些元数据有没有真的被组织进最终喂给模型的内容里。

3. MCP 里也有个叫 "Prompts" 的原语,但和"Prompt 作为协议"是两回事,容易混淆。MCP 的 Prompts 指的是服务端预置的、参数化的提示词模板(比如"周报生成""文档格式化"),是给特定工作流复用的固定套路,用户或客户端主动选择调用。而我们之前讲的"Prompt 协议"是更宏观的概念------覆盖整个上下文窗口里所有内容(系统规则、工具描述、检索结果、历史状态等)该如何组织和约束模型行为。MCP 的 Prompts 原语只是这个大概念里的一小块可复用组件。

简单归纳

可以这样理解分工:

  • MCP 解决"连接"和"发现"问题------怎么用标准化接口把工具、数据、模板递给模型,让集成可复用、可维护。
  • 上下文工程解决"组织"和"决策"问题------这些递过来的材料,最终该按什么顺序、什么权重塞进上下文窗口,模型该扮演什么角色、边界在哪、输出契约是什么、不确定时怎么失败。

换句话说,MCP 让材料供应标准化了,但不会自动帮你把材料装配好。即便所有工具都走 MCP 接入、所有标注都写得很规范,如果上下文装配方式还是老问题------规则写太后、示例和任务不匹配、没有区分权威信息和参考信息------一样会出现之前讲的模型行为漂移。这也是为什么,做 Agent 工程化,MCP 和上下文工程是两件都要做好的事,而不是有了 MCP 就不需要做上下文工程了。

二、上下文工程:把材料按正确顺序装配起来

如果说"角色---边界---契约---失败协议"是 Prompt 协议要回答的问题,那么上下文工程就是回答这些问题时所依赖的施工方法:把用户输入、系统规则、工具说明、历史状态、检索结果、输出 schema 这几类材料,按照正确的顺序和权重组织进有限的上下文窗口里,让模型在约束下做出更可控的决策。

这里有几个关键认知,值得单独展开:

上下文不是"信息越多越好"。 上下文窗口是有限资源,塞入过多低相关性内容,不仅浪费 token,还会稀释真正重要的约束------模型对长文本中段落的关注度并不均匀,越靠后写的强约束,反而可能被前面大段说明"稀释"掉权重。

顺序本身携带语义。 系统规则如果出现得太晚,模型在处理前半段任务描述时已经形成了行为倾向,后面追加的规则未必能完全纠正。这就是原始材料里提到的"规则写得太后"会导致漂移的原因------这不是模型"不听话",而是工程侧没有把优先级更高的约束放在优先级更高的位置。

示例是隐性的规则。 Few-shot 示例往往比文字说明更强地塑造模型行为,如果示例和当前任务的分布不一致(比如示例是简单场景,任务是边界场景),模型会倾向于模仿示例的浅层模式,而不是真正理解规则的意图。

工具说明需要区分读写风险。在 Agent 场景里,工具分为"只读查询"和"会产生副作用的写操作"两类。如果 Prompt 里对工具的描述没有明确标注这种风险等级,模型很容易把一个高风险的写操作当成普通查询去调用,这是 Agent 系统里最容易出安全事故的地方之一。

检索片段需要携带来源。 检索增强(RAG)场景下,如果检索片段只给内容不给来源、时间戳、可信度,模型在需要判断"这条信息是否权威、是否过期"时就没有依据,只能靠猜------而猜测正是幻觉的温床。

综合来看,很多 Agent 质量问题,根因并不在模型能力不足,而在于上下文装配方式错误:规则写得太后、示例与任务不一致、检索片段缺少来源、工具描述不区分读写风险,这几类问题各自独立,但共同的表现都是同一个症状------模型行为漂移,即在相似输入下给出不一致、不可预测的输出。


三、从调 Prompt到做回归测试

理解了 Prompt 是协议、上下文工程是施工方法之后,剩下的问题是:怎么迭代工程师面对这个问题时最容易掉入的陷阱,是把调 Prompt 当成"调文案"------改一句话,跑一次,看着顺眼就上线。这种方式在个人验证阶段可以接受,但完全不具备工程可持续性。

一个更接近工程实践的方法是:为同一任务准备 20 到 50 条有代表性的样本,把 Prompt 显式版本化,然后用固定的指标去比较不同版本的表现。这里的样本不是随便挑的正常案例,而应该刻意覆盖典型场景、边界场景和失败场景的组合,这样才能真正暴露出 Prompt 协议里的漏洞。

在这套回归测试里,值得重点跟踪的指标包括:

  • 成功率:任务是否真的完成,而不只是"回答看起来像对的"。这是最容易被误判的一项------一个语言流畅、格式规整的回答,完全可能在事实或逻辑上是错的,所以成功率的判定标准必须和任务的真实目标绑定,而不是和"回答的观感"绑定。
  • 格式通过率:返回的 JSON 或结构化文本能否直接被程序消费,不需要额外的人工修补或容错解析。这项指标本质上是在检验输出契约有没有真正被模型遵守。
  • 稳定性:同一个输入多次运行,结果波动是否可接受。Agent 系统一旦上线,同样的输入不可能只跑一次,如果稳定性差,线上表现就会呈现出"时好时坏"的随机性,这种随机性对下游系统的可靠性是致命的。
  • 拒答质量:在信息不足、权限不足、工具失败这几种典型场景下,模型是否能明确说明原因,而不是硬着头皮给出一个看似完整、实则编造的答案。这项指标直接对应前面提到的"失败协议"是否设计到位。
  • 成本:P rompt 变长、加入更多示例和约束之后,成功率的提升是否值得额外的 token 开销。这是很多团队会忽略的一项------把 Prompt 无限加长确实可能提升某个指标,但如果收益边际递减而成本线性增长,这就是一笔需要被量化权衡的工程决策,而不是"越详细越好"的直觉判断。

这五个指标共同构成了一套可以像接口回归测试、SQL 性能测试一样被反复运行、对比、追溯的评估体系。它的价值不在于给出一个绝对分数,而在于让每一次 Prompt 修改都变成一次可验证的变更,而不是一次凭感觉的"感觉这样应该更好"。


四、真正进入 Agent 工程化的标志

回到最初的问题:什么时候算是真正掌握了 Prompt 与上下文工程

答案不是能写出效果不错的 Prompt,而是当你能定位到是哪一段上下文、哪一个示例、哪一条输出约束导致了效果变化时。这句话背后的工程含义是:Prompt 的每个组成部分都应该是可以被单独修改、单独测试、单独归因的单元,而不是一整段互相耦合、改一处就要祈祷全局不出问题的自然语言块。

这也是为什么本文反复强调"协议"而不是"提示词"这个词------协议意味着结构化、可拆分、可测试;提示词容易让人误以为这是一门语感手艺。当团队把 Prompt 的维护方式,从"修改文案、观察感觉"转变为"修改结构化组件、跑回归指标、对比版本",Agent 系统的质量才会从"运气好坏"变成"工程可控"。

这不是一次性的调优动作,而是一个需要长期维护的习惯:不要凭感觉调 Prompt,要像调接口和 SQL 一样做回归测试。这句话值得写在每一个 Agent 项目的工程规范里。

相关推荐
aaPIXa6221 小时前
C++大型项目模块化拆分实战记录
开发语言·c++
z落落1 小时前
C# WinForm 线程池与事件等待+进度条暂停恢复实战案例
开发语言·c#
石山代码1 小时前
C++23 新特性在 CLion 中的实战体验
开发语言·c++·c++23
浮江雾1 小时前
Flutter第四节------核心概念学习笔记:从基础语法到异步编程
linux·服务器·开发语言·笔记·flutter·入门·基础
KINGSEA_1681 小时前
AgentScope Java 2.0 架构解析
java
北冥you鱼1 小时前
Go Modules 使用指南:从入门到精通
开发语言·后端·golang
l1t1 小时前
duckdb 1.6dev新增的.manual命令
开发语言·数据库
你怎么知道我是队长2 小时前
JavaScript的函数介绍
开发语言·前端·javascript
YMWM_2 小时前
python-venv虚拟环境
linux·开发语言·python