关键词:Walking Skeleton/状态机(FSM)/Outbox/Idempotency/事件驱动/LLM 契约化
本文落地一条“最短闭环”(人工触发 → 计划 → 审批 → 执行 → 验证 → 归档),强调:确定性的状态机做“合法性与原子落库”,不确定性的 LLM 做“生成、检索、归纳”,两者以“可验证的契约”交汇。
PATCH /case/{id}/transition
;事务内同时写 ops_case
、case_timeline
、outbox
;所有副作用“先出盒,后投递”。LLM 擅长“生成与归纳”,但不提供事务性与幂等保证。生产级 OPS 需要:
1) 可证明的合法迁移(纯函数 FSM)
2) 原子副作用(状态 + 时间线 + 出盒)
3) 至少一次投递 + 消费幂等(Outbox/Inbox Pattern)
原则:确定性包围不确定性。所有非确定性的 AI 产物都转为结构化契约(JSON/YAML + Schema 校验)后,才允许进入状态机的下一跳。
目标:先打通一条最短闭环,可演示、可观测、可回滚。
实现顺序:
plan_proposed
与 evt.plan.proposed.v1
。plan_approved
与 evt.plan.approved.v1
。echo/script
与 k8s rollout
两个适配器。metric_1m
的阈值判定。
6~8. Analyst / Sensor / Librarian:把入口从“人工”扩展到“信号驱动”,把 Planner 从模板升级为基于证据的拟合。flowchart LR
subgraph UI/API
H[Human/UI]
end
subgraph Core[Core Services]
O[Orchestrator\nFSM+Outbox+Timeline]
P[Planner\n(LLM/模板→ plan_proposed)]
G[Gatekeeper\n(策略/OA判定)]
E[Executor\n(Adapters: script/k8s/...)]
V[Verifier\n(SLO阈值)]
A[Analyst\n(规则/统计→ findings)]
S[Sensor\n(oo_locator / ingest)]
L[Librian\n(pgvector RAG)]
end
EV[(Event Bus\nNATS/Redpanda)]
DB[(PostgreSQL/Timescale)]
OO[(OpenObserve/Logs)]
H -- REST --> O
O -- outbox/cmd.* --> EV
EV -- evt.* --> O
P & G & E & V & A & S & L -- evt./cmd. <--> EV
O -- R/W --> DB
E -- logs/refs --> OO
接口与事件最小集
- REST:
- `POST /case/create`,`GET /case/{id}`,`GET /case/{id}/timeline`
- `PATCH /case/{id}/transition`(**唯一改状态入口**;需 `Idempotency-Key` + 建议 `If-Match`)
- `POST /plan/generate`,`POST /gate/eval`,`POST /adapter/exec`,`POST /verify/run`
- 事件:
- `evt.case.transition.v1`(任何迁移事实)
- `evt.plan.proposed.v1`,`evt.plan.approved.v1`
- `evt.exec.step_result.v1`,`evt.exec.done|failed.v1`
- `evt.verify.pass|failed.v1`,`evt.analysis.findings.v1`
- `cmd.plan.generate / cmd.gate.eval / cmd.exec.run / cmd.verify.run`
---
stateDiagram-v2
[*] --> NEW
NEW --> ANALYZING: start_analysis
ANALYZING --> PLANNING: analysis_done
PLANNING --> WAIT_GATE: plan_ready (guard: complete plan)
WAIT_GATE --> EXECUTING: gate_approved
WAIT_GATE --> PARKED: gate_rejected
EXECUTING --> VERIFYING: exec_done
EXECUTING --> PARKED: exec_failed
VERIFYING --> CLOSED: verify_pass
VERIFYING --> PARKED: verify_failed
PARKED --> PLANNING: resume / fix
守卫示例(plan 完整性)
steps
至少 1 个;每个 step 定义 action
、timeout
、retry
;rollback
与 verify
字段必须存在;任何守卫失败都不改变状态,仅记录时间线并返回 409/422。
单事务保证:
1) 更新 ops_case.version, status
2) 追加 case_timeline
3) 写入 outbox
(cmd. 或 evt. 载荷)
幂等与并发:
Idempotency-Key
:命中即返回首次结果,不重复写时间线/出盒。If-Match
/expected_version
:不匹配 → 409/412
。伪代码(Go/Pseudocode):
func Transition(ctx, caseID, event, meta) (Case, error) {
return WithTx(ctx, func(tx Tx) (Case, error) {
c := repo.GetForUpdate(tx, caseID)
allowed, next := workflow.Decide(c.Status, event)
if !allowed { return error(409) }
// guards (schema/plan completeness/risk window etc.)
if err := Guards.Pass(tx, c, event, meta); err != nil { return error(422) }
c.Status, c.Version = next, c.Version+1
repo.Update(tx, c)
timeline.Append(tx, c.ID, event, meta)
outbox.Append(tx, BuildMessages(c, event, meta))
return c, nil
})
}
口号:LLM 只输出“结构化、可验证、可回放”的产物;状态改变永远经 Orchestrator。
evidence_link
)、历史相似案 kb_chunk
、环境约束。{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "ChangePlan",
"type": "object",
"required": ["title","steps","rollback","verify"],
"properties": {
"title": {"type":"string"},
"risk_score": {"type":"number","minimum":0,"maximum":1},
"steps": {
"type":"array","minItems":1,
"items": {"type":"object","required":["action","timeout_s","retry"],
"properties": {
"action": {"enum":["k8s.rollout","script.exec","gateway.flip","noop"]},
"params": {"type":"object"},
"timeout_s": {"type":"integer","minimum":10},
"retry": {"type":"integer","minimum":0,"maximum":3}
}
}
},
"rollback": {"type":"object"},
"verify": {"type":"object"}
}
}
/plan/generate
→ 校验 Schema → 入库 plan_proposed
→ 发 evt.plan.proposed.v1
。Prompt 片段(示意):
System: 你是资深 SRE。产出必须是符合 JSON Schema 的 ChangePlan。不要输出自然语言解释。
User: {问题摘要/证据/限制}
Assistant: {ChangePlan JSON}
event_envelope
)。kb_doc/kb_chunk
(pgvector)做相似案例/Runbook 检索;LLM 用于拟合与注释,生成可执行/可审阅的 Plan。-- 1) 事实源
CREATE TABLE ops_case (
id UUID PRIMARY KEY,
tenant TEXT, title TEXT,
status TEXT NOT NULL,
version INT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE case_timeline (
id BIGSERIAL PRIMARY KEY,
case_id UUID REFERENCES ops_case(id),
event TEXT, actor TEXT, reason TEXT,
correlation_id TEXT, meta JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE outbox (
id BIGSERIAL PRIMARY KEY,
topic TEXT, key TEXT, payload JSONB,
created_at TIMESTAMPTZ DEFAULT now(),
published_at TIMESTAMPTZ
);
CREATE TABLE idempotency (
key TEXT PRIMARY KEY,
response JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
-- 2) Planner/Exec/Verify(示意)
CREATE TABLE plan_proposed (
id UUID PRIMARY KEY,
case_id UUID REFERENCES ops_case(id),
plan JSONB NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE plan_approved (
id UUID PRIMARY KEY,
case_id UUID REFERENCES ops_case(id),
decision JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
case_transition_total{from,to,event}
conflict_total{reason}
(409/412/422)idempotent_replay_total
outbox_publish_total{topic,status}
/ inbox_dedup_total
adapter_step_latency_seconds
、verify_latency_seconds
trace_id
并写入 case_timeline.meta
。exec_step.*_ref
指向 OO/Loki 的 oo_locator
。# 1) 创建 Case
CASE=$(curl -sX POST /case/create -d '{"title":"p95 spike","tenant":"t-001"}'|jq -r .data.case_id)
# 2) 开始分析
curl -X PATCH /case/$CASE/transition \
-H 'Idempotency-Key:a1' \
-d '{"event":"start_analysis","actor":"analyst@svc"}'
# 3) 生成计划(模板/LLM)
curl -X POST /plan/generate -d '{"case_id":"'$CASE'","template":"k8s.rollout.canary"}'
# Planner 发 evt.plan.proposed.v1 → Orchestrator 判定 plan_ready → WAIT_GATE
# 4) 自动审批
curl -X POST /gate/eval -d '{"case_id":"'$CASE'"}'
# Gatekeeper 发 evt.plan.approved.v1 → EXECUTING
# 5) 执行适配器(示例:script)
curl -X POST /adapter/exec -d '{"case_id":"'$CASE'","adapter":"script","script":"echo ok"}'
# Executor 发 evt.exec.done.v1 → VERIFYING
# 6) 验证通过 → CLOSED
curl -X POST /verify/run -d '{"case_id":"'$CASE'"}'
.
├─ api/ # Gin/Fiber + OpenAPI + 中间件
├─ workflow/ # 纯函数 FSM
├─ plans/ # 纯函数计划生成(模板 + LLM 契约)
├─ gatekeeper/ # 策略引擎(本地规则/OPA/Cedar)
├─ executor/
│ ├─ adapters/ # k8s/script/gateway...
│ └─ runner/ # step 执行/超时/重试
├─ verifier/
├─ analyst/
├─ sensor/
├─ librarian/
├─ internal/pubsub/ # NATS/Redpanda(至少一次 + 去重)
├─ ports/ # Repo/Outbox/Timeline/Idempotency 接口
├─ services/ # Orchestrator 等聚合服务
└─ migrations/ # DDL
case_transition_total / conflict_total / idempotent_replay_total
;Outbox 消费无重复副作用。plan_proposed
入库;DSL 过 schema;守卫挡下不完整计划。exec_failed
并落 PARKED。metric_1m
发现简单异常并自动开 Case。oo_locator
可被 evidence_link
回链;写入 p99 < 2s。1) 事务一致性:禁止在 FSM 回调内做外部 IO;先落地再发事件。
2) 幂等:所有写接口都要 Idempotency-Key
;消费者对 message_id
去重。
3) 并发:SELECT ... FOR UPDATE
+ 版本控制;冲突返回 409/412。
4) 守卫松紧:先宽后紧,避免卡死;策略统一在 Gatekeeper。
5) 可观测:先打指标,再写业务;没有指标就无法排障。
Orchestrator 只做两件事:判定合法迁移(纯函数)+一次事务内记录(状态/时间线/出盒)。其余模块要么被它的命令驱动,要么把“事实事件”交还它,由它来改变世界。LLM 负责提出“更好的方案”,而不是亲自“按下回车”。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。