
大家好我是你们的老朋友。最近在社区里看到一个特别高频的问题为什么 ChatGPT、Claude 这类 AI Agent 能一口气连续执行几十步操作像是自己写代码、自己运行、自己改错最后把任务完整交付而我自己在本地用 LangChain 或 LlamaIndex 搭的 Agent往往跑几步就断不是上下文超限就是工具调用报错甚至模型生成的下一轮动作直接“失忆”。这篇文章我就想把这层窗户纸彻底捅破。我们会从模型本身的能力边界讲起再深入到 Agent 的运行时调度、架构设计最后给你一个最小可运行的完整长任务示例并附上高频故障排查清单。无论你是刚开始接触 AI Agent 开发还是已经在生产环境里趟过坑这篇文章都能帮你建立一张完整的“长任务架构地图”。1. 先说清楚Agent、模型、运行时到底分别是什么很多人把 Agent 和“模型”混为一谈觉得 Agent 厉害就是因为背后的大模型聪明。实际上模型只是 Agent 的“大脑”而 Agent 是一个完整的“身体系统”。1.1 三者各自的职责先说模型也就是我们常说的 LLM大语言模型。它负责“理解”和“生成”给它一段 Prompt它返回一段文本。它本身没有记忆没有循环没有工具也没有执行能力它只是一次次地做文字接龙。然后是 Agent它是构建在模型之上的智能体程序。它拥有目标拆解能力、任务规划能力、工具调用能力、记忆管理能力以及自我纠错能力。你可以把 Agent 理解为一个“会调用模型来做决策的自动化流程引擎”。最后是运行时这是容易被忽略却最关键的一层。运行时负责调度 Agent 的执行循环、保存和恢复状态、管理上下文窗口、执行工具调用、处理错误和重试。没有运行时模型再聪明也无法独立跑完一个几十步的任务。打个比方模型是发动机Agent 是驾驶员运行时是整辆车的底盘、油箱和控制系统。发动机马力再大没有底盘和控制系统它也无法完成一次长途旅行。1.2 长任务的定义什么是长任务这里需要给出一个可量化的标准。一个长任务通常具备以下特征执行步骤超过 10 步调用工具、生成中间结果、修改计划都算一步。需要在执行过程中多次读取或写入外部状态例如数据库、文件、API。模型单次推理无法覆盖全部上下文必须依赖外部记忆进行截断、摘要或检索。过程中存在不确定性Agent 需要根据中间结果动态调整后续计划。换句话说短任务靠“提示词工程”就能实现长任务必须靠“架构设计”才能稳定落地。2. 长任务的核心挑战为什么 Agent 跑不了几步就“断片”在深入研究架构之前我们必须先理解长任务到底难在哪里。这有助于理解后面每一层的设计动机。2.1 上下文窗口的物理限制大模型有一个固定的上下文窗口Context Window例如 32K、128K、200K。这个窗口包含系统提示词、用户新输入、历史对话、工具返回结果。以 128K 窗口为例看起来很大但如果一个工具返回了一大段 JSON 或日志几轮对话就能把窗口撑爆。一旦超出窗口会出现两种结果一是直接报错“已达到输出 token 上限”二是早期内容被无形截断有些 API 会自动丢弃中间内容。这就带来一个致命问题** Agent 执行到第 20 步时可能已经忘记了第 1 步的任务目标和约束条件。**2.2 工具调用链路断裂真实世界的 Agent 不可能只靠模型自己的知识完成所有事。它需要调用搜索引擎、数据库、代码解释器、文件系统、第三方 API。工具调用链路可能因为以下原因断裂参数格式错误模型生成了一个不符合工具规范的结构化参数。权限不足Agent 尝试访问未授权的资源。外部服务超时API 响应 30 秒未返回Agent 等待超时。返回结果与预期不符模型无法从工具返回的异常数据中恢复。2.3 状态维护困难短任务不需要状态管理模型输入一轮输出一轮即可。但长任务要求 Agent 在执行过程中始终知道当前处于哪个阶段已经完成了哪些子任务还剩下哪些子任务哪些结果已经被验证过。如果这些“状态”全部放到 Prompt 里很快就会超出上下文窗口。如果放到外部存储里则需要运行时具备读写和同步机制。2.4 错误恢复能力缺失一个稳定的长任务系统必须具备“容错机制”步骤失败后是重试是更换工具是修改 Prompt还是直接跳过该步骤大多数 Agent 框架只提供了最简单的“异常抛出 → 流程终止”结果就是用户常常看到这样的报错agent execution terminated due to error.然后整个任务前功尽弃所有耗时全部浪费。3. 模型层为什么 LLM 本身具备支撑长任务的潜力我们要理解长任务架构必须先弄清楚模型层提供了哪些基础能力让 Agent 有了连续运行的可能性。3.1 Transformer 架构带来了什么现在的 LLM 几乎都基于 Transformer 架构。这个架构的核心优势在于它能对输入序列中的每个 Token 与其他所有 Token 之间建立注意力关系。注意力机制意味着模型在生成下一步输出时可以“回看”当前上下文中的关键信息。举个例子如果你在 Prompt 中写了“第 1 步已完成第 2 步的任务是 XX”Transformer 可以在生成第 3 步时依然把注意力放在这句话上从而保持行为一致性。这也是为什么 Agent 可以不依赖外部记忆跑完一小段连续操作因为模型本身具备一定的上下文跟随能力。3.2 函数调用/工具调用能力目前主流模型如 GPT-4、Claude、Qwen 系列等都支持结构化的函数调用Function Calling / Tool Use。也就是说模型可以输出一个 JSON 格式的调用意图{ name: search_web, arguments: { query: Python 列表去重方法 } }运行时解析这个 JSON执行对应的函数再把结果返回给模型。这个能力是 Agent 能“动手做事情”的基础。如果没有函数调用能力模型只能生成一段“假想代码”无法真正与环境交互。3.3 ReAct 模式推理与行动交替现代 Agent 的工作方式绝大多数都遵循一种叫 ReAct 的模式Thought思考模型分析当前状态决定下一步做什么。Action行动模型输出一个工具调用意图。Observation观察运行时执行工具返回结果。循环模型基于观察结果继续思考、行动。这个循环就是 Agent 能连续执行多步的基础。每一步都会产生新的观察驱动下一步的推理。模型层解决了“每步应该干什么”的问题而运行时层解决的是“怎么把这一步步串起来不中断”。3.4 输出 Token 上限的约束这里必须说一个很容易踩的坑即使模型的上下文窗口很大单次推理的输出 Token 上限往往是单独限制的。什么意思假设上下文窗口是 128K但一次输出最多只能生成 4096 个 Token。如果模型试图在一次回复中输出一份完整的代码、一份长文档、或者许多步工具调用就会被截断。很多 Agent 新手遇到这种问题第一反应是“模型不行”其实正确做法是拆分任务让模型每次只输出一步而不是一次输出多步。这也是长任务架构中一个非常核心的设计原则小步快跑。4. 运行时层长任务连续执行的核心引擎聊完模型我们进入整篇文章的重头戏——运行时。4.1 为什么模型能力再强也需要运行时模型本身是一个“无状态函数”你调用一次它返回一次结果。它不会自己维护循环、不会自动重试、不会管理历史记录。运行时把这些能力补上。可以说没有运行时设计模型就只能做单轮交互无法完成长任务。一个成熟的长任务运行时至少包含以下模块循环调度器决定 Agent 当前是否要继续执行、暂停或终止。状态管理器保存任务执行的中间状态包括已完成步骤、当前步骤、剩余步骤、已验证结果。上下文管理器控制哪些历史信息保留在 Prompt 中哪些被摘要哪些被存入外部存储。工具执行器解析模型生成的工具调用请求执行对应函数并校验返回结果。错误处理器捕获工具抛出的异常决定是重试、修改参数、换工具还是终止任务。我们可以用一个流程图来描述运行时主循环---------------------- | 任务初始化 | | 加载目标、历史、工具表 | --------------------- | v ---------------------- | 调用模型生成下一步 | | (ReAct: 思考行动) | --------------------- | v ---------------------- | 解析模型输出 | | 是否为最终答案 | --------------------- |否 v ---------------------- | 执行工具调用 | | 追加观察结果 | --------------------- | v ---------------------- | 错误- 重试/替换 | | 成功 - 继续循环 | ----------------------4.2 上下文管理策略上下文管理是运行时设计中最难、也最关键的部分。假设你的 Agent 要执行 30 步任务每一步的思考、工具调用和结果都放进 Prompt那么基本上到第 5 步就会超限。运行时通常采用以下策略第一种是截断丢弃最早的历史消息只保留最近 N 轮对话。缺点是模型可能会丢失初始任务目标。第二种是摘要每执行几步就让模型把之前的历史压缩成一小段摘要替换掉完整历史。这是比较推荐的做法代价是增加一次模型调用。第三种是检索增强将历史信息写入外部向量数据库每次执行时只检索相关的记忆片段再放入 Prompt。实际上生产中通常混合使用这三种策略保留任务目标、压缩过程细节、按需检索长期记忆。4.3 状态机与任务追踪长任务系统在运行时内部通常会构建一个状态机。每个任务可以处于以下状态状态含义后续动作pending等待执行进入运行队列running正在执行持续调度paused暂停等待人工输入或外部条件恢复或终止completed执行成功输出最终结果failed执行失败触发重试或告警状态机的好处是即使运行时崩溃重启后仍然可以从持久化存储中读取任务状态恢复到崩溃前的步骤而不是从头开始。这种设计通常叫“断点续跑”。很多开源 Agent 框架如 LangGraph、AutoGen、CrewAI 的底层都实现了类似的状态管理机制。5. 架构层一套完整的长任务系统由哪些组件构成当我们把视角拉高从单个 Agent 上升到一个完整的系统长任务架构需要覆盖的组件就更多了。5.1 调度器调度器是整个长任务系统的“指挥中心”。它负责接收用户请求、创建任务实例、分配运行时资源、监控任务状态。在微服务架构下调度器通常是独立部署的服务通过消息队列下发任务指令这样可以避免单个任务阻塞整个系统。常见的做法是用户请求 → API 网关 → 消息队列 → 工作节点消费 → 执行 Agent 循环。5.2 记忆模块长任务必然需要持久化记忆。记忆模块分为短期记忆和长期记忆。短期记忆保存在运行时内存中通常是当前任务的上下文。长期记忆保存在外部存储中可以是 Redis、关系型数据库、向量数据库如 Milvus、Qdrant、Chroma等。长期记忆保存的是历史任务的总结、业务规则、用户偏好等跨任务复用信息。5.3 工具注册与执行中心每个 Agent 需要知道自己能调用哪些工具以及每个工具的参数结构是什么。工具执行中心通常维护一张工具注册表[ { name: search_web, description: 搜索互联网信息返回相关网页摘要, parameters: { type: object, properties: { query: { type: string, description: 搜索关键词 } }, required: [query] } }, { name: execute_code, description: 在沙箱环境中运行 Python 代码, parameters: { type: object, properties: { code: { type: string, description: 要执行的 Python 代码 } }, required: [code] } } ]这个工具注册表在每次调用模型时会被转换为模型能识别的函数描述随 Prompt 一起发送给模型。运行时还需要做一层“参数校验”因为模型偶尔会生成非法参数。5.4 观测与日志长任务系统必须可观测。因为一个任务执行 20 步中间任何一步出错都需要有日志可以回溯。实践中建议记录以下关键事件每一步的思考内容。每一步的工具调用请求与原始返回。上下文窗口的占用率变化。每一步耗时与 Token 消耗。错误信息与重试决策。有了这些日志排查“为什么 Agent 在 17 步突然做出了错误决策”这种问题时才能有据可依。5.5 并发与隔离在生产环境系统通常要同时处理多个用户的长任务请求。如果所有任务共用一个上下文池任务之间会互相污染。建议采用“任务级隔离”方案每个任务拥有独立的运行时实例、独立的上下文窗口、独立的工具调用配额。在 Kubernetes 环境中可以通过 Pod 级别的调度实现资源隔离。你也可以使用 Actor 模型将每个长任务封装为一个 Actor通过消息传递进行通信。6. 完整实战设计一个最小可运行的长任务 Agent理论讲再多不如动手写一个。下面我用 Python 实现一个极简但完整的长任务 Agent 示例。这个示例会模拟一个“先搜索信息 → 再写代码 → 再运行代码 → 最后总结”的四步任务但代码结构上支持无限多步执行。6.1 项目结构agent-demo/ ├── agent.py # Agent 主程序 ├── tools.py # 工具注册与执行 ├── memory.py # 简单上下文管理 └── requirements.txt # 依赖6.2 依赖准备openai1.0.0 python-dotenv1.0.0这个示例是基于“模型具备工具调用能力”这个前提来设计的。如果你的模型不支持结构化函数调用可以退化为“让模型输出 JSON 文本”原理相同。6.3 工具层tools.py# 文件路径agent-demo/tools.py import json # 一个极简的工具注册表实际项目中应该支持动态注册 TOOL_REGISTRY { search_web: { description: 搜索互联网信息返回相关网页摘要。, parameters: { type: object, properties: { query: { type: string, description: 搜索关键词 } }, required: [query] }, handler: lambda query: f根据「{query}」找到 3 条结果Python 列表去重可以使用 set()保持顺序可以使用 dict.fromkeys()。 }, execute_code: { description: 在沙箱环境中运行 Python 代码返回执行结果。, parameters: { type: object, properties: { code: { type: string, description: 要执行的 Python 代码 } }, required: [code] }, handler: lambda code: run_code_safely(code) } } def run_code_safely(code: str) - str: 在实际生产中这里应该使用 Docker 或子进程沙箱来隔离代码执行。 本示例只做演示不真正执行任意代码。 # 为了安全这里只允许执行 print 语句的代码 if print( not in code: return 代码中没有找到 print 语句请修改代码后重试。 try: exec(code, {__builtins__: {}}, {}) return 代码执行成功结果已通过 print 输出。 except Exception as e: return f代码执行失败: {str(e)} def execute_tool(tool_name: str, arguments: dict) - str: 执行指定的工具并返回字符串结果。 if tool_name not in TOOL_REGISTRY: return f错误未知工具 {tool_name} try: handler TOOL_REGISTRY[tool_name][handler] return handler(**arguments) except TypeError as e: return f工具参数错误: {str(e)} except Exception as e: return f工具执行异常: {str(e)} def get_tool_schemas() - list: 返回供模型使用的工具描述列表。 schemas [] for name, meta in TOOL_REGISTRY.items(): schema { type: function, function: { name: name, description: meta[description], parameters: meta[parameters] } } schemas.append(schema) return schemas6.4 记忆管理memory.py真实的长任务场景中上下文管理器是非常复杂的这里先实现一个最简单的版本保留所有消息直到超过阈值才触发截断策略。# 文件路径agent-demo/memory.py class SimpleMemory: 极简上下文管理器。 维护消息历史并在超过最大长度时进行裁剪此处只保留系统消息和最后两轮。 def __init__(self, max_messages: int 10): self.max_messages max_messages self.messages [] def add_message(self, role: str, content: str): self.messages.append({role: role, content: content}) self._trim() def get_messages(self) - list: return self.messages def _trim(self): # 保留系统消息和最后 N 条消息 if len(self.messages) self.max_messages: # 假设第一条是系统消息保留它 system_msgs [m for m in self.messages if m[role] system] recent_msgs self.messages[-self.max_messages:] self.messages system_msgs recent_msgs6.5 Agent 主循环agent.py这是核心。在这个 Agent 里主循环的逻辑是调用模型获取下一步动作。模型可能返回两种结果最终回答、工具调用。如果是工具调用执行工具追加观察信息再次调用模型。直到模型返回最终回答或者达到最大步数。# 文件路径agent-demo/agent.py import json import os from openai import OpenAI from tools import get_tool_schemas, execute_tool from memory import SimpleMemory class LongTaskAgent: def __init__(self, model: str gpt-4o-mini): self.model model self.client OpenAI( api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL) # 兼容第三方网关 ) self.memory SimpleMemory(max_messages20) self.max_steps 50 # 最多执行 50 步 def run(self, task: str) - str: # 1. 初始化记忆 self.memory.add_message(system, 你是一个擅长拆解任务并执行工具的 AI 助手。 你需要一步步完成任务每轮最多调用一个工具。 当所有子任务都执行完毕后再给出最终总结。) self.memory.add_message(user, task) # 2. 循环执行 for step in range(1, self.max_steps 1): print(f\n Step {step} ) # 调用模型 response self.client.chat.completions.create( modelself.model, messagesself.memory.get_messages(), toolsget_tool_schemas(), tool_choiceauto ) message response.choices[0].message # 打印模型当前的思考内容如果有 if message.content: print(f[模型回复] {message.content[:200]}) # 3. 检查是否有工具调用 if message.tool_calls: for tool_call in message.tool_calls: fn_name tool_call.function.name fn_args json.loads(tool_call.function.arguments) print(f[工具调用] {fn_name}({json.dumps(fn_args, ensure_asciiFalse)})) # 执行工具 result execute_tool(fn_name, fn_args) print(f[工具结果] {result[:200]}) # 将模型请求和工具结果追加到 memory self.memory.add_message(assistant, message.content or ) self.memory.add_message(tool, result) continue # 4. 没有工具调用说明模型给出了最终回答 if message.content: return message.content return 已达到最大执行步数任务未能在限定步骤内完成。 if __name__ __main__: agent LongTaskAgent() task ( 我需要完成一个 Python 任务\n 1. 搜索 Python 列表去重的最佳方法\n 2. 根据搜索结果写一段 Python 代码实现去重并保持顺序\n 3. 执行这段代码\n 4. 最后总结结果。 ) final_answer agent.run(task) print(\n 最终回答 ) print(final_answer)6.6 运行与预期结果在项目目录下执行export OPENAI_API_KEY你的_API_KEY python agent.py预期你会看到类似下面的输出过程 Step 1 [模型回复] 我需要先搜索 Python 列表去重的最佳方法。 [工具调用] search_web({query: Python 列表去重方法}) [工具结果] 根据「Python 列表去重方法」找到 3 条结果... Step 2 [模型回复] 根据搜索结果我来写一段代码实现列表去重并保持顺序。 [工具调用] execute_code({code: ...}) [工具结果] 代码执行成功结果已通过 print 输出。 Step 3 [模型回复] 代码已经成功执行。模型会在第 3 步左右返回最终总结。这个示例虽然只有 3 步左右但它的架构完全支持扩展到几十步。6.7 示例代码的注意点这个代码是“最小演示版”直接用于生产会存在几个问题我在这里提前说明第一没有实现断点续跑。运行时重启后任务状态会丢失。生产环境建议把 memory 中的数据持久化到 Redis。第二上下文裁剪策略过于简单。真实场景需要摘要和检索增强否则一旦工具返回大段文本memory 很快就会爆炸。第三工具执行使用了 exec非常不安全。生产环境必须使用 Docker 沙箱或子进程隔离。7. 常见问题与排查思路在实际开发和生产中Agent 长任务失败的形态五花八门。这里整理几个高频问题及其排查方案都是实战中非常典型的情况。问题现象常见原因解决思路任务执行到第 10 步后Agent 开始重复做同一件事上下文窗口丢失了早期“已完成”信息模型误以为之前步骤没做过将“已完成子任务列表”压缩后注入 Prompt并不断更新模型连续生成无效的工具调用参数工具描述不够清晰或模型本身函数调用能力较弱优化工具名称和参数描述增加示例选择更擅长工具调用的模型工具返回结果太长下一轮 Prompt 超限没有对工具返回做截断或摘要日志类返回只保留尾部数据类返回只保留结构摘要Agent 中途因异常退出任务白跑运行时缺少错误恢复机制加入重试、退避、分支策略至少实现“失败后重试一次”模型在长任务中输出被截断单次输出 Token 上限不够让模型分步输出不要一次生成全部内容多个任务并发运行时上下文互相干扰缺少任务级隔离每个任务使用独立的 Agent 实例和独立的 memory7.1 模型“失忆”怎么排查如果 Agent 在后期步数表现出“忘记目标”的迹象第一步先看日志在最近几轮的 Prompt 中是否还包含系统提示词中的任务目标如果上下文管理器把系统消息也截断了那模型自然失去目标锚点。解决方案是把“任务目标”单独保留不参与上下文裁剪。即使历史全部截断任务目标也永远在 Prompt 的最前面。# 示例裁剪时始终保留任务目标 def build_prompt(system_prompt: str, task: str, history: list, max_len: int) - list: messages [{role: system, content: system_prompt}] messages.append({role: user, content: task}) # history 按最近优先保留 remaining_len max_len - 2 messages.extend(history[-remaining_len:]) return messages7.2 工具调用循环死锁怎么处理有时模型会反复调用同一个工具拿到相同的结果然后继续调用同一个工具形成死循环。最常见的原因是任务目标本身与工具能力不匹配。排查时先问自己两个问题模型能通过当前工具获取到解决问题的信息吗模型输出的调用参数是否总是缺失关键字段如果是参数问题可以通过“工具参数校验器”强校验发现缺参时让系统返回一条明确错误信息引导模型修正。如果工具能力本身不够需要新增更合适的工具而不是试图靠 Prompt 硬掰。7.3 Token 被截断怎么处理在开发 Agent 时最常看到的报错之一就是已达到输出 token 上限回答被截断已有输出保留在对话中。这不是 API 故障只是模型输出长度达到限制。解决手段有两类第一类降低单次输出长度期望把大任务拆成多步。第二类改用输出上限更高的模型或配置更大的 max_tokens 参数如果服务商支持。8. 最佳实践与工程建议到此我们已经从模型层到运行时层再到系统架构层完整拆解了 Agent 长任务的工作原理。下面我根据实际项目经验给出几条高价值的工程建议。8.1 尽量让 Agent “小步快跑”不要追求一步到位很多长任务失败的本质原因是“步子迈得太大”。模型试图在一次输出中完成思考、工具调用、总结等多件事结果输出 Token 上限被撑爆。建议做法每轮只让模型做一件事要么思考要么调用一个工具。这样即使错误发生影响面也是可控的。8.2 为每一个长任务接入“任务看板”生产级 Agent 系统需要随时能回答三件事任务当前卡在哪个步骤为什么卡住下一步预计什么时候完成实现方式就是引入持久化任务状态表。推荐字段如下字段说明task_id任务唯一标识current_step当前已执行步骤total_steps规划总步骤statuspending/running/paused/completed/failedlast_error最近一次错误信息created_at创建时间updated_at最后更新时间有了这个表调度器可以随时干预人工暂停、跳过步骤、终止任务。8.3 构建工具调用失败熔断机制工具调用失败是长任务中最常见的故障源。建议在运行时加入“熔断器”模式同一个工具连续失败 3 次则标记为不可用后续所有尝试直接短路不再消耗模型调用额度。class CircuitBreaker: def __init__(self, max_failures: int 3): self.max_failures max_failures self.failures 0 self.is_open False def record_failure(self): self.failures 1 if self.failures self.max_failures: self.is_open True def record_success(self): self.failures 0 self.is_open False def can_execute(self) - bool: return not self.is_open8.4 日志里永远记录 Token 消耗不要只记录任务结果还要记录每一步的 Token 消耗、耗时和模型版本。这样不仅可以做成本分析还能帮助判断“模型升级后是否影响任务质量”。8.5 在生产环境使用任务队列不要直接同步调用当你的 Agent 需要跑几十步时单次 HTTP 请求很难承载这么长的执行时间网关通常有 30-60 秒超时。正确做法是API 只负责创建任务并返回 task_id后台 Worker 异步执行任务前端或客户端通过轮询或 WebSocket 获取任务进度。8.6 上下文管理必须考虑成本很多人忽略一点Prompt 越长模型调用费用越高。长任务如果上下文管理不当成本会成倍增长。实践建议是能摘要就摘要能截断就截断。每执行 5 步用一次“压缩调用”把历史总结成一段 200 字以内的摘要再继续后续步骤。这能显著降低长期运行的成本。9. 总结与进阶方向Agent 能连续跑几十步不是某个单一组件的功劳而是模型层、运行时层与系统架构层协同工作的结果。模型层提供了“理解与生成”的基础能力包括工具调用与推理循环。运行时层解决了上下文管理、状态维护、错误恢复与调度执行。系统架构层则保障了并发、隔离、可观测性与生产可用性。如果你正打算深入 Agent 开发建议按照以下路径持续学习先熟悉 ReAct 模式的完整流程亲手实现一个 10 行左右的 Agent 循环。再负责上记忆管理理解上下文裁剪、摘要与向量检索的适用场景。然后学习 LangGraph、AutoGen 这类成熟框架看看它们的运行时是怎么设计的。最后把 Agent 放到真实业务场景中打磨尤其是测试工具调用的边界情况与异常恢复能力。如果你对长任务架构中某个环节特别感兴趣欢迎在评论区留言我可以在后续文章中展开写。觉得有帮助的朋友也可以先收藏备用后续调试 Agent 时再翻出来对照排查。