Appearance
实战:研究型 Agent 🔥
前面几页分别讲了 Agent、Toolkit、上下文压缩、Workspace 沙箱与 Plan 工具。这一页把它们组合成一个 完整可跑的研究型 Agent:给它一个主题和一个资料目录,它会自主拆解任务、读取资料、产出一份中文摘要 报告。这是 AgentScope 2.x 五大支柱在同一个脚本里协同工作的最小闭环。
版本基线:本文示例以
agentscope 2.0.4(PyPI,2026-07-07)为准,要求 Python>=3.11。 导入路径为from agentscope.agent import Agent, ContextConfig、from agentscope.tool import Toolkit, Bash, Read, Write, Edit, Glob, Grep, TaskCreate, TaskGet, TaskList, TaskUpdate、from agentscope.workspace import LocalWorkspace、from agentscope.middleware import TracingMiddleware(源:src/agentscope/agent/__init__.py、src/agentscope/tool/__init__.py、src/agentscope/workspace/__init__.py、src/agentscope/middleware/__init__.py)。
调用真实 LLM 会产生费用
本页示例会调用真实模型 API,按 provider 计费产生费用。研究型 Agent 会多轮调用模型(规划、读文件、 写报告),token 消耗显著高于单轮问答。如果想零成本跑通,可改用下方 Ollama 本地模型 变体,它在本地推理、不需要 API key、不产生云端费用。
目标
完成后你会组合五个构建块,得到一个能独立完成"资料调研 -> 摘要报告"任务的 Agent:
- 用
Agent驱动 ReAct 循环,把模型、工具、上下文、工作区串成一条执行链。 - 用
Toolkit装配内置文件工具(Read/Write/Edit/Glob/Grep)与 Plan 工具 (TaskCreate/TaskGet/TaskList/TaskUpdate),让 Agent 能规划进度并读写文件。 - 用
ContextConfig配置上下文压缩阈值,长任务逼近上限时自动压缩历史并卸载到 Workspace。 - 用
LocalWorkspace同时承担工具执行环境与上下文卸载目标两个角色。 - 用
TracingMiddleware(可选)挂载全生命周期 OpenTelemetry 追踪,无后端时自动降级为 no-op。
前端类比
可以把这个研究型 Agent 类比为一个带任务看板的 Node CLI 脚本:TaskCreate 是往看板上贴卡片, Read / Grep 是 fs.readFile / grep,Write 是 fs.writeFile,ContextConfig 像一个自动 归档旧 log 的滚动策略,LocalWorkspace 是脚本的工作目录,TracingMiddleware 像一个挂载在中间件 链上的 OpenTelemetry 插桩。
AgentScope 原生语义:这不是脚本,而是一个事件驱动的 ReAct 循环。Agent 调用模型生成推理与工具 调用,工具在 Workspace 中执行后把结果回灌进上下文,循环直到模型不再产出工具调用。Plan 工具不是 外部看板,而是被 Toolkit 装配、状态写入 AgentState.tasks_context 的 FunctionTool 子类; ContextConfig 的压缩由 Agent 在 ReAct 循环特定节点自动触发,不是手动 setTimeout。把前端类比 作为理解入口即可,不要把脚本同步执行模型硬套到异步事件循环上。
实战架构
下面这张图说明一次研究任务如何穿过五个构建块。图前先说明:输入是一条 UserMsg(主题 + 资料目录), 输出是最终 Msg(含报告路径与摘要),中间穿插任务规划、文件读取、上下文压缩与报告写入。
节点解释:
- Agent(ReAct 循环) 是核心引擎。它消费
UserMsg,调用模型生成推理与工具调用,把工具结果 回灌进上下文,直到产出最终回复。 - TaskCreate / TaskList:Agent 先用
TaskCreate把"调研主题"拆成若干子任务(如"列出目录文件"、 "逐个读取关键文件"、"撰写摘要"),再用TaskList回顾全局进度,决定下一步。任务状态存在AgentState.tasks_context里,跨多轮推理可见。 - Glob / Grep / Read:Agent 用
Glob按通配符列出文件、用Grep搜索关键词定位、用Read读取命中的文件内容。这些工具在LocalWorkspace.workdir下执行。 - ContextConfig 压缩:当 token 占用超过
trigger_ratio × model.context_size时,Agent 自动 把旧消息总结并卸载到offloader(即LocalWorkspace),保留reserve_ratio比例的最近消息。 - Write:Agent 调用
Write把中文摘要报告写到工作区,路径通常为report.md。 - TaskUpdate:每完成一项子任务,Agent 调用
TaskUpdate把状态推进到completed。 - reply 返回:模型不再产出工具调用时,
reply返回最终Msg,其中包含报告路径与简要摘要。
用到的构建块
| 构建块 | 本实战作用 |
|---|---|
Agent | 驱动 ReAct 循环,串联模型、工具、上下文、工作区 |
Toolkit(内置 + Plan) | 装配文件工具与任务工具,让 Agent 能规划进度并读写文件 |
ContextConfig | 配置压缩阈值,长任务逼近上限时自动压缩历史并卸载 |
LocalWorkspace | 工具执行环境 + 上下文卸载目标,报告文件也落在这里 |
TracingMiddleware(可选) | 全生命周期 OTel 追踪,无 TracerProvider 时自动降级为 no-op |
5. 完整代码
下面是一个完整可跑的脚本。把它存为 research_agent.py,准备一个资料目录(如 ./materials,里面 放几个 .md 或 .txt 文件),设置好环境变量后执行 python research_agent.py。
python
import asyncio
import os
from agentscope.agent import Agent, ContextConfig
from agentscope.credential import DashScopeCredential
from agentscope.message import UserMsg
from agentscope.middleware import TracingMiddleware
from agentscope.model import DashScopeChatModel
from agentscope.tool import (
Toolkit,
Bash,
Read,
Write,
Edit,
Glob,
Grep,
TaskCreate,
TaskGet,
TaskList,
TaskUpdate,
)
from agentscope.workspace import LocalWorkspace
# 资料目录与报告路径(相对于 workdir)
MATERIALS_DIR = "./materials"
REPORT_PATH = "report.md"
SYSTEM_PROMPT = """你是一个严谨的中文研究型 Agent。你的工作流程:
1. 先用 TaskCreate 把任务拆解为可执行的子任务(如"列出资料目录文件"、"定位关键内容"、
"逐个读取关键文件"、"撰写中文摘要报告")。
2. 用 TaskList 回顾进度,每开始一项就用 TaskUpdate 标记为 in_progress,完成后再标记 completed。
3. 用 Glob 列出文件、用 Grep 搜索关键词、用 Read 读取文件内容。所有文件操作限制在资料目录内。
4. 综合读取到的资料,用 Write 把中文摘要报告写到工作区,路径由用户指定。
5. 报告须为中文,结构包含:概述、关键发现(分点)、结论与建议。
完成后用一句话告诉用户报告已写入哪个路径。"""
async def main() -> None:
# 1. 构造并初始化工作区(同时作为工具执行环境与上下文卸载目标)
ws = LocalWorkspace(workdir="./research_workspace")
await ws.initialize()
# 2. 装配 Toolkit:内置文件工具 + Plan 工具
toolkit = Toolkit(
tools=[
Bash(),
Read(),
Write(),
Edit(),
Glob(),
Grep(),
TaskCreate(),
TaskGet(),
TaskList(),
TaskUpdate(),
],
)
# 3. 构造 Agent,组合五个构建块
agent = Agent(
name="research_agent",
system_prompt=SYSTEM_PROMPT,
model=DashScopeChatModel(
credential=DashScopeCredential(
api_key=os.environ["DASHSCOPE_API_KEY"],
),
model="qwen-plus",
),
toolkit=toolkit,
context_config=ContextConfig(
trigger_ratio=0.8,
reserve_ratio=0.1,
),
offloader=ws,
middlewares=[TracingMiddleware()],
)
# 4. 发起研究任务
topic = "AgentScope 2.x 的核心设计目标与五大支柱"
result = await agent.reply(
UserMsg(
name="user",
content=(
f"请调研以下主题:{topic}\n\n"
f"资料目录:{MATERIALS_DIR}\n"
f"把中文摘要报告写到:{REPORT_PATH}"
),
),
)
print(result.get_text_content())
# 5. 关闭工作区(LocalWorkspace 会断开有状态 MCP 连接,workdir 保留)
await ws.close()
asyncio.run(main())python
import asyncio
from agentscope.agent import Agent, ContextConfig
from agentscope.message import UserMsg
from agentscope.middleware import TracingMiddleware
from agentscope.model import OllamaChatModel
from agentscope.tool import (
Toolkit,
Bash,
Read,
Write,
Edit,
Glob,
Grep,
TaskCreate,
TaskGet,
TaskList,
TaskUpdate,
)
from agentscope.workspace import LocalWorkspace
# 资料目录与报告路径(相对于 workdir)
MATERIALS_DIR = "./materials"
REPORT_PATH = "report.md"
SYSTEM_PROMPT = """你是一个严谨的中文研究型 Agent。你的工作流程:
1. 先用 TaskCreate 把任务拆解为可执行的子任务(如"列出资料目录文件"、"定位关键内容"、
"逐个读取关键文件"、"撰写中文摘要报告")。
2. 用 TaskList 回顾进度,每开始一项就用 TaskUpdate 标记为 in_progress,完成后再标记 completed。
3. 用 Glob 列出文件、用 Grep 搜索关键词、用 Read 读取文件内容。所有文件操作限制在资料目录内。
4. 综合读取到的资料,用 Write 把中文摘要报告写到工作区,路径由用户指定。
5. 报告须为中文,结构包含:概述、关键发现(分点)、结论与建议。
完成后用一句话告诉用户报告已写入哪个路径。"""
async def main() -> None:
# 1. 构造并初始化工作区(同时作为工具执行环境与上下文卸载目标)
ws = LocalWorkspace(workdir="./research_workspace")
await ws.initialize()
# 2. 装配 Toolkit:内置文件工具 + Plan 工具
toolkit = Toolkit(
tools=[
Bash(),
Read(),
Write(),
Edit(),
Glob(),
Grep(),
TaskCreate(),
TaskGet(),
TaskList(),
TaskUpdate(),
],
)
# 3. 构造 Agent,组合五个构建块(Ollama 本地推理,零云端费用)
agent = Agent(
name="research_agent",
system_prompt=SYSTEM_PROMPT,
model=OllamaChatModel(
model="qwen2.5:7b",
),
toolkit=toolkit,
context_config=ContextConfig(
trigger_ratio=0.8,
reserve_ratio=0.1,
),
offloader=ws,
middlewares=[TracingMiddleware()],
)
# 4. 发起研究任务
topic = "AgentScope 2.x 的核心设计目标与五大支柱"
result = await agent.reply(
UserMsg(
name="user",
content=(
f"请调研以下主题:{topic}\n\n"
f"资料目录:{MATERIALS_DIR}\n"
f"把中文摘要报告写到:{REPORT_PATH}"
),
),
)
print(result.get_text_content())
# 5. 关闭工作区(LocalWorkspace 会断开有状态 MCP 连接,workdir 保留)
await ws.close()
asyncio.run(main())代码要点
await ws.initialize()必须在构造 Agent 之前调用,完成 MCP 恢复与 Skill 播种。LocalWorkspace也支持async with LocalWorkspace(...) as ws:语法,进入时自动initialize()、退出时自动close()。offloader=ws把LocalWorkspace同时作为工具执行环境与上下文卸载目标。若你已改用DockerWorkspace或E2BWorkspace做沙箱,直接把同一个实例传给offloader即可。ContextConfig(trigger_ratio=0.8, reserve_ratio=0.1)是默认值,这里显式写出以便调整。当 token 占用超过0.8 × model.context_size时自动压缩,压缩后保留0.1比例的最近消息。TracingMiddleware()无条件挂载是安全的:进程未注册TracerProvider时它会自动降级为 no-op,不产出 span 也不产生开销。需要采集时配置 OTel exporter 即可。- Plan 工具的状态存在默认自动创建的
AgentState.tasks_context里,不需要手动构造TaskContext。TaskCreate/TaskList等是FunctionTool子类,必须显式装进Toolkit才能被 Agent 调用。
6. 运行与期望输出
准备资料目录:
bash
mkdir -p materials
# 在 materials/ 下放几个 .md 或 .txt 文件,内容与调研主题相关
echo "AgentScope 2.x 以安全、高效、灵活、完备为目标..." > materials/intro.md
echo "五大支柱:ReAct Agent、Toolkit、Context、Sandbox、Agent as Service..." > materials/pillars.md运行脚本(DashScope 版本):
bash
export DASHSCOPE_API_KEY="你的密钥"
python research_agent.py运行后 Agent 的典型行为序列:
- 规划阶段:调用
TaskCreate创建若干子任务(列出文件、定位关键内容、读取文件、撰写报告), 调用TaskList回顾,调用TaskUpdate把第一项标记为in_progress。 - 读取阶段:调用
Glob列出./materials下的文件,调用Grep搜索关键词,调用Read读取 命中文件的内容。每读完一项调用TaskUpdate推进状态。 - 压缩阶段(可选):若资料较多、token 占用超过
0.8 × context_size,Agent 自动压缩旧消息并 卸载到./research_workspace,保留最近消息继续推理。 - 写入阶段:调用
Write把中文摘要报告写到report.md,调用TaskUpdate标记最后一项 completed。 - 返回阶段:模型不再产出工具调用,
reply返回最终Msg,打印类似:
已完成调研。中文摘要报告已写入 report.md,涵盖 AgentScope 2.x 的四大设计目标(安全、高效、灵活、
完备)与五大支柱(ReAct Agent、Toolkit、Context、Sandbox Isolation、Agent as Service)。报告文件 report.md 会出现在 ./research_workspace 目录下(即 LocalWorkspace.workdir)。具体 措辞与结构由模型决定,不必与示例完全一致。
7. 扩展点
- 换
DockerWorkspace做强隔离:把LocalWorkspace换成DockerWorkspace,工具在容器内执行, 宿主文件系统不受影响。构造方式与offloader传法完全一致,见 Workspace 沙箱。 - 加
Permission限制写路径:默认LocalWorkspace无强隔离,Agent 可以在 workdir 内任意写。 若要限制只能写report.md、禁止执行危险 Bash 命令,装配PermissionContext与路径规则,见 权限系统。 - 加 RAG 注入外部知识:若资料不在本地文件而在向量库,用
RAGMiddleware在推理前检索相关片段 注入上下文,替代Read/Grep,见 RAG。 - 加长期记忆跨会话复用:用
Mem0/ReMe中间件把调研结论沉淀为长期记忆,下次同主题任务可 复用,见 长期记忆。 - 加 Token 预算控制:研究型 Agent 多轮调用模型容易超预算,挂载预算中间件在超限时终止循环, 见 中间件。
- 流式观察执行过程:把
reply换成reply_stream,逐个消费ToolCallStartEvent/ToolResultEndEvent/TextBlockDeltaEvent,实时展示规划与读取进度,见 流式事件。
8. 常见错误
模型不支持 tool calling
装了 toolkit 却发现 Agent 永远不调用 TaskCreate / Read 等工具,直接用文本"假装"完成了任务。 原因是所选模型不支持 function calling。AgentScope 的 ChatModelBase 子类要求模型原生支持 tool calling。qwen-plus、gpt-4.1、qwen2.5:7b 均支持;部分小模型或基座模型不支持。换模型即可。
未装 Plan 工具却期望 Agent 规划
TaskCreate 等是 FunctionTool 子类,必须显式装进 Toolkit 的 tools=[...] 才能被 Agent 调用。 只写 system_prompt 里"请先规划任务"不会触发任何工具调用--Agent 只能用文本"想",无法把任务状态 写入 AgentState.tasks_context。Plan 工具的详细语义见 计划模式。
LocalWorkspace workdir 权限问题
await ws.initialize() 会在 workdir 下创建子目录结构。若 workdir 路径父目录无写权限,会抛 PermissionError。确保运行进程对 workdir 父目录有写权限,或把 workdir 指向用户可写目录 (如 ~/.cache/agentscope/research_workspace)。
费用超预期
研究型 Agent 会多轮调用模型:规划 1 次 + 每读一个文件 1~2 次 + 压缩 1 次 + 写报告 1 次。资料目录 文件多时 token 消耗显著。控制成本的三个手段:
- 用
ReActConfig(max_iters=...)限制最大迭代次数,防止 Agent 陷入无限工具调用。 - 调低
ContextConfig.reserve_ratio,压缩后保留更少消息,但会丢失更多细节。 - 改用 Ollama 本地模型,零云端费用(但推理速度与质量取决于本地硬件)。
误用 1.x 调用方式
agent(msg)(__call__)、ReActAgent、sys_prompt= 在 2.x 已全部移除。2.x 统一用 Agent + await agent.reply(msg)。DashScopeModel / model_name= 也已移除,改用 DashScopeChatModel(credential=..., model=...)。详见 1.x 到 2.x 迁移。
先修
- Agent 核心 - 理解
Agent构造参数与 ReAct 循环 - 工具与 MCP - 理解
Toolkit与内置工具装配 - 上下文管理 - 理解
ContextConfig压缩阈值与offloader - 计划模式 - 理解
TaskCreate等 Plan 工具的状态语义
如果你从 1.x 迁移而来,可先读 1.x 到 2.x 迁移 建立 API 对照,再回到本页组合实战。
下一步
- 实战:多 Agent 应用 - 用 Agent Team 构建 Leader-Worker 多 Agent 协作