Skip to content

[Bug]: Feature Request:面向开发者的统一 Redis 接入入口 #32

Description

Checklist

  • 我已经搜索过相关问题,但没有得到预期的帮助。
  • 最新版本中该错误尚未修复。
  • 请注意,如果您提交的Bug描述缺少相应的环境信息和最小可复现的demo,我们将很难复现和解决该问题,从而降低收到反馈的可能性,甚至该问题将被关闭。

🐞 问题详细描述

Feature Request:面向开发者的统一 Redis 接入入口

提出日期:2026-07-29
提出角度:agent 应用开发者(使用 core-java + runtime-java 构建业务 agent 的一方)
上下文:FEAT-003 v2 spec § 5.1.6 "统一可配置 TTL"、demo 侧接入 checkpointer / Todolist / A2A TaskStore 的实际体验


我们(开发者)想要什么

在 agent 应用里,出于业务需要,我们可能要把很多东西保存到 Redis:

  • Checkpointer(agent state / graph / workflow)
  • Todolist(DeepAgent 的 task-scoped 计划)
  • A2A Task 快照
  • 未来还会有更多:会话缓存、模型响应缓存、rate limiter、幂等键、审计事件……

我们希望的开发体验:应用启动时,配置好一次 Redis 连接(host、集群、密码、TTL 默认值等等),拿到一个 Redis 接入句柄,然后所有需要写 Redis 的能力(现在的 + 将来的)都通过这个句柄读写。

至于底层是不是同一个物理 Redis 实例、要不要分 database、要不要 key 前缀分区——这些是我们做部署时才需要考虑的运维决策,不应该在应用代码或者 SDK API 层暴露成"你要给 checkpointer 一套配置、给 Todolist 另一套配置"。


我们目前遇到的事

按当前 core-java 730 + runtime-java develop 的实现,我们(demo 侧)经历了下面这些:

1. 同一份 Redis,配置要在两个地方各自装配

application-redis-checkpointer.yml 里:

  • openjiuwen.service.middleware.checkpointer.type=redis + redis-ref=default → runtime 会造一个 RuntimeRedisClient bean 给 checkpointer 用
  • 但是 Todolist 走的是 DeepAgent.setKvStore(BaseKVStore),这条路 runtime 没自动接线;我们要在应用代码里手动:
    BaseKVStore kvStore = KVStoreFactory.create("redis", Map.of("redis_client", runtimeRedisClient));
    deepAgent.setKvStore(kvStore);
  • 结果:同一个 Redis 实例、同一个 client bean,但是装配路径两条配置消费点两个在应用代码里显式做 bridge

我们的疑问:为什么要我们感知到 checkpointer 和 Todolist 的接入方式不一样?在开发者视角,两者都是"写 Redis",不应该有区别。

2. 增加新的"要写 Redis 的能力"时,我们不知道应该怎么接

假设明天我们想加一个 session-cache(比如缓存 A2A 子 agent 的一些中间态),或者一个 rate limiter,或者别的什么。

我们不知道

  • 应该走 deepAgent.setKvStore(...) 那条路?—— 但这是给 Todolist 设计的,绑在 DeepAgent 生命周期上
  • 还是自己去 runtime 层拿 RuntimeRedisClient bean,然后手动 wrap KVStoreFactory.create("redis", ...)?—— 每个开发者都要写一遍相同的 glue
  • 还是复用 checkpointer 那套 Map<String, Object> conf?—— 那个格式 undocumented,是 core 内部约定

我们的疑问:文档里也好、SDK 契约里也好,我们没找到"新的 Redis 消费者应该沿着哪条 SPI 接入"这个答案。每次都是读代码猜。

3. TTL 是每个消费者各自实现的

  • Checkpointer 有 checkpointer.ttl-seconds 配置,装配处 AgentCoreCheckpointerConfigAssembler 把它塞进 BaseRedisStoragettlSeconds 字段;BaseRedisStorage.save 里手动 pipeline.set(k, v, ttlSeconds)
  • A2A TaskStore 有独立的 TTL 处理(WriteThrottlingTaskStore 那套)
  • Todolist 目前没有 TTL(KvTodoStorage.save 直接调 BaseKVStore.set(k, v),无 TTL 参数、也不后续 EXPIRE)—— key 会永久驻留

开发者视角看到的事

  • Checkpointer 的 key 会过期,Todolist 的 key 不会
  • 但 spec §5.1.6 明明写"简单统一的可配置 TTL"
  • 我们没有一个统一的地方能配"我的应用里所有 Redis 写入都用同一个默认 TTL"
  • 如果我们想给 session-cache 加 TTL,得自己再 patch 一遍 —— 因为 BaseKVStore.set(k, v) 这个签名根本没 TTL 参数,得绕开它

我们的疑问:TTL 是"写 Redis 的一等属性",应该在 SDK 提供给开发者的那个接入入口上就是 first-class 参数,而不是每个消费者自己想办法。

4. Redis 连接细节在两个仓库里各出现一次

从我们(应用侧)视角能看到的:

  • runtime 侧有一整套 MiddlewareProperties.RedisEndpoint + RedisConnectionAssembler + RedisMiddlewareAutoConfiguration + RuntimeRedisClient bean(JedisPooled / JedisCluster 分支),管密码解密、集群、诊断、生命周期
  • core 侧 com.openjiuwen.extensions.store.kv 下也有一套 RedisStore / RedisKVStoreProvider,甚至能自己用反射 new Jedis(host, port)RedisKVStoreProvider.createClientByReflection

开发者视角看到的困惑

  • 我们感受到的是"runtime 提供中间件接入服务,core 提供业务能力" —— 但 core 侧居然有能自己造 Redis 连接的能力
  • 而且现在两套是靠 Map<String, Object> conf + 反射 duck-typing 粘合的(core 的 RedisStore 反射调 runtime 的 RuntimeRedisClient 方法名)—— 这条通路很脆,出问题时排错要跨两个仓
  • 我们不知道应该往哪套 API 写,也不知道哪套是官方推荐的开发者入口

我们的疑问:从开发者视角,能不能只有一个"官方入口",不需要我们理解 core 里为什么还有一套 Redis 实现?

5. 具体现场:三个已经吃过的亏

以下三条不是本 feature request 的范围,但都是"没有统一入口"的直接后果,附在这里作为佐证:

现场 现象 根因(跟统一入口的关系)
gitcode issue #62 Redis checkpointer 因 InteractiveInput 不可序列化,checkpoint 100% 保存失败但静默返回假成功 Checkpointer 走的是 core 侧自建的 Redis path,异常兜底是内部实现细节,开发者感知不到
MUST2 gap #1(跟开发组沟通中) Todolist 保存到 Redis 无 TTL BaseKVStore.set 无 TTL 参数,消费者想加 TTL 得绕开 SPI
MUST2 gap #2 Todolist 配置错误时静默降级到进程内 in_memory,无日志 Provider 在 core 侧能自己造 KV 实现,才有兜底的余地;也没有统一契约要求 fail-fast

我们想请开发组回答 / 讨论的事

我们不给方案。作为消费方,我们希望开发组从架构定位出发,把下面这些讲清楚:

  1. 统一入口是不是 SDK 应该提供的能力?
    即:应用启动配置一次 Redis任意消费者(checkpointer / Todolist / 未来的 X)都从这一个入口取 —— 这个体验目标,开发组认不认可?

  2. 如果认可,Redis 中间件接入是不是应该完全归属 runtime 层?
    core 侧目前的 RedisStore / RedisKVStoreProvider / checkpointer 里的 Redis 装配代码,是不是应该收敛到 runtime?core 只保留 BaseKVStore 之类的抽象 SPI?
    (我们理解 core 需要"能独立跑"的历史约束,但从开发者体验看,"能独立跑"跟"要在 core 里塞 Redis 客户端实现"不是同一件事)

  3. TTL 作为 Redis 写入的一等属性,应该在哪一层?
    现在是每个消费者自己 patch。是不是应该在开发者拿到的那个入口签名上(如 set(key, value, ttlSeconds))就是必选/可选参数?

  4. 未来新增"要写 Redis 的能力"(session-cache / rate-limiter / …),开发组希望我们(消费方)沿着哪条路接?
    希望能有一份明确的官方接入指南,或者能明确说"就走 xxx 这个入口,其他历史 API 不要用"。

  5. 配置层的 middleware.redis.<name> map 结构(现在有 default,未来可能 todo / cache / …)
    我们理解这是运维层选择"用几套物理 Redis"的机制。这个跟"SDK 只暴露一个开发者入口"能不能同时成立?(我们的直觉是能:应用配置层写 name→endpoint 映射,SDK 提供的入口方法可以带一个 redisRef 参数或者干脆按消费者名字自动选,但一个消费者永远只对一个 endpoint,不需要开发者手动 juggle 多个 client)


我们不主张的事(避免误会)

  • 不主张"给 checkpointer / Todolist / 未来 X 各自维护一套 Redis 配置"—— 这是我们明确反对的方向
  • 不主张"应用代码自己在两个仓的 API 之间做 bridge"—— 这是我们现在被迫做的事,也是我们希望消除的
  • 不主张开发组一定要按上面 (1)~(5) 的问法回答—— 上面只是我们试图表达清楚的思路,实际怎么落地由架构组判断

详细的环境信息描述

core java 730分支
runtime Java develop分支

其他辅助信息

版本信息

感谢您的贡献 🎉!

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions