From 5d4238228cac54c5a1d8b2a539aef40bbe65be54 Mon Sep 17 00:00:00 2001 From: dark Date: Fri, 29 May 2026 14:08:50 +0800 Subject: [PATCH] =?UTF-8?q?langgraph=E6=9E=84=E5=BB=BA=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc_scripts/PAM_LangGraph_重构设计文档.md | 1838 +++++++++++++++++++++ 1 file changed, 1838 insertions(+) create mode 100644 doc_scripts/PAM_LangGraph_重构设计文档.md diff --git a/doc_scripts/PAM_LangGraph_重构设计文档.md b/doc_scripts/PAM_LangGraph_重构设计文档.md new file mode 100644 index 0000000..6f5c55c --- /dev/null +++ b/doc_scripts/PAM_LangGraph_重构设计文档.md @@ -0,0 +1,1838 @@ +# PAM 智能部署 LangGraph 重构设计文档 + +## 1. 背景 + +当前 PAM 智能部署能力由三部分组成: + +- `PAM_AUTO_DEPLY_SKILL.md`:定义 Agent/Skill 的流程约束、参数确认、执行顺序、回滚规则、检查点和输出规范。 +- PAM_NODE MCP 工具:面向 Node 在线执行场景,提供可由 Agent 调用的节点侧部署工具能力。 +- `deploy.sh` / `deploy.ps1`:提供实际 API 调用能力,已经暴露 action 入口,可按步骤执行建版、上传、发布、节点发现、云下载、升级、启停、校验、日志下载和回滚。 + +当前接口能力边界: + +- PAM_HOME 接口不具备 MCP 能力,只能通过现有脚本 action 调用。 +- PAM_NODE 接口支持 MCP,可以由 Agent 通过 MCP 工具调用。 + +现有模式的主要问题是:核心编排规则写在 Skill 文档中,依赖 Agent 按文本约束执行;HOME 脚本 action 与 NODE MCP 工具之间缺少统一动作路由;用户意图理解、参数抽取、缺参追问、异常解释等智能能力没有被显式建模;断点续跑、人工确认、回滚确认、进度事件和最终报告缺少一个可测试、可复用、可持久化的 Agent Runtime。 + +本设计将这套 Skill + PAM_NODE MCP + Shell/PowerShell 脚本组合重构为 LangGraph Agent。LangGraph 不只是顺序执行器,而是承载“大模型推理 + Skill 规则约束 + 混合工具路由 + 人工确认 + 状态持久化”的智能部署 Agent。 + +## 2. 设计目标 + +1. 将 LangGraph 实现为 PAM 智能部署 Agent,而不是单纯脚本编排器。 +2. 支持 `Skill + MCP`:Skill 提供规则、约束和流程策略,PAM_NODE MCP 提供节点侧在线工具能力。 +3. 保留现有 `deploy.sh` / `deploy.ps1` action 能力,作为 PAM_HOME 固定执行后端,并作为全脚本离线模式后端。 +4. 引入大模型参与意图理解、参数抽取、缺参追问、执行计划生成、失败解释和报告摘要。 +5. 对高风险动作使用确定性图节点和人工确认兜底,禁止大模型绕过确认直接部署或回滚。 +6. 支持 checkpoint 持久化和断点续跑。 +7. 支持部署过程进度流式输出,避免长时间静默执行。 +8. 将 PAM_NODE MCP 和 PAM_HOME 脚本 action 输出解析、错误分类、最终报告生成标准化。 +9. 为后续服务化、可视化和 Python 原生 API client 迁移预留接口。 + +## 3. 非目标 + +第一阶段不做以下事情: + +- 不自动生成、覆盖或修改 `deploy.sh`、`deploy.ps1`、`deploy.bat`、测试脚本。 +- 不调用脚本主流程做真实部署,只调用 action 入口。 +- 不把 `CLIENT_SECRET` 等敏感字段拼接到命令行。 +- 不自动执行回滚。 +- 不在正式主流程里使用 `download-cloud-to-node`。 +- 不一次性重写所有 PAM HTTP API 调用。 +- 不让大模型直接执行高风险工具调用;大模型只能生成建议、解释和结构化计划,真实动作由图节点在确认后执行。 + +## 4. 总体架构 + +```text +用户输入 / CLI / API / Chat + | + v +LangGraph PAM Deploy Agent + | + |-- Skill Loader + | |-- 加载 PAM_AUTO_DEPLY_SKILL.md + | |-- 提取硬约束、参数规范、流程策略 + | + |-- LLM Reasoning Layer + | |-- 意图理解 + | |-- 参数抽取与缺参追问 + | |-- 部署计划生成 + | |-- 失败原因解释 + | |-- 报告摘要生成 + | + |-- Deterministic Graph Layer + | |-- 人工确认 interrupt + | |-- 流程路由 + | |-- checkpoint 更新 + | |-- 回滚确认 interrupt + | |-- 安全策略校验 + | + |-- Tool Runtime Layer + |-- ActionRouter + |-- MCPActionRunner + |-- ScriptActionRunner + |-- OutputParser + |-- EventBus + | + v +工具后端 + | + |-- PAM_NODE MCP tools + |-- deploy.sh --action ... + |-- deploy.ps1 -Action ... + | + v +PAM HOME / PAM NODE API +``` + +核心原则是:大模型负责理解、规划、解释和生成候选动作;Skill 负责约束大模型和 Agent 的行为边界;LangGraph 确定性节点负责确认、状态和动作路由;PAM_HOME 动作固定走脚本 action;PAM_NODE 动作优先走 MCP,必要时可按用户确认降级到脚本 action。 + +### 4.1 Agent 分层 + +PAM LangGraph Agent 分为四层: + +1. `Skill Policy Layer` + - 读取并解析 `PAM_AUTO_DEPLY_SKILL.md`。 + - 形成系统提示词、工具白名单、确认点、禁止事项和流程约束。 + - 对 LLM 输出做规则校验。 + +2. `LLM Reasoning Layer` + - 从自然语言中识别部署意图。 + - 抽取业务参数和控制参数。 + - 判断是否缺参或存在歧义。 + - 生成用户可读的部署计划、风险提示、失败解释和最终摘要。 + +3. `Deterministic Orchestration Layer` + - 用 LangGraph StateGraph 固化主流程。 + - 执行人工确认、条件路由、checkpoint、重试和断点续跑。 + - 决定何时允许调用工具。 + +4. `Tool Runtime Layer` + - 按 action 粒度路由 HOME/NODE 动作。 + - PAM_HOME 脚本 action 后端。 + - PAM_NODE MCP 在线工具后端。 + - 全脚本离线后端。 + - Fake runner 测试后端。 + - 统一 action 结果、错误和事件格式。 + +### 4.2 大模型介入点 + +大模型介入但不越权执行: + +| 阶段 | LLM 作用 | 确定性约束 | +| --- | --- | --- | +| 意图识别 | 判断是部署、预演、查询、回滚还是用法说明 | 输出必须符合枚举 `intent` | +| 执行策略选择 | 结合用户表达判断使用混合执行还是全脚本执行 | HOME 固定脚本,NODE 可选 MCP 或脚本 | +| 参数抽取 | 从自然语言、文件片段或对话上下文抽取参数 | 必填参数缺失时不能进入真实 action | +| 缺参追问 | 生成简短追问 | 只问缺失或歧义字段 | +| 部署计划 | 生成用户可读执行计划和风险提示 | action 顺序由图固定,不由 LLM 任意改 | +| 失败解释 | 将 action 错误、HTTP 响应和日志摘要解释给用户 | 原始错误必须保留 | +| 回滚建议 | 根据失败阶段建议 `stopFirst` | 回滚必须 interrupt 等用户确认 | +| 最终报告 | 生成摘要和后续建议 | 统计数据来自 State,不由 LLM 编造 | + +禁止大模型做的事情: + +- 直接调用真实部署工具。 +- 跳过参数确认。 +- 跳过 IP 范围确认。 +- 自动执行回滚。 +- 修改现有脚本文件。 +- 编造 action 执行结果。 + +### 4.3 Skill + MCP 混合执行方式 + +`Skill + MCP` 在本项目中的实际含义是“HOME 脚本 action + NODE MCP 工具”的混合执行,而不是全链路 MCP。 + +```text +PAM_AUTO_DEPLY_SKILL.md + | + v +SkillPolicy + | + v +LangGraph Agent + | + v +ActionRouter + | + |-- PAM_HOME action -> ScriptActionRunner -> deploy.sh/deploy.ps1 + | + |-- PAM_NODE action -> MCPActionRunner -> PAM_NODE MCP tools + | + v +PAM HOME / PAM NODE +``` + +当用户明确要求“用 MCP”“直接在线执行”“不要生成脚本”,Agent 仍需要说明:PAM_HOME 当前没有 MCP 能力,HOME 阶段会通过现有脚本 action 执行;PAM_NODE 阶段使用 MCP 工具执行。 + +当 PAM_NODE MCP 不可用、用户明确要求脚本模式、或需要离线执行时,整轮部署可切换为全脚本执行,但必须在真实部署前向用户确认。 + +执行策略选择规则: + +1. 默认策略:`hybrid_node_mcp`,即 HOME 走脚本 action,NODE 走 MCP。 +2. 用户明确指定 MCP:使用 `hybrid_node_mcp`;如果 NODE MCP 不可用则停止并说明,不自动改为全脚本。 +3. 用户明确指定脚本或离线:使用 `script_only`,所有 action 都走 `deploy.sh` / `deploy.ps1`。 +4. 用户只说“帮我部署”:默认 `hybrid_node_mcp`;如果 NODE MCP 不可用,询问是否切换 `script_only`。 +5. 用户只要用法或预演:不调用 MCP,也不调用脚本。 +6. 高风险动作无论 MCP 还是脚本,都必须先通过参数确认。 + +标准 action 归属: + +| action | 接口归属 | 默认执行后端 | +| --- | --- | --- | +| `get-token` | PAM_HOME | 脚本 action | +| `create-version` | PAM_HOME | 脚本 action | +| `upload-package` | PAM_HOME | 脚本 action | +| `publish-version` | PAM_HOME | 脚本 action | +| `get-node-url` | PAM_HOME | 脚本 action | +| `get-online-ips` | PAM_NODE | MCP | +| `create-download-task` | PAM_NODE | MCP | +| `poll-download-progress` | PAM_NODE | MCP | +| `upgrade-ip` | PAM_NODE | MCP | +| `poll-upgrade-progress` | PAM_NODE | MCP | +| `start-ip` | PAM_NODE | MCP | +| `stop-ip` | PAM_NODE | MCP | +| `verify-ip` | PAM_NODE | MCP | +| `download-log` | PAM_NODE | MCP | +| `rollback-ip` | PAM_NODE | MCP,且必须人工确认 | + +## 5. 推荐目录结构 + +```text +pam_deploy_graph/ + __init__.py + agent.py + cli.py + graph.py + state.py + constants.py + skill_policy.py + + nodes/ + __init__.py + skill.py + intent.py + params.py + planning.py + confirmation.py + backend.py + tools.py + scripts.py + config_writer.py + global_actions.py + ip_actions.py + rollback.py + report.py + + llm/ + __init__.py + client.py + prompts.py + structured_outputs.py + validators.py + + runtime/ + __init__.py + action_router.py + tool_registry.py + mcp_runner.py + script_runner.py + fake_runner.py + output_parser.py + checkpoint_store.py + event_bus.py + file_lock.py + + schemas/ + __init__.py + skill.py + params.py + plan.py + action_result.py + checkpoint.py + report.py + + tests/ + test_output_parser.py + test_skill_policy.py + test_llm_structured_outputs.py + test_mcp_runner.py + test_script_runner.py + test_graph_routes.py + test_checkpoint_resume.py + fixtures/ + action_outputs/ +``` + +后续服务化时可新增: + +```text +pam_deploy_api/ + app.py + routes.py + sse.py +``` + +## 6. 关键状态模型 + +LangGraph 的 State 是整个 Agent 的事实来源。建议使用 `TypedDict` 或 Pydantic model。第一版如果需要开发速度,可用 `TypedDict`;如果需要更强校验,使用 Pydantic。 + +```python +class DeployState(TypedDict, total=False): + run_id: str + thread_id: str + user_input: str + messages: list[dict] + + skill: SkillPolicy + skill_version: str + skill_source_path: str + + llm_intent_result: LlmIntentResult + llm_param_result: LlmParamResult + llm_plan: LlmDeployPlan + + mode: Literal["MCP", "API脚本"] + execution_strategy: Literal["hybrid_node_mcp", "script_only", "fake"] + action_backends: dict[str, Literal["mcp", "script", "fake"]] + intent: Literal[ + "deploy", + "show_usage", + "preview", + "query_node_ips", + "rollback" + ] + show_usage_only: bool + + params: DeployParams + control: ControlParams + + node_mcp_server_name: str + node_mcp_tool_names: dict[str, str] + script_entry: Literal["deploy.sh", "deploy.ps1"] + script_base_dir: str + config_path: str + checkpoint_path: str + trace_file_path: str + + confirmation: ConfirmationState + completed_global_steps: list[str] + + token_acquired: bool + hash_code: str + node_url: str + online_ips: list[str] + target_ips: list[str] + + ip_states: dict[str, IpDeployState] + + last_success_step: str + last_failed_step: str + pending_confirmation: str + errors: list[DeployError] + + events: list[DeployEvent] + final_report: str +``` + +### 6.1 SkillPolicy + +`SkillPolicy` 是从 `PAM_AUTO_DEPLY_SKILL.md` 加载出的规则对象,供 LLM prompt、图路由和工具执行共同使用。 + +```python +class SkillPolicy(TypedDict): + name: str + source_path: str + allowed_modes: list[str] + allowed_actions: list[str] + forbidden_actions: list[str] + required_confirmations: list[str] + required_params: list[str] + optional_params: dict[str, object] + action_sequence: list[str] + ip_action_sequence: list[str] + rollback_rules: dict[str, object] + output_requirements: dict[str, object] +``` + +用途: + +- 作为 LLM system prompt 的事实来源。 +- 作为确定性节点的规则输入。 +- 作为工具白名单和执行顺序约束。 +- 当 Skill 文档更新时,Agent 不需要散落修改多个节点。 + +### 6.2 ActionRoute + +`ActionRoute` 描述每个标准 action 的接口归属和实际执行后端。 + +```python +class ActionRoute(TypedDict): + action: str + api_domain: Literal["PAM_HOME", "PAM_NODE"] + backend: Literal["script", "mcp", "fake"] + required_inputs: list[str] + produces: list[str] +``` + +默认路由: + +- `PAM_HOME` action 固定 `backend=script`。 +- `PAM_NODE` action 在 `hybrid_node_mcp` 策略下 `backend=mcp`。 +- `PAM_NODE` action 在 `script_only` 策略下 `backend=script`。 + +### 6.3 DeployParams + +```python +class DeployParams(TypedDict): + HOME_BASE_URL: str + CLIENT_ID: str + CLIENT_SECRET: str + AIRPORT_CODE: str + APP_NAME: str + MODULE_NAME: str + VERSION_NUMBER: str + ZIP_FILE_PATH: str + ACTION_TYPE: str + TIMEOUT: int + LOG_NAME: str +``` + +默认值: + +- `ACTION_TYPE = "FULL"` +- `TIMEOUT = 120` +- `LOG_NAME = "app.log"` + +### 6.4 ControlParams + +```python +class ControlParams(TypedDict): + user_specified_ips: list[str] + all_or_nothing: bool + rollback_approved: bool + os_target: Literal["windows", "linux", "macos", "git-bash"] + resume_from_checkpoint: bool + step_interval_sec: int + first_poll_delay_sec: int + per_ip_step_interval_sec: int + per_ip_interval_sec: int + failure_pause_sec: int +``` + +默认值: + +- `step_interval_sec = 2` +- `first_poll_delay_sec = 2` +- `per_ip_step_interval_sec = 1` +- `per_ip_interval_sec = 3` +- `failure_pause_sec = 0` + +### 6.5 LLM 结构化输出 + +LLM 节点必须输出结构化对象,不直接输出可执行命令。 + +```python +class LlmIntentResult(TypedDict): + intent: str + mode_preference: Literal["MCP", "API脚本", "未指定"] + confidence: float + reasons: list[str] + needs_clarification: bool + clarification_questions: list[str] + +class LlmParamResult(TypedDict): + extracted_params: dict[str, object] + extracted_control: dict[str, object] + missing_required_params: list[str] + ambiguous_fields: list[str] + sensitive_fields_present: list[str] + +class LlmDeployPlan(TypedDict): + summary: str + risk_notes: list[str] + planned_actions: list[str] + requires_confirmation: bool +``` + +结构化输出必须经过 `validators.py` 校验: + +- `intent` 必须在枚举内。 +- `planned_actions` 必须是 Skill 允许的 action 子集。 +- 不允许出现 shell 命令或 PowerShell 命令文本。 +- 不允许包含 `CLIENT_SECRET` 明文。 + +### 6.6 IpDeployState + +```python +class IpDeployState(TypedDict, total=False): + ip: str + status: Literal["PENDING", "RUNNING", "SUCCESS", "FAILED", "SKIPPED"] + current_stage: str + completed_steps: list[str] + failed_stage: str + failure_reason: str + rollback_status: Literal[ + "ROLLBACK_NOT_RUN", + "PENDING_AGENT_CONFIRMATION", + "ROLLBACK_SUCCESS", + "ROLLBACK_FAILED", + "ROLLBACK_REQUEST_FAILED", + "ROLLBACK_VERIFY_FAILED", + "ROLLBACK_SKIPPED" + ] + rollback_stop_first: bool + log_file: str + raw_outputs: dict[str, str] +``` + +## 7. 工作流主图 + +### 7.1 主流程 + +```text +START + -> load_skill_policy + -> llm_understand_request + -> validate_llm_intent + -> llm_extract_params + -> validate_and_normalize_params + -> llm_generate_plan + -> apply_skill_guardrails + -> route_by_intent + +route_by_intent: + show_usage -> show_script_usage -> END + preview -> render_preview -> END + query_node_ips -> confirm_params -> select_execution_strategy -> build_action_routes -> prepare_tool_runtimes -> query_node_ips_flow -> END + deploy -> confirm_params -> select_execution_strategy -> build_action_routes -> prepare_tool_runtimes -> deploy_flow + rollback -> rollback_entry_flow + +deploy_flow: + init_runtime_files + -> maybe_resume_from_checkpoint + -> get_token + -> create_version + -> upload_package + -> publish_version + -> get_node_url + -> get_online_ips + -> filter_target_ips + -> confirm_target_scope_if_needed + -> create_download_task + -> wait_first_poll_delay + -> poll_download_progress + -> deploy_ip_loop + -> aggregate_result + -> final_report + -> END +``` + +说明: + +- `llm_understand_request`、`llm_extract_params`、`llm_generate_plan` 是智能层节点。 +- `validate_llm_intent`、`validate_and_normalize_params`、`apply_skill_guardrails` 是确定性安全节点。 +- `select_execution_strategy` 根据用户意图、Skill 规则和 PAM_NODE MCP 可用性选择 `hybrid_node_mcp`、`script_only` 或 `fake`。 +- `build_action_routes` 生成每个 action 的实际后端;HOME action 固定脚本,NODE action 按策略走 MCP 或脚本。 +- `prepare_tool_runtimes` 检查 PAM_NODE MCP 工具、脚本文件和运行时配置。 + +### 7.2 单 IP 子流程 + +```text +deploy_ip: + mark_ip_running + -> upgrade_ip + -> wait_per_ip_step_interval + -> poll_upgrade_progress + -> wait_per_ip_step_interval + -> start_ip + -> wait_per_ip_step_interval + -> verify_ip + -> wait_per_ip_step_interval + -> download_log + -> mark_ip_success + +on failure: + record_ip_failure + -> download_log_best_effort + -> mark_pending_rollback + -> rollback_confirm_interrupt + -> route_rollback_decision +``` + +第一版建议逐台顺序执行 IP,不做并发。原因是部署动作本身风险较高,当前 Skill 文档也要求逐台播报。后续如果需要并发,可以在单 IP 子图稳定后引入受控 fan-out,并保留最大并发数和失败策略。 + +### 7.3 Agent ReAct 子图边界 + +本设计不采用开放式 ReAct 循环执行真实部署工具。原因是 PAM 部署包含建版、发布、升级和回滚等高风险动作,不能让模型自由选择下一步真实工具。 + +允许使用受限 ReAct 子图的场景: + +- 缺参追问。 +- 用户意图澄清。 +- 失败原因解释。 +- 日志摘要。 +- 最终报告文字润色。 + +不允许使用开放式 ReAct 的场景: + +- `create-version` +- `upload-package` +- `publish-version` +- `create-download-task` +- `upgrade-ip` +- `rollback-ip` + +这些动作必须由确定性图节点按照 Skill 固定顺序调用。 + +## 8. 节点设计 + +### 8.1 load_skill_policy + +职责: + +- 读取 `PAM_AUTO_DEPLY_SKILL.md`。 +- 解析 Skill frontmatter 和正文中的关键规则。 +- 生成 `SkillPolicy`。 +- 将 Skill 规则注入 LLM system prompt 和后续图节点。 + +失败策略: + +- Skill 文件缺失或解析失败时,Agent 不进入真实部署。 + +### 8.2 llm_understand_request + +职责: + +- 调用大模型理解用户自然语言。 +- 输出结构化 `LlmIntentResult`。 +- 判断用户是否要求 MCP、脚本、预演、查询或回滚。 + +约束: + +- LLM 只输出结构化意图,不输出命令。 +- 置信度低或存在歧义时,进入澄清分支。 + +### 8.3 validate_llm_intent + +职责: + +- 校验 LLM 输出是否符合枚举。 +- 按 Skill 规则修正或拒绝不合法意图。 +- 例如用户要求“生成脚本”时,转入 `show_usage` 或限制说明分支。 + +### 8.4 llm_extract_params + +职责: + +- 从用户输入、对话上下文、上传的参数文件或历史 checkpoint 中抽取业务参数。 +- 识别缺失字段和歧义字段。 +- 标记 `CLIENT_ID`、`CLIENT_SECRET` 等敏感字段是否已提供。 + +约束: + +- 不把 `CLIENT_SECRET` 明文写入普通消息。 +- 不猜测必填参数。 + +### 8.5 validate_and_normalize_params + +职责: + +- 将 LLM 抽取结果映射为 `DeployParams` 和 `ControlParams`。 +- 补齐默认值。 +- 校验必填参数。 +- 对机场码、版本号、IP 列表、路径格式做基础校验。 + +硬约束: + +- 必填参数缺失时,不进入真实 action。 +- 参数有歧义时,应中断并要求补充。 + +### 8.6 llm_generate_plan + +职责: + +- 基于 Skill 和已归一化参数生成用户可读部署计划。 +- 说明执行策略:默认是 HOME 脚本 action + NODE MCP 的混合执行。 +- 如果用户要求 MCP,明确提示 HOME 阶段仍会通过脚本 action 执行,因为 PAM_HOME 不具备 MCP 能力。 +- 说明高风险动作和确认点。 + +约束: + +- 计划中的 action 顺序必须与 Skill 固定流程一致。 +- 计划只用于展示和确认,不作为真实执行序列的唯一来源。 + +### 8.7 apply_skill_guardrails + +职责: + +- 校验 LLM 输出是否违反 Skill 禁止事项。 +- 校验是否试图调用脚本主流程。 +- 校验是否试图自动回滚。 +- 校验是否试图修改脚本。 + +失败策略: + +- 发现违规时终止真实执行并输出限制说明。 + +### 8.8 parse_intent + +职责: + +- 判断用户要真实部署、查看用法、预演计划、查询 Node/IP,还是手动回滚。 +- 判断用户是否明确要求 `MCP` 或 `API脚本`。 +- 如果用户要求生成或修改脚本,直接进入受限说明分支。 + +输出: + +- `intent` +- `mode` +- `show_usage_only` + +说明: + +- 该节点保留为确定性兜底逻辑,用于 LLM 不可用或 structured output 失败时的基础规则识别。 + +### 8.9 normalize_params + +职责: + +- 从用户输入、环境变量、配置文件或外部调用参数中收集业务参数。 +- 补齐默认值。 +- 标记缺失参数。 +- 敏感字段不进入普通播报内容。 + +硬约束: + +- 必填参数缺失时,不进入真实 action。 +- 参数有歧义时,应中断并要求补充。 + +说明: + +- 该节点保留为确定性兜底逻辑。 + +### 8.10 confirm_params + +职责: + +- 渲染归一化参数确认单。 +- 使用 LangGraph interrupt 等待用户确认。 +- 未确认前不得调用任何真实 action。 + +确认单不展示 `CLIENT_SECRET` 明文,只展示“已提供/未提供”。 + +### 8.11 select_execution_strategy + +职责: + +- 根据用户偏好、PAM_NODE MCP 可用性、Skill 规则和运行环境选择执行策略。 +- 输出 `execution_strategy = hybrid_node_mcp | script_only | fake`。 + +选择规则: + +1. 用户明确指定 MCP:选择 `hybrid_node_mcp`;PAM_NODE MCP 不可用则停止。 +2. 用户明确指定脚本或离线:选择 `script_only`。 +3. 用户未指定且 PAM_NODE MCP 可用:选择 `hybrid_node_mcp`。 +4. 用户未指定且 PAM_NODE MCP 不可用:询问是否切换 `script_only`。 +5. 测试环境可显式选择 fake runner。 + +### 8.12 build_action_routes + +职责: + +- 根据 `execution_strategy` 生成 `action_backends`。 +- HOME action 固定为 `script`。 +- NODE action 在 `hybrid_node_mcp` 下为 `mcp`。 +- NODE action 在 `script_only` 下为 `script`。 +- fake 策略下所有 action 为 `fake`。 + +输出示例: + +```json +{ + "get-token": "script", + "create-version": "script", + "upload-package": "script", + "publish-version": "script", + "get-node-url": "script", + "get-online-ips": "mcp", + "create-download-task": "mcp", + "poll-download-progress": "mcp", + "upgrade-ip": "mcp", + "poll-upgrade-progress": "mcp", + "start-ip": "mcp", + "verify-ip": "mcp", + "download-log": "mcp", + "rollback-ip": "mcp" +} +``` + +### 8.13 prepare_tool_runtimes + +职责: + +- 混合执行下:检查脚本文件、选择脚本入口、写入 HOME 脚本配置,同时检查 PAM_NODE MCP server 和工具映射。 +- 全脚本执行下:检查脚本文件、选择脚本入口、写入完整脚本配置。 +- fake 模式下:加载 fixture 输出。 + +### 8.14 check_scripts + +职责: + +- 检查当前目录下是否存在 `deploy.sh` / `deploy.ps1`。 +- 检查目标脚本是否可读。 +- 不自动补写脚本。 + +失败策略: + +- 缺少脚本时终止流程并输出原因。 + +### 8.15 select_script_entry + +职责: + +- Windows 默认选择 `deploy.ps1`。 +- Linux / macOS / Git Bash 默认选择 `deploy.sh`。 +- `deploy.bat` 只作为兼容包装入口,不作为 LangGraph action runner 默认入口。 + +### 8.16 write_config + +职责: + +- 将确认后的业务参数写入 `config.txt` 或本次运行专属配置文件。 +- 命令行只传 action 级参数,不传 `CLIENT_SECRET`。 + +建议: + +- 默认写入 `./runtime/config_.txt`,避免覆盖人工维护的 `config.txt`。 +- 如果必须兼容脚本默认路径,可由 CLI 参数指定 `--config-path ./config.txt`。 + +说明: + +- 混合执行下仍需要写配置文件,因为 PAM_HOME action 固定通过脚本执行。 +- 全脚本执行下写完整配置文件。 +- PAM_NODE MCP 工具不从 `config.txt` 读参数,由 Agent 通过 MCP tool input 传入必要字段。 +- `CLIENT_SECRET` 只允许写入受控配置文件或安全凭证通道,不进入命令行和普通日志。 + +### 8.17 init_runtime_files + +职责: + +- 创建 `logs/` 或 `runtime/` 目录。 +- 生成本次运行的 `checkpoint_path`。 +- 生成本次运行的 `trace_file_path`。 +- 写入初始业务 checkpoint。 + +命名建议: + +```text +logs/deploy_checkpoint____.json +logs/api_trace_____.log +``` + +### 8.18 action 节点 + +每个 action 节点只做四件事: + +1. 播报即将执行的阶段。 +2. 通过 `ActionRouter` 按 action 归属调用 MCP 或脚本 action。 +3. 解析 key=value 输出并更新 State。 +4. 更新 checkpoint 和进度事件。 + +全局 action 节点: + +- `get_token` +- `create_version` +- `upload_package` +- `publish_version` +- `get_node_url` +- `get_online_ips` +- `create_download_task` +- `poll_download_progress` + +单 IP action 节点: + +- `upgrade_ip` +- `poll_upgrade_progress` +- `start_ip` +- `verify_ip` +- `download_log` +- `rollback_ip` + +### 8.19 llm_explain_failure + +职责: + +- 在全局失败或单 IP 失败后,基于 `ActionResult`、原始输出、阶段名和 Skill 规则生成用户可读解释。 +- 给出下一步建议,例如检查凭证、包路径、Node 连通性、在线 IP 或是否回滚。 + +约束: + +- 不能替换原始错误。 +- 不能承诺未发生的恢复动作。 +- 回滚建议必须进入确认节点。 + +### 8.20 final_report + +职责: + +- 汇总模式、脚本入口、机场、应用、模块、版本。 +- 汇总在线 IP 数、目标 IP 数、成功数、失败数。 +- 汇总每台 IP 的状态、失败阶段、失败原因、回滚状态、日志路径。 +- 输出 checkpoint 和 trace 路径。 +- 输出是否断点续跑和当前间隔控制参数。 +- 调用 LLM 生成简短摘要,但所有统计值必须来自 State。 + +## 9. Tool Runtime 设计 + +### 9.1 统一 ToolRunner 接口 + +```python +class ToolRunner: + def run( + self, + action: str, + *, + params: DeployParams, + config_path: str | None = None, + script_entry: str | None = None, + ip: str | None = None, + hash_code: str | None = None, + stop_first: bool = False, + trace_file_path: str | None = None, + timeout_sec: int | None = None, + ) -> ActionResult: + ... +``` + +`ActionRouter` 根据 `state.action_backends[action]` 按 action 粒度分发到具体 runner: + +```python +class ActionRouter: + def run_action(self, state: DeployState, action: str, **kwargs) -> ActionResult: + backend = state["action_backends"][action] + if backend == "mcp": + return self.node_mcp_runner.run(action, params=state["params"], **kwargs) + if backend == "script": + return self.script_runner.run( + action, + params=state["params"], + config_path=state["config_path"], + script_entry=state["script_entry"], + **kwargs, + ) + return self.fake_runner.run(action, params=state["params"], **kwargs) +``` + +### 9.2 MCPActionRunner + +MCPActionRunner 只负责 PAM_NODE action。PAM_HOME 当前没有 MCP 能力,HOME action 不进入 MCPActionRunner。 + +PAM_NODE MCP 工具映射: + +| 标准 action | MCP tool 建议名 | 说明 | +| --- | --- | --- | +| `get-online-ips` | `pam_get_online_ips` | 获取在线工作站 | +| `create-download-task` | `pam_create_download_task` | 创建云下载任务 | +| `poll-download-progress` | `pam_poll_download_progress` | 轮询云下载进度 | +| `upgrade-ip` | `pam_upgrade_ip` | 创建单 IP 升级任务 | +| `poll-upgrade-progress` | `pam_poll_upgrade_progress` | 轮询单 IP 升级进度 | +| `start-ip` | `pam_start_ip` | 启动应用 | +| `stop-ip` | `pam_stop_ip` | 停止应用 | +| `verify-ip` | `pam_verify_ip` | 校验应用 | +| `download-log` | `pam_download_log` | 下载日志 | +| `rollback-ip` | `pam_rollback_ip` | 手动回滚 | + +MCP runner 职责: + +- 发现 PAM_NODE MCP server 和工具列表。 +- 将标准 action 映射为 MCP tool。 +- 将 `DeployParams` 转为 tool input。 +- 读取 `node_url`、`airportCode`、`applicationName`、`moduleName`、`versionNumber`、目标 IP 等运行时状态。 +- 调用工具并统一转为 `ActionResult`。 +- 保存工具原始返回。 +- 对敏感字段做脱敏。 + +MCP runner 约束: + +- 只能调用 Skill 白名单中的 PAM_NODE 工具。 +- 不能调用 PAM_HOME action。 +- `rollback-ip` 必须在回滚确认后调用。 +- 工具返回结果不得由 LLM 改写后写入 State。 +- PAM_NODE MCP 不可用时,不能静默切换脚本后端,除非用户确认 `script_only`。 + +### 9.3 ScriptActionRunner + +ScriptActionRunner 有两类职责: + +1. 在 `hybrid_node_mcp` 策略下,固定执行 PAM_HOME action: + - `get-token` + - `create-version` + - `upload-package` + - `publish-version` + - `get-node-url` +2. 在 `script_only` 策略下,执行全部 action,包括 PAM_NODE action。 + +脚本后端仍然只允许调用 action 入口,不允许调用脚本主流程。 + +### 9.4 Shell 命令 + +```bash +bash ./deploy.sh \ + --config ./config.txt \ + --action upload-package \ + --trace-file ./logs/api_trace_xxx.log +``` + +带 IP: + +```bash +bash ./deploy.sh \ + --config ./config.txt \ + --action upgrade-ip \ + --ip 192.168.1.10 \ + --trace-file ./logs/api_trace_xxx.log +``` + +发布版本: + +```bash +bash ./deploy.sh \ + --config ./config.txt \ + --action publish-version \ + --hash-code 43858bcf \ + --trace-file ./logs/api_trace_xxx.log +``` + +回滚: + +```bash +bash ./deploy.sh \ + --config ./config.txt \ + --action rollback-ip \ + --ip 192.168.1.10 \ + --stop-first \ + --trace-file ./logs/api_trace_xxx.log +``` + +### 9.5 PowerShell 命令 + +```powershell +powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action upload-package +``` + +带 IP: + +```powershell +powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action upgrade-ip -Ip 192.168.1.10 +``` + +发布版本: + +```powershell +powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action publish-version -HashCode 43858bcf +``` + +回滚: + +```powershell +powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action rollback-ip -Ip 192.168.1.10 -RollbackStopFirst +``` + +### 9.6 当前能力差异 + +Shell 侧当前支持 `--trace-file`,可以由 LangGraph 在一轮部署中传入同一个 trace 文件。 + +PowerShell 侧当前 `deploy.ps1` action 参数未暴露 `-TraceFile`。第一阶段处理策略: + +- LangGraph 统一保存每个 PowerShell action 的 stdout/stderr 到工作流 trace。 +- 业务 checkpoint 中仍记录同一个 `trace_file_path`。 +- 如果必须做到 PowerShell 脚本内部 HTTP trace 也复用同一个文件,需要后续单独评估是否修改 `deploy.ps1`,不纳入第一阶段。 + +### 9.7 FakeRunner + +Fake runner 用于图路由、LLM 结构化输出、checkpoint 和报告生成测试。 + +职责: + +- 根据 action 返回 fixture。 +- 模拟全局失败、单 IP 失败、回滚确认和日志下载失败。 +- 不访问真实 PAM 环境。 + +## 10. Action 输出解析 + +`OutputParser` 负责把脚本 stdout 和 MCP tool result 都归一化为 `ActionResult`。 + +脚本后端优先解析 `key=value` 行。 + +输入示例: + +```text +ACTION=upload-package +HASH_CODE=43858bcf +TRACE_FILE=./logs/api_trace_xxx.log +``` + +解析结果: + +```python +{ + "ACTION": "upload-package", + "HASH_CODE": "43858bcf", + "TRACE_FILE": "./logs/api_trace_xxx.log" +} +``` + +解析规则: + +1. 只将形如 `KEY=VALUE` 的行作为结构化结果。 +2. `[INFO]`、`[WARN]`、`[FLOW]` 作为日志,不参与主结果判断。 +3. 同名 key 多次出现时: + - `IP` 保留为列表。 + - 其他字段默认以后出现的值覆盖先出现的值。 +4. 非零退出码视为 action 失败。 +5. stderr、非结构化 stdout 和原始输出必须保存在 `ActionResult.raw_output`。 +6. 出现 `PENDING_AGENT_CONFIRMATION(...)` 时,必须转入人工确认分支。 + +MCP 后端解析规则: + +1. MCP tool 如果返回 JSON object,直接映射到 `values`。 +2. MCP tool 如果返回文本,先尝试按 JSON 解析,再尝试按 `key=value` 解析。 +3. MCP 原始返回必须保存在 `raw_output`。 +4. MCP tool error 统一转换为 `ok=false` 和 `error_summary`。 +5. 结果字段名归一化为脚本 action 同款字段,例如 `hashCode` 归一为 `HASH_CODE`,`nodeUrl` 归一为 `NODE_URL`。 + +### 10.1 ActionResult + +```python +class ActionResult(TypedDict): + action: str + backend: Literal["mcp", "script", "fake"] + tool_name: str + exit_code: int + ok: bool + values: dict[str, str | list[str]] + stdout: str + stderr: str + raw_output: str + error_summary: str +``` + +## 11. Checkpoint 设计 + +需要两层 checkpoint: + +1. LangGraph checkpointer:保存图执行位置和完整 State,用于 `interrupt` 恢复和工作流续跑。 +2. 业务 checkpoint JSON:兼容现有 Skill 文档,方便人工排查,也方便脱离 LangGraph 查看部署状态。 + +### 11.1 业务 checkpoint 文件 + +路径: + +```text +./logs/deploy_checkpoint____.json +``` + +内容: + +```json +{ + "runId": "20260529_103000_abcd", + "mode": "MCP", + "executionStrategy": "hybrid_node_mcp", + "actionBackends": { + "get-token": "script", + "create-version": "script", + "upload-package": "script", + "publish-version": "script", + "get-node-url": "script", + "get-online-ips": "mcp", + "create-download-task": "mcp", + "poll-download-progress": "mcp", + "upgrade-ip": "mcp", + "poll-upgrade-progress": "mcp", + "start-ip": "mcp", + "verify-ip": "mcp", + "download-log": "mcp", + "rollback-ip": "mcp" + }, + "skillVersion": "pam-auto-deply", + "scriptEntry": "deploy.ps1", + "nodeMcpServerName": "pam-node", + "checkpointPath": "./logs/deploy_checkpoint_HET_PAM_Node_2.0.5.json", + "resumeFromCheckpoint": true, + "params": { + "HOME_BASE_URL": "https://pam.home.example.com", + "AIRPORT_CODE": "HET", + "APP_NAME": "PAM", + "MODULE_NAME": "Node", + "VERSION_NUMBER": "2.0.5", + "ZIP_FILE_PATH": "C:\\path\\to\\pam-2.0.5.zip", + "ACTION_TYPE": "FULL", + "LOG_NAME": "app.log" + }, + "artifacts": { + "configPath": "./runtime/config_20260529_103000_abcd.txt", + "traceFilePath": "./logs/api_trace_HET_PAM_Node_2.0.5_20260529_103000_abcd.log", + "hashCode": "43858bcf", + "nodeUrl": "https://pam-node.example.com" + }, + "onlineIps": [ + "192.168.1.10", + "192.168.1.11" + ], + "targetIps": [ + "192.168.1.10" + ], + "completedGlobalSteps": [ + "get-token", + "create-version", + "upload-package", + "publish-version" + ], + "ipStates": { + "192.168.1.10": { + "status": "FAILED", + "completedSteps": [ + "upgrade-ip", + "poll-upgrade-progress", + "start-ip" + ], + "failedStage": "verify-ip", + "failureReason": "Health check failed", + "rollbackStatus": "PENDING_AGENT_CONFIRMATION", + "rollbackStopFirst": true, + "logFile": "./logs/deploy_192.168.1.10.zip" + } + }, + "lastSuccessStep": "start-ip", + "lastFailedStep": "verify-ip", + "pendingConfirmation": "rollback-ip:192.168.1.10", + "updatedAt": "2026-05-29 10:30:00" +} +``` + +敏感字段不写入业务 checkpoint: + +- `CLIENT_SECRET` +- token +- Authorization header + +### 11.2 断点续跑策略 + +1. 启动时如果指定 `resume_from_checkpoint=true`,先读取业务 checkpoint。 +2. 校验当前核心参数与 checkpoint 参数是否一致。 +3. 核心参数不一致时,不直接续跑,必须要求用户确认重新开始或使用旧参数。 +4. 已完成的全局步骤默认跳过。 +5. `create-download-task` 已成功但 `poll-download-progress` 未完成时,从 `poll-download-progress` 继续。 +6. 单 IP 已成功的默认跳过。 +7. 单 IP 失败且处于回滚确认状态时,恢复后先进入回滚确认,不继续后续动作。 + +## 12. 人工确认设计 + +使用 LangGraph interrupt 表示需要外部输入的确认点。 + +### 12.1 参数确认 + +触发点: + +- 真实部署前。 +- 查询 Node/IP 前。 +- 手动回滚前。 + +确认内容: + +- 模式 +- 脚本入口 +- HOME_BASE_URL +- AIRPORT_CODE +- APP_NAME +- MODULE_NAME +- VERSION_NUMBER +- ZIP_FILE_PATH +- ACTION_TYPE +- TIMEOUT +- LOG_NAME +- 指定 IP +- CLIENT_ID 是否已提供 +- CLIENT_SECRET 是否已提供 + +### 12.2 IP 范围确认 + +触发点: + +- 用户指定 IP 和在线 IP 取交集后,部署范围发生变化。 +- 过滤结果为空。 +- 用户启用 `all_or_nothing=true` 且部分 IP 不在线。 + +### 12.3 回滚确认 + +触发点: + +- `upgrade-ip` 失败。 +- `poll-upgrade-progress` 失败。 +- `start-ip` 失败。 +- `verify-ip` 失败。 +- action 输出 `PENDING_AGENT_CONFIRMATION(...)`。 + +确认内容: + +- 目标 IP +- 失败阶段 +- 失败原因 +- 建议是否回滚 +- `stopFirst` 建议值 + +默认建议: + +- 升级失败:建议回滚,`stopFirst=false` +- 启动失败:建议回滚,`stopFirst=true` +- 校验失败:建议回滚,`stopFirst=true` + +## 13. 进度事件设计 + +工作流运行中输出结构化事件,供 CLI、日志、SSE 或 WebSocket 使用。 + +```python +class DeployEvent(TypedDict): + run_id: str + level: Literal["INFO", "WARN", "ERROR"] + type: Literal[ + "FLOW_START", + "FLOW_DONE", + "FLOW_FAIL", + "ACTION_START", + "ACTION_DONE", + "ACTION_FAIL", + "WAIT", + "IP_START", + "IP_DONE", + "IP_FAIL", + "CONFIRMATION_REQUIRED", + "REPORT" + ] + stage: str + ip: str | None + message: str + next_stage: str | None + wait_sec: int | None + data: dict + created_at: str +``` + +事件示例: + +```json +{ + "type": "ACTION_DONE", + "stage": "upload-package", + "message": "软件包上传完成", + "data": { + "HASH_CODE": "43858bcf" + } +} +``` + +等待事件示例: + +```json +{ + "type": "WAIT", + "stage": "create-download-task", + "message": "等待 2 秒后开始首次下载进度轮询", + "next_stage": "poll-download-progress", + "wait_sec": 2 +} +``` + +## 14. 分支流程 + +### 14.1 混合智能执行 + +适用场景: + +- 用户明确要求“用 MCP”“直接在线执行”“不要生成脚本”。 +- 用户只说“帮我部署”,且当前环境存在可用 PAM_NODE MCP 工具。 + +流程: + +```text +load_skill_policy + -> llm_understand_request + -> llm_extract_params + -> validate_and_normalize_params + -> llm_generate_plan + -> confirm_params + -> select_execution_strategy(hybrid_node_mcp) + -> build_action_routes + -> prepare_tool_runtimes + -> deploy_flow +``` + +行为: + +- PAM_HOME action 通过 `ScriptActionRunner` 调用 `deploy.sh` / `deploy.ps1` action。 +- PAM_NODE action 通过 `MCPActionRunner` 调用 PAM_NODE MCP 工具。 +- 需要写配置文件供 HOME 脚本 action 使用,但敏感字段不进入命令行和普通日志。 +- 仍然按 Skill 固定 action 顺序执行。 +- 仍然写业务 checkpoint 和 trace。 +- 仍然要求参数确认、IP 范围确认和回滚确认。 + +### 14.2 脚本离线执行 + +适用场景: + +- 用户明确要求“用脚本”“离线执行”。 +- PAM_NODE MCP 不可用且用户确认切换全脚本后端。 + +流程: + +```text +load_skill_policy + -> llm_understand_request + -> llm_extract_params + -> validate_and_normalize_params + -> llm_generate_plan + -> confirm_params + -> select_execution_strategy(script_only) + -> build_action_routes + -> prepare_tool_runtimes + -> deploy_flow +``` + +行为: + +- 只调用 `deploy.sh` / `deploy.ps1` action 入口。 +- 不调用脚本主流程。 +- `CLIENT_SECRET` 只写入配置文件,不进入命令行。 + +### 14.3 只说明脚本用法 + +适用场景: + +- 用户要求“给我脚本用法”。 +- 用户要求“生成脚本”,但 Skill 禁止生成或修改脚本。 +- 用户明确“不直接动环境”。 + +行为: + +- 说明现有 `deploy.sh` / `deploy.ps1` action 用法。 +- 不写 `config.txt`。 +- 不调用 MCP。 +- 不调用真实脚本 action。 + +### 14.4 预演部署计划 + +适用场景: + +- 用户只想确认参数和计划,不执行真实部署。 + +行为: + +- LLM 抽取和归一化参数。 +- LLM 生成用户可读部署计划。 +- 输出确认单。 +- 输出预计 action 顺序。 +- 输出执行策略:混合执行或全脚本执行。 +- 明确 HOME action 和 NODE action 的执行后端。 +- 如存在 checkpoint,说明可从哪个步骤恢复。 +- 不调用真实 action。 + +### 14.5 只查询 Node 和在线 IP + +流程: + +```text +confirm_params + -> select_execution_strategy + -> build_action_routes + -> prepare_tool_runtimes + -> get_token + -> get_node_url + -> get_online_ips + -> report_node_ips +``` + +禁止执行: + +- `create-version` +- `upload-package` +- `publish-version` +- `create-download-task` +- `upgrade-ip` + +### 14.6 手动回滚 + +流程: + +```text +rollback_entry + -> llm_extract_rollback_target + -> confirm_rollback_target + -> select_execution_strategy + -> build_action_routes + -> prepare_tool_runtimes + -> rollback_ip + -> verify_ip optional + -> download_log + -> update_checkpoint + -> final_report +``` + +回滚必须由用户明确确认,不能根据失败状态自动执行。 + +## 15. 错误处理策略 + +### 15.1 全局失败 + +以下阶段失败时终止整轮部署: + +- `get-token` +- `create-version` +- `upload-package` +- `publish-version` +- `get-node-url` +- `get-online-ips` +- `create-download-task` +- `poll-download-progress` + +处理: + +- 记录 `last_failed_step`。 +- 写 checkpoint。 +- 输出失败报告。 +- 保留原始 action 输出。 + +### 15.2 单 IP 失败 + +以下阶段失败时只标记当前 IP 失败: + +- `upgrade-ip` +- `poll-upgrade-progress` +- `start-ip` +- `verify-ip` + +处理: + +- 记录失败阶段和原因。 +- 尽力执行 `download-log`。 +- 写 checkpoint。 +- 转入回滚确认。 +- 未确认回滚前,不执行 `rollback-ip`。 + +### 15.3 日志下载失败 + +`download-log` 失败不覆盖原始失败原因。 + +处理: + +- 记录日志下载失败。 +- 保留主失败阶段。 +- 报告中明确日志缺失或下载失败。 + +## 16. 安全设计 + +1. `CLIENT_SECRET` 不进入命令行。 +2. `CLIENT_SECRET` 不进入普通日志、进度事件、最终报告。 +3. token 不进入最终报告。 +4. action 原始输出保存前需要做敏感字段脱敏。 +5. `config.txt` 或运行时配置文件权限应尽量限制为当前用户可读写。 +6. 回滚必须显式确认。 +7. 若用户要求“不落地配置文件”,真实部署流程直接终止并说明原因。 +8. 对脚本路径使用白名单,只允许当前工作目录下既有脚本。 +9. MCP 工具调用使用白名单,只允许 Skill 中定义的 PAM_NODE 工具。 +10. LLM 输出必须经过结构化校验和 Skill guardrails。 +11. LLM 不能生成可执行 shell/PowerShell 命令作为真实执行依据。 +12. PAM_HOME action 不允许被路由到 MCP。 + +## 17. CLI 设计 + +第一阶段建议先落地 Agent CLI,便于本地验证 Skill、LLM、MCP 和脚本后端。 + +### 17.1 部署 + +```bash +python -m pam_deploy_graph.cli deploy \ + --strategy hybrid-node-mcp \ + --home-base-url https://pam.home.example.com \ + --client-id xxx \ + --client-secret "***" \ + --airport-code HET \ + --app-name PAM \ + --module-name Node \ + --version-number 2.0.5 \ + --zip-file-path C:\path\to\pam-2.0.5.zip \ + --target-ip 192.168.1.10 \ + --target-ip 192.168.1.11 +``` + +脚本后端: + +```bash +python -m pam_deploy_graph.cli deploy \ + --strategy script-only \ + --config ./deploy_params.json +``` + +### 17.2 预演 + +```bash +python -m pam_deploy_graph.cli preview --config ./deploy_params.json +``` + +### 17.3 对话式 Agent + +```bash +python -m pam_deploy_graph.cli chat +``` + +对话式 Agent 行为: + +- 使用 LLM 理解用户自然语言。 +- 使用 Skill 规则控制工具调用。 +- 在真实部署前输出确认单并等待用户确认。 +- 支持继续追问缺失参数。 + +### 17.4 断点续跑 + +```bash +python -m pam_deploy_graph.cli resume \ + --checkpoint ./logs/deploy_checkpoint_HET_PAM_Node_2.0.5.json +``` + +### 17.5 回滚确认恢复 + +```bash +python -m pam_deploy_graph.cli resume \ + --thread-id \ + --approve-rollback \ + --ip 192.168.1.10 \ + --stop-first +``` + +## 18. 服务化设计 + +后续可将 Agent CLI 包装为 FastAPI 服务。 + +接口建议: + +```text +POST /agent/chat +POST /deploy/preview +POST /deploy/start +POST /deploy/{run_id}/confirm +POST /deploy/{run_id}/rollback/confirm +POST /deploy/{run_id}/resume +GET /deploy/{run_id} +GET /deploy/{run_id}/events +GET /deploy/{run_id}/report +``` + +事件输出: + +- CLI:直接打印结构化进度。 +- API:通过 SSE 或 WebSocket 输出 `DeployEvent`。 + +## 19. 测试策略 + +### 19.1 单元测试 + +覆盖: + +- `SkillPolicy` 从 Skill 文档提取规则。 +- LLM structured output schema 校验。 +- LLM 输出违反 Skill guardrails 时会被拒绝。 +- `OutputParser` 解析 `key=value`。 +- 多个 `IP=` 行解析为列表。 +- `PENDING_AGENT_CONFIRMATION(...)` 检测。 +- 敏感字段脱敏。 +- MCP tool input 构造。 +- 脚本 action 命令构造。 +- checkpoint JSON 写入和读取。 + +### 19.2 图路由测试 + +覆盖: + +- `show_usage` 不调用 action。 +- `preview` 不调用 action。 +- 参数未确认不调用 action。 +- PAM_NODE MCP 可用且用户未指定策略时使用 `hybrid_node_mcp`。 +- PAM_NODE MCP 不可用时需要用户确认才切 `script_only`。 +- HOME action 始终路由到脚本。 +- NODE action 在混合策略下路由到 MCP。 +- IP 范围变化进入确认。 +- 单 IP 失败进入回滚确认。 +- 用户拒绝回滚后继续或结束。 + +### 19.3 LLM 节点测试 + +覆盖: + +- 自然语言中抽取机场、应用、模块、版本和包路径。 +- 缺少 `CLIENT_SECRET` 时生成追问。 +- 用户说“不要动环境”时进入预演或用法说明。 +- 用户说“用 MCP”时 strategy preference 为 `hybrid_node_mcp`,并提示 HOME 仍走脚本 action。 +- 用户说“离线脚本执行”时 mode preference 为 API脚本。 +- LLM 计划中出现非法 action 时被 guardrails 拒绝。 + +### 19.4 MCP runner 测试 + +覆盖: + +- PAM_NODE 标准 action 到 MCP tool 的映射。 +- PAM_NODE MCP 工具缺失时停止。 +- PAM_HOME action 不会进入 MCP runner。 +- MCP 返回结构化结果转为 `ActionResult`。 +- MCP 原始错误进入 `raw_output`。 +- 回滚未确认时不调用 `pam_rollback_ip`。 + +### 19.5 Runner 假实现测试 + +使用 fake runner 模拟脚本输出: + +- 全部成功。 +- 全局失败。 +- 上传返回 `HASH_CODE`。 +- Node 返回 `NODE_URL`。 +- 在线 IP 返回多个 `IP=`。 +- 单 IP 校验失败。 +- 日志下载失败。 + +### 19.6 集成测试 + +MCP 或脚本真实后端接入后,只做可控环境下的 smoke: + +- `get-token` +- `get-node-url` +- `get-online-ips` + +完整部署集成测试需要人工指定测试环境和测试包,不默认纳入自动 CI。 + +## 20. 实施计划 + +### 阶段一:Agent MVP + +交付: + +- `DeployState` +- `SkillPolicy` 加载器 +- LLM structured output schema +- `llm_understand_request` +- `llm_extract_params` +- `llm_generate_plan` +- Skill guardrails +- 主图和单 IP 顺序流程 +- fake runner +- output parser +- 参数确认 interrupt +- 业务 checkpoint JSON +- 最终报告 + +验收: + +- 对话式输入能抽取参数并生成部署计划。 +- 能跑通 `preview`。 +- 能跑通 `query_node_ips`。 +- fake runner 能跑通完整部署成功链路。 +- fake runner 能验证单 IP 失败和回滚确认。 +- LLM 不能绕过参数确认调用 fake runner。 + +### 阶段二:混合工具路由接入 + +交付: + +- `ActionRouter`。 +- PAM_HOME action 到 `ScriptActionRunner` 的固定路由。 +- PAM_NODE MCP server/tool discovery。 +- `MCPActionRunner`。 +- PAM_NODE 标准 action 到 MCP tool 的映射。 +- MCP 工具结果统一转 `ActionResult`。 +- 混合执行 `query_node_ips` smoke:`get-token/get-node-url` 走脚本,`get-online-ips` 走 MCP。 + +验收: + +- 用户指定 MCP 时进入 `hybrid_node_mcp`,HOME 阶段仍走脚本。 +- 用户未指定策略且 PAM_NODE MCP 可用时默认混合执行。 +- PAM_NODE MCP 不可用时不会静默切全脚本。 +- 回滚未确认时不会调用 MCP 回滚工具。 + +### 阶段三:全脚本后端完善 + +交付: + +- Shell runner 接入全部 `deploy.sh --action`。 +- PowerShell runner 接入全部 `deploy.ps1 -Action`。 +- 真实 stdout/stderr 收集。 +- action 超时控制。 +- 统一工作流 trace 文件。 + +验收: + +- 在 Windows 上可调用 `deploy.ps1` action。 +- 在 Git Bash/Linux 上可调用 `deploy.sh` action。 +- 不调用脚本主流程。 + +### 阶段四:断点续跑完善 + +交付: + +- 从业务 checkpoint 恢复。 +- 参数一致性校验。 +- 已完成全局步骤跳过。 +- 已成功 IP 跳过。 +- pending rollback 恢复后继续等待确认。 + +验收: + +- 模拟中断后可从指定阶段恢复。 +- 参数变化时会要求用户确认。 + +### 阶段五:服务化和可视化 + +交付: + +- Chat Agent API。 +- FastAPI 包装。 +- SSE/WebSocket 进度事件。 +- 运行记录查询。 +- 最终报告接口。 + +验收: + +- 前端或调用方可实时看到进度。 +- 可通过接口完成确认和恢复。 + +### 阶段六:Python 原生 API client 迁移 + +交付: + +- 将脚本内部 HTTP 能力逐步迁移为 Python client。 +- 保留脚本作为兼容入口。 + +验收: + +- Python client 与脚本 action 行为一致。 +- 关键接口都有单元测试和契约测试。 + +## 21. 风险与待决事项 + +1. PowerShell 脚本当前未暴露 `-TraceFile`,接口级 trace 复用存在能力差异。 +2. HOME action 只能通过脚本 action 执行,脚本内部会重复获取 token。第一阶段接受该开销,后续若 PAM_HOME 支持 MCP 或 Python client 后再优化。 +3. `config.txt` 落地包含敏感字段,需要明确文件权限和清理策略。 +4. 单 IP 是否允许并发需要谨慎评估,第一版不并发。 +5. 真实回滚策略需要业务方确认“失败后是否继续处理后续 IP”的默认行为。 +6. PAM_NODE MCP 工具名称、参数 schema 和返回 schema 需要与 MCP server 对齐。 +7. LLM 结构化输出需要稳定模型和严格 schema 校验,否则只能作为建议,不能驱动真实工具。 +8. Skill 文档如果继续演进,需要建立 SkillPolicy 的解析测试,避免规则变更后 Agent 行为漂移。 +9. 混合执行会同时依赖脚本环境和 MCP 环境,需要在启动前做两类运行时检查。 + +## 22. 推荐第一版取舍 + +第一版只做三件事: + +1. 用 LangGraph 实现 Agent 骨架:Skill 加载、LLM 结构化理解、确认点、固定流程和 fake runner。 +2. 优先接混合工具路由:HOME 脚本 action + NODE MCP,形成符合当前接口能力的智能部署闭环。 +3. 再完善全脚本策略作为离线和兜底路径,不重写现有脚本。 + +这样能先体现智能部署:用户用自然语言发起部署,LLM 负责理解和计划,Skill 负责约束,LangGraph 负责状态和确认,ActionRouter 按 HOME/NODE 归属分发到脚本或 MCP。流程稳定后,再决定是否把脚本能力迁移到 Python 原生实现。