Agent Runtime这个词这两年几乎被说烂了。但真正能把它讲清楚、真正能把它落地的人并不多。我见过太多团队用一个大模型 API 加上几行循环代码就对外宣称我们做了 Agent结果一遇到真实业务场景——多步工具调用、任务中断恢复、上下文超限、并发任务抢占——整个系统就散架了。过去一年我带着团队从零开始搭了一套 Agent Runtime中间推翻重来了三次也踩了不少坑。这篇文章想把这条路上的全貌讲清楚Agent 编排的底层原理是什么Runtime 的架构该怎么拆工程落地时哪些决策真正决定成败。不吹概念、不堆架构图全部基于可运行代码和真实线上问题总结出来的经验。如果你正准备自研 Agent 运行时或者正在各方案之间做选型这篇文章值得从头读一遍。1. 为什么说 Demo 级 Agent 和可用的 Agent 之间隔着一整个 Runtime1.1 三种常见的 Agent 项目死法先说结论把 Agent 从能演示做到能干活难点从来不在模型调用而在围绕模型调用建立的那一层基础设施。我见过三类很典型的失败方式。第一类是 Notebook 型。在交互式环境里用几行代码调通大模型接口能回答几个预设问题看起来任务规划也靠谱。但一部署成服务就出问题用户请求是并发的、任务是长时运行的、模型偶尔会返回格式错误、工具调用失败需要重试……Notebook 里的线性脚本完全扛不住这些。第二类是脚本堆砌型。把 Agent 逻辑写成一个巨大的函数里面塞满 if-else 和硬编码分支。这种项目在一两个固定场景下能跑但每加一个新工具、新任务类型就要动主流程。改到第三个月没人敢碰那段代码。第三类是框架过重型。一上来就引入某个大而全的编排框架把项目彻底绑在框架的抽象上。框架的更新节奏、设计哲学和你的业务不完全匹配时你会发现想在框架的 API 之上插桩、改行为、做自定义痛苦程度不亚于自己写一个。而且框架越大黑盒越深线上出问题就越难排查。这三类死法的共同点是什么是把 Agent 当成一个函数来写而没有把它当成一个系统来建设。Agent 本质上是一个复杂的分布式系统有任务生命周期、有状态、有并发、有故障恢复、有资源消耗。你需要为它建 Runtime就像业务代码需要应用服务器、需要数据库、需要消息队列一样。1.2 Agent Runtime 到底管哪些事用一句话定义我理解的 Agent Runtime它是承接 Agent 任务执行全生命周期的运行基础设施负责把用户给一个目标变成Agent 完成一次可观测、可恢复、可控成本的执行过程。具体来说一个完整的 Runtime 至少有这些职责任务编排决定任务按什么顺序执行哪些步骤可以并行哪些步骤存在依赖。状态管理记录每一步的执行状态支持任务中断后从某个节点恢复。模型调度管理大模型调用的上下文窗口、Token 预算和调用频率。工具执行注册、校验、调用外部工具并把执行结果反馈给模型。记忆管理短期会话上下文和长期知识的读写与更新。队列与并发控制同时运行的 Agent 任务数量处理限流和重试。可观测性记录每一次模型调用、每一步推理、每一次工具执行的轨迹和成本。换句话说Runtime 是一个横切层位于大模型和业务应用之间。它把模型本身的不可靠性、无状态性、高成本性用工程手段包装成业务方可依赖的稳定服务。业务方只需要提交一个任务、拿到一个结果中间所有过程都交给 Runtime 处理。1.3 判断一个 Runtime 好不好用先问五个问题很多团队在选型或者自研时容易陷入框架时髦、代码能跑的误区。我提供一个更务实的检验清单拿它去审视任何方案——不管是你自己写的还是别人开源的——都能很快看出差距。线上进程突然重启正在执行的任务能不能从断点继续跑而不是从头再来一次任务跑了一半Token 预算快用完系统能不能在接近上限时自动切换策略而不是直接报错一个用户同时提交十个任务系统能不能保证每个任务的状态互不干扰新接入一个工具需要改动 Runtime 核心代码吗还是只要写一份工具描述注册进去就行某一步工具调用失败了能看到完整的调用轨迹和失败原因吗能不能一键重放这五个问题几乎每一个都对应 Runtime 的一项核心能力。接下来我会逐个展开讲清楚这些能力背后的设计和实现。2. 编排模型选型链式、循环、图式到底有什么区别2.1 先把几个词拆清楚编排Orchestration、工作流Workflow、Agent 循环Agent Loop这几个词经常被混用但在工程上含义差别很大不拆清楚很容易在建 Runtime 时走错方向。工作流强调的是预先定义好的路径。每个节点做什么、节点之间的顺序和数据传递都是确定的典型代表就是 DAG有向无环图。它适合流程固定的业务比如先查库存、再算运费、最后生成订单。Agent 循环强调的是模型驱动的决策。模型根据当前观察自行决定下一个动作是调用工具还是直接回答。路径是动态的、不可预测的典型代表就是 ReAct 模式推理和行动交替进行每一轮都基于前面结果重新判断。编排则是这两者的上层概念。真正的编排引擎应该既能承载确定性的工作流也能承载动态的 Agent 循环甚至能把两者混合编排在同一个任务里。Runtime 的核心难点不是实现某一种模式而是抽象出一种统一的执行模型让两种路径都能在上面跑。只抱着一种模式不放注定会碰到大量不适用的场景。2.2 四种主流编排模式各自的长处与死穴我把实际工程中见到的编排模式归纳成四种用一个表说明各自特性。模式执行方式适合场景典型风险链式严格顺序前一步输出是后一步输入固定流程、管道式处理灵活性差路径一变就要改代码循环式ReAct模型每轮决定下一个动作直到命中终止条件开放式交互、工具调用频繁的任务容易陷入死循环Token 消耗不可控规划-执行先由模型生成完整计划再按计划逐步执行多步骤复杂任务需要全局视角计划一旦过时或错误后续全错图式DAG节点与依赖边预先定义无依赖节点并行执行流程明确、需要并发提速的任务动态性不足无法应对突发状况这里强调一点不要迷信某一种模式。我有段时间很迷信 ReAct觉得模型自主决策才是 Agent 的灵魂。结果在客服场景里发现用户问一句订单到哪里了ReAct 循环要调用订单系统、物流系统、可能还会追问用户绕三四圈才出结果用户早就等得不耐烦了。后来改成 DAG 直达路径延迟降了 80%。反过来在数据分析场景里你没法预先定义用户的每个问题要经过哪些步骤只能靠模型现场规划。这时候 ReAct 或规划-执行就远比 DAG 合适。选型的本质是看任务的路径确定性有多高。2.3 混合编排把规则路由放在最前面我最终的实践方案和选一种模式一条路走到黑完全相反。我在 Runtime 的入口加了一个路由层根据任务类型和元数据把请求分发到不同的执行模式里。伪代码思路如下def route_task(task: UserTask) - Executor: # 规则优先路径固定的任务直接走 DAG if task.type STATUS_QUERY: return DagExecutor.query_status_flow # 半开放任务先规划再执行中途允许模型微调计划 if task.type REPORT_GENERATION: return PlanExecuteExecutor.report_flow # 完全开放的任务交给模型自主循环 if task.type OPEN_ENDED: return ReactExecutor.default_loop # 兜底规则没覆盖到的交给模型判断如何分发 return RouterAgent.dispatch(task)这个路由层看起来简单但它解决了一个本质矛盾Runtime 要同时保证效率和灵活。能用确定性流程解决的问题绝不让模型绕圈子必须让模型发挥的场景才放开循环。这样一来80% 的高频任务走的是又稳又快的老路20% 的开放式任务才消耗宝贵的模型推理。这个比例关系带来的成本红利在月度账单上看得清清楚楚。3. 状态机是 Runtime 的心脏把一次执行建模成可恢复的状态流转3.1 为什么顺序脚本扛不住真实任务如果你只是在一个脚本里用 for 循环调用大模型你不需要状态机。但 Runtime 面对的是长期运行、可能中断、必然出错的任务顺序脚本会立刻暴露三个致命问题。第一不可恢复。脚本跑到第 7 步进程崩了一切归零重来。如果第 7 步调用了一个有外部副作用的工具——比如已经下了订单——重跑还会造成重复业务操作。第二不可观测。脚本只有一个调用栈你没法回答这个任务现在到底走到哪一步了、上一步模型输出是什么、工具返回了什么。第三不可并发。你要同时跑 50 个任务每个任务都有自己的进度。脚本里一个全局变量就搞不定这种并发隔离。状态机的好处在于把任务执行这个连续的、混沌的过程抽象成一系列离散的、可记录的状态迁移。每个状态都是一个稳定点每个迁移都对应一个明确的触发条件。恢复、观测、并发全都变成了状态本身的问题。3.2 核心状态定义与迁移规则在我的实现里一个 Agent 任务的状态机并不复杂核心状态只有七个。from enum import Enum class TaskState(str, Enum): PENDING pending # 已提交等待执行 RUNNING running # 执行中 WAITING_TOOL waiting_tool # 等待工具返回 COMPLETED completed # 正常完成 FAILED failed # 执行失败 PAUSED paused # 人工或策略暂停 CANCELLED cancelled # 取消每次状态迁移都必须经过一个统一的transition函数不允许任何外部代码直接改状态字段。def transition(self, new_state: TaskState, event: str, payload: dict): if new_state not in self.valid_transitions[self.state]: raise InvalidTransition(f{self.state} - {new_state} 不被允许) self.state new_state self.events.append({ event: event, from: self.state, to: new_state, payload: payload, timestamp: now(), })valid_transitions是一张静态表例如valid_transitions { TaskState.PENDING: {TaskState.RUNNING, TaskState.CANCELLED}, TaskState.RUNNING: {TaskState.WAITING_TOOL, TaskState.COMPLETED, TaskState.FAILED, TaskState.PAUSED}, TaskState.WAITING_TOOL: {TaskState.RUNNING, TaskState.FAILED, TaskState.PAUSED}, TaskState.PAUSED: {TaskState.RUNNING, TaskState.CANCELLED}, }为什么要强制走统一的迁移函数两个原因。一是所有状态变化都会被记录成事件流为后续审计和重放提供完整依据二是非法迁移能在开发期就被拦截而不是等线上出了诡异问题再去排查。3.3 检查点与断点恢复把任务快照落到可靠存储状态机只存在于内存里是远远不够的进程一挂就全没了。可靠的做法是把任务快照定期持久化这个过程叫检查点Checkpoint。我采用的做法是每次模型调用完成、每次工具调用完成、每次状态迁移时都写一个快照。快照里至少包含四样东西{ task_id: task_8f3a2b, state: waiting_tool, context: { input: 用户原始问题..., steps: [ {step_id: 1, type: tool_call, tool: query_order, result: ...} ], current_step: 2, token_used: 3860 }, checkpoint_at: 2025-06-11T10:23:45Z, version: 3 }写入频率和存储成本需要平衡。我实践的节奏是变更频繁的阶段每完成一次动作就写一次稳定等待阶段只在状态迁移时写。写入用批量事务保证状态 事件 快照三者原子一致避免出现状态标记为 completed 但快照还停留在 running 的脏情况。恢复逻辑就简单了进程启动时扫描待处理任务找到最近一次合法快照把状态恢复到上次迁移点重新挂入调度队列。同时所有工具调用都要求实现幂等性——比如下单工具要求外部传一个 request_id恢复重放时用同一个 request_id 去重。这样即使恢复后重复执行某个工具调用也不会产生重复的业务副作用。这个设计让我在线上省了无数心。最典型的一次一个跑了 40 分钟的报表生成任务在接近尾声时容器被调度到另一台机器正常人的第一反应是完蛋了白跑了。但因为是检查点恢复任务从倒数第三步继续跑两分钟后就正常完成并返回了结果。4. 支撑 Runtime 的五块基石上下文、工具、记忆、队列、观测有了状态机Runtime 的骨架就立起来了。但真正决定好不好用的是骨架上的这五个模块。每一个单独看都不算惊天动地但每一个都藏着大量工程细节少一块都会在线上冒烟。4.1 上下文窗口预算不要让 Token 悄悄击穿你的成本大模型的上下文窗口是有限的而 Agent 的任务是无限累积的。如果不做预算管理任何长任务都会在某次模型调用时超出窗口上限直接报错。我把它拆成两层来做。第一层是静态预算。每次模型调用前根据模型窗口大小留出安全余量默认 20%把历史消息、当前推理、工具结果按优先级塞进窗口def build_messages(task_ctx, max_window_tokens: int 8000) - list: budget int(max_window_tokens * 0.8) # 预留 20% 给模型输出 # 优先级系统提示 当前推理片段 最近结果 早期历史 messages [] messages.append(task_ctx.system_prompt) # 永远保留 budget - token_count(task_ctx.system_prompt) messages.append(task_ctx.recent_steps[-3:]) # 最近的步骤 budget - token_count(messages[-1]) # 剩余预算用于早期历史超出则截断或压缩 messages.append(compress_history(task_ctx.history, budget)) return messages第二层是策略级预算。每个任务在创建时就带了一个 Token 配额比如A 类任务最多 5 万 Token。执行过程中每次模型调用都扣减配额。剩余配额低于 15% 时Runtime 自动切换策略禁止模型再调用高消耗的多轮工具只允许做总结性回答剩余 5% 时强制收尾把当前结果整理成最终输出。这套机制上线后我再没遇到过任务跑到一半 Token 超限直接中断的线上故障。更重要的是成本从不可预测变成了可预算——每个任务类型的平均成本第一天就能统计出来。4.2 工具注册与调用把外部世界做成标准接口Agent 的能力上限取决于工具数量但工具管理的混乱程度也往往与数量成正比。我的原则是工具接入必须标准化工具实现必须和 Runtime 解耦。每个工具只需要提供一个描述文件和一个执行函数由工具注册中心统一管理register_tool( namequery_order_status, description查询订单当前物流状态需要订单号, parameters{ order_id: {type: string, description: 订单号形如 ORD-2025-0001}, include_detail: {type: boolean, default: False} } ) def query_order_status(order_id: str, include_detail: bool False): result order_api.query(order_id, detailinclude_detail) return {status: result.status, detail: result.detail}关键点在 parameters 的 JSON Schema。模型调用工具时Runtime 会先对参数做 Schema 校验不合法就直接打回附上具体错误信息让模型重新生成。这个校验环节极其重要——模型填错参数是高频事故不校验就调用工具轻则浪费一次调用重则造成误操作。工具调用的完整链路是模型输出一个结构化工具调用指令函数名 参数。Runtime 解析指令做格式校验。Runtime 做参数 Schema 校验和权限校验。执行工具函数设置超时默认 30 秒。捕获执行结果或异常格式化为标准反馈。把反馈拼接进上下文继续下一轮模型调用。任何一步出错都走反馈错误给模型让模型自行修正的路径而不是直接中断整个任务。这也是循环式编排能自愈的关键设计。4.3 记忆分层短期、工作、长期各司其职很多 Agent 项目把记忆想得太简单——把全部对话历史都塞给模型就算有记忆了。这在小规模下能用但任务一变长窗口和成本都会失控。我实践中的方案是把记忆分成三层短期记忆当前任务进行中的上下文消息由预算机制管理任务结束即清理。工作记忆跨步骤、但仅在单个任务内有效的结构化信息比如用户输入的订单列表决策过程中产生的中间变量。它不是原始对话而是模型每步提取后的结构化摘要。长期记忆跨任务、跨会话的知识比如用户偏好、历史结论、常用术语解释。长期记忆通常落到向量数据库或键值存储里在需要时通过检索引入。长期记忆我会额外加一道写入审核不是模型说什么都直接写入长期记忆而是先做轻量校验判断这条信息是否真的值得保留、是否与已有知识冲突。这一步能防止把模型的幻觉固化进记忆里。我见过不少团队忽略这个环节结果 Agent 越用越蠢。4.4 任务队列与重试策略关注失败而不是成功Agent Runtime 的另一个隐藏工作是失败处理。模型调用可能超时、限流、返回畸形 JSON工具可能挂掉、慢响应、返回脏数据。如果这些失败都直接抛给用户体验会非常差。我的做法是给每个任务配一个重试策略分三个级别失败类型处理方式最大次数说明瞬时错误指数退避重试3 次网络超时、限流模型格式错误带错误信息回填重试2 次让模型看到错误并自行修正工具逻辑错误不自动重试转人工标记1 次涉及外部副作用避免重复执行瞬时错误的指数退避参数这样设初始 1 秒倍增系数 2上限 30 秒。这个组合在大多数限流场景下不会把自己封死。格式错误的重试我会把模型上一次的完整输出一起放回去让它看出自己错在哪。工具逻辑错误则倾向于保守不重试宁可人工介入也不要让一个可能下了单的工具被重复执行。4.5 可观测性Agent 是黑盒但 Runtime 不能是大模型的输出天然不可预测这已经是共识。所以 Runtime 的一项核心职责就是让不可预测的过程变得尽可能可追踪。我在 Runtime 里为每个任务生成一棵执行树span tree根节点是任务子节点分别是模型调用、工具调用、记忆检索等。每个节点记录五类信息输入发给模型或工具的内容摘要。输出返回的内容摘要。耗时该节点从开始到结束的时长。成本该节点的 Token 消耗和估算费用。状态成功、失败、重试次数。这些信息落到日志系统后我每天只看两个面板一个是今日任务成功率一个是异常任务执行树。出问题时从根节点一路看到叶子节点基本五分钟内就能定位到是哪一步、哪个工具、哪次调用出了岔子。做 Agent 系统没有这套追踪能力排查线上问题会像大海捞针。5. 上线之后我踩过的五个 Runtime 大坑这一节写的是实际线上翻过车、然后被逐一修掉的问题。每个坑都按症状 - 根因 - 修复的结构讲方便你对号入座。5.1 工具调用死循环Agent 在同一个问题上打转症状某个任务日志显示Agent 连续八轮循环调用同一个工具、每次得到同样的结果却始终不进入下一步。根因模型的推理陷入局部最优——它认为再查一次是最稳妥的行动但实际上对比前后结果已经可以得出结论。我们的循环没有设置最大轮次也没有对重复动作做检测于是它无限转圈Token 却哗哗地烧。修复加了两个硬性机制。第一任务级最大迭代次数默认 15 轮到了就强制收尾并生成答案第二重复动作检测——如果模型连续两轮使用相同工具 相同或高度相似参数Runtime 会主动注入一条系统提示你已经尝试过该操作结果未变化请换一种策略或基于现有信息回答。这个提示通常能让模型果断收手而不是继续打转。5.2 长任务上下文撑爆窗口症状一个需要处理二十份文档的任务跑到第八份时模型调用开始报上下文超限错误任务直接中断。根因我们把每份文档都全文塞进上下文虽然做了窗口预算截断但截断策略太粗暴——只保最新内容早期的关键结论反而被挤掉了。修复改成分层压缩。每处理一份文档先用模型抽取一份结构化摘要关键词 结论 引用编号把摘要放进工作记忆原始全文只在当轮分析时临时调用不进入长期上下文。这样二十份文档的任务上下文里始终只有二十份摘要和当前正在分析的一份全文窗口占用降低了一个数量级。代价是每次摘要抽取会多消耗几百 Token但对比原来动不动就崩这个成本完全值得。5.3 进程重启后任务状态丢失症状某个深夜线上容器因为内存问题被自动重启所有正在执行的任务全部消失。用户第二天反馈我提交的订单分析任务怎么没了。根因状态机在内存里维护但没有把快照真正持久化到外部存储。重启等于清空一切连失败重试的机会都没有。修复就是检查点机制把状态 事件 快照原子写入外部存储同时补了启动恢复器进程起来后先扫描未完成任务按优先级重新入队。另外所有可能产生外部副作用的工具都增加幂等键支持。这轮改造之后再也没有因为进程重启丢过任务。5.4 并发任务间的状态串扰症状两个用户同时提交任务A 用户在结果里看到了 B 用户的数据。这是数据安全事件性质非常严重。根因Runtime 里用了几个全局变量缓存当前上下文和当前步骤索引没有做会话隔离。单线程运行没问题一旦并发调度多个任务的执行逻辑互相覆盖了同一个全局状态。修复把所有任务上下文全部收敛到各自的 TaskContext 对象里任务之间没有任何共享可变状态调度器只负责传递 task_id所有读写都必须通过该 task_id 对应的上下文实例。同时补了压测用例并发 500 个任务随机注入失败验证状态零串扰。教训很简单Runtime 并发模型的第一原则就是绝不共享可变状态。任何为图省事把状态放到全局的做法都是在埋雷。5.5 模型乱填工具参数症状模型调用创建工单工具时把客户姓名填成了订单号、把优先级填成了很高很高导致业务侧创建了一堆异常数据。根因工具的参数校验过于宽松基本非空就算过。模型在上下文压力较大时容易把不同字段的信息搞混尤其是中文字段名相近的时候。修复三管齐下。第一严格校验参数类型和取值枚举不允许模型自由发挥字符串字段第二校验失败时不直接丢弃而是把详细错误信息连同模型上一次的工具调用指令一起喂回给模型让它自己修正第三给每个工具的每个参数补充常见错误示例放在工具描述里例如priority 参数只能取 low, normal, high 之一不要写成中文描述。三管齐下后参数错误率从最初的 7% 降到了 1% 以下。6. 生产环境配置清单可以直接抄的一版出厂设置文章写到这核心原理和踩坑经历都讲得差不多了。最后给一份我在搭建完 Runtime 之后沉淀下来的配置清单和指标体系。你不用照搬但可以在选型和自查时逐项对照。6.1 核心配置项与推荐值配置项推荐值说明单任务最大迭代轮数15防止工具调用死循环模型输入窗口安全余量20%给模型输出留足空间工具调用超时30 秒超时按失败处理并反馈模型瞬时错误重试3 次退避 1s/2s/4s应对限流与网络抖动格式错误重试2 次回填错误信息让模型自愈单实例最大并发任务按模型限流而定建议小于限流上限的 80%检查点写入时机每次动作完成后动作指模型调用或工具调用上下文预算告警阈值剩余 15%触发策略切换任务级 Token 配额按任务类型预设例如 A 类上限 5 万需要提醒的是这里的数值是经验起点不是标准答案。你的模型供应商、任务复杂度、成本预期都不一样上线后要按实际指标再调。尤其是并发任务数我的做法是把实测堆出问题的临界值再打八折而不是顶着理论峰值跑。6.2 上线后必须盯住的四个指标我自己的运维看板只放四个核心指标多了反而干扰判断。任务成功率完成数除以提交数。低于 90% 基本说明系统有问题。单任务平均成本按任务类型分组的平均 Token 消耗。异常上涨通常意味着上下文压缩失灵或死循环。端到端时延分位数重点看 P95长尾任务最容易破坏体验。人工介入率需要人工兜底的任务占比。超过 3%说明工具自愈和重试机制还需要加强。这四个指标之间是关联的。比如人工介入率上升往往伴随任务成功率下降时延 P95 上涨经常是上下文预算告警后触发了降级策略。我习惯每两周过一次这几个指标的滚动趋势而不是只看当天数字。6.3 安全与治理工具权限和审计日志不能省Agent 拥有了调用真实业务系统的能力之后安全就不是加分项而是保命项。我踩过最痛的一个教训是一个测试账号通过工具接口误删了一批演示数据虽然不是生产库但也当场把人吓出一身冷汗。从那以后工具权限遵循最小化原则每个工具按读 / 写 / 删除三个级别单独授权Agent 默认只有读权限写操作需要用户确认后才放行删除操作默认不给模型。模型调用任何工具之前Runtime 都会做权限校验失败时返回的是一条当前权限不足的提示不泄露具体业务细节。审计日志同样重要。每个工具调用、每次状态迁移、每个模型输入输出都要留痕。我见过大量线上问题最后都是靠翻审计日志才定位到的。日志这件事宁可多不可少。6.4 上线前必做的三类测试最后说说测试。Agent 系统的测试和传统软件差别很大因为输出不可预期断言答案正确与否不可靠。我建议按下面三类来做。状态机单元测试用固定的事件序列驱动状态迁移断言每个迁移是否合法、无效迁移是否被拦截。这部分的确定性最高应该覆盖所有状态和非法迁移路径。集成测试Mock 模型用一份录制的模型响应脚本替代真实模型跑通任务提交 - 工具调用 - 结果返回的完整链路。好处是确定性高、速度快适合回归坏处是覆盖不到模型的真实行为所以还需要第三类。混沌式真实模型测试用真实模型跑高难度任务集我准备了 200 条通过人工筛选的典型任务检查指标是否在容忍范围内。这类测试不追求单次断言而是看统计分布——成功率、成本中位数、最长调用链。每次升级 Runtime 或换模型版本就跑一遍把结果和基线对比。三层测试体系搭好之后我对每次变更的信心完全不一样了。最后说个这几个月最深的体会。造 Runtime 最大的坑不是技术不会而是总觉得自己快了。每次以为再补一个模块就能收工跑两天就又冒出一个新问题。后来我给自己定了个规矩每一个新模块上线前先拿开头那五个问题问一遍自己全部答得过才允许进主干。事实证明这五个问题几乎能挡住所有常见的翻车姿势。如果你也在造自己的 Runtime建议把这篇文章里的配置表和测试清单打出来贴在工位旁边比到处抄代码靠谱得多。