📌 系列简介:「JS全栈AI Agent学习」系统学习 21 个 Agent 设计模式,篇数随学习进度持续更新。
⏱️ 预计阅读时间:15 分钟
📖 原书地址:adp.xindoo.xyz
前端转 JS 全栈,正在学 AI,理解难免有偏差,欢迎批评指正 ~
🗺️ 系列导航
| 篇 | 主题 | 状态 |
|---|---|---|
| 第一篇 | 提示链 · 路由 · 并行化 | ✅ |
| 第二篇 | 反思 · 工具使用 · 规划 | ✅ |
| 第三篇 | 多智能体 · 记忆管理 · 学习适应 | ✅ |
| 第四篇 | MCP 协议 | ✅ |
| 本篇 | 目标设定与监控 · 异常处理与恢复 | ✅ |
前言
我在做 my-resume 的全栈改造——把一个静态展示页面,改成 NestJS 后端 + 数据库 + 前端交互 + AI 能力 + 部署上线的完整项目。
在设计 AI 能力这块,我一直在思考一个问题:
如果我要做一个 Agent,帮用户自动优化他的简历内容,这个 Agent 应该怎么工作?
这个问题看起来简单,但细想下去,有三个坑:
坑一: Agent 怎么知道"优化完了"?没有明确的完成标准,它可能会一直改、改到跑偏。
坑二: Agent 执行到一半,LLM 接口没有响应,怎么办?已经处理好的内容就丢了吗?
坑三: LLM 正常返回了,但内容是错的——用户有2段工作经历,AI 整理出了4个,自相矛盾。代码不会报错,但结果是坏的。
这三个坑,正好是这篇文章的全部内容:
- 第11章:目标设定与监控——解决坑一,让 Agent 知道自己在做什么、做到什么算完成
- 第12章:异常处理与恢复——解决坑二和坑三,让 Agent 在出错时不失控
两章放在一起讲,因为它们本质上是同一件事的两面:
第11章是"正向设计"——怎么让 Agent 朝着目标跑 第12章是"防御设计"——跑的过程中出了问题怎么兜底
一、一个问题热热身
假设你雇了一个实习生,让他帮你做 my-resume 的全栈改造。
你只说了一句话:"帮我把这个项目改造成全栈的",然后你去忙别的了。
三天后你回来,面对三个问题:
- 你怎么判断他做得好不好?
- 他自己怎么知道该在哪里停下来?
- 如果他走偏了,你们怎么发现?
我自己的答案是这样的:
问题一:回看需求,看有没有完成最终任务,偏差能不能接受。
问题二:每到一个里程碑,做任务拆解,符合单一职责,每完成一个 task 做 review / 自测 / 验证,没有偏离就 commit,然后继续开发。
问题三:丢了任务就不管,3天后看结果,根据结果比对判断。
这三个答案,就是第11章的全部内容——只不过那个"实习生",换成了 AI
实习生开始工作
PS: 目前初步重构已OK,这个系列学习完会作为下阶段的实战项目再放出来,现在AI模块还没接入和完成 …
1、先确定技术选型
作为学习项目难得用用React,这次就准备用 monorepo + Next.js 来进行了。
可以让 AI 出一版技术选型方案,比如我们作为前端选 MongoDB 还是 SQLite、PostgreSQL,让AI分析并给出理由我们来确认就好了。
现在有了AI辅助,思路要变,不要畏惧,边做边学就好。于是我的技术选型也确认了
# 个人简历 Monorepo 重构总方案(v1 学习版)
## Summary
- **目标**:把现有静态 Vue 简历站,重构为一个**可学习、可开源、可部署、可写教程系列**的全栈 monorepo。
- **v1 产品边界**:只做 **1 份标准通用版双语简历(`zh/en`)**,不做按 JD 派生的多版本简历;JD 定制版、商业版能力全部后置。
- **主题策略**:`light / dark` 作为 `v1` 基础能力纳入设计;多模板主题暂不实现,但要预留扩展钩子。
- **推荐技术栈**:`pnpm workspace` + `Turborepo` + `Next.js(App Router)`(`web/admin`)+ `NestJS` + `Drizzle ORM` + `SQLite`。
- **角色模型**:`admin` 可编辑/发布/触发 AI;`viewer` 只读,只能体验**缓存好的预设分析结果**,不能发起真实 AI 调用。
- **导出策略**:`pdf/md` 统一由 `NestJS` 生成与下载。
- **部署主线**:`web/admin` 部署到 `Vercel`,`NestJS/Redis/SQLite(libsql)` 部署到云服务器;CI/CD 作为正式里程碑纳入教程。
## Key Changes
### Monorepo 结构
- `apps/web`:公开简历展示,SSR/SEO 优先,提供在线浏览与导出下载入口。
- `apps/admin`:后台管理,登录后可编辑简历、发布、查看 AI 结果、体验缓存报告。
- `apps/server`:唯一业务后端,承载鉴权、简历 CRUD、AI、文件、导出、队列、权限。
- `packages/ui`:共享 React UI 组件与主题。
- `packages/api-client`:共享 API 请求层、DTO 类型、错误码。
- `packages/config`:共享 `tsconfig`、`eslint`、环境变量约束。
### 前端选型
- `web` 与 `admin` 都使用 `Next.js App Router + HeroUI + TypeScript + Tailwind`。
- 后台数据请求统一使用 `TanStack Query`;表单统一使用 `React Hook Form + Zod`。
- 不引入 Redux;局部状态保持简单。
### 后端选型
- `NestJS` 继续作为唯一业务后端。
- `Redis/BullMQ` 用于 AI 任务、文件解析、重试和状态流转。
- AI Provider 使用**适配器模式**,允许用户通过 `.env.local` 配置自己的模型、协议和 base URL。
### 数据库与内容模型
- 只维护**一份标准简历**,同一条记录直接包含 `zh/en` 多语言内容。
- 数据按模块拆分:`profile`、`education`、`experiences`、`projects`、`skills`、`highlights`、`attachments`。
- 增加“**草稿 / 已发布**”状态,后台编辑草稿,公开站只读取已发布内容。
接下来就是让你的 OpenCode / Claude Code / CodeX 跑起来:
为了更好的效果,记得提前把相关 skills 安装一下,推荐一下 frontend-design
我准备重构我的 my-resume 项目,技术方案如下(上面那大段粘贴进来)
1. 评估重构方案进行 milestone 规划,并拆分相应的 issue;
2. 进行 TDD 开发,开发完成自测,完成后再继续;
3. 自测通过后,记录开发文档到docs目录下,按标准git流程提交并推送到远端;
4. 并继续下一个里程碑内issue,按上述步骤进行;
5. 该里程碑 issue 进行后继续下一阶段规划直到重构全部完成。
并不是一次完成的,主要思路是这样,中间有些忘了。这只是个小插曲,继续回到我们的学习中来.
二、第11章:目标设定与监控
2.1 目标要具体到"可以被判断"
"优化简历"不是一个好目标,因为它没法被判断——什么叫优化好了?
在 my-resume 的场景里,我想到了两种目标模式:
模式A:以用户自身为原点
→ 补全和完善
→ 基本信息、工作履历、项目经验都解释清楚了
→ 成功标准:完整性 ✅
模式B:以企业JD为导向
→ 根据 JD 来补全当前欠缺的部分
→ 成功标准:和 JD 的关键词匹配度 ✅
两种模式都可以,但对 Agent 来说,模式B 更友好——因为有外部锚点,完成度可以被量化比对,Agent 知道什么时候该停。
💡 一句话原则:目标要具体到"可以被程序判断 True/False"
这也是第11章一个很妙的设计——
用 LLM 来判断"是否达标",返回 True/False
把"简历质量好不好"这种模糊的主观判断,变成可以被程序处理的布尔值。这是第4章 Reflection(反思)模式在目标监控里的具体应用。
2.2 执行循环:你的开发节奏就是 Agent 的工作节奏
我平时开发的节奏是这样的:
里程碑拆解
→ 符合单一职责的 task
→ 完成一个 task
→ review / 自测 / 验证
→ 没偏离就 commit & push
→ 继续下一个
翻译成 Agent 语言:
| 我的开发节奏 | Agent 里的概念 |
|---|---|
| 里程碑拆解 | Sub-goal Decomposition(子目标分解) |
| 单一职责的 task | Atomic Action(原子动作) |
| review / 自测 / 验证 | Self-evaluation(自我评估) |
| commit & push | Checkpoint(检查点) |
| 继续下一个 | Next iteration(进入下一轮) |
这套节奏,就是 Agent 的 Plan → Execute → Evaluate 循环。
第11章的核心模式图:
定义目标
+ 成功标准
+ 容忍阈值
↓
拆解子目标(里程碑)
↓
执行 → 自我评估 ──── 偏离 ──→ 重新规划 ──┐
↓ ↑ │
通过 └── 检查点 ─────────────────┘
↓
终态评估:达成目标?
├─ 是 → 结束 ✅
└─ 否 → 重新规划 or 上报
2.3 和前几章的关系
第11章不是新技能,而是一个元模式——把之前学的能力组织起来,给 Agent 装上方向盘和仪表盘:
| 章节 | 负责什么 | 在第11章里的位置 |
|---|---|---|
| 第6章 规划 | 生成计划、拆解子目标 | "拆解子目标"这一步 |
| 第4章 反思 | 评估输出质量 | "自我评估"这一步 |
| 第10章 MCP | 工具怎么调用 | "执行"这一步的底层 |
| 第11章 | 把上面串起来,加上目标和监控 | 整个循环的框架 |
🎯 第11章是一个"元模式"——它不引入新技能,而是给之前所有模式装上"方向感"和"自我管理能力"
三、第12章:异常处理与恢复
第11章解决了"Agent 要做什么",第12章解决"做的过程中出了问题怎么办"。
3.1 两种完全不同的错误
先做一个区分,这非常重要——
Agent 调用 LLM 优化第三段工作经历,LLM 正常返回了,没有超时,没有报错,但返回的内容是:
"该用户在2015年同时担任 Google 工程师和 Facebook CTO"
代码层面会报错吗?不会。
但这个结果显然是错的——一个人同一时间只能在一家公司任职。
这说明 Agent 的错误有两种完全不同的类型:
| 错误类型 | 检测方式 | 简历 Agent 举例 | |
|---|---|---|---|
| 硬规则错误 | 格式、字段、语法、依赖 | 代码 try/catch | 字段为空、日期格式错误、LLM 接口超时 |
| 语义层错误 | 逻辑矛盾、因果不对 | LLM 来判断 | 时间线冲突、职位与工作内容不符 |
硬规则错误,代码能发现。语义层错误,只有 AI 能发现。
💡 用 AI 检测 AI 的输出——这是第4章 Reflection 模式的另一个具体应用
3.2 出错了,Agent 应该怎么决策?
发现错误之后,Agent 有三条路:
路径A:自己修复
路径B:问用户
路径C:任务失败,干净退出
怎么决定走哪条?我自己推导出来的判断链是这样的:
发现错误
↓
临时抖动?→ retry(重试)
↓ 还是不行
有备用节点?→ 切换备用,继续
↓ 备用也挂了
影响最终结果?→ 告知用户,询问是否降级继续
↓ 用户不同意 / 无法降级
任务失败,干净退出
这个链条在第12章里叫做 异常升级(Escalation)——问题解决不了,就往上"升级"一层处理,直到有人能接手。
3.3 Checkpoint:出错不从头来
回到最开始的问题:LLM 接口没有响应,已经处理好的内容就丢了吗?
答案是:不应该丢,因为有 Checkpoint。
这个概念和 git commit 是同一个道理:
每完成一个稳定的功能点,就 commit 一次。 即使后面出问题,也能回到上一个稳定状态,而不是从头来。
在简历 Agent 里,Checkpoint 的打法是这样的:
① 识别简历内容,读取文件 → Checkpoint 1
② 梳理大纲结构,分层归类 → Checkpoint 2
③ 整理第一段工作经历 → Checkpoint 3
④ 整理第二段工作经历 → Checkpoint 4
⑤ 整理第三段工作经历 → ❌ LLM 无响应
→ 从 Checkpoint 4 恢复,重试第三段
不用从头来,成本最小。
3.4 Human-in-the-loop:什么时候让人拍板
不是所有错误都该 Agent 自己处理。
有一类错误,影响范围大、涉及用户核心信息、难以撤回——这种必须让人来决定。
比如简历 Agent 检测到:
用户说有5段工作经历,但整理完只有3段时间线,有2段找不到对应的时间区间。
这种情况,Agent 不能自己"脑补"填上去,必须告知用户,让用户来核实。
但另一方面,如果 Agent 什么都要问用户,就会变成这样:
"你确定要输入这个字吗?" → 确认 "你确定要移动光标吗?" → 确认 "你确定要删除这个空格吗?" → 确认
这不是助手,这是折磨。
所以需要一个原则——最小干预(Minimal Footprint):
| 条件 | Agent 自己处理 | 需要问用户 |
|---|---|---|
| 影响范围 | 小、局部 | 大、影响全局 |
| 可逆程度 | 容易撤销 | 难以撤回 |
| 用户授权 | 已明确授权 | 未授权 |
对应到简历 Agent:
Agent 默默处理(最小干预):
✅ 错别字:「负则」→「负责」
✅ 日期格式:「2020.3」→「2020-03」
✅ 语言流畅度优化(用户已授权"帮我润色")
必须问用户(Human-in-the-loop):
⚠️ 工作经历数量对不上
⚠️ 同一时间段出现两家公司
⚠️ 职位描述和工作内容严重不符
⚠️ 用户没有授权的内容扩写
💡 一句话原则:只要是对用户最终结果有影响、难以撤回的,都该人工介入
3.5 执行前验证:在出错之前就预判风险
有没有办法在出错之前,就预判到"这一步可能有风险"?
比如用户突然说:
"帮我把所有工作经历都删掉,只保留教育背景"
Agent 在执行之前,应该先质疑一下合理性:
"您的目标是优化简历,工作经历是简历的核心部分。确认要删除全部工作经历吗?"
这叫做 Pre-execution Validation(执行前验证)——在执行高风险操作之前,先做一次意图确认。
它和 Human-in-the-loop 的区别是:
Human-in-the-loop:出错之后,让人来决定怎么处理 → 事后补救
Pre-execution Validation:执行之前,先确认用户真的要这么做 → 事前预防
两个都需要,缺一不可。
四、把两章串起来:完整的简历 Agent 设计
现在把第11章和第12章的所有概念,放进简历 Agent 的完整流程里:
用户输入:"帮我优化整份简历,一共5段工作经历"
↓
【目标定义】(第11章)
目标:5段工作经历全部优化完成
成功标准:完整性 + 清晰度 + 一致性全部通过
容忍阈值:小的格式问题可以自动修复
↓
【执行前验证】(第12章)
检查输入:简历文件是否可读?字段是否齐全?
↓
【循环执行】(第11章 + 第12章)
┌──────────────────────────────────────┐
│ 处理第N段工作经历 │
│ ↓ │
│ 硬规则检测:字段完整?格式正确? │
│ ↓ │
│ 语义层检测:逻辑一致?时间线正确? │
│ ↓ │
│ 发现问题? │
│ ├─ 小问题(错别字/格式)→ 自动修复 │
│ └─ 大问题(矛盾/缺失)→ 问用户 │
│ ↓ │
│ Checkpoint:保存当前进度 │
│ ↓ │
│ LLM 出错?→ retry → 备用 → 上报 │
└──────────────────────────────────────┘
↓ 5段全部完成
【终态评估】(第11章)
LLM 裁判:整份简历达到目标了吗?
├─ True → 输出结果 ✅
└─ False → 重新规划,继续改进
五、核心洞察总结
| 概念 | 核心思想 | 一句话记住 |
|---|---|---|
| 目标定义 | 目标要具体到可以被判断 | 能返回 True/False 的才是好目标 |
| 子目标分解 | 大任务拆成原子动作 | 你的里程碑拆解,就是 Agent 的 Sub-goals |
| Checkpoint | 每个稳定点存档 | 就是 git commit,出错不从头来 |
| 硬规则检测 | 代码发现格式错误 | try/catch 能抓到的 |
| 语义层检测 | AI 发现逻辑矛盾 | 代码看不出来,要用 LLM 当裁判 |
| 异常升级 | 问题层层往上处理 | retry → 备用 → 问用户 → 退出 |
| Human-in-the-loop | 影响结果的让人拍板 | 影响最终结果 + 难以撤回,就介入 |
| 最小干预 | 小事默默处理 | 默默兜底的好伙伴 |
| 执行前验证 | 高风险操作先确认 | 事前预防 > 事后补救 |
结语
这两章放在一起,讲的其实是同一件事:
怎么让 Agent 像一个靠谱的开发者一样工作。
靠谱的开发者是什么样的?
- 接到任务,先搞清楚"做到什么算完成"
- 拆成里程碑,每完成一个就 commit
- 遇到小问题自己解决,不麻烦别人
- 遇到大问题及时上报,不自作主张
- 出了 bug,从上一个稳定版本恢复,不从头来
这套工作方式,我们作为开发者已经内化了。第11章和第12章,就是把这套工作方式,显式地教给 Agent。
学到这里,越来越觉得:AI 工程和软件工程,底层真的是同一套思维。 目标管理、检查点、容错分层——这些事工程师早就在做了,只不过现在执行者从代码变成了模型,从人变成了 Agent。
对于 my-resume 的全栈改造来说,这两章给了我一个很清晰的设计原则:
AI 能力不是"扔给 LLM 就完事",而是需要设计目标、检查点、容错机制——就像设计一个可靠的后端服务一样。
下一篇预告: 第13章——人机协同(Human-in-the-Loop)。这章会把"什么时候让人介入"这个问题讲得更系统,期待一下 🚀
💬 系列地址:持续更新中 📖 原书地址:adp.xindoo.xyz 🛠️ 实战项目:my-resume(静态页面 → NestJS + 数据库 + AI + 部署上线,进行中)
如果这篇对你有帮助,欢迎点赞收藏,我们下篇见 👋