xiaowei-system/scripts/kanban-2b-worker.py

98 lines
4.0 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.

#!/usr/bin/env python3
"""
kanban-2b-worker.py — 分环节 kanban 代码 worker牧尘 2026-09-10 定案)
=====================================================================
环节拆分2B 不做协议,只做执行。本脚本由 opencode profile worker 首轮运行:
环节1 读任务 :从 env/kanban 读任务描述(确定性代码)
环节2 执行 :调 opencode CLI + llama-local/minicpm5-2b 写代码(唯一 LLM 点)
环节3 验证 :运行产物,检查 exit code + 输出(确定性代码)
环节4 输出结果 :打印 JSON由 worker agent 据此 kanban_complete/block
用法worker 内):
python3 ~/.hermes/scripts/kanban-2b-worker.py
"""
import json
import os
import subprocess
import sys
import time
OPENCODE = "/home/muc/nodejs/node-v24.16.0-linux-x64/bin/opencode"
# 执行模型链本地2B(0成本) → 智谱glm-4-flash(免费) → 商汤deepseek(免费)
# 牧尘 2026-09-10opencode 加智谱也加本地 2B
MODEL_CHAIN = [
"llama-local/minicpm5-2b", # 本地 0 成本(主力)
"zhipu/glm-4-flash", # 智谱免费(代码倾向强)
"sensenova/deepseek-v4-flash", # 商汤免费
]
HERMES = "/home/muc/bin/hermes"
def sh(cmd, timeout=120, cwd=None, max_out=3000):
try:
r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, cwd=cwd,
env=dict(os.environ, PATH="/home/muc/nodejs/node-v24.16.0-linux-x64/bin:" + os.environ.get("PATH", "")))
return r.returncode, (r.stdout + r.stderr)[:max_out]
except Exception as e:
return -1, str(e)[:500]
def get_task():
"""从 kanban CLI 读任务详情"""
task_id = os.environ.get("HERMES_KANBAN_TASK", "")
if not task_id:
# fallback: 尝试从 argv
task_id = sys.argv[1] if len(sys.argv) > 1 else ""
if not task_id:
return None, "no HERMES_KANBAN_TASK env"
rc, out = sh([HERMES, "kanban", "show", task_id, "--json"], max_out=20000)
if rc != 0:
return None, f"kanban show failed: {out[:300]}"
try:
d = json.loads(out)
t = d.get("task") or d # kanban show --json 返回 {task:{...}}
title = t.get("title") or t.get("summary") or ""
body = t.get("body") or ""
ws = t.get("workspace_path") or os.environ.get("HERMES_KANBAN_WORKSPACE") or ""
return {"id": task_id, "title": title, "body": body, "workspace": ws}, None
except Exception as e:
return None, f"parse failed: {e}"
def run_opencode(task_desc, workspace):
"""调 opencode CLI + 2B 执行(唯一 LLM 环节)"""
if not workspace or not os.path.isdir(workspace):
workspace = "/tmp"
prompt = (
f"完成以下任务。写代码 → 实际运行验证 → 报告结果。\n"
f"任务: {task_desc[:1500]}\n"
"要求: 1) 在当前目录创建所需文件 2) 实际运行验证 3) 最后输出一行 JSON: "
'{"files": ["..."], "verified": true/false, "output": "运行输出摘要"}'
)
# 模型链逐个试本地2B → 智谱 → 商汤(全免费;超时/失败下一个)
last_err = "no model attempted"
for model in MODEL_CHAIN:
t0 = time.time()
rc, out = sh([OPENCODE, "run", "--model", model, prompt], timeout=280, cwd=workspace)
if rc == 0 and out.strip():
return {"model": model, "elapsed_s": round(time.time() - t0), "raw": out[-2000:]}
last_err = out[-300:]
return {"error": f"opencode all models failed: {last_err}"}
def main():
task, err = get_task()
if err:
print(json.dumps({"ok": False, "stage": "read_task", "error": err}, ensure_ascii=False))
return
print(json.dumps({"ok": True, "stage": "read_task", "task_id": task["id"]}, ensure_ascii=False))
desc = task["title"] + ("\n" + task["body"] if task.get("body") else "")
result = run_opencode(desc, task.get("workspace", ""))
print("=== OPENCODE_RESULT ===")
print(json.dumps(result, ensure_ascii=False)[:2500])
if __name__ == "__main__":
main()