手把手带你了解 AgentScope 核心架构

15 阅读16分钟

为什么需要 AgentScope?

Agent 框架如雨后春笋。LangChain 凭借先发优势占据了大量心智,AutoGPT 引爆了自主 Agent 的想象力,CrewAI 让多 Agent 协作变得简单……但当你真正要把 Agent 应用落地到生产环境时,问题来了:

  • LangChain 的抽象层级太多,调试起来像在剥洋葱,而且和具体 LLM 的绑定太紧
  • AutoGPT 很酷,但"完全自主"在生产环境约等于"不可控"
  • CrewAI 的工作流编排很香,但遇到需要深度定制的场景就显得力不从心
  • 几乎所有框架都在用僵化的 Prompt 工程来约束模型行为,而不是释放模型的推理潜能

它想解决的核心问题是:

  1. 模型能力快速迭代,框架如何跟上? —— 通过模块解耦和标准协议(A2A、MCP),让框架不被特定模型绑架
  2. 长对话场景下上下文爆炸怎么办? —— 工作记忆 + 长期记忆双轨制,配合智能压缩
  3. 复杂任务如何拆解执行? —— 内置 PlanNotebook 任务规划系统
  4. 如何让多个 Agent 协作? —— MsgHub 消息中心 + A2A 跨框架通信
  5. 怎么知道 Agent 到底行不行? —— 完整的评估框架 + 强化学习微调支持

说白了,AS 的野心是做 Agent 领域的 Spring Boot——开箱即用,但又足够灵活,能应对各种复杂场景。

接下来,我们深入代码,看看它是怎么做到的。


目录

  1. 整体架构速览
  2. Agent 核心:ReActAgent 的推理-行动循环
  3. A2A:Agent 间通信协议
  4. Memory:工作记忆与长期记忆双轨制
  5. Plan:任务规划系统
  6. Formatter:消息格式化层
  7. MCP:Model Context Protocol 集成
  8. Embedding:多模态向量化
  9. Evaluate:基准测试框架
  10. Model:LLM 适配层

整体架构速览

AgentScope 的核心设计理念是模块解耦、异步优先。整个框架以 ReActAgent 为核心,围绕其构建了完整的工具调用、记忆管理、任务规划、多 Agent 协作等能力。

flowchart TB
    subgraph Core["🧠 AgentScope 核心"]
        direction TB
        Model["Model<br/>(LLM)"]
        Formatter["Formatter<br/>(API适配)"]
        Memory["Memory<br/>(工作/长期)"]
        Toolkit["Toolkit<br/>(Tools+MCP+Skills)"]
        
        Model & Formatter & Memory & Toolkit --> Agent
        Agent["ReActAgent<br/>(推理-行动循环)"]
    end
    
    subgraph Extensions["🔌 扩展能力"]
        Plan["Plan<br/>(任务规划)"]
        RAG["RAG<br/>(知识检索)"]
        A2A["A2A<br/>(Agent通信)"]
    end
    
    Agent --> Plan
    Agent --> RAG
    Agent --> A2A

关键设计特点:

  • 全异步架构:所有核心方法都是 async,支持高并发场景
  • 消息统一抽象Msg 类支持多模态内容块(文本、图片、音频、视频、工具调用)
  • Hook 机制:支持类级别和实例级别的钩子,在 reply/observe/print 前后注入自定义逻辑
  • 状态管理:通过 StateModule 基类提供统一的状态序列化/反序列化能力

Agent 核心:ReActAgent 的推理-行动循环

ReActAgent 是 AgentScope 的核心实现,采用经典的 ReAct (Reasoning + Acting) 模式。

为什么选择 ReAct 模式?

在 Agent 架构设计上,业界有几种主流思路:

模式代表框架核心思想适用场景
Chain/GraphLangChain, LangGraph预定义执行流程,节点间传递状态流程固定、可预测的任务
Plan-and-ExecuteAutoGPT先规划完整计划,再逐步执行目标明确、步骤可预判的任务
ReActAgentScope, OpenAI Swarm推理-行动交替,边做边想动态环境、需要实时调整的任务
Crew/RoleCrewAI多角色分工协作需要专业化分工的复杂任务

AgentScope 选择 ReAct 作为核心,但通过 PlanNotebook 模块补充了计划能力——这是一个务实的选择:

  • ReAct 更贴近人类解决问题的方式:遇到问题 → 思考 → 尝试 → 观察结果 → 调整策略,循环往复
  • 对模型能力的依赖更合理:不需要模型一次性规划出完美方案(这对当前的 LLM 来说很难),而是允许它在执行过程中不断调整
  • 更好的容错性:单步失败可以在下一轮推理中修正,而不是整个计划报废

与 LangChain 的关键差异:

LangChain 的 Agent 核心是 AgentExecutor,它本质上也是 ReAct 循环,但:

  • LangChain 的抽象层级更多(Chain → Agent → Tool → Memory),调试时经常迷失在层层回调中
  • AgentScope 更扁平,ReActAgent.reply() 就是完整的推理-行动循环,一目了然

与 AutoGPT 的关键差异:

AutoGPT 的"完全自主"意味着人类几乎无法介入。AgentScope 的设计哲学是人机协作——支持实时打断、动态调整计划、Hook 注入等机制,让人类在需要时能随时接管。

核心流程

async def reply(self, msg, structured_model=None) -> Msg:
    # 1. 循环前准备
    await self.memory.add(msg)                        # 记录输入
    await self._retrieve_from_long_term_memory(msg)   # 长期记忆检索
    await self._retrieve_from_knowledge(msg)          # RAG 知识库检索
    
    # 2. 推理-行动循环
    for _ in range(self.max_iters):
        await self._compress_memory_if_needed()       # 内存压缩检查
        msg_reasoning = await self._reasoning()       # 推理
        
        # 行动(支持并行/串行工具调用)
        futures = [self._acting(tool_call) for tool_call in tool_calls]
        if self.parallel_tool_calls:
            await asyncio.gather(*futures)
        else:
            [await f for f in futures]
        
        # 退出条件检查
        if 满足结构化输出要求 or 无工具调用:
            break
    
    # 3. 循环后处理
    if reply_msg is None:
        reply_msg = await self._summarizing()  # 达到最大迭代时生成摘要
    
    # 记录到长期记忆
    if self._static_control:
        await self.long_term_memory.record([...])
    
    return reply_msg

推理阶段 _reasoning()

推理阶段的核心职责是调用 LLM 生成下一步行动:

async def _reasoning(self, tool_choice=None) -> Msg:
    # 1. 插入计划提示(如果启用了 PlanNotebook)
    if self.plan_notebook:
        hint_msg = await self.plan_notebook.get_current_hint()
        await self.memory.add(hint_msg, marks=_MemoryMark.HINT)
    
    # 2. 格式化消息并调用 LLM
    prompt = await self.formatter.format([
        Msg("system", self.sys_prompt, "system"),
        *await self.memory.get_memory(),
    ])
    await self.memory.delete_by_mark(mark=_MemoryMark.HINT)  # 使用后清除提示
    
    res = await self.model(prompt, tools=self.toolkit.get_json_schemas())
    
    # 3. 处理输出(支持流式、TTS)
    msg = Msg(name=self.name, content=[], role="assistant")
    if self.model.stream:
        async for chunk in res:
            msg.content = chunk.content
            await self.print(msg, False)
    # ...
    
    await self.memory.add(msg)
    return msg

关键点:

  • 提示消息(hint)使用后立即清除,不污染记忆
  • 支持流式输出和 TTS 语音合成
  • 用户中断时会伪造工具结果以保持消息格式一致性

行动阶段 _acting()

async def _acting(self, tool_call: ToolUseBlock) -> dict | None:
    # 创建工具结果消息容器
    tool_res_msg = Msg("system", [ToolResultBlock(...)], "system")
    
    # 执行工具调用
    tool_res = await self.toolkit.call_tool_function(tool_call)
    
    # 异步流式处理工具输出
    async for chunk in tool_res:
        tool_res_msg.content[0]["output"] = chunk.content
        await self.print(tool_res_msg, chunk.is_last)
    
    await self.memory.add(tool_res_msg)
    return structured_output if 是结构化输出函数 else None

内存压缩机制

长对话是 Agent 应用的普遍痛点。一个复杂任务可能需要几十轮交互,Token 很快就爆了。业界的常见做法包括:

  • 截断:简单粗暴,但会丢失关键上下文
  • 滑动窗口:保留最近 N 条,远古记忆全丢
  • 摘要压缩:用 LLM 生成摘要,但摘要质量参差不齐

AgentScope 的方案是结构化摘要压缩——不是让 LLM 自由发挥写摘要,而是用 Pydantic Schema 约束输出格式,确保关键信息不丢失:

class CompressionConfig(BaseModel):
    trigger_threshold: int      # 触发阈值
    keep_recent: int = 3        # 保留最近 N 条消息
    summary_schema: Type[BaseModel] = SummarySchema  # 结构化摘要模型

class SummarySchema(BaseModel):
    task_overview: str          # 任务概览
    current_state: str          # 当前状态
    important_discoveries: str  # 重要发现
    next_steps: str             # 下一步
    context_to_preserve: str    # 需要保留的上下文

压缩策略亮点:

  • 保留工具调用的 ID 对应关系,确保消息完整性
  • 使用结构化输出生成高质量摘要
  • 支持配置独立的压缩模型(可以用便宜的模型做压缩)

RAG 知识检索

async def _retrieve_from_knowledge(self, msg):
    # 可选:查询重写(将 "yesterday" 具体化为实际日期)
    if self.enable_rewrite_query:
        res = await self.model(rewrite_prompt, structured_model=_QueryRewriteModel)
        query = res.metadata["rewritten_query"]
    
    # 从所有知识库检索
    docs = []
    for kb in self.knowledge:
        docs.extend(await kb.retrieve(query=query))
    
    # 按相关性排序并添加到记忆
    docs = sorted(docs, key=lambda doc: doc.score, reverse=True)
    await self.memory.add(retrieved_msg)

A2A:Agent 间通信协议

想象一个场景:你的团队用 AgentScope 构建了一个代码审查 Agent,隔壁团队用 LangChain 搞了一个安全扫描 Agent,领导想让它们协作。怎么办?

在 A2A 协议出现之前,答案是——改代码、写适配器、祈祷别出 bug。

A2A (Agent-to-Agent) 是 Google 提出的 Agent 间通信协议,类似于微服务领域的 gRPC——定义了 Agent 如何发现彼此、如何交换消息、如何管理任务生命周期。AgentScope 是首批完整实现 A2A 的框架之一。

A2AAgent 设计

class A2AAgent(AgentBase):
    """支持与远程 Agent 通信的客户端实现"""
    
    def __init__(self, agent_card: AgentCard, client_config=None):
        self.agent_card = agent_card  # 远程 Agent 的描述卡片
        self._a2a_client_factory = ClientFactory(config=client_config)
        self.formatter = A2AChatFormatter()  # 消息格式转换器
        self._observed_msgs = []  # 本地观察消息缓存

核心流程

async def reply(self, msg) -> Msg:
    # 1. 合并观察消息与输入消息
    msgs_list = self._observed_msgs + [msg]
    
    # 2. 创建 A2A 客户端
    client = self._a2a_client_factory.create(card=self.agent_card)
    
    # 3. 格式转换:内部 Msg → A2A Message
    a2a_message = await self.formatter.format(msgs_list)
    
    # 4. 发送并处理响应流
    async for item in client.send_message(a2a_message):
        if isinstance(item, A2AMessage):
            response_msg = await self.formatter.format_a2a_message(item)
        elif isinstance(item, tuple):  # (Task, artifacts)
            response_msg = await self.formatter.format_a2a_task(task)
    
    self._observed_msgs.clear()  # 处理完成后清除
    return response_msg

A2A Formatter

A2AChatFormatter 负责双向格式转换:

class A2AChatFormatter:
    async def format(self, msgs: list[Msg]) -> A2AMessage:
        """AgentScope Msg → A2A Message"""
        parts = []
        for msg in msgs:
            for block in msg.get_content_blocks():
                if block["type"] == "text":
                    parts.append(TextPart(text=block["text"]))
                elif block["type"] == "image":
                    parts.append(FilePart(file=...))  # base64 或 URL
        return A2AMessage(parts=parts, role=...)
    
    async def format_a2a_message(self, name: str, msg: A2AMessage) -> Msg:
        """A2A Message → AgentScope Msg"""
        content = []
        for part in msg.parts:
            if isinstance(part, TextPart):
                content.append(TextBlock(type="text", text=part.text))
            # ...
        return Msg(name=name, content=content, role="assistant")

限制说明:

  • A2A 协议不支持结构化输出
  • 当前仅支持单用户-单助手的对话场景

Memory:工作记忆与长期记忆双轨制

如果你用过 ChatGPT,一定体验过这种挫败感:聊了几十轮后告诉它"按照我们之前讨论的方案来",它一脸懵逼地问"请问是哪个方案?"

这是因为大多数 Agent 框架只有工作记忆——本质上就是一个消息列表,塞进上下文窗口。一旦对话变长,要么截断丢信息,要么 Token 爆炸。

人类的记忆不是这样工作的。我们有工作记忆(当前正在想什么)和长期记忆(过去的经验、知识),两者协同工作。AgentScope 模仿了这个结构:

工作记忆 MemoryBase

工作记忆存储当前会话的对话历史:

class MemoryBase(StateModule):
    _compressed_summary: str = ""  # 压缩后的历史摘要
    
    async def add(self, memories, marks=None): ...
    async def delete(self, msg_ids): ...
    async def get_memory(self, mark=None, exclude_mark=None, prepend_summary=True): ...
    async def update_messages_mark(self, new_mark, old_mark=None, msg_ids=None): ...

实现方式:

  • InMemoryMemory:内存列表存储,适合简单场景
  • RedisMemory:Redis 存储,支持分布式
  • SQLAlchemyMemory:数据库存储,支持持久化

Mark 机制: 消息可以打上标记(mark),用于过滤和批量操作:

await memory.add(hint_msg, marks=_MemoryMark.HINT)      # 打标记
await memory.delete_by_mark(mark=_MemoryMark.HINT)     # 按标记删除
await memory.get_memory(exclude_mark="compressed")      # 排除已压缩的消息

长期记忆 LongTermMemoryBase

长期记忆用于跨会话的信息持久化:

class LongTermMemoryBase(StateModule):
    # 开发者接口:框架自动调用
    async def record(self, msgs: list[Msg]): ...
    async def retrieve(self, msg, limit=5) -> str: ...
    
    # Agent 工具接口:作为工具函数暴露给 Agent
    async def record_to_memory(self, thinking, content) -> ToolResponse: ...
    async def retrieve_from_memory(self, keywords, limit=5) -> ToolResponse: ...

两种控制模式:

long_term_memory_mode: Literal["agent_control", "static_control", "both"]

# agent_control: 将 record/retrieve 注册为工具函数,由 Agent 自主调用
# static_control: 框架在 reply 开始/结束时自动检索/记录
# both: 同时启用两种模式

实现方式:

  1. Mem0 实现:自动提取对话中的关键信息,支持语义搜索
  2. ReMe 实现:细分为三种记忆类型
    • 个人记忆(Personal):用户偏好、个人信息
    • 任务记忆(Task):执行经验
    • 工具记忆(Tool):工具使用模式

Plan:任务规划系统

"帮我写一个电商网站"——这种请求,单靠 ReAct 循环是搞不定的。Agent 需要把大任务拆成小任务,一步步完成。

业界有两种主流思路:

  1. 前置规划(AutoGPT 风格):先让 LLM 生成完整计划,然后严格执行
  2. 动态规划(AgentScope 风格):边做边调整,计划是活的

前置规划的问题是,LLM 很难一次性规划出完美方案——它不知道执行过程中会遇到什么坑。AgentScope 的 PlanNotebook 采用动态规划:计划随时可以修改、子任务可以增删、进度实时追踪。

数据模型

class SubTask(BaseModel):
    name: str
    description: str
    expected_outcome: str
    state: Literal["todo", "in_progress", "done", "abandoned"]
    outcome: str | None = None

class Plan(BaseModel):
    name: str
    description: str
    expected_outcome: str
    subtasks: list[SubTask]
    state: Literal["todo", "in_progress", "done", "abandoned"]

提供的工具函数

def list_tools(self) -> list[Callable]:
    return [
        # 子任务相关
        self.view_subtasks,           # 查看子任务详情
        self.update_subtask_state,    # 更新子任务状态
        self.finish_subtask,          # 完成子任务(需提供具体成果)
        
        # 计划相关
        self.create_plan,             # 创建新计划
        self.revise_current_plan,     # 修改计划(增/改/删子任务)
        self.finish_plan,             # 完成或放弃计划
        
        # 历史计划
        self.view_historical_plans,   # 查看历史计划
        self.recover_historical_plan, # 恢复历史计划
    ]

提示消息生成

PlanNotebook 根据当前计划状态动态生成引导提示:

class DefaultPlanToHint:
    no_plan: str = "如果用户查询复杂,需要先创建计划..."
    at_the_beginning: str = "当前计划:{plan}\n你的选项包括:标记第一个子任务为进行中..."
    when_a_subtask_in_progress: str = "子任务 {subtask_name} 正在进行中..."
    when_no_subtask_in_progress: str = "前 {index} 个子任务已完成,没有进行中的任务..."
    at_the_end: str = "所有子任务已完成,调用 finish_plan..."

关键约束

  • 顺序执行:子任务必须按顺序完成,不能跳跃
  • 单一进行中:同时只能有一个 in_progress 状态的子任务
  • 完成需成果finish_subtask 必须提供具体的产出物,不能是模糊描述

Formatter:消息格式化层

Formatter 是 AgentScope 与不同 LLM API 之间的适配层,负责将统一的 Msg 对象转换为各厂商要求的格式。

基类设计

class FormatterBase:
    @abstractmethod
    async def format(self, msgs: list[Msg]) -> list[dict[str, Any]]:
        """将 Msg 列表转换为 API 所需的消息格式"""
    
    @staticmethod
    def convert_tool_result_to_string(output) -> tuple[str, list]:
        """将工具结果转换为文本(用于不支持多模态工具结果的 API)"""

已实现的 Formatter

Formatter厂商特殊处理
OpenAIFormatterOpenAI标准实现
AnthropicFormatterAnthropic/Claudethinking block 支持
GeminiFormatterGoogleURL → base64 转换
DashscopeFormatter阿里通义-
OllamaFormatterOllama本地模型适配
DeepseekFormatterDeepSeek-
A2AChatFormatterA2A 协议Agent 间通信格式

多模态格式转换示例

# Gemini 需要将 URL 图片下载后转为 base64
class GeminiFormatter(FormatterBase):
    async def _process_image_block(self, block: ImageBlock) -> dict:
        if block["source"]["type"] == "url":
            # 下载并转换
            data = await download_and_encode(block["source"]["url"])
            return {"inline_data": {"data": data, "mime_type": "..."}}
        else:
            return {"inline_data": block["source"]}

MCP:Model Context Protocol 集成

工具调用是 Agent 的核心能力,但每个框架都在重复造轮子——LangChain 有自己的 Tool 抽象,CrewAI 有自己的,OpenAI 有 Function Calling……

Anthropic 提出的 MCP (Model Context Protocol) 试图解决这个问题:定义一套标准的工具协议,让工具服务可以被任何 Agent 框架复用

这意味着:

  • 高德地图的 MCP 工具,LangChain 能用,AgentScope 也能用
  • 你开发的 MCP 工具,不用为每个框架写一遍适配器

AgentScope 对 MCP 的集成非常深入——不仅能批量注册 MCP 工具,还能把单个 MCP 工具拿出来当本地函数用,方便二次封装。

MCP 客户端

支持三种传输方式:

  • SSE:Server-Sent Events
  • StdIO:标准输入输出(本地进程)
  • HTTP:HTTP 接口

MCPToolFunction

将 MCP 工具转换为可直接调用的函数对象:

class MCPToolFunction:
    name: str              # 工具名称
    description: str       # 工具描述
    json_schema: dict      # 参数 JSON Schema
    
    async def __call__(self, **kwargs) -> ToolResponse:
        # 建立连接并调用
        async with self.client_gen() as cli:
            async with ClientSession(cli[0], cli[1]) as session:
                await session.initialize()
                res = await session.call_tool(self.name, arguments=kwargs)
        
        # 转换结果格式
        if self.wrap_tool_result:
            return ToolResponse(
                content=MCPClientBase._convert_mcp_content_to_as_blocks(res.content),
                metadata=res.meta,
            )
        return res

Toolkit 集成

# 注册 MCP 客户端的所有工具
await toolkit.register_mcp_client(
    mcp_client=my_mcp_client,
    group_name="mcp_tools",
    enable_funcs=["func1", "func2"],  # 可选:只启用部分函数
    disable_funcs=["func3"],          # 可选:排除部分函数
)

# 移除 MCP 客户端的工具
await toolkit.remove_mcp_clients(["client_name"])

Embedding:多模态向量化

EmbeddingModelBase 支持多种数据类型的向量化:

class EmbeddingModelBase:
    model_name: str
    supported_modalities: list[str]  # ["text", "image", "video"]
    dimensions: int
    
    async def __call__(self, *args) -> EmbeddingResponse: ...

class EmbeddingResponse:
    embeddings: list[list[float]]  # 向量列表
    usage: EmbeddingUsage          # token 使用统计

已实现的适配器

适配器厂商支持模态
OpenAIEmbeddingOpenAI文本
GeminiEmbeddingGoogle文本
OllamaEmbeddingOllama文本
DashscopeEmbedding阿里文本
DashscopeMultimodalEmbedding阿里文本 + 图片

与 RAG 的配合

class KnowledgeBase:
    embedding_store: VDBStoreBase      # 向量存储(Milvus/Chroma/...)
    embedding_model: EmbeddingModelBase # 向量化模型
    
    async def retrieve(self, query, limit=5) -> list[Document]:
        # 1. 将查询文本向量化
        query_embedding = await self.embedding_model(query)
        # 2. 在向量库中检索
        return await self.embedding_store.search(query_embedding, limit)
    
    async def add_documents(self, documents: list[Document]):
        # 1. 向量化文档
        embeddings = await self.embedding_model([d.content for d in documents])
        # 2. 存入向量库
        await self.embedding_store.insert(embeddings, documents)

Evaluate:基准测试框架

AgentScope 提供了完整的 Agent 评估框架。

核心概念

@dataclass
class Task:
    id: str                              # 任务唯一标识
    input: JSONSerializableObject        # 输入
    ground_truth: JSONSerializableObject # 标准答案
    metrics: list[MetricBase]            # 评估指标列表
    tags: dict[str, str] | None          # 分类标签
    metadata: dict | None                # 额外元数据(如工具函数)
    
    async def evaluate(self, solution: SolutionOutput) -> list[MetricResult]:
        return [await metric(solution) for metric in self.metrics]

评估流程

class EvaluatorBase:
    benchmark: BenchmarkBase      # 基准测试集
    storage: EvaluatorStorageBase # 结果存储
    
    async def run(self, solution: Callable) -> dict:
        results = []
        for task in self.benchmark.tasks:
            # 1. 执行解决方案
            output = await solution(task.input)
            # 2. 评估结果
            metrics = await task.evaluate(output)
            results.append(metrics)
        
        # 3. 聚合统计
        return self.aggregate(results)

存储结构

  • Solution:单次任务的输入输出
  • Evolution:同一任务的多次尝试历史
  • Aggregation:整体统计结果

Model:LLM 适配层

ChatModelBase 为不同 LLM 提供统一接口:

class ChatModelBase:
    model_name: str
    stream: bool  # 是否流式输出
    
    async def __call__(
        self, 
        prompt: list[dict],
        tools: list[dict] | None = None,
        tool_choice: str | None = None,
        structured_model: Type[BaseModel] | None = None,
    ) -> ChatResponse | AsyncGenerator[ChatResponse, None]: ...

class ChatResponse:
    content: list[ContentBlock]  # 内容块列表
    metadata: dict | None        # 结构化输出存放在此
    usage: ModelUsage           # token 使用统计

已适配的模型

Model厂商特性
OpenAIModelOpenAIGPT-4/3.5, 支持 function calling
AnthropicModelAnthropicClaude, 支持 extended thinking
GeminiModelGoogleGemini Pro/Flash
DashscopeModel阿里通义千问全系列
OllamaModelOllama本地模型运行
TrinityModel携程内部内部模型服务

结构化输出

所有模型统一支持 Pydantic BaseModel 的结构化输出:

class MyOutput(BaseModel):
    summary: str
    confidence: float

response = await model(prompt, structured_model=MyOutput)
# response.metadata = {"summary": "...", "confidence": 0.95}

消息模型:Msg 类

Msg 是 AgentScope 中所有消息的统一抽象:

class Msg:
    id: str                    # 唯一标识 (shortuuid)
    name: str                  # 发送者名称
    role: Literal["user", "assistant", "system"]
    content: str | list[ContentBlock]  # 支持多模态
    metadata: dict | None      # 结构化输出、额外信息
    timestamp: str            
    
    def get_content_blocks(self, block_type=None) -> list[ContentBlock]: ...
    def get_text_content(self) -> str | None: ...
    def has_content_blocks(self, block_type) -> bool: ...

内容块类型

TextBlock = {"type": "text", "text": str}
ThinkingBlock = {"type": "thinking", "thinking": str}  # Claude extended thinking
ImageBlock = {"type": "image", "source": {"type": "url"|"base64", ...}}
AudioBlock = {"type": "audio", "source": {...}}
VideoBlock = {"type": "video", "source": {...}}
ToolUseBlock = {"type": "tool_use", "id": str, "name": str, "input": dict}
ToolResultBlock = {"type": "tool_result", "id": str, "name": str, "output": ...}

Toolkit:工具管理系统

Toolkit 是 AgentScope 中管理工具函数的核心模块:

class Toolkit(StateModule):
    tools: dict[str, RegisteredToolFunction]  # 已注册的工具
    groups: dict[str, ToolGroup]              # 工具分组
    skills: dict[str, AgentSkill]             # Agent 技能

核心功能

1. 工具注册

toolkit.register_tool_function(
    tool_func=my_function,
    group_name="basic",           # 分组
    preset_kwargs={"api_key": x}, # 预设参数(不暴露给 LLM)
    namesake_strategy="rename",   # 同名策略:raise/override/skip/rename
)

2. 分组管理

toolkit.create_tool_group("web_tools", description="网页相关工具")
toolkit.update_tool_groups(["web_tools"], active=True)

# 元工具:让 Agent 自己管理工具激活状态
toolkit.register_tool_function(toolkit.reset_equipped_tools)

3. MCP 集成

await toolkit.register_mcp_client(mcp_client)

4. Agent Skills

toolkit.register_agent_skill("/path/to/skill_dir")
# skill_dir 必须包含 SKILL.md(带 YAML front matter)

后记

目前的AS还有很多局限的地方,一方面是目前没有较好的解决方案,另一方面是工作量非常大,但整体来说,已经算是一个比较可靠的Agent框架了