docs(saa): 新增 SAA 图编排 skill + 索引(AGENTS §7 沉淀)

知识缺口收口:现有 ai-generation-pipeline.md 是退役 Dify 蓝图、无 SAA 编排 playbook。
新建 .agents/skills/saa-graph-orchestration.md:最小依赖集/拓扑/加节点/接 new-api(剥 /v1 坑)/checkpoint(含 saved_at 无 tiebreaker 框架坑+显式 checkPointId 修法)/observation/派发契约/验证门(门E/门B)。
索引更新 .agents/README.md + AGENTS.md §5。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
zizi 2026-06-16 02:36:41 +00:00
parent b92eba6b06
commit b7786caa40
3 changed files with 83 additions and 0 deletions

View File

@ -55,6 +55,7 @@
| `skills/ui-walkthrough-cdp.md` | 真 UI 走查配方(CDP on mini-desktop):studio/admin 走查 + CDP 七坑 + 信道探针 |
| `skills/game-e2e-cdp-harness.md` | Canvas 游戏 e2e 证据 harness:编排形制/驱动器六律/出厂红线/四件套口径(T1b-α 实证;§7 = W-G1 生成游戏真玩九门 = 假绿守卫 G 门 + H 机制/latch + I 控制手感 + 自产 gatespec) |
| `skills/cheap-model-game-generation.md` | 便宜模型(new-api DeepSeek/MiniMax)直出可真玩轻游戏 L1 worker:链路/模型与成本/L1 纪律/坑红线/latch 套壳/design agent 自产 gatespec/质量三层(W-G1 HJ-GEN-001 实证) |
| `skills/saa-graph-orchestration.md` | SAA(Spring AI Alibaba)裸 StateGraph 生成编排:拓扑/加节点/接 new-api(剥 /v1 坑)/checkpoint(含 saved_at 无 tiebreaker 框架坑+显式 checkPointId 修法)/observation/最小依赖集/派发契约/验证门(HJ-AGI-002 实证) |
**workflows/**

View File

@ -0,0 +1,81 @@
# SAA 图编排 playbook(生成流水 · HJ-AGI-002)
> **定位**:用 **Spring AI Alibaba(SAA v1.1.2.2)裸 `StateGraph`** 在 game-cloud(Java/Boot 3.5.14)里搭"确定性多步生成编排"的标准配方。现行 agentic 基建(HJ-AGI-002,short-term SAA-only)。
> **何时用**:把"渲染→设计→生成代码→[校验/构建/真玩 九门]→修复回环→玩家顾问→出包"这类**确定性多步**流程落成图。**不用 ReactAgent/asNode/subAgents**(那是"LLM+工具自治",与"done 由九门确定性门定、不让 LLM 自评"冲突;自治节点延 Phase1.5,见 [`saa-agentic-infra-decision` 记忆] / `docs/agent-specs/2026-06-15-SAA-AgentScope-agent平台-目标架构-review.md §10`)。
> **参考**:能力/API 速查 `docs/agent-specs/2026-06-15-SAA-能力API接入-dossier.md`;迁移设计(9 步计划/拓扑/坑)`docs/agent-specs/2026-06-15-python-to-SAA-migration-design.md`;落地实现见 `game-module-aigc/.../saa/SaaStudioGraph.java`(唯一布线源)/`SaaGraphDispatcher.java`(生产派发)。
---
## 1. 依赖集(最小证成集,别贪"完整推荐集")
`game-module-aigc-server/pom.xml` 引 **3 个 BOM(仅管版本,不引 spring-boot-dependencies)** + **3 个直接依赖**:
```xml
<!-- BOM:spring-ai-bom:1.1.2 / spring-ai-alibaba-bom:1.1.2.2 / spring-ai-alibaba-extensions-bom:1.1.2.2 -->
<dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-graph-core</artifactId></dependency> <!-- 图引擎+MysqlSaver+observation 机芯 全在此 -->
<dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter-graph-observation</artifactId></dependency> <!-- 仅 Boot 自动配置层 -->
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-openai</artifactId></dependency> <!-- OpenAiApi/OpenAiChatModel,接 new-api -->
```
**别引**:`agent-framework`(ReactAgent/asNode,Phase1.5 才要)、`builtin-nodes`(节点全自写 NodeAction,且它脱 BOM 硬钉 1.1.2.2 拖 tika 全家桶=版本钉债)、**SAA 的 Boot BOM**(让项目 3.5.14 胜,同 minor 补丁前向兼容)、**RedisSaver**(避 Redisson 3.22↔4.4 冲突)。
**铁律**:动 pom 删依赖前,先 `dependency:tree`/读 jar 核**类的模块归属**——`MysqlSaver`、`GraphObservationLifecycleListener` 都在 `graph-core`,删 agent-framework/builtin-nodes 不影响它们。
## 2. 拓扑(声明式条件边,非硬编码 while)
`SaaStudioGraph.assemble()` 是**唯一布线源**(生产派发器 + 回归测试共用,杜绝两处漂移)。节点 11 个、条件边 4 处:
```
START→render→design→generate→validate ─┬(ok)→ scaffold→build ─┬(ok)→ play ─┬(ok)→ player ─┬(ok)→ emit→END
├(repair)→ repair ──────┘(repair)────┘(repair)──────┘(repair)→ generate(回环)
└(giveup)→ giveup→END
```
- 回环/分支用 `addConditionalEdges("validate"|"build"|"play"|"player", router, mapping)`;router 是 `EdgeAction` 返回 `ok|repair|giveup|emit` 等字符串键。
- **超步刹车**:`CompileConfig.builder().recursionLimit(N)`(repair 成环时优雅终止)+ state 里的 repair 计数器,**不靠 LoopAgent**。
- 节点 = `NodeAction` lambda 经 `node_async()` 注册;状态 `OverAllState` + `KeyStrategyFactory`(多数键用 `ReplaceStrategy`)。
**加一个节点**:写 `NodeAction`(读 state→干活→`Map.of(key,val)` 写回)→ `graph.addNode("x", node_async(xNode))` → `addEdge`/`addConditionalEdges` 接进拓扑 → 若新键,在 `KeyStrategyFactory` 注册其合并策略。
## 3. 模型接 new-api(OpenAI 兼容)
五角色(design/code/fix/player_text/player_vision)共用**一个** `OpenAiApi`,仅 `defaultOptions.model/temperature` 异:
```java
OpenAiApi api = OpenAiApi.builder().baseUrl(stripV1Suffix(newApiBase)).apiKey(key).build();
OpenAiChatModel m = OpenAiChatModel.builder().openAiApi(api).defaultOptions(opts).build();
```
- ⚠️ **baseUrl 坑**:Spring AI 自拼 `completionsPath=/v1/chat/completions`,故 `baseUrl` 必须是 **host 根、不带 `/v1`**(`http://100.64.0.8:3000`);同一 `.env`(`NEWAPI_BASE_URL=.../v1`)两侧通用须 Java 侧 `stripV1Suffix` 剥之,否则 404 `/v1/v1/...`。
- 视觉位(M3)走多模态 `UserMessage.builder().media(Media(IMAGE_PNG, ...))`。成本:全走 new-api 单一 key→自动入 `newapi_cost`(logs.quota) 计费平面,不内联折账。
## 4. checkpoint(崩溃续跑 · 迁移 step5)
```java
MysqlSaver saver = MysqlSaver.builder().dataSource(yudaoDruid).createOption(CreateOption.CREATE_IF_NOT_EXISTS).build();
SaverConfig sc = SaverConfig.builder().register(saver).build();
// compile: CompileConfig.builder().saverConfig(sc).releaseThread(false)... run: RunnableConfig.builder().threadId(traceId)
```
- **一图一 saver**(注册俩抛异常);`releaseThread(false)` 才能长期续跑;DataSource 直接收 yudao `DynamicRoutingDataSource`(primary=master)。
- **Flyway 共存**:表 `GRAPH_CHECKPOINT`/`GRAPH_THREAD`(大写引擎私有 DDL,非 Flyway 管)与 yudao 迁移零撞名;测试隔离 `saa_spike` schema,生产 `ruoyi-vue-pro` 零污染。生产**懒建**(仅 dispatcher=saa 且 DataSource 在席时首个 dispatch 触 DDL;http 态零碰 DB)。
- 🔴 **必踩的框架坑(实证)**:`MysqlSaver.saved_at` 是**秒级 TIMESTAMP**,load checkpoint 的 SQL 仅 `ORDER BY saved_at DESC` **无 tiebreaker**。一次 interrupt 在同秒落多条 checkpoint(`__START__→n1` 与 `n1→n2`)→ 默认 `resume()`(取"最新")**非确定**地选其一→续跑点漂移(节点可能重放)。**修法(不改框架)**:用 `getStateHistory` 按内容(`StateSnapshot.node()=="n1"`)确定性选中断点、取其稳定 `config().checkPointId()`,用**显式 checkPointId** 续跑(走 saver by-id 精确分支,绕开 saved_at 排序)。验证见 `SaaCheckpointResumeTest`(真恢复:查库 `GRAPH_CHECKPOINT` 行 + 跨实例 `N1_RUNS==1`)。
- ⚠️ resume 实例**不要再挂 `interruptBefore`**(previousNodeId 非 null 会再次停在该点,后续节点永不执行)。
## 5. 可观测(observation 埋点)
`SaaStudioGraph.build` 在 `ObservationRegistry` 非空且非 NOOP 时挂 graph-core 的 `GraphObservationLifecycleListener` + `CompileConfig.observationRegistry(..)` → 每节点发 `spring.ai.alibaba.graph.node.<id>` observation(trace/耗时/失败→Micrometer)。node id 编码在 observation **名 + 低基数键**里(非自定义 context 类型,测试按名前缀捕获)。`ObservationRegistry` 经 `ObjectProvider` 软注入 yudao 共享 bean。
## 6. 生产派发契约(框架可换、无 split-brain)
- `GenerationDispatcher.dispatch(job)` 接口 → 两实现 `WorkerDispatchClient`(http) | `SaaGraphDispatcher`(进程内 SAA 图) 经 `aigc.executor.dispatcher`(默认 **http** 非破坏) 二选一**单写**。
- `SaaGraphDispatcher`:job→state→后台单线程 `compiledGraph.invoke(threadId=traceId)`→出口组 `DifyCallbackReqVO` **进程内直调 `difyCallbackService.handleCallback`**(消灭 HTTP+HMAC,复用唯一三表同事务写链);串行真玩门 `Semaphore(1)` 守端口;图执行不包大事务(落库走 handleCallback 内短事务);失败统一归 `llm_error`(图内真因不在契约#6 七值枚举内)。
## 7. 验证门(无 harness 端口也能验大半)
- **门 E(依赖/启动)**:`dependency:tree` 断言 Boot 全 3.5.14 / redisson 4.4.0 / 零 3.22 泄漏 / 删的依赖消失;`test-compile`(main+test) 绿;最小 Context 共存不破 BeanCreation。
- **门 B(checkpoint 真恢复)**:纯 Java 2 节点小图(不调 LLM、不碰端口),跑一半 interrupt→查 MySQL 断 checkpoint 落库→全新 `CompiledGraph` 实例 + 显式 checkPointId resume→抵 END 且节点不重放。gated 在 `-Dsaa.checkpoint.mysql=1 -Dsaa.mysql.pass=…`(密码经 `-D` 注入不入库)。
- 真玩 e2e(图真跑九门→进程内 handleCallback→建版本/组包/入 feed)须占 4320/9222,与生成批跑串行(`flock` 单实例锁)。harness-gated 测试无 `-Dsaa.harness=1` 时正确 skip。
---
**关联**:[`contract-first-development.md`](contract-first-development.md)(job/callback 即契约#6)|[`cheap-model-game-generation.md`](cheap-model-game-generation.md)(被编排的便宜模型生成)|[`staging-ops.md`](staging-ops.md)(mini-desktop 构建/门)|决策与坑全集见记忆 `saa-agentic-infra-decision`。

View File

@ -122,6 +122,7 @@ The 5 documents below form a complete picture of the project. **On first onboard
| [`.agents/skills/add-game-template.md`](.agents/skills/add-game-template.md) | New gameplay-template onboarding recipe (contract→prompt→runtime→backend→orchestrator→five-level acceptance gates) |
| [`.agents/skills/ai-generation-pipeline.md`](.agents/skills/ai-generation-pipeline.md) | AI generation pipeline (Dify + OpenGame + aigc shell) dev handbook |
| [`.agents/skills/cheap-model-game-generation.md`](.agents/skills/cheap-model-game-generation.md) | Cheap-model game generation (W-G1): worker loop · nine-gate real-play harness · design-agent self-produced gatespec · cost/model selection · 5 pitfalls (HJ-GEN-001 proven) |
| [`.agents/skills/saa-graph-orchestration.md`](.agents/skills/saa-graph-orchestration.md) | SAA bare-StateGraph generation orchestration: topology / add-node / new-api (strip /v1) / checkpoint (incl. saved_at no-tiebreaker framework pitfall + explicit checkPointId fix) / observation / minimal dep set / dispatcher contract / gates (HJ-AGI-002 proven) |
| [`.agents/skills/prompt-governance.md`](.agents/skills/prompt-governance.md) | Prompt as the 8th contract: Registry / load-inject / eval gates / HITL governance |
| [`.agents/skills/runtime-and-multichannel.md`](.agents/skills/runtime-and-multichannel.md) | Runtime packaging, sandbox, SDK & multi-channel export handbook |
| [`.agents/skills/contract-first-development.md`](.agents/skills/contract-first-development.md) | Contract-first: aligning API/DB/SDK/event contracts & decoupling parallel work |