ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

大模型开发 - 43 客服助手:01_项目构建,用 TaoToken 统一 Key 打通 Spring AI Tool Calling

大模型开发 - 43 客服助手:01_项目构建,用 TaoToken 统一 Key 打通 Spring AI Tool Calling 1. 客服助手项目构建从零搭骨架用 TaoToken 统一 Key 打通 Spring AI Tool Calling大模型客服助手是当前 Java 后端最容易落地的 AI 场景之一用户说一句“帮我查下 101 号订单”系统就能自动识别意图、提取参数、调用后端业务方法再把结果用自然语言返回。这套能力的核心就是 Spring AI 的 Tool Calling工具调用而项目构建阶段最容易卡住的地方往往不是代码本身而是模型接入的 Key 管理——不同平台一个 Key、环境变量到处配、团队协作时还要互相传密钥。我这次的做法是用 TaoToken 统一管理模型 Key一个 Base URL 加一个 Key 就能切换模型项目骨架搭好之后直接跑通工具调用回环。这篇文章面向 Java/Spring 开发者聚焦客服助手从零搭建的第一步项目骨架与依赖选型。你会看到可复制的 pom 依赖、application.yml 配置、TaoToken 统一 Key 的接入方式以及一次本地启动加工具调用回环的完整验证步骤。适合谁适合已经会 Spring Boot、想快速把大模型接进业务系统、但不想在 Key 管理和多平台适配上浪费时间的开发者。读完你手上会有一个能跑的最小工程后续加订单查询、工单创建、退换货这些工具就是往 ToolsService 里加方法的事。我试过把 Key 硬编码在配置文件里结果换模型时改了三处配置还漏了一处调试半天才发现是旧 Key 没清干净。所以这次项目构建阶段就把 Key 统一收口到 TaoToken后面无论换 qwen 还是别的模型只改一个 model 字段。2. TaoToken 前置准备统一 Key 接入 Spring AI 的配置思路在动手写代码之前先把模型接入这一层理清楚。Spring AI 的模型接入本质上是三件事Base URL请求发到哪、API Key身份凭证、Model ID用哪个模型。传统做法是每个平台一套配置DashScope 一套、DeepSeek 一套、Ollama 一套配置文件里堆一堆前缀。TaoToken 的思路是把这三件事统一成一套 OpenAI 兼容的接口你只需要记住一个 Base URL 和一个 Key模型通过 Model ID 区分。TaoToken 官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 接入地址是 https://taotoken.net/api 。注意 API 地址不带 UTM 参数配置里填的就是这个。你需要先去控制台创建一个 API Key控制台入口在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 创建页面在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。创建好之后先复制保存页面刷新后就不再完整显示了。为什么项目构建阶段就要把 Key 统一因为客服助手这类项目后期一定会遇到多模型场景简单问答用便宜的小模型复杂工单理解用强模型测试环境用本地模型。如果一开始就是多套配置后面切换成本很高。统一 Key 之后Spring AI 的配置里只需要一个 base-url 和一个 api-key模型通过 spring.ai.openai.chat.options.model 指定换模型就是改一行。这里要说明一点TaoToken 是模型接入服务不是替代 Spring AI 或编辑器的工具。你的业务代码、Tool 定义、Controller 都还是标准的 Spring AI 写法TaoToken 只负责把模型请求这一层统一掉。这样设计的好处是将来如果某天要换成直连某个平台只需要改配置业务代码一行不动。配置上还有一个细节Spring AI 的 OpenAI starter 默认走 OpenAI 官方地址接入 TaoToken 需要显式指定 base-url。同时要注意 base-url 的路径TaoToken 的 API 根路径是 https://taotoken.net/api Spring AI 会在后面拼接 /v1/chat/completions 这类路径所以配置里填到 /api 即可不要多加 /v1。这个坑我在别的项目里踩过多写一层路径会直接 404。另外Key 的管理建议走环境变量不要写死在 application.yml 里。团队协作时每个人本地配自己的环境变量配置文件进 Git 也不会泄露密钥。IDEA 里在 Run Configuration 的 Environment Variables 里加一行就行命令行用 export 或 set。下面第三节会给出完整的可复制配置。3. 可复制配置pom 依赖、application.yml 与 Tool 定义这一节是项目构建的核心所有配置都可以直接复制。先看 pom.xml。Spring Boot 版本用 3.3.x 以上Spring AI 用 1.0.x 的稳定版。关键依赖是 spring-ai-starter-model-openai因为 TaoToken 提供 OpenAI 兼容接口用这个 starter 最省事。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.3.5/version relativePath/ /parent groupIdcom.example/groupId artifactIdcs-assistant/artifactId version0.0.1-SNAPSHOT/version namecs-assistant/name description大模型客服助手 - 项目构建/description properties java.version17/java.version spring-ai.version1.0.0/spring-ai.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build /project接下来是 application.yml。这里把 TaoToken 的 Base URL、Key、Model ID 三件套配齐。Key 走环境变量 TAOTOKEN_API_KEY避免硬编码。server: port: 8080 spring: application: name: cs-assistant ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: qwen-plus temperature: 0.7 logging: level: org.springframework.ai: DEBUG org.springframework.ai.chat.client.advisor: DEBUG三件套对照表如下方便你核对配置项值说明Base URLhttps://taotoken.net/apiTaoToken 统一接入地址不带 UTMAPI Key环境变量 TAOTOKEN_API_KEY控制台创建勿硬编码Model IDqwen-plus可换成其他支持的模型然后是 Tool 定义。客服助手最典型的两个动作是订单查询和工单创建这里给出 ToolsService 的骨架。注意 Tool 的 description 要写清楚使用场景ToolParam 要写清楚参数约束这是 AI 能否正确提取参数的关键。package com.example.csassistant.service; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Service; import java.util.Map; Service public class ToolsService { Tool(description 根据订单号查询订单详情返回订单状态、金额和下单时间。用户提到订单号时调用此工具。) public MapString, Object queryOrder( ToolParam(description 订单号纯数字字符串例如 20241028001) String orderNo) { // 这里替换为真实业务查询 return Map.of( orderNo, orderNo, status, 已发货, amount, 299.00, createdAt, 2024-10-28 10:30:00 ); } Tool(description 为用户创建一条客服工单。当用户的问题无法通过查询解决或用户明确要求转人工时调用。) public String createTicket( ToolParam(description 工单标题简要描述问题不超过50字) String title, ToolParam(description 问题详细描述包含用户提供的关键信息) String detail) { // 这里替换为真实工单系统调用 return 工单创建成功工单号TK System.currentTimeMillis(); } }ChatClient 的配置里把 ToolsService 注册进去这样模型在对话时就能看到这两个工具。package com.example.csassistant.config; import com.example.csassistant.service.ToolsService; import org.springframework.ai.chat.client.ChatClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class ChatClientConfig { Bean public ChatClient chatClient(ChatClient.Builder builder, ToolsService toolsService) { return builder .defaultSystem(你是一个电商客服助手负责查询订单和创建工单。回答要简洁友好涉及订单号时先确认再查询。) .defaultTools(toolsService) .build(); } }Controller 层提供一个流式对话接口方便前端逐字展示。package com.example.csassistant.controller; import org.springframework.ai.chat.client.ChatClient; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient chatClient) { this.chatClient chatClient; } GetMapping(value /ai/chat, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxString chat(RequestParam String message) { return chatClient.prompt() .user(message) .stream() .content(); } }到这里项目骨架就搭完了。目录结构建议按 config、controller、service、tool 分层工具类单独放一个包后面工具多了好管理。启动类就是标准的 SpringBootApplication不用额外加注解。4. 验证请求本地启动与工具调用回环实测配置写完接下来验证。第一步设置环境变量。macOS/Linux 用 exportWindows 用 setIDEA 在 Run Configuration 里加。export TAOTOKEN_API_KEY你的Key然后启动应用mvn spring-boot:run看到 Started CsAssistantApplication 就说明启动成功。如果启动阶段报 401先别急着改代码往下看第五节排查。先验证普通对话确认模型接入通了curl -N http://localhost:8080/ai/chat?message你好你能做什么预期返回一段流式文本说明 Base URL 和 Key 都正确。这一步不通后面工具调用肯定也不通所以先过这一关。再验证工具调用回环。发一条带订单号的请求curl -N http://localhost:8080/ai/chat?message帮我查一下订单20241028001的状态如果工具调用生效你会在日志里看到 Spring AI 打印的工具调用记录类似 ToolCallback 被触发、参数是 orderNo20241028001。返回内容里会包含“已发货”“299.00”这些来自 queryOrder 方法的数据。这就是一次完整的回环用户自然语言 → 模型识别意图 → 提取参数 → 调用 Java 方法 → 结果回传模型 → 模型组织语言返回。再测工单创建curl -N http://localhost:8080/ai/chat?message我的快递三天没动了帮我转人工预期模型调用 createTicket返回工单号。如果模型只是回复“请联系人工客服”而没有调用工具说明工具描述不够明确回到第三节调整 Tool 的 description。验证阶段有个小技巧把 logging.level.org.springframework.ai 设成 DEBUG日志里能看到完整的请求和工具调用链路排查问题非常直观。我实测下来工具调用是否触发看日志比看返回文本更准因为模型有时会“假装”调用了工具实际只是编了个结果。5. 本篇常见错排查401、工具不触发与参数提取失败项目构建阶段最容易遇到的几个报错这里逐个对照。401 Unauthorized。这是最常见的。报错信息通常是401 Unauthorized from POST https://taotoken.net/api/v1/chat/completions。原因有三个环境变量没设置、Key 复制不完整、Key 已失效。排查顺序是先 echo 环境变量确认有值再确认 Key 前后没有空格最后去控制台确认 Key 状态。注意 IDEA 里改了环境变量要重启 Run Configuration 才生效热部署不会重新读环境变量。local proxy failed 或连接超时。这类报错说明请求根本没发出去通常是 base-url 写错或者网络层有问题。确认 application.yml 里 base-url 是 https://taotoken.net/api 不要多写 /v1也不要少写 /api。如果公司网络有出口限制确认能正常访问该域名。reading choices 相关报错。比如Cannot deserialize value of type ... from Object value (token JsonToken.START_OBJECT)或者提示 readingchoices字段失败。这通常是模型返回格式和 Spring AI 预期不一致常见于 Model ID 填错、填了一个不支持 chat completions 的模型。确认 model 字段是对话模型比如 qwen-plus而不是 embedding 或图像模型。OAuth 或鉴权相关报错。如果你看到 OAuth、token expired 这类字样说明请求被当成需要 OAuth 的接口处理了。检查是不是 base-url 填成了别的地址或者 Key 类型不对。TaoToken 的 API Key 直接放在 Authorization Bearer 头里Spring AI 会自动处理不需要额外配 OAuth。工具不触发。模型只返回文本日志里没有 ToolCallback 记录。排查三步确认 ToolsService 上有 Service确认 ChatClient 配置里调用了 defaultTools确认 Tool 的 description 写清楚了使用场景。description 太简单比如只写“查询订单”模型可能不确定什么时候用。写成“根据订单号查询订单详情用户提到订单号时调用”就明确多了。参数提取失败。模型调用了工具但参数是 null 或错误值。这通常是 ToolParam 描述不够。比如订单号参数写“订单号”不如写“订单号纯数字字符串例如 20241028001”。给出格式和示例提取准确率会明显提升。中文乱码。返回内容出现问号或方块。在 application.yml 里加编码配置server: servlet: encoding: charset: UTF-8 enabled: true force: true还有一个容易忽略的点Spring AI 版本和 Spring Boot 版本要匹配。Spring AI 1.0.x 对应 Spring Boot 3.3.x 以上版本不匹配会出现 Bean 创建失败或 starter 找不到的情况。pom 里用 spring-ai-bom 统一管理版本避免手动指定每个依赖的版本号。6. 后续扩展与接入文档把客服助手继续做深项目骨架跑通之后客服助手的扩展方向很清晰。工具层面往 ToolsService 里继续加方法就行退换货申请、物流查询、优惠券核销、修改收货地址每个方法加 Tool 注解ChatClient 配置里不用改因为 defaultTools 传的是整个 ToolsService 实例。业务层面把内存里的模拟数据换成真实数据库查询工具方法内部调 Repository 即可模型侧完全无感。对话记忆是下一个值得加的能力。现在每次请求都是独立的用户说“查一下订单”之后再说“那帮我退了吧”模型不知道“那个”指哪个订单。加上 ChatMemory 之后多轮上下文就能串起来。Spring AI 提供了 MessageChatMemoryAdvisor在 ChatClient 配置里加一个 defaultAdvisors 就行具体用法可以参考接入文档。模型切换也是统一 Key 带来的便利。测试环境想用便宜模型把 application.yml 里的 model 改成对应 Model ID 即可Base URL 和 Key 都不用动。如果要做 A/B 测试甚至可以按请求动态指定模型Spring AI 的 ChatOptions 支持运行时覆盖。需要提醒的是工具方法里涉及写操作创建工单、取消订单时一定要在 Java 代码里做参数校验和权限判断不能只靠 ToolParam 的描述约束模型。模型可能被诱导传入非法参数业务层的校验是最后一道防线。另外工具方法不要直接连生产库做写操作建议走业务服务层保留事务和审计日志。如果你在配置过程中遇到接入问题可以对照接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 检查参数。想先验证模型对话是否正常可以用模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 发几条消息测试。如果这个客服助手项目后续要长期迭代、加更多 Agent 能力可以考虑 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 适合长期编码和 Agent 场景。Key 管理在 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 建议给不同项目建不同的 Key方便按项目统计用量和随时吊销。最后留一个实操建议项目构建完成后先写一个最简单的工具比如返回当前时间确认整条链路通了再往里加业务工具。这样出问题时能快速定位是配置问题还是业务代码问题。骨架通了后面就是堆工具和调 prompt 的体力活。
返回列表