Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

374 lines
16 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.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()