Agent 认知基础 -- 从 API 调用到 Agent 自主
本文基于 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 个特征:
- 自主性(Autonomy):不需要人类逐步指挥,自己决定行动序列
- 推理(Reasoning):通过 LLM 进行思考,决定下一步做什么
- 行动(Acting):执行工具调用,获取外部信息
- 工具(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 个关键洞察
-
一次用户输入 = 多轮 Reasoning-Acting 循环。用户只说了一句话,Agent 内部可能跑了好几轮"推理 -> 行动 -> 再推理"。
-
Agent 自己决定调几次工具。在这个例子里它调了 1 次。如果用户问"对比北京和上海的天气",Agent 可能调 2 次
get_weather,然后自己做对比。 -
Agent 自己决定何时结束。当 LLM 在某轮推理中没有产出工具调用时,Agent 知道"不需要更多工具了",于是生成最终回复并结束。这个"无工具调用即终止"的逻辑是内建的。
-
每一步都产出事件。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 个参数中,只有 name、system_prompt、model 是必填的。其余 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 事件给前端
这四步构成了一个完整的闭环:
- 输入:用户通过 WS 发送文本消息,被包装成
Msg对象 - 触发:
chat_run_registry.spawn()异步启动 chat run,立即返回不阻塞 - 回流:
_feeder订阅 MessageBus 的事件流,把 AgentScope 的 28 种事件映射成 6 种前端消息 - 兜底:
_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 加一个"记忆"功能,应该改哪个参数?
更多推荐



所有评论(0)