Part 3 · 第 14 章 · 建立 AI 系统

搭建你的第一个 Agent 助手
当工作流碰到边界,Agent 接管

第13章的工作流跑到第473份简历时卡住了——那份扫描版 PDF 被默默归档了错误候选人。工作流按规则执行,不会说"我要换一种方式"。这一章要做的事是:给工作流装上判断力,让它遇到意外时自己决定下一步。这个东西有个名字,叫 Agent。

核心方法论:任务委托设计 — Agent 不是高科技玩意,它就是一个带判断逻辑的 Skill 编排器。你把业务规则写成文档,它照着执行;碰到意外,它调用 AI 决定下一步,而不是直接报错退出。主工具依然是 Claude Code,不需要 LangChain,不需要 Coze,只需要一个 orchestrator 脚本 + 若干 Skill 函数 + 一个问 AI 的接口。

工作流跑到哪里会卡死?

承接 Ch13 · 三类边界

第13章结尾留下了一个未解决的问题。处理1023份简历的工作流运行到第473份,遇到一份扫描版 PDF,文字提取返回空字符串,AI 评分输出0分,这份简历被自动归入 C 层归档。整个过程没有报错,工作流继续处理下一份——但那可能是一个很强的候选人,被错误分类了,而你完全不知道。

这个场景精准地描述了工作流的天花板。工作流按照你预先写好的规则执行,它不会说"这条规则在这种情况下不合适,我要换一种方式"。所有应对异常的方式——try/except 跳过、记录错误日志、发邮件通知——都需要你在写脚本时就想到并写进去。真实世界的意外,总是比你预想的更多。

分析工作流在实践中遇到的障碍,大致归入三类。第一类是格式边界:输入文件不符合预期——扫描版图片、密码保护的 PDF、特殊编码的 Word 文档。解析函数收到的不是文字,是报错。第二类是判断边界:候选人简历里工作经历的时间段有重叠,是跳槽时间的模糊,还是信息造假?工作流里没有"这是一个主观判断"的概念,它只能按规则给分,而这个规则你无法提前写清楚。第三类是动态边界:工作流运行到第600份时,用人部门临时修改岗位要求。运行中的固定脚本不会重新读取需求说明,会继续用旧标准处理剩下的423份。

三类边界的代价

格式边界的代价是漏判——强候选人因文件格式被错误归档,你不知道。判断边界的代价是误判——工作流给出一个看似客观的评分,但判断逻辑根本不适用眼前的情况。动态边界的代价是滞后——工作流拿着过时的规则处理了几百份,最后发现需要重跑。

三类边界有一个共同根源:工作流的智慧在设计时就被固化了,遇到设计时没有预料到的情况,只能用最保守的方式处理。

工作流:固定路径 遇到意外 → 只能报错退出 parse_skill() 解析简历 score_skill() 候选人评分 ⚠ 扫描PDF → 解析失败 ✗ 跳过归档 / 整体退出 Agent:动态路径 遇到意外 → AI 判断下一步 parse_skill() 解析简历 ⚠ 扫描PDF → 解析失败 ask_claude() AI 判断 跳过 标记复核 重试OCR ✓ 处理继续,不中断队列

工作流遇到异常只能报错或跳过;Agent 遇到异常,交给 AI 判断再选择支路继续

✅ 工作流与 Agent 的分水岭

工作流是固定剧本:你提前写好所有步骤和分支条件,运行时照剧本执行。Agent 是目标导向:遇到工作流设计时没有预料到的情况,AI 在运行时动态判断下一步。工作流的智慧在设计时;Agent 的智慧在运行时。

Agent 的最小实现:一个 orchestrator 脚本

代码实现 · 核心架构

提到 Agent,很多人第一反应是"这需要复杂的框架吧"——LangChain、AutoGPT、各种平台。这是一个误解。Agent 最小的实现是:一个主脚本(orchestrator)+ 若干 Skill 函数 + 一个调用 AI 判断的接口。你在第13章已经有了前两样,现在只需要加上第三样。

orchestrator 本身不做任何业务工作。它的职责只有三件事:从待处理队列里取任务、调用合适的 Skill 函数执行、遇到异常时请示 AI 决策。业务逻辑全部在各个 Skill 函数里,AI 判断全部在一个叫 ask_claude() 的函数里。三个角色分工清晰——任何一个出问题,不影响另外两个。

recruit_agent.py — orchestrator 骨架
PYTHON
from pathlib import Path from anthropic import Anthropic client = Anthropic() # ── 角色 ①:核心判断接口,遇到意外就问 AI ──────────────── def ask_claude(situation, options): resp = client.messages.create( model="claude-opus-4-6", max_tokens=80, messages=[{"role": "user", "content": f"情况:{situation}\n选项:{options}\n只回复其中一个选项名称。"}] ) return resp.content[0].text.strip() # ── 角色 ②:orchestrator,只做调度,不做业务 ───────────── def run_agent(resumes_dir): mem = load_memory() # 读取已处理记录 → 见下一节 queue = [f for f in Path(resumes_dir).glob("*") if str(f) not in mem["processed"]] for resume in queue: try: # ── 角色 ③:Skill 函数,来自 Ch12,原封不动 ────── parsed = parse_skill(resume) score = score_skill(parsed) mem["processed"].append(str(resume)) except Exception as e: # 异常不退出——这里是工作流和 Agent 的分水岭 decision = ask_claude( situation=f"简历 {resume.name} 处理失败:{e}", options="跳过 / 标记人工复核 / 重试OCR模式" ) if "人工复核" in decision: mem["flagged"].append({"file": str(resume), "reason": str(e)}) save_memory(mem) # 每处理一份立即存盘 → 见下一节 return mem["stats"]

与第13章的工作流脚本相比,实质变化只有一处:把 try/except 里的静默跳过换成了 ask_claude() 调用。Skill 函数、循环逻辑、条件路由,全部原封不动。Agent 是工作流的最小升级,不是推倒重来。

代码里有两行被注释为"见下一节"—— load_memory()save_memory()。这两个函数决定了 Agent 重启之后的行为:是从第一份简历重跑,还是从第624份继续?答案就在 memory.json 里。

💡 架构原则

orchestrator 本身不做业务,只做调度。业务逻辑在 Skill 函数里(可以独立测试、独立更新);AI 判断在 ask_claude() 里(可以替换模型、调整提示词)。三者解耦,任何一个出问题,不影响另外两个。这才是能长期维护的 Agent 结构。

给 Agent 加记忆:memory.json 是什么

持久化 · 断点续跑 · 审计追踪

工作流没有记忆,每次运行都从零开始。如果第13章的脚本处理到第623份时电脑关机了,下次启动会重新从第一份开始,前面的工作全部重来。Agent 有了 memory.json 之后,重启会从第624份继续,这叫断点续跑。但记忆的价值远不只是断点续跑。

第一批1023份处理完之后,用人部门又来了第二批342份。工作流版本对两批一视同仁,每次重建评分基准。Agent 版本可以从 memory.json 读取第一批的评分分布,用已有基准校准第二批,让两批评分口径保持一致。这是工作流做不到的跨轮次积累。

还有一个容易被忽视的价值:审计追踪。每个被 AI 标记人工复核的候选人,memory.json 里都有一条记录,写明了文件名、被标记的原因、标记时间。HR 负责人随时可以打开这个文件,看到 Agent 做了哪些判断、理由是什么。处理涉及人事决策的任务时,能解释每个结果的来源,这不是可选项,而是必须的。

memory.json
JSON
{ "session_id": "2024-0315-春招季", // 已处理的简历路径列表(断点续跑:下次启动直接跳过这些) "processed": [ "resumes/张明.pdf", "resumes/李华.docx", "... 共 623 条" ], // AI 判断为需要人工复核的条目(审计追踪:HR 可以查) "flagged_for_review": [ { "file": "resumes/王某某.pdf", "reason": "工作经历时间段有重叠,可能存在填写错误或信息造假", "flagged_at": "2024-03-15T09:23:11" } ], // 统计摘要(跨轮次对比:第二批可以参考这里的评分分布) "stats": { "total_in_queue": 1023, "processed": 623, "scored": 601, "flagged": 22 }, "last_updated": "2024-03-15T11:45:33" }
Agent 启动 run_agent() 读 memory.json 获取已处理列表 处理一份简历 Skill + ask_claude() 任意时刻中断 写 memory.json 记录处理结果 下一份 循环继续 完成 输出统计 ↓ 重启时读 memory.json,跳过已处理,从断点继续

memory.json 每处理一份后立即写入,任何时刻中断都能从断点恢复,不会重复处理

💡 记忆设计原则

记忆不是越多越好。processed 列表只存文件路径,不存内容,文件体积可控。flagged 列表存完整记录,审计用途,完整性比体积更重要。stats 存汇总数字,不存明细,快速查看整体进度。有意识地区分"存什么、存多少",Agent 才容易维护。

到目前为止,HR 招聘这个 Agent 里,四个步骤的顺序是相对固定的:解析简历 → 评分 → 遇到异常再判断。ask_claude() 只在出错时介入,平常不工作。但有一类任务,每走完一步都需要 AI 判断"下一步去哪"——连执行顺序本身也是动态的。这才是 Agent 能力的完整形态。

Skill 调 Skill:研报 Agent 案例

通用模式 · 动态编排

换一个你可能更熟悉的场景:每周给公司生成竞争对手研究报告。流程看起来清晰——搜索资讯、提取数据、写分析、输出报告——但实际操作中,每个步骤完成后都需要判断"够用吗?"某个公司本周新闻极少,搜索只有两条,是扩大搜索范围还是直接标注"信息不足"?某个财务数据明显异常,是录入错误还是真实情况,需不需要找第二数据源验证?这些判断工作流里没有地方放,只有 Agent 的 ask_claude() 能处理。

输入:公司名称 research_agent.py ① search_skill() 搜索财报 + 新闻 ask_claude() 判断 结果够用 ② extract_skill() 提取关键财务数据 结果不足 扩大范围重搜 或标注"信息不足" ask_claude() 判断 数据正常 ③ analyze_skill() 生成分析段落 数据异常 补找第二来源 或标注"数据存疑" → ④ format_skill() → 输出标准研报

研报 Agent:每个 Skill 之后有 AI 判断节点,路径不是固定的,根据中间结果动态选择

这张图里最关键的元素,不是四个 Skill 节点,而是那两个 ask_claude() 判断节点。正是这两个判断把固定的工作流变成了 Agent。工作流是 1→2→3→4;Agent 是 1→判断→2 或重做1→判断→3 或补充→4。同样的 Skill,加上判断层,能力完全不同。

核心认知

Agent 和工作流的本质区别不是技术复杂度,而是"谁在做决策"。工作流里,所有决策在你设计脚本时就做完了;Agent 里,有一部分决策是 AI 在运行时实时做的。你把哪些决策委托给 AI,就定义了这个 Agent 的能力范围。

研报 Agent 里调用了四个 Skill——search、extract、analyze、format。但现在这四个 Skill 还是第12章的卡片形式:一份 Markdown 文档,每次调用都要把提示词手动复制进脚本。Agent 的下一步升级,是让这些卡片变成可以被代码直接 import 的可执行包。

SKILL.md 升级:从提示词卡片到可执行包

资产沉淀 · 开放标准

第12章建 Skill 卡片时,核心是把经验和判断规则写成可复用的提示词文档。这已经是很大的进步——从每次重新写提示词,到有一份标准卡片可以参考和调用。但卡片终究还是"人读的文档":你指导 AI 对话时用它,但代码没办法直接 import 它。

现在 Agent 已经在跑了,parse_skill() 和 score_skill() 被 orchestrator 频繁调用。是时候把 Skill 卡片升级为完整的可执行包——让代码加载它、直接运行它,不再靠手动复制提示词。这个标准结构最初由 Anthropic 定义,2025年12月正式成为跨平台开放标准(agentskills.io),被 OpenAI Codex、GitHub Copilot、VS Code、Cursor 等超过50个工具原生采纳。这意味着你今天建的 Skill 包,换工具不需要重建:

resume-parse-skill/ 目录结构
SHELL
resume-parse-skill/ ├── SKILL.md ← 任务说明 + 调用规范(人读 + 机器读) ├── scripts/ │ └── parse.py ← 实际执行代码(Claude Code 帮你写) ├── reference/ │ └── field-mapping.md ← 字段映射规则(如:工作经历格式说明) └── assets/ └── sample-resume.pdf ← 测试样本(开发和调试用)

其中 SKILL.md 本身的格式也值得深入了解。官方 agentskills.io 规范的最小集合极简,只要求两个字段:name(名称)和 description(描述)。但 Claude Code、VS Code 等工具在官方基础上扩展了额外字段,让工具能在运行时动态调整行为,不再是纯粹给人看的文档,而是真正意义上人机共读的执行规范:

SKILL.md(官方必填 + 工具扩展字段示例)
MARKDOWN
--- name: 候选人评分 description: 评估简历与岗位匹配度,输出0-100分 + 复核理由 version: 1.2 effort: medium # 工具扩展字段:low/medium/high,工具据此分配算力 model: claude-opus-4 # 工具扩展字段:覆盖默认模型(精度优先时用更强模型) context: fork # 工具扩展字段:fork = 独立子Agent并行执行 --- # 候选人评分 Skill 你是一位资深 HR,请根据以下岗位要求评估候选人简历。 ## 输入字段 - {resume_content}: 简历全文 - {job_description}: 岗位要求 ## 输出格式 返回 JSON: "score": 0-100, "reason": 判断依据(2-3句话), "highlights": [匹配亮点列表], "concerns": [需关注点列表] ## 评分标准 85+ 进入下一轮;60-84 备选;60以下不通过
📌 字段规范说明

namedescription 是 agentskills.io 官方规范要求的必填字段,所有支持该标准的工具都认识它们。effort、model、context 是工具生态在官方基础上扩展的字段(如 Claude Code 支持),其他工具可能有不同的扩展字段或不支持这些扩展——迁移工具时请注意确认。

这个扩展字段里最值得关注的是 context: fork 字段——它告诉工具把这个 Skill 作为独立子 Agent 并行执行,主流程不用等它完成再继续。当 orchestrator 需要同时对10份简历评分时,加了 fork 的 Skill 会被自动并行化处理,而不是逐一排队。这个能力不需要你改任何代码,只需要在 SKILL.md 里加一行。

这个目录结构有三个层次的用途。对于简单任务,只读 SKILL.md 就够了——任务说明里写清楚输入和输出,调用一行代码就能运行。对于中等复杂的任务,去 reference/ 目录查字段映射规则,理解每个字段的含义和边界情况。对于复杂或定制化的场景,进入 scripts/ 目录直接修改实现代码,加入特殊处理逻辑。

维度 Ch12 Skill 卡片 Agent Skill 包
存储形式 单个 Markdown 文档 目录包,含代码和参考资料
调用方式 复制粘贴提示词到 AI 对话 代码直接 import,一行调用
可测试性 人工测试,难以批量验证 有 assets/ 样本,可自动化测试
可移植性 只能在你熟悉的 AI 工具里用 任何支持 Agent Skills 规范的平台都能运行
团队共享 发一个文档链接 发目录路径,别人能直接调用,行为一致
不被工具绑定:这不再是未来承诺

2025年12月,Agent Skills 规范正式成为开放标准,OpenAI Codex、GitHub Copilot、VS Code、Cursor 已经原生支持这个目录结构。这意味着:同一个 Skill 包,Claude Code 能加载,VS Code 也能加载,明年出现的新工具大概率也能直接用。你积累的是可移植的能力资产,而不是某个厂商的配置文件。

一个简单的判断标准:如果某个工具要求你把 Skill 只存在它自己的格式或平台里,那你在用它的同时也在给自己上锁。标准化的 Skill 包打破了这个锁。

三层选择决策树:Prompt、工作流、还是 Agent?

判断框架 · 三层选择

到这里,你已经掌握了三层工具:Prompt(第6章)、工作流(第13章)、Agent(本章)。每层都有自己最适合的场景,用错了层次不是工具不好,是工具选错了。下面这个决策树,是遇到任何新任务时的第一个判断框架。

这个任务需要重复执行吗? 同样的步骤,要做很多次 不需要重复 Prompt 直接 AI 对话完成 需要重复 执行路径能提前写清楚吗? 每一步做什么、分支条件怎么写 能写清楚 工作流 Python 脚本自动执行 路径会变 中途需要根据结果调整策略吗? 遇到意外、需要重试、中间结果影响后续步骤 相对固定 工作流 增加条件分支 需要动态判断 Agent orchestrator + Skill + ask_claude() Prompt 典型场景 写一封邮件 / 总结会议纪要 工作流 典型场景 批量简历筛选 / 每周报告生成 Agent 典型场景 竞品研究报告 / 异常检测

遇到新任务先判断是否重复,再判断路径是否固定,再判断是否需要运行时动态决策

这个决策树还有一个隐含的用法:帮你抵制"用 Agent 解决所有问题"的冲动。能用 Prompt 解决的任务,就用 Prompt;工作流够用的,就不要用 Agent。工具越复杂,维护成本越高,出错时越难排查。刚好够用,是最好的方案。

从零启动你的第一个 Agent

四步改造 · 行动清单

你现在手上已经有一条跑通的工作流脚本了——这是第13章的成果。把它升级为 Agent,只需要四步改造。每一步都是在现有代码基础上做加法,不需要推倒重写。

1
找到所有的 pass 和 raise Exception
打开你的工作流脚本,搜索 passraisecontinue。这些地方就是工作流遇到异常时的"放弃点"——把每一个放弃点改成 ask_claude() 调用,就是 Agent 要接管的判断点。判断点越少越好,只在真正需要主观判断的地方加。
2
实现 ask_claude() 函数
参考本章 §2 的代码,给脚本加一个 ask_claude(situation, options) 函数。situation 描述遇到了什么情况,options 列出可选的处理方式,让 AI 只回复一个选项名称,便于代码解析。这个函数不需要复杂,30行以内。
3
加入 memory.json 读写
在脚本开头加 load_memory(),在每次循环末尾加 save_memory()。不需要设计完整的数据结构,先只存两样东西:已处理条目的标识符(断点续跑用)和被标记条目的完整记录(审计追踪用)。其他字段用到了再加。
4
把常用 Skill 整理成可执行包
把第12章的 Skill 卡片升级为 §5 描述的目录结构——SKILL.md + scripts/ + reference/。先从最常用的那个开始,不需要一次全做。每完成一个,脚本里就能用 import 直接调用,不用再手动复制提示词。
🔷 Coze 适用场景

前四步完成的 Agent 跑在你自己的电脑上,每次需要手动启动。如果你需要以下三种能力,这时候引入 Coze 才是合理的选择:定时触发——每天早上8点自动运行,不需要你手动执行命令;消息推送——运行完成后把结果自动发到飞书群或企微;团队共享——让不会使用 Claude Code 的同事也能触发一次 Agent 运行。

Coze 解决的是"部署和分发"问题,不是"构建判断逻辑"的问题。核心的 orchestrator 代码、Skill 函数、memory.json,依然建议用 Claude Code 维护。工具选对了位置,两者配合比单独用任何一个都强。

本章核心认知

非技术出身的领域专家,离自己做专业 Agent 只剩隔着一层窗户纸——把你的专业经验和工作流程,用文档形式写清楚,Agent 就能照着执行。HR 知道什么样的候选人值得人工复核,分析师知道什么样的数据需要二次验证,这些专业判断力写成 SKILL.md 之后,就变成了 Agent 的业务规则。

AI 时代真正的竞争力不是写代码,而是把业务判断力固化成系统的能力。从第6章到第14章走过的这条路——Prompt → Skill → 工作流 → Agent——每一步不是工具变复杂了,而是你固化业务判断力的粒度越来越细、能力越来越强。