Agent Runtime 架构拆解:Prompt 如何变成可校验的执行链路

一套可上线的 Agent Runtime,关键不在模型,而在可追踪、可验证、可修复的执行系统。

很多人看 Agent,第一眼会盯着模型:参数多大、上下文多长、推理强不强。工程里真正麻烦的地方往往不在这里。用户只发出一句话,系统却要判断意图、补齐字段、选择执行模式、调用工具、整理证据、生成草稿、做校验,最后还要决定哪些信息值得沉淀。

一个可用的 ​Agent Runtime​,本质上是在模型外面加了一套执行系统。模型负责推理和表达,Runtime 负责把一次概率生成变成一条可追踪、可回放、能重试的链路。没有这层工程约束,模型很容易在复杂任务里变成“看起来会做事的聊天框”。

下面用一个简单任务贯穿全文:

帮我整理上周几个项目会议的结论,生成一份给团队看的周报。

这句话看起来不复杂。真要让系统自动完成,它至少要知道时间范围、项目范围、读者是谁、最终格式是什么,哪些字段可以自动查,哪些字段不能猜。Agent 的工作从这里开始。

Runtime 总图:模型只是一段执行链路里的一个节点

Agent 不是“LLM + 工具列表”。更准确的拆法,是把它看成一条运行时管线:前面把自然语言编译成任务结构,中间用上下文和工具驱动执行,后面用证据和校验决定能不能交付。

mermaid-01.png

图:Agent Runtime 从用户请求到校验收尾的完整执行链路

这张图把 Agent Runtime 拆成更适合工程讨论的视角:模型不是入口,也不是终点,它夹在编排、状态、证据和校验之间。

inline-01-runtime-contract.jpg

图:Prompt 进入 Runtime 后先被编译成可执行任务契约

Prompt Compiler​:先把一句话编译成任务契约

Agent 收到用户请求后,第一步不该急着回答。更稳的做法是先做 Prompt Compiler:把自然语言转成一个带字段、约束和风险标记的任务契约。

对“整理上周几个项目会议”这类任务来说,系统要抽出 task_typetime_rangeaudienceoutput_format,还要标出哪些字段没有明确给出。例如项目列表可以通过日历和文档发现,但 owner、deadline、项目状态不能随口补。

{
  "task_type": "weekly_report",
  "slots": {
    "time_range": "last_week",
    "projects": "discover_from_tools",
    "audience": "team",
    "output_format": "weekly_report"
  },
  "missing_slots": ["project_list"],
  "must_not_guess": ["owner", "deadline", "project_status"],
  "confidence": 0.82,
  "clarification": {
    "needed": false,
    "reason": "projects can be discovered from calendar and docs"
  }
}

这里最关键的是“能不能猜”。低风险字段可以从默认偏好或上下文里补;高风险字段必须查证,查不到就标待确认。很多 Agent 失败,不是模型不会写,而是入口阶段没有把“禁止编造”的字段圈出来。

机制 运行时职责 输出
输入清洗 处理指代、隐私字段、噪声文本,必要时做 query rewrite 更干净的用户请求
意图分类 判断当前是问答、写作、查询、操作还是混合任务 task_type
槽位填充 抽取时间、对象、格式、收件人、范围等关键字段 slots
缺口检测 按任务 schema 检查必填字段是否缺失 missing_slots
澄清策略 判断追问、自动补全、继续执行还是兜底 clarification

样例库在这一层也有用。系统可以把用户请求转成检索 query,找历史上相似任务的 task_type、槽位结构和路由结果,作为 few-shot 参考。这样模型不是凭感觉分类,而是在已有执行样例附近做判断。

Execution Router​:先选执行模式,再绑定 Skill 和 Tool

意图识别之后,Runtime 要做一次路由决策。这里不只是“选哪个工具”。它要判断当前任务应该直接回答、追问、走 ReAct、走 Plan-Execute,还是进入安全兜底。

简单事实问题可以直接回答。目标明确、步骤稳定的任务适合 Plan-Execute。需要边查边判断、下一步强依赖工具返回的任务,适合 ReAct。高风险或字段缺失严重的任务,应该先问清楚,不要用聊天模式硬跑。

{
  "decision": {
    "route": "use_skill_and_tools",
    "reasoning_mode": "plan_execute",
    "selected_skill": "weekly-report",
    "tool_candidates": ["calendar.search", "docs.search", "im.search"],
    "plan": [
      "find meetings from last week",
      "collect project evidence",
      "draft weekly report",
      "verify claims"
    ],
    "fallback": {
      "if_low_confidence": "ask_clarification",
      "if_tool_unavailable": "safe_execute_with_uncertainty"
    }
  }
}

ReAct 和 Plan-Execute 的差别,可以用下面这张图看清楚:
mermaid-02.png

图:ReAct 与 Plan-Execute 在执行节奏上的差异

工程上最好不要让模型在全量 Skill 和 Tool 里自由挑。先由 Runtime 根据任务类型、风险和依赖关系筛出候选,再把候选能力注入上下文。能力列表越大,越需要路由层,不能把“在一千个工具里选一个”的压力全甩给模型。

决策问题 更稳的处理方式
什么时候用 ReAct 下一步依赖工具观察结果,或者资料状态不确定
什么时候用 Plan-Execute 目标清楚、步骤可拆、依赖关系比较稳定
什么时候追问 缺失字段会直接影响结果或带来风险
什么时候兜底 工具不可用、权限不足、风险超过阈值
怎么避免无限循环 设置最大轮次、工具预算、重复观察退出条件

inline-02-context-runtime.jpg

图:上下文运行时把稳定规则与任务现场分层管理

Context Runtime​:静态前缀管规则,动态状态管现场

模型每一轮看到的,不是系统里所有信息。Runtime 会把上下文分成两类:静态前缀和动态状态。

静态前缀放稳定材料:系统规则、工具 schema、Skill 说明、输出格式、长期项目约定。动态状态放当前任务现场:已确认事实、未决问题、工具结果摘要、下一步目标。两者混在一起,模型会被噪声拖垮,缓存也很难命中。

{ 
"system\_rules": [
    "do not fabricate facts",
    "mark owner or deadline as pending if unsupported"
  ],
  "tool\_schemas": [
    "calendar.search(range, keyword)",
    "docs.search(query, project)",
    "im.search(query, time\_range)"
  ],
  "skill": {
    "name": "weekly-report",
    "sections": ["结论", "进展", "风险", "下周计划"]
  },
  "project\_rules": {
    "tone": "concise",
    "must\_trace\_sources": true
  }
}

这里有两个容易混淆的缓存概念。KV Cache 是单次生成内部的缓存,避免每生成一个 token 都重新计算历史上下文。Prompt Cache 复用的是跨请求的稳定前缀,比如系统规则和工具定义。前者提升 decode 阶段效率,后者降低重复前缀的计算成本。

缓存对象 复用范围 典型内容 主要收益
KV Cache 单次生成内部 已处理 token 的 Key/Value 降低逐 token 解码开销
Prompt Cache 多次请求之间 系统规则、工具 schema、固定输出模板 减少稳定前缀重复计算

动态模板要特别小心。只要前缀里混入 {{user_name}}、当前日期、临时工具结果,原本可复用的稳定部分就可能变得不稳定。更好的做法是把固定规则和变化字段拆开,稳定前缀尽量稳定,变化字段放到动态后缀。

Observation Loop​:工具结果不能原样塞回模型

Agent 调工具后,最忌讳把原始返回全文直接塞回下一轮。工具返回的是 observation,不是下一轮 prompt。Runtime 要做的是抽取结构化事实、更新 working state,再把下一轮要解决的问题交给模型。

一个任务跑到中途,状态里至少应该有这些内容:任务目标、执行到哪一步、已确认槽位、事实列表、实体状态、未决问题、工具历史、证据索引和质量标记。

{
  "task": {
    "task_id": "task_20260624_001",
    "task_type": "weekly_report",
    "user_goal": "整理上周几个项目会议的结论,生成一份给团队看的周报",
    "status": "in_progress"
  },
  "execution": {
    "reasoning_mode": "plan_execute",
    "current_step": "collect_project_evidence",
    "loop_count": 3,
    "retry_count": 0,
    "next_action": "retrieve_more_evidence"
  },
  "slots": {
    "time_range": {
      "value": "last_week",
      "status": "confirmed",
      "source": "user_prompt"
    },
    "projects": {
      "value": ["A", "B", "C"],
      "status": "confirmed",
      "source": "calendar.search"
    }
  },
  "open_questions": [
    {
      "field": "deadline",
      "project": "A",
      "reason": "周报需要 deadline,但当前证据中没有明确日期",
      "priority": "medium",
      "next_probe": {
        "tool": "im.search",
        "query": "A 项目 deadline"
      }
    }
  ],
  "quality_flags": {
    "has_missing_required_fields": true,
    "has_conflicts": false,
    "needs_more_retrieval": true,
    "safe_to_generate_draft": true
  }
}

这份状态不是越详细越好。它应该像飞行仪表盘,只保留继续执行需要的信息。工具原文可以保留 source id,没必要长期塞在 prompt 里。否则循环几轮之后,上下文会变成日志垃圾场,模型很难判断哪些是事实,哪些只是历史噪声。
mermaid-03.png

图:工具返回结果进入工作状态的处理过程

同一事实多次返回不一致时,不要直接覆盖。状态里应保留来源、时间、权威性和置信度;新版本优先,正式来源优先,冲突内容进入待确认列表。

Evidence Pipeline​:先召回一批,再留下能支撑答案的证据

生成之前,Runtime 还需要做证据筛选。RAG 不是“搜到什么塞什么”。更合理的链路是:query rewrite、混合召回、元数据过滤、reranker 重排、证据裁剪,最后输出一个能支撑关键断言的 evidence bundle。
mermaid-04.png

图:证据从召回到裁剪的质量控制链路

Reranker 不应该只看 embedding 相似度。对周报任务来说,是否包含 owner、deadline、风险、决策结论,来源是不是正式会议纪要,更新时间是不是最新,都要参与排序。政策、合规、生产操作这类场景还要提高权威来源的权重。

"evidence_bundle": [
    {
      "claim_target": "A 项目优先做 MVP",
      "source_type": "meeting_note",
      "authority": "official",
      "freshness": "high",
      "rank": 1
    }
  ],
  "dropped_items": ["无关闲聊", "重复讨论", "过时草稿"],
  "memory_summary": {
    "confirmed_facts": 6,
    "open_questions": ["B owner", "C deadline"]
  }
}

证据裁剪的目标不是让上下文更短,而是让模型只看到“能支撑当前回答”的材料。旧版本文档、重复聊天、情绪化闲聊、没有 source id 的转述,都应该被降权或丢弃。

证据处理环节 主要风险 工程处理
召回 漏掉关键来源 多路检索,query rewrite,适当放宽召回
重排 语义相似但不权威 加入来源权威性、新鲜度、字段覆盖度
裁剪 上下文被噪声挤满 去重,压缩,按 claim 保留材料
冲突处理 新旧结论互相打架 同时保留冲突来源,标待确认

Draft Generator​:生成的是草稿,不是最终答案

到了生成阶段,模型拿到的已经不是用户原始 prompt,而是任务契约、静态规则、动态状态和证据集合。好的生成要受约束:输出结构要固定,关键断言要有来源,不确定字段要显式标出来。

{
  "draft_answer": {
    "summary": "上周 A/B/C 三个项目均有明确进展",
    "sections": [
      {
        "name": "结论",
        "content": "..."
      },
      {
        "name": "风险",
        "content": "..."
      }
    ],
    "uncertain_items": ["B owner", "C deadline"],
    "citations": ["meeting_note_2026_06_21", "project_doc_A"]
  }
}

这里最好把生成产物叫 draft。这个命名很重要,它会提醒系统后面还有校验。很多事故都发生在“模型写完就直接返回”的链路里:语言很顺,但里面混进了无证据 owner、错误 deadline 或旧版本状态。

生成阶段常见的工程约束包括:

机制 作用
Prefill 让模型先吸收静态前缀、动态状态和证据集合
Decode 按 token 连续生成正文
Stream Generation 分片返回,支持取消、中断和超时处理
Format Constrained Generation 用 schema、固定标签或模板减少格式漂移

表达可以润色,事实不能自由发挥。证据里没有的结论,不要靠“合理推断”补上;缺字段就写待确认。
inline-03-verifier-repair.jpg

图: 按关键断言检查证据支持,再决定保留、补查或改写

Verifier:按 claim 检查,不要只给整段答案打分

草稿生成后,Runtime 要做验证。验证不是看文章顺不顺,而是看有没有漏字段、有没有编造、有没有和证据冲突。比较稳的办法是 claim extraction + ​evidence linking​:把草稿拆成最小关键断言,再逐条找证据支持。

{
  "verification_result": {
    "coverage_ok": false,
    "unsupported_claims": ["B 项目 owner 是李四"],
    "missing_fields": ["C 项目 deadline"],
    "contradictions": [
      {
        "claim": "A 项目风险解除",
        "evidence": "会议纪要里仍标记为高风险"
      }
    ],
    "next_action": "retry_with_more_evidence"
  }
}

claim 不需要切得过碎。结论、风险、负责人、截止日期、数值、状态变化要查;修辞性句子不用查。证据不足和证据矛盾也要分开处理:找不到证据是 insufficient,证据明确反向支持是 contradiction。前者可以补查或标不确定,后者必须改写或阻断。
mermaid-05.png

图:草稿按关键断言进行证据校验与修复分流

校验还包括格式、敏感信息和内容安全。比如输出结构不合法时直接修格式;存在未脱敏人名、手机号、邮箱时阻断;高危操作建议要进人工确认或安全降级。

Repair Loop​:校验失败后要补查、改写或降级

校验没过,不等于整个任务失败。Runtime 应该把问题映射成具体修正动作:缺证据就补查,表达不合规就改写,字段不确定就标待确认,工具失败就走重试策略。关键是别无脑重生整篇,这样很可能把原来的问题换个说法再写一遍。

{
  "finalization": {
    "status": "retry_required",
    "reason": "missing evidence for B owner and C deadline",
    "retry_plan": [
      {
        "action": "retrieve_more",
        "source": "im.search",
        "target": "B owner"
      },
      {
        "action": "revise_answer",
        "mode": "mark_uncertain"
      }
    ],
    "user_visible_output": null
  }
}

重试必须有预算。比如最多补查两次、改写一次;再失败就进入 fail-safe,说明缺口和可用信息,不要一直循环。LangChain 多代理流程陷入长时间循环、工具费用失控,根源往往就是没有把退出条件和预算做成硬约束。

失败类型 修复动作
工具超时 按工具类型重试,超过次数后降级
证据不足 针对缺口字段二次检索
证据冲突 保留冲突来源,删除确定性结论
格式错误 走结构化修复,不重跑全部任务
高风险不确定 请求用户确认或转人工

Closure​:答案发出后,任务还没真正结束

Agent 返回最终答案之后,还需要做收尾。这里的核心问题是:哪些信息可以进入​长期记忆​,哪些只属于本次任务现场,哪些日志要保留给评估和排障。

{
  "task_closure": {
    "status": "completed",
    "long_term_memory_candidates": [
      {
        "type": "user_preference",
        "content": "团队周报偏好结论先行、风险单独列出",
        "write_back": true,
        "reason": "stable and reusable"
      }
    ],
    "short_term_memory_cleanup": [
      "raw_tool_results",
      "temporary_search_candidates",
      "intermediate_drafts"
    ],
    "evaluation_log": {
      "tools_used": ["calendar.search", "docs.search"],
      "retry_count": 1,
      "verification_passed": true
    }
  }
}

长期记忆不能变成垃圾桶。稳定偏好、项目固定规则、用户明确确认的信息可以写入;一次性会议结论、临时搜索候选、原始工具日志不应该长期保存。记忆还要带来源、时间戳和版本,新信息和旧记忆冲突时先进入候选区,别直接覆盖。

短期记忆和长期记忆的职责可以这样分:

类型 保存内容 读写方式 退出策略
短期记忆 当前任务状态、最近工具结果摘要、未决问题 每轮读取和更新 任务结束后压缩或清理
长期记忆 稳定偏好、项目规则、可复用背景 新任务开始时读取,确认后低频写入 被新版本覆盖、长期不用或用户否认时降权

任务日志也要保留到合适粒度。工具调用、重试次数、失败原因、验证结果值得保存;原始敏感内容、无关中间草稿、可还原隐私的 trace 不该长期留着。

结语:好 Agent 靠 Runtime 托底

Agent 的难点不在“让模型说得像会做事”,而在把一次请求拆成可执行、可检查、可回滚的链路。Prompt Compiler 负责入口契约,Execution Router 负责路径选择,Context Runtime 负责状态,Evidence Pipeline 负责事实质量,Verifier 和 Repair Loop 负责交付边界,Closure 负责把经验留下来。

模型越强,这套 Runtime 越重要。因为强模型更容易让人忽略工程边界,直到它在高风险任务里自信地编出一个看似合理的答案。真正能上线的 Agent,不是每次都像天才一样一次答对,而是知道什么时候该查证、什么时候该停、什么时候该承认不确定。
aaa_compressed_under_1M.png

推荐阅读

Agent 评测系统架构:从指标分层到 GT/Judge 闭环的工程化落地

团队落地 Agent 工程化 Loop 的一些必看小技巧!

Agent 工程化新底座:用 CLI 契约层打通 HTTP 接口与业务能力

AI Coding 不只靠 Prompt:Agent 工程闭环如何接入 DevOps

SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent

posted @ 2026-07-09 10:51  AI小老六  阅读(77)  评论(0)    收藏  举报