diff --git a/.agents/README.md b/.agents/README.md index 9e3ad3df..c6af37d5 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -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/** diff --git a/.agents/skills/saa-graph-orchestration.md b/.agents/skills/saa-graph-orchestration.md new file mode 100644 index 00000000..127d807b --- /dev/null +++ b/.agents/skills/saa-graph-orchestration.md @@ -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 + +com.alibaba.cloud.aispring-ai-alibaba-graph-core +com.alibaba.cloud.aispring-ai-alibaba-starter-graph-observation +org.springframework.aispring-ai-starter-model-openai +``` + +**别引**:`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.` 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`。 diff --git a/AGENTS.md b/AGENTS.md index 3af83a1f..25b3aaaf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 |