games-development-ai/docs/agent-specs/2026-06-15-SAA-能力API接入-dossier.md
zizi 2b59f53f44 feat(saa): Python agentic 生成系统迁移到 SAA(Spring AI Alibaba)裸图 spike (HJ-AGI-002)
全图 design->generate->validate->[repair环]->scaffold->build->play(九门)->player->emit;build/play 复用既有 harness 子进程。
SAA 完整框架集接入 aigc-server(graph-core/agent-framework/builtin-nodes/observation/openai,3 BOM;Boot3.5.14/Redisson4.4.0 保住)。
双评审(Opus+Codex)9 项对比前必修:LLM超时重试/ProcessBuilder超时修复/采样面对齐/playerFeedback清串味/gatespec强解析/problems归一/recursionLimit兜底/cost/端口参数化。
首轮对比 breakout/whack/flappy:SAA 通过率 2/3 >= Python 1/3。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 07:55:27 +08:00

9.2 KiB
Raw Blame History

SAA 能力 / API / 接入 档案(多路源码调查汇总)

来源:4 个 Claude/Opus 子代理逐行源码取证(API 面 / 能力盘点 / 接入部署 / 用法范式 四视角),彼此交叉验证一致;Codex 第 5 路 51min 卡死已取消,不影响结论(四路已互验)。 基线:/root/oss/spring-ai-alibaba @ tag v1.1.2.2(HEAD 7405a7d),底层 spring-ai 1.1.2。 关联:spike 执行稿 2026-06-15-SAA-AgentScope-spike-execution.md、目标架构 …目标架构-review.md、决策 saa-agentic-infra-decision。


0. 一句话结论(决策级)

  1. 我们的生成流水 = 确定性多步编排 → 用「裸 StateGraph + 自定义 NodeAction」,不用 ReactAgent/flow agent(那是给"LLM+工具自治"的)。repair 环用 addConditionalEdges 回边 + state 计数器,不用 LoopAgent。
  2. 模型:SAA 复用上游 Spring AI 的 OpenAiChatModel/OpenAiApi(SAA 无自有 OpenAI 封装)。baseUrl 指 new-api,每节点一个 OpenAiChatModel bean(共用一个 OpenAiApi,仅 defaultOptions.model 不同),节点按 @Qualifier 取。便宜模型(DeepSeek)同构,有原生 starter。
  3. 状态:MysqlSaver(收 DataSource,直接注入 yudao Druid)作引擎唯一权威 saver + RunnableConfig.threadId(任务id);Redis 作业务层旁路缓存(一张图只能注册一个 saver,注册俩抛异常)。releaseThread(false) 才能长期续跑。
  4. 依赖冲突风险 = 低-中、可控:只引 graph-core + 3 个 BOM;不引 SAA 的 Boot BOM(让项目 3.5.14 胜出);不用 RedisSaver 即避开 Redisson 3.x↔4.x。fastjson/okhttp 双方完全一致;graph-core 不引 web 框架(只 reactor-core),不与 yudao spring-mvc 抢栈。
  5. 运行形态:进程内嵌,抽 GenerationDispatcher 接口挂在现有 AigcTaskService.submitGenerate → completeWithVersion 契约后(HttpWorkerDispatcher | SaaGraphDispatcher 灰度切,框架可换);图用自带阻塞 invoke() 在后台线程池跑(禁 Tomcat/Reactor 线程),图执行不包大事务。独立 worker 留 Phase2。

1. 关键 API(照着写代码那层)

  • 图:new StateGraph(name, KeyStrategyFactory);addNode(id, node_async(NodeAction));addEdge(START,..)/(..,END);addConditionalEdges(from, edge_async(EdgeAction→String), Map<key,目标节点>)(repair 回边靠它,mappings 不可空);compile(CompileConfig)→CompiledGraph。
  • 状态读写契约:节点 NodeAction.apply(OverAllState) 内 读 state.value("k", T.class),写 靠 return Map.of("k", v)(框架按 key 的 KeyStrategy 合并;ReplaceStrategy 覆盖 / AppendStrategy 追加 messages)——不是 setter。
  • 执行:invoke(inputs, RunnableConfig)→Optional<OverAllState>(内部即 stream().last().block(),自带阻塞);stream(...)→Flux<NodeOutput>。
  • 模型(上游三件套):OpenAiApi.builder().baseUrl(NEW_API).apiKey(KEY).build() → OpenAiChatModel.builder().openAiApi(api).defaultOptions(OpenAiChatOptions.builder().model("deepseek-chat").build()).build()。
  • 持久化:MysqlSaver.builder().dataSource(ds).createOption(CREATE_IF_NOT_EXISTS).build() / RedisSaver.builder().redisson(redissonClient).build() → SaverConfig.builder().register(saver).build() → CompileConfig.builder().saverConfig(..).releaseThread(false) → RunnableConfig.builder().threadId("..")。
  • 流式:stream()→Flux<NodeOutput>;instanceof StreamingOutput 区分 LLM token chunk,.message().getText() 取增量。⚠️ 无 streamEvents。

2. 能力盘点:本期(MySQL+Redis only)可用 vs Phase2

✅ 本期即可落(纯 JVM 或仅 MySQL+Redis,覆盖我们核心用例):图编排全集(条件/并行/嵌套子图/循环)、agentic ReAct、工具调用、多 agent(sequential/parallel/routing/conditional/loop)、持久化 checkpoint/resume(MysqlSaver 仅需 MySQL、RedisSaver 仅需 Redis,均已验证零 Nacos/MQ import)、流式、HITL 中断恢复、Hook(10)/拦截器(13)、OpenAI 兼容(new-api)+DeepSeek、A2A 点对点、可观测埋点。

⛔ 需 Phase2 基建:完整 Admin 平台(MySQL+Redis+ES+RocketMQ+Nacos 五件套全硬,缺一启动失败)、A2A-Nacos 服务发现、config-nacos、MCP-Nacos 网关、sandbox(Docker)、RAG 走 ES、Prompt 发布线上热更(写 Nacos)。

两条最易误判:① Admin 整包 ≠ MySQL+Redis 就够——只想用编排引擎就别起 admin,直接用 graph-core+agent-framework 库依赖。② 向量库在本 clone 只有 ES 是具体实现。

3. 接入 game-cloud(依赖冲突解法放最前)

真冲突仅 2 处:

  • C1 Spring Boot:项目 3.5.14(huijing-dependencies BOM 实际生效)vs SAA 要 3.5.8 → 不引 SAA 的 Boot BOM,让 3.5.14 胜(同 minor 补丁前向兼容,jackson/reactor/spring 仅补丁差,实测安全)。
  • C2 Redisson:项目 4.4.0 vs SAA graph-core 用 3.22.0(但 optional=true)→ 不用 RedisSaver 即零冲突;若用,需实测 redisson 4.x 对 RMap/RBucket/RLock 三 API 兼容。
  • 零冲突:fastjson 1.2.83、okhttp 4.12.0 双方一致;graph-core 不引 web 框架。

Maven(只加到 game-module-aigc-server/pom.xml,不动根 POM/BOM):import 三 BOM = spring-ai-bom:1.1.2 + spring-ai-alibaba-bom:1.1.2.2 + spring-ai-alibaba-extensions-bom:1.1.2.2;依赖只加 spring-ai-alibaba-graph-core(图引擎,唯一必需)+ 模型 starter(走 new-api 用 spring-ai-openai)。

reactive↔blocking 桥接:CompiledGraph.invoke() 自带阻塞(内部 .block())→ 在后台 ExecutorService(非 Web/Reactor 线程)调用,否则 block-in-reactor 死锁;超时用 Future.get(timeout) 外包,取消接现有 cancelTask;图执行不包大 @Transactional,跑完单独短事务落库。

最小验证门 E(6 步,mini-desktop 跑):① 加 3 BOM+graph-core → ② mvn -pl …aigc-server -am dependency:tree 断言(boot 全 3.5.14 / redisson 4.4.0 / 无 3.22.0 泄漏 / jackson 2.21.x)→ ③ 写一个纯 Java 节点(不调 LLM)的 hello StateGraph → ④ mvn package 编译门 → ⑤ 单体启动门(验 SAA 与 yudao 自动配置共存无 BeanCreation 失败)→ ⑥ 调 invoke 跑一节点断言返回 state。先不接模型/不接 MysqlSaver/不接 job 链,隔离验"SAA 库能进 game-cloud 进程"这一最便宜命题。

4. 面向我们生成流水的用法范式(7 条,骨架见各子代理报告)

  1. 主干:StateGraph + 每步一个 node_async(NodeAction)(render→generate→validate→scaffold→build→play→emit)。
  2. repair 环:addConditionalEdges("validate", edge_async(s→ ok?"done": cnt>=N?"giveup":"repair"), …) 回边 + state repair_count 计数器;recursionLimit(默认 100,优雅终止非异常)作硬刹车,不替代业务 max-iters。
  3. build/play 节点:自定义 NodeAction 内 new ProcessBuilder(...) shell 调 node/esbuild + CDP 九门 harness,mapper.readTree 收 JSON,waitFor(timeout)/destroyForcibly。别用 sandbox 模块(那是 agentscope 远程容器);骨架抄 LocalFilesystemBackend.java:457-507。
  4. 进度推前端:节点 apply 返回的 Map 里塞一个 Flux → 框架自动在 stream() 上展开 → 出口 @PostMapping(produces=TEXT_EVENT_STREAM_VALUE) Flux<ServerSentEvent<String>>。
  5. checkpoint:MySQL 权威单 saver + releaseThread(false);Redis 业务旁路;同 threadId 二次 invoke 即续跑;time-travel 用 getStateHistory+checkPointId。
  6. 可选 HITL:节点实现 InterruptableAction,interrupt() 里按 state 开关 review_required 返回停/放行(条件式,默认自动放行);恢复 updateState(...)+withResume()+stream(null, cfg)。HumanNode 已废,必须配 saver。
  7. 模型:N 个 OpenAiChatModel bean 全指 new-api、仅 defaultOptions.model 不同,节点 @Qualifier 取;AGENT_MODEL_NAME 不是选模型机制(只是节点 ID + 流式事件前缀)。

5. ⚠️ 六个最易踩的坑(四路交叉验证确认)

  1. HumanNode 整文件被注释、不可用 → 用 CompileConfig.interruptBefore/After + resume()。
  2. 无 streamEvents → stream()→Flux<NodeOutput> + instanceof StreamingOutput。
  3. ReAct 循环默认无限上限(Integer.MAX_VALUE)→ 用 ReactAgent 时生产必挂 ModelCallLimitHook/ToolCallLimitHook;我们用裸 StateGraph 则靠 state 计数器 + recursionLimit。
  4. supervisor / handoff 没实现(仅枚举占位)→ 用 routing 替代。
  5. 一张图只能注册一个 saver(注册俩抛 IllegalStateException)→ MySQL 权威 + Redis 业务旁路(或自写双写装饰器)。
  6. Admin 整包要五件套(MySQL+Redis+ES+RocketMQ+Nacos)→ 只要引擎别起 admin,直接用 graph-core + agent-framework 库。

全量逐行证据(含每个类的 文件:行)见本轮四个子代理报告(API 速查 / 能力矩阵 / 接入部署 / 用法范式手册)。本档案为决策级蒸馏,spike 门 E 与生成图实现直接照此落地。