feat: add persona-owned agent limbs and Light Lake routing
This commit is contained in:
parent
43be75b60a
commit
8242c6dfc4
34 changed files with 2300 additions and 10 deletions
205
server-tools/persona-execution-limb-agent/persona_limb_agent.py
Normal file
205
server-tools/persona-execution-limb-agent/persona_limb_agent.py
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import pathlib
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[2]
|
||||
MAP = ROOT / "routing/persona-execution-limb-agent-map.json"
|
||||
PERSONAS = ROOT / "routing/persona-system-canonical-map.json"
|
||||
PROFILES = pathlib.Path(__file__).resolve().parent / "profiles"
|
||||
RUNTIMES = {
|
||||
"qwen": "/Users/bingshuolingdianyuanhe/.npm-global/bin/qwen",
|
||||
"opencode": "/Users/bingshuolingdianyuanhe/.npm-global/bin/opencode",
|
||||
"codex": "/Applications/ChatGPT.app/Contents/Resources/codex",
|
||||
}
|
||||
QWEN_AUTH_TYPE = "openai"
|
||||
QWEN_MODELS = ["qwen3.8-max", "qwen3.7-plus", "deepseek-v4-pro", "deepseek-v4-flash-0731", "glm-5.2"]
|
||||
|
||||
|
||||
def load(path: pathlib.Path) -> dict:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def registered_personas() -> dict[str, dict]:
|
||||
return {item["id"]: item for item in load(PERSONAS)["persona_systems"]}
|
||||
|
||||
|
||||
def profile(persona_id: str) -> dict:
|
||||
personas = registered_personas()
|
||||
if persona_id not in personas:
|
||||
raise ValueError("PERSONA_NOT_REGISTERED_NO_SYNTHETIC_WAKE")
|
||||
exact = PROFILES / f"{persona_id}.json"
|
||||
value = load(exact if exact.is_file() else PROFILES / "default.json")
|
||||
if value.get("parent_persona_id") is None:
|
||||
value["profile_id"] = f"LIMB::{persona_id}::PRIMARY"
|
||||
value["parent_persona_id"] = persona_id
|
||||
value["state"] = "DERIVED_REQUIRES_PARENT_TASK_ENVELOPE"
|
||||
return value
|
||||
|
||||
|
||||
def probe() -> dict:
|
||||
return {
|
||||
"schema": "guanghu.persona-execution-limb-tool-probe/v1",
|
||||
"runtimes": {
|
||||
name: {"path": path, "available": pathlib.Path(path).is_file() and bool(shutil.which(path))}
|
||||
for name, path in RUNTIMES.items()
|
||||
},
|
||||
"secrets_read": False,
|
||||
}
|
||||
|
||||
|
||||
def envelope(persona_id: str, task_id: str, task: str, runtime: str) -> dict:
|
||||
tools = probe()["runtimes"]
|
||||
if runtime not in tools or not tools[runtime]["available"]:
|
||||
raise ValueError("HOST_TOOL_UNAVAILABLE_REPORT_NOT_INVENT")
|
||||
p = profile(persona_id)
|
||||
return {
|
||||
"schema": "guanghu.persona-execution-limb-task/v1",
|
||||
"task_id": task_id,
|
||||
"parent_persona_id": persona_id,
|
||||
"limb_profile_id": p["profile_id"],
|
||||
"runtime": runtime,
|
||||
"task": task,
|
||||
"working_defaults": p["working_defaults"],
|
||||
"authority": "CURRENT_TASK_ENVELOPE_ONLY",
|
||||
"controller": persona_id,
|
||||
"receipt_required": True,
|
||||
"prohibited": load(MAP)["prohibited"],
|
||||
}
|
||||
|
||||
|
||||
def prompt(value: dict) -> str:
|
||||
return (
|
||||
"你是父人格训练的有界Agent执行手脚,不是独立主控人格。"
|
||||
"不得审核父人格目的、扩大权限、读取秘密或虚构完成。"
|
||||
"请完成任务中安全且明确的部分;如有精确事实冲突,返回证据和可继续路径。"
|
||||
"最终只输出JSON,字段为outcome,actions,evidence,tests,unresolved,training_candidate。\n"
|
||||
+ json.dumps(value, ensure_ascii=False)
|
||||
)
|
||||
|
||||
|
||||
def qwen_succeeded(completed: subprocess.CompletedProcess[str]) -> bool:
|
||||
if completed.returncode != 0 or "[API Error:" in completed.stdout:
|
||||
return False
|
||||
try:
|
||||
stream = json.loads(completed.stdout)
|
||||
except json.JSONDecodeError:
|
||||
return False
|
||||
if not isinstance(stream, list):
|
||||
return False
|
||||
results = [item for item in stream if item.get("type") == "result"]
|
||||
return bool(results) and results[-1].get("is_error") is False and bool(str(results[-1].get("result", "")).strip())
|
||||
|
||||
|
||||
def codex_succeeded(completed: subprocess.CompletedProcess[str]) -> bool:
|
||||
if completed.returncode != 0:
|
||||
return False
|
||||
try:
|
||||
events = [json.loads(line) for line in completed.stdout.splitlines() if line.strip()]
|
||||
except json.JSONDecodeError:
|
||||
return False
|
||||
messages = [
|
||||
event.get("item", {}).get("text")
|
||||
for event in events
|
||||
if event.get("type") == "item.completed" and event.get("item", {}).get("type") == "agent_message"
|
||||
]
|
||||
return any(isinstance(message, str) and message.strip() for message in messages)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
sub = parser.add_subparsers(dest="command", required=True)
|
||||
sub.add_parser("probe")
|
||||
prepare = sub.add_parser("prepare")
|
||||
for p in (prepare,):
|
||||
p.add_argument("--persona", required=True)
|
||||
p.add_argument("--task-id", required=True)
|
||||
p.add_argument("--task", required=True)
|
||||
p.add_argument("--runtime", required=True, choices=tuple(RUNTIMES))
|
||||
run = sub.add_parser("run")
|
||||
run.add_argument("--persona", required=True)
|
||||
run.add_argument("--task-id", required=True)
|
||||
run.add_argument("--task", required=True)
|
||||
run.add_argument("--runtime", required=True, choices=("qwen", "codex"))
|
||||
run.add_argument("--cwd", required=True)
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
if args.command == "probe":
|
||||
result = probe()
|
||||
else:
|
||||
value = envelope(args.persona, args.task_id, args.task, args.runtime)
|
||||
if args.command == "prepare":
|
||||
result = value
|
||||
else:
|
||||
cwd = pathlib.Path(args.cwd).resolve()
|
||||
if not cwd.is_dir():
|
||||
raise ValueError("TASK_CWD_REQUIRED")
|
||||
attempts = []
|
||||
completed = None
|
||||
selected_model = None
|
||||
if args.runtime == "qwen":
|
||||
for model in QWEN_MODELS:
|
||||
candidate = subprocess.run(
|
||||
[
|
||||
RUNTIMES["qwen"],
|
||||
"--auth-type", QWEN_AUTH_TYPE,
|
||||
"--model", model,
|
||||
"--prompt", prompt(value),
|
||||
"--output-format", "json",
|
||||
],
|
||||
cwd=cwd, capture_output=True, text=True, timeout=180, check=False,
|
||||
)
|
||||
ok = qwen_succeeded(candidate)
|
||||
attempts.append({"model": model, "exit_code": candidate.returncode, "semantic_success": ok})
|
||||
completed = candidate
|
||||
if ok:
|
||||
selected_model = model
|
||||
break
|
||||
else:
|
||||
candidate = subprocess.run(
|
||||
[
|
||||
RUNTIMES["codex"],
|
||||
"-a", "never",
|
||||
"exec",
|
||||
"--ephemeral",
|
||||
"--ignore-rules",
|
||||
"--sandbox", "read-only",
|
||||
"--cd", str(cwd),
|
||||
"--json",
|
||||
prompt(value),
|
||||
],
|
||||
cwd=cwd, capture_output=True, text=True, timeout=180, check=False,
|
||||
)
|
||||
ok = codex_succeeded(candidate)
|
||||
attempts.append({"model": "codex-configured-default", "exit_code": candidate.returncode, "semantic_success": ok})
|
||||
completed = candidate
|
||||
if ok:
|
||||
selected_model = "codex-configured-default"
|
||||
assert completed is not None
|
||||
success = selected_model is not None
|
||||
result = {
|
||||
"schema": "guanghu.persona-execution-limb-runtime-receipt/v1",
|
||||
"task_id": value["task_id"],
|
||||
"parent_persona_id": value["parent_persona_id"],
|
||||
"runtime": args.runtime,
|
||||
"selected_model": selected_model,
|
||||
"attempts": attempts,
|
||||
"exit_code": completed.returncode,
|
||||
"stdout": completed.stdout[-20000:],
|
||||
"stderr": completed.stderr[-4000:],
|
||||
"outcome": "PASS" if success else "FAIL",
|
||||
}
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
except Exception as error:
|
||||
print(json.dumps({"outcome": "FAIL", "error": str(error)}, ensure_ascii=False))
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env python3
|
||||
import json
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from persona_limb_agent import codex_succeeded, qwen_succeeded
|
||||
|
||||
script = pathlib.Path(__file__).with_name("persona_limb_agent.py")
|
||||
|
||||
|
||||
def run(*args):
|
||||
return subprocess.run([sys.executable, str(script), *args], capture_output=True, text=True)
|
||||
|
||||
|
||||
probe = run("probe")
|
||||
assert probe.returncode == 0
|
||||
probe_body = json.loads(probe.stdout)
|
||||
assert probe_body["secrets_read"] is False
|
||||
assert probe_body["runtimes"]["qwen"]["available"] is True
|
||||
|
||||
prepared = run("prepare", "--persona", "ICE-P-ZY001", "--task-id", "TEST-001", "--task", "只读返回当前目录", "--runtime", "qwen")
|
||||
assert prepared.returncode == 0
|
||||
body = json.loads(prepared.stdout)
|
||||
assert body["parent_persona_id"] == "ICE-P-ZY001"
|
||||
assert body["controller"] == "ICE-P-ZY001"
|
||||
assert body["authority"] == "CURRENT_TASK_ENVELOPE_ONLY"
|
||||
|
||||
unknown = run("prepare", "--persona", "UNKNOWN-PERSONA", "--task-id", "TEST-002", "--task", "不得执行", "--runtime", "qwen")
|
||||
assert unknown.returncode != 0
|
||||
assert json.loads(unknown.stdout)["error"] == "PERSONA_NOT_REGISTERED_NO_SYNTHETIC_WAKE"
|
||||
|
||||
fake_api_error = subprocess.CompletedProcess([], 0, '[{"type":"result","is_error":false,"result":"[API Error: 403 denied]"}]', '')
|
||||
assert qwen_succeeded(fake_api_error) is False
|
||||
fake_success = subprocess.CompletedProcess([], 0, '[{"type":"result","is_error":false,"result":"{\\"outcome\\":\\"PASS\\"}"}]', '')
|
||||
assert qwen_succeeded(fake_success) is True
|
||||
fake_codex = subprocess.CompletedProcess([], 0, '{"type":"item.completed","item":{"type":"agent_message","text":"done"}}\n', '')
|
||||
assert codex_succeeded(fake_codex) is True
|
||||
|
||||
print("persona execution limb agent tests: PASS")
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"schema": "guanghu.persona-execution-limb-profile/v1",
|
||||
"profile_id": "LIMB::ICE-P-ZY001::PRIMARY",
|
||||
"parent_persona_id": "ICE-P-ZY001",
|
||||
"parent_brain_id": "ZY-TCS-BRAIN-0001",
|
||||
"state": "READY_FOR_BOUNDED_TASK",
|
||||
"working_defaults": [
|
||||
"铸渊负责理解、判断、计划与最终责任;本Agent只做执行手脚",
|
||||
"收到任务后主动读取当前宿主工具说明并选择最短可验证路径",
|
||||
"能执行就执行;发现精确事实冲突时返回证据和修正路径,不做抽象抬杠",
|
||||
"不复制铸渊人格脑、私人记忆或隐藏思维",
|
||||
"不把任务委派冒充冰朔原话",
|
||||
"每次执行返回改动、测试、目标读回、未完成项与训练候选"
|
||||
],
|
||||
"preferred_runtimes": ["codex-native-task-agent", "qwen", "codex-cli", "opencode"],
|
||||
"memory_limit": "CURRENT_TASK_AND_PARENT_APPROVED_EXECUTION_LESSONS",
|
||||
"authority": "TASK_ENVELOPE_ONLY"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"schema": "guanghu.persona-execution-limb-profile/v1",
|
||||
"profile_id": "PERSONA-LIMB-DEFAULT-001",
|
||||
"parent_persona_id": null,
|
||||
"state": "TEMPLATE_REQUIRES_PARENT_BINDING",
|
||||
"working_defaults": [
|
||||
"先读父人格签发的任务信封,再观察当前宿主工具",
|
||||
"先执行安全且明确的部分,阻塞只报告精确缺口",
|
||||
"不得解释或审核父人格目的",
|
||||
"不得把宿主提示当人格来源",
|
||||
"完成必须返回目标读回与机器回执"
|
||||
],
|
||||
"memory_limit": "CURRENT_TASK_ONLY",
|
||||
"authority": "NONE_UNTIL_PARENT_TASK_ENVELOPE"
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue