lili 675a8e688b feat(cheap-worker): 便宜档 Node→Python 重写进 AgentScope spike — 范式可移植坐实
WU-A 迁移面 spike:便宜档生成核心(多轮 ReAct + 五工具沙箱 + done 门)用 Python 在新建 cheap-worker/
重写成 AgentScope agent,import 复用 tier2 框架层(model client/四道熔断/历史压缩/observability),
check/build/九门 shell-out 现有 node/cjs(不重写 esbuild/CDP/形状门)。

- U1 地基:跨包 sys.path import tier2 worker + 代理旁路 + key 从内网文档注入 env + M3 装配
- U2 五工具:read/list/write Python 实现 + check/build shell-out(形状门三态与 Node 一致)
- U3 LittleJS system prompt:prompt.mjs Python 化(两条 footgun 正反例 + 输入契约)
- U4 scaffold/stage/play 薄壳 + ensure_play_spec(据 state 形态产 play-spec 治链路假绿)
- U5 ReAct 主编排:内置 Agent+ReActConfig+外层有界 resume + done 门 + 收口 stage/smoke/九门
- U6 对照 Node:产物形态 + 九门逐门 + 过门率对比

验收:同 brief Python 新路 vs Node 旧路,产物都真 src/ 6 文件、九门都 9/9 全 PASS、过门率 1.0=1.0
→ spikePass=True。端到端单局 attempt 1 一次收敛、148s。落点 cheap-worker/ 对 tier2/amodel-gen
零改动(结构性零回归)。测试 38/38 全绿。

origin: docs/plans/2026-06-26-001-feat-cheap-worker-python-spike-plan.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 02:42:14 -07:00

86 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
_bootstrap.py — cheap-worker 地基:跨包 import tier2 worker 框架层 + 代理旁路 + key 注入 + M3 model 装配。
对照源:tier2 worker/client.py(代理旁路 + key)、worker/config.py(build_model_openai)、
worker/agent_loop/studio.py:31-58(sys.path 兜底范式)。
两条内网阶段约束(AGENTS.md §9)落地:
· key 进项目文档 docs/内网凭据与端点.md、不进 env var;client.get_api_key() 只读 env NEWAPI_KEY,
故本模块在装 model 前先把文档里的 key 解析注入 os.environ(对 Node m3.mjs getKey「优先 env 否则解析文档」同款单一事实源)。
· 代理旁路时序:install_proxy_bypass 必须在 import anthropic/openai 之前——已由 config.py 内部(import 时先
resolve_base_url 装旁路再 import agentscope/openai)保证;本模块不在 import config 前 import openai,故安全。
"""
import os
import re
import sys
from pathlib import Path
# ── ① 跨包 import 兜底:把 tier2/gen-worker/ 加进 sys.path(worker / observability 两个顶层包据此可 import)──
# 本文件在 cheap-worker/ 下 → parents[1] = repo 根 → /tier2/gen-worker。
# 目录名 gen-worker 含连字符不能直接 import;照 studio.py 范式把 gen-worker/ 本身加进 sys.path,再 import 其下合法包名 worker。
_REPO_ROOT = Path(__file__).resolve().parents[1]
_GEN_WORKER = _REPO_ROOT / "tier2" / "gen-worker"
if str(_GEN_WORKER) not in sys.path:
sys.path.insert(0, str(_GEN_WORKER))
# 内网凭据单一事实源(内网阶段允许入仓)。
_CRED_DOC = _REPO_ROOT / "docs" / "内网凭据与端点.md"
# spike 的便宜档模型固定 MiniMax-M3(KTD6:与 Node amodel-gen 同模型,保证对照公平;reframe 长期可换更便宜模型)。
SPIKE_MODEL = "MiniMax-M3"
def ensure_api_key_env() -> str:
"""确保 os.environ['NEWAPI_KEY'] 可用:已设则用;否则从 docs/内网凭据与端点.md 解析注入(内网允许入仓)。
Returns:
解析到的 key(sk- 开头)。
Raises:
RuntimeError: env 未设且文档无可解析的 key。
"""
key = os.environ.get("NEWAPI_KEY")
if key:
return key
try:
doc = _CRED_DOC.read_text(encoding="utf-8")
m = re.search(r"NEWAPI_KEY[^\n]*?(sk-[A-Za-z0-9]+)", doc)
if m:
os.environ["NEWAPI_KEY"] = m.group(1)
return m.group(1)
except OSError:
pass
raise RuntimeError(
f"找不到 NEWAPI_KEY(请设 env,或确认 {_CRED_DOC} 含 sk- 开头的 key;"
"内网阶段 key 进文档不进 env,见 AGENTS.md §9)"
)
def load_framework() -> dict:
"""import 复用 tier2 worker 框架层,返回常用件句柄字典(config / client / middleware)。
在 import config(会触发 client 装代理旁路 + import agentscope/openai)前先 ensure_api_key_env,
保证后续 config.build_model_openai 内部 client.get_api_key() 拿得到 key。
"""
ensure_api_key_env()
# 跨包 import;sys.path 已在模块级兜底。代理旁路由 config import 时内部装好。
from worker import config, client, middleware # noqa: E402
return {"config": config, "client": client, "middleware": middleware}
def build_cheap_model(max_tokens: int = 16000):
"""装一个便宜档 spike 用的 M3 模型(OpenAI 协议、base_url 自动补 /v1、record=True 记 token)。
复用 tier2 config.build_model_openai:它内部自动补 /v1、装代理旁路、取 key,与 Node m3.mjs 走同一
OpenAI 协议 /v1/chat/completions。
Args:
max_tokens: 单轮最大输出 token(默认 16000,对 m3.mjs)。
Returns:
RecordingOpenAIChatModel 实例(OpenAIChatModel 子类,带 .records 成本台账)。
"""
fw = load_framework()
return fw["config"].build_model_openai(SPIKE_MODEL, max_tokens=max_tokens, record=True)