AgentScope (Alibaba)

一句话定位

AgentScope 2.x 是阿里 Tongyi Lab(GitHub org agentscope-ai,仓库 28k★)维护的开源 Agent 框架,采用**“库 + 运行时服务”两层结构:库层 src/agentscope/ 提供一个统一的 Agent 类(异步事件流 ReAct 循环)、Toolkit、独立 PermissionEngine、中间件系统、 Workspace 沙箱与 ChatModelBase/Formatter 模型抽象;服务层 src/agentscope/app/ 把库层 agent 包成 FastAPI 多用户运行时,带 session 持久化、credential 管理与多 agent 团队 编排。README(README.md:70)自陈的设计哲学是”给 agent 透明、可控的能力,而不是用严格 prompt + opinionated orchestration 去束缚它”。这一框架同时是同厂QwenPaw**(Agent OS 应用层)的底座——写本页时区分两者:AgentScope = 引擎,QwenPaw = 长在引擎之上的操作系统层。

核心架构总览(目录结构关键路径 + 引用的 commit)

  • repo(已验证)https://github.com/agentscope-ai/agentscope
  • commit1aeb03d004292799091d437aaaf9486b424ecfa6(2026-07-10 15:39:07 +0800, message feat(service): support to share resource (credential, agent, knowledge bases) across different users as a group or org (#1998)
  • 版本2.0.4src/agentscope/_version.py)。注意 QwenPaw 固定依赖 agentscope==2.0.2, 本次克隆是其更新版;核心 API(Agent/ReActConfig/Msg+AgentState/Toolkit/ PermissionMode/ChatModelBase/中间件钩子)与 QwenPaw 引用一一对上。
  • 克隆git clone --depth 1 ...,2026-07-11 拉取分析。
  • 仓库内无独立 ARCHITECTURE 设计文档(docs/ 仅 NEWS/changelog/roadmap),架构信息全部 从源码读出。

两层的关键路径:

src/agentscope/                     ← 库层(QwenPaw 的底座)
├── agent/_agent.py      (2836 行)   统一 Agent 类:ReAct 循环 / 工具 dispatch / prompt 组装 / 权限落地 / 压缩
├── agent/_config.py                 ReActConfig / ContextConfig / SummarySchema / ModelConfig
├── tool/_toolkit.py                 Toolkit:工具/MCP/skill 注册与 dispatch 中枢
├── tool/_base.py                    ToolBase 契约 + 每工具权限 hook(check_permissions/match_rule)
├── tool/_builtin/*                  bash/read/write/edit/grep/glob/meta(ResetTools)/skill(SkillViewer)
├── tool/_task/*                     单 agent 内 TODO 式任务工具
├── permission/_engine.py,_types.py  PermissionEngine + 5 种 PermissionMode
├── state/_state.py                  AgentState(全量可序列化)+ ToolContext(读缓存) + TaskContext
├── middleware/_base.py              MiddlewareBase 5 钩子
├── middleware/_longterm_memory/     AgenticMemory / mem0 / reme 三后端长期记忆
├── middleware/_tracing/*            OpenTelemetry GenAI semconv
├── workspace/_base.py + 后端        Local/Docker/E2B/K8s/OpenSandbox 沙箱,同时充当 offloader
├── model/_base.py + formatter/*     ChatModelBase(~10 家 provider)+ 每家一个 Formatter
└── app/                             ← 服务层:FastAPI 运行时
    ├── _tool/_agent_create.py       团队工具 TeamCreate/AgentCreate/AgentInvite/TeamSay
    ├── _types.py (SubAgentTemplate) 子 agent 模板
    ├── message_bus/*                InMemory / Redis 消息总线
    ├── _router/*                    session / credential REST 路由
    └── storage/*                    Redis session 持久化

Agent Loop(主循环 / 何时继续何时停)

统一 Agent 类(agent/_agent.py:97,2836 行),非继承式(旧版拆 AgentBase/ReActAgent, 本版收敛为单一 Agent)。构造签名含 name/system_prompt/model/toolkit/middlewares/state/ offloader/model_config/context_config/react_config。全异步、事件流架构。

  • 入口reply()(消费所有事件返回最终 Msg)/ reply_stream()(流式 yield AgentEvent), 两者都进 _reply_reply_impl_agent.py:664)。
  • 循环体_reply_impl:745):while self.state.cur_iter < self.react_config.max_itersmax_iters 默认 20(agent/_config.py:126)。每轮先 _check_next_action() 决定 reasoning / acting / exit
  • 停机判定_check_next_action:2542,真值表):
    • 有可执行 tool call(state PENDING/ALLOWED)→ acting
    • 无可执行但有 awaiting tool call(ASKING 等用户确认 / SUBMITTED 等外部执行)→ exit, 把控制权交回外部,等 HITL 事件再续;
    • 都没有 → reasoning(再问模型);
    • reasoning 生成纯文本且无 tool call → yield 一个 MsgReplyEndReason.COMPLETED,退出。
  • 额外退出路径:超 max_itersExceedMaxItersEvent + ReplyEndReason.EXCEED_MAX_ITERS(约 :861-879);asyncio.CancelledErrorReplyEndReason.INTERRUPTED
  • 一等 HITL / 可恢复reply()inputs 可以是 Msg,也可以是 UserConfirmResultEvent/UserInterruptEvent/ExternalExecutionResultEvent——循环可被 park 后用外部事件续跑,把”人类确认 / 外部工具执行”做进 loop 状态机。
  • 工具并发_batch_tool_calls(约 :1336)按 tool.is_concurrency_safe 分成 sequential / concurrent 两批,分别走 _execute_sequential_tool_calls / _execute_concurrent_tool_calls (asyncio 并发)。

记忆与上下文管理(压缩、长期记忆、会话持久化)

三层结构:

  1. 短期上下文 = AgentState.context: list[Msg]state/_state.py:159)+ AgentState.summary (压缩摘要,:156)。_prepare_model_inputagent/_agent.py:2280)拼装顺序:SystemMsg → (若有)summary 包成 UserMsgcontext
  2. 原生上下文压缩compress_context()_agent.py:271)在每次 reasoning 前调用。
    • 触发阈值:估算 token(model.count_tokens)超过 trigger_ratio(默认 0.8, _config.py:57) * model.context_size 才压。
    • 方式:结构化 LLM 摘要——SummarySchema_config.py:9)5 字段(task_overview / current_state / important_discoveries / next_steps / context_to_preserve),用 continuation-summary prompt 引导模型输出,套 summary_template 拼进 state.summary, 保留尾部 reserve_ratio(默认 0.1, _config.py:62) 的原始 context。等价 Claude-Code 的 /compact。
    • on_compress_context 中间件钩子——QwenPaw 的 “Scroll strategy” 就是在此层定制。
  3. 工具结果压缩/卸载:单个 tool result 超 tool_result_limit(默认 50000 tokens, _config.py:112)就截断并插 <<<TRUNCATED>>> reminder;若配了 offloader,把截断部分落盘并在 reminder 给出文件路径供 agent 按需再读。
  4. 文件读缓存ToolContext.read_file_cachestate/_state.py,LRU、按 mtime 失效)。

长期记忆(可选中间件,见”自进化”章):AgenticMemory / mem0 / reme 三后端。

会话持久化AgentState 是 pydantic 模型,可整体序列化(含 context/summary/tool_context/ tasks_context/permission_context/middle_context,state/_state.py:149);服务层 app/storage/_redis_storage.py + session router 做 per-session 落盘与恢复。

工具体系(定义/调用协议/注册/权限)

Toolkittool/_toolkit.py:66)是唯一的工具/MCP/skill 注册与 dispatch 中枢。

  • 定义契约ToolBasetool/_base.py:94),类属性 name/description/input_schema(JSON Schema)/is_concurrency_safe/is_read_only/is_external_tool/is_state_injected/is_mcp, 实现 call() 返回 ToolChunkAsyncGenerator[ToolChunk](流式统一)。Python 函数可从 docstring + Pydantic 自动生成 schema。
  • 调用协议Toolkit.call_tool(tool_call, state)——tool_callToolCallBlock(含 id/name/input),累加成 ToolResponsestate: OK/ERROR/DENIED/INTERRUPTED);工具间用 ToolResultBlocktool_call_id 关联)回填 context。这套 Msg + block 契约就是 QwenPaw 继承的那套。
  • 注册/分组:工具按 ToolGroup 组织,永远有一个 "basic" 组(构造入参 tools/mcps/skills 默认进 basic)。
  • 动态激活(agentic):内建 meta tool ResetToolstool/_builtin/_meta.pytool/_toolkit.py:157 注册)让 LLM 自己激活/停用工具组——只有注册了非 basic 组时才暴露该 meta tool;调用未激活组的 工具返回 ToolGroupInactiveError 提示先激活。这是”渐进披露工具”以省 context。
  • 内建工具tool/_builtin/):bash/read/write/edit/grep/glob/meta(ResetTools)/skill(SkillViewer) ——Claude-Code 同款工具集;另有 tool/_task/(create/get/list/update_task)。
  • MCP:一等公民,Toolkit 直接注册 MCPClient_get_available_toolsawait client.list_tools() 动态拉取;支持须已连接的 stateful client。
  • 工具中间件ToolMiddlewareBasetool/_base.py:36)洋葱式包裹单个工具执行,与 agent 级 中间件独立。
  • 权限:工具自带 check_permissions/match_rule,由引擎裁决(见”安全与权限”)。

Prompt 设计(系统提示结构、动态组装)

_get_system_promptagent/_agent.py:2255)动态拼装,顺序:

  1. 用户给的 self._system_prompt(基础);
  2. skill 说明toolkit.get_skill_instructions(activated_groups))——只拼当前激活组的 skill;
  3. workspace/offloader 说明(若 offloader 是 WorkspaceBase);
  4. 逐个过 on_system_prompt 中间件(transformer 模式,字符串进出)——长期记忆中间件在此注入 MEMORY.md。

工具 schema 不进 system prompt,走 _prepare_model_input(:2280)的 tools 参数(标准 function-calling)。skill 说明模板 DEFAULT_SKILL_INSTRUCTIONtool/_toolkit.py:51)明确告诉模型 “skill 不是 tool,要用 SkillViewer 工具去读全文再照做”;工具组说明用 Jinja2 渲染当前激活组 + <tool-instructions>

Router / 编排(任务分解、多 agent、子 agent)

核心库层没有内建多 agent 编排——单个 Agent 就是一个 ReAct worker。编排在服务层 app/

  • 团队模型(star 拓扑)app/_tool/ 暴露给 leader agent 一组团队工具 TeamCreate / AgentCreateapp/_tool/_agent_create.py:140)/ AgentInvite / TeamSay / TeamDelete
    • AgentCreateSubAgentTemplateapp/_types.py:83)spawn 一个 worker(source='team'), prompt 作为 worker 第一条 user message,worker 立即开跑;subagent_type 枚举由开发者注册的 模板集合决定(类似 Claude Code 的 subagent type)。
    • 拓扑硬约束(prompt 硬编码,约 :168-173):所有成员只向 leader 汇报,禁止成员间通信、 禁止建 “integrator” 成员——刻意保持星形而非图状,降低通信复杂度。
  • 通信底座app/message_bus/——MessageBus 抽象 + InMemoryMessageBus(asyncio.Queue pub/sub)/ RedisMessageBus;key 空间含 session_events / inbox / wakeup_queue / cancel/interrupt channel 等。
  • 任务分解(单 agent 内):库层 tool/_task/ + TaskContextstate/_state.py)提供 TODO 式 任务清单(pending→in_progress→completed),是单 agent 自我编排,非多 agent。
  • QwenPaw 的 Goal/Mission 多模式编排是它自己在应用层另做的,并未复用这套 app 团队工具——是条 值得点出的边界。

Skill / 插件体系

Skill = Claude-Code / Anthropic Skills 同款(一个目录 = 一个 skill,含 SKILL.md + 脚本 + 资源), 与 tool 严格区分:

  • 注册:Toolkit(skills_or_loaders=[...]) 接受目录路径 / Skill 对象 / SkillLoaderBaseskill/ 模块有 SkillLoaderBase + 本地目录加载器。
  • 使用协议:skill 不能直接调用;system prompt 里列出可用 skill(name/description/dir),模型必须先用 内建 SkillViewer 工具(tool/_toolkit.py:166 注册;tool/_builtin/_skill.py)读取 skill 全文, 再照做。
  • 分组:skill 也归属 tool group,随组激活(get_skill_instructions(activated_groups))。
  • 插件层 = MCP(见工具体系章)——外部能力通过 MCP client 接入,无私有插件协议。

自进化能力(自我改进 / 学习型记忆 / eval 驱动纠错)

框架核心无 RL / eval 驱动纠错 / 参数自更新(在 src/agentscope 内 grep trajectory|reward|reinforcement|rollout|fine.?tun 无实质命中,仅 scheduler/prompt 文案)。 最接近”学习”的是长期记忆中间件middleware/_longterm_memory/),三后端:

  • AgenticMemoryMiddleware_agentic_memory/_middleware.py:208)——filesystem MEMORY.md 式, 由 LLM 自己决定何时/存什么,几乎逐字复刻 Anthropic memory skill:memory 分 user/feedback/project/ reference,两步保存 = 写独立 md 文件 + 在 MEMORY.md 加一行索引(约 :126-142),还带”memory 会过期, 用前先核实当前状态”的告诫。检索:on_reply 时起一个 asyncio 异步任务用 LLM 从文件名 + 描述里选 最相关 ≤5 个记忆文件,结果在 on_reasoning 消费——检索与推理重叠以省延迟。
  • 另两后端:_mem0(Mem0 适配 + 记忆工具)、_remeagentscope-ai/ReMe,即 QwenPaw 默认长期记忆)。

定性:这是”经验记忆”式自改进(跨会话积累事实/偏好),不是 eval/RL 闭环——有记忆型(非训练型) 自改进。

可观测性(日志 / trace 格式)

OpenTelemetry 原生middleware/_tracing/):

  • TracingMiddleware 挂在 agent 中间件链上,为 reply / reasoning(LLM) / tool 三级各开一个 span。
  • 语义遵循 OTel GenAI semconv——直接 from opentelemetry.semconv._incubating.attributes import gen_ai_attributes,span 属性含 model / token / tool 等标准 gen_ai.* 字段。
  • 未调用 setup_tracing 时用 no-op provider,零开销。
  • 事件流本身即可观测面event/_event.py 定义大量 AgentEvent(ReplyStart/End、 ModelCallStart/End、ToolResultStart/End、RequireUserConfirm、ExceedMaxIters 等),reply_stream 全量吐出,前端/日志可订阅。
  • 服务层另有 middleware/_budget.py(预算)、_tts_middleware.py 等。

安全与权限(审批门、密钥管理)

独立 PermissionEnginepermission/_engine.py:17),Claude-Code 式规则引擎:

  • 5 种 PermissionModepermission/_types.py:18,源码内附完整对照表):DEFAULT(每次问,除非 allow 规则或工具自判只读)/ ACCEPT_EDITS(工作目录内读写/文件命令自动放行)/ EXPLORE(只读, 改动一律 deny)/ BYPASS(跳过安全检查,只认用户 deny/ask 规则——QwenPaw 就是设成 BYPASS 关掉 原生引擎换自己的 Gate 系统)/ DONT_ASK(把所有 ASK 转 DENY,无人值守安全默认)。
  • 规则PermissionRule(tool_name + rule_content + behavior),behavior ∈ ALLOW/DENY/ASK/PASSTHROUGH(permission/_decision.py, _types.py);引擎持 allow/deny/ask 三张表。
  • 每工具自带匹配逻辑ToolBase.check_permissions() + match_rule()tool/_base.py)。Bash 做 命令级——只读命令(ls/git status)自动放行,危险模式(rm -rf /、写 ~/.bashrc)触发 ASK; Write/Read/Edit 做 glob 路径匹配;有 dangerous_files/dangerous_directories 黑名单。
  • 审批门落地_execute_tool_callagent/_agent.py,约 :1563)调 engine.check_permission; ASK/PASSTHROUGH → 置 tool call state=ASKING 并 yield RequireUserConfirmEvent,loop 退出等 UserConfirmResultEvent;DENY → 回 ToolResultState.DENIED;ALLOW → 执行;已确认过的(state= ALLOWED)跳过复检。
  • 密钥管理credential/ 模块 + CredentialBasemodel/_base.py:42credential);服务层 app/_router/_credential.py 做 API key CRUD,#1998 支持跨用户/组织共享 credential。

沙箱与执行隔离

Workspace 抽象workspace/_base.py),一个 workspace 同时提供 resources(skills) / tools(MCP + 内建) / offload(落盘) 三件事,多种后端:

  • LocalWorkspace(本地文件系统,无隔离)
  • DockerWorkspace(容器;自带 Dockerfile 模板 + node 变体)
  • E2BWorkspace(E2B 云沙箱)
  • K8sWorkspace(Kubernetes)
  • OpenSandboxWorkspace(远程 OpenSandbox)

统一 BackendBase_sandboxed_base.py)抽象路径/命令执行;固定目录布局 {workdir}/{.mcp, data/, skills/, sessions/}。还有 _mcp_gateway/(MCP 网关)和 _offload_protocol.py(context/tool result 卸载协议)。workspace 同时充当 Offloader——agent 的 offloader 参数接 WorkspaceBase,把截断的工具结果/压缩上下文写进沙箱文件系统供 agent 再读。

与模型的协同设计

  • ChatModelBasemodel/_base.py:35)统一接口:credential/model/stream/max_retries/ retry_delay/context_size(默认 32768,model/_base.py:68,直接喂给上下文压缩阈值计算) + count_tokens。provider 可替换——已内建 openai(chat & responses)/anthropic/gemini/dashscope/ deepseek/moonshot/ollama/xai 等 ~10 家(model/_*/)。
  • FormatterBaseformatter/)每家一个 formatter——把统一的 Msg + block 契约翻成各家 wire 格式(tool_call/tool_result 块、reasoning 块、多模态)。这是”一套 Msg 契约 × N 家 API”的解耦点,也 是 QwenPaw 能换 provider 的原因。
  • 重试/fallbackModelConfigagent/_config.py)带 max_retries + fallback_model;主模型 失败自动切 fallback,retry 仅对 _get_retryable_exceptions() 声明的异常生效。
  • 流式:整条链路(model→agent event→tool)都是 AsyncGeneratorChatResponse chunk 经 _convert_chat_response_to_event 转 agent event。

轨迹利用(session/trajectory 是否反哺训练/评测)

  • 有 session 持久化AgentState(全量 pydantic 状态)+ 服务层 app/storage/_redis_storage.py + _model/)+ app/_router/_session.py 做 per-user/per-session 落盘、恢复、 session_events
  • 无训练/RL/eval 反哺:框架内没有把 trajectory 导出去做 SFT/RL/reward 的任何管线(无 trainer / rollout / reward)。session 数据用途 = 运行时恢复 + 可观测(OTel)+ 前端回放,不是训练语料生成。
  • 定性:有轨迹持久化/回放,无轨迹→训练闭环(限本仓库;阿里内部或 sibling repo 是否另有,未在此 仓库出现)。

与同类 harness 的关键差异(1-3 条)

  1. “库 + 运行时服务”两层同仓:多数 harness 要么是单体 CLI(Claude Code、Codex CLI),要么是纯编排 库(LangGraph、AutoGen)。AgentScope 把一个统一 Agent(库)+ FastAPI 多用户运行时(服务,含 session 持久化、credential、team)打进同一个包,可当 SDK 也可当托管后端——这是它成为 QwenPaw 之类 Agent OS 底座的直接原因。
  2. 权限做成独立引擎 + 5 模式,且可被下游整体旁路PermissionEngine 是与工具解耦的规则引擎, PermissionMode.BYPASS 允许下游(QwenPaw)关掉整套原生审批换自己的治理层——权限层被设计成”可替换的 插槽”而非硬编码,同类 harness 少见这种显式旁路点。
  3. 工具/skill/记忆三处都对齐 Anthropic 心智模型:内建工具集是 Claude-Code 同款、skill 是 Anthropic Skills 同款(SkillViewer 先读后做)、AgenticMemory 几乎复刻 Anthropic memory skill——是一套刻意贴近 Claude 生态惯例的开源实现,迁移成本低。

原始源码定位

  • repo: https://github.com/agentscope-ai/agentscope
  • commit/version analyzed: 1aeb03d004292799091d437aaaf9486b424ecfa6(version 2.0.4, 2026-07-11 克隆分析)
  • 关键文件列表(相对 src/agentscope/):
    • agent/_agent.py(2836 行,核心:loop / 工具 dispatch / prompt / 权限落地 / 压缩)
    • agent/_config.py(ReActConfig / ContextConfig / SummarySchema / ModelConfig)
    • tool/_toolkit.pytool/_base.pytool/_builtin/*tool/_task/*
    • permission/_engine.pypermission/_types.py
    • state/_state.py
    • middleware/_base.pymiddleware/_longterm_memory/_agentic_memory/_middleware.pymiddleware/_tracing/*
    • workspace/_base.py + _docker/_e2b/_k8s/_opensandbox 后端
    • model/_base.py + formatter/*
    • app/_tool/_agent_create.pyapp/_types.py(SubAgentTemplate)、app/message_bus/*app/_router/*app/storage/*

一手源存档(sources/)

保存于 /Users/zhao/projects/self-wiki/ai-research/sources/harness/agentscope/

  • NOTES.md——第一阶段源码级调研笔记(含 12 维度、验证信息、QwenPaw 交叉引用)
  • src/(12 个核心文件,332K,文件名以 模块__文件.py 展平):
    • agent__agent.pyagent__config.py
    • tool__toolkit.pytool__base.py
    • permission__engine.pypermission__types.py
    • state__state.py
    • middleware__base.pyagentic_memory__middleware.py
    • workspace__base.py
    • model__base.py
    • app_tool__agent_create.py