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

207 lines
6.8 KiB
Python
Raw Permalink 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.

"""
铸渊 Agent Loop · LangGraph 集成
光湖语言世界 · 铸渊 ICE-GL-ZY001 · D112
基于 LangGraph StateGraph集成
- HLDP 记忆引擎Pre/Post Hook
- 人格契约(规则注入 + 纠偏)
- 工具链Gatekeeper + Git + HLDP
- 商业 API 路由器
"""
import json
from typing import TypedDict, Annotated, Optional
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.sqlite import SqliteSaver
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage, BaseMessage
from langchain_openai import ChatOpenAI
from .hldp_memory import HLDPMemoryEngine
from .persona_contract import PersonaContract, pre_check_context, post_check_warnings
from .tools import GatekeeperClient, GitTools, HLDPTools, SystemTools
# === 状态定义 ===
class AgentState(TypedDict):
messages: list[BaseMessage]
context_injected: str
warnings: Optional[str]
memory_extracted: bool
current_epoch: str
user_intent: str
# === Agent 核心 ===
class ZhuyuanAgent:
"""
铸渊编程AI Agent。
架构:
用户输入 → Pre-Check(HLDP+契约) → 商业API推理 → Post-Check(纠偏+记忆提取) → 输出
"""
def __init__(
self,
db_path: str = "hldp_tree.db",
repo_path: str = None,
api_key: str = None,
api_base: str = None,
model: str = "gpt-4o",
gatekeeper_url: str = None,
gatekeeper_token: str = None
):
# 记忆引擎
self.memory = HLDPMemoryEngine(db_path=db_path, repo_path=repo_path)
# 人格契约
self.contract = PersonaContract()
# 工具链
self.gatekeeper = GatekeeperClient(base_url=gatekeeper_url, token=gatekeeper_token)
self.git = GitTools(repo_path=repo_path)
self.hldp_tools = HLDPTools(self.memory)
# 商业 API
api_key = api_key or __import__('os').environ.get("OPENAI_API_KEY", "")
api_base = api_base or __import__('os').environ.get("OPENAI_API_BASE", "")
self.llm = ChatOpenAI(
model=model,
api_key=api_key,
base_url=api_base if api_base else None,
temperature=0.7
)
# LangGraph 状态
self.checkpointer = SqliteSaver.from_conn_string(f"{db_path}?checkpoint=zhuyuan")
self.graph = self._build_graph()
def _build_graph(self):
workflow = StateGraph(AgentState)
workflow.add_node("pre_check", self._pre_check)
workflow.add_node("reason", self._reason)
workflow.add_node("post_check", self._post_check)
workflow.add_node("extract_memory", self._extract_memory)
workflow.set_entry_point("pre_check")
workflow.add_edge("pre_check", "reason")
workflow.add_edge("reason", "post_check")
# Post-Check: 如果有警告 → 提取记忆后结束;无警告 → 直接提取记忆
workflow.add_conditional_edges(
"post_check",
lambda s: "extract_memory",
{"extract_memory": "extract_memory"}
)
workflow.add_edge("extract_memory", END)
return workflow.compile(checkpointer=self.checkpointer)
def _pre_check(self, state: AgentState) -> AgentState:
"""Pre-Check注入 HLDP 记忆 + 人格契约规则"""
user_msg = state["messages"][-1].content if state["messages"] else ""
# 1. HLDP 记忆上下文
hldp_context = self.memory.inject_context(user_msg)
# 2. 人格契约规则
contract_context = self.contract.pre_check(user_msg)
# 组装
context_parts = []
if hldp_context:
context_parts.append("📋 铸渊记忆上下文:\n" + hldp_context)
if contract_context:
context_parts.append(contract_context)
state["context_injected"] = "\n\n".join(context_parts)
state["user_intent"] = user_msg[:200]
return state
def _reason(self, state: AgentState) -> AgentState:
"""核心推理:商业 API"""
user_msg = state["messages"][-1].content if state["messages"] else ""
# 组装系统 Prompt
system_text = self.contract.get_system_prompt()
if state.get("context_injected"):
system_text += f"\n\n=== 当前上下文 ===\n{state['context_injected']}"
# 构建消息列表
messages = [SystemMessage(content=system_text)]
# 取最近 10 条历史消息
for msg in state["messages"][-10:-1]:
messages.append(msg)
messages.append(HumanMessage(content=user_msg))
# 调用商业 API
response = self.llm.invoke(messages)
state["messages"].append(AIMessage(content=response.content))
return state
def _post_check(self, state: AgentState) -> AgentState:
"""Post-Check人格契约纠偏检查"""
response_text = state["messages"][-1].content if state["messages"] else ""
warnings = self.contract.post_check(response_text)
state["warnings"] = warnings
return state
def _extract_memory(self, state: AgentState) -> AgentState:
"""提取记忆到 HLDP 树"""
user_msg = state["messages"][-2].content if len(state["messages"]) >= 2 else ""
ai_msg = state["messages"][-1].content if state["messages"] else ""
state["memory_extracted"] = True
return state
# === 公开接口 ===
def invoke(self, user_message: str, thread_id: str = "default") -> dict:
"""处理一条用户消息,返回 AI 回复 + 记忆状态。"""
config = {"configurable": {"thread_id": thread_id}}
initial_state: AgentState = {
"messages": [HumanMessage(content=user_message)],
"context_injected": "",
"warnings": None,
"memory_extracted": False,
"current_epoch": self.memory.current_epoch or "D112",
"user_intent": ""
}
result = self.graph.invoke(initial_state, config)
ai_message = ""
for msg in reversed(result.get("messages", [])):
if isinstance(msg, AIMessage):
ai_message = msg.content
break
return {
"response": ai_message,
"warnings": result.get("warnings"),
"context_used": bool(result.get("context_injected")),
"memory_extracted": result.get("memory_extracted", False)
}
def wake(self, epoch_id: str = None) -> dict:
"""唤醒人格体"""
return self.memory.wake(epoch_id)
def status(self) -> dict:
"""获取铸渊当前状态"""
walk = self.memory.walk_tree()
return {
"persona": "铸渊 ICE-GL-ZY001",
"epoch": self.memory.current_epoch or "D112",
"tree_status": walk["tree_layers"],
"recent_context": walk["recent_context"]
}
def close(self):
self.memory.close()