ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Spring AI 实战指南(六):用 TaoToken 统一 Key 打通 DeepSeek + PGVector + Redis 的企业级 AI Agent 落地架构

Spring AI 实战指南(六):用 TaoToken 统一 Key 打通 DeepSeek + PGVector + Redis 的企业级 AI Agent 落地架构 1. 从单模型 Demo 到企业级 AI AgentSpring AI 多模型接入的真实痛点很多同学用 Spring AI 写第一个 Demo 时都很顺chatClient.prompt(你好).call().content()跑通那一刻确实爽。但一旦要把这套东西放进真实项目问题立刻暴露出来——模型 Key 散落在各个application.yml、测试环境和生产环境混用同一个 Key、想从 DeepSeek 切到别的模型要改十几处配置、向量库和会话缓存各写各的、Agent 调用工具时上下文丢失。这些才是 Spring AI 企业级 AI Agent 落地架构真正要解决的问题。我见过太多项目卡在这一步Demo 能跑但没法交付。核心原因不是 Spring AI 不好用而是缺少一个统一的模型接入层。Spring AI 本身对 OpenAI 兼容协议支持得很好DeepSeek 也兼容 OpenAI 协议所以理论上换模型只需要改base-url和api-key。但现实是你不可能把 Key 硬编码在每个微服务里也不希望每次换模型都重新发版。这篇要做的就是用 TaoToken 作为统一的 Key 与 API 通道管理层把 DeepSeek 调用、PGVector 向量检索、Redis 会话与缓存串成一条可运行的链路。适合谁适合已经会 Spring Boot、想认真做一个能写进简历的 AI Agent 项目的 Java 开发者。读完之后你能在本地跑通「请求 → 向量召回 → 模型响应」的完整闭环而不是只有一个聊天接口。关键词先摆出来Spring AI、DeepSeek、PGVector、Redis、AI Agent。这五个词会贯穿全文每一个都会落到可复制的配置和代码上。2. TaoToken 前置准备统一 Key 与 API 通道管理在动手写代码之前先把模型接入层这件事想清楚。企业级项目里模型调用不应该由业务代码直接持有 Key而应该收敛到一个统一的通道。TaoToken 在这里扮演的角色就是「统一 Key / API 通道管理」——你只需要在 TaoToken 控制台创建一次 Key后续所有 Spring AI 的模型调用都走这个通道换模型、加模型、限流、看用量都在这一层完成。具体操作路径是这样的先到 TaoToken 控制台创建一个 API Key然后在模型对话页面确认你要用的模型 ID比如 DeepSeek 系列。这里有个细节要注意Spring AI 的 OpenAI starter 需要的是 OpenAI 兼容的base-urlTaoToken 的 API 地址是https://taotoken.net/api注意这个地址后面不要带多余的路径Spring AI 会自动拼接/v1/chat/completions这类端点。创建 Key 的入口在控制台的 API Keys 页面建议按环境分 Key本地开发一个、测试环境一个、生产一个。这样做的好处是某个环境的 Key 泄露或超额不会影响其他环境。Key 创建后只显示一次记得立刻存到你的配置中心或本地.env不要提交到 Git。模型 ID 这块DeepSeek 常用的有deepseek-chat对应 V3和deepseek-reasoner对应 R1。如果你要做 Agent 的工具调用建议先用deepseek-chat因为工具调用对模型的 function calling 支持要求更稳定。R1 更适合推理类任务但工具调用场景下响应格式偶尔会有波动。这里要强调一个工程习惯不要把base-url和api-key写死在 Java 代码里也不要每个 Service 各配一份。统一放在application.yml的spring.ai.openai下通过环境变量注入 Key。这样本地、测试、生产三套环境只需要换环境变量代码零改动。如果你还没创建 Key可以先到控制台把 Key 建好后面第 3 节的配置片段会直接用到。整个前置准备大概 5 分钟但这一步决定了你后面换模型时是改一行配置还是改一天代码。3. 可复制配置application.yml Maven 依赖 PGVector 建表这一节是全文最核心的部分所有片段都可以直接复制。先看 Maven 依赖。Spring AI 1.x 的坐标和 0.x 有区别注意版本对齐。核心依赖包括 OpenAI starter用于对接 DeepSeek、PGVector store、Redis、PostgreSQL 驱动。dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-pgvector-store/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdorg.postgresql/groupId artifactIdpostgresql/artifactId /dependency然后是application.yml。这里把 TaoToken 的base-url、Key、模型 ID 三件套配齐同时把 PGVector 和 Redis 的连接信息写进去。注意api-key用环境变量占位不要写明文。spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: deepseek-chat temperature: 0.7 vectorstore: pgvector: index-type: HNSW distance-type: COSINE_DISTANCE dimensions: 1536 datasource: url: jdbc:postgresql://localhost:5432/eduagentx username: postgres password: ${PG_PASSWORD} data: redis: host: localhost port: 6379 database: 0PGVector 需要先启用扩展并建表。维度要和你的 Embedding 模型输出维度一致这里用 1536 作为示例实际以你选用的 Embedding 模型为准。CREATE EXTENSION IF NOT EXISTS vector; CREATE TABLE IF NOT EXISTS knowledge_vector ( id BIGSERIAL PRIMARY KEY, content TEXT NOT NULL, metadata JSONB, embedding vector(1536) ); CREATE INDEX ON knowledge_vector USING hnsw (embedding vector_cosine_ops);配置类里把ChatClient和VectorStore注入进来统一封装成一个AiChatService。这样业务层不直接碰模型换模型只改 yml。Configuration public class AiConfig { Bean public ChatClient chatClient(ChatClient.Builder builder) { return builder .defaultSystem(你是一个企业级 AI 学习助手回答要准确、简洁。) .build(); } }到这里配置层就齐了。三件套Base URL Key Model ID全部落在 ymlPGVector 和 Redis 的连接也准备好了。下一步就是验证这条链路能不能真的跑通。4. 验证请求从向量召回到模型响应的完整闭环配置写完不代表能跑通必须做一次端到端的验证。这一节给出一个完整的问答链路用户提问 → 向量检索召回相关文档 → 拼接 Prompt → 调用 DeepSeek → 返回答案同时把会话写入 Redis。先写一个 RAG 检索服务把 PGVector 的相似度检索封装起来。Service public class RagService { private final VectorStore vectorStore; public RagService(VectorStore vectorStore) { this.vectorStore vectorStore; } public ListDocument search(String question, int topK) { return vectorStore.similaritySearch( SearchRequest.builder() .query(question) .topK(topK) .build() ); } }然后是核心的问答服务把检索结果拼进 Prompt再调用模型。注意这里用ChatClient的 advisor 机制挂上 Redis 会话记忆。Service public class AiChatService { private final ChatClient chatClient; private final RagService ragService; private final StringRedisTemplate redisTemplate; public AiChatService(ChatClient chatClient, RagService ragService, StringRedisTemplate redisTemplate) { this.chatClient chatClient; this.ragService ragService; this.redisTemplate redisTemplate; } public String ask(String sessionId, String question) { ListDocument docs ragService.search(question, 3); String context docs.stream() .map(Document::getText) .collect(Collectors.joining(\n---\n)); String prompt 请根据以下知识回答用户问题若知识中没有相关内容请如实说明。 知识 %s 问题%s .formatted(context, question); String answer chatClient.prompt(prompt) .call() .content(); redisTemplate.opsForList() .rightPush(chat: sessionId, question || answer); return answer; } }验证动作分三步。第一步往 PGVector 里塞几条测试文档确认vectorStore.add()成功。第二步启动应用用 curl 或 Postman 打一个/chat接口观察日志里是否出现向量检索的 SQL 和模型请求。第三步检查 Redis 里chat:{sessionId}这个 List 是否有数据。curl http://localhost:8080/chat?sessionId1001msg什么是Redis缓存穿透如果返回的答案里包含了你在 PGVector 里存的「缓存穿透」相关内容说明向量召回生效了如果答案通顺且带上下文说明 DeepSeek 调用成功如果 Redis 里有记录说明会话记忆也通了。这三步全过闭环就算跑通。实测下来最容易出问题的是 Embedding 维度和 PGVector 表定义不一致报错通常是expected 1536 dimensions, not 1024。遇到这个先检查你的 Embedding 模型输出维度再改表定义。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth跑不通的时候报错信息往往很吓人但归类下来就那么几种。这一节按真实报错逐个拆。401 Unauthorized最常见。原因通常是api-key没注入成功或者环境变量名写错。检查application.yml里的${TAOTOKEN_API_KEY}是否在启动时被正确解析。如果你用 IDEA 启动记得在 Run Configuration 里配环境变量光在.env文件里写是不够的。还有一种情况是 Key 前后带了空格或换行复制的时候容易带上。local proxy failed / connection refused这个报错说明请求根本没发出去通常是base-url写错了。TaoToken 的地址是https://taotoken.net/api不要写成https://taotoken.net/api/v1Spring AI 会自己拼/v1/chat/completions。多写一层路径就会 404 或连接失败。另外检查本地网络是否能正常访问该域名。Error reading choices / choices is null这个报错说明请求发出去了但响应格式解析失败。常见原因是模型 ID 写错比如把deepseek-chat写成了deepseek。还有一种情况是返回了错误 JSON但 Spring AI 按成功响应解析。建议打开logging.level.org.springframework.aiDEBUG把原始响应打出来看。OAuth / token expired如果你用的是某些需要 OAuth 的通道会出现这个。TaoToken 用的是标准 API Key 鉴权正常不会出现 OAuth 报错。如果出现先确认你没有误配其他 starter 的鉴权方式。检查spring.ai.openai下是否混入了其他 provider 的配置。排查顺序建议固定下来先看 Key 是否注入 → 再看 base-url 是否正确 → 再看模型 ID → 最后看响应日志。按这个顺序90% 的问题能在 5 分钟内定位。如果还是不通可以对照接入文档逐项核对或者直接在模型对话页面手动发一条请求确认 Key 本身是有效的。6. 语义一致 CTA把这条链路用到你的项目里到这里Spring AI DeepSeek PGVector Redis 的闭环已经能跑了。接下来怎么把它变成你自己的东西我的建议是先把这条链路跑稳再往上加 Agent 和 Tool Calling。因为工具调用的前提是模型通道稳定、上下文管理清晰否则工具一多排查成本会指数上升。如果你在接入阶段卡住优先看接入文档里面把 Base URL、Key、Model ID 三件套讲得很清楚。想先验证模型本身是否可用可以直接在模型对话页面手动发一条请求确认通道没问题再回到代码。如果你打算长期做编码类 Agent或者要把这套架构用到团队项目里可以了解一下 Coding Plan它在用量和通道管理上更适合持续开发场景。最后给一个实用技巧把chat:{sessionId}的 Redis 结构设计成 List配合LTRIM限制长度避免会话无限增长。热门问题的答案可以用 MD5 做 Key 缓存到 Redis命中直接返回能省下不少 Token。这些优化不复杂但在真实项目里很管用。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进