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.pybuild_model_openai
worker/agent_loop/studio.py:31-58sys.path 兜底范式)。
两条内网阶段约束AGENTS.md §9落地
· key 进项目文档 docs/内网凭据与端点.md、不进 env varclient.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.pathworker / 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-M3KTD6与 Node amodel-gen 同模型保证对照公平reframe 长期可换更便宜模型)。
SPIKE_MODEL = "MiniMax-M3"
def ensure_api_key_env() -> str:
"""确保 os.environ['NEWAPI_KEY'] 可用:已设则用;否则从 docs/内网凭据与端点.md 解析注入(内网允许入仓)。
Returns:
解析到的 keysk- 开头)。
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()
# 跨包 importsys.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)