Spring-AI 基础知识

普通概念

Model

  • ChatModel: 基于自回归语言模型,其核心是 Transformer Decoder 架构。它通过“逐字生成”的方式工作:接收一段文本(Prompt),预测下一个最可能的词(Token),然后将新生成的词加入输入,再次预测下一个,如此循环,直到生成完整的回答。这种机制保证了生成文本的上下文连贯性和逻辑性。
  • EmbeddingModel: EmbeddingModel(如text-embedding-ada-002、text2vec-chinese)基于双向编码器,其核心是 Transformer Encoder 架构。与ChatModel不同,它会同时分析整个输入文本的上下文,然后将文本的深层语义信息映射到一个高维、密集的数字向量(Vector)中。在这个向量空间里,语义上相似的文本在空间距离上会更接近。

EmbeddingModel负责检索阶段:就是我们常说的嵌入模型

- 将查询和文档转换为向量表示

- 通过余弦相似度计算相关性

- 检索最相关的K个文档片段

ChatModel负责生成阶段:就是我们常用的聊天的那种模型

- 接收检索到的上下文+用户查询

- 基于上下文生成回答

- 保证回答的连贯性和准确性

那么我们如何选择模型呢?

  1. 维度大小:向量的维度。维度越高,通常能编码更丰富的语义信息,但也会增加存储和计算开销。

    • 示例BERT-base 通常是768维,而OpenAI的 text-embedding-ada-002 是1536维。
  2. 语言支持:模型是否针对特定语言(如中文)进行过优化。对于中文场景,使用专门的中文Embedding模型(如 text2vec-chinese)效果远超通用模型。

  3. 领域适配:通用模型在开放域表现良好,但在专业领域(如法律、医疗),使用经过该领域数据微调过的模型能显著提升准确性。

  4. 性能指标:

    检索准确率 (Recall/Precision):衡量Embedding模型检索到的相关文档的准确度。

    推理延迟 (Latency):模型处理一次请求所需的时间,直接影响用户体验。

比如我自定义知识库使用的就是Embedding v2 ada

重排模型:

VectorStore

VectorStore 负责存储EmbeddingModel生成的向量,并提供高效的相似度检索能力。

特性 Redis Vector Pinecone Weaviate
架构 内存+持久化 云原生分布式 图数据库+向量
索引算法 HNSW/IVF 专有优化算法 HNSW
存储成本 中等(内存占用高) 高(按量计费) 低(开源版免费)
检索延迟 <10ms 10-50ms 20-100ms
扩展性 水平扩展复杂 自动扩缩容 手动扩展
  • 小规模验证 (<10万文档)Redis Vector 是绝佳选择。部署简单,延迟极低,与现有Java生态无缝集成。
  • 中等规模生产 (10万-1000万)Pinecone 提供完全托管的服务,免去运维烦恼,让你专注于业务逻辑。
  • 大规模或定制化场景 (>1000万)Weaviate 的开源和分布式特性提供了极高的灵活性和成本优势,但需要投入运维资源。
  • 成本敏感场景Weaviate 开源版 自建部署是理想选择。

RAG

RAG就是检索增强,是一种让LLM访问外部知识库以回答问题的框架,极大地减少了模型幻觉,提高了回答的准确性。

我们使用嵌入模型的时候是需要对我们传入的文档进行切分的

切分策略:

一般的切分策略就是按照大小进行切分的,一般就是多少个字就切分。这样的话比较简答,但是可能破坏语句的完整性。适用于API,代码等

还有就是按照语义进行切分,这样就是按照句子边界,标点符号进行切分。这样保证了语句的完整性,但是我们切分的效率比较慢。适用于文章报告等

我们切分的时候要去按照场景进行选择

  • 检索准确率:语义切分通常能确保每个Chunk包含完整的答案片段,从而提升准确率。

  • 上下文利用率:合适的Chunk大小可以最大化利用模型的上下文窗口,不多也不少。

  • 重叠比例 (Overlap):设置一部分重叠内容(如10%)可以防止重要信息在切分边界处丢失。

    在Spring AI中,通常通过实现DocumentTransformer接口来定义切分逻辑。


检索策略:

密集检索,就是使用模型调用embed方法,然后调用向量库进行密集搜索。这样的话语义理解强,但是对罕见词汇搜索较差

bm25检索,传统的关键词匹配算法,对罕见词、专业术语友好,但缺乏语义理解。

混合检索,就待用密集检索和bm25检索,然后分析文档。结合了两种检索,但是复杂度增加了

上下文构建

检索到的文档片段不能直接丢给LLM,需要精心组织成“上下文”(Context),以避免噪声干扰。

1.如何避免上下文噪声干扰

  • 重排序 (Reranking):使用更轻量、但更精确的模型(如Cross-Encoder)对初步检索到的Top-K结果进行二次排序,将最相关的文档排在最前面。
  • 上下文压缩 (Context Compression):从检索到的文档中提取与用户问题最相关的句子或摘要,丢弃无关信息,减少噪声。
  • 分层检索 (Hierarchical Retrieval):对于结构化的长文档,可以先检索到相关的章节标题,再在章节内部进行精确检索。
  • 动态上下文长度 (Dynamic Context Length):根据问题的复杂度和模型上下文窗口的限制,动态调整送入模型的文档数量。

在Spring AI中,这些策略通常在调用ChatModel之前,通过自定义逻辑实现。

Function Calling

函数调用允许LLM将自然语言指令转化为对外部工具(API、数据库查询等)的结构化调用。

  • 参数校验:这是安全的第一道防线。可以使用JSON Schema来定义函数期望的参数格式、类型和范围,在执行前进行严格校验。

  • 安全机制

    • 参数沙箱:绝不直接将用户输入用于代码执行或数据库查询,进行严格的无害化处理。
    • 权限控制:根据用户身份,限制其能调用的函数集合。
    • 执行隔离/超时:在独立、受限的环境中执行函数,并设置超时,防止恶意调用消耗系统资源。
    • 审计日志:记录所有函数调用,便于追踪和分析。
  • 错误处理:健壮的错误处理至关重要。需要明确处理参数验证失败(ParameterValidationException)、执行超时(ExecutionTimeoutException)、资源超限(ResourceLimitException)等情况,并设计合理的重试策略(如使用@Retryable

    先捕捉ParameterValidationException,参数验证

    再捕捉ExecutionTimeoutException,执行超时

    再捕捉ResourceLimitException,资源限制

    然后再执行重试策略,加上重试注解

    1
    @Retryable(maxAttempts = 3, backoff = @Backoff(delay = 1000))

在Spring AI中,你可以定义一个@Bean,通过@Description注解描述其功能,Spring AI会自动将其注册为可供LLM调用的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class FunctionParameterValidator {

public ValidationResult validate(FunctionCall call) {
return ValidationResult.builder()
.typeValidation(validateTypes(call.getParameters()))
.rangeValidation(validateRanges(call.getParameters()))
.formatValidation(validateFormats(call.getParameters()))
.businessValidation(validateBusinessRules(call.getParameters()))
.build();
}

private boolean validateTypes(Map<String, Object> params) {
// JSON Schema验证
// 类型强制转换
// null值处理
}
}

Agent

Agent是什么,是一个自主决策的智能体

如何设计多Agent协作的通信机制?

消息总线模式:

- 使用Redis/RabbitMQ作为消息中介

- Agent间异步通信

- 支持广播和点对点通信

协调者模式:

- 中央协调器管理任务分配

- Agent向协调器汇报状态

- 协调器负责冲突解决

契约式协作:

- 定义Agent间的服务契约

- 使用OpenAPI规范描述接口

- 支持版本管理和向后兼容

状态同步机制:

- 共享状态存储(Redis Cluster)

- 乐观锁处理并发冲突

- 事件溯源记录状态变更

决策树/状态机实现:

  • 可以使用状态机来定义Agent的行为逻辑。例如,一个Agent的状态可以流转于:IDLE -> PLANNING -> EXECUTING_TOOL -> WAITING_FOR_RESULT -> COMPLETED/ERROR。每个状态转移都由特定的事件触发。这种方式使得Agent的行为清晰、可控、易于调试。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class AgentStateMachine {

public enum AgentState {
IDLE, PLANNING, EXECUTING, WAITING_INPUT, COMPLETED, ERROR
}

public AgentState transition(AgentState current, AgentEvent event) {
return switch (current) {
case IDLE -> event == START ? PLANNING : IDLE;
case PLANNING -> event == PLAN_READY ? EXECUTING :
event == NEED_INPUT ? WAITING_INPUT : PLANNING;
case EXECUTING -> event == SUCCESS ? COMPLETED :
event == FAILURE ? ERROR : EXECUTING;
default -> current;
};
}
}

MCP

MCP是一个由Anthropic、OpenAI、Google等行业巨头共同支持的开放标准。你可以把它理解为AI世界的JDBC或JMS——它旨在标准化AI模型与外部工具、数据源进行交互的方式。

  • MCP服务器 (MCP Server):任何外部工具、API或数据源(比如你的Spring Boot应用提供的服务)都可以通过实现MCP协议,将自己暴露为一个MCP服务器。它会“宣告”自己能提供哪些能力(如查询订单读取文件)。
  • MCP客户端 (MCP Client):AI模型或代理(Agent)作为客户端,可以发现连接到这些MCP服务器,使用标准化的请求/响应格式与之交互,而无需关心服务器底层的具体实现。
特性 传统函数调用 (Function Calling) 模型上下文协议 (MCP)
本质 模型特定的API 开放的、通用的通信协议
耦合度 高度耦合(与特定LLM提供商绑定) 松耦合(与任何支持MCP的LLM兼容)
互操作性 弱(切换模型成本高) (工具可被不同模型复用)
生态 封闭(各厂商各自为政) 开放(促进工具和服务的生态系统)

作为MCP客户端(消费工具)

  1. application.yml中配置需要连接的外部MCP服务器的地址。
  2. Spring AI将提供一个统一的McpTemplate或类似的客户端Bean。
  3. 我们的Agent通过这个McpTemplate来发现并调用外部工具。
  4. McpTemplate会将调用转化为标准的MCP请求,并发送给目标服务器。

我们如何去导入一个mcp呢在我的项目中?

如果是内部的,我直接写一个mcp工具即可,实现mcptool接口,继承抽象mcp基类

外部的我先导入依赖,中添加spring-ai-mistralai-spring-boot-starter依赖

然后配置mcp服务器,在里面指定mcp的json配置

然后再spring ai中配置我们chatmodel的apikey

然后我们就可以使用注册好mcp的chatmodel,来完成使用mcp完成

Dify

Dify是一个开源的、一站式的LLM应用开发平台极大降低构建和管理生产级生成式AI应用的门槛,让开发者可以更专注于业务逻辑,而不是费力地搭建和维护复杂的AI基础设施。

过去我们需要自己手动管理Prompt、对接不同的大模型、搭建RAG(检索增强生成)流程、处理对话历史等,而Dify将这些复杂繁琐的工作产品化、可视化了。

核心能力:

  1. 可视化的提示词编排 (Prompt Studio):提供一个图形界面,让开发者可以像填表格一样设计和调试Prompt,管理变量、上下文和模型输出。
  2. 内置的RAG引擎:允许用户直接上传文档(PDF, TXT, Markdown等),Dify会自动处理数据清洗、分块、向量化和索引,快速构建起一个可供检索的知识库。
  3. Agent能力:支持应用集成“工具 (Tools)”,让大模型可以调用外部API(如查询天气、搜索、计算等),完成更复杂的任务,而不仅仅是文本生成。
  4. 统一的API服务:一旦你在Dify上构建好应用,它会自动生成一套标准的API。你的前端或业务后端可以直接调用这个API,无需关心背后的大模型是哪个、RAG流程如何运作。
  5. 监控与分析:内置日志和数据分析功能,可以让你监控应用的调用情况、用户反馈、Token消耗等,方便持续运营和优化。

那么我们怎么构建一个dify,比如n8n工作流

1.创建新的应用

2.编排提示词,加入变量

3.构建RAG知识库,选择嵌入模型

4.调试程序问题

5.发布