还记得我第一次写 Agent 原型时的代码吗?一台笔记本,一个 while 循环套上模型调用,工具函数全部写死在代码里。跑通 Demo 的那一刻觉得自己已经掌握 Agent 开发了,直到被生产环境狠狠教育:模型偶尔会反复调用同一个失败工具,上下文越滚越大最终爆掉,想回滚一个版本无从下手,想让两个任务并行跑更是天方夜谭。回头去复盘,问题根本不是"模型不够聪明",而是底层缺了一套工程结构。
后来我逐渐把 Agent 工程拆成 Harness、Loop、Graph 三层来设计,整个系统的稳定性、可观测性、可维护性都上了一个台阶。这篇文章把这三层架构讲透,重点落在真实项目里会踩的坑:Harness 这层我会聊运行时底座、Skill 离线部署、插件加载失败和版本回退;Loop 这层聊循环失控、上下文膨胀、多窗口状态隔离;Graph 层聊多 Agent 编排、商图监控、流程建模。无论你是刚入门的开发者,还是在维护线上 Agent 服务的老手,这套分层都能直接拿来用。
1. 三层架构到底是什么:为什么 Harness、Loop、Graph 缺一不可
1.1 从"能跑的 Agent 代码"到工程化系统
很多人的第一个 Agent 都是这么写出来的:一个 while True,把用户问题塞给大模型,模型说要调工具就调工具,把结果拼回去再问模型,直到模型说"完成了"。这套东西跑通一个简单对话没问题,但它有三个致命缺陷:
- 没有边界。模型能接触到什么工具完全靠提示词约束,一个提示词写漏了,模型就可能去调用不该调的系统命令。
- 没有恢复机制。循环中途崩了,没有 checkpoint,下一次启动什么上下文都没有,整个任务从零再来。
- 没有拓扑。任务只能线性推进,遇到"先并行采集三个数据源、再汇总分析"这种需求,代码就爆炸了。
Harness、Loop、Graph 三层拆分,本质就是把这三个问题分别交给不同的层去解决。
1.2 三层各自的职责与边界
拿我自己的定义来说,这三层的边界非常清晰:
| 层次 | 一句话定义 | 解决的核心问题 | 生产中的典型产物 |
|---|---|---|---|
| Harness | Agent 的运行时与边界设施 | 模型怎么连、工具怎么给、技能怎么挂、记忆怎么存、权限怎么控 | 配置文件、插件系统、Skill 包、审计日志 |
| Loop | Agent 的决策与行动循环 | 模型与工具如何反复交互,直到任务完成或明确失败 | 主循环状态机、重试策略、终止条件 |
| Graph | 多 Agent 的编排拓扑 | 多个子 Agent 和工具节点如何组织成 DAG、分支、并行、人机回环 | 编排定义、节点数据契约、商图监控视图 |
它们之间的关系是递进的:Graph 层决定"这件事分几步做、哪些步骤并行";Loop 层决定"每一步内部怎么反复试";Harness 层决定"所有这些运行在什么样的沙箱里、能被谁调用、出问题时到哪里查"。
1.3 直观类比:驾驶舱、飞行检查和塔台航线
我的经验是,纯讲概念容易晕,用飞机来类比会清楚得多。
- Harness 是驾驶舱与仪表盘。飞行员通过仪表盘看高度、油量、航向,通过操纵杆控制飞机。Harness 给模型提供了"仪表盘":工具调用接口、上下文窗口、日志、安全权限。模型不直接碰发动机,而是通过 harness 暴露的控件来操作。
- Loop 是飞行员每圈的标准检查程序。每次循环都是"观察仪表数据、做判断、操纵飞机、再看仪表反馈"。单架飞机飞得好不好,就取决于这个循环是否稳定。
- Graph 是塔台和航线图。多架飞机同时在空中,谁先起飞、谁在哪个高度层飞行、谁需要绕飞,是塔台基于航线图来协调的。Graph 层就是塔台,它管理的是多个 Loop 实例之间的编排关系。
顺带说一句,别被 Harness 这个词吓到。做硬件的人听到 harness 会想到线束,把一堆散乱的线整理成一个可插拔的连接器;做 Agent 的人说 harness,是把模型调用、工具调用、记忆存取这些散乱的逻辑整理成一个可运维的运行时外壳。两个领域的直觉是相通的。
2. Harness 层:Agent 的运行时底座与可控边界
2.1 Harness 到底在管哪些事
我见过不少团队把 Harness 理解成"一个启动 Agent 的入口脚本",这个理解太窄了。真正的 Agent Harness 至少要管好下面这几件事,缺一件都会在生产环境里还债:
| 组件 | 职责 | 生产环境容易踩的坑 |
|---|---|---|
| 模型网关 | 统一封装底层模型 API,支持多模型切换、流式输出、超时重试 | 换模型时只改代码不改系统提示词,行为大变 |
| 工具注册表 | 声明 Agent 可以调用的工具,包含参数 schema、权限级别 | 工具返回数据过大,直接把上下文撑爆 |
| 插件总线 | 动态加载插件,扩展 harness 能力 | 插件入口未激活,加载失败但主程序不报错 |
| Skill 管理 | 把提示词、工具组合、示例打包成可复用的技能单元 | Skill 包放在错误目录,harness 启动时根本没有扫描到 |
| 记忆接口 | 为 Loop 层提供短期上下文、摘要、向量检索的统一读写入口 | 记忆写操作丢在主循环里,性能瓶颈 |
| 审计与安全 | 记录工具调用链、敏感操作确认、权限拦截 | 审计日志不全,事故后无法追溯 |
对这些组件,我个人的排序是:安全边界和审计日志优先级最高,然后是记忆接口。模型网关和工具注册表反而是相对容易替换的部分。
2.2 Harness 和 Agent 的区别:运行时与业务逻辑
很多人问过我同一个问题:Harness 和 Agent 到底是不是一回事?
我的理解是这样的。Agent 是一个由模型权重、系统提示词、工具 schema 共同定义出来的"行为体"。你把提示词改一句话,Agent 的行为就变了。但 Harness 是承载这个行为体的"环境"——挂载哪些模型、开放哪些工具、按什么权限策略审核、用什么样的日志格式记录。Harness 不会因为改一句提示词而变化,它是相对稳定的运行骨架。
用一个具体例子说明。用同一个开源 LLM 底座,分别接两套提示词和工具集,你可以跑出一个"客服 Agent"和一个"代码审查 Agent"。但如果换了一个 Harness,给 Agent 提供不了代码仓库读取能力,代码审查 Agent 就算提示词写得再好也跑不起来。这就能看出来,Agent 解决的是"聪明不聪明",Harness 解决的是"能不能稳定地跑在受控环境里"。
2.3 实操:私有化部署 Harness 与 Skill 加载
2.3.1 离线环境的安装思路
在生产里,很多 Agent 服务要部署到隔离网络环境。比如企业内网要跑一套基于开源模型的 Agent 助手,外网的公共模型服务和插件市场都不可达。这个场景我部署过好几次,踩过的坑可以汇总成一句话:先把所有依赖在外面解决掉,再整体搬进去,最后逐个验证入口。
- 第一,准备依赖包。在能联网的机器上把所有 Python 或 Node 依赖下载成离线安装包,尤其要注意一些需要编译的依赖,必须指定与目标服务器相同的 CPU 架构和操作系统版本,否则装进去就是段错误或者缺库报错。
- 第二,确认版本一致性。Harness 主程序、模型网关插件、内置工具插件之间通常有版本匹配关系,跨版本组合时会出现运行期才爆的兼容性问题。
- 第三,用白名单方式启动。先只启用核心插件,跑通最小链路,再逐步放开其他插件。这个习惯能帮你快速定位是环境问题还是插件本身的问题。
2.3.2 Skill 的本质与离线部署步骤
网上搜"Agent Skill 教程"的非常多,但很多教程只讲了前端怎么调,没讲清 Skill 到底是什么。我在实践中把它理解为三件套:一个结构化的提示词片段、一组工具调用的绑定声明、一份可执行的示例或脚本。Harness 启动时会扫描指定的 Skill 目录,把每个 Skill 注册成可加载的"能力单元"。
放在内网服务器上的部署步骤其实不复杂:
- 在开发机上把 Skill 目录整体打个包,里面通常包含 skill 描述文件、提示词模板、配套脚本。
- 把 Skill 包拷贝到目标服务器的数据目录下,注意不是放在主程序目录,而是要放到 Harness 配置里指定的技能根目录。
- 重启 Harness,或者在运行时触发技能目录重载。
- 用一条包含该 Skill 关键动作的测试指令验证:服务端日志里能看到 skill 命中,工具调用记录能查到,才能算部署成功。
我踩过最冤的一次坑,是 Skill 包放对了目录,但描述文件里的格式写错了一个字段,Harness 启动时直接静默跳过,从外部看就是"Agent 什么技能都没有"。排查半天最后是在日志的 Debug 级别里才看到解析告警。
2.3.3 插件加载失败的排查链路
启动时如果出现类似 "entry did not activate" 的插件加载失败,别慌,按下面这条链查,大概率 10 分钟内定位:
- 第一步,打开完整日志,找到该插件入口的加载记录。Harness 一般会打印是哪一步没激活,是入口文件不存在,还是初始化函数抛了异常。
- 第二步,检查插件的入口路径配置是否与实际文件名一致。很多加载失败纯粹是大小写或者路径前缀对不上。
- 第三步,检查插件版本与 Harness 主版本是否兼容。跨大版本插件经常出现 API 签名不匹配,表现为"加载了但没有任何功能暴露"。
- 第四步,把该插件临时从启动列表里去掉,确认主程序能正常启动。这一步能帮你判断是不是某个插件拖垮了整个启动链。
2.3.4 代码回退的正确姿势
热词里有一条是"deepseek harness 代码回退",其实就是升级翻车后要回退到上一版本。我的建议是,不要直接覆盖旧版本,而是先做三件事:
- 备份当前配置目录和 Skill 目录。因为新版本启动时可能会迁移配置结构,回退后会用到原配置。
- 确认旧版本与当前 Skill 包的兼容性。Skill 是一种相对独立的东西,但如果是随版本一起更新的内置 Skill,回退之后要一并还原旧版本。
- 回退后跑一遍冒烟用例。至少覆盖一次带工具调用的多轮对话,确认模型网关、工具执行、日志输出三条链路都通。
我曾经因为只回退了二进制包、没有回退配套 Skill,导致新老版本加载同一份 Skill 时解析规则不一致,整条 Agent 链路瘫痪半小时。从那以后我学乖了:版本回退永远是一个"整体快照"的回退,不是单个文件的替换。
2.4 Harness 层的安全边界
安全这个话题单独拎出来说。Agent 的能力越强,安全边界越重要。我在 Harness 层至少有四个必做项:
- 工具白名单。模型只能调用显式注册过的工具,注册时就要填权限级别,比如只读、可写、高危。
- 高危操作二次确认。对删除文件、执行系统命令这类操作,Harness 可以拦截下来,要求人工在交互界面点确认,再放行。
- 调用链审计。每次工具调用都要记录:谁触发的、带了什么参数、返回了什么、用了多长时间。
- 上下文隔离。不同会话、不同用户的 Agent 实例,在记忆和文件系统访问层面要隔离,防止 A 用户的会话读到 B 用户的数据。
很多团队在 Demo 阶段觉得这些是多余的,等 Agent 真的开始连数据库、发邮件时,才知道安全边界不是某一行代码,而是 Harness 层的整体设计。
3. Loop 层:Agent 的心跳循环,也是最容易失控的地方
3.1 三种主流循环形态对比
Loop 层是 Agent 真正"思考 + 行动"的地方。生产里我常用的循环形态有三种,不是所有任务都该用同一种:
| 循环形态 | 工作方式 | 适合场景 | 典型风险 |
|---|---|---|---|
| ReAct | 推理、行动交替进行,每次观察工具结果后生成下一步推理 | 需要动态查信息、反复实验的任务 | 容易在同一个工具上打转 |
| Plan-and-Execute | 先规划出步骤清单,再逐个执行,执行结果反馈给规划器 | 多步骤明确的任务 | 环境变化后计划过期 |
| Self-Refine | 生成结果后自我批评,再基于批评意见重写 | 写作、代码改进、需要质量迭代的任务 | token 消耗成倍增加 |
ReAct 的核心循环用伪代码表示非常直观:
while not finished: thought, action, action_input = model(context) if action == "finish": finished = True else: observation = tool_execute(action, action_input) context.append(observation)这个循环简洁,但它暴露了 Agent 生产化最尖刻的问题:**循环什么时候才真正"finished"?**模型说 "finish" 不代表结果正确,模型也可能永远不说 finish,就在那儿一遍遍调同一个工具。
3.2 循环失控的典型症状与刹车机制
很多人看到 "self-referencing loop detected" 这个报错,是在写后端序列化的时候,对象相互引用导致无限递归。我在 Agent 系统里见过另一层意义上的"自我引用循环",症状是高度相似的:模型在上下文里反复引用自己前面某一步的推理,导致同一段内容不断膨胀,最终要么把上下文撑爆,要么把同一个工具调用重放几十遍。
应对循环失控,我的经验是必须上"刹车机制",而且要在设计初期就上,不是等出了事故再补:
- 最大迭代步数。无论什么循环,都要有一个硬上限。常见做法是在任务开始前根据任务复杂度分配一个步数预算,比如 20 步或 50 步,超了就强制收敛。
- Token 预算。每次循环前检查已消耗 token,接近阈值时强制要求模型进入收尾总结。
- 幂等工具设计。工具本身要做到重复调用不会产生副作用差异。比如"创建订单"这种操作必须带幂等键,循环重放时不会真的一次次创建订单。
- 人工中断与人工确认。给人类操作员一个随时可以暂停循环、修改上下文、重新指定方向的入口。
- 行为检测。识别到模型连续三次调用同一个工具且入参完全一致时,主动打断并提示"当前动作已重复,建议换策略"。
有一次测试环境跑一个多小时的批量任务,某个子 Agent 在循环里反复调用一个读取接口,因为返回数据一直不满足条件,它又只会这一个动作,白白烧了上万次调用。加了一个"相同工具连续调用次数上限"之后,这类问题直接被提前终止,系统会自动标记为需要人工介入。
3.3 多窗口与会话隔离下的 Loop 状态管理
热词里有个"loop 多窗口",实际指的是多会话并行场景。一个生产服务里,往往同时跑着十几个任务,每个任务一个独立的 Loop 实例,这些实例之间不能共享上下文,不然 A 任务的中间状态就会污染 B 任务的推理。
我的设计遵循三条原则:
- Loop 实例独立。每个会话/窗口维护自己的历史消息列表和状态对象,Harness 层通过会话 ID 做路由。
- 状态快照定期落盘。每隔 N 步把当前上下文摘要、待执行步骤、已执行结果序列化保存。进程崩溃后,可以恢复到最近的检查点继续跑,而不是整条链重来。
- 状态隔离不等于数据完全隔离。如果两个任务需要共享某个知识库查询结果,图(Graph)层可以通过只读共享数据源的方式传递,而不是直接共享 Loop 内的原始上下文。
3.4 上下文窗口溢出与记忆策略
长任务跑到后面,最头痛的就是上下文爆掉。我的方案是分三级记忆:
- 短期上下文:当前循环窗口内的消息。控制数量,超过阈值就做一轮压缩。
- 中期摘要:每完成一个子目标,把这段对话压缩成摘要,摘要取代原始对话加入上下文。压缩时我会要求模型保留:已完成的关键动作、未解决的关键问题、可复用的事实。
- 长期记忆:把需要跨会话保留的事实写入向量数据库,通过检索按需拉取,而不是全量塞进上下文。
到这里你会发现,记忆已经不只是 Loop 层的事,它需要 Harness 层提供存储接口。这就是三层架构的有趣之处:每一层有个自包含的职责,但真正跑得稳,靠的是层与层之间的契约。记忆接口就是 Loop 与 Harness 之间的契约之一。
4. Graph 层:从单 Agent 单循环到多 Agent 编排
4.1 为什么循环本身不够,还需要图
单个 Loop 哪怕再稳定,能处理的也只是线性任务。生产里真正的复杂任务长这样:先判断用户意图,再决定调用 NLP 服务还是订单服务,还要同时查库存和物流,最后汇总生成报告,如果某个环节失败还要分支重试。
这种拓扑没法用一个大循环写完。于是在 Loop 之上,我们引入 Graph 层,用节点和边描述整个任务流:
- 节点是 Agent、工具调用、人工确认点或简单的转换逻辑。
- 边是数据流和依赖关系。
- 图上还可以有条件分支、并行分支、循环回路。
我常用的编排思路是:Graph 管拓扑,Loop 管单点深度。Graph 上一个"查库存"节点内部是 Loop 还是单次调用,这是节点自己的事,Graph 不关心。反过来,Loop 里冒出的"需要并行处理两个数据源"这种请求,应该上抛给 Graph 层,由 Graph 来做真正的并行。
4.2 有向无环图、条件边、并行与人机回环
拿一个实际的运维故障诊断 Agent 举例,Graph 可以设计成:
- 节点 1:收集信息。输入服务器 IP,汇聚日志、监控指标、工单上下文。
- 节点 2:分析原因。基于节点 1 的数据生成候选故障原因列表。这个节点内部是一个 Self-Refine 循环,可能要跑好几轮。
- 节点 3/4/5:并行验证。三个子 Agent 分别验证 CPU 异常、磁盘异常、网络异常,它们互不依赖,可以同时跑。
- 节点 6:人机回环。把候选原因和验证证据发给运维人员确认,等人工确认后再往下走。
- 节点 7:生成修复建议并执行。
这里有几个 Graph 层常见的工程细节,值得展开说。
条件边。节点 2 如果判断"无候选原因",直接走节点 7 输出"无法诊断,建议人工介入",而不是硬走验证分支。条件边的判定逻辑不能写在节点内部,要作为图的一部分显式建模,这样才能在监控图上看到走了哪条分支。
并行节点之间的数据契约。节点 3/4/5 并行跑完后,输出的结果格式必须统一,否则节点 6 没法聚合。我一般在建模时给每条边定义 schema,节点输入输出都按 schema 校验。
人机回环。这是 Graph 层比单体循环强太多的地方。单循环里要插一个"等人回应"的操作非常别扭;Graph 层可以直接挂一个"人工确认"节点,这个节点执行到一半暂停,等 Harness 层的交互接口收到人工反馈后再继续。
4.3 商图视角:把细粒度执行压成可监控的层面
"商图"这个词最早是图论里的概念,英文叫 quotient graph。它的核心思想很朴素:把图中一些节点按某种等价关系合并成一个"超级节点",从而得到一个更粗粒度的图。在大图上,商图可以保留整体结构、压缩细节。
Agent 系统的监控就是一个非常适合用商图的场景。底层的 Graph 可能有几十个节点,有 Agent 节点、工具节点、分支判断节点,还有重试节点,直接看监控面板会非常杂乱。我的做法是:
- 定义"阶段标签",把同属一个阶段(比如信息收集、原因分析、方案执行)的节点聚合为商图上的一个超级节点。
- 监控面板默认展示商图,看到的是三五个阶段框和它们之间的流转状态。
- 某一阶段异常时,点击进入原始图,查看具体是哪个底层节点卡住。
商图不是花架子。它解决的是真实的生产痛点:一个几小时的长流程任务,运维要能一眼看出它现在流到哪一步、卡在哪里;如果只展示原始节点图,信息密度太大,人眼根本处理不过来。
4.4 用 Graph Builder 建模的实际流程
现在不少编排框架都带可视化 Graph Builder,你可以用拖拽方式搭出节点和边,再导出为结构化定义。我自己的建模步骤一般是五步:
- 列出所有步骤,先不管并行,把流程从头到尾写出来。
- 标注依赖,找出哪些步骤必须等前面某一步完成,哪些可以同时进行。
- 标出分支条件,哪一步的结果会改变后续路径。
- 标出人工介入点,哪里必须由人确认或输入。
- 定义节点间数据契约,明确每个节点输出什么、下一个节点需要什么。
把这五步做完,Graph 的定义基本就成型了,代码层面的实现只是机械的翻译工作。很多团队的 Agent 项目乱,不是因为模型不给力,而是步骤 1 和步骤 2 根本没人认真做,直接跳到代码层写编排。
5. 三层联动:一次请求在生产环境中的完整旅程
5.1 从入口到 Harness,再到 Loop,再到 Graph
把三层拆开讲完之后,最后合起来看一次真实请求是怎么跑的。
用户发来一条消息,假设是"帮我看看服务器为什么这么慢"。完整链路是这样的:
- 入口接入。HTTP/WebSocket 请求到达 Harness 暴露的接口,Harness 先做身份认证和会话识别。
- Harness 加载上下文。Harness 从记忆接口取这个会话的长期记忆,从 Skill 仓库加载相关技能,组装出系统提示词和工具列表。
- 进入 Loop。Harness 把组装好的上下文交给 Loop 层,Loop 开始标准的"思考-行动-观察"循环。
- Loop 发现任务复杂。多轮对话后,Loop 意识到需要并行查日志和查监控,此时它把这些动作抽象成一个子任务列表,上抛给 Graph 编排引擎。
- Graph 编排。Graph 层按既有拓扑创建多个子任务节点,有些节点内部又各自创建了自己的 Loop 实例去执行深度探索。
- 结果汇聚。子任务全部结束后,Graph 层把结果按数据契约汇总,交回最初的 Loop 实例。
- Loop 总结回答。最初的 Loop 基于汇聚结果,生成最终回答返回用户。
- Harness 收尾。Harness 把这次的完整轨迹写入审计日志,把值得沉淀的事实写入长期记忆。
这个链路看下来,你会发现每一层都只做好自己的事:Harness 提供能力与边界,Loop 提供思考与行动,Graph 提供组织与调度。任何一个环节的生产故障,都能快速定位到具体层级去排查。
5.2 Graph 编排介入的时机判断
有读者可能会问:是不是所有请求都要走完完整链路?我的答案是:不要过度设计。
简单任务,比如"把这段英文翻译成中文",直接在 Loop 层两三步内完成就够了,没必要建一张图。Graph 的价值在复杂任务里才显现出来。我一般用两条标准来判断是否要启用 Graph:
- 任务是否需要并行。只要出现"同时查多个数据源再合并",单体循环就很难优雅处理,上 Graph。
- 任务是否有分支和人工介入。只要路径会随中间结果改变,或者中途需要人确认,上 Graph 能让你用结构化的方式管理这些变化。
实际上我在生产里会把两者结合起来:默认请求先在 Loop 跑,Loop 遇到复杂度超出阈值的情况时再触发 Graph 编排。相当于让 Graph 作为"升级通道",而不是让所有流量都先过一遍图。
5.3 回滚、重试与观测:三层分别负责什么
最后用一张表总结三层在生产运维中分别扛哪类问题:
| 运维动作 | Harness 层 | Loop 层 | Graph 层 |
|---|---|---|---|
| 版本回滚 | 回退 Harness 二进制、配置、Skill 包 | 回退循环策略参数 | 回退编排定义 |
| 任务重试 | 保证工具调用可重放 | 循环从最近 checkpoint 恢复 | 单个节点重跑,不整图重跑 |
| 观测 | 工具调用审计、资源消耗 | 循环次数、token 消耗、终止原因 | 商图状态、节点耗时、分支走向 |
有个细节值得特别提醒:Graph 层支持单节点重跑,这看起来只是编排能力,实际对生产成本影响极大。如果整条图重跑,意味着之前所有子 Agent 调用的模型费用全部白费。能在错误节点精准重跑,省下来的直接就是真金白银。
5.4 不同实现生态下的统一分层模型
聊实现的时候,很多人会提到基于 Rust 写的轻量 Agent、挂载在 Obsidian 里的个人知识管理 Agent、或者主打端侧运行的 Agent 运行时。这些项目听上去形态各异,但底层几乎都逃不出三层模型:Rust 实现往往把 Harness 做得极小,适合资源受限的边缘环境;以 Obsidian 知识库为记忆源的 Agent,本质是把长期记忆放到本地文件系统,Harness 提供检索工具;主打随时可用的轻量 Agent 运行时,则是在端侧做了一个自适应的 Harness 层,把模型接入、工具权限、会话管理全部收敛在里面。
我个人的观点是:技术栈可以百家争鸣,但架构分层值得统一。统一分层带来的好处是团队沟通时可以拿层来对齐,比如"这个 bug 在 Loop 层"、"那个需求要改 Graph 的条件边",一句话说清楚问题域,比扒着源码争论半天高效得多。
从最早那个 while 循环原型,到今天维护几十个线上 Agent 服务,我最大的体会是:Agent 工程化并不神秘,它就是把"模型调用"这个单一能力,装进可控的运行时,配上会收敛的循环,再接上可编排的拓扑。三者各司其职,系统的复杂度就压得住。
最后分享一个我在新项目里执行的小习惯:先画 Graph,再定 Loop,最后配 Harness。流程没理顺之前不写代码,循环策略没定之前不接模型,权限边界没画清楚之前不接工具。顺序反了,后面的返工一定让代价翻倍。