agent_deply/doc_scripts/PAM_LangGraph_重构设计文档.md
2026-05-29 14:08:50 +08:00

1839 lines
50 KiB
Markdown
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.

# 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 动作固定走脚本 actionPAM_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 走脚本 actionNODE 走 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_<run_id>.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_<AIRPORT_CODE>_<APP_NAME>_<MODULE_NAME>_<VERSION_NUMBER>.json
logs/api_trace_<AIRPORT_CODE>_<APP_NAME>_<MODULE_NAME>_<VERSION_NUMBER>_<run_id>.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_<airportCode>_<applicationName>_<moduleName>_<versionNumber>.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 <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 原生实现。