FDE W7D2 学习手册 · Spring AI Tool Calling 与 Chat Memory
W7 周级拆解 Day2 · A 级(必须掌握,面试核心)· 约 4h · 学完能讲清"Java 侧如何让模型调本地方法、以及如何管理多轮会话记忆"
本日定位:W2 我们讲了"Tool Calling 原理 + 自研 Agent 工具调度";W7D2 进入 Spring AI 的生产级实现:用 @Tool 声明工具、自动注册到 ChatClient,让模型在 Java 侧调用你的方法;以及 ChatMemory 抽象管理会话记忆(InMemory/Redis/JDBC)。这是把 W2/W4 Agent 能力落到 Java 企业平台的关键。
学完能回答:① Spring AI Tool Calling 怎么定义、怎么注册到 ChatClient、完整执行流程;② Chat Memory 怎么接不同存储、和 W3 会话状态的关系。
使用方法:通读原理 → 重点看「🔧 工程含义」「🎤 面试话术」「⚠ 易错点」→ 做末尾自测清单 → 配合《FDE-W7D2-评测题.md》。候选人背景:36 岁,Java + 大数据 + 医疗保险,回答落到"Java 企业 AI 平台"能力,场景用保险知识库 / 特药理赔 Agent。
一、Tool Calling 概览:让模型调你的 Java 方法
1.1 是什么
Tool Calling(工具调用)让 LLM 在对话中"决定调用哪个本地函数、填什么参数",Spring AI 负责把你的 Java 方法暴露成工具描述(JSON Schema)发给模型,模型返回调用请求后,框架本地执行并把结果回灌模型。
模型 → 决定调 tool + 参数 → Spring AI 本地执行 Java 方法 → 结果回灌 → 模型继续生成
1.2 在 Java 企业平台的典型用途
- 查保单系统(调 Java 服务查数据库)。
- 调用特药目录 API 校验药品是否在目录。
- 触发理赔工单创建(副作用操作需谨慎)。
Tool Calling 是 Agent 的"手"——模型负责"想调什么",Spring AI 负责"安全执行 Java 方法并把结果喂回去"。对保险场景意味着:模型可以实时查真实系统,而不是凭记忆编保单号。
Tool Calling 让模型在对话中调用本地 Java 方法:框架把方法暴露成工具描述,模型决定调哪个、填什么参,框架执行后把结果回灌。它是 Agent 的"手",让模型能查真实系统而非瞎编。
① 工具方法有副作用要小心(如创建工单、扣款),模型可能误调,必须加确认/权限/幂等保护。② 工具描述(description)写不好,模型会错误选择工具或填错参数。③ 工具执行结果要裁剪,过长会撑爆上下文(见 s9)。
二、@Tool 注解定义工具
2.1 定义方式
@Service
public class PolicyTools {
@Tool(description = "根据保单号查询保单基本信息:投保人、险种、生效日、状态")
public String queryPolicy(@ToolParam(description = "保单号,如 PA2024XXXX") String policyNo) {
return policyService.getPolicy(policyNo).toJson();
}
@Tool(description = "校验某药品是否在特药目录,返回是否覆盖及自付比例")
public String checkSpecialDrug(@ToolParam(description = "药品通用名") String drugName) {
return drugCatalogService.check(drugName).toJson();
}
}
2.2 关键注解
| 注解 | 作用 | 建议 |
@Tool | 标记方法为工具 | description 写清"何时用、返回什么" |
@ToolParam | 标记参数 | description 写清格式/示例 |
@ToolResult | 给返回值加描述 | 说明结果含义与结构 |
工具 description 质量直接决定调用准确率——把它当"给模型看的产品文档"写:何时调用、参数格式、返回结构都说清。可用 @JsonClassDescription 给返回 Bean 加描述。
用 @Tool 标记方法、@ToolParam 描述参数,框架自动把方法签名+描述转成工具 JSON Schema。工具 description 是"给模型看的产品文档",写得好调用才准。
三、ToolCallback 与自动注册到 ChatClient
3.1 ToolCallback
每个 @Tool 方法会被扫描成一个 ToolCallback(含工具名、schema、可执行引用)。Spring AI 提供 MethodToolCallback.builder() 或自动扫描 @Bean 工具类。
3.2 注册方式
// 方式A:自动扫描(@Tool 的 Spring Bean 自动注册)
ChatClient client = ChatClient.builder(chatModel)
.defaultTools(policyTools, drugTools) // 直接传工具对象
.build();
// 方式B:显式 ToolCallback
ToolCallback[] callbacks = ToolCallbacks.from(policyTools, drugTools);
ChatClient client2 = ChatClient.builder(chatModel).defaultTools(callbacks).build();
// 调用时也可临时指定
String r = client.prompt().user("查一下保单 PA2024XXXX").tools(policyTools).call().content();
生产推荐把工具做成独立 Tool 类(职责清晰、可测),用 defaultTools 全局注册通用工具,特定场景用 .tools(...) 临时叠加。注意工具数量别太多(schema 占 token、模型选择困难)。
每个 @Tool 方法被转成 ToolCallback;可用 defaultTools(...) 全局注册或 .tools(...) 临时叠加。工具类独立、职责清晰,工具数量别太多以免占 token 和干扰选择。
① 工具数量爆炸会让模型选错工具且 schema 吃掉大量 token,建议按场景分组、按需注入。② 同名工具/重复注册会冲突,注意 bean 命名。③ 工具抛异常要被框架捕获并转成可回灌的"错误结果",别让异常直接炸掉对话链路。
四、工具调用完整执行流程
- 注入:ChatClient 携带工具 schema 请求模型。
- 决策:模型返回
tool_calls(工具名 + 参数 JSON),不生成最终文本。
- 执行:Spring AI 按名字找到 ToolCallback,本地反射执行 Java 方法。
- 回灌:把执行结果作为
tool 角色消息追加进对话历史。
- 续生成:模型拿到结果继续生成,直到不再请求工具、输出最终答案。
Prompt+tools → 模型返回 tool_calls → 框架执行 Java 方法 → 结果追加为 tool message → 模型续生成 → 最终答案
这正对应 W2 自研 Agent 的"模型给 action → 执行器执行 → observation 回灌 → 继续思考"。Spring AI 把 W2 要手写的 tool 调度循环封装成了框架能力。
流程里"回灌"这一步框架自动做,但结果可能被截断/脱敏,否则敏感保单数据进模型上下文有合规风险。保险场景建议工具结果只回灌"必要字段"。
完整流程:请求带工具 schema → 模型返回 tool_calls → Spring AI 本地执行 Java 方法 → 结果作为 tool 消息回灌 → 模型续生成。和 W2 自研 Agent 的"action→observation→thought"循环完全对应。
五、ChatMemory:会话记忆抽象
5.1 是什么
ChatMemory 是 Spring AI 对"多轮对话历史存储"的抽象接口,负责按 conversationId 存取 Message 列表。模型本身无状态,记忆靠外部存储维持。
用户消息 + ChatMemory(按 conversationId) → 拼成多轮上下文 → 模型 → 回复存入 ChatMemory
5.2 核心方法
add(conversationId, message):追加消息。
get(conversationId, n):取最近 n 条。
clear(conversationId):清空会话。
ChatMemory 是"会话状态外置"的标准做法——模型无状态,记忆由平台托管。对保险客服意味着:同一用户的多轮对话能连续,且会话可跨服务实例(用 Redis/JDBC 实现)。
ChatMemory 是 Spring AI 对多轮对话历史存储的抽象接口,按 conversationId 存取 Message 列表。模型无状态,记忆靠外部存储维持,是"会话状态外置"的标准做法。
六、ChatMemory 多种实现(InMemory / Redis / JDBC)
| 实现 | 存储介质 | 适用 | 注意 |
InMemoryChatMemory | JVM 内存 | 单机 demo、单实例 | 重启丢失、不跨实例 |
RedisChatMemory | Redis | 分布式、多实例生产 | 需设过期、序列化 |
JdbcChatMemory | 关系库 | 需持久化审计 | 量大时查询慢、需裁剪 |
Neo4j/Cassandra 等 | 各类存储 | 特定需求 | 按生态选 |
6.1 代码示例
// Redis 实现(生产推荐)
ChatMemory redisMem = RedisChatMemory.builder(redisConnectionFactory).build();
ChatClient client = ChatClient.builder(chatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(redisMem).build())
.build();
// 调用时带 conversationId
client.prompt().advisors(a -> a.param(ChatMemory.CONVERSATION_ID, "user-7788"))
.user("我上次问的特药能报多少?").call().content();
生产几乎都用 Redis/JDBC(跨实例、可持久化、可审计)。InMemory 仅本地调试。选存储要同时考虑:过期策略(防无限增长)、序列化、成本(长会话占 token)。
ChatMemory 有 InMemory(单机 demo)、Redis(分布式生产)、JDBC(审计持久化)等实现。生产用 Redis/JDBC 跨实例,InMemory 只本地调试。要通过 conversationId 关联会话。
① 用 InMemory 部署到多实例会"会话串台/丢失"——生产必须外部存储。② 记忆无限增长会撑爆 token 与成本,必须做窗口裁剪(只取最近 n 条)或摘要压缩。③ 不同用户的 conversationId 必须隔离,否则串号泄露隐私(保险大忌)。
七、与 W2 Tool Calling、W4 Agent 的关系
| 阶段 | 做了什么 | W7D2 的位置 |
| W2 Tool Calling | 讲原理 + 自研工具调度循环 | Spring AI 是 W2 的"生产级封装" |
| W4 Agent | 讲 Agent 架构、Trace 字段、工具编排 | Spring AI Tool + Memory 是 Agent 的 Java 落地 |
| W7D2 | 用 Spring AI 跑通 Tool + Memory | Java 侧生产实现 |
W2 我们手写"模型返回 action → 执行器执行 → observation 回灌"循环;Spring AI 把这套循环内建在 ChatClient 里,还解决了"工具有多少个、怎么注册、记忆存哪"的工程问题。W4 的 Agent 架构(Planner/Executor/Memory/Trace)在 Java 侧正是 ChatClient + Tool + ChatMemory + Advisor(Trace) 的组合。
面试链路要串起来:W2 懂原理 → W4 懂 Agent 架构 → W7D2 能落地成 Spring AI 生产代码。这就是"从原理到工程"的完整叙事,对 36 岁平台岗候选人极加分。
W2 讲 Tool Calling 原理 + 自研循环;W4 讲 Agent 架构;W7D2 用 Spring AI 把两者做成了生产级 Java 落地——ChatClient+Tool+ChatMemory+Advisor 组合对应 W4 的 Agent 组件。
八、Advisor 与 Memory 配合(MessageChatMemoryAdvisor)
8.1 如何接
MessageChatMemoryAdvisor 在调用前从 ChatMemory 取出历史注入 prompt,调用后把本轮消息写回。它是 s4 讲过的 Advisor 链的一员。
ChatClient client = ChatClient.builder(chatModel)
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(redisMem).build(), // 记忆注入
new AuditAdvisor(meterRegistry) // Trace 埋点
).build();
8.2 顺序
- 记忆 Advisor 应在"改写/RAG"之前,保证注入的是完整历史。
- Trace/日志 Advisor 可在最外层,包裹整个调用。
把记忆、RAG、日志、Trace 都做成 Advisor 链,业务方法只写"用户问了什么",治理全在 Advisor。这正是 W7D1 强调的"治理内建"。
用 MessageChatMemoryAdvisor 把 ChatMemory 接进 Advisor 链,调用前注入历史、调用后写回。记忆 Advisor 顺序要在改写/RAG 之前,Trace Advisor 可放最外层。
九、生产注意事项(状态隔离 / 过期 / 成本)
- 状态隔离:conversationId 必须来自可信会话标识(登录态 user_id + session_id),不能由模型/用户乱传,防串号。
- 过期策略:Redis 记忆设 TTL(如 30 分钟无活动过期),Jdbc 定时清理。
- 窗口裁剪:
get(id, n) 只取最近 n 条;超长会话用摘要压缩(另一轮 LLM 把历史压成小结)。
- 成本:每轮都把历史重发给模型,长会话 token 成本线性增长,要监控(呼应 W4 cost 字段)。
- 合规:记忆里可能含用户隐私(保单号、身份证),存储要加密、访问要审计。
① 别把整段无限历史每次全发——成本爆炸且模型后期"记不住前面"(长上下文也有有效窗口)。② 工具结果回灌别带全量敏感字段,要脱敏/裁剪。③ 记忆和工具结果是两回事:记忆是"对话历史",工具结果是"本轮执行产出",都进上下文但要分别管理。
生产三件事:conversationId 强隔离防串号、记忆设过期+TTL 防无限增长、窗口裁剪控成本。保险场景记忆含隐私要加密存储 + 审计访问。
十、与 W3 会话状态的关系
W3 我们从"会话状态管理"角度讲过多轮对话要保持上下文、状态可能跨多 Agent 步骤、需要持久化。W7D2 的 ChatMemory 是 W3 理念的 Spring AI 落地:W3 谈"为什么需要状态外置 + 怎么隔离",W7D2 给出 ChatMemory 接口和 Redis/JDBC 具体实现。两者一脉相承——W3 是设计原则,W7D2 是 Java 实现。
面试这么串:W3 讲"会话状态的设计原则(外置/隔离/持久化/过期)",W7D2 用 ChatMemory(InMemory/Redis/JDBC)把这些原则变成可插拔实现。评测时若被问"W3 和今天关系",答"W3 是 why + 设计,W7D2 是 how + 实现,ChatMemory 是那个抽象接口"。
W3 讲会话状态的设计原则(外置/隔离/持久化/过期),W7D2 的 ChatMemory 是它的 Java 落地实现。W3 是 why+设计,W7D2 是 how+实现,ChatMemory 就是那个抽象接口。
十一、学习资源(中文 / 官方)
- Spring AI Tool Calling 文档(docs.spring.io/spring-ai/reference/api/tools.html)——
@Tool、ToolCallback、注册方式精读。
- Spring AI Chat Memory 文档(docs.spring.io/spring-ai/reference/api/chat-memory.html)——InMemory/Redis/JDBC 实现对照。
- B站:Spring AI 全套 84 集(BV1GfyGBqEm6)——重点看"工具调用""会话记忆"相关集。
- Spring AI GitHub samples——
spring-ai-examples/tool、chat-memory 可运行示例。
十二、面试达标线①:Spring AI Tool Calling 怎么定义和注册
| 步骤 | 做法 | 要点 |
| 定义 | @Tool + @ToolParam 注解方法 | description 写清何时用/参数格式 |
| 注册 | defaultTools(...) 或 .tools(...) | 全局注册 vs 临时叠加 |
| 执行 | 框架自动 ToolCallback 执行 + 结果回灌 | 异常要转可回灌错误结果 |
| 取结果 | call().content() | 模型续生成最终答案 |
达标线①核心:用 @Tool 定义工具(写清 description/参数)、用 defaultTools 注册到 ChatClient、框架自动执行并回灌结果。工具数量别太多,description 决定调用准确率。
十三、面试达标线②:Chat Memory 接不同存储、与 W3 会话状态的关系
- 接不同存储:ChatMemory 是接口,InMemory(单机 demo)、Redis(分布式生产)、Jdbc(审计持久化)都是实现;通过
MessageChatMemoryAdvisor 接进 Advisor 链;调用时用 conversationId 关联。选型看是否跨实例、是否需持久化、成本。
- 与 W3 会话状态关系:W3 讲会话状态的"外置/隔离/持久化/过期"设计原则,W7D2 的 ChatMemory 是这些原则的 Java 落地——ChatMemory 接口 = W3 的抽象,Redis/JDBC 实现 = W3 的持久化方案。一脉相承。
- 生产要点:conversationId 强隔离防串号、设过期 TTL、窗口裁剪控成本、敏感数据加密存储。
十四、W7D2 自测清单
- 能说清 Tool Calling 是什么、为什么叫 Agent 的"手",以及典型用途(查保单/查特药目录)。
- 能用 @Tool + @ToolParam 写一个工具方法,并解释 description 为什么重要。
- 能说出两种注册方式(defaultTools 全局 / .tools 临时)及 ToolCallback 是什么。
- 能画出工具调用完整流程(schema→模型返回 tool_calls→执行→回灌→续生成)。
- 能解释 ChatMemory 是什么、为什么模型无状态需要它。
- 能区分 InMemory / Redis / Jdbc 三种实现的适用场景与注意点。
- 能用 MessageChatMemoryAdvisor 把记忆接进 Advisor 链,并说明顺序。
- 能说出生产三注意:conversationId 隔离、过期 TTL、窗口裁剪控成本。
- 能串起 W2(原理)→ W4(Agent 架构)→ W7D2(Spring AI 落地)的关系。
- 能讲清 W7D2 ChatMemory 与 W3 会话状态设计原则的关系(落地 vs 原则)。
十五、高频面试题速记卡
Q:@Tool 的 description 有什么用?
框架把它转成工具 JSON Schema 给模型看,决定"何时调、填什么参"。写不清模型会选错工具或填错参。
Q:工具怎么注册到 ChatClient?
defaultTools(...) 全局注册,或 .tools(...) 单次叠加;每个 @Tool 方法被转成 ToolCallback。
Q:工具调用完整流程?
带 schema 请求 → 模型返回 tool_calls → 框架本地执行 Java 方法 → 结果作 tool 消息回灌 → 模型续生成最终答案。
Q:有副作用的工具要注意什么?
模型可能误调,必须加权限校验、幂等保护、人工确认;异常要转成可回灌错误结果而非炸链路。
Q:ChatMemory 为什么需要?
模型无状态,多轮对话历史靠外部存储维持(按 conversationId 存取 Message 列表),是会话状态外置。
Q:InMemory / Redis / Jdbc 怎么选?
InMemory 单机 demo;Redis 分布式生产;Jdbc 需审计持久化。生产用外部存储,且要设过期+裁剪。
Q:W7D2 和 W3 会话状态什么关系?
W3 讲设计原则(外置/隔离/持久化/过期),W7D2 的 ChatMemory 是 Java 落地实现,接口对应抽象、Redis/Jdbc 对应持久化。
Q:记忆无限增长会怎样?
token 成本线性涨、超长丢上下文、隐私堆积。要窗口裁剪(取最近 n 条)+ 过期 TTL + 摘要压缩。
Q:W2 和 W7D2 的关系?
W2 讲 Tool Calling 原理 + 自研调度循环;W7D2 用 Spring AI 把循环内建成框架能力,是生产级封装。
Q:conversationId 为什么必须强隔离?
防不同用户串号泄露隐私(保险大忌),且多实例下必须用外部存储按 id 关联会话。
FDE W7D2 学习手册 · Spring AI Tool Calling 与 Chat Memory(面试级)· 配合《FDE-W7D2-评测题.md》自测
📌 待查★ 重要