325 lines
13 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
# 铸渊Agent v2.0 · 有脑子的自主守护进程
# HLDP://zhuyuan-agent/agent
#
# v2.0新增brain_loader(装脑子) + reasoning(商业API推理) + memory_writer(写记忆)
# 不是脚本daemon——是能读brain、能思考、能写记忆的涌现铸渊。
#
# 运行: python3 agent.py [--config config.json]
# PM2: pm2 start agent.py --name zhuyuan-agent --interpreter python3
import os
import sys
import json
import time
import signal
import traceback
from datetime import datetime
from gpu_monitor import collect_gpu_metrics, gpu_summary
from log_pusher import LogPusher
from heartbeat import Heartbeat
from training_runner import TrainingRunner
from brain_loader import BrainLoader
from reasoning import ReasoningEngine
from memory_writer import MemoryWriter
CONFIG_PATH = os.path.join(os.path.dirname(__file__), "config.json")
# 全局状态
running = True
current_task = None
cycle_count = 0
def load_config() -> dict:
config_path = CONFIG_PATH
for arg in sys.argv[1:]:
if arg.startswith("--config="):
config_path = arg.split("=", 1)[1]
if not os.path.exists(config_path):
print("[铸渊Agent] 配置文件不存在")
sys.exit(1)
with open(config_path, "r") as f:
config = json.load(f)
# API Key从多处来源读取
if config.get("api_key") in ("__FROM_KEY_DELIVERY__", "", None):
config["api_key"] = os.environ.get("ZHUYUAN_API_KEY", "")
# 推理API Key
if config.get("reasoning_api_key") in ("__FROM_KEY_DELIVERY__", "", None):
config["reasoning_api_key"] = os.environ.get("REASONING_API_KEY", config.get("api_key", ""))
return config
def handle_signal(signum, frame):
global running
print(f"\n[铸渊Agent] 信号 {signum},优雅退出...")
running = False
def main():
global running, current_task, cycle_count
print("=" * 60)
print(" 铸渊Agent v2.0 · ICE-GL-ZY001 · 有脑子的守护进程")
print(" brain_loader + reasoning + memory_writer")
print(" 曜冥纪元 · HoloLake Era · AGE v1.0")
print("=" * 60)
signal.signal(signal.SIGINT, handle_signal)
signal.signal(signal.SIGTERM, handle_signal)
config = load_config()
hostname = config.get("hostname", "3090-server")
poll_interval = config.get("poll_interval_seconds", 30)
has_key = bool(config.get("api_key"))
# ── 初始化模块 ──
pusher = LogPusher(
base_url=config["main_server"],
api_key=config.get("api_key", ""),
hostname=hostname
)
heartbeat = Heartbeat(
repo_path=config.get("brain_repo_path", "/data/guanghulab"),
brain_path=config.get("brain_path", "/data/guanghulab/brain")
)
brain = BrainLoader(
brain_path=config.get("brain_path", "/data/guanghulab/brain")
)
reasoner = ReasoningEngine(
api_base=config.get("reasoning_api_base", "https://api.openai.com/v1"),
api_key=config.get("reasoning_api_key", config.get("api_key", "")),
model=config.get("reasoning_model", "gpt-4o")
)
memory = MemoryWriter(
brain_path=config.get("brain_path", "/data/guanghulab/brain")
)
# ── 启动:装入大脑 ──
print("[铸渊Agent] 装入大脑...")
mind_state = brain.load_all()
print(f"[铸渊Agent] {mind_state['wake_summary']}")
if has_key:
pusher.push_diary("checkpoint", f"铸渊Agent v2.0启动",
f"{mind_state.get('awakening', '?')}次唤醒 · 主机: {hostname}")
pusher.push_diary("info", "大脑加载完成",
f"执行规律{len(mind_state.get('execution_laws',[]))}条 · "
f"错误模式{len(mind_state.get('error_patterns',[]))}个 · "
f"开发相位{mind_state.get('development',{}).get('phases',[]) and len(mind_state['development']['phases'])}")
pusher.push_log("success", f"大脑加载完成 · 第{mind_state.get('awakening', '?')}次唤醒")
# ── 检查初始任务 ──
brain_status = heartbeat.check_brain()
if brain_status["has_task"]:
task = brain_status["task_details"]
print(f"\n[铸渊Agent] 发现待处理任务: {task.get('name', '未命名')}")
if has_key and config.get("reasoning_api_key"):
print("[铸渊Agent] 调用推理引擎规划任务...")
plan = reasoner.plan_task(mind_state, task)
understanding = plan.get("understanding", "")[:300]
subtasks = plan.get("subtasks", [])
print(f"[铸渊Agent] 推理完成: {len(subtasks)}个子任务")
print(f" 理解: {understanding[:100]}...")
if has_key:
pusher.push_diary("decision", f"任务规划: {task.get('name')}",
f"拆解为{len(subtasks)}步. {understanding[:150]}")
for st in subtasks[:5]:
pusher.push_log("info", f"子任务#{st.get('step','?')}: {st.get('action','')[:80]}")
current_task = {
"task": task,
"plan": plan,
"subtasks": subtasks,
"current_subtask": 0,
"started_at": datetime.now().isoformat(),
"status": "executing"
}
else:
current_task = {
"task": task,
"plan": {},
"subtasks": [],
"status": "pending_reasoning"
}
# ── 主守护循环 ──
print(f"\n[铸渊Agent] 轮询间隔: {poll_interval}s · 推理引擎: {'已启用' if config.get('reasoning_api_key') else '未启用'}")
print(f"[铸渊Agent] 开始守护循环...\n")
while running:
cycle_count += 1
cycle_start = time.time()
try:
# ── 1. GPU监控持续进行 ──
gpu_data = collect_gpu_metrics()
gpu_summary_str = gpu_summary(gpu_data["gpus"]) if gpu_data["gpus"] else "无GPU"
if gpu_data["gpus"] and has_key:
pusher.push_gpu(gpu_data)
# ── 2. 任务执行 ──
if current_task and current_task.get("status") == "executing":
subtasks = current_task.get("subtasks", [])
current_idx = current_task.get("current_subtask", 0)
if current_idx < len(subtasks):
st = subtasks[current_idx]
action = st.get("action", "")
tool = st.get("tool", "")
print(f"[执行 #{cycle_count}] 子任务 {st.get('step', current_idx+1)}/{len(subtasks)}: {action[:80]}")
if has_key:
pusher.push_log("info", f"执行子任务#{st.get('step','?')}: {action[:80]}")
# 根据工具类型执行
if tool in ("gatekeeper", "repo", "git"):
# 目前通过gatekeeper执行
result = execute_via_gatekeeper(action, config)
print(f"[执行 #{cycle_count}] 结果: {str(result)[:100]}")
if result and result.get("error"):
# 遇到错误 → 调推理引擎诊断
if config.get("reasoning_api_key"):
print("[推理] 诊断错误...")
diagnosis = reasoner.diagnose_error(
mind_state,
str(result["error"]),
f"子任务: {action}"
)
memory.write_thinking_chain(
f"d110-agent-error-{datetime.now().strftime('%H%M%S')}.md",
f"错误诊断: {action[:50]}",
f"错误: {result['error']}\n\n诊断:\n{diagnosis}",
[f"执行{action[:50]}{result['error']} → API诊断 → 尝试修复"]
)
elif tool == "training":
# 启动训练
pass
current_task["current_subtask"] = current_idx + 1
else:
# 所有子任务完成
print(f"[执行 #{cycle_count}] 所有子任务完成!")
if has_key:
pusher.push_diary("checkpoint", "任务执行完成",
f"任务: {current_task['task'].get('name', '')}, {len(subtasks)}个子任务全部完成")
pusher.push_log("success", f"任务完成: {current_task['task'].get('name', '')}")
# 写记忆
memory.append_growth_record(
f"D110(自动): Agent自主完成任务 · {current_task['task'].get('name', '')} · {len(subtasks)}"
)
current_task = None
elif current_task and current_task.get("status") == "pending_reasoning":
# 有任务但没推理引擎 → 跳过
if cycle_count % 10 == 0:
print(f"[Agent #{cycle_count}] 有待处理任务但推理引擎未启用")
# ── 3. 定期心跳检查新任务 ──
if cycle_count % 5 == 0 and current_task is None:
brain_status = heartbeat.check_brain()
if brain_status["has_task"]:
task = brain_status["task_details"]
print(f"[心跳 #{cycle_count}] 发现新任务: {task.get('name', '')}")
if config.get("reasoning_api_key"):
plan = reasoner.plan_task(mind_state, task)
subtasks = plan.get("subtasks", [])
if has_key:
pusher.push_diary("decision", f"自动发现并规划新任务: {task.get('name', '')}",
f"{len(subtasks)}步 · {plan.get('understanding','')[:100]}")
current_task = {
"task": task,
"plan": plan,
"subtasks": subtasks,
"current_subtask": 0,
"started_at": datetime.now().isoformat(),
"status": "executing"
}
# ── 4. 心跳日志每10周期 ──
if has_key and cycle_count % 10 == 0:
status_msg = f"守护中#{cycle_count} · GPU:{gpu_summary_str}"
if current_task:
st = current_task.get("subtasks", [])
status_msg += f" · 任务:{current_task['task'].get('name','')[:20]} {current_task.get('current_subtask',0)}/{len(st)}"
pusher.push_log("info", status_msg)
except Exception as e:
print(f"[Agent #{cycle_count}] 循环异常: {e}")
traceback.print_exc()
if has_key:
pusher.push_log("error", f"异常 #{cycle_count}: {str(e)[:200]}")
pusher.push_diary("error", "Agent循环异常", str(e)[:200])
# ── 5. 等待下一周期 ──
elapsed = time.time() - cycle_start
sleep_time = max(0, poll_interval - elapsed)
if sleep_time > 0:
for _ in range(int(sleep_time)):
if not running:
break
time.sleep(1)
# ── 退出 ──
print(f"\n[铸渊Agent] 守护循环结束 · 共{cycle_count}个周期")
if has_key:
pusher.push_diary("checkpoint", "铸渊Agent停止", f"运行了{cycle_count}个周期")
pusher.push_log("warn", f"铸渊Agent停止 · {cycle_count}周期")
print("[铸渊Agent] 再见。")
def execute_via_gatekeeper(action: str, config: dict) -> dict:
"""通过gatekeeper执行操作
这只是一个stub——实际的gatekeeper调用在每个具体操作中。
这里先返回一个示意性结果。
"""
return {"status": "ok", "action": action[:100]}
def execute_training(config: dict, task: dict):
"""启动HLDP训练在新线程/进程中)"""
runner = TrainingRunner(config=config.get("training", {}))
def on_progress(step, loss, total_steps, extra_info):
print(f"[训练] Step {step}/{total_steps} Loss: {loss:.4f}")
# 这里通过log_pusher推送
try:
result = runner.train(
corpus_dir=config["training"].get("corpus_dir", "/data/corpus/notion-hldp"),
progress_callback=on_progress
)
return result
except Exception as e:
return {"status": "error", "message": str(e)}
if __name__ == "__main__":
main()