myxce/docs/Scheduler-Agent-设计文档.md
lei.chen 62dee0edc1 fix(configs): add worker-builtin load indices missing from resources.yaml
worker/src/resource_sync.c init_builtin_load_indices registers 11 load
indices (r15s/r1m/r15m/ut/pg/ls/it/swp/mem/tmp/io) but resources.yaml only
defined r1m/r15s/mem/tmp. The 7 missing ones (r15m/ut/pg/ls/it/swp/io) caused
controller "update_resval: Success" ERRORs and scheduler "unknown resource in
per-worker resvals update" warnings, leaving the worker resource view
incomplete. Add the 7 definitions matching the existing r1m/r15s pattern.

Docs: log bug-1494 (all jobs PEND, scheduler never dispatches; after this
config fix jobs still PEND — remaining suspect is the scheduler→controller
join path after restart), update design doc + verification manual known-issue
tables and root CLAUDE.md cluster-verification status.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-02 07:36:03 +00:00

604 lines
31 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.

# Unified Scheduler Agent Framework — 设计文档
> **项目代号**:scheduler-agent
> **文档版本**:v1.2
> **最近更新**:2026-08-01
> **文档状态**:实施中(Phase 0-1 代码完成;集群验证:读 + submit ✅,control 有 apiserver 预存 bug 待修)
---
## 1. 文档目的
本文档定义"自然语言调度器管理 Agent 框架"的完整设计——一套架构同时适配自研调度器、LSF、Slurm、Kubernetes,让用户通过自然语言完成集群管理操作。
本文档是**设计权威来源(source of truth)**,实施进度、架构变更、决策记录均在此维护。
---
## 2. 背景与项目定位
### 2.1 为什么做
HPC 集群(调度器)的使用者——研究员、运维——操作门槛高。`bjobs`/`squeue`/`bsub` 等命令行语法复杂,跨系统(LSF/Slurm/K8s)命令不互通。AI Agent 可以将这些操作抽象为自然语言。
### 2.2 核心定位
> **我们是调度器开发者,不是壳。**
与 iris-mcp、srunx 等项目的结构性差异:
| 对比维度 | 其他项目 | 本项目 |
|----------|---------|--------|
| 交互方式 | CLI 文本解析 | 内核级嵌入 Agent 原生接口 |
| 事件能力 | 无,只能轮询 | **实时事件流**(自研独有) |
| 授权粒度 | 粗粒度 | 细粒度 RBAC |
| API 能力 | 受限 | 高级 API 原生支持 |
### 2.3 目标用户
- **研究员**:提交作业、查状态、诊断失败原因,用自然语言
- **运维**:集群监控、资源管理、问题排查,用自然语言
### 2.4 市场叙事
- Phase 0-2:自研调度器体验碾压 LSF/Slurm(实时 + 自然语言)
- Phase 3-4:"Any cluster, natural language"
---
## 3. 总体架构
### 3.1 分层架构图
```
┌─────────────────────────────────────────────────────────────────┐
│ Layer 0 · 用户界面层 │
│ 🧑 用户 ──→ 💻 CLI (Claude Code) │
│ → 🌐 Web Chat (自建 Chat UI) │
│ → 🔌 IDE Plugin (Cursor / VS Code) │
└─────────────────────────────────────────────────────────────────┘
│ 自然语言
┌─────────────────────────────────────────────────────────────────┐
│ Layer 1 · 推理规划层 │
│ 🧠 Orchestrator LLM │
│ 意图识别 → 规划 → 工具调用 → 纠错 │
│ (Claude / GPT / 本地模型) │
└─────────────────────────────────────────────────────────────────┘
│ Tool Call (JSON-RPC)
┌─────────────────────────────────────────────────────────────────┐
│ Layer 2 · Agent 核心层 │
│ 🔌 MCP 协议 (stdio/SSH/HTTP) │
│ ⚙️ 核心引擎 (Tool 路由 · Session · 多后端调度) │
│ 📦 统一资源模型 URM (Job/Queue/Node) │
│ 🔐 鉴权 + 审计 (RBAC · 审计日志) │
│ 📋 Session 上下文管理 │
└─────────────────────────────────────────────────────────────────┘
│ Adapter 接口
┌─────────────────────────────────────────────────────────────────┐
│ Layer 3 · 后端适配器层 │
│ 🏆 Native Adapter (自研调度器) ★ 实时事件流 │
│ 📟 LSF Adapter (CLI/Python SDK) │
│ 📡 Slurm Adapter (REST API/CLI) │
│ ☸️ K8s Adapter (client-go/Watch) │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────────┐
│ Layer 4 · 调度器层 │
│ 🚀 自研调度器 (事件总线 · RBAC · 高级 API) │
│ 📡 LSF 集群 (9.x/10.x) · 📡 Slurm 集群 (23.x+) │
│ ☸️ Kubernetes (1.28+) │
└─────────────────────────────────────────────────────────────────┘
```
### 3.2 数据流
```
用户自然语言 → LLM → MCP Tool Call → Agent 核心 → Adapter → 调度器
↑
事件流实时回传(自研独有)
```
---
## 4. 核心设计
### 4.1 统一资源模型(URM)
每个调度器的概念不同。URM 提供通用抽象,同时通过 `extensions` 暴露后端专有能力。
#### 4.1.1 核心类型
```go
type URMJob struct {
ID string `json:"id"`
Name string `json:"name"`
Status JobStatus `json:"status"` // pending | running | succeeded | failed | cancelled | suspended | unknown
RawStatus string `json:"raw_status"` // 原始状态(PEND / RUNNING / Suspended)
Queue string `json:"queue"` // 队列 / partition / namespace
Nodes int `json:"nodes"`
Cores int `json:"cores"`
GPUs int `json:"gpus"`
MemoryBytes int64 `json:"memory_bytes"`
WalltimeSeconds int `json:"walltime_seconds"`
Dependencies []JobRef `json:"dependencies"`
SubmissionTime time.Time `json:"submission_time"`
StartTime *time.Time `json:"start_time,omitempty"`
ExitCode *int `json:"exit_code,omitempty"`
Owner string `json:"owner"`
LogsURL string `json:"logs_url,omitempty"`
Extensions map[string]any `json:"extensions,omitempty"` // 后端专有能力
}
type URMQueue struct {
Name string `json:"name"`
Status string `json:"status"` // active | inactive | draining
NodeCount int `json:"node_count"`
RunningJobs int `json:"running_jobs"`
PendingJobs int `json:"pending_jobs"`
MaxWalltimeSeconds int `json:"max_walltime_seconds"`
DefaultResources *ResourceSpec `json:"default_resources,omitempty"`
Extensions map[string]any `json:"extensions,omitempty"`
}
type URMNode struct {
Name string `json:"name"`
Status string `json:"status"` // idle | busy | down | drain | unknown
CoresTotal int `json:"cores_total"`
CoresUsed int `json:"cores_used"`
MemoryBytesTotal int64 `json:"memory_bytes_total"`
MemoryBytesUsed int64 `json:"memory_bytes_used"`
GPUCount int `json:"gpu_count"`
Features []string `json:"features"`
Extensions map[string]any `json:"extensions,omitempty"`
}
```
#### 4.1.2 设计原则
- **核心字段** = 所有调度器都有,LLM 使用同一套 prompt 理解
- **扩展字段** = `Extensions` map 兜底后端专有能力(fairshare / GRES / tolerations)
- **能力文档动态注入** = LLM prompt 中动态注入当前后端的能力文档
### 4.2 后端适配器层
#### 4.2.1 SchedulerAdapter 接口
```go
type SchedulerAdapter interface {
// ── 读操作 ──
ListJobs(ctx context.Context, filter JobFilter) ([]*URMJob, error)
GetJob(ctx context.Context, id string) (*URMJob, error)
ListQueues(ctx context.Context) ([]*URMQueue, error)
ListNodes(ctx context.Context) ([]*URMNode, error)
GetMetrics(ctx context.Context) (*Metrics, error)
GetJobLogs(ctx context.Context, id string, opts LogOpts) ([]string, error)
// ── 写操作 ──
SubmitJob(ctx context.Context, spec *JobSpec) (*URMJob, error)
CancelJob(ctx context.Context, id string) (*CancelResult, error)
HoldJob(ctx context.Context, id string) error
ReleaseJob(ctx context.Context, id string) error
ModifyJob(ctx context.Context, id string, changes *JobModification) (*URMJob, error)
// ── 高级(可选实现) ──
CreateReservation(ctx context.Context, spec *ReservationSpec) (*Reservation, error)
AnalyzeFailure(ctx context.Context, jobID string) (*FailureAnalysis, error)
// ── 事件流 ──
WatchJobs(ctx context.Context, filter EventFilter) (<-chan JobEvent, error)
}
```
#### 4.2.2 各后端实现策略
| 后端 | 适配器语言 | 实现方式 | 事件流 |
|------|-----------|---------|--------|
| **自研调度器** | **Go** | 调用 apiserver REST API(cgo 桥接 C backend) | ✅ 原生实时事件推送 |
| **LSF** | **Go + Python sidecar** | Phase 0-1: `bjobs`/`bsub` CLI 解析;Phase 2+: Python SDK | ⚠️ 轮询 |
| **Slurm** | **Go** | Phase 0-1: `squeue`/`sbatch` CLI 解析;Phase 2+: slurmrestd | ⚠️ 轮询 |
| **Kubernetes** | **Go** | client-go 直接调用 K8s API Server | ✅ Informer/Watch |
> **⚠️ 已修正(2026-07-26)**:myxce 调度内核是 C(Cap'n Proto + Raft HA),Go 只存在于 `apiserver`(cgo 桥接 `libxce`)和 `xcecli`。因此 Native Adapter 的实现路径是 **mcp-server 调用 apiserver 现成 REST API**,而非进程内 import Go package。
### 4.3 语言选择策略
**主体语言:Go**。
1. **自研调度器是 Go 栈**——Adapter 可 import 内部 client 包(性能最优)
2. **单二进制分发**——ssh 到 login node 不需要装运行时
3. **`os/exec` + stdout 解析**天然适配 CLI-based adapter(LSF/Slurm)
4. **并发/事件流**——goroutine + channel 完美适配 WatchJobs
**分阶段语言演进**:
```
Phase 0-1: 纯 Go (MCP Server + 所有 adapter)
↓ 当需要 LSF C 库 / Python SDK 深度集成时
Phase 2+: Go 主体 + LSF 侧 Python Sidecar(UDS/gRPC 通信)
```
### 4.4 MCP 协议接口(对外暴露的工具集)
通过 MCP Server 暴露给 Claude Code、Cursor 等 MCP 客户端,或自建的 Chat UI。
#### 4.4.1 读工具
| Tool | 说明 |
|------|------|
| `scheduler_list_jobs(filter)` | 列出作业,支持按状态/队列/owner 过滤 |
| `scheduler_get_job(id)` | 获取作业详情 |
| `scheduler_list_queues()` | 列出队列及容量、运行/排队数 |
| `scheduler_list_nodes()` | 列出节点及资源使用情况 |
| `scheduler_job_logs(id, tail?, since?)` | 获取作业输出日志 |
| `scheduler_cluster_metrics()` | 集群整体负载、使用率概览 |
#### 4.4.2 写工具(默认 require confirm)
**Phase 1 已实现(2026-08-01)**:
| Tool | 说明 | confirm 机制 |
|------|------|--------------|
| `scheduler_submit(template, command, ...)` | 提交作业,支持模板(`general_serial` 通用串行脚本)+ 格式化参数;`dry_run=true` 返回 review card | `dry_run` review 模式 + destructive 注解 |
| `scheduler_cancel(id)` | 取消作业(→ action `kill`,默认 SIGTERM) | destructive 注解,客户端确认 |
| `scheduler_hold(id)` | 暂停止作业(→ action `stop`,默认 SIGTSTP) | destructive 注解,客户端确认 |
| `scheduler_release(id)` | 恢复暂停的作业(→ action `resume`,默认 SIGCONT) | destructive 注解,客户端确认 |
> **Phase 1 未做**:`scheduler_modify(id, changes)` —— apiserver 当前无 modify 端点(job control 仅支持 kill/stop/resume/checkpoint),需新增后端端点,推迟。
>
> **confirm 实现说明**:MCP 协议(mcp-go v0.57.0)无原生 `requiresConfirmation` 字段,只有 `ToolAnnotation{ReadOnlyHint, DestructiveHint}`。实现采用两层:① `scheduler_submit` 用 `dry_run` 参数作为协议内 review 门(先返回 resolved JobSpec 供用户确认,再以 `dry_run=false` 真正提交);② cancel/hold/release 标记 `DestructiveHint: true`,由 MCP 客户端(Claude Code)在执行前弹出确认。这与设计 §4.5"写操作默认 require confirm"一致。
#### 4.4.3 高级工具
| Tool | 说明 |
|------|------|
| `scheduler_analyze_failure(id)` | 自动诊断作业失败原因(日志分析 + 退出码 + 资源分析) |
| `scheduler_suggest_resources(description)` | 根据自然语言任务描述推荐资源配置 |
| `scheduler_estimate_wait_time(queue_spec)` | 估算排队时间 |
### 4.5 安全模型
这是作为调度器厂商**最重要的护城河**。Agent 的错误操作会直接侵蚀对产品的信任。
#### 4.5.1 安全链路
```
┌──────┐ ┌──────┐ ┌───────────┐ ┌────────────┐ ┌─────────┐
│ 用户 │ → │ LLM │ → │ Agent 层 │ → │ Audit Proxy│ → │ 调度器 │
└──────┘ └──────┘ └───────────┘ └────────────┘ └─────────┘
↑ │
│ └→ 审计日志
└── 写操作 require confirm
Autopilot 需用户显式启用
```
#### 4.5.2 安全原则
1. **所有写操作默认 require confirm**:LLM 生成命令/参数,展示给用户确认后再执行
2. **Autopilot 模式**:用户通过 `--autopilot` 显式启用后,Agent 可在授权范围内自主执行
3. **完整审计日志**:每条 AI 操作记录 `[timestamp] [user] [action] [params] [confirmed?]`
4. **拒绝不安全操作**:直接修改调度器配置、操作他人作业(无授权时)、sudo/root 操作
5. **操作范围绑定**:用户可通过 prompt 限定 Agent 作用域("只操作我的作业" / "只在这个队列")
#### 4.5.3 凭证模型
- **mcp-server 用服务账号 API Key**(`xce_ak_` 前缀)调用 apiserver,而非透传终端用户 JWT
- API Key 不做 IP 绑定(面向长期运行的自动化客户端,源 IP 不稳定)
- Phase 0 用 `mcp_readonly` 角色(仅 `:view` 权限)—— ✅ 已实施(2026-07-26)
- **Phase 1 引入 `mcp_operator` 角色** —— ✅ 已实施(2026-08-01):5 个 `*:view`(domain all)+ `queue:submit` + `job:control`(domain all)。覆盖 `scheduler_submit` 和 cancel/hold/release 的全部写端点权限。**不含** host:control / cluster:control / rbac:* / file:* / user:*——MCP 服务账号仍不能管理集群配置或其他用户/角色。
- 生成方式:管理员用 `POST /xce/v1/apikeys`(`rbac:control`,仅 cluster_admin)创建,`role_name: "mcp_operator"`。handler 同时注册 key 和 `AddRoleForUser(username, role)`。
### 4.6 事件流设计
#### 4.6.1 当前调研结论(2026-07-30)
**XCE 产品目前零对外实时推送实现。** 所有客户端通信均为 100% 请求/响应模式。
已有(进程内):
- C Controller → `NotificationHandler`(`OnJobCreated`/`OnJobUpdated`/`OnJobFinished`...)→ `DefaultHandler` → 写入内存 `DataStore`
- **事件到进程就停了,没有对外通道**
确认没有的:
- SSE endpoint、WebSocket、gRPC streaming、长轮询、Subscribe/Watch API、Pub/Sub 均不存在
- Gin 已带 `github.com/gin-contrib/sse`(间接依赖),无需额外引入
#### 4.6.2 设计方向
在 apiserver 侧加 **SSE endpoint** + EventBroker,把 `DefaultHandler` 收到的事件同时广播给订阅者。
```
C Controller ──OPCode_push──→ apiserver ──SSE──→ mcp-server / 浏览器
NotificationHandler
↓ 仅需在 handler 加一行
broker.Publish(event)
```
#### 4.6.3 工作量估算
| 任务 | 工作量 | 位置 |
|------|--------|------|
| apiserver 加 SSE endpoint + EventBroker | 1 天 | `controllers/events.go`(新建)|
| NotificationHandler → Broker 转发 | 0.5 天 | `handler.go` 加 publish |
| RBAC `event:subscribe` 权限 | 0.5 天 | `config/rbac.yaml` + middleware |
| schedulerclient 加 `WatchJobs()` | 0.5 天 | `httpclient.go` SSE 解析 |
| mcp-server 集成 | 0.5 天 | Agent 工作流:提交→监控→自动处理 |
**总计 2-3 天。** 决策:Phase 1 走通写操作后再接入。
---
## 5. 实施路线图
### 5.1 Phase 0 — 只读 MCP Server(✅ 已完成代码,待集群验证)
- 实现自研调度器的只读 adapter(ListJobs / GetJob / ListQueues / ListNodes / GetJobLogs)
- 暴露 6 个 MCP 读工具
- 验证:自然语言问"我的作业状态"、"队列里有什么"
**输入**:`schedulerclient` Go package(只读接口)
**输出**:可运行的 MCP Server,连接 Claude Code 后可以用自然语言查集群状态
### 5.2 Phase 1 — 作业提交 + 模板(✅ 已完成代码,待集群验证;2026-08-01)
- **Submit / Cancel / Hold / Release 写操作工具**(✅ 4 个已实现;Modify 因 apiserver 无端点推迟)
- 作业模板(✅ `general_serial` 通用串行脚本已实现;registry 机制支持后续扩展 GPU/array/dependency)
- 严格的参数校验 + review 模式(✅ `scheduler_submit` 的 `dry_run=true` 返回 resolved JobSpec 供用户确认)
- 安全性审查(✅ destructive 注解 + `mcp_operator` RBAC 角色)
- **待集群验证**:运行中的 apiserver + 生成 `mcp_operator` API Key + Claude Code MCP 配置(同 Phase 0 阻塞项)
### 5.3 Phase 2 — 监控 + 纠错 loop(+1 周)
- Agent 工作流:提交 → WatchJobs 接收事件 → 发现失败 → 分析日志 → 建议修复
- **自研调度器的实时事件流是核心差异点**:作业状态变化时 adapter 直接推送事件,LLM 自动触发处理
- 与 LSF/Slurm 适配器的"轮询"模式形成体验代差
### 5.4 Phase 3 — LSF 适配器(+1 月)
- LSF adapter:先用 CLI 解析(`bjobs -o "id stat queue slots" -noheader`),需要时加 Python Sidecar
- 目标:LSF 用户迁移到自研调度器时体验零摩擦
### 5.5 Phase 4 — Slurm + K8s 适配器(+1 月)
- Slurm adapter:slurmrestd REST API + CLI 回退
- K8s adapter:client-go
- 市场叙事:"Any cluster, natural language"
---
## 6. 代码仓库结构
### 6.1 目标结构(scheduler-agent 全量)
```
scheduler-agent/
├── cmd/
│ └── mcp-server/ # MCP Server 入口
├── internal/
│ ├── server/ # MCP Server 核心
│ │ ├── server.go # JSON-RPC 处理
│ │ ├── tools.go # Tool 定义注册
│ │ └── session.go # Agent session 状态管理
│ ├── urm/ # 统一资源模型
│ │ ├── job.go
│ │ ├── queue.go
│ │ └── node.go
│ └── adapters/ # 后端适配器
│ ├── native/ # 自研调度器
│ │ └── adapter.go
│ ├── lsf/ # LSF
│ │ ├── adapter.go
│ │ └── parser.go # CLI 输出解析
│ ├── slurm/ # Slurm
│ │ ├── adapter.go
│ │ └── parser.go
│ └── k8s/ # Kubernetes
│ └── adapter.go
├── test/
│ └── golden/ # Golden test
```
### 6.2 当前落地位置(myxce 仓库内)
```
myxce/src/frontend/
├── apiserver/ # Go REST API server(cgo 桥接 C backend)
│ └── config/rbac.yaml # 含 mcp_readonly(Phase 0)+ mcp_operator(Phase 1)角色
├── xcecli/ # Go CLI 工具
├── schedulerclient/ # ← Phase 0-1 ✅ scheduler-agent Go client(读 + 写)
│ ├── client.go # Client interface(6 只读 + 4 写方法)
│ ├── types.go # URMJob / URMQueue / URMNode
│ ├── statusmap.go # 状态映射(含单测)
│ ├── httpclient.go # resty HTTP 实现(GET)
│ └── write.go # ← Phase 1 写接口:JobSpec / SubmitJobResult / doPost /
│ # SubmitJob / CancelJob / HoldJob / ReleaseJob(含单测)
└── mcp-server/ # ← Phase 0-1 ✅ MCP Server 二进制
├── cmd/main.go # 入口,注册 10 个 MCP Tool(6 只读 + 4 写),stdio server
└── internal/
├── app.go # Tool handlers 连接 schedulerclient(读 + 写)
└── templates.go # ← Phase 1 作业模板 registry + general_serial(含单测)
```
---
## 7. 测试策略
测试是商业化的真正护城河。模型不重要,**验证套件才重要**。
### 7.1 测试分层
```
test/
├── backends/
│ ├── native/ # 自研调度器 mock
│ ├── lsf/ # 需要 LSF 测试集群
│ ├── slurm/ # 需要 Slurm 测试集群
│ └── k8s/ # kind/minikube
├── scenarios/
│ ├── simple_submit.yaml # "帮我跑这个脚本"
│ ├── job_dependency.yaml # "按 A→B→C 顺序提交"
│ ├── job_array.yaml # "做 100 个参数的扫描"
│ ├── failure_diagnosis.yaml # "为什么失败了"
│ ├── multi_step_pipeline.yaml # "提交→监控→分析→重试"
│ └── cross_platform.yaml # 同场景在不同后端的表现对比
├── golden/
│ └── expected_tool_calls/ # 每个 scenario 期望的 tool call 链
└── integration/
└── agent_e2e_test.go # 端到端:自然语言输入 → 验收结果
```
### 7.2 Golden Test 回归
每次 adapter 改动用 golden test 回归——**同样的自然语言输入,产生同样的 tool call 序列**。
---
## 8. 设计原则
### 8.1 Narrow First(先窄后宽)
先吃透自研调度器,再扩展。自研调度器适配器的体验必须显著优于第三方适配器,形成竞争壁垒。
### 8.2 功能 vs 兼容的取舍
URM 不做最小公分母。核心字段通用,扩展字段用 `extensions` 兜底。LLM prompt 动态注入后端能力文档。
- 通用能力:所有后端一致
- 专有能力:extensions 暴露,模型自动识别是否可用
### 8.3 Write-Protected by Default
所有写操作默认 require confirm。Audit Log 是产品的一等公民,不是后加的功能。
### 8.4 LLM 是路由器,不是执行器
LLM 负责意图识别、规划、纠错。调度器交互由 adapter 的确定性代码执行。
- 调度器升级只需更新 adapter,不需调优模型
- 错误路径清晰:LLM 幻觉不会传递到调度器操作层
### 8.5 渐进的上下文窗口管理
Agent loop 跨多轮调度器轮次保持上下文:
- 作业 <= 60s:一步到位(提交→等待→返回)
- 作业 > 60s:事件流异步通知,状态持久化,Agent 通过 `GetJob()` 恢复
- 上下文满:auto-compact 历史轮次,保留最新状态摘要
---
## 9. 决策记录(ADR)
### 9.1 ADR-001:mcp-server 用服务账号 API Key 鉴权(已确认)
- **状态**:✅ 已实施(2026-07-26)
- **决策**:mcp-server 用服务账号 API Key(`xce_ak_` 前缀)调用 apiserver,而非透传终端用户 JWT
- **理由**:API Key 面向长期运行的自动化客户端,源 IP 不一定稳定,不做 IP 绑定
- **实现**:`store/apikey.go` + `models/apikey.go` + `controllers/auth.go` + `middleware/validatehandler.go` + `router/routers.go` + `config/rbac.yaml`
### 9.2 ADR-002:schedulerclient 放在 myxce 仓库内(已确认)
- **状态**:✅ 已实施
- **决策**:代码放在 `myxce/` 仓库内新增目录(与 `apiserver`/`xcecli` 同级),不另起独立仓库
### 9.3 ADR-003:Native Adapter 走 apiserver REST API(已确认)
- **状态**:✅ 已确认(2026-07-26)
- **决策**:mcp-server 调用 apiserver 现成 REST API,不是进程内 import Go package
- **理由**:myxce 调度内核是 C,Go 只存在于 apiserver(cgo 桥接)和 xcecli。直接 cgo 链接 `libxce` 绕开 apiserver 复杂度接近重写一次 apiserver
### 9.4 ADR-004:事件流对外通道用 SSE(待实施)
- **状态**:🕐 设计完成,Phase 1 后实施
- **决策**:apiserver 加 SSE endpoint + EventBroker,复用已有 `NotificationHandler` 进程内事件
---
## 10. 当前进度状态(2026-08-01)
### 10.1 已完成
**Phase 0(只读链路)**:
| 任务 | 内容 | 状态 |
|------|------|------|
| Task #1 | 架构与设计文档 | ✅ |
| Task #2 | API Key 鉴权机制(TDD) | ✅ 全绿 |
| Task #3 | `schedulerclient` 只读客户端 | ✅ 33 单测 |
| Task #4 | `mcp-server` MCP 二进制 | ✅ 构建通过 |
**Phase 1(写操作链路,2026-08-01)**:
| 任务 | 内容 | 状态 |
|------|------|------|
| Task #5 | `schedulerclient` 写接口(Submit/Cancel/Hold/Release + JobSpec + doPost) | ✅ 12 单测 |
| Task #6 | `mcp-server` 4 个写工具 + `general_serial` 模板 + `dry_run` review 模式 | ✅ 14 单测 |
| Task #7 | 修复 6 个只读工具注解(原被默认误标 destructive) | ✅ |
| Task #8 | `mcp_operator` RBAC 角色(`queue:submit` + `job:control`) | ✅ 测试全绿 |
**Phase 0-1 完整链路(代码层面就绪)**:
```
用户自然语言 → Claude Code → MCP stdio → mcp-server → schedulerclient → apiserver REST API → 调度器
(读:6 只读工具 + 写:4 写工具)
```
### 10.2 待办(阻塞项)
| # | 阻塞项 | 状态 | 责任方 |
|---|--------|------|--------|
| 1 | 运行中的 apiserver + C 编译环境 | ✅ t1 单节点 controller + apiserver + scheduler + worker 已启动(2026-08-01) | 已完成 |
| 2 | 生成 API Key | ✅ `mcp_operator` key 已生成并验证读 + submit | 已完成 |
| 3 | 配置 Claude Code MCP | 🚫 拿到集群 API 后配置 | 用户 |
| 4 | Phase 1 端到端验证:提交→列表→hold→release→cancel | ⚠️ 部分完成:读 + submit ✅;**hold/release/cancel 返回 HTTP 500**(apiserver control 链路预存 bug,见 bug-1490) | 待修 apiserver |
### 10.2b 本次集群验证发现并修复的 apiserver bug(2026-08-01)
| Bug | 现象 | 修复 |
|-----|------|------|
| whitelist 缺 `/xce/v1/apikeys` | API Key 生成端点 403 "URL is not allowed" | `middleware/whitelist.go` 加 `/xce/v1/apikeys`(bug-1489,已修) |
| control 的 `job_status` int64 断言 | control 返回 "Invalid job_status" | `controllers/jobs.go` 加 `normalizeJobStatus()` 兼容 int/int64/float64(已修) |
| control 链路 500(未修) | hold/release/cancel 返回 500,请求未达 controller | 待排查(bug-1490) |
| 读接口 OrderMap 解析(已修) | `scheduler_list_nodes/queues/jobs` 实体数量对但字段全空 | `httpclient.go` 用 `apiRow` 解析列式格式(bug-1491,已修 + 真实验证) |
| 作业全部 PEND(未修) | 提交的作业从不被调度(scheduler 日志 0 job 行;新作业也 PEND) | 已补 resources.yaml 缺失的 worker 负载指标 + 重建 cluster.bin;剩余疑 scheduler→controller join 链路(bug-1494) |
### 10.3 未决决策项
- [ ] 事件流接口(`SubscribeJobEvents`)对外通道设计 —— 已验证前提成立,待 Phase 2 实施
- [ ] LLM 选型:Claude API / 自部署本地模型 / 混合策略
- [ ] 多集群场景:URM 如何标识作业来源(集群 ID 字段)
- [ ] on-prem vs cloud:客户私有化部署时能否访问 LLM API
---
## 11. 风险与缓解
| 风险 | 影响 | 缓解 |
|------|------|------|
| LLM 幻觉导致错误操作 | 作业丢失/资源浪费 | 写操作 require confirm;LLM 只做路由不做执行 |
| 自研调度器事件流未打通 | Phase 2 体验差 | 已确认进程内事件存在,SSE 通道工作量 2-3 天 |
| LSF/Slurm 轮询延迟 | 体验代差 | 通过 agent 工作流 + 模板缓解 |
| 上下文窗口膨胀 | 长任务监控失效 | auto-compact + 状态摘要策略 |
---
## 12. 附录
### 12.1 相关文档索引
| 文档 | 位置 | 说明 |
|------|------|------|
| 本文档 | `doc/Scheduler-Agent-设计文档.md` | 设计权威来源 |
| 集群部署与验证手册 | `src/docs/chatbot-verification.md` | t1/t2/t3 集群启动 + Phase 0-1 验证步骤(2026-08-01) |
| CLAUDE.md(根目录) | `CLAUDE.md` | AI 实施向导(含最新进度) |
| 架构图源文件 | `architecture_overview` | Graphviz DOT |
| 架构图渲染 | `architecture_overview.svg` | 可视化架构图 |
| 架构图生成脚本 | `generate_diagram.py` | diagrams 库 |
| myxce 产品文档 | `myxce/.kiro/steering/` | 产品/技术/结构/安全等 |
| feature 规格 | `myxce/.kiro/specs/` | 各功能 spec |
### 12.2 关键实现细节备忘
- **认证**:apiserver 的 `GetUsernameFromToken`/`isValidToken` 已支持 `xce_ak_` 前缀,JWT 和 API Key 透明共存(27 处调用点零改动)
- **schedulerclient 内存单位**:假设 apiserver 返回 MB(HPC 惯例),转 bytes 存入 URM(`mbToBytes = 1_000_000` decimal)
- **schedulerclient 认证**:Authorization header 直接传 token(不拼 Bearer,服务端会 TrimPrefix)
- **mcp-server 依赖**:`github.com/mark3labs/mcp-go@v0.57.0`,`replace schedulerclient => ../schedulerclient`
- **写端点 HTTP 状态陷阱(Phase 1)**:`ReturnSuccess` 恒返回 HTTP 200,逻辑状态在 envelope `code` 字段(CreateJob→201,ControlJob→202);只有 `ReturnError` 才把 HTTP 状态设为错误码。`doPost` 检查 `resp.StatusCode()==200` 再 `env.Code==expectedCode`,**不要**要求 HTTP 201/202
- **`run_limit` 分钟陷阱(Phase 1)**:apiserver `CustomInt` 解析裸整数为**分钟**(`minutes*60`),`"HH:MM"` 才是秒。schedulerclient 只发 `"HH:MM"` 字符串;模板 `parseWalltimeSeconds` 拒绝裸整数避免歧义
- **`mem_limit` wire 格式(Phase 1)**:`CustomLimit` 只接受 `"<number> <unit>"` 字符串(`"4096 MB"`),GB=1024MB 二进制;客户端通过 `formatMemory` 发 MB 字符串,模板解析用 decimal 与 `mbToBytes` 保持一致
- **confirm 无原生支持(Phase 1)**:mcp-go v0.57.0 无 `RequiresConfirmation`,仅 `ToolAnnotation` 且 `NewTool` 默认 `destructive=true`(Phase 0 只读工具因此被误标,已在 Phase 1 修复);写操作 confirm 用 `dry_run` review 参数 + destructive 注解实现