Day 64 / 共 68 天 · 阶段 12 实战求职

作品集③ 多智能体工作流(压轴,串起全课)

昨天(Day 63)让单个 Agent 学会调工具。今天做最能撑起简历的压轴作品:让"研究员 + 写手 + 审校"三个 Agent 协作产出一篇报告(Day 48 的实战),再接上评测(阶段 9)和可观测(阶段 10)。RAG、工具、多智能体协作、评测、可观测——前面 60 天学的全汇到这一件里。明天(Day 65)教你把三件作品包装成 GitHub 上拿得出手的开源项目。

📍 你在阶段 12(实战求职 D62-68)的位置
D62 作品集①RAG D63 作品集②工具 D64 作品集③多智能体 D65 开源包装 D66 简历 D67-68 面试冲刺
💡 用一个类比兜住今天(今天全程沿用「一家小型内容工作室」的世界观) 多智能体工作流 = 一家分工明确的内容工作室研究员 = 满世界找资料、整理素材的实习生;写手 = 拿着素材码字成文的编辑;审校 = 挑错、查有没有编造、不合格就打回重写的主编;工作流(编排) = 车间的流水线,规定"先研究→再写→后审→不过就返工";共享黑板(state) = 工作室墙上的白板,谁产出的东西都贴上去,下一个人接着用(Day 37);可观测 = 每道工序都记工时台账,出了问题能倒查是哪一环慢了、错了(阶段 10)。今天你是这家工作室的老板,把三个员工编成一条能出活、还能被检查的产线。
L01

定需求:为什么要三个 Agent 而不是一个

🤔 痛点"写一篇报告",让一个大模型一口气干完不行吗?行,但质量飘、爱编造、改一处得全推倒。就像让一个人既当调研、又当写作、又当审核——精力分散、还自己挑不出自己的错(自己写的自己总觉得对)。
💡 本质拆成多个各司其职的 Agent,好处是每个角色目标单一、prompt 更聚焦、还能互相把关(Day 45 协作模式)。尤其"审校"独立出来,专门挑错、查编造,比"自己审自己"靠谱得多。先填需求卡,把三个角色的职责和交接物写清。
📝 举个例子:填好的需求卡 项目名:一句话生成带出处的调研简报
输入:一个主题(如"2024 年电动车电池技术进展")
三个角色:①研究员——检索资料,整理成带出处的要点清单(复用作品①的 RAG!) ②写手——把要点写成结构化简报 ③审校——检查是否有"资料里没有的说法",不合格打回
输出:一篇 500 字、每个论点都带出处的简报
成功标准:15 个主题,审校通过率 ≥ 80%,且抽查无编造出处
👶 这不就是把前两件作品拼起来?正是!研究员那一步,直接把作品①的 RAG 检索搬来用;要联网/查库,就用作品②的工具。压轴作品的价值就在于"集大成"——面试时你能说"我把 RAG、工具调用、多智能体协作、评测、可观测串成了一个完整系统",这比三个孤立小 demo 有分量得多。
L02

三个角色:各写各的"岗位说明书"

🤔 痛点三个 Agent 说到底还是同一个大模型。凭啥它一会儿是严谨的研究员、一会儿是文笔好的写手?——靠给每个角色配不同的系统提示(岗位说明书),把它"捏"成不同性格和职责。
💡 本质每个 Agent = 一段专属 system prompt(呼应 Day 16 角色设定)+ 它能用的工具 + 它的输入输出约定。岗位说明书越清楚,员工越不越界。核心是让三张说明书首尾相接:写手的输入正好是研究员的输出,审校的输入正好是写手的输出。
# 三个角色 = 三段岗位说明书(system prompt)
RESEARCHER = """你是研究员。只负责检索与整理,不写成文。
输出格式:要点清单,每条要点后用【出处:文件名】标注来源。资料没有的绝不编。"""

WRITER = """你是写手。把研究员给的【要点清单】组织成一篇 500 字结构化简报。
只能使用清单里的内容,不得自行添加清单里没有的事实。保留每个论点的【出处】。"""

REVIEWER = """你是审校(主编)。检查写手的稿子:
1) 是否有清单/出处里没有的说法(编造)?
2) 出处是否对得上?
输出 JSON:{"pass": true/false, "problems": ["..."]}。有问题就 pass=false,列出问题。"""

def run_agent(system, user_input):     # 一个通用"员工":换 system 就换角色
    resp = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "system", "content": system},
                  {"role": "user",   "content": user_input}])
    return resp.choices[0].message.content
📝 举个例子:为什么审校要输出 JSON 审校输出 {"pass": false, "problems": [...]} 这种结构化结果(Day 14),是因为流水线要靠它做判断:pass=true 就出稿,false 就带着 problems 打回写手返工。如果审校只回一段"感觉还行有点小问题"的散文,程序没法据此自动分流。结构化 = 能被流程消费。
L03

串起工作流:白板 + 流水线

🤔 痛点三个员工各自会干活了,可谁先谁后、产出往哪传、审校不过怎么返工?没有一条"流水线"把它们编排起来,就是三个各说各话的散兵。
💡 本质用一块共享黑板(state)传递产物,用一段编排逻辑规定顺序(Day 39~41)。流程:研究员写"要点"上黑板 → 写手取"要点"、写"稿子"上黑板 → 审校看"稿子",过就结束、不过就把问题贴上黑板让写手返工。返工设个上限(比如最多 2 次),防止无限打回(Day 32 循环上限)。
研究 → 写作 → 审校(不过则返工) 研究员产出要点+出处 写手写成简报 审校(主编)查编造/出处 ✗ 不过 → 带问题返工(≤2次) ✓ 通过→出稿 共享黑板 state:{要点, 稿子, 审校意见, 返工次数} —— 谁产出都贴这
图注:这就是 Day 47 主管-专家、Day 48 三 Agent 协作的落地形态。返工闸带上限,防死循环。
# 编排骨架:手写版流水线(先不上框架,看清流程)
import json
def workflow(topic):
    state = {"topic": topic, "retries": 0}
    state["points"] = run_agent(RESEARCHER, f"主题:{topic},检索并整理要点")
    while state["retries"] <= 2:                          # 返工上限 2 次
        state["draft"]  = run_agent(WRITER, state["points"])
        review = json.loads(run_agent(REVIEWER,
                    f"要点:{state['points']}\n稿子:{state['draft']}"))
        if review["pass"]:                               # 审校通过 → 出稿
            return state["draft"]
        # 不过:把问题贴回黑板,让写手带着问题返工
        state["points"] += f"\n【审校要求修正】{review['problems']}"
        state["retries"] += 1
    return state["draft"] + "\n(注:已达返工上限,请人工复核)"  # 兜底不硬崩
这条手写流水线,正是 LangGraph 的 StateGraph 想帮你管的东西(节点=员工、边=流转、条件边=审校分流)。作品集里先手写讲清原理,想升级成可视化、可持久化的图,去看精讲(选学,学完回来继续 Day 64):
🔗 深入《LangGraph 20 天精讲》→ 🔗 深入《CrewAI 精讲》看角色协作 →
L04

审校 = 防幻觉质检闸

🤔 痛点大模型最大的信任杀手是"一本正经地编"(幻觉,Day 10)。写手为了文章流畅,很可能顺手加一句"资料里根本没有的数据"。没有质检,这种编造会大摇大摆混进最终报告。
💡 本质审校就是一道独立的质检闸(Critic)(呼应 Day 53 防幻觉 Critic)。它的唯一使命是拿"稿子"逐句去对"研究员给的要点+出处",凡是要点里查无实据的说法一律标红打回。让"生产"和"质检"由不同角色做,是保证可信的关键——就像出厂前必过质检,不让写手自己给自己盖章。

👶 小白:审校自己也是大模型,它会不会也看走眼、或者放水?

👨‍🏫 老师:会,所以有三招提升它的可靠性:①给它明确的核对规则(逐条对出处 ID,而不是笼统"看看对不对",Day 53 证据 ID 核对);②让它宁可误杀不可放过(拿不准就打回,偏保守);③用评测(L05)量出审校本身的准确率,不达标就改它的 prompt。审校不是万能,但"有一道独立质检"永远比"没有"强得多——这也是面试谈"你怎么防幻觉"的标准答法。

L05

评测 + 可观测:给整条产线装台账

🤔 痛点多个 Agent 串起来,一旦结果不好,你根本不知道是哪一环出的问题:研究员没找全?写手瞎发挥?审校放水了?没有记录,就只能瞎猜、盲改(Day 49 说的"没评测=盲改")。
💡 本质两件事一起上。评测(阶段 9):攒主题 golden set,量"审校通过率、抽查编造率、端到端耗时/成本"。可观测(阶段 10):给每次运行记一条trace(工时台账)——每个 Agent 的输入输出、耗时、Token,串成一条链。这样出问题能一眼倒查到是哪一环。
# 极简可观测:给每一步打结构化日志(trace 的雏形,Day55)
import time, json
def traced(role, fn, inp):
    t0 = time.time()
    out = fn(inp)
    print(json.dumps({                     # 结构化日志:一行一步,好检索、好统计
        "role": role, "ms": int((time.time()-t0)*1000),
        "in_len": len(inp), "out_len": len(out)}, ensure_ascii=False))
    return out
# 每步都包一层:traced("研究员", lambda x: run_agent(RESEARCHER, x), topic)
# 跑完一条链,日志就是一份"工时台账":哪步慢、哪步输出异常短,一目了然
📝 举个例子:可观测帮你抓到的真实问题 看 trace 发现"审校通过率只有 40%,而且每次都卡在写手→审校反复返工 2 次仍不过"。顺着日志看写手输出,发现它总爱加一句"据专家预测……"(编造)。定位是写手 prompt 没强调"只用清单内容" → 补一句约束 → 通过率升到 85%。"我通过 trace 定位到多智能体链条里的薄弱环节并优化"——这是资深味十足的面试故事,把阶段 9、10 学的全用上了。想看生产级可信/可观测怎么做,去看精讲(选学):
🔗 深入《可信 Agent 工程化精讲》→
L06

部署 + 可复用骨架

🤔 痛点三个 Agent + 编排 + 评测 + 日志,文件一多容易乱。压轴项目更要有清爽的结构,让面试官一打开仓库就看懂"哦,这是个分工明确的多智能体系统"。
💡 本质还是"各归各位":角色(prompt)一个文件夹、编排一个文件、评测/可观测各一份。它复用了作品①的检索、作品②的工具,所以可以把那两个当依赖引进来——让三件作品形成一个体系,而不是三个孤岛,这本身就是加分的架构叙事。
# 可复用项目骨架(压轴项目,集大成)
report-crew/
├── agents/
│   ├── researcher.py   # 研究员:复用作品①的 RAG 检索
│   ├── writer.py       # 写手
│   └── reviewer.py     # 审校(质检闸,L04)
├── workflow.py         # 编排:研究→写→审→返工(L03)
├── tools/              # 复用作品②的工具(联网/查库等)
├── trace.py            # 可观测:结构化日志 / trace(L05)
├── app.py              # FastAPI:POST /report {topic} → 出简报(Day09)
├── eval.py             # 评测:通过率 / 编造率 / 耗时成本(L05)
├── golden.json
├── requirements.txt
├── Dockerfile          # 打包(Day57)
└── README.md           # 架构图 + 效果数字 + demo(Day65)
👶 三件作品做完,我到底攒下了啥?一条完整的能力链:作品① 证明你会 RAG + 评测;作品② 证明你会工具调用 + 安全护栏;作品③ 证明你会多智能体编排 + 可观测 + 防幻觉。这三件覆盖了这行最核心的能力面。明天(Day 65)就教你怎么把它们"陈列"出来——好的作品也要会展示,酒香也怕巷子深。
L07

今日小结 + 动手 10 分钟

🧠 今天你应该能回答

  • 为什么把"写报告"拆成研究员/写手/审校三个 Agent,而不用一个搞定?
  • 三个角色靠什么被"捏"成不同性格?(不同 system prompt + 首尾相接的输入输出)
  • 共享黑板(state)和返工上限各解决什么问题?
  • 审校为什么必须独立?它怎么防幻觉(逐条对出处、宁杀勿放)?
  • 评测量哪些指标?可观测的 trace 怎么帮你倒查是哪一环出错?

✋ 动手 10 分钟:手动扮演三个角色跑一遍流水线

不接大模型,你亲自当研究员/写手/审校,把"黑板 + 返工"的流程走通,体会编排逻辑:

state = {"topic": "咖啡因对睡眠的影响", "retries": 0}
# 你扮研究员:写 2 条带出处的要点
state["points"] = "1) 咖啡因阻断腺苷【出处:A.pdf】\n2) 半衰期约5小时【出处:B.pdf】"
# 你扮写手:只用上面要点写一句话(故意混进一条没出处的“编造”试试)
state["draft"] = "咖啡因阻断腺苷、半衰期约5小时,且能提升智商20%。"
# 你扮审校:对照要点挑错 → 第三句“提升智商”查无出处 → 打回
review = {"pass": False, "problems": ["‘提升智商20%’在要点里查无出处,疑似编造"]}
print("审校结果:", review)     # pass=False → 该带着 problems 返工
# 想想:如果 retries 已经=2 了,流程该怎么兜底不死循环?

能说清"黑板上此刻有什么、审校为什么打回、返工怎么终止",今天就通关了。

明日预告 · Day 65:三件作品做完,该"陈列"了。明天讲GitHub / 博客 / 开源:README 怎么写才让人 30 秒看懂、怎么录一段抓人的 demo、怎么写一篇"讲设计取舍"的技术博客,以及怎么给开源项目提第一个 PR。会做 + 会展示,offer 才会来。
← Day 63 · 作品集② 多工具 Agent Day 65 · GitHub / 博客 / 开源 →