dark 14e297a488 feat: 落地 PAM 智能部署 Agent 骨架
- 新增 pam_deploy_graph 包,包含 Agent runtime、ActionRouter、脚本/MCP/fake runner
- 支持 hybrid_node_mcp 策略:PAM_HOME 走脚本 action,PAM_NODE 走 MCP
- 支持 script_only 离线策略,全部 action 走现有脚本 action
- 新增 LLM structured output 骨架和规则 fallback,支持意图识别、参数抽取、计划生成
- 新增 LangGraph StateGraph 工厂和 MCP client adapter
- 新增 CLI:preview、analyze、run-global、run-deploy
- 增加 fake 完整部署流程、单 IP 失败待回滚确认状态和报告输出
- 增加单元测试覆盖路由、parser、runner、Skill 加载、LLM 输出、MCP adapter 和 LangGraph 图
- 更新 README,记录当前代码骨架、进度、使用方式和下一步计划
2026-05-29 15:53:47 +08:00

68 lines
2.3 KiB
Python

"""LangGraph integration for the PAM deploy Agent."""
from __future__ import annotations
from typing import Any, Literal
from .agent import PamDeployAgent
GraphFlow = Literal["global", "deploy"]
def build_langgraph(agent: PamDeployAgent | None = None, flow: GraphFlow = "deploy"):
try:
from langgraph.graph import END, START, StateGraph
except ImportError as exc: # pragma: no cover - depends on optional package
raise RuntimeError(
"langgraph is not installed. Install project dependencies with "
"`pip install -e .`."
) from exc
runtime = agent or PamDeployAgent()
def create_state_node(state: dict[str, Any]) -> dict[str, Any]:
agent_state = runtime.create_state(
params=state["params"],
execution_strategy=state.get("execution_strategy", "hybrid_node_mcp"),
run_id=state.get("run_id"),
script_entry=state.get("script_entry"),
config_path=state.get("config_path"),
trace_file_path=state.get("trace_file_path"),
target_ips=state.get("target_ips"),
)
return {"agent_state": agent_state}
def run_global_node(state: dict[str, Any]) -> dict[str, Any]:
agent_state = runtime.run_global_flow(state["agent_state"])
return {"agent_state": agent_state}
def run_ip_node(state: dict[str, Any]) -> dict[str, Any]:
agent_state = runtime.run_ip_flow(state["agent_state"])
return {"agent_state": agent_state}
def report_node(state: dict[str, Any]) -> dict[str, Any]:
return {"report": runtime.render_report(state["agent_state"])}
graph = StateGraph(dict)
graph.add_node("create_state", create_state_node)
graph.add_node("run_global", run_global_node)
graph.add_node("run_ip", run_ip_node)
graph.add_node("report", report_node)
graph.add_edge(START, "create_state")
graph.add_edge("create_state", "run_global")
if flow == "global":
graph.add_edge("run_global", END)
else:
graph.add_edge("run_global", "run_ip")
graph.add_edge("run_ip", "report")
graph.add_edge("report", END)
return graph.compile()
def build_graph_or_none(agent: PamDeployAgent | None = None, flow: GraphFlow = "deploy"):
try:
return build_langgraph(agent=agent, flow=flow)
except RuntimeError:
return None