跳到主要内容

数据截至 (上游 commit 3a4e2ae3eec0)

AgentScope — 架构与原理

30 秒导读: AgentScope 是阿里通义实验室开源的 Python agent 框架。它给你一个 Agent 类,你塞进模型、工具、系统提示词,它就跑一个能被中途打断、能停下来等你点「同意」、能在上下文快满时自己总结压缩、跑完还能整个存盘的 ReAct 循环;上面再叠一层 FastAPI 服务,把单个 agent 变成多租户、多会话、可组队的应用。

本文档对应的是 AgentScope 2.0.6src/agentscope/_version.py:4)。2.0 是一次推倒重来:1.x 里的 msghubsequential_pipeline 这些编排原语在本 commit 的源码里已经完全不存在(全仓库 grep 为 0 命中),多智能体协作改由 app 层的团队工具 + 消息总线承担。


1. 这是什么(零基础也能懂)

一句话定义

AgentScope 是一个 agent 运行时:你描述「一个 agent 由哪个模型、哪些工具、什么提示词组成」,它负责把「模型说话 → 调工具 → 拿结果 → 再说话」这个循环可靠地跑起来。

解决谁的什么问题

假设你要做一个能改代码的终端助手。你很快会撞上四类问题,而它们都不是「调用模型」本身:

你会撞上的问题具体表现AgentScope 的答复
危险操作模型想跑 rm -rf build,你想先看一眼再放行权限引擎 + 「等你确认」的可挂起状态
上下文爆炸读了三个大文件,token 就满了自动压缩历史 + 超大工具结果卸载到文件
用户中途反悔Ctrl+C 打断,但工具调用悬在半空没有结果中断时给每个悬空调用补一条「已被中断」的结果
跑到一半要存盘进程重启后要从上次的位置继续全部运行时状态收敛进一个 AgentState

给谁用:要把 agent 做成产品的后端工程师。不是给做研究 demo 的人用的——它的重量几乎全压在「工程可靠性」上。

它能做什么

  • 跑 ReAct(推理-行动交替)循环,支持流式输出、结构化输出。
  • 统一接八家模型 API:OpenAI、Anthropic、Gemini、DashScope、DeepSeek、Moonshot、xAI、Ollama。
  • 自带编码工具:BashReadWriteEditGrepGlob,以及任务规划工具 TaskCreate / TaskList 等。
  • 接 MCP 服务器和「技能」(skill,一组 Markdown 指令 + 脚本)。
  • 把工具执行放进沙箱:Docker、Apple Container、Bubblewrap、E2B、Daytona、K8s、OpenSandbox。
  • 一条命令起一个 FastAPI 服务,带多租户、多会话、Web UI、飞书/Discord 接入。

用起来什么样

下面是仓库自带的最小例子(节选自 examples/console/main.py:47-84,真实可跑):

async with LocalWorkspace(workdir=args.workdir) as workspace:
agent = Agent(
name="Friday",
system_prompt="You are a helpful assistant named Friday. ...",
model=DashScopeChatModel(
credential=DashScopeCredential(api_key=api_key),
model=args.model,
stream=True,
),
toolkit=Toolkit(
tools=await workspace.list_tools(), # 文件工具绑到工作区后端
skills_or_loaders=await workspace.list_skills(),
),
offloader=workspace, # 超长内容卸载到工作区
)
await launch_console(agent, verbosity=args.verbosity)

launch_consolesrc/agentscope/console/_console.py:95)把终端里的流式渲染、工具调用确认、Ctrl+C 打断全包了——你写的只有「这个 agent 是谁」。

一句话直觉

Agent.reply() 想成一个「可暂停的函数调用」。

普通函数调用要么返回、要么抛异常。AgentScope 的一次 reply 多了第三种结局:停在半路——因为它要等你点确认、等外部系统回结果。这个「停在半路」不是挂着一个线程,而是把状态写进 AgentState;下次带着确认结果再调一次 reply(),它从断点接着跑。整个框架的绝大多数设计,都是这句话的推论。


2. 顶层全景(它大概怎么转)

2.1 部件图

怎么读这张图:中间是 Agent,左右是它依赖的四大部件;Toolkit 下面挂着安全与执行的两层。

┌───────────────┐
输入 Msg ─────►│ Agent │────► AgentEvent 流 ──► 控制台 / SSE / Web UI
└───────┬───────┘
┌──────────────────┼──────────────────┬────────────────┐
▼ ▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│ AgentState │ │ Toolkit │ │ ChatModel │ │ Middleware │
│ 唯一可存盘 │ │ 工具/MCP/ │ │ + Formatter│ │ 七个挂点 │
│ 的状态包 │ │ 技能 │ │ 八家 API │ │ 洋葱链 │
└────────────┘ └─────┬──────┘ └────────────┘ └────────────┘

┌────────────────────────┐
│ PermissionEngine │ 谁能跑
│ Workspace / Backend │ 在哪跑
└────────────────────────┘

2.2 部件一句话职责

部件干什么在哪个文件
Agent唯一的 agent 类,跑 reply 循环src/agentscope/agent/_agent.py:112
AgentState上下文、摘要、权限上下文、工具缓存、任务表——全部可序列化src/agentscope/state/_state.py:178
Toolkit注册/分组/查找工具,统一成流式调用src/agentscope/tool/_toolkit.py:66
PermissionEngine五种模式 × 三类规则,判 allow / deny / asksrc/agentscope/permission/_engine.py:17
ChatModelBase统一模型接口,自带重试与流式累加src/agentscope/model/_base.py:37
FormatterBaseMsg 翻译成各家 API 的 JSONsrc/agentscope/formatter/_formatter_base.py:22
MiddlewareBase七个挂载点,不改源码改行为src/agentscope/middleware/_base.py:13
WorkspaceBase工具在哪执行(本地/容器/沙箱),兼做卸载存储src/agentscope/workspace/_base.py:223
MessageBusapp 层的活跃传输层:队列 / 回放日志 / 广播src/agentscope/app/message_bus/_base.py:53

2.3 主线走一遍

一次 await agent.reply(msg) 高层上发生这些事(不进代码):

① 收输入 把 Msg 追加进 state.context,开一个新的 reply_id

② 决策 _next_action 读当前状态,返回 Reasoning / Acting / Exit 之一

├─ Reasoning ─► 压缩上下文(若需要)→ 注入运行时状态 → 调模型 → 事件流

├─ Acting ────► 按并发安全性分批 → 逐个查权限 → 执行 → 结果写回上下文

└─ Exit ──────► 发 ReplyEndEvent,产出最终 Msg,退出

③ 回到 ② 每完成一轮「推理+它产生的全部工具调用都有结果」,cur_iter += 1

关键是 ② 是纯读_next_actionsrc/agentscope/agent/_agent.py:3248)的 docstring 明写 "Read-only: all side effects are performed by the caller"。所有写状态的动作都发生在 _reply_impl 里。这条分工是整个框架能做到「随时挂起、随时恢复」的根。

2.4 一次 reply 的三种结局

结局触发条件外部看到什么
完成模型给了纯文本回答,或结构化输出已生成ReplyEndEvent(finished_reason=COMPLETED) + 最终 Msg
挂起有工具调用在等用户确认 / 等外部执行没有 ReplyEndEvent,只吐一条「我在等你」的 Msg
中断Ctrl+C、上游取消、显式 UserInterruptEvent悬空调用被补上 INTERRUPTED 结果,再发 ReplyEndEvent(INTERRUPTED)

「挂起」这一列是理解 AgentScope 的分水岭,第 1 章会把它拆开讲。


3. 阅读地图

建议按顺序读;每章都能独立跳源码。

章节讲什么什么时候读它
01-reply-loop.mdreply 状态机、事件协议、中断与 HITL 恢复想搞懂 agent 循环怎么写才不乱——先读这章
02-tool-and-permission.mdToolkit、工具组、权限五模式、Bash 静态分析你要做「能改文件/跑命令」的 agent
03-context-engineering.md压缩、卸载、运行时状态注入你的 agent 老是把上下文撑爆
04-middleware-model-formatter.md中间件洋葱、模型降级、多家 API 适配你要接新模型或插自定义逻辑
05-multi-agent-service.md消息总线、inbox 交接、团队工具、沙箱你要做多 agent 协作或线上服务
06-essence-and-boundaries.md精华、坑、横向对比、总代码地图读完想带走什么 / 想知道它哪里会崩

4. 代码地图(入口级)

主题文件路径符号名
agent 主类与循环src/agentscope/agent/_agent.pyAgent_reply_impl_next_action
四个配置类src/agentscope/agent/_config.pyContextConfigInjectionConfigReActConfigModelConfig
三种「下一步动作」src/agentscope/agent/_utils.pyReasoningActingExit
可存盘状态src/agentscope/state/_state.pyAgentStateReplyContextToolContext
消息与内容块src/agentscope/message/_block.pyToolCallBlockToolCallStateHintBlock
事件协议src/agentscope/event/_event.pyEventTypeReplyEndEventRequireUserConfirmEvent
工具管理src/agentscope/tool/_toolkit.pyToolkitcall_toolcheck_tool_available
权限引擎src/agentscope/permission/_engine.pyPermissionEnginecheck_permission
模型基类src/agentscope/model/_base.pyChatModelBasecount_tokens
中间件基类src/agentscope/middleware/_base.pyMiddlewareBase
应用层入口src/agentscope/app/_service/_chat.pyChatServicerun
最小可跑例子examples/console/main.pymain