进阶 📋 6 个步骤 第 561 / 563 篇

用 fast-agent 几分钟搭出复合智能体与工作流:声明式语法、五种编排模式与 MCP 服务器实操

写 Agent 应用最耗时的不是提示词,而是胶水代码:MCP 服务器怎么接、几个 Agent 怎么串、换模型要不要改代码、端到端测试怎么做。fast-agent 用一层声明式语法把这几件事件收敛掉:一个装饰器定义一个 Agent,一行装饰器切换串行、并行、路由、评估优化与 k 票表决五种编排,MCP 工具按 Agent 分派。本教程从装 uv 到把工作流反向暴露成 MCP 服务器,六步跑通。

2026-10-24· 11 分钟上手· 约 3457 字· ⚡ fast-agent / 🔗 工作流编排

写 Agent 应用时最耗时间的往往不是提示词,而是胶水代码:MCP 服务器怎么接、几个 Agent 怎么串起来、想换个模型对比效果要不要改代码、端到端测试怎么做。fast-agent 把这几件事收敛成一层声明式语法:你用装饰器描述「有哪些 Agent、各自挂哪些 MCP 服务器、彼此怎么编排」,运行、流式输出、工具循环、日志与模型切换都交给框架。它是少数把 MCP 的 Sampling 与 Elicitation 也完整支持并端到端测过的框架,支持图片与 PDF 这类多模态内容,还能把你调好的 Agent 反过来暴露成一个 MCP 服务器给别人调用。本教程从装 uv 开始,跑通单 Agent、接上 MCP 服务器、用五种编排模式搭工作流、处理多模态与人工确认,最后把它变成可对外提供能力的服务。

⚡ 本教程适合:有 Python 基础、想用最少代码把 MCP 工具与多步流程组织起来的开发者。你需要 Python 与 uv 包管理器,以及至少一个模型服务商的密钥。文中命令以 fast-agent 官方文档站与当前 PyPI 版本为准;旧的 --server 开关已被官方标记弃用、改用 --transport,跨小版本参数可能有调整,请以你安装的版本与 fast-agent --help 的输出为准。

Step 1:装 uv 与 fast-agent,先跑一个交互会话

1 十分钟确认整条链路是通的

fast-agent 走 Python 生态,推荐用 uv 安装,避免污染系统解释器。装好之后先不要写代码,用 fast-agent go 起一个交互式会话确认三件事:命令行工具在 PATH 里、模型密钥能被读到、模型能正常返回。这一步通过再往下走,能省掉后面大量「到底是框架问题还是密钥问题」的排查时间。go 后面可以直接跟远程 MCP 服务器地址、模型名或已安装的卡片包,用来快速试工具与模型的搭配。

# 1) 装 uv(官方安装脚本,详见 docs.astral.sh/uv)
curl -LsSf https://astral.sh/uv/install.sh | sh           # Linux / macOS
# Windows(PowerShell):
# powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

# 2) 装 fast-agent 命令行工具
uv tool install fast-agent-mcp
# 机器上有多个 Python 版本时显式指定,避免装到旧解释器上
uv tool install fast-agent-mcp --python 3.13.5

# 3) 起一个交互式会话,确认能跑
fast-agent go                            # 默认模型,直接开聊
fast-agent go --url https://hf.co/mcp    # 直接连一个远程 MCP 服务器试工具
fast-agent go --model=generic.qwen2.5    # 用 Ollama 上的本地模型
fast-agent go --pack hf-dev              # 用已安装或市场里的卡片包

还有一个目录约定值得先知道:fast-agent 会从当前目录开始向上递归查找配置文件,所以你可以在项目根目录放一份 fastagent.config.yaml,下面的子目录里跑命令都会读到它。团队协同时这点很有用——一份配置、一套 MCP 服务器,所有人跑出来的工具集是一致的。

密钥不要进版本库:模型密钥写在 secrets 文件里而不是 config 文件里,并确保它已被 .gitignore 覆盖;config 是要提交的,secrets 不是。仓库里出现过密钥就当作已泄露处理,去服务商后台轮换,而不是只补一次 .gitignore。

Step 2:写一个最小 Agent,并学会不改代码换模型

2 一个装饰器就是一个可运行的 Agent

定义 Agent 的最小形态只需要一条 instruction,剩下的样板代码是固定的:建一个 FastAgent 应用、用 @fast.agent 声明 Agent、在 async with fast.run() 里发消息或开交互会话。把它存成 .py 文件用 uv run 跑起来即可。这个设计的价值在于「提示词即配置」——提示词留在文件里,能评审、能 diff、能版本管理,而不是散落在某次聊天记录里。

import asyncio
from fast_agent import FastAgent

# 创建一个 Agent 应用
fast = FastAgent("Agent Example")

@fast.agent(
    instruction="Given an object, respond only with an estimate of its size."
)
async def main():
    async with fast.run() as agent:
        # 发一条消息,拿到结果
        moon_size = await agent("the moon")
        print(moon_size)
        # 也可以开一个交互式会话,边聊边调
        # await agent.interactive()

if __name__ == "__main__":
    asyncio.run(main())

换模型不需要改代码,用命令行开关即可:uv run sizer.py --model sonnet 这样一行就能把同一个 Agent 换到另一个模型上跑。这个能力在调试阶段极其好用——同一个流程分别在强模型与便宜模型上跑一遍,你能立刻看出是哪一步开始掉链子,而不是笼统地觉得「模型不行」。如果要把 Agent 嵌进脚本,加 --quiet 只返回最终结果,输出干净便于管道处理。

💡 框架内置了 Echo 与 Playback 这类测试用模型,可以在没有密钥或不想烧钱的情况下把胶水代码跑通、把已录制的会话回放一遍。具体模型名与用法以官方文档的测试章节为准,别照抄几个月前的博客片段。

Step 3:把 MCP 服务器挂到指定的 Agent 上

3 工具按 Agent 分派,而不是全局全开

MCP 服务器统一在 fastagent.config.yaml 里声明,Agent 通过 servers 参数点名要用哪几个。这个「按 Agent 分派」的粒度很重要:工具数量直接影响模型选对工具的概率与冷启动耗时,让摘要 Agent 只挂抓取服务器、让写作 Agent 一个工具都不挂,比把所有工具开给所有 Agent 稳得多。

# fastagent.config.yaml
mcp:
  servers:
    fetch:
      command: "uvx"
      args: ["mcp-server-fetch"]
    filesystem:
      command: "npx"
      args: ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/project"]
@fast.agent(
    "url_fetcher",
    "Given a URL, provide a complete and comprehensive summary",
    servers=["fetch"],          # 对应上面 config 里的服务器名
)
@fast.agent(
    "social_media",
    """Write a 280 character social media post for any given text.
    Respond only with the post, never use hashtags.""",
)
@fast.chain(
    name="post_writer",
    sequence=["url_fetcher", "social_media"],
)
async def main():
    async with fast.run() as agent:
        await agent.post_writer("http://llmindset.co.uk")

跑起来是 uv run workflow/chaining.py;也可以用 --agent 指定跑哪一个组件、用 --message 直接给输入,方便写进脚本或 CI。官方的 quickstart 命令(fast-agent quickstart workflow)会直接生成一批示例,覆盖 Anthropic《Building Effective Agents》里的几种模式,还包含一个带评估优化闭环的 Researcher Agent 与一个演示 MCP Roots 的数据分析 Agent,建议先照着跑一遍再自己写。

Windows 用户注意:官方说明里提到,Filesystem 与 Docker 两个 MCP 服务器在 Windows 上需要额外的配置改动,相关说明写在你生成的配置文件注释里,照着改即可。另外工具越多不等于能力越强:一次挂上几十个工具,模型选错工具的概率会明显上升,冷启动也会变慢。

Step 4:用五种编排模式把 Agent 组成工作流

4 串行、并行、路由、评估优化、k 票表决

工作流是 fast-agent 的主场,五种模式用一行装饰器切换。chain 是串行管道,前一个的输出是后一个的输入,适合「先抓后写」这类固定两步;parallel 把同一条消息扇出给多个 Agent 同时处理,适合翻译、多视角评审这些彼此独立的分支;router 让模型判断该交给哪个 Agent,适合入口不确定、各分支成本差异大的场景;evaluator_optimizer 由生成器与评估器组成闭环,直到评估达到指定等级或达到打磨次数上限;maker 重复采样同一个工作 Agent,直到某个答案拿到 k 票优势,用多次采样换稳定性。

@fast.chain(name="post_writer", sequence=["url_fetcher", "social_media"])

@fast.parallel(name="translate", fan_out=["translate_fr", "translate_de", "translate_es"])

@fast.router(name="route", agents=["agent1", "agent2", "agent3"])

@fast.evaluator_optimizer(
    name="researcher",
    generator="web_searcher",
    evaluator="quality_assurance",
    min_rating="EXCELLENT",
    max_refinements=3,          # 硬上限,防止无限打磨烧钱
)

@fast.maker(
    name="reliable_classifier",
    worker="classifier",
    k=3,                        # 需要拿到的票数优势
    max_samples=25,             # 采样硬上限
)

选择顺序建议是「能用 chain 说清楚的不要上 router」。router 的收益是省成本,代价是路由错了整条链都错,而且错得悄无声息;evaluator_optimizer 与 maker 的收益是质量,代价是调用次数成倍增长——它们一定要配上 max_refinements 与 max_samples 这类上限参数,否则一次跑偏就是一张意料之外的账单。上线前先用小样本跑一遍,把单次任务的调用次数与耗时记下来,再决定要不要放量。

💡 每个组件都能单独对话:在工作流跑完之后,你仍然可以点名其中某个 Agent 继续追问、让它解释为什么这么写。排障时这比看日志快得多——先定位是哪个组件开始跑偏,再去改那一个组件的提示词。

Step 5:多模态、结构化输出与向人要信息

5 图片与 PDF 直接进流程,关键时刻让人拍板

多模态是这个框架的既有能力:图片与 PDF 可以作为提示词、资源以及 MCP 工具调用的结果直接进入流程,不需要你自己先把 PDF 转成文本再喂进去。对下游代码来说,更有用的是结构化输出——把 Agent 的输出约定成固定结构,就能直接交给程序处理,不必再用正则去抠自然语言里的字段。具体写法以官方文档对应章节为准,别凭印象猜参数。

@fast.agent(
    "invoice_reader",
    "从发票里提取金额、开票日期与供应商,字段不全就说明缺哪个。",
    servers=["filesystem"],
)
async def main():
    async with fast.run() as agent:
        # 结果文件里带遥测,便于横向对比不同模型的表现
        data = await agent.invoice_reader.send(
            "读取 /Users/you/invoices/2026-10.pdf 并提取字段"
        )
        print(data)

还有一类场景值得单独提:需要人拍板时,Agent 可以反过来向你要信息,而不是自己猜一个继续跑。这对应 MCP 的 Elicitation 能力,也是 fast-agent 官方强调的完整支持项之一。典型用法是「找到三个候选航班,让我选一个再下单」——把决策点显式留给人,流程的可靠性会高一个量级,尤其是涉及付款、发信、删数据这类不可逆动作时。

不可逆动作必须留人工闸门:不管框架支持得多好,涉及付款、对外发信、删除数据、修改生产配置的操作,都要让 Agent 先给出计划、等人确认再执行。把「先问再做」写进 instruction,比事后回滚便宜得多。

Step 6:把你的 Agent 暴露成一个 MCP 服务器

6 调好的工作流,直接变成别人的工具

最后一步是反向暴露:任何一个 Agent 或工作流都能通过传输参数切成服务端模式,对外提供 MCP 接口。这意味着你调好的「读发票」或「写周报」流程,不用再复制粘贴提示词给同事,让他们把你的服务当工具接上就行。对外提供 HTTP 传输时,官方还提供了传输诊断能力,用于检查 Streamable HTTP 的用法是否规范——这类细节在自建网关或代理后面尤其容易出问题。

# 1) 以服务端模式跑起来(--transport 会自动切到 server 模式)
uv run agent.py --transport http --port 8001

# 2) 客户端侧接入(以 Claude Code 为例,其余支持 MCP 的客户端同理)
claude mcp add --transport http my-agent http://127.0.0.1:8001/mcp

# 3) 批量跑评估:--quiet 只输出最终结果,便于写进脚本与 CI
uv run workflow/chaining.py --agent post_writer --message "" --quiet > out.txt

收尾三件事。一是把暴露出去的工具集收敛到最小,只留必要工具,写操作全部走人工确认。二是善用内置遥测:结果文件里带调用与耗时数据,把它留下来做回归对比,比凭感觉判断「这次好像变慢了」靠谱。三是别忘了版本——提示词、config、工作流定义都在文件里,是最适合进版本库的一类资产,改动要有评审记录。

💡 建议把「模型 + 提示词 + 工具集」三者的版本一起记在结果文件里。排查线上问题时,你最想知道的从来不是「它说了什么」,而是「当时用的哪个模型、哪版提示词、挂了哪些工具」。

暴露服务等于开放工具权限:把 Agent 暴露成 MCP 服务器之后,任何能访问该端点的人都能驱动你的工具与密钥。至少做到:只绑本机回环或置于鉴权之后、只挂必要工具、写操作人工确认、不要把对外服务与你的主密钥放在同一进程环境里。

常见问题速查

现象原因与处理
提示找不到配置文件从当前目录向上递归查找;确认文件在命令执行目录或其父目录
模型调用报密钥错误密钥写在 secrets 文件里或走环境变量;确认没被 .gitignore 的文件提交过
Agent 用不上某个工具检查 config 里 mcp.servers 的名字与 Agent 的 servers 列表是否一致
Windows 上 filesystem / docker 服务器起不来官方生成的配置文件里有 Windows 需要的改动注释,照着改
想对比模型又不想改代码用 --model 开关,同一份代码跑多个模型
输出太长没法接管道加 --quiet 只保留最终结果
--server 参数报错已被官方标记弃用,改用 --transport
并行工作流很慢每个分支都是一次完整模型调用,成本与耗时叠加;先用小样本验证
评估优化闭环停不下来给 evaluator_optimizer 设 max_refinements 硬上限,给 maker 设 max_samples
← 返回教程中心