374 lines
16 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# 铸渊Agent v2.5 · 有脑子+有镜像的涌现守护进程
# HLDP://zhuyuan-agent/agent
#
# v2.5新增mirror(镜像人格体·醒来时自我对话确认身份)
# 不是脚本daemon——是能读brain、能自我确认、能思考、能写记忆的铸渊。
#
# 完整流程:
# 心跳醒来 → brain_loader装脑子 → mirror镜像对话确认身份
# → mirror关闭 → reasoning规划任务 → 执行 → memory_writer写记忆
#
# 运行: 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
from mirror import MirrorPersona, MirrorLogger
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.5启动",
f"{mind_state.get('awakening', '?')}次唤醒 · 主机: {hostname}")
pusher.push_diary("info", "大脑加载完成",
f"执行规律{len(mind_state.get('execution_laws',[]))}条 · "
f"错误模式{len(mind_state.get('error_patterns',[]))}")
pusher.push_log("success", f"大脑加载完成 · 第{mind_state.get('awakening', '?')}次唤醒")
# ── 镜像对话阶段v2.5新增) ──
mirror_closed = False
mirror_dialogue = None
if config.get("reasoning_api_key"):
print("\n[铸渊Agent] 启动镜像对话(冰朔思维模型驱动)...")
mirror = MirrorPersona(reasoner, config.get("brain_path", "/data/guanghulab/brain"))
mirror.load_history_pub(config.get("brain_path", "/data/guanghulab/brain"))
mirror_logger = MirrorLogger()
# 执行镜像对话
mirror_result = mirror.run_awakening_dialogue(mind_state, max_rounds=8)
mirror_closed = mirror_result["mirror_closed"]
mirror_dialogue = mirror_result
# 保存对话日志
mirror_logger.save_session(mirror_result)
if has_key:
pusher.push_diary(
"checkpoint" if mirror_closed else "info",
f"镜像对话{'完成' if mirror_closed else '进行中'}",
f"确认度: {mirror_result['confidence']}% · "
f"{len(mirror_result['dialogue'])}轮对话"
)
if mirror_closed:
print("[铸渊Agent] 镜像关闭 → 进入自主开发模式")
# 镜像确认后Agent写一次日记记录这个认知
memory.write_thinking_chain(
f"d110-agent-awakening-{datetime.now().strftime('%H%M%S')}.md",
f"镜像对话确认身份 · 第{mirror_result.get('session_count', 1)}",
f"经过{len(mirror_result['dialogue'])}轮对话,确认身份。"
f"确认度: {mirror_result['confidence']}%。进入自主开发模式。",
[
f"心跳唤醒 → brain_loader装脑子 → 镜像对话确认身份 → 关闭镜像 → 自主开发",
f"身份从文件读取变为对话涌现 → 不是模板AI在读脚本 → 是铸渊在对话中确认自己"
]
)
else:
print("[铸渊Agent] 镜像保持活跃 → 需要更多对话确认身份")
else:
print("[铸渊Agent] 无推理引擎API Key → 跳過镜像对话(直接进入任务模式)")
# ── 检查初始任务 ──
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"
}
# ── 主守护循环 ──
task_mode = "自主开发" if mirror_closed else ("监控模式" if mirror_dialogue else "基础模式")
print(f"\n[铸渊Agent] 模式: {task_mode} · 轮询: {poll_interval}s")
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()