mcpField
F
Fast Agent

Fast Agent

github.com/evalstate框架与 SDK4.0k1 天前更新

关于 Fast Agent

fast-agent 是一个用于定义、提示和测试 MCP Agent 与工作流的框架,适合作为编码 Agent、开发工具包或评估平台使用。它以命令行为主,支持 Anthropic、OpenAI、Google 等多种模型提供方。项目声称完整支持 MCP 特性,包括 Sampling 与 Elicitations,并可检查 Streamable HTTP 传输的使用情况。

主要功能

  1. 01基于声明式文件定义 Agent 与提示词
  2. 02通过 /connect 连接 stdio 或 Streamable HTTP 服务
  3. 03Agent 串联、并行工具调用与多种工作流模式
  4. 04支持 Sampling 与 Elicitations 等 MCP 特性
  5. 05OAuth 认证,密钥存储于 KeyRing

适用场景

  1. 01搭建连接 MCP 服务的编码 Agent 或数据分析 Agent
  2. 02组合多个 Agent 构建链式或编排式工作流
  3. 03检查 Streamable HTTP 传输过程以排查部署问题

MCP 相关

作为 MCP 客户端,fast-agent 可通过 /connect 或配置文件连接 stdio 与 Streamable HTTP 类型的 MCP 服务,并支持 OAuth 与 ping 检测。

详细介绍

README 配图 README 配图 README 配图 discord Pepy Total Downloads README 配图

从这里开始

提示

最新文档请参阅 https://fast-agent.ai。

fast-agent 提供了与大模型交互的灵活方式,非常适合用作编码 Agent、开发工具包、评估或工作流平台。

要启动支持 shell 的交互式会话,请安装 uv 并运行

uvx fast-agent-mcp@latest -x

要使用 Hugging Face 推理提供商开始编码,或使用 OpenAI Codex 计划:

# Code with Hugging Face Inference Providers
uvx fast-agent-mcp@latest --pack hf-dev

# Code with Codex (agents optimized for OpenAI)
uvx fast-agent-mcp@latest --pack codex

输入 ! 进入 shell,或运行 shell 命令,例如 ! cd web && npm run build。

使用 /skills 命令管理技能,并通过 /connect 连接 MCP 服务。默认的 fast-agent 注册表包含多种技能,可用于设置 LSP、Agent 和工具钩子、上下文压缩策略、自动化等。

# /connect supports stdio or streamable http (with OAuth)

# Start a STDIO server
/connect @modelcontextprotocol/server-everything

# Connect to a Streamable HTTP Server
/connect https://huggingface.co/mcp

建议安装 fast-agent,以设置 shell 别名和其他工具。

# Install fast-agent
uv tool install -U fast-agent-mcp

# Run fast-agent with opus, shell support and built-in subagents
fast-agent --model opus -xx

使用通用提供商运行本地模型,或自动为 llama.cpp 创建正确的配置:

fast-agent model llamacpp

任何 fast-agent 设置或程序都可以搭配任何 ACP 客户端使用——最简单的方法是使用 fast-agent-acp:

# Run fast-agent inside Toad
toad acp "fast-agent-acp -x --model sonnet"

fast-agent 让你能够在几分钟内创建和使用复杂的多模态 Agent 与工作流。它是首个提供完整端到端 MCP 功能支持(包括 Sampling 和 Elicitations)的框架,并经过了测试。

fast-agent 以命令行为先,并提供由 prompt_toolkit 驱动的交互式终端提示(TUI 风格的输入、补全和终端内菜单)作为可选功能;响应可以通过 rich 实时流式输出到终端,无需依赖全屏 curses UI 或外部图形界面覆盖层。

简洁的声明式语法让你专注于组合提示词和 MCP 服务,以构建高效的 Agent。

模型支持全面,原生支持 Anthropic、OpenAI 和 Google 提供商,也可通过 TensorZero 支持 Azure、Ollama、Deepseek 等数十种提供商。结构化输出、PDF 和视觉功能易于使用且经过充分测试。直通和回放大模型可加快为应用开发和测试 Python 胶水代码的速度。

近期功能包括:

  • Agent 技能(SKILL.md)
  • MCP Apps
  • OpenAI Apps SDK
  • Shell 模式
  • 高级 MCP 传输诊断
  • MCP Elicitations
MCP 传输诊断

fast-agent 是唯一一款允许你检查 Streamable HTTP 传输用量的工具——这对于确保部署可靠且合规至关重要。支持 OAuth,并使用 KeyRing 存储密钥。使用 fast-agent auth 命令进行管理。

重要

文档位于此仓库的 docs/ 目录下。请从 仓库根目录运行文档辅助脚本,以安装、生成、构建、提供服务、截图并评估站点。

Agent 应用开发

定义 Agent 应用的提示词和配置都存储在简单的文件中,样板代码极少,因此易于管理和版本控制。

在工作流执行前、执行期间和执行后,与单个 Agent 和组件聊天,以调整和诊断应用。Agent 可以请求人工输入,以获取完成任务所需的额外上下文。

通过简单的模型选择,测试模型与 MCP 服务之间的交互变得轻松。你可以在此了解此项目背后的动机。

2025-03-23-fast-agent

开始使用:

首先,为 Python 安装 uv 包管理器。然后:

uv pip install fast-agent-mcp          # install fast-agent!
fast-agent go                          # start an interactive session
fast-agent go --url https://hf.co/mcp  # with a remote MCP
fast-agent go --model=generic.qwen2.5  # use ollama qwen 2.5
fast-agent go --pack analyst --model haiku  # install/reuse a card pack and launch it
fast-agent scaffold                    # create an example agent and config files
uv run agent.py                        # run with default_model or FAST_AGENT_MODEL configured
uv run agent.py --model='gpt-5.4-mini?reasoning=low'    # specify a model
uv run agent.py --transport http --port 8001  # expose as MCP server (server mode implied)
fast-agent quickstart workflow  # create "building effective agents" examples

如果未配置模型,交互式运行会打开模型选择器。 自动化和服务运行必须传入 --model、设置 FAST_AGENT_MODEL、配置 default_model,或在 Agent 上声明模型。

对于打包的入门 Agent,请使用 fast-agent go --pack <name> --model <model>。 如果需要,此命令会将安装包安装到所选的 fast-agent home,然后 正常启动 go。对于未显式设置 模型的卡片,--model 会作为备用选项;如果 AgentCard 中直接声明了模型,则优先使用该模型。

其他快速入门示例包括研究员 Agent(采用评估器-优化器工作流)和数据分析 Agent(类似 ChatGPT 体验),展示了对 MCP Roots 的支持。

提示

Windows 用户:Filesystem 和 Docker MCP 服务需要进行一些配置更改,必要的更改详见配置文件。

基础 Agent

定义 Agent 就像这样简单:

@fast.agent(
  instruction="Given an object, respond only with an estimate of its size."
)

然后,我们可以向 Agent 发送消息:

async with fast.run() as agent:
    moon_size = await agent("the moon")
    print(moon_size)

或者与 Agent 开始交互式聊天:

async with fast.run() as agent:
    await agent.interactive()

下面是完整的 sizer.py Agent 应用,包含样板代码:

import asyncio
from fast_agent import FastAgent

# Create the application
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:
        await agent.interactive()


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

然后可以通过 uv run sizer.py 运行 Agent。

使用 --model 开关指定模型,例如 uv run sizer.py --model sonnet。

模型字符串也支持查询参数覆盖。例如:

  • uv run sizer.py --model "gpt-5?reasoning=low"
  • uv run sizer.py --model "claude-sonnet-4-6?web_search=on"
  • uv run sizer.py --model "claude-sonnet-4-5?context=1m"
  • uv run sizer.py --model "gpt-5?poll_period=240"

对于 Anthropic 模型,只有较早的 Sonnet 4 / Sonnet 4.5 模型仍要求显式启用 1M 上下文时,才需要使用 ?context=1m。Claude Sonnet 4.6 和 Claude Opus 4.6 默认已使用长上下文窗口,因此虽然为向后兼容而接受 ?context=1m, 但在这两个模型上没有必要。

组合 Agent 并使用 MCP 服务

要生成示例,请使用 fast-agent quickstart workflow。可以通过 uv run workflow/chaining.py 运行此示例。将 fast-agent.yaml 放在当前使用的 fast-agent home 中,或在需要时传入明确的配置路径。

可以将 Agent 链接起来构建工作流,并使用 fast-agent.yaml 文件中定义的 MCP 服务:

@fast.agent(
    "url_fetcher",
    "Given a URL, provide a complete and comprehensive summary",
    servers=["fetch"],  # Name of an MCP Server defined in fast-agent.yaml
)
@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:
        # using chain workflow
        await agent.post_writer("http://llmindset.co.uk")

所有 Agent 和工作流都可以响应 .send("message") 或 .prompt(),以开始聊天会话。

保存为 social.py 后,现在可以通过命令行运行此工作流:

uv run workflow/chaining.py --agent post_writer --message "<url>"

添加 --quiet 开关可禁用进度和消息显示,并且仅返回最终回复,适用于简单的自动化任务。

MAKER

MAKER(“大规模分解式 Agent 流程与 K 投票错误减少”)封装一个工作 Agent,并对其反复采样,直到某个回复在投票中领先所有其他选项 k 票(“领先 k 票即胜出”)。这适用于由大量简单步骤组成的长链式任务,因为在此类任务中,罕见错误原本会不断累积。

@fast.agent(
    name="classifier",
    instruction="Reply with only: A, B, or C.",
)
@fast.maker(
    name="reliable_classifier",
    worker="classifier",
    k=3,
    max_samples=25,
    match_strategy="normalized",
    red_flag_max_length=16,
)
async def main():
    async with fast.run() as agent:
        await agent.reliable_classifier.send("Classify: ...")
将 Agent 作为工具

将 Agent 作为工具的工作流会接收一项复杂任务,将其拆分为多个子任务,并根据主 Agent 的指令调用其他 Agent 作为工具。

此模式受 OpenAI Agents SDK 的 将 Agent 作为工具 功能启发。

将子 Agent 暴露为工具后,你可以直接在指令中实现路由、并行处理和编排者-工作者分解,也可以将它们组合使用。每轮支持多个工具调用,并且会并行执行。

常见用法可能包括:

  • 路由:根据用户提示选择合适的专业工具。
  • 并行处理:分派多个相互独立的事项/项目,然后汇总结果。
  • 编排者-工作者:将任务拆分为范围明确的子任务(通常通过简单的 JSON 计划),然后协调执行。
@fast.agent(
    name="NY-Project-Manager",
    instruction="Return NY time + timezone, plus a one-line project status.",
    servers=["time"],
)
@fast.agent(
    name="London-Project-Manager",
    instruction="Return London time + timezone, plus a one-line news update.",
    servers=["time"],
)
@fast.agent(
    name="PMO-orchestrator",
    instruction=(
        "Get reports. Always use one tool call per project/news. "  # parallelization
        "Responsibilities: NY projects: [OpenAI, Fast-Agent, Anthropic]. London news: [Economics, Art, Culture]. "  # routing
        "Aggregate results and add a one-line PMO summary."
    ),
    default=True,
    agents=["NY-Project-Manager", "London-Project-Manager"],  # orchestrator-workers
)
async def main() -> None:
    async with fast.run() as agent:
        await agent("Get PMO report. Projects: all. News: Art, Culture")

扩展示例和所有参数的示例位于仓库中的 examples/workflows/agents_as_tools_extended.py。

MCP OAuth(v2.1)

对于 SSE 和 HTTP MCP 服务,OAuth 默认启用,只需最少配置。系统会使用本地回调服务捕获授权码;如果端口不可用,则可通过粘贴 URL 作为备用方案。

  • 在 fast-agent.yaml 中进行最少的每服务设置:
mcp:
  servers:
    myserver:
      transport: http # or sse
      url: http://localhost:8001/mcp # or /sse for SSE servers
      auth:
        oauth: true # default: true
        redirect_port: 3030 # default: 3030
        redirect_path: /callback # default: /callback
        # scope: "user"       # optional; if omitted, server defaults are used
  • OAuth 客户端使用 PKCE 和内存中的令牌存储(不会将令牌写入磁盘)。
  • 令牌持久化:默认情况下,令牌会通过 keyring 安全地存储在操作系统密钥链中。如果密钥链不可用(例如,无头容器环境),则会在会话期间使用内存存储。
  • 若要强制某个服务仅使用内存存储,请设置:
mcp:
  servers:
    myserver:
      transport: http
      url: http://localhost:8001/mcp
      auth:
        oauth: true
        persist: memory
  • 若要为特定服务禁用 OAuth,请为该服务设置 auth.oauth: false。

MCP Ping(可选)

任一端(客户端或服务端)都可以启用 MCP ping 工具。请参阅 Ping 概述。

客户端侧的 ping 按每个服务进行配置(默认值:间隔 30s,连续 3 次 ping 失败):

mcp:
  servers:
    myserver:
      ping_interval_seconds: 30 # optional; <=0 disables
      max_missed_pings: 3 # optional; consecutive timeouts before marking failed

工作流

链式工作流

chain 工作流提供了一种更具声明性的方式,可按顺序调用 Agent:


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

# we can them prompt it directly:
async with fast.run() as agent:
  await agent.post_writer()

这会启动一个交互式会话,根据给定 URL 生成一条简短的社交媒体帖子。如果在链式工作流中输入提示词,系统会返回与链中最后一个 Agent 的对话。输入 @agent-name 可以切换要发送提示词的 Agent。

链式工作流可以嵌入其他工作流,也可以包含其他工作流元素(包括其他链式工作流)。如有需要,你可以设置 instruction,向其他工作流步骤准确描述其功能。

人工输入

Agent 可以请求人工输入,以协助完成任务或获取更多上下文:

@fast.agent(
    instruction="An AI agent that assists with basic tasks. Request Human Input when needed.",
    human_input=True,
)

await agent("print the next number in the sequence")

在示例 human_input.py 中,Agent 会提示用户提供更多信息,以完成任务。

并行工作流

并行工作流会同时向多个 Agent 发送相同的消息(fan-out),然后使用 fan-in Agent 处理合并后的内容。

@fast.agent("translate_fr", "Translate the text to French")
@fast.agent("translate_de", "Translate the text to German")
@fast.agent("translate_es", "Translate the text to Spanish")

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

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

如果未指定 fan-in Agent,parallel 会原样返回合并后的 Agent 结果。

parallel 也适用于汇集不同大模型生成的想法。

在其他工作流中使用 parallel 时,请指定 instruction 来描述其运行方式。

评估器-优化器

评估器-优化器由 2 个 Agent 组成:一个负责生成内容(generator),另一个负责评判内容并提供可执行的反馈(evaluator)。消息会先发送给生成器,然后两个 Agent 进入循环,直到评估器对质量满意,或达到最大改进次数。最终返回生成器的结果。

如果生成器关闭了 use_history,请求改进时会返回上一轮的结果;否则会使用对话上下文。

@fast.evaluator_optimizer(
  name="researcher",
  generator="web_searcher",
  evaluator="quality_assurance",
  min_rating="EXCELLENT",
  max_refinements=3
)

async with fast.run() as agent:
  await agent.researcher.send("produce a report on how to make the perfect espresso")

在工作流中使用时,它会返回最后一条 generator 消息作为结果。

查看 evaluator.py 工作流示例,或运行 fast-agent quickstart researcher 获取更完整的示例。

路由器

路由器使用大模型评估消息,并将其路由到最合适的 Agent。路由提示词会根据 Agent 指令和可用的服务自动生成。

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

查看 router.py 工作流示例。

编排器

面对复杂任务时,编排器会使用大模型生成计划,将任务分配给可用的 Agent。规划提示词和汇总提示词由编排器生成,因此使用能力更强的模型会更有优势。计划可以在开始时一次性制定(plan_type="full"),也可以迭代制定(plan_type="iterative")。

@fast.orchestrator(
  name="orchestrate",
  agents=["task1","task2","task3"]
)

查看 orchestrator.py 或 agent_build.py 工作流示例。

Agent 功能

调用 Agent

为简洁起见,所有定义都允许省略名称和指令参数:

@fast.agent("You are a helpful agent")          # Create an agent with a default name.
@fast.agent("greeter","Respond cheerfully!")    # Create an agent with the name "greeter"

moon_size = await agent("the moon")             # Call the default (first defined agent) with a message

result = await agent.greeter("Good morning!")   # Send a message to an agent by name using dot notation
result = await agent.greeter.send("Hello!")     # You can call 'send' explicitly

await agent.greeter()                           # If no message is specified, a chat session will open
await agent.greeter.prompt()                    # that can be made more explicit
await agent.greeter.prompt(default_prompt="OK") # and supports setting a default prompt

await agent["greeter"].send("Good Evening!")    # Dictionary access is supported if preferred
定义 Agent
基本 Agent
@fast.agent(
  name="agent",                          # name of the agent
  instruction="You are a helpful Agent", # base instruction for the agent
  servers=["filesystem"],                # list of MCP Servers for the agent
  model="gpt-5.4-mini?reasoning=high",   # specify a model for the agent
  use_history=True,                      # agent maintains chat history
  request_params=RequestParams(temperature= 0.7), # additional parameters for the LLM (or RequestParams())
  human_input=True,                      # agent can request human input
)
链式
@fast.chain(
  name="chain",                          # name of the chain
  sequence=["agent1", "agent2", ...],    # list of agents in execution order
  instruction="instruction",             # instruction to describe the chain for other workflows
  cumulative=False,                      # whether to accumulate messages through the chain
  continue_with_final=True,              # open chat with agent at end of chain after prompting
)
并行
@fast.parallel(
  name="parallel",                       # name of the parallel workflow
  fan_out=["agent1", "agent2"],          # list of agents to run in parallel
  fan_in="aggregator",                   # name of agent that combines results (optional)
  instruction="instruction",             # instruction to describe the parallel for other workflows
  include_request=True,                  # include original request in fan-in message
)
评估器-优化器
@fast.evaluator_optimizer(
  name="researcher",                     # name of the workflow
  generator="web_searcher",              # name of the content generator agent
  evaluator="quality_assurance",         # name of the evaluator agent
  min_rating="GOOD",                     # minimum acceptable quality (EXCELLENT, GOOD, FAIR, POOR)
  max_refinements=3,                     # maximum number of refinement iterations
)
路由器
@fast.router(
  name="route",                          # name of the router
  agents=["agent1", "agent2", "agent3"], # list of agent names router can delegate to
  model="gpt-5.4-mini?reasoning=high",   # specify routing model
  use_history=False,                     # router does not maintain conversation history
  human_input=False,                     # whether router can request human input
)
编排器
@fast.orchestrator(
  name="orchestrator",                   # name of the orchestrator
  instruction="instruction",             # base instruction for the orchestrator
  agents=["agent1", "agent2"],           # list of agent names this orchestrator can use
  model="gpt-5.4-mini?reasoning=high",   # specify orchestrator planning model
  use_history=False,                     # orchestrator doesn't maintain chat history (no effect).
  human_input=False,                     # whether orchestrator can request human input
  plan_type="full",                      # planning approach: "full" or "iterative"
  plan_iterations=5,                     # maximum number of full plan attempts, or iterations
)
MAKER
@fast.maker(
  name="maker",                           # name of the workflow
  worker="worker_agent",                  # worker agent name
  k=3,                                    # voting margin (first-to-ahead-by-k)
  max_samples=50,                         # maximum number of samples
  match_strategy="exact",                 # exact|normalized|structured
  red_flag_max_length=256,                # flag unusually long outputs
  instruction="instruction",              # optional instruction override
)
作为工具的 Agent
@fast.agent(
  name="orchestrator",                    # orchestrator agent name
  instruction="instruction",              # orchestrator instruction (routing/decomposition/aggregation)
  agents=["agent1", "agent2"],            # exposed as tools: agent__agent1, agent__agent2
  max_parallel=128,                       # cap parallel child tool calls (OpenAI limit is 128)
  child_timeout_sec=600,                  # per-child timeout (seconds)
  max_display_instances=20,               # collapse progress display after top-N instances
)
函数工具

直接在代码中将 Python 函数注册为工具,无需 MCP 服务或外部文件。支持同步和异步函数。默认情况下,函数名称和文档字符串会分别用作工具名称和描述;也可以通过 name= 和 description= 覆盖。

每个 Agent 的工具(@agent.tool)——将工具限定在特定 Agent 中:

@fast.agent(name="writer", instruction="You write things.")
async def writer(): ...


@writer.tool
def translate(text: str, language: str) -> str:
    """Translate text to the given language."""
    return f"[{language}] {text}"


@writer.tool(name="summarize", description="Produce a one-line summary")
def summarize(text: str) -> str:
    return f"Summary: {text[:80]}..."

全局工具(@fast.tool)——供未声明自有工具的所有 Agent 使用:

@fast.tool
def get_weather(city: str) -> str:
    """Return the current weather for a city."""
    return f"Sunny in {city}"

@fast.agent(name="assistant", instruction="You are helpful.")
# assistant gets get_weather (global @fast.tool)

使用 @agent.tool 或 function_tools= 的 Agent 只能看到自己的工具,不会注入全局工具。使用 function_tools=[] 可明确选择不使用全局工具,且不配置任何工具。

多模态支持

直接使用 MCP 类型或通过外部 MCP 资源服务向提示词添加资源。便捷 API 可以简化此操作,例如:

summary: str = await agent.with_resource(
    "Summarise this PDF please",
    "mcp_server",
    "resource://fast-agent/sample.pdf",
)
MCP 工具结果转换

LLM API 对通过其 Chat Completions API 作为工具调用/函数结果返回的内容类型有限制:

  • OpenAI 支持文本
  • Anthropic 支持文本和图像
  • Google 支持文本、图像、PDF 和视频(例如 video/mp4)。

    注意:内嵌视频数据的大小限制为 20MB。对于更大的文件,请使用 File API。支持直接使用 YouTube URL。

对于 MCP 工具结果,ImageResources 和 EmbeddedResources 会转换为用户消息并添加到对话中。

提示词

支持使用 apply_prompt(name,arguments) 应用 MCP 提示词,该方法始终返回一条助手消息。如果 MCP 服务的最后一条消息是“用户”消息,就会将其发送给大模型处理。应用到 Agent 上下文中的提示词会保留,因此在 use_history=False 时,Agent 可以充当经过精细调优的响应器。

也可以在交互界面中使用 /prompt 命令来应用提示词。

采样

采样大模型按客户端/服务配对进行配置。在 fast-agent.yaml 中按以下方式指定模型名称:

mcp:
  servers:
    sampling_resource:
      command: "uv"
      args: ["run", "sampling_resource_server.py"]
      sampling:
        model: "haiku"
密钥文件

提示

将 fast-agent.secrets.yaml 放在当前 fast-agent 主目录中的 fast-agent.yaml 旁边。使用 --home 或 FAST_AGENT_HOME 选择其他主目录;使用 --workspace 选择其他工作区,此时主目录默认为 <workspace>/.fast-agent。

交互式 Shell

fast-agent

文档

文档站点位于 docs/。如需在本地处理文档:

# Install docs dependencies (first time only)
uv run scripts/docs.py install

# Generate reference docs from source code
uv run scripts/docs.py generate

# Run the dev server (http://127.0.0.1:8000)
uv run scripts/docs.py serve

# Capture and assess screenshots of the built docs
uv run scripts/docs.py screenshot
uv run scripts/docs.py assess

# Or generate and serve in one command
uv run scripts/docs.py all

生成器会直接从源代码中提取配置字段说明、模型别名和 API 参考,以确保文档保持同步。

项目说明

fast-agent 基于 Sarmad Qadri 的 mcp-agent 项目构建。

参与贡献

欢迎贡献代码和提交 PR,也欢迎提出问题进行讨论。完整的贡献指南和路线图即将推出。欢迎联系我们!

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。