程序性记忆以 @Tool 注解的方法实现,位于查询真实数据库表的 Spring 组件中。下面是两个具有代表性的简化方法;完整类包含六个工具(见 AgentTools.java):
@Tool(description ="Look up the status of a customer order by its order ID. "+"Returns the current status including shipping information.")publicStringlookupOrderStatus(@ToolParam(description ="The order ID to look up, e.g. ORD-1001")String orderId){// Fetches order from DB via JPA, returns formatted status string}@Tool(description ="Initiate a product return for a given order. "+"Validates the order exists, checks that it is in DELIVERED status, "+"and verifies the return is within the 30-day return window.")publicStringinitiateReturn(@ToolParam(description ="The order ID to return")String orderId,@ToolParam(description ="The reason for the return")String reason){// Validates order exists, checks DELIVERED status and 30-day window, updates status via JPA}技术支持
@RestController@RequestMapping("/api/v1/agent")publicclassAgentController{publicAgentController(ChatClient.Builder builder,JdbcChatMemoryRepository chatMemoryRepository,JdbcTemplate jdbcTemplate,AgentTools agentTools){// Builds a ChatClient with:// - MessageChatMemoryAdvisor (episodic: last 100 messages per conversation)// - RetrievalAugmentationAdvisor + OracleHybridDocumentRetriever (semantic: hybrid search)// - AgentTools via .defaultTools() (procedural: 6 @Tool methods)// - System prompt defining the agent persona and tool usage rules}@PostMapping("/chat")publicResponseEntity<String>chat(@RequestBodyString message,@RequestHeader("X-Conversation-Id")String conversationId){// Sends message to ChatClient with conversation ID, returns LLM response}@PostMapping("/knowledge")publicResponseEntity<String>addKnowledge(@RequestBodyString content){// Inserts text into POLICY_DOCS table via JDBC (hybrid index handles embedding)}}技术支持
两个端点、两个 advisor、六个工具、一个 ChatClient。下面分别拆解三类记忆。
用 Spring Advisors 管理聊天记忆
Spring AI 的 advisor 模式是“魔法”所在。Advisor 会拦截每次对 LLM 的调用,能在请求发出前修改提示词,并在响应返回时处理结果。
publicList<Document>retrieve(Query query){// Builds a JSON spec with hybrid index name, RRF scorer, vector + text search clauses// Calls DBMS_HYBRID_VECTOR.SEARCH via JDBC, parses JSON results into List<Document>}技术支持
这在检索环节完全绕过了 OracleVectorStore。text.contains 子句使用普通关键词 OR 匹配(超过 2 个字符的词),由 Oracle Text 内置词干提取处理词形变化。
defsend_message(prompt, url):# POSTs plain text to /api/v1/agent/chat with X-Conversation-Id header# Renders user message and assistant response in Streamlit chat UIif prompt := st.chat_input("Type a message..."):
send_message(prompt, backend_url)技术支持
它为每个会话生成一个 UUID 作为会话 ID,向后端发送纯文本并渲染响应。就是这么简单。
在本地运行 AI 代理 POC
简要步骤:启动 Oracle DB 容器,加载 ONNX 模型并创建混合索引(一次性设置),安装 Ollama 并拉取聊天模型(qwen2.5),使用 local 配置文件运行 Spring Boot 后端,并可选启动 Streamlit UI。嵌入由 ONNX 模型在数据库内处理,无需 Ollama 的嵌入模型。完整安装说明请见repo README。
用 curl 快速测试:
curl-X POST http://localhost:8080/api/v1/agent/chat \-H"Content-Type: text/plain"\-H"X-Conversation-Id: test-1"\-d"What orders do I have?"技术支持
结语
此方案是用 Spring AI 和 Oracle 数据库构建真实代理的强力默认选项,因为它把所有记忆层整合到一套可运维栈中:情景上下文、语义检索与程序性动作。Spring AI 提供干净的抽象(Advisors + @Tool),使代理行为保持可读、可测试;而 Oracle 在同一系统中处理事务性数据与混合检索,借助库内嵌入与 RRF 排名,既提高语义又兼顾精确词项。
所有 LLM 都有同样的问题:对话一结束就把一切忘光,有时甚至在长对话中就开始遗忘。花二十分钟解释项目设置、约束条件和您的偏好,它就能给出很准确的回答。关掉标签页、开启新会话,它又像初次见面一样打招呼。所有那些上下文,都没了。
如果您想构建一个 AI 代理——能真正记住上下文、了解您领域知识的系统——就需要给它“记忆”。是那种实用的记忆,能确实记住您说过的话,并能查到您教给它的事实。
这是一份我为此目的构建的 POC。三种记忆、一个数据库、很少的代码。完整源码在 GitHub 上可用。
使用 Spring AI 的 AI 代理架构
图 1. 使用 Spring AI 和 Oracle AI Database 26ai 的端到端代理记忆架构
技术栈:
三种 AI 代理记忆类型
谈到代理,通常会涉及情景记忆、语义记忆、程序性记忆和工作记忆。
工作记忆就是 LLM 的上下文窗口;当前请求的活动“草稿本”。它不持久化,所以无需构建。我实现了另外三种:
情景记忆:持久化聊天历史
情景记忆 就是聊天历史。代理会记住您在对话早些时候说过的话。第 1 条消息“我叫 Victor”,到了第 50 条它仍然知道您的名字。这些以行的形式存储在关系表中。
图 2. 情景记忆即聊天历史
语义记忆:集成 RAG
语义记忆 是领域知识。您向代理提供事实(产品文档、公司政策等),它在回答问题时检索相关内容。
这就是 RAG(检索增强生成):文本被转换为密集向量(嵌入),将语义映射到几何空间,使语义相近的文本彼此靠近。
但纯向量搜索也有盲点;它擅长捕捉含义,却不擅长精确词项,比如订单 ID 或产品编码。
因此此 POC 使用 Oracle 的混合向量索引,并行运行向量相似度搜索与关键词(词汇)搜索,然后使用互惠排序融合(RRF)将两份结果列表融合。
在查询时,混合搜索会找到按语义或精确词项匹配的文档,并将前几条结果注入 LLM 提示词。嵌入由 ONNX 模型(all-MiniLM-L12-v2,384 维)在数据库内直接计算,无需外部嵌入 API 调用。下面将详述其工作方式。
图 3. 语义记忆即领域知识
程序性记忆:LLM 工具调用
程序性记忆 是“如何做”,即代理会执行的分步工作流:查询订单、发起退货、升级至支持。在 Spring AI 中,这些是带有
@Tool注解的方法,LLM 在判断需要采取动作而非仅给出答案时即可调用。图 4. 程序性记忆的实际应用:代理通过工具调用查询实时订单数据并返回与任务相关的结果。
图 5. 通过 AgentController 的请求流转,将来自 Oracle AI Database 各表的情景、语义与程序性记忆合并后,再用 Ollama 生成响应。
两张表都在同一个 Oracle 数据库里。没有 Pinecone。没有 Redis。没有第二个数据库。一个连接池、一套凭据、一个监控对象。
实现 Spring AI 工具
程序性记忆以
@Tool注解的方法实现,位于查询真实数据库表的 Spring 组件中。下面是两个具有代表性的简化方法;完整类包含六个工具(见 AgentTools.java):@Tool的描述告诉 LLM 何时 使用各方法,而@ToolParam描述参数。当用户说“我想退货订单 ORD-1001”时,LLM 会阅读工具描述,判断initiateReturn是正确流程,从对话中提取参数,调用方法,并把结果纳入回复中。另外四个工具是
getCurrentDateTime(从数据库获取当前日期/时间)、listOrders、escalateToSupport和listSupportTickets。它们遵循相同模式:通过 JPA 或 JDBC 查询数据库。LLM 决定何时行动;Java 方法定义如何行动。构建 Spring Boot 控制器
控制器把一切串起来:两个 advisor、六个工具、一个
ChatClient。核心代码如下,为清晰起见做了简化(完整版包含输入校验、错误处理和会话管理端点。见 AgentController.java):两个端点、两个 advisor、六个工具、一个
ChatClient。下面分别拆解三类记忆。用 Spring Advisors 管理聊天记忆
Spring AI 的 advisor 模式是“魔法”所在。Advisor 会拦截每次对 LLM 的调用,能在请求发出前修改提示词,并在响应返回时处理结果。
MessageChatMemoryAdvisor负责情景记忆。每次调用 LLM 前,它会从SPRING_AI_CHAT_MEMORY表加载当前会话最近 100 条消息并前置到提示词中。响应返回后,它会保存这次新的问答交换。会话由X-Conversation-Id头标识:ID 不同,记忆不同。用文档检索器实现 RAG
RetrievalAugmentationAdvisor负责语义记忆。每次调用 LLM 前,自定义的OracleHybridDocumentRetriever会调用 DBMS_HYBRID_VECTOR.SEARCH,并行运行向量相似度搜索与 Oracle Text 关键词搜索,再用互惠排序融合(RRF)合并结果。前 5 个匹配文档会通过自定义
ContextualQueryAugmenter注入到提示词中作为上下文;LLM 被告知这些文档仅用于政策相关问题,而不用于动作请求或对话上下文。这样可避免 RAG 上下文覆盖聊天历史或抑制工具调用。为何选混合而非纯向量搜索?密集嵌入能捕捉含义:关于“退货政策”的查询会匹配到“退款”“换货”的文档,即使没有出现完全相同的词。
但它对精确词项较弱:“ORD-1001”的查询会变差,因为嵌入模型编码语义而非关键词。混合搜索两者兼顾:向量侧负责语义,关键词侧负责精确匹配,RRF 则按名次而非试图归一化不相容的分数来合并两份结果列表。
注册代理工具
AgentTools负责程序性记忆。调用.defaultTools(agentTools)会注册该组件中所有六个带@Tool注解的方法。每个请求中,LLM 会连同用户消息一起接收工具描述。如果任务需要动作而非仅检索知识,LLM 会调用合适的工具,拿到结果并将其编织进回答。Spring AI 会自动处理工具调用协议。三类记忆在每次请求中同时运行。代理既能记住您说过的话、查到相关知识,也知道如何执行任务。
图 6 端到端请求序列:将聊天历史、混合 RAG 上下文和可选工具调用组合,生成并持久化最终响应。
创建 RAG 知识端点
/knowledge端点很简单:POST 一段文本,即通过 JDBC 插入到POLICY_DOCS表。混合向量索引会自动使用库内ONNX 模型 进行嵌入,无需外部嵌入 API。下次有人问相关问题时,混合搜索就会命中它。为 Oracle AI 数据库预置数据
一个
DataSeeder(SpringCommandLineRunner)会在启动时向数据库填充 8 条演示订单与 12 篇政策文档(退货、运输、支持、质保、支付、取消、换货、国际运输、隐私、促销、产品保证及批量订单政策)。策略从policies.json资源文件加载,通过 JDBC 插入POLICY_DOCS表。订单使用相对日期,确保 30 天退货窗逻辑在演示中始终有效。Seeder 会检查现有计数,避免重启时重复插入。升级语义记忆:混合搜索
这个 POC 的首个版本使用了 Spring AI 的 QuestionAnswerAdvisor 搭配 OracleVectorStore:纯向量相似度搜索,加余弦阈值。对干净、表述良好的政策问题效果不错。但在精确词项和拼写错误上就崩了。比如“order ORD-1001”的查询会被拿去跟政策文档做语义匹配,这就不合逻辑。拼成“retrun polcy”的问题也会因为嵌入模型不识别拼写错误而失分。
创建 Oracle 混合向量索引
Oracle 26ai 提供 DBMS_HYBRID_VECTOR.SEARCH,一个同时并行运行向量相似度与 Oracle Text 关键词搜索并融合结果的单一 PL/SQL 调用。
关键在于互惠排序融合(RRF):不再尝试把有界的余弦相似度(0-1)与无界的 BM25 关键词得分做归一化,而是按各自结果列表中的名次来排名。
某文档若在向量结果里排第 1、在关键词结果里排第 3,则合并名次会同时体现两种信号。
设置步骤是一份一次性的 SQL 脚本:将 ONNX 嵌入模型加载到 Oracle AI Database 中,并创建一个 混合索引:
索引创建后,在插入行时会自动计算嵌入——无需外部嵌入 API 调用。
Spring AI 检索集成
Spring AI 的 QuestionAnswerAdvisor 只封装了
VectorStore.similaritySearch(),也就是纯向量搜索,别无他物。要使用混合搜索,我改用了 RetrievalAugmentationAdvisor,这是模块化方案:它接受自定义 DocumentRetriever 和自定义QueryAugmenter来控制如何把检索到的文档注入提示词。自定义的
OracleHybridDocumentRetriever实现了DocumentRetriever,通过 JDBC 调用DBMS_HYBRID_VECTOR.SEARCH,并传入一个 JSON 参数,指定混合索引、打分器(RRF)及关键词匹配:这在检索环节完全绕过了
OracleVectorStore。text.contains子句使用普通关键词OR匹配(超过 2 个字符的词),由 Oracle Text 内置词干提取处理词形变化。为何混合搜索能改进 AI 代理
代理需要精准的检索来做出良好决策。如果语义记忆返回了错误或低置信度的政策文档,LLM 可能会臆造工具参数,或错过本该调用的工具。
混合搜索意味着更高置信度的上下文,也就带来更好的自主决策;当语义与精确词项同时匹配时,代理更不易误引政策或漏掉相关文档。
Spring AI 的数据库配置
配置集中在一个 application.yaml 中。关键设计:Hibernate 自动创建 JPA 表(ddl-auto: update),Spring AI 自动创建聊天记忆表(initialize-schema: always),POLICY_DOCS 表及混合向量索引由一次性 SQL 脚本(setup-hybrid-search.sql)创建,Oracle UCP 在三类记忆中共享同一个连接池。
无需 Flyway,无需自定义
@Configuration类。Spring AI 的自动配置会检测到 Oracle JDBC 驱动并完成装配。详见仓库中的 application.yaml。构建 Streamlit Web UI
Streamlit 前端向后端发送消息并渲染响应。核心代码如下(完整应用包含快速开始按钮和新建会话功能。见 app.py):
它为每个会话生成一个 UUID 作为会话 ID,向后端发送纯文本并渲染响应。就是这么简单。
在本地运行 AI 代理 POC
简要步骤:启动 Oracle DB 容器,加载 ONNX 模型并创建混合索引(一次性设置),安装 Ollama 并拉取聊天模型(qwen2.5),使用 local 配置文件运行 Spring Boot 后端,并可选启动 Streamlit UI。嵌入由 ONNX 模型在数据库内处理,无需 Ollama 的嵌入模型。完整安装说明请见 repo README。
用 curl 快速测试:
结语
此方案是用 Spring AI 和 Oracle 数据库构建真实代理的强力默认选项,因为它把所有记忆层整合到一套可运维栈中:情景上下文、语义检索与程序性动作。Spring AI 提供干净的抽象(Advisors +
@Tool),使代理行为保持可读、可测试;而 Oracle 在同一系统中处理事务性数据与混合检索,借助库内嵌入与 RRF 排名,既提高语义又兼顾精确词项。结果很务实:更少的活动部件、更少的集成失败、更低的延迟、更易的生产运维。无需把聊天历史、向量搜索、关键词搜索和业务表分散在不同系统中拼接,您可以基于一个平台,同时获得高质量检索与可靠的工具执行。对于要交付代理功能的团队而言,这种简单性、准确性与可控性的组合,很难被超越。
想进一步了解 AI 代理及其工作方式,推荐查看 DataCamp 的 AI Agent Fundamentals 技能路径。