我给Agent装了「三明治」架构——90%的「忘记查知识库」问题被代码消灭了

现状之痛:你给Agent配了知识库、写了SOP、建了技能包。但它执行任务时,经常跳过知识库自己瞎猜,或者不按SOP走流程。
读完你将:学会「三明治」架构——用确定性代码「包抄」概率性LLM,一步到位解决Agent流程失控问题。

一、问题:你的「确定性」建在了「概率」的沙滩上

你给Agent配了一套东西:

  • SOP → 写到System Prompt里了
  • 知识库 → 做成了Tool,让Agent自己决定要不要查
  • 技能包 → 挂在Agent边上

但执行起来,Agent经常「忘记」查知识库、「不遵守」SOP流程。

根因不是Agent笨,是你的架构错了。

LLM本质是一个概率模型。你把「是否检索知识库」这个选择权交给了一个概率模型——它在足够多次的调用中,总会因为注意力漂移、上下文过长、或者「以为」自己知道答案,而跳过检索步骤。

这不是Agent的错。是你把「确定性流程」寄托在了「概率性自觉」上。


![三明治架构图](/tmp/wechat-series/diagrams/sandwich-architecture.png)

*▲「三明治」架构:确定性代码在上层和下层包夹中间的LLM推理层*


二、解药:「三明治」架构——确定性代码包抄概率LLM

全球业界在2024-2026年达成了一个核心共识(Anthropic、LangChain、OpenAI、Google的实践者反复强调):

对于流程固定的业务场景,不要做一个「什么都自己决定」的全自主Agent。要做一个由确定性代码驱动的、仅在认知节点调用LLM的系统。

这就是「三明治」架构:用两层确定性代码包夹中间一层概率性LLM。

上层:前馈层(Pre-hook)——不让Agent做选择题

错误做法:把知识库查询做成了一个Tool,让Agent在Loop里自己决定要不要调。

正确做法:在任务进入Agent之前,代码强制检索知识库,把结果直接注入System Prompt。

def prepare_context(scene_id, task_data):
    # 1. 强制拉取SOP
    sop = load_sop(scene_id)

    # 2. 强制检索知识库
    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. 拼装System Prompt
    system_prompt = f"""
【强制SOP - 必须严格遵循】
{sop}

【已注入的最新知识 - 禁止自行猜测】
{kb_text}

工作守则:
1. 你无需再检索任何内容。最新最准确的知识已注入。
2. 严格按照SOP步骤执行。
3. 如知识不足以回答,输出 NEED_HUMAN_INTERVENTION。
"""
    return system_prompt

✅ 验证:这段代码我每天都在用。Agent一睁眼,SOP和知识已经摆在面前,它根本没有「要不要查」的选择。

踩坑:最开始的版本我把知识库做成Tool让Agent自己调。第一周没问题,第二周开始Agent跳过检索的次数越来越多。改为强制注入后,问题彻底消失。

价值:把「是否检索」从选择题变成必选项。100%执行率,不是99%。

认知跃迁:商业化的第一原则——不要把选择权交给概率。

中层:推理层(Agent)——只做推理,不做决策

这层就是你现在的Hermes 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),  # 只给该场景必需的工具
        temperature=0.1,                   # 推低,降低飘忽
    )

    return agent.run(task_data["content"])

三个关键限制:

  1. 温度0.1:商业化场景下temperature必须是0.1或更低。0.7是写诗用的,不是处理业务用的。
  2. 工具有白名单:每个场景只挂载该场景必需的工具。场景A不需要 search_web,就物理移除。
  3. 最大轮次:设置硬性上限,防止Loop无限空转。

下层:反馈层(Post-hook)——不做完别想走

Agent输出完不等于任务结束。代码层检查输出是否合规:

def validate_and_retry(scene_id, task_data, max_retries=3):
    for attempt in range(max_retries):
        # 运行Agent
        response = run_agent(scene_id, task_data)
        logs = response.tool_logs

        # 校验:是否调了必须的工具?
        if "search_database" not in [log.tool for log in logs]:
            task_data["content"] += "\n\n【校验报错】你未执行数据库查询步骤,请重新执行。"
            continue

        # 校验:输出是否包含合规声明?
        if "合规条款" not in response.text:
            task_data["content"] += "\n\n【校验报错】输出未包含SOP要求的合规条款。"
            continue

        # 全部通过
        return response.text

    raise Exception(f"Agent在{scene_id}场景中连续{max_retries}次违反SOP,转人工")

✅ 验证:这是我系统的 handoff_to_yuanbao.py 的逻辑变体。每篇发出去的文章都经过了这道门。

踩坑:早期的版本Agent「看起来」执行完了,但跳过了关键步骤。只有加验证层之后,才知道它有没有真的按SOP走。

价值:失败→重试→转人工,三级兜底。不完美不出门。

认知跃迁:输出校验,比输出本身更重要。


三、额外三招:让这套架构更稳

招数1:每任务独立Session

每个定时任务启动时,必须开新的Agent Session,清空历史Memory。

# 错误做法:累加历史上下文
agent.run(task_1)
agent.run(task_2)  # ← task_1的上下文污染了task_2

# 正确做法:每任务全新Session
def run_task(task_data):
    agent = fresh_agent()  # 全新Session
    return agent.run(task_data)

跨任务需要继承的状态,写成JSON文件存盘,由代码读取。不要依赖Agent的记忆。

招数2:场景隔离

不要搞一个「全能超级Agent」处理所有场景。

错误做法:
  一个Agent + 10个场景的SOP + 20个工具

正确做法:
  Router → 判定场景 → 分发到该场景的专有Agent
  专有Agent只带该场景的SOP + 最少工具

招数3:建评估集

20个典型任务就够。每次改动后跑一遍,看看Agent是否还遵守SOP。这不难——就是 evals_writing.py 的同样思路。


四、此刻的你

此刻的你,已经不再是那个「反复调prompt、给Agent加工具、期待它自觉遵守SOP」的调试者。你正在成为一个能用确定性的代码包抄概率性LLM的系统设计者。

知识库不是让Agent自己查的——是代码替它查好、喂到嘴边的。 SOP不是让Agent自己读的——是代码检查它有没有执行的。 Loop不是让Agent自由探索的——是代码确保它在正确轨道上反复校验的。

记住了:90%的遗忘问题,靠架构消灭,不靠模型自觉。


️ 实体:Hermes Agent, Harness Engineering, Loop Engineering 价值:Agent商业化, SOP流程固化, 确定性架构 认知:从调Prompt到搭三明治——确定性代码包抄概率LLM

posted @ 2026-08-05 21:16  魏无记  阅读(2)  评论(0)    收藏  举报