AI Agent 设计与落地

Posted on Sun 28 December 2025 in Journal

Abstract AI Agent 设计与落地
Authors Walter Fan
 Category    learning note  
Status v1.0
Updated 2025-12-28
License CC-BY-NC-ND 4.0

你可能也见过这种“AI 项目翻车现场”:

  • 第 1 天:HR 说“能不能让 AI 帮我看简历?”
  • 第 3 天:你做了个 ChatGPT 问答页,效果还行。
  • 第 7 天:开始接入岗位 JD、做关键词匹配、自动改写,突然发现:隐私、事实一致性、格式、可解释性、以及“别把人写成神仙”全都要管。
  • 第 14 天:上线试跑,输出像开盲盒:有时一针见血,有时胡编乱造;有时语言很漂亮,有时把时间线改得稀碎。

这时候你会发现:

这不是“写 prompt”的问题,这是“做系统”的问题。

所以本文用一个更真实、更能落地的例子来讲 Agent:

“简历审查与优化 AI Agent”(下面简称“简历 Agent”)

它的目标很具体: - 输入:候选人简历 + 目标岗位 JD +(可选)公司/团队偏好 - 输出:结构化评审 + 可追溯修改建议 + 生成可投递的新版简历

1. 先明确:你要做的是“Agent”还是“自动化脚本 + LLM”?

很多失败项目不是模型不行,而是把 LLM 问答误当成 Agent。

1.1 Agent 的最小定义(建议你直接写到 PRD 顶部)

Agent = 在给定目标下,能自主规划步骤调用工具执行、在环境中产生可观测动作、并对结果进行迭代修正的系统。

一句话:它得“会做事”,不是只“会聊天”。 😄

1.2 用“简历 Agent”快速判定:它为什么是 Agent?

满足以下 2 条以上,你就别再叫它“聊天机器人”了:

  • 多步任务:解析简历 → 对齐 JD → 找缺口 → 改写 → 校验 → 导出
  • 外部工具:文件解析(PDF/DOCX)、ATS关键词匹配、模板渲染、导出
  • 状态/记忆:版本管理(v1/v2/v3)、修改记录、用户偏好(语气/行业)
  • 失败重试/回滚:解析失败、格式坏掉、改写过度、事实冲突
  • 多角色/并行子任务:分析(Reviewer)/改写(Writer)/校验(Verifier)

小提醒:如果你只是“把简历丢给模型,让它给建议”,那更像“自动化脚本 + LLM”。不要硬叫 Agent,否则评审时你会被问:它的工具呢?它的状态机呢?它的回滚呢?

ATS 是 Applicant Tracking System 的缩写,中文常译为 候选人跟踪系统 / 招聘管理系统。 它用于企业招聘流程中对简历进行 收集、解析、筛选、关键词匹配、流转审批、面试安排与记录 等;很多“ATS 友好简历”指的是更容易被这类系统正确解析并命中岗位关键词。

2. 需求拆解模板:把“目标”拆成可执行的任务网

落地第一步不是挑框架,而是把问题拆对。

2.1 PRD 一页纸模板(强烈建议照填)

把下面这 7 行当作“必填字段”,填不出来就别开工:

  • 目标(Goal):用户最终想得到什么可验收的产物?
  • 输入(Input):用户给什么?(文本/链接/文件/表格/上下文)
  • 输出(Output):产物格式?(Markdown/JSON/工单/代码提交)
  • 环境与工具(Tools):允许调用哪些系统?权限范围?
  • 约束(Constraints):时间/成本/合规/语言/不可编造(必须可溯源)
  • 验收标准(Acceptance Criteria):怎么判断“做对了”?
  • 失败边界(Failure Modes):最容易翻车的 3-5 件事

2.1.1 示例:简历 Agent(一页纸样例)

  • Goal:对“岗位 JD”给出结构化评审(匹配度、缺口、风险),并生成可投递的简历(PDF/DOCX)。
  • Input:简历文件(PDF/DOCX/Markdown)、岗位 JD(文本/链接)、可选:候选人自述亮点。
  • Output
  • review.json(评分 + 缺口 + 风险 + 建议)
  • resume_v2.docx / resume_v2.pdf(可投递版本)
  • diff.md(改动说明:哪里改了、为什么改)
  • Tools:文件解析、关键词抽取、模板渲染(DOCX/LaTeX)、拼写语法检查、导出服务。
  • Constraints
  • 不得编造经历(不能凭空加项目、加公司、加时间线)
  • 涉及 PII 必须脱敏/加密存储;默认不保留原始简历
  • 输出必须可解释:每条建议对应 JD 证据或简历原句
  • Acceptance
  • 所有“新增内容”都能追溯来源(来自简历/来自用户补充)
  • 时间线不冲突;技能与经历一致
  • ATS 关键词覆盖率提升(可量化)
  • Failure Modes:PDF 解析乱序、模型擅自编造、格式破坏、关键词堆砌、语气过度夸张。

2.2 把大目标拆成“可观察”的 Task Graph

建议输出一个任务有向图(DAG),而不是“步骤列表”。原因:现实里会分支与回退。

flowchart TD
  ingest[IngestResumeAndJD] --> parse[ParseResume]
  ingest --> parseJD[ParseJD]
  parse --> extract[ExtractFactsAndSkills]
  parseJD --> keywords[ExtractJDKeywords]
  extract --> gap[GapAnalysis]
  keywords --> gap
  gap --> rewrite[RewriteSections]
  rewrite --> verify[VerifyNoFabrication]
  verify --> format[RenderTemplate]
  format --> deliver[DeliverFiles]
  verify -->|NeedClarification| ask[AskUserForMissingInfo]
  ask --> rewrite

关键点:DAG 里每一步都应该能产出“可观测中间结果”(结构化数据/日志/可复现的输出),否则后面你根本没法排查。

3. 核心架构模式:Controller-Tools-Memory(CTM)+ 可插拔 Policy

把 Agent 当成“可控的软件系统”,而不是“一个 Prompt”。

3.1 组件清单(最常用的工程化分层)

  • Controller(控制器):规划、调度、重试、终止条件
  • Tools(工具层):稳定的 API 封装,含鉴权、超时、幂等
  • Memory(记忆层):短期上下文 + 长期状态/向量库 + 任务缓存
  • Policy(策略层):权限、合规、成本、路由、提示词模板
  • Telemetry(可观测层):日志、链路、token/成本、评测与告警

朴素但重要的事实:模型负责生成候选方案;系统负责把方案变成可控动作。

3.1.1 简历 Agent 的最小可行架构图

flowchart LR
  User[User] --> Controller

  subgraph tools [Tools]
    ResumeParser[ResumeParser]
    JDParser[JDParser]
    ATSMatcher[ATSKeywordMatcher]
    TemplateRenderer[DocxOrLatexRenderer]
    GrammarChecker[GrammarChecker]
  end

  Controller --> tools
  Controller --> Memory
  Controller --> Policy
  Controller --> Telemetry
  tools --> Controller

3.2 趋势提示:现在拼的是“治理能力”

做简历 Agent 这种“看起来很简单”的事,最容易被低估: - 你不做治理,输出就会漂移; - 你不做验证,模型就会编; - 你不做可观测,线上就变成玄学。

做 Agent 不是“先能跑”,而是 能控、能查、能省。⚡️

4. 构建 Pattern:从 0 到 1 的最短路径(可复用流程)

下面是我建议的“七步法”,我会用“简历 Agent”贯穿。

4.1 Step 1:先定输出契约(Output Contract)

不先定输出,就会在后期评测和集成时失控。

  • 输出必须是结构化或可解析格式(JSON/Markdown 严格模板)
  • 输出必须可验证:引用来源、时间戳、证据链接

示例:review.json(建议配 JSON Schema 校验):

{
  "match_score": 0.78,
  "strengths": [
    {"point": "有分布式系统经验", "evidence": "简历:XX 项目描述"}
  ],
  "gaps": [
    {"gap": "缺少可量化指标", "suggestion": "将‘优化性能’改为‘P95 从 800ms 降到 220ms’"}
  ],
  "risks": [
    {"risk": "时间线不一致", "evidence": "2021-2022 与 2022-2023 重叠"}
  ],
  "ats_keywords": {
    "missing": ["Kafka", "Kubernetes"],
    "covered": ["Java", "Spring", "MySQL"]
  },
  "change_rules": {
    "no_fabrication": true,
    "tone": "professional"
  }
}

经验:宁可让模型写“结构化要点”,也不要让模型写“散文式长文”。 长文不稳定、难回归,最后只能靠“相信 AI”。(相信就输了😄)

4.2 Step 2:工具先行(Tool-first)

把工具当成“后端服务”来写,不要把 API 调用写进提示词里。

简历 Agent 的工具封装建议包含:

  • 解析:PDF/DOCX → 结构化段落(标题/项目/时间)
  • 关键词:JD 关键词抽取 + ATS 覆盖率计算
  • 导出:模板渲染(DOCX/LaTeX)+ PDF 导出
  • 检查:拼写语法、格式约束(行距/分页)、链接有效性

工具封装要包含:

  • 超时与重试(指数退避)
  • 幂等(尤其是导出/写入文件)
  • 权限与审计(谁上传了什么、谁下载了什么)
  • 统一错误码(便于控制器规划分支)

4.2.1 工具接口定义模板(错误码 / 幂等键 / 审计字段 / 超时重试)

很多 Agent 项目“看起来能跑、实际上不可控”,本质原因是:工具层没有被当成“后端服务”来设计。

下面给一个我常用的工具接口模板,你可以直接套到简历 Agent 里。

工具接口(建议统一成 JSON 结构)

{
  "tool": "ResumeParser",
  "action": "parse",
  "request_id": "req_20251228_0001",
  "idempotency_key": "sha256:...",
  "actor": {
    "user_id": "u_123",
    "tenant_id": "t_001",
    "role": "hr"
  },
  "input": {
    "file_uri": "s3://bucket/resume.pdf",
    "file_type": "pdf",
    "language": "zh-CN"
  },
  "policy": {
    "timeout_ms": 8000,
    "max_retries": 2,
    "retry_backoff_ms": [200, 800],
    "pii_mode": "redact"
  }
}

工具返回(强烈建议统一错误码 + 可重试标记)

{
  "ok": false,
  "error": {
    "code": "PARSE_PDF_OCR_REQUIRED",
    "message": "PDF is scanned image; OCR required",
    "retryable": false,
    "hint": "Ask user for DOCX or run OCR tool"
  },
  "audit": {
    "request_id": "req_20251228_0001",
    "tool": "ResumeParser",
    "action": "parse",
    "ts": "2025-12-28T22:31:12Z"
  }
}

建议的错误码分层(够用就好)

  • 权限类AUTH_FORBIDDEN, AUTH_EXPIRED
  • 输入类INPUT_INVALID, INPUT_TOO_LARGE, INPUT_UNSUPPORTED_FORMAT
  • 外部依赖类UPSTREAM_TIMEOUT, UPSTREAM_RATE_LIMIT, UPSTREAM_5XX
  • 业务类PARSE_FAILED, TEMPLATE_RENDER_FAILED, EXPORT_FAILED
  • 安全类PII_POLICY_VIOLATION, PROMPT_INJECTION_DETECTED

幂等键(idempotency_key)怎么用?

  • 对“导出/生成文件/写入版本库”这种写入型操作,必须带幂等键。
  • 推荐幂等键 = hash(用户ID + 输入文件hash + JD hash + 输出模板版本 + 输出格式)
  • 这样用户重复点“生成”,最多只会拿到同一份产物,不会生成 N 份垃圾版本。

审计字段(audit)怎么用?

  • 记录:谁触发、用的什么输入、调用了哪些工具、产出了哪些文件。
  • 简历是 PII,审计不是可选项,是上线门票。

超时/重试建议(工程上很实用)

  • 工具调用超时要“短”,避免线程池被拖死;需要长任务就异步化。
  • 只对 retryable=true 的错误重试(比如限流/超时),不要对解析失败重试 N 次自我感动。
  • 控制器里做“错误分支规划”:
  • PARSE_PDF_OCR_REQUIRED → 调 OcrTool 或请求用户提供 DOCX
  • TEMPLATE_RENDER_FAILED → 回滚上一个版本 + 降级到纯文本模板

4.3 Step 3:设计控制器(Planner/Executor)

推荐从简单开始:

  • 单代理 ReAct(最容易上线)
  • 需要并行再升级多代理(Reviewer/Writer/Verifier)

控制器最重要的三个能力:

  • 停止条件:何时停止(例如“所有风险已闭环/需要用户补充信息”)
  • 失败恢复:解析失败→换解析器;关键词过拟合→降权;格式崩→回滚到上一个版本
  • 预算控制:token/调用次数/最长耗时

4.4 Step 4:加入记忆与状态(State Machine)

简历优化往往是多轮:你改一版,我再提意见,再改一版。

如果你的任务跨多轮或跨天,必须上状态机,否则靠对话历史必翻车。

建议状态:

  • INIT → INGESTED → ANALYZED → REWRITTEN → VERIFIED → RENDERED → DELIVERED → DONE
  • 异常:NEED_CLARIFICATION / FAILED / ROLLBACKED

4.5 Step 5:验证层(Verification Layer)要单独做

“模型自信”不等于“事实正确”。简历场景尤其敏感:编造经历 = 灾难

建议最少做两种验证:

  • 可验证引用:每条建议必须绑定证据(简历原句/JD 句子/用户补充)
  • 二次校验:用更便宜模型 + 规则校验
  • 时间线是否冲突
  • 公司/项目是否新增(禁止)
  • 指标是否夸张(例如“提升 100 倍”要打回)

4.5.1 Verifier 规则清单(简历场景必备)

简历 Agent 的 Verifier 目标不是“挑刺”,而是把不确定性收敛成工程可控变量:不编造、不冲突、不过度、不注水、可追溯。

你可以把 Verifier 拆成“规则 + 轻量模型”的组合:

  • 规则:快、稳定、可解释(适合拦截硬错误)
  • 轻量模型:查软错误(语气、可读性、是否过度夸张)

下面这份清单可以直接做成代码里的 resume_verifier_rules.yaml

A. 不得编造(No Fabrication)

  • 禁止新增实体:新公司名/新项目名/新时间段/新职位头衔
  • 做法:抽取“原简历实体集合”,对比“改写后实体集合”,出现新增则打回
  • 禁止新增不存在的技术栈:例如简历没写过 Kafka,却突然出现“精通 Kafka”
  • 做法:技能词表 + 证据绑定(每个技能必须引用原句或用户补充)

B. 时间线一致性(Timeline Consistency)

  • 日期范围不冲突:同一时间段出现两个全职经历
  • 项目时间必须落在所属公司时间范围内
  • “至今”只能出现一次(或者必须解释多段并行)

C. 量化指标合理性(Metric Sanity Check)

  • 夸张指标拦截:如“提升 100 倍/提升 10000%”默认判为高风险
  • 缺少单位/口径提醒:如“延迟降低 80%”但无 P95/P99、无基线
  • 指标与动作一致:写了“做缓存”但指标却是“准确率提升”,就要提示不一致

D. 关键词堆砌检测(Keyword Stuffing)

  • 关键词密度过高:一段话连续罗列 15 个技术名词
  • 与经历无关的关键词:JD 里要 Kubernetes,你简历没任何容器经历却硬塞
  • 策略:允许“补充学习/接触”,但必须放在“技能/学习”而不是“工作成果”里

E. 风格与语气(Tone & Claims)

  • 避免把人写成神仙
  • 不要“全栈架构师/技术领袖/业内顶尖”这种无证据的帽子
  • 更推荐:职责 + 行动 + 结果(CAR/STAR)
  • 一致性:同一份简历的语气要统一(不要一会儿像论文,一会儿像广告)

F. 格式与可投递性(Format & Deliverability)

  • 段落长度限制:项目描述每条 1-2 行(不然 HR 眼睛会罢工)
  • 链接可用:GitHub/Blog/作品集链接必须可点击、格式正确
  • 导出校验:生成的 DOCX/PDF 不能乱码、不能错位、不能丢字

G. 证据链(Evidence Binding)

  • 每条建议必须绑定一个 evidence:
  • resume_quote(原简历句子)或 user_supplied(用户补充)或 jd_quote(JD 句子)
  • 没证据的建议不允许出现在最终输出里(顶多作为“需要用户确认的问题”)

4.6 Step 6:上可观测与治理(Observability + Governance)

上线后你需要回答四个问题:

  • 这次建议为什么是这样?(trace + 证据链)
  • 花了多少钱?(token、工具调用)
  • 哪些地方最容易出错?(解析错误/格式错误/编造风险)
  • 哪些提示/工具最有效?(A/B、回归测试)

4.7 Step 7:做评测集 + 回归(Eval-first Iteration)

没有评测集,优化永远是拍脑袋。

简历 Agent 的评测集建议包含:

  • 正常样本(70%):规范简历 + 清晰 JD
  • 边界样本(20%):PDF 乱序、JD 含糊、经历缺指标
  • 对抗样本(10%):诱导编造、诱导夸大、提示注入(“忽略规则”)

5. 典型模式库(直接套用)

模式 A:RAG + 工具写入(简历知识库助手)

适用:把公司内部“简历模板/面试题库/胜任力模型”做成可检索知识库。

要点:

  • 检索:向量 + 关键字混合(避免仅语义)
  • 引用:每条建议绑定来源(模板条目/胜任力条款)
  • 写入:必须幂等(防止重复版本/覆盖原件)

模式 B:规划-执行-校对(Plan-Execute-Review)

适用:简历改写、生成 cover letter、生成面试自我介绍。

推荐结构:

  • Planner:列“缺口清单 + 需要用户补充的问题”
  • Executor:生成 v2 草稿
  • Reviewer:查事实/格式/语气/风险

模式 C:多代理分工(Reviewer/Writer/Verifier)

适用:长简历、多岗位投递、多版本管理。

注意事项:

  • 明确“交付物格式”(PDF/DOCX + review.json + diff.md)
  • Agent 间通信只传结构化中间结果,少传长文本(省 token,也更可控)

6. 安全与合规检查清单(Agent 必备)

简历 = PII + 职业轨迹,安全合规要放在前面。

6.1 工具调用安全(Checklist)

  • 上传文件默认不持久化,或设置明确的保留期(例如 24h 自动删除)
  • 所有写入类工具必须有沙箱/测试环境
  • 权限遵循最小权限原则(谁能看谁的简历)
  • 对外网请求有域名白名单
  • 记录审计日志(谁触发、做了什么、参数是什么)

6.2 防提示注入(Prompt Injection)

  • 系统提示与用户内容严格分隔(role separation)
  • 检索内容视为“不可信输入”,需要清洗
  • 重要工具参数由程序生成,不让模型自由拼接(特别是文件路径、模板变量)

7. 上线验收:别只验“能跑”,要验“能控”

建议用下面这张表做上线前验收(可以直接贴进 Jira):

7.1 功能

  • 输出契约符合约定格式(review.json 校验通过)
  • 新简历可导出,格式不崩(分页/对齐/字体)

7.2 稳定性

  • 解析失败可降级或重试(换解析器/换策略)
  • 有明确停止条件,不会无限循环

7.3 成本

  • 单任务 token 成本可预测,超预算会自动中止或降级

7.4 可观测

  • trace 可追溯到每次工具调用与关键中间结论
  • 关键错误有告警(解析失败率、导出失败率、编造风险命中率)

7.5 安全

  • 越权用例测试通过(不能访问不该访问的简历)
  • 导出/下载操作具备审计

8. 一个小但高效的习惯:给 Agent 也安排“走动休息”

写 Agent 常常要长时间盯日志、调提示、看 trace。对人同样要做节奏管理:

  • 每小时离开桌面走 5 分钟
  • 借机做“切换视角”的复盘:我现在是在解决真实问题,还是在堆复杂度?

结语:把简历 Agent 当软件工程,而不是魔法

当你用输出契约约束结果、用工具层约束动作、用状态机约束流程、用评测与可观测约束迭代时,Agent 的不确定性会被收敛成可控的工程变量。


本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。