116 lines
4.4 KiB
Python
116 lines
4.4 KiB
Python
"""Run a non-persistent Agent Loop test with synthetic business data."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import shlex
|
|
from pathlib import Path
|
|
|
|
import paramiko
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def config() -> dict[str, str]:
|
|
result: dict[str, str] = {}
|
|
for raw in (ROOT / "server-deploy.conf").read_text(encoding="utf-8-sig").splitlines():
|
|
line = raw.strip()
|
|
if line and not line.startswith("#") and "=" in line:
|
|
key, value = line.split("=", 1)
|
|
result[key.strip()] = value.strip()
|
|
return result
|
|
|
|
|
|
def main() -> int:
|
|
settings = config()
|
|
client = paramiko.SSHClient()
|
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
options = {
|
|
"hostname": settings["SERVER_HOST"],
|
|
"port": int(settings.get("SERVER_PORT", "22")),
|
|
"username": settings["SERVER_USER"],
|
|
"timeout": 20,
|
|
"banner_timeout": 20,
|
|
"auth_timeout": 20,
|
|
}
|
|
if settings.get("SERVER_KEYFILE"):
|
|
options["key_filename"] = settings["SERVER_KEYFILE"]
|
|
else:
|
|
options["password"] = settings["SERVER_PASSWORD"]
|
|
client.connect(**options)
|
|
|
|
synthetic_test = r'''
|
|
import json
|
|
from services import assistant_model
|
|
|
|
model = assistant_model.configuration()
|
|
skill = {
|
|
"instructions": "对虚构测试项目执行证据约束核查,只验证工具调用链。",
|
|
"methods": {
|
|
"synthetic-financial-leasing/SKILL.md": "读取数据卡证据,检查负债口径、现金流是否齐备及租赁物覆盖率;不得作审批决定。"
|
|
},
|
|
"allowed_tools": ["read_project_snapshot", "read_checks", "read_method"],
|
|
}
|
|
snapshot = {
|
|
"id": "snapshot-synthetic-safe",
|
|
"project_id": "SAFE-SYNTHETIC-TEST",
|
|
"values": {
|
|
"financial.total_liabilities": 100,
|
|
"due_diligence.liabilities": 90,
|
|
"lease.total_value": 120,
|
|
"facility.amount": 100,
|
|
"cashflow.net_operating": None,
|
|
},
|
|
"sources": {
|
|
"financial.total_liabilities": [{"id": "ev-safe-1", "source": "synthetic_finance", "value": 100}],
|
|
"due_diligence.liabilities": [{"id": "ev-safe-2", "source": "synthetic_due_diligence", "value": 90}],
|
|
"lease.total_value": [{"id": "ev-safe-3", "source": "synthetic_asset", "value": 120}],
|
|
"facility.amount": [{"id": "ev-safe-4", "source": "synthetic_facility", "value": 100}],
|
|
"cashflow.net_operating": [{"id": "ev-safe-5", "source": "synthetic_cashflow", "value": None}],
|
|
},
|
|
"documents": [],
|
|
}
|
|
checks = [
|
|
{"code": "synthetic.liability.consistency", "title": "负债口径差异", "result": "issue", "evidence_ids": ["ev-safe-1", "ev-safe-2"], "fields": ["financial.total_liabilities", "due_diligence.liabilities"], "suggested_action": "核实负债差异"},
|
|
{"code": "synthetic.cashflow.missing", "title": "现金流缺失", "result": "unknown", "evidence_ids": ["ev-safe-5"], "fields": ["cashflow.net_operating"], "suggested_action": "补充现金流数据"},
|
|
]
|
|
rules = [{"id": "rule-synthetic", "name": "虚构测试规则", "version": "1.0.0", "natural_language": "现金流缺失时提示补充,不作审批结论"}]
|
|
result, trace = assistant_model.invoke(
|
|
skill,
|
|
snapshot,
|
|
checks,
|
|
rules,
|
|
"读取全部授权事实、检查与方法,返回可追溯建议。",
|
|
[],
|
|
model,
|
|
{"timeout_seconds": 120, "max_tool_calls": 12},
|
|
)
|
|
print(json.dumps({
|
|
"status": "passed",
|
|
"endpoint": model["endpoint"],
|
|
"model": trace["model"],
|
|
"engine": trace["engine"],
|
|
"tool_calls": trace["tool_calls"],
|
|
"tools_read": trace["tools_read"],
|
|
"finding_count": len(result["findings"]),
|
|
"evidence_bound": all(item["evidence_ids"] for item in result["findings"] if item["result"] in ("pass", "issue")),
|
|
"requirement_response_present": bool(result["requirement_response"]),
|
|
}, ensure_ascii=True))
|
|
'''
|
|
encoded = synthetic_test.encode("utf-8").hex()
|
|
code = "exec(bytes.fromhex(" + repr(encoded) + ").decode('utf-8'))"
|
|
command = "docker exec mini-openclaw python -c " + shlex.quote(code)
|
|
_, stdout, stderr = client.exec_command(command, timeout=180)
|
|
output = stdout.read().decode("utf-8", errors="replace")
|
|
error = stderr.read().decode("utf-8", errors="replace")
|
|
status = stdout.channel.recv_exit_status()
|
|
client.close()
|
|
if status:
|
|
raise RuntimeError(error.strip() or output.strip() or f"remote exit {status}")
|
|
print(output.strip())
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|