通过 Pre/Post-hook 三层架构强制 Agent 遵循知识库和 SOP,提供完整 Python 实现和 20 案例评估集,解决生产环境中 Agent 忽视指引的常见问题。
痛点——你给 Agent 配了知识库,编写了 SOP,还构建了一套 Skill 包。然而真正执行时,它却跳过知识库,凭空猜测,或者完全无视 SOP 流程。你越是调优 prompt、添加工具,它就越容易“忘记”。
为什么“让 Agent 自己决定是否检索”是一种设计缺陷,而不是行为上的小毛病?
三明治架构:用两个确定性层(Pre-hook 和 Post-hook)包裹一个概率性的 LLM 层。
可直接运行的 Python 代码:强制注入上下文、约束推理,并通过重试机制验证输出。
三个额外的加固手段:每个任务使用全新 Session、场景隔离,以及一套包含 20 个案例的 eval 集。
这个问题实在太常见了,值得专门写一篇文章。如果你曾经上线过一个配有知识库、标准作业流程和若干工具的 Agent,却只能眼睁睁看着它自由发挥,那么这篇文章就是为你准备的。
通常,你会给 Agent 配置这些东西:
SOP → 写进 System Prompt
知识库 → 作为 Tool 暴露,让 Agent 自己决定是否查询
Skill 包 → 挂载在 Agent 旁边
但真正运行起来后,Agent 却“忘记”查询知识库,也“没有遵循” SOP。是不是很熟悉?
根本原因不是 Agent 太笨,而是架构错了。
LLM 本质上是一种概率模型。当你把“我是否应该检索知识库?”这个决定交给概率模型时,实际上是在下注:无论调用多少次,它都不会发生注意力漂移、上下文溢出,也不会产生“这个我已经知道了”的错觉,从而跳过检索。
但它迟早会跳过。不是因为它坏了,而是因为概率采样在分布尾部本来就会产生这样的结果。
这不是 Agent 的错。是你把一个确定性流程,建立在了概率模型的自律之上。
三明治架构:在概率性的 LLM 两侧,用确定性代码将其包裹起来——全部诀窍就在这里。

从 2024 年到 2026 年,整个行业逐渐形成了一项核心共识。Anthropic、LangChain、OpenAI 和 Google 的从业者,都在反复强调同一个观点:
对于流程固定的业务场景,不要构建一个可以自主决定一切的全自治 Agent。应该构建一个由确定性代码驱动的系统,只在需要认知判断的节点调用 LLM。
这就是三明治架构:两层确定性代码,将一层概率性的 LLM 推理夹在中间。
+-----------------------------------------------------------------+
| PRE-HOOK | DETERMINISTIC CODE |
| load_sop . vector_db.search . inject into prompt |
+-----------------------------------------------------------------+
| LLM REASONING | PROBABILISTIC |
| Agent reasons only . temperature=0.1 . whitelist |
+-----------------------------------------------------------------+
| POST-HOOK | DETERMINISTIC CODE |
| validate tool calls . check output . retry |
+-----------------------------------------------------------------+
Agent 必须掌握的所有信息,都由代码在它开始思考之前注入。Agent 产生的所有内容,都由代码在它输出之后检查。留给概率模型的,只有夹在中间的推理过程——这才是概率真正能够发挥价值的地方。
错误做法:把知识库作为 Tool 暴露出来,让 Agent 在自己的循环中决定是否调用。这样一来,“是否检索”就成了二十个选项中的一个——而它很可能落选。
正确做法:在任务到达 Agent 之前,由代码强制检索知识库,并将结果直接注入 System Prompt。Agent 根本没有选择的机会:
def prepare_context(scene_id, task_data):
# 1. always fetch the SOP for this scene
sop = load_sop(scene_id)
# 2. always search the knowledge base
docs = vector_db.search(
query=task_data["summary"],
top_k=3,
filter={"scene": scene_id}
)
kb_text = "\n".join(d.page_content for d in docs)
# 3. assemble the System Prompt
system_prompt = f"""
[MANDATORY SOP - must be followed strictly] {sop}
[INJECTED LATEST KNOWLEDGE - do not guess on your own] {kb_text}
Working rules:
1. You do not need to retrieve anything. The latest and most accurate knowledge has been injected.
2. Execute strictly according to the SOP steps.
3. If the injected knowledge is insufficient to answer, output NEED_HUMAN_INTERVENTION.
"""
return system_prompt
✅ 已在生产环境验证:我每天都在运行这段代码。Agent 一“睁开眼睛”,SOP 和知识就已经摆在了它面前。它不需要再做“我是否应该查询?”这个选择——在 Agent 出现之前,这个问题就已经被消除了。
🩸 踩坑经历:我的第一个版本把知识库作为 Tool 暴露出来,让 Agent 自行调用。第一周运行得很好。从第二周开始,Agent 跳过检索的频率越来越高。改为强制注入后,这个问题彻底消失了。
💼 价值:“是否检索”从一道选择题,变成了一个强制步骤。执行率是 100%,而不是 99%。
🧠 认知升级:Agent 商业化的第一原则——永远不要把选择权交给概率。
这一层就是你已经拥有的 Agent,不过要加上三个约束:
def run_agent(scene_id, task_data):
context = prepare_context(scene_id, task_data)
agent = HermesRunner(
system_prompt=context,
tools=get_scene_tools(scene_id), # only the tools this scene needs
temperature=0.1, # keep it low to reduce drift
)
return agent.run(task_data["content"])
Temperature 设为 0.1。在生产环境中,temperature 必须是 0.1 或更低。0.7 适合写诗,不适合处理业务任务。熵每增加一点,输出偏离 SOP 的可能性就多一分。
Tool 白名单。每个场景只挂载该场景真正需要的工具。如果场景 A 不需要 search_web,就从工具集中彻底移除它。选项越少,走错路的机会越少。
最大轮数。为循环设置一个严格的上限,避免 Agent 在重试过程中永远打转。
Agent 完成输出,并不代表任务已经完成。代码层还需要检查输出是否合规:
def validate_and_retry(scene_id, task_data, max_retries=3):
for attempt in range(max_retries):
# run the agent
response = run_agent(scene_id, task_data)
logs = response.tool_logs
# check 1: did it call the mandatory tool?
if "search_database" not in [log.tool for log in logs]:
task_data["content"] += "\n\n[VALIDATION ERROR] You skipped the database query step. Please re-run."
continue
# check 2: does the output contain the required compliance clause?
if "compliance clause" not in response.text:
task_data["content"] += "\n\n[VALIDATION ERROR] The output is missing the compliance clause required by the SOP."
continue
# all checks passed
return response.text
raise Exception(
f"Agent violated the SOP {max_retries} times in a row in scene {scene_id}. Escalating to a human."
)
Post-hook 关卡:验证工具调用 → 验证输出 → 重试(最多 3 次)→ 升级给人工处理。

✅ 已在生产环境验证:这就是我自己系统中 handoff_to_yuanbao.py 模块背后的逻辑。我发布的每一篇文章,在对外发出之前,都必须经过这道关卡。
🩸 踩坑经历:早期版本“看起来”已经完成了任务,实际上却跳过了关键步骤。直到加入验证层,我才真正知道 Agent 是否遵循了 SOP——仅凭最终输出,永远无法判断这一点。
💼 价值:失败 → 重试 → 人工升级,三层兜底。不完善的结果绝不能流出系统。
🧠 认知升级:输出验证比输出本身更重要。
改造前与改造后:从“Agent 决定是否查询知识库”,变成“代码在 Agent 运行之前,把知识库内容注入 prompt”。

每个定时任务都必须启动一个新的 Agent Session,并清空记忆。上一个任务的历史信息绝不能泄漏到下一个任务中。
错误做法——不断积累历史上下文:
agent.run(task_1)
agent.run(task_2) # task_1's context pollutes task_2
正确做法——每个任务使用一个全新的 Session:
# right way: a brand-new session per task
def run_task(task_data):
agent = fresh_agent() # brand-new session
return agent.run(task_data)
必须跨任务保留的状态,应写入磁盘上的 JSON 文件,再由代码读取回来。任何真正重要的信息,都不要依赖 Agent 的记忆来保存。
不要为所有场景构建一个“超级 Agent”。单个 Agent 的爆炸半径越大,它发生偏移的方式就越多。
错误做法:一个 Agent + 10 个场景的 SOP + 20 个工具。
Router -> detects the scene -> dispatches to the scene's dedicated Agent
Dedicated Agent carries only that scene's SOP + the minimum toolset
一个只配有三个工具的场景专用 Agent,最多会在三个地方出错,而不是二十个。限制故障范围本身就是一种能力。
二十个典型任务就足够了。每次修改 prompt、工具或 SOP 后,都运行一遍 eval 集,检查 Agent 是否仍然遵循 SOP。
这并不难——与我工具包中的 evals_writing.py 思路相同:准备一组固定输入、一份必需行为检查清单,并为每个案例给出通过或失败的判定。
你不再是那个不停调 prompt、不断给 Agent 加装工具,然后寄希望于它靠自律遵循 SOP 的调试者。你正在成为一名系统设计者:用确定性代码包裹概率性的 LLM。
知识库不是让 Agent 自己查询的——代码替 Agent 查询,然后直接把知识送到它嘴边。
SOP 不是让 Agent 自己阅读的——代码负责检查它是否真正执行。
Loop 不是让 Agent 自由游荡的——代码会让它在正确的轨道上不断重新检查。
请记住:90% 的“遗忘”问题,都应该靠架构解决,而不是依靠模型自律。
核心要点——实体:Hermes Agent、Harness Engineering、Loop Engineering。价值:Agent 商业化、SOP 流程加固、确定性架构。认知:从 prompt 调优转向三明治架构——用确定性代码包裹概率性的 LLM。
关于作者:吴记(Wu Ji)——AI 与数字化实践者,专注于 Agent 工程、Loop Engineering 和数字化转型。教程注重实践与动手操作——跟着做,就能运行起来。
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。