374 lines
16 KiB
Python
374 lines
16 KiB
Python
#!/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()
|