本文基于 AgentScope 2.0.4 源码,示例项目为 myagent(AgentScope + FastAPI + Postgres + WebSocket)。
Agent 不是更高级的 API 调用,而是完全不同的编程范式 – 从"你写代码调用 LLM"到"Agent 自主推理、行动、回流"。

如果你有 Web 开发经验,你可能已经熟悉调用 LLM API 的方式:构造 prompt、发起请求、处理返回。这和调用任何 REST API 没有本质区别 – 你是编排者,API 是工具。

Agent 彻底改变了这个关系。你不再决定调用几次、怎么处理返回值、何时结束。Agent 自己决定。

这个转变有多大?想象一下从"手写 SQL 查询"到"ORM 自动生成查询"的距离 – Agent 范式跃迁比这个还要大一个量级,因为它连"要不要查询"这个决策都交出去了。

学什么

  • 理解 Agent vs Chatbot vs Assistant 的本质区别
  • 掌握 AgentScope Agent 类的 10 个参数和 4 组分类
  • 理解 reply() 是 reply_stream() 的消费者这个核心认知
  • 了解 Agent 从装配到持久化的完整生命周期

前置依赖

  • 了解 Python 基本语法和 async/await 概念
  • 有 Web 开发经验(REST API、HTTP request-response 模型)
  • 无需 AgentScope 经验

一、两种范式的根本差异

先看两段代码。它们解决同一个问题:“北京天气如何?”

你熟悉的方式 – 传统 LLM API 调用:

import openai

response = openai.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "北京天气如何?"}]
)
print(response.choices[0].message.content)
# "我无法获取实时天气数据,建议您查看天气预报应用。"

这段代码你完全可控:你决定调用几次、你处理返回值、你决定何时结束。LLM 只是一个"接收 prompt、返回 text"的函数。

AgentScope 的方式 – Agent 自主行动:

from agentscope.message import Msg, TextBlock

input_msg = Msg(
    content=[TextBlock(text="北京天气如何?")],
    role="user",
)

async for event in agent.reply_stream(inputs=input_msg):
    await ws.send_json(event)
    # Agent 自己决定:
    #   1. 是否需要调用 get_weather 工具
    #   2. 调用后怎么用结果
    #   3. 何时生成最终回复
    #   4. 何时结束

看到区别了吗?第二段代码里没有 if、没有 for、没有任何流程控制逻辑。你只做了一件事:把用户消息喂给 Agent,然后循环接收它产出的事件。

三个根本差异

┌──────────────────┬─────────────────────────┬──────────────────────────────┐
│                  │ 传统 LLM API 调用        │ Agent 范式                   │
├──────────────────┼─────────────────────────┼──────────────────────────────┤
│ 控制权           │ 你 -- 你写 if-else 编排  │ Agent -- 它自己 ReAct 循环   │
│ 返回方式         │ 同步一个 JSON            │ 流式多个事件                 │
│ 编排逻辑         │ 你的代码里              │ Agent 内部的 ReAct 循环      │
└──────────────────┴─────────────────────────┴──────────────────────────────┘

控制权转移是核心。传统模式下,你是编排者 – 你决定先调 API、再处理结果、再决定是否调第二次。Agent 模式下,Agent 是编排者 – 它自己决定调哪个工具、调几次、何时结束。你的角色从"编排者"变成了"配置者":你配置 Agent 能用什么工具、最多循环几次、什么行为是被允许的,然后放手让它自己跑。

如果你有 Java 背景,这类似于从"手写 Servlet"到"Spring Boot 自动装配"的跃迁 – 但比那更大,因为 Spring Boot 不会自己决定要不要调 weatherService.getWeather()

二、Agent vs Chatbot vs Assistant

很多人把"带工具的 ChatGPT"等同于 Agent。这是最常见的认知误区。让我们厘清三个层次。

三层区分

┌────────────┬──────────┬──────────────┬──────────────┐
│            │ Chatbot  │ Assistant    │ Agent        │
├────────────┼──────────┼──────────────┼──────────────┤
│ 对话能力   │ ✅       │ ✅           │ ✅           │
│ 工具使用   │ ❌       │ ✅ 人类编排  │ ✅ 自主编排  │
│ 自主决策   │ ❌       │ ❌           │ ✅           │
│ 自主终止   │ ❌       │ ❌           │ ✅           │
├────────────┼──────────┼──────────────┼──────────────┤
│ 谁选工具   │ -        │ 人类         │ Agent        │
│ 谁定何时停 │ 人类     │ 人类         │ Agent        │
│ 谁编排流程 │ 人类     │ 人类         │ Agent        │
├────────────┼──────────┼──────────────┼──────────────┤
│ 例子       │ 早期     │ ChatGPT +   │ AgentScope   │
│            │ ChatGPT  │ plugins     │ Agent        │
└────────────┴──────────┴──────────────┴──────────────┘

关键区别是谁做编排决策

  • Chatbot:纯对话,无工具。你说一句它回一句。你决定何时结束对话。
  • Assistant:有工具,但由人类编排。ChatGPT 的 plugins 模式就是 Assistant – 用户手动选择"用天气插件查天气",系统执行,用户再决定下一步。
  • Agent:有工具,且自主编排。AgentScope 的 Agent 自己决定"这个问题需要调用天气工具",调用后自己决定"现在可以组织回复了",然后自己决定"回复完成,结束本次循环"。

为什么 ChatGPT + 插件不是 Agent

ChatGPT 的 plugins 模式看起来像 Agent – 它能调用工具。但关键在于:用户选插件、用户决定何时结束。编排权在人类手里。

AgentScope Agent 则不同:你给它一个 toolkit(工具箱),它自己从中选择合适的工具;你给它一个 react_config.max_iters(最大循环次数),它自己在循环内决定何时退出。编排权在 Agent 手里。

Agent 的 4 个特征

一个真正的 Agent 具备 4 个特征:

  1. 自主性(Autonomy):不需要人类逐步指挥,自己决定行动序列
  2. 推理(Reasoning):通过 LLM 进行思考,决定下一步做什么
  3. 行动(Acting):执行工具调用,获取外部信息
  4. 工具(Tools):通过工具扩展能力边界(查天气、算数学、读写文件…)

这 4 个特征缺一不可。只有推理没有工具的是 Chatbot。有工具但不自主编排的是 Assistant。四者齐备的才是 Agent。

三、Agent 的一次思考过程

在深入代码之前,让我们先建立"Agent 怎么思考"的画面。以下是一次完整 reply 的过程 – 用户问"北京天气如何?",Agent 内部发生了什么:

用户输入: "北京天气如何?"

┌─ Agent 内部(一次 reply 的完整过程)──────────────────────────────┐
│                                                                │
│  ReplyStartEvent  ← 回复开始                                    │
│       │                                                        │
│       ▼                                                        │
│  ┌─ Reasoning 1 ──────────────────────────────────────────┐   │
│  │  LLM 推理:                                              │   │
│  │  "用户问天气,我需要调用 get_weather 工具"                │   │
│  │  产出: ToolCallStartEvent(tool_name="get_weather")     │   │
│  └────────────────────────────────────────────────────────┘   │
│       │                                                        │
│       ▼                                                        │
│  ┌─ Acting 1 ────────────────────────────────────────────┐   │
│  │  执行工具: get_weather(city="北京")                    │   │
│  │  结果: "晴, 25°C"                                       │   │
│  │  产出: ToolResultEndEvent(result="晴, 25°C")           │   │
│  └────────────────────────────────────────────────────────┘   │
│       │                                                        │
│       ▼                                                        │
│  ┌─ Reasoning 2 ──────────────────────────────────────────┐   │
│  │  LLM 推理:                                              │   │
│  │  "拿到天气数据了,组织回复"                               │   │
│  │  产出: TextBlockDeltaEvent("北京今天晴,")              │   │
│  │         TextBlockDeltaEvent("气温 25°C")               │   │
│  └────────────────────────────────────────────────────────┘   │
│       │                                                        │
│       ▼                                                        │
│  ReplyEndEvent    ← 回复结束                                   │
│       (无更多工具调用,Agent 自主终止)                          │
│                                                                │
└────────────────────────────────────────────────────────────────┘

4 个关键洞察

  1. 一次用户输入 = 多轮 Reasoning-Acting 循环。用户只说了一句话,Agent 内部可能跑了好几轮"推理 -> 行动 -> 再推理"。

  2. Agent 自己决定调几次工具。在这个例子里它调了 1 次。如果用户问"对比北京和上海的天气",Agent 可能调 2 次 get_weather,然后自己做对比。

  3. Agent 自己决定何时结束。当 LLM 在某轮推理中没有产出工具调用时,Agent 知道"不需要更多工具了",于是生成最终回复并结束。这个"无工具调用即终止"的逻辑是内建的。

  4. 每一步都产出事件。Agent 不是等全部跑完才返回一个结果,而是每做一步都"喊一声" – 开始推理了、要调工具了、工具返回了、在生成文本了… 这些"喊声"就是事件(Event),前端可以逐个接收、逐个渲染。

如果你有 Web 开发经验,可以把这个模型类比为一个"会自己写 while 循环的 request handler" – 它接收一个请求后,内部自己决定要查几次数据库、调几次外部 API、何时返回响应,而你只需要在旁边"听"它每一步在干什么。

四、AgentScope 的 Agent 类

现在让我们打开 AgentScope 源码,看看 Agent 到底是什么。

源码位置:agentscope/agent/_agent.py:100(AgentScope 2.0.4)

Agent.init 的 10 个参数

class Agent:
    def __init__(
        self,
        name: str,                    # 1. Agent 名称
        system_prompt: str,           # 2. 系统提示词
        model: ChatModelBase,         # 3. LLM 模型
        toolkit: Toolkit | None,      # 4. 工具箱
        middlewares: list[...] | None,# 5. 中间件链
        state: AgentState | None,     # 6. 运行时状态
        offloader: Offloader | None,   # 7. workspace 引用
        model_config: ModelConfig,     # 8. fallback 配置
        context_config: ContextConfig,# 9. 上下文压缩
        react_config: ReActConfig,    # 10. ReAct 循环配置
    ) -> None:

这 10 个参数可以分成 4 组:

┌─ 核心身份 ──────────────────────────────────────────────┐
│  name              Agent 名称 (如 "myagent-assistant")  │
│  system_prompt     系统提示词 (定义 Agent 行为)          │
└────────────────────────────────────────────────────────┘
┌─ 核心能力 ──────────────────────────────────────────────┐
│  model             LLM 模型 (ChatModelBase)              │
│  toolkit           工具箱 (Toolkit: tools + MCP + ...)   │
│  middlewares       中间件链 (3 条: reply/reasoning/acting)│
└────────────────────────────────────────────────────────┘
┌─ 运行时状态 ────────────────────────────────────────────┐
│  state             AgentState (context/reply_id/cur_iter)│
│  offloader         workspace 引用 (文件操作 + offload)   │
└────────────────────────────────────────────────────────┘
┌─ 行为配置 ──────────────────────────────────────────────┐
│  model_config      fallback model + retries             │
│  context_config    上下文压缩 (trigger_ratio/reserve)    │
│  react_config      ReAct 循环 (max_iters/stop_on_reject)│
└────────────────────────────────────────────────────────┘

与 Spring Bean 的对应

如果你有 Spring 经验,这个结构会更容易理解:

Agent 参数 Spring 对应 说明
name + system_prompt bean id + @Component 标识和行为定义
model + toolkit @Autowired service + dao 核心能力注入
middlewares @Around interceptor 行为拦截
state session-scoped bean 运行时状态
react_config @Value properties 行为参数配置

必填 vs 可选

在这 10 个参数中,只有 namesystem_promptmodel 是必填的。其余 7 个都有默认值:

  • toolkit 默认空工具箱(Agent 可以对话但不能用工具)
  • middlewares 默认空列表
  • state 默认创建新的 AgentState()
  • 三个 config 都有各自的默认值

这意味着你可以在最简单的情况下只传 3 个参数就创建一个 Agent。但一个"能干活"的 Agent 通常需要 toolkit(有工具可用)和 react_config(配置循环行为)。

中间件的三条核心链

middlewares 参数接收一个列表,但 Agent 内部会按实现的 hook 分成三条独立链:

# 源码: _agent.py:170-179
self._reply_middlewares = [_ for _ in middlewares if _.is_implemented("on_reply")]
self._reasoning_middlewares = [_ for _ in middlewares if _.is_implemented("on_reasoning")]
self._acting_middlewares = [_ for _ in middlewares if _.is_implemented("on_acting")]

这意味着一个中间件可以只实现某个 hook(比如只拦截工具调用),而不影响其他链。Agent 共有 6 条中间件链(reply / reasoning / acting / model_call / system_prompt / compress_context),这里聚焦 3 条核心链。这比传统 HTTP middleware 多一维 – HTTP middleware 只有一条链(请求 -> 响应),Agent 有多条(reply -> reasoning -> acting)。关于中间件的深入解析,我们留到系列文章 11 。

五、reply() vs reply_stream()

这是全文最重要的认知转折。理解了这一点,你就理解了 Agent 的本质。

reply_stream() 是 Agent 的核心

# 源码: _agent.py:194
async def reply_stream(
    self,
    inputs: Msg | list[Msg] | ... | None = None,
) -> AsyncGenerator[AgentEvent, None]:
    """Reply to the given inputs and stream agent events."""
    async for chunk in self._reply(inputs=inputs):
        if not isinstance(chunk, Msg):
            yield chunk

reply_stream() 是一个 async generator – 它不返回一个值,而是 yield 一连串事件。每产出一步(开始推理、调工具、生成文本…)就 yield 一个事件给调用方。

这和传统 API 调用形成鲜明对比:

┌─ 传统 API: 同步返回 ─────────────────────────┐
│                                             │
│  response = api.call(prompt)                │
│  # 调用方等待全部完成                        │
│  # 然后一次性拿到完整结果                     │
│  print(response)                            │
│                                             │
└─────────────────────────────────────────────┘

┌─ Agent: 流式产出 ───────────────────────────┐
│                                             │
│  async for event in agent.reply_stream():   │
│      # Agent 每做一步就 yield 一个事件       │
│      # 调用方可以逐个接收、逐个渲染           │
│      await render(event)                    │
│                                             │
└─────────────────────────────────────────────┘

reply() 是 reply_stream() 的消费者

# 源码: _agent.py:225
async def reply(
    self,
    inputs: Msg | list[Msg] | ... | None = None,
) -> Msg:
    """Reply to the given inputs, consuming all streamed events."""
    final_msg: Msg | None = None
    async for evt_or_msg in self._reply(inputs=inputs):
        if isinstance(evt_or_msg, Msg):
            final_msg = evt_or_msg
    if final_msg is None:
        raise RuntimeError("Agent did not produce a final message.")
    return final_msg

看到了吗?reply() 内部就是一个 async for 循环 – 它消费 reply_stream() 产出的所有事件,等循环结束后返回最终的 Msg

换句话说:同步是流式的语法糖。

┌─ reply_stream (流式) ──────────────────────────────┐
│                                                    │
│  yield ReplyStartEvent                             │
│  yield ModelCallStartEvent                         │
│  yield TextBlockDeltaEvent("北京")                 │
│  yield TextBlockDeltaEvent("今天晴")               │
│  yield ToolCallStartEvent                          │
│  yield ToolResultEndEvent                          │
│  yield TextBlockDeltaEvent("气温25°C")            │
│  yield ReplyEndEvent                               │
│                                                    │
│  -> 前端可以逐事件渲染(打字机效果)                 │
└────────────────────────────────────────────────────┘

┌─ reply (同步) ─────────────────────────────────────┐
│                                                    │
│  (内部消费完所有事件)                               │
│  return Msg(content="北京今天晴,气温25°C")         │
│                                                    │
│  -> 前端等全部完成才看到结果                         │
└────────────────────────────────────────────────────┘

核心等式:
  reply() = 消费 reply_stream() 的所有事件后返回最终 Msg

为什么 Agent 天生是流式的

这不是设计偏好,而是底层约束决定的:LLM 的生成过程本身就是流式的。当你调用 openai.chat.completions.create(stream=True) 时,API 会逐 token 返回。Agent 内部的 Reasoning 阶段就是调用 LLM,自然就是流式的。再加上工具调用有"开始 -> 执行 -> 结束"的生命周期,整个 reply 过程天然是一个事件流。

reply() 只是为了方便调用方在不需要流式处理的场景下使用 – 比如后台任务、测试用例。但 Agent 的"原生语言"是 reply_stream(),是事件流。

六、Agent 的生命周期

Agent 不是凭空出现的。它有一个从"装配"到"运行"到"持久化"的完整生命周期。理解这个生命周期,就理解了 Agent 在系统中的位置。

与 HTTP Request 生命周期对比

HTTP Request 生命周期              Agent 生命周期
──────────────────                ──────────────────
TCP accept                        ChatService._run_impl() Step 1:
                                   加载 agent_record + session_record
     │                                  │
HTTP parse                        Step 2: get_workspace
                                   (解析 workspace + workdir)
     │                                  │
路由匹配                           Step 3: 组装 middlewares
                                   (Inbox + StateChange + ToolOffload + extra)
     │                                  │
handler 执行                      Step 4-5: get_toolkit + get_model
     │                                  │
┌─业务逻辑──┐                    Step 6: Guard
│           │                     (parked-on-HITL 检查)
└───────────┘                          │
                                   Step 7: acquire_lock +
     │                                agent.reply_stream (事件发布)
response send                           │
                                   Step 8: shield(persist)
     │                                (reply_msg + state 写回 storage)
TCP close                              │
                                   lock release + log_trim

关键差异:有状态 vs 无状态

HTTP request 是无状态的 – 请求结束,所有上下文销毁。下一个请求从头开始。

Agent 是有状态的 – AgentState 持有 context(对话历史)、reply_id(当前回复 ID)、cur_iter(当前循环轮次)。每次 reply 结束后,这些状态被持久化到 Storage。下次用户继续对话时,Agent 会加载之前的 state,“接着上次继续”。

这就是为什么 ChatService 的 Step 8 如此重要 – 它用 asyncio.shield() 保护持久化操作,确保即使整个 chat run 被取消,状态也会写回 storage。否则下次用户对话时,Agent 会"失忆"。

分布式锁

Step 7 中有一个 acquire_lock – 这是分布式锁,保证同一个 session 同时只能有一个 chat run 在跑。为什么需要这个?因为 AgentState 是共享的 – 如果两个请求同时修改同一个 session 的 state,会导致数据损坏。

这和 Web 开发中的 @Transactional 类似 – 但粒度不同。@Transactional 保护的是数据库行,acquire_lock 保护的是整个 session 的执行权。关于并发与调度的深入解析,留到系列文章 19。

七、myagent 实战

让我们看看真实的 Agent 应用长什么样。以下代码来自 myagent 项目,一个基于 AgentScope 构建的 Agent 服务。

server.py: Agent 如何被组装进服务

# 源码: src/myagent/server.py (gitee: bytesifter/myagent)
from agentscope.app import create_app
from agentscope.app.message_bus import InMemoryMessageBus
from agentscope.app.workspace_manager import LocalWorkspaceManager

app = create_app(
    storage=storage,                              # PostgreSQL 存储
    message_bus=InMemoryMessageBus(),            # 内存消息总线
    workspace_manager=LocalWorkspaceManager(      # 本地文件工作空间
        basedir=WORKSPACE_DIR,
    ),
    extra_agent_tools=create_tools,               # 注入自定义工具
    custom_subagent_templates=[_translator],     # 注入子 Agent 模板
    title="myagent API",
)

app.include_router(ws_router)   # 挂载 WebSocket 路由

create_app() 一步完成所有装配 – 框架内置的 9 个 router 自动注册,lifespan 启动链自动初始化 storage / message_bus / workspace / dispatcher 等组件。你只需要传入三个核心依赖(storage / message_bus / workspace_manager)和你的自定义扩展(extra_agent_tools / custom_subagent_templates)。

extra_agent_tools=create_tools 是关键注入点 – create_tools 是一个工厂函数,签名是 (user_id, agent_id, session_id) -> list[ToolBase]。框架在每次 chat run 时调用它,产出当前用户可用的工具列表。myagent 在这里注入了天气查询工具和计算器工具。

ws.py: 从 WS 请求到事件回推的完整链路

# 源码: src/myagent/routers/ws.py (gitee: bytesifter/myagent)

# 1. 用户通过 WebSocket 发送 chat 消息
input_msg = Msg(
    content=[TextBlock(text=content)],
    role="user",
)

# 2. fire-and-forget 启动 chat run
chat_task = chat_run_registry.spawn(
    chat_service.run(
        user_id=user_id,
        session_id=session_id,
        agent_id=_AGENT_ID,
        input_msg=input_msg,
    ),
    session_id=session_id,
)

# 3. feeder 订阅事件流,转发给前端
async for evt in message_bus.subscribe(
    MessageBusKeys.session_events(session_id),
):
    mapped = _map_event(evt, session_id)
    if mapped is not None:
        await ws.send_json(mapped)

# 4. watchdog 监控 chat run 崩溃
await chat_task  # 崩溃时发送 error 事件给前端

这四步构成了一个完整的闭环:

  1. 输入:用户通过 WS 发送文本消息,被包装成 Msg 对象
  2. 触发chat_run_registry.spawn() 异步启动 chat run,立即返回不阻塞
  3. 回流_feeder 订阅 MessageBus 的事件流,把 AgentScope 的 28 种事件映射成 6 种前端消息
  4. 兜底_watchdog 监控 chat run 是否崩溃,崩溃时发送 error 事件

注意 Step 2 和 Step 3 是解耦的 – spawn() 不等 chat run 完成,_feeder 不直接调用 reply_stream()。它们通过 MessageBus 间接通信:chat run 往 MessageBus 发布事件,feeder 从 MessageBus 订阅事件。这种解耦让 chat run 和前端推送可以独立运行,互不阻塞。

这就是 Agent 应用的典型架构:HTTP/WS 触发 + MessageBus 解耦 + 事件流推送。和传统的"HTTP 请求 -> 同步处理 -> HTTP 响应"模式完全不同。

八、与 LangChain 对比

维度 AgentScope LangChain
定位 全栈框架(App + Service + Bus + Storage) 组装库(Agent + Chain + Tool)
Web 服务 内置 create_app() 开箱即用 无(自己起 FastAPI)
事件系统 28 种事件类型,原生流式 callback chain
中间件 多条链(核心 3 条:reply / reasoning / acting) runnable chain
持久化 内置 StorageBase(Postgres/Redis) 无(自己接)
消息总线 内置 MessageBus(5 种原语)
Session 管理 内置 + 分布式锁 Memory 类(简单)
子 Agent SubAgentTemplate sub-chain
设计哲学 约定优于配置 组合优于继承
类比 Spring Boot Spring Framework

AgentScope 和 LangChain 不是竞争关系,而是不同层次的选择。LangChain 适合需要灵活拼装各种组件的场景 – 你可以像搭积木一样组合 Chain、Tool、Memory。AgentScope 适合需要完整 Agent 服务的场景 – 你传入 storage 和 message_bus,框架帮你处理路由、Session 管理、并发控制、事件分发等一切基础设施。

选型建议:如果你的项目核心是"对话 + 工具调用"且需要生产级可靠性(Session 持久化、并发控制、事件流),AgentScope 开箱即用。如果你需要深度定制每个环节,LangChain 给你更多自由度。

九、常见认知陷阱

陷阱 1: “Agent 就是带工具的 ChatGPT”

事实:ChatGPT + 插件是 Assistant,不是 Agent。区别在于编排权:ChatGPT 的插件由人类选择和触发,Agent 的工具由 Agent 自己选择和触发。"有工具"只是 Agent 的必要条件,"自主编排"才是充分条件。

陷阱 2: “Agent = prompt engineering”

事实:Agent 是工程系统,prompt 只是其中一环。一个生产级 Agent 应用还需要:工具设计(FunctionTool 的 schema 和 description 直接影响 LLM 的调用准确率)、中间件(日志、权限、观测)、持久化(Session 状态、消息历史)、并发控制(分布式锁、ChatRunRegistry)、可观测性(request_id 链路追踪)。写好 system_prompt 只是起点,不是终点。

陷阱 3: “Agent 不需要后端工程”

事实:Agent 比传统 Web 应用更需要后端工程。传统 Web 的后端是 CRUD + 业务逻辑,Agent 的后端还要跑 ReAct 循环、管理 Session 上下文、处理工具调用并发、持久化对话状态、保证分布式串行化。你以为 Agent 框架帮你搞定了一切?它确实帮你搞定了框架层,但你的业务工具、权限策略、可观测性埋点、部署配置 – 这些都需要你自己来。

陷阱 4: “Agent 是自主的,所以它可以做任何事”

事实:Agent 的自主是在配置约束内的自主。

┌─ 约束 ──────────────────────────────────────┐
│  max_iters          限制循环次数             │
│  toolkit            限制可用工具             │
│  permission_context 限制工具权限             │
│  system_prompt      引导行为方向             │
│  react_config       控制循环行为             │
└─────────────────────────────────────────────┘

Agent 在这些约束内自主决策:

  • 调哪个工具? Agent 自己决定(从 toolkit 里选)
  • 调几次? Agent 自己决定(但不超过 max_iters)
  • 何时结束? Agent 自己决定(无工具调用时终止)
  • 工具被拒? Agent 自己决定(根据 stop_on_reject 配置)

“自主"不等于"无限制”。你是配置者,Agent 是执行者 – 它在你画的圈子里自主行动,但圈子的边界由你定义。

总结

回到开头的问题:Agent 到底是什么?

Agent 不是更高级的 API 调用,不是带工具的 ChatGPT,不是 prompt engineering 的升级版。Agent 是一种编程范式 – 和面向对象编程、函数式编程同级别的范式。

在传统编程中,你写代码控制流程。在 Agent 编程中,你配置约束,Agent 自主控制流程。你的角色从"流程控制者"变成了"约束设计者"。

这个转变不容易 – 它要求你放下"我要控制每一步"的执念,学会信任 Agent 在约束内做出合理决策。但一旦完成这个认知跃迁,你会发现 Agent 能解决的问题空间远比传统 API 调用大得多 – 那些你曾经用 200 行 if-else 写的编排逻辑,Agent 可能用 3 轮 ReAct 循环就搞定了。

本系列后续文章将深入 AgentScope 的每个组件,从 asyncio 基础到 ReAct 循环源码,从 MessageBus 消息总线到 WebSocket 集成实战。但请记住这篇文章的核心认知:Agent 是范式,不是工具。

延伸思考

  • 如果 Agent 的 max_iters=1,它还是 Agent 还是 Assistant?
  • reply_stream() 的 async generator 模式和 SSE 有什么本质区别?
  • 如果你想给 Agent 加一个"记忆"功能,应该改哪个参数?
Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐