- 闸门①分布式 PASS(带 caveat):RedisMessageBus 跨 2 进程 3 agent 协作实测跑通; 1.x RpcAgent/to_dist 已删,framework 原生多 worker serving 仍 Beta(#1722/#1868,取证未亲测) - 闸门②持久化 PASS:AgentState→Redis,os._exit 与真 kill -9 两种硬崩后新进程恢复、记忆 3/3 无损 - 闸门③L1 开销 PASS:最小 Agent vs 裸 openai 本地+24.4ms/输入+11tok(几乎全 system_prompt),§5-⑤ 未被迫触发 - §5 无换轨触发器成立 → 继续 AgentScope,锁 2.0.1 - durable:VERDICT.md(含 §2 控制面↔worker 契约形状)+ artifacts 实测数据 - 关键坑:本机代理未旁路 Tailscale 网关致 SDK 全 502(_common 已修) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
15 KiB
W-G1 spike · AgentScope 2.x 落地三闸门验证 · VERDICT
任务源:
docs/agent-specs/2026-06-12-agentic基建框架选型-review.md(HJ-AGI-001,终选 A=AgentScope 2.x)。 本 spike = §5 换轨闸门的亲验门:分布式 / 持久化 / L1 开销,任一验不过即触发换轨。 执行:2026-06-13 · 本机 Apple M1 Pro 32GB · Python 3.12.13 · agentscope==2.0.1(锁版本)。 所有结论严格区分 【实测】(本机跑出)/ 【取证】(读 2.0.1 源码或 GitHub,高置信二手)/ 【推断】。
0. 结论速览(结论先行)
建议:继续 AgentScope 2.x,不触发换轨切 LangGraph。 三道闸门均通过其可测量门槛:
| 闸门 | 判定 | 一句话证据 |
|---|---|---|
| ①分布式 | PASS(带重要 caveat) | 3 个 agent 跨 2 个独立 OS 进程经 RedisMessageBus 协作产出一致产物(pid 56350/56351 实证跨进程) |
| ②持久化/恢复 | PASS | AgentState→Redis,经 os._exit 与真 kill -9 两种硬崩后,全新进程恢复 8 条上下文、记忆 3/3 复述无损 |
| ③L1 退化开销 | PASS(§5-⑤ 未被迫触发) | 最小 Agent 相对裸 openai:本地封装开销中位 +24.4ms/次、输入 token +11/次(几乎全是 system_prompt,框架固有 token 税≈0) |
最重要的一条判定(必须如实传达):AgentScope 2.0 的「分布式」与 1.x 完全不同——RpcAgent/to_dist() 进程级 actor 已整段删除。 2.0 跨进程靠 RedisMessageBus(Redis Streams 队列/pub-sub/锁)这套原语;原语本身实测可用(闸门①过),但框架并不提供开箱即用的「分布式 agent 运行时」——多 worker 编排要你自己写(我就是手写 worker 循环+队列接力把闸门①跑通的)。而官方那条「整机 serving 多 worker」路径(create_app FastAPI + 多 uvicorn worker + Agent Team)目前是 Beta、维护者自承单进程不可横向扩展(#1722),且有今日新开的多实例重复执行 bug(#1868)——这条我未亲测(只读到 issue,属【取证】),但它意味着 L3 多 agent 开发组若要真·多机多 worker,现在还不成熟,须盯版本。
为何仍判「继续」而非「换轨」:① §5 触发器④是「分布式/持久化验不过即切 B」——而本机实测两者都跑通了(分布式原语可用、持久化恢复无损),故④不成立;② §5-A 的风险敞口逻辑成立:L3 尚在数月之外、AgentScope 有半年+稳定窗、退出成本本来就低(普通 Python+状态字典+我们这套薄 worker 编排,换 LangGraph 只改 worker 内部);③ 三闸门的可测量门槛都过了。风险有界、可继续,但把「框架原生分布式 serving 成熟度」列为头号盯防项 + 锁版本盯 changelog。
1. 三闸门逐条(实测证据 + 复现命令)
闸门① 分布式 —— PASS(带 caveat)
方法:用 2.0 真实跨进程原语 agentscope.app.message_bus.RedisMessageBus(Redis Streams),把 3 个 agent 拆到 2 个独立 OS worker 进程,经本地 Redis 接力协作产出一个「游戏点子」产物。拓扑:Coordinator ─seed→[q_plan]→ W1:Planner →[q_design]→ W2:Designer →[q_name]→ W1:Namer →[q_done]→ Coordinator(job 流经 W1→W2→W1 两进程往返)。
【实测】证据(artifacts/gate1_distributed.json):
- 3 个独立进程:W1
pid=56350、W2pid=56351、Coordinatorpid=56353。 - 跨进程铁证:
design_pid(56351) ≠ plan_pid(56350)(Designer 确在另一进程),plan_pid==name_pid(Planner/Namer 同进程)。 - 协作产物自洽、层层叠加:concept=「猫咪经营小烘焙坊的休闲挂机游戏」→ mechanic=「顾客带来新配方,猫咪用心爱点心交换解锁」→ title=「Feline Bakes」。
复现:
export NEWAPI_KEY=<your-key> # 见 README;密钥不入库
redis-server --port 6399 --daemonize yes --dir .redis-data --save "" --appendonly no
.venv/bin/python src/gate1_distributed.py worker W1 & # 进程1:Planner+Namer
.venv/bin/python src/gate1_distributed.py worker W2 & # 进程2:Designer
.venv/bin/python src/gate1_distributed.py coordinator # 播种+收口+判定
关键坑/形态:
- 【取证】1.x 分布式 API 整段删除:grep 安装源码,
to_dist/RpcAgent/AgentServer/launch_server/rpc_meta零命中;无agentscope[distribute]extra;无MsgHub/SequentialPipeline。别移植任何 1.x 分布式代码,导入即炸。 - 【实测】跨进程要
agentscope[service,storage](FastAPI+redis),非 base。RedisMessageBus是 async 上下文管理器(async with ... as bus),_client在__aenter__才建,直接调方法报NoneType。 - 【实测】
queue_drain是xrange+xdel两步、非原子:单队列单消费者安全(本 demo 即如此);若多 worker 抢同一队列需自加锁,否则可能重复消费。 - 【取证·未亲测】官方整机多 worker 路径不成熟:维护者 issue #1722 自承 agent service 单进程、SessionManager 等需改 DB 后端才能横向扩展;#1868(2026-06-13 新开) 多实例共享一 Redis 时,一次 wake-up 会让同一 session 跑 N 次(重复执行)。→ 这是 L3 的头号盯防项,正式上多机前必须复测/等修复。
判定逻辑:「≥2 worker 进程 + 2~3 agent 协作」本机实测跑通 → 闸门①PASS,§5-④ 不触发。但「PASS」是靠原语自拼,不是框架原生 turnkey;framework 原生 serving 多 worker 仍 Beta/有 bug,作为 caveat 写死。
闸门② 持久化/恢复 —— PASS
方法:2.0.1 真实机制(已读源码验证)= agent.state(AgentState pydantic:含 context 对话历史、cur_iter 等)→ model_dump_json() 落 Redis;恢复 = Redis 读回 → AgentState.model_validate_json() → Agent(..., state=loaded) 续跑。等价于 app 层 RedisStorage(SessionRecord 内嵌 AgentState)的「每轮落盘」,但剥掉 FastAPI 整机使证据最小可审计。
【实测】证据(artifacts/gate2_persist.json),两种硬崩模式都过:
- 场景A(os._exit 硬退出):进程A 注入 3 条记忆、逐轮落 Redis(快照 2257B→3100B),
os._exit(137)无清理硬崩;全新进程B 仅靠 Redis 恢复,复述 3/3(Alice/Zephyr/dark)。 - 场景B(真
kill -9):worker(pid 56223)落盘 3 轮后阻塞,外部kill -9真 SIGKILL;全新进程恢复 8 条上下文(含 reasoning 块),复述 3/3 无损。 - per-turn 边界佐证:故意只落 2 轮即崩,恢复后正确复述前 2 条、并正确回答「无 UI 偏好记录」——证明崩溃前已落盘的轮无损、未落盘的轮干净丢失(正是 per-turn checkpoint 应有语义)。
复现:
.venv/bin/python src/gate2_persist.py run1 --crash-after 3 # 注入+落盘+os._exit 硬崩
.venv/bin/python src/gate2_persist.py run2 # 新进程恢复,判 PASS/FAIL
# 真 kill -9 版:python src/gate2_persist.py block & ; kill -9 <pid> ; python src/gate2_persist.py run2
关键坑:
- 【取证】无
StateModule/register_state/state_dict/SessionBase——那是 1.x / AgentScope-Java 命名;§4 表述用了旧名,2.0.1 实物是AgentState+RedisStorage。 - 【取证】恢复是 per-turn,非字节级:turn 跑到一半被杀,该 turn 从上轮快照重跑,半成品不保;工具对工作区的副作用(写文件)不回滚——工具须设计成可重入/幂等(与我们 §2 验证门子进程 CLI 形态相容)。
- 【取证·未亲测】开 context 压缩时 issue #1624:
RedisMemory._compressed_summary只在内存、不写 Redis → 重启丢失;短任务不触发,长会话须留意。
闸门③ L1 退化开销 —— PASS(§5-⑤ 未被迫触发)
方法:同一最小 LLM 调用,三路对照,各 N=20(warmup=3),模型 deepseek-v4-flash(L1 目标)。用 usage.time(框架自报 API 耗时)从 wall 中剔除网络耗时,净测框架本地 CPU 开销;并比对输入 token 膨胀。
【实测】数据(artifacts/gate3_overhead.json,中位数):
| 路径 | wall 中位 | 本地开销中位 | 输入 token | 输出 token |
|---|---|---|---|---|
| A 裸 openai SDK(§3 裸路) | 0.934s | 0(基准) | 18 | 28.5 |
| B AgentScope 模型客户端 | 0.920s | 22.2ms | 18 | 28 |
| C AgentScope 最小 Agent | 0.936s | 24.4ms | 29 | 27.5 |
- 关键差值(Agent vs 裸路):输入 token +11/次、本地封装开销 +24.4ms/次、Agent 单次 reply 内部模型调用 恰 1 次(空 toolkit 不发散)。
- 解读:+11 token 几乎全是 system_prompt(裸路没发 system,Agent 强制注入)——框架固有 token 税≈0~2;若 L1 裸路也带等量结构化指令,token 差会归零。+24.4ms 是事件对象/pydantic/
count_tokens的纯 CPU,占 ~930ms 单次调用的 ~2.6%,不构成「吃掉单价」。 - 结论:框架不对 L1 施加致命开销 → §5-⑤ 不被迫触发。但 §3「L1 走裸通路」的预设依然正确并被本测佐证:裸路更简单、且省掉这 24ms×海量调用,L1 维持 §3 旁路不变(注意:这是 L1 旁路决策的实证,不等于 AgentScope 出局;L2/L3 仍用框架原语)。
复现:.venv/bin/python src/gate3_overhead.py(WG1_N=20)。
2. §5 换轨触发器逐条评估
| 触发器 | 是否触发 | 依据 |
|---|---|---|
| ④ A 路 spike 分布式/持久化验不过→切 B | 否 | 分布式原语【实测】跑通、持久化恢复【实测】无损,两者均过门 |
| ⑤ 任何框架 L1 开销吃掉单价 → L1 退 §3 裸路 | 未被迫触发(但 §3 裸路本就保留) | 框架开销 +24ms/+11tok【实测】不致命;§3 旁路维持 |
| ①②③(许可收紧 / 自建 Server / 出生产案例) | 不适用本 spike | 属 LangGraph 侧或时间触发,非本门范围 |
净结论:无任一换轨触发器成立 → 继续 AgentScope。
3. §2 控制面↔worker 契约形状(durable 产物)
本 spike 实证了 §2-⑤「框架躲在 worker 进程内、Java 控制面零感知、换轨零改动」的可成立性:三闸门里 AgentScope 全部活在 Python worker 进程内,对外只暴露「下发 job / 回调结果」这层稳定契约。建议契约最小形状(REST 下发 + 队列回调,二选一或并用):
# 下发(Java 控制面 → Python worker):POST /worker/jobs 或 入队 jobs:{tier}
{ "job_id":"uuid", "tier":"L1|L2|L3", "kind":"generate|design|review|...",
"input":{...业务输入...}, "callback":{"type":"queue|http","target":"..."},
"idempotency_key":"uuid", "deadline_ms":120000 }
# 回调(worker → 控制面):POST {callback} 或 入队 results:{job_id}
{ "job_id":"uuid", "status":"succeeded|failed|partial",
"output":{...结构化产物 / 验证门 pass-fail JSON...},
"usage":{"input_tokens":..,"output_tokens":..,"model":".."},
"error":{"code":"..","retryable":true}, "worker":{"framework":"agentscope-2.0.1"} }
换轨零改动性:Java 侧只认上面两个 JSON;worker 内部把 AgentScope 换成 LangGraph,契约字节不变。worker.framework 字段仅作可观测标记。本 spike 的 gate1_distributed.py 已示范 worker 侧「队列接力 + 框架内编排」的形态,可直接抽象为该契约的 worker 实现骨架。
4. 关键坑总表(给主 agent / 创始人少踩半天)
- 【实测·最坑】本机代理吃掉所有 SDK 调用:本机
HTTP_PROXY/HTTPS_PROXY=127.0.0.1:7897(clash 类),NO_PROXY未含 Tailscale 网关 → httpx/openai(含 AgentScope)默认trust_env=True把发往100.64.0.8的请求经代理转发 → 502;而 curl 只认小写http_proxy故直连成功,制造「curl 通、SDK 全挂」的假象,极易误判成「网关坏/框架坏」。修复:NO_PROXY并入网关 host(src/_common.py已内置)。网关本身一直健康。 - 【实测】new-api 网关对便宜模型有偶发 502 突发(数秒级 burst,代理坑排除后仍偶发):worker→模型出口必须带重试/超时/幂等(AgentScope
max_retries默认 3,实测自动重试生效)——印证 §2 对外部调用的容错要求。 - 【取证】2.0 是对仅几个月大的 1.x 的破坏式重写:
ReActAgent类没了(统一为Agent);模型要OpenAICredential对象(base_url在 credential 上,非 1.x 的client_args);hooks→middleware;memory 独立模块、rag/evaluate/tts/realtime 被临时移除/降级。凭记忆/旧博客写 2.0 代码必炸,锁版本盯 changelog。 - 【实测】reasoning 模型(deepseek-v4-flash):小
max_tokens会被 reasoning 烧光、content 空;MiniMax-M2则直出内容。基准测试已用「净本地开销 + token 膨胀」绕开 reasoning 方差。 - 【取证·未亲测·须复检】影响本任务的 OPEN bug(GitHub
agentscope-ai/agentscope,2026-06-13 快照):#1722 单进程不可横扩、#1868 多实例重复执行、#1860 流式零块时核心循环 None 崩、#1624 压缩摘要不落 Redis。正式上多机/长会话前请逐条复检是否已在 2.0.2+ 修复。
5. 早期采用者风险与版本锁(verdict 输入)
- 【取证】版本时间线:v2.0.0=2026-05-25、v2.0.1=2026-06-05(今天起算 ~8 天);PyPI 分类仍是
Development Status :: 4 - Beta(README 却宣称 production-ready——信 Beta 分类);11 天内 ~25 个 PR,含破坏性改名,API 面高频抖动。 - 【取证】仓库迁移:
modelscope/agentscope→github.com/agentscope-ai/agentscope;文档用docs.agentscope.io/v2(旧doc.agentscope.io是 1.0)。 - 【实测】退出成本确低:我们这套 = 普通 Python +
AgentState字典 + 薄 worker 编排;换框架只动 worker 内部,§3 契约不变 → 印证 §5-A「风险敞口有界」。 - 【实测】装配成本极低:
uv pip install agentscope==2.0.1解析 87 包 5.82s、装机 ~9s;+[service,storage]5 包再 4s。
6. 给创始人 / 主 agent 的建议(可执行)
- 继续 AgentScope 2.x,锁死 2.0.1,建 changelog/issue 盯防清单(尤其 #1722/#1868)。
- L1 维持 §3 裸通路(本测佐证其更省更简),框架从 L2 起重度介入。
- L3 多机多 worker 暂缓押注:framework 原生 serving 多 worker 未成熟;若近期就要多机,先用本 spike 的
RedisMessageBus自拼骨架(原语可用),或等 2.0.2+ 修 #1868 后复测。 - 把本 spike 的「代理旁路 / 容错重试 / worker 边界契约」沉淀进
.agents/,避免主弧再踩。 - 再评闸门(任一触即重审):#1868 等多 worker bug 半年内不修、或 2.0.x 持续破坏式抖动伤及我们 worker、或许可/生态出现 §5-①②③ 情形。
附:复现环境与版本(锁)
- 机器:Apple M1 Pro 32GB · macOS 25.5 · Python 3.12.13 · uv 0.4.30。
- 模型出口:new-api 网关
http://100.64.0.8:3000/v1(OpenAI 兼容,Tailscale),模型deepseek-v4-flash(L1)。 - 锁版本(
requirements.lock全量):agentscope==2.0.1·openai==2.41.1·redis==8.0.0(py)/redis-server v8.8.0·fastapi==0.136.3·pydantic==2.13.4·httpx==0.28.1。 - 产物:
artifacts/gate{1,2,3}_*.json(本机实测原始数据)。