提示工程还是上下文、资源管理、循环工程?通过分层方式调试人工智能智能体
提示工程、上下文工程、资源管理工程和循环工程各自都会以不同的方式出错。基于追踪的调试流程能够找出出问题的环节。
当人工智能代理在实际应用中表现异常时,团队往往争论的是术语而非证据:有的工程师认为需要更好的提示词,有的则归咎于上下文,还有的指向框架问题。提示词设计、上下文管理、框架使用以及循环工程并非相互对立的理念,而是层层叠加的四个部分,每一部分都会以各自特有的方式出错。本指南通过简单的编程代理示例说明每一层的作用,随后提供一种以追踪为优先的流程,帮助你在修改任何代码之前确定究竟是哪一层出了问题。
人工智能代理的四个层次简述
- 提示词工程负责确定传递给模型的指令。
- 上下文工程决定模型在回答问题之前能看到什么内容。
大多数代价高昂的智能体故障都是因为修复了这些层面中的错误部分所致。
演示环境正常而生产环境出问题
这种情况很常见:智能体在演示环境中表现完美,但上线几天后就开始陷入循环、消耗代币,忘记一小时前被告知的某些信息,或者一旦工具返回意外数据就会崩溃。
团队分成了几派。有人提议重写提示词,有人认为这是上下文问题,还有人怀疑是框架的问题。由于没有统一的故障定位方法,原本两天就能解决的故障反而需要两周时间来重写代码,因为每个补丁都应用在原本运行正常的层上。
这就是将这四个概念区分开来的实际原因。每个概念都对应着不同的故障发生点,一旦能够区分它们,智能体调试就会变成一种有章可循的过程,而非猜测游戏。
PACT:四个层的助记符
在处理故障时,一种便于记住这些层的简洁方法就是PACT:提示词、感知、控制、轨迹。每个单词都对应一个问题:
- 提示词:任务是否被明确指定?
- 感知:模型是否获得了执行当前步骤所需的信息?
目标并非增加更多专业术语,而是让不同故障类型之间的界限清晰到足以在出现问题时立即被想起并加以运用。
这些层次是如何形成的,以及顺序为何重要
随着各种能力的发展,这些术语也大致按顺序出现:
- 提示词工程(大约2022年至2024年)是最初的技能:通过措辞、示例、约束条件以及少量样本模式来优化单次模型调用。
- 上下文工程(大约2024年至2025年)在智能体需要同时处理检索到的文档、对话历史以及工具定义时开始受到重视。人们关注的焦点从如何表述请求转变为模型实际能看到什么。Anthropic、LangChain和Chroma推动了这一理念在从业者中的普及,而Andrej Karpathy的相关论述则帮助将其纳入更广泛的工程讨论范畴。
- 资源利用工程(2026年初)在智能体获得文件系统访问权限、能够执行shell命令且任务运行时间可达数小时之后成为研究重点。人们开始关注智能体的功能范围以及工具调用失败后的行为表现。OpenAI在Codex方面的研究使得这一议题在编程智能体领域变得尤为重要;其关于结合Codex进行资源利用工程的论述是极具参考价值的核心资料。
- 循环工程(2026年中)是最新提出的概念。即便有了相应的框架,仍需确定每次迭代的具体操作以及何时停止:观察、行动、验证、重复还是终止。如果您需要更详细的解释,IBM提供了关于循环工程的说明文档。
这些日期指的是相关术语开始被广泛使用的时刻,并非正式的里程碑,且在撰写本文时这些术语仍在不断发展中。需要强调的是,并没有什么被取代——每一层都是建立在之前层之上的,因为每增加一点自主性就需要相应的控制机制。
提示词工程:局部契约
提示工程是一门通过精心措辞、构建结构及加以说明来让指令更易于产生可预测结果的技艺。没有它,指令就会模糊不清,输出格式也会混乱,模型不得不猜测你的真实意图。
在编程智能体中,代码审查提示可能如下所示:
SYSTEM: You are a code reviewer.
Given a diff, output ONLY valid JSON:
{"issues": [{"line": int,
"severity": "low|medium|high", "note": str}]}
No prose. No markdown fences.
If there are no issues, return {"issues": []}.
该提示语可设定本地契约。它指定角色、描述转换内容(输入差异与问题列表)、明确输出结构直至字段名称及允许的严重程度值,甚至定义空值情况,从而确保后续解析无需处理冗长文本或缺失键值的问题。
有一种常见观点认为提示工程已经过时。事实并非如此,它才是最核心的部分。每个上下文处理流程最终都会向模型传递指令,而即便指令本身较弱,若置于完善的框架之中,依然只能产生较差的结果,只不过现在有了出色的基础设施作为支撑。
上下文工程:在预算限制下的精选处理
上下文工程的任务是为每次调用决定哪些文档、历史记录、工具定义和记忆内容会被展示在界面中,同时也有意识地决定哪些内容不展示。如果忽视这一环节,模型就会依据过时的信息作答,被无关的检索内容带偏方向,或者因为界面中塞满了以防万一而加入的内容而失去重点。
编程智能体的上下文构建器可以检索候选内容、对它们重新排序,并压缩历史记录:
def build_context(query, full_history, kb):
relevant = retrieve(query, kb, top_k=8)
reranked = rerank(relevant, query)[:3]
summary = summarize_if_long(
full_history, max_tokens=800
)
return {
"docs": reranked,
"history": summary,
"query": query,
}
可以将其视为一个漏斗结构。检索阶段会抛出八个候选项,再排序阶段会保留最相关的三个,而当对话历史过长时,才会将其总结为大约800个标记。该函数返回的是结构化的小数据包,而非原始数据。这里所体现的能力是筛选、排序和压缩,而非处理海量数据。
这就是为什么最常见的误解——认为上下文工程就是向模型提供更多信息——往往是错误的。过大的上下文往往包含更多噪声:过时的指令、重复的事实以及相互矛盾的依据。大部分工作其实在于决定哪些内容应该舍弃。
上下文工程的范围比RAG更广
检索增强生成是上下文工程中的一种技术,涉及检索环节。如上所述,一个RAG流程可能会提取八个信息块,再将其重新排序为三个。上下文工程还包括压缩历史记录、格式化工具定义、保持关键任务状态、将验证结果反馈给模型,以及决定哪些内容可以省略。
即便没有向量存储的编码代理,依然面临着实际的上下文处理问题。Git状态、打开的文件、编译错误、测试结果、当前计划以及之前操作的日志,都需要以可用的形式传递给模型。
利用工程手段:意图与效果之间的界限
该框架是系统中与模型无关的部分:包括工具与文件访问、持久化内存、权限规则、沙箱环境、追踪功能,以及出现故障时的处理机制。如果没有一个良好的框架,要么得到的代理虽然能够进行合理推理却无法付诸行动,要么虽然能够执行操作,但在工具调用出错时却没有可靠的恢复方式。
一个工具调用封装层可以很好地体现这一概念:
def call_tool(tool_name, args, retries=2):
for attempt in range(retries + 1):
try:
result = TOOLS[tool_name](**args)
log_trace(tool_name, args, result, status="ok")
return result
except ToolError as e:
log_trace(tool_name, args, str(e), status="failed")
if attempt == retries:
return {"error": str(e), "recoverable": False}
args = repair_args(args, e)
该封装层并不试图直接解决问题,而是负责界定模型所请求的操作与实际系统执行内容之间的边界。每一次尝试都会被记录下其参数、结果及状态。一旦发生失败,系统会使用修正后的参数进行有限次重试;当所有重试都失败后,它会返回一个被标记为无法恢复的结构化错误信息,这样调用方就能获得可用于进一步处理的数据,而非导致程序终止的异常。
人们很容易将这个封装层与自己安装的任何代理框架相混淆。框架只是提供基础结构,而封装层则是你在其之上做出的具体决策:哪些内容需要被记录、失败调用之后应如何处理、崩溃后哪些状态仍能保留、允许使用哪些命令,以及如何将执行结果反馈给模型。
循环设计:终止契约
循环设计决定了每次迭代的具体工作内容、智能体判断自身是否取得进展的方式,以及最重要的——停止运行的条件。其典型的失败模式就是无限循环:智能体不断调用工具并消耗代币,因为从未有机制告知它任务已完成或已陷入僵局。
一个最简单的控制器结构如下:
def run_loop(task, harness, max_iters=15, stall_limit=3):
state = init_state(task)
stalls = 0
for i in range(max_iters):
action = plan_next_step(state)
result = harness.call_tool(action.tool, action.args)
state = update_state(state, result)
if is_goal_met(state):
return state, "success"
if made_no_progress(state):
stalls += 1
if stalls >= stall_limit:
return state, "stalled — escalate"
else:
stalls = 0
return state, "max iterations reached"
此处设置了多层限制。max_iters限制了总迭代次数,is_goal_met用于判断是否达成目标从而终止流程,而停滞计数器则记录连续无进展的迭代次数,并在恢复进展时重置。当出现stall_limit次停滞后,循环会返回一个特殊状态,要求上级介入处理而非继续默默运行。每条退出路径都会返回明确的理由,便于事后对运行情况进行分析分类。
其核心理念是终止契约:明确的目标、可量化的进展指标、有限的重试次数、时间和资源预算、验证步骤,以及针对进展停滞时的处理策略。如需了解相同理念在TypeScript中的实现,可参阅用于LLM工具开发的受限智能体循环相关文章。
循环机制与工具集架构常常被混为一谈。工具集决定了智能体可以操作什么内容,以及如何在各个工具层面处理故障。而循环机制则位于其之上,负责决定逐步骤的行为,包括整个执行过程何时应当结束。
四层结构并列展示
- 提示词(P):负责设定指令。常见故障包括模型误解意图或返回错误格式。首要检查点为输出结果本身。
- 上下文(A):负责维护模型可处理的状态。常见故障为模型基于过时、缺失或干扰性信息作出反应。首要检查点为每轮的上下文快照。
- 工具集(C):负责执行与故障恢复。常见故障包括工具调用出错、盲目重试或无法恢复。首要检查点为工具调用记录中的失败项。
区分工具故障与循环故障
最能节省时间的观察方法是:两种不同的故障从外部看可能完全相同。由于控制器问题而陷入循环的智能体,以及由于工具处理故障而停滞的智能体,都会表现出相同的症状:持续运行、费用不断上升,且没人知道原因。通过一个简短的检查清单即可将它们与其他类型的故障区分开来。
1. 查看工具调用轨迹
如果所有调用都成功且结果合理,而智能体却使用几乎不变的参数不断调用同一个工具,那就说明存在循环问题。
2. 检查工具调用是否反复失败
如果某个工具屡次出错,而智能体仍不改变处理方式反复尝试,那就该检查harness部分。
3. 检查模型能看到什么
如果模型在运行过程中似乎忘记了某些事实,就需要检查上下文构建:包括内容截断、替换、压缩,以及状态在各轮次之间的传递方式。
4. 最后检查输出结果
如果工具运行正常、上下文准确且循环也正常结束,但答案依然错误,那就需要查看提示词。
一个经验法则
循环错误通常出在关于下一步该做什么的决策上;harness错误则出在出现问题时的处理方式上;上下文错误与模型能看到的内容有关;提示词错误则源于你所提出的要求。
在修改任何内容之前,先确定这四个问题中哪一个是原因。
案例研究:一个停不下来的拉取请求审核工具
想象一个团队正在部署一个用于审核拉取请求的编程智能体。测试进行得很顺利。但在实际使用中,某个拉取请求的审核时间会超过40分钟,从而导致意外的费用产生。
第一种推测是提示词过于模糊,导致智能体过度思考。于是重新编写了提示词,但问题依旧存在。第二种推测与上下文有关:也许智能体在每一步都会重新读取整个代码库。但追踪记录显示并非如此——数据检索的范围是恰当的,只有相关的文件会被加载进来。
接着有人仔细查看了工具调用日志。该代理反复调用其测试运行工具,每次调用都无错误完成。实际上测试套件并未失败,问题出在某个与拉取请求无关的偶发故障集成测试上。由于没有机制提示它已对该子目标进行了足够多的尝试,应当停止并上报问题,因此该代理不断尝试通过无关的代码修改和重新运行测试来解决故障。
这不是提示问题,也不是上下文问题,更不是框架问题;这些工具的表现完全符合设计要求。这纯粹是一个循环错误。逐步骤分析就能得出这一结论。
第一步:确认工具正常工作
日志中的成功执行记录排除了最简单的框架故障,比如命令从未运行或适配器持续抛出异常的情况。
步骤2:比较各迭代间的状态
测试结果存在于智能体的上下文中,且检索范围被正确限定,因此模型不会遗漏相关证据。
步骤3:检查是否收敛
每次迭代时代码库都会发生变化,但关键的验证指标却始终没有改善。该系统既没有停滞检测机制,也没有对同一子目标的尝试次数设置上限。
步骤4:让控制器学会检测停滞状态
解决方案是在控制器逻辑中加入一小段代码,用于比较各步骤之间的进展特征:
if progress_signature == previous_signature:
stagnant_steps += 1
else:
stagnant_steps = 0
if stagnant_steps >= 3:
escalate("no measurable progress")
stop()
progress_signature 可以是任何能够体现实际进展的内容,例如失败的测试项及其错误信息。如果该值没有变化,停滞计数器就会增加;当出现三次停滞后,控制器会带着原因升级并停止运行。任何实质性的变化都会重置该计数器。
你还可以进一步限制针对每个失败特征的尝试次数,而不仅仅是总迭代次数。这种区分很重要:十五次有效的迭代可能完全没问题,但针对一个无法修复的测试失败进行十五次尝试则纯粹是浪费。真正的解决办法不是让模型变得不那么固执,而是为控制器提供停滞的明确定义。
案例研究:从上下文中消失的约束
现在想象这样一个智能体:在迁移开始时,它确定绝不能破坏现有的客户端。四十轮之后,对话内容被压缩,而该约束条件却在摘要中丢失了。于是这个智能体提出了一个可能破坏现有系统的架构变更方案。
这看似是错误的推理,但问题出在别的地方。对比模型在不同轮次实际接收到的上下文即可。如果该约束条件在第五轮存在,却在第四十轮消失了,那么解决办法就在于上下文设计:将关键不变量与对话内容分开存储,让摘要明确保留约束条件,并且不再将原始历史记录视为唯一的真实来源。
“模型忘记了”从来都不是完整的诊断结果。真正有意义的问题是模型实际上获得了什么信息。
分层优化,而非全面替换
每一项新兴技术都是在前人基础上的发展,而非取代它们。一个任务执行代理会将清晰的提示语包裹在精心挑选的上下文中,通过可靠的执行框架运行它,并通过有意识的循环来控制整个流程。这些概念的演变反映了团队随着时间推移所需添加的内容:更明确的指令,随后是更优质的信息来源,接着是模型的运行环境,最后则是能够让该环境无人值守持续运行的控制器。如果您正在考虑是由自己的运行时系统还是框架来负责这个外部循环,运行时框架与组合式框架的对比可以帮您了解其中的权衡。
用PACT框架重新阐述:
- 提示语:定义指令内容。
然后将解决方案与故障对应起来。如果模型误解了明确指定的任务,则需要针对性优化;如果模型缺少其依赖的事实,则需要补充上下文信息;如果合理的决策未能转化为可靠的行动,则需要完善机制;如果整体运行从未收敛但某些行动却成功了,则需要优化循环逻辑。
关于智能体层的常见问题
“机制工程”不就是智能体框架的另一种叫法吗?
不是的。框架提供的是构建模块,而“工具包”则包含你在组装这些模块时所做的决策:工具故障时的处理方式、状态持久化、日志记录、权限设置,以及如何将结果反馈给模型。
单问题助手需要循环设计吗?
其实不需要。只有当智能体在每个任务中需要执行多个操作,并且必须自行或通过控制器代码来判断任务是否完成时,循环设计才变得重要。单轮问答式的机器人几乎不需要或完全不需要设计循环。
你应该先学习哪一层?
先从提示词与上下文设计开始,然后再学习工具包和循环设计。在调试运行时逻辑和控制器之前,你得先能区分糟糕的指令和缺失的状态。
一个错误会涉及多个层次吗?
是的,这类问题往往最难解决。上下文错误可能会使循环无法接收到进度信号,从而导致问题看起来像循环错误。应当深入追踪问题的根源,而非仅仅修补最初显现的症状。
处理下一次智能体故障的检查清单
- 这四个术语描述的是控制机制,而非相互冲突的趋势:指令、模型就绪状态、执行与恢复,以及带有停止规则的重复进度检测。
- 所有调查都应从追踪日志开始,而非提示信息:需了解模型看到了什么、运行时做了什么、各迭代之间发生了什么变化,以及控制器为何选择继续运行。
- 使用几乎完全相同的参数反复成功调用工具,说明存在循环问题;而盲目重试却不断失败,则表明问题出在集成框架上。