AI智能体笔记

AI 智能体笔记

项目介绍

以 AI 开发实战为核心的项目,通过开发 AI 视力助手 + 拥有自主规划能力的超级智能体

AI 视力助手应用可以依赖 AI 大模型解决用户的眼健康问题,支持多轮对话、基于自定义知识库进行问答、自主调用工具和 MCP 服务完成任务,比如调用地图服务获取附近地点并制定恢复计划。

项目知识点

  • AI 应用平台的使用
  • 接入 AI 大模型
  • AI 开发框架(Spring AI + LangChain4j)
  • AI 大模型本地部署
  • Prompt 工程和优化技巧
  • 多模态特性
  • Spring AI 核心特性:如自定义拦截器、上下文持久化、结构化输出
  • RAG 知识库和向量数据库
  • Tool Calling 工具调用
  • MCP 模型上下文协议和服务开发
  • AI 智能体 Manus 原理和自主开发
  • AI 服务化和 Serverless 部署

需求分析

  • AI 视力助手:用户在眼康训练过程中难免遇到各种难题,让 AI 为用户提供贴心健康指导。支持多轮对话、对话记忆持久化、RAG 知识库检索、工具调用、MCP 服务调用。
  • AI 超级智能体:可以根据用户的需求,自主推理和行动,直到完成目标。
  • 提供给 AI 的工具:包括联网搜索、文件操作、网页抓取、资源下载、终端操作、PDF 生成。
  • AI MCP 服务:可以从特定网站搜索图片。

技术选型

项目以 Spring AI 开发框架实战为核心,涉及到多种主流 AI 客户端和工具库的运用。

  • Java 21 + Spring Boot 3 框架
  • ⭐ Spring AI + LangChain4j
  • ⭐ RAG 知识库
  • ⭐ PGvector 向量数据库
  • ⭐ Tool Calling 工具调用
  • ⭐ MCP 模型上下文协议
  • ⭐ ReAct Agent 智能体构建
  • ⭐ Serverless 计算服务
  • ⭐ AI 大模型开发平台百练
  • ⭐ Cursor AI 代码生成 + MCP
  • 第三方接口:如 SearchAPI / Pexels API
  • Ollama 大模型部署
  • Kryo 高性能序列化
  • Jsoup 网页抓取
  • iText PDF 生成
  • Knife4j 接口文档
image-20250710195800855

架构设计

从客户端发送请求开始,自上而下经过一系列处理,最终得到响应结果。架构图如下:

image-20250710195858282

AI 大模型接入

从 AI 大模型上手,学会如何使用 AI 大模型以及通过编程调用 AI 大模型。

具体内容:

  • AI 大模型概念
  • 接入 AI 大模型(3种方式)
  • 后端项目初始化
  • 程序调用 AI 大模型(4种方式)
  • 本地部署 AI 大模型
  • Spring AI 调用本地大模型

AI 大模型概念

image-20250710200334173

大模型举例:

OpenAI

  • GPT-4o (多模态)
  • GPT-4(文本 + 图像)
  • GPT-3.5 Turbo (主要处理文本)

Anthropic

  • Claude 3 系列(Opus, Sonnet, Haiku, 由强到弱)

Google

  • Gemini Ultra / Pro / Nano(多模态能力)

Meta

  • Llama 3(开源, 70B 和 8B 参数版本)
  • Llama 2 (开源, 多种参数规模)

国内大模型

  • 文心一言
  • 通义千问
  • 豆包
  • 星火

后续的 AI 介绍不再记录 。。。

接入 AI 大模型

主要有 2 种途径来接入 AI 大模型,分别是云服务和自部署

云服务

直接使用其他云服务商在云端已部署好的大模型服务, 无需自己考虑基础设施(比如服务器、GPU 算力),特点如下:

  • 提供纯净的大模型能力和构建应用(智能体)的工具
  • 按需付费,无需前期大量基础设施投入
  • 随时可用,维护成本低
  • 自动更新到最新版本的模型
  • 通常具有更完善的安全措施和合规保障

自部署

本地或私有云环境部署开源大模型,特点如下:

  • 完全控制数据流,更高的数据隐私保障
  • 可根据特定需求微调和定制模型
  • 无网络延迟,适合对响应速度有严格要求的场景
  • 一次性成本较高,需要专业的技术团队维护
  • 适合企业级应用和对数据安全有严格要求的场景

接入大模型的 3 种方式

  1. AI 应用平台接入

通过与服务商提供的 AI 应用平台来使用 AI 大模型

本次学习以阿里云百炼为例

点击去阿里云百炼 ->>> 阿里云百炼

在使用阿里云百炼产品时,可能会看到另一个产品 - 模型服务灵积(DashScope) 很容易把这两个产品混淆。百炼是一个可视化平台,同时服务于技术和非技术同学,使用更简单,更上层;而灵积旨在通过灵活、易用的 模型 API 接口,让开发者能够快速调用丰富的大模型能力,面向技术开发同学,更底层。

  1. AI 软件客户端接入

如 Cursor ,不是本文学习重点,跳过

  1. 程序接入

可以通过编程的方式在自己的项目中调用 AI 大模型,又可以分为 2 种方式:

  • 直接调用 AI 大模型,比如调用 DeepSeek (更原生)
  • 调用 AI 大模型平台创建的应用或智能体(更方便)

对于第一种方式,可以使用特定平台提供的 SDK 或 API,参考平台的文档来接入;也可以使用 AI 开发框架,比如 Spring AI、Spring AI Alibaba、LangChain4j 等自主选择大模型进行调用,可以灵活切换使用的大模型而几乎不用修改代码。

对于第二种方式,一般只能使用特定平台提供的 SDK 或 API,参考 平台的文档来接入,每个大模型服务平台的代码都不一样

后端初始化省略。。。

程序调用 AI 大模型

在实际开发中,有多种方式可以在应用程序中调用 AI 大模型。下面详细介绍 4 种主流的接入方式,并通过实例代码展示如何在 Java 项目中实现与 AI 大模型的交互

  • SDK 接入:使用官方提供的软件开发工具包,最直接的集成方式
  • HTTP 接入:通过 REST API 直接发送 HTTP 请求调用模型
  • Spring AI:基于 Spring 生态系统的 AI 框架,更方便地接入大模型
  • LangChain4j:专注于构建 LLM 应用的 Java 框架,提供丰富的 AI 调用组件

本次选择阿里云百炼平台接入,因为阿里大模型对 Java 开发生态支持较好,更容易与现有 Java 框架集成。

SDK 接入

SDK (软件开发包)是官方提供的最直接的集成方式,通常提供了完善的类型支持和错误处理机制。

  1. 首先需要按照官方文档安装 SDK 安装 SDK 官方指南

在 pom.xml 种引入依赖

1
2
3
4
5
6
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dashscope-sdk-java</artifactId>
<!-- 请将 'the-latest-version' 替换为最新版本号:https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java -->
<version>2.20.6</version>
</dependency>
  1. 在阿里云百炼申请一个 API Key,不要泄露
image-20250710205017023
  1. 项目中新建 demo.invoke 包,集中存放调用 AI 大模型的示例代码。

具体的代码示例可参考官方文档 通过 API 调用通义千问

通过 SDK 调用模型的完整示例代码: 注意下面的代码是用的是 dashscope 不是 HTTP请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 建议dashscope SDK的版本 >= 2.12.0

import java.util.Arrays;
import java.lang.System;

import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.JsonUtils;

public class SdkAiInvoke {

public static GenerationResult callWithMessage() throws ApiException, NoApiKeyException, InputRequiredException {
Generation gen = new Generation();
Message systemMsg = Message.builder()
.role(Role.SYSTEM.getValue())
.content("You are a helpful assistant.")
.build();
Message userMsg = Message.builder()
.role(Role.USER.getValue())
.content("你是谁?")
.build();
GenerationParam param = GenerationParam.builder()
// 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx")
.apiKey(TestApiKey.API_KEY)
// 此处以qwen-plus为例,可按需更换模型名称。模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
.model("qwen-plus")
.messages(Arrays.asList(systemMsg, userMsg))
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
.build();
return gen.call(param);
}

public static void main(String[] args) {
try {
GenerationResult result = callWithMessage();
System.out.println(JsonUtils.toJson(result));
} catch (ApiException | NoApiKeyException | InputRequiredException e) {
// 使用日志框架记录异常信息
System.err.println("An error occurred while calling the generation service: " + e.getMessage());
}
System.exit(0);
}
}

开发接入 阿里系的大模型,可直接引入 Spring AI Alibaba,完全兼容 Spring AI 框架

1
2
3
4
5
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M6.1</version>
</dependency>

编写配置类

1
2
3
4
5
6
7
8
9
spring:
application:
name: spring-ai-alibaba-qwq-chat-client-example
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY} // 替换API
chat:
options:
model: qwen-plus

编写实例代码,注意要注入dashscopeChatModel:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 取消注释即可在 SpringBoot 项目启动时执行
@Component
public class SpringAiAiInvoke implements CommandLineRunner {

@Resource
private ChatModel dashscopeChatModel; // 此处的原理是在配置类中写了配置所以能根据名字注入

@Override
public void run(String... args) throws Exception {
AssistantMessage output = dashscopeChatModel.call(new Prompt("你好,我是鱼皮"))
.getResult()
.getOutput();
System.out.println(output.getText());
}
}

CommandLineRunner,实现这个接口的目的是在Spring boot 启动后直接执行一次,为了测试

LangChain4j 和 使用 Ollama 本都部署接入的内容不继续写入,本次学习不作重点

Ollama 官方文档

AI 应用开发

熟悉 Prompt 工程和优化技巧,并设计开发一款 AI 视力助手,实战 Spring AI 调用大模型、对话记忆、Advisor、结构化输出、自定义对话记忆、Prompt 模板等特性

具体内容包括:

  • Prompt 工程基本概念 (不做介绍)
  • Prompt 优化技巧
  • AI 视力助手应用需求分析
  • AI 视力助手应用方案设计
  • Spring AI ChatClient / Advisor / ChatMemory 特性
  • 多轮对话 AI 应用开发
  • Spring AI 自定义 Advisor
  • Spring AI 结构化输出 - 视力报告功能
  • Spring AI 对话记忆持久化
  • Spring AI Prompt 模板特性
  • 多模态概念和开发

Prompt 优化技巧

利用资源

  1. Prompt 学习
  1. Prompt 提示词库

基础提示技巧

  1. 明确指定任务和角色
1
2
系统:你是一位经验丰富的Python教师,擅长向初学者解释编程概念。
用户:请解释 Python 中的列表推导式,包括基本语法和 2-3 个实用示例。
  1. 提供详细说明和具体示例
1
2
3
4
5
6
7
8
9
10
11
请提供一个社交媒体营销计划,针对一款新上市的智能手表。计划应包含:
1. 目标受众描述
2. 三个内容主题
3. 每个平台的内容类型建议
4. 发布频率建议

示例格式:
目标受众: [描述]
内容主题: [主题1], [主题2], [主题3]
平台策略: [平台] - [内容类型] - [频率]

  1. 使用结构化格式引导思维
1
2
3
4
5
6
7
8
9
10
分析以下公司的优势和劣势:
公司: Tesla

请使用表格格式回答,包含以下列:
- 优势(最少3项)
- 每项优势的简要分析
- 劣势(最少3项)
- 每项劣势的简要分析
- 应对建议

  1. 明确输出格式要求
1
2
3
4
5
6
撰写一篇关于气候变化的科普文章,要求:
- 使用通俗易懂的语言,适合高中生阅读
- 包含5个小标题,每个标题下2-3段文字
- 总字数控制在800字左右
- 结尾提供3个可行的个人行动建议

进阶提示技巧

  1. 思维链提示法(Chain-of-Thought)

引导模型展示推理过程,逐步思考问题,提高复杂问题的准确性

1
2
3
4
5
6
7
8
问题:一个商店售卖T恤,每件15元。如果购买5件以上可以享受8折优惠。小明买了7件T恤,他需要支付多少钱?

请一步步思考解决这个问题:
1. 首先计算7件T恤的原价
2. 确定是否符合折扣条件
3. 如果符合,计算折扣后的价格
4. 得出最终支付金额

  1. 少样本学习(Few-Shot Learning)

通过提供几个输入-输出对的示例,帮助模型理解任务模式和期望输出。

1
2
3
4
5
6
7
8
9
10
11
我将给你一些情感分析的例子,然后请你按照同样的方式分析新句子的情感倾向。

输入: "这家餐厅的服务太差了,等了一个小时才上菜"
输出: 负面,因为描述了长时间等待和差评服务

输入: "新买的手机屏幕清晰,电池也很耐用"
输出: 正面,因为赞扬了产品的多个方面

现在分析这个句子:
"这本书内容还行,但是价格有点贵"

  1. 分步骤指导(Step-by-Step)

将复杂任务分解为可管理的步骤,确保模型完成每个关键环节

1
2
3
4
5
6
7
8
请帮我创建一个简单的网站落地页设计方案,按照以下步骤:

步骤1: 分析目标受众(考虑年龄、职业、需求等因素)
步骤2: 确定页面核心信息(主标题、副标题、价值主张)
步骤3: 设计页面结构(至少包含哪些区块)
步骤4: 制定视觉引导策略(颜色、图像建议)
步骤5: 设计行动召唤(CTA)按钮和文案

  1. 自我评估和修正

让模型评估自己的输出并进行改进,提高准确性和质量

1
2
3
4
5
6
7
8
9
解决以下概率问题:
从一副标准扑克牌中随机抽取两张牌,求抽到至少一张红桃的概率。

首先给出你的解答,然后:
1. 检查你的推理过程是否存在逻辑错误
2. 验证你使用的概率公式是否正确
3. 检查计算步骤是否有误
4. 如果发现任何问题,提供修正后的解答

  1. 知识检索和引用

引导模型检索相关信息并明确引用信息来源,提高可靠性

1
2
3
4
5
6
7
8
请解释光合作用的过程及其在植物生长中的作用。在回答中:
1. 提供光合作用的科学定义
2. 解释主要的化学反应
3. 描述影响光合作用效率的关键因素
4. 说明其对生态系统的重要性

对于任何可能需要具体数据或研究支持的陈述,请明确指出这些信息的来源,并说明这些信息的可靠性。

  1. 多视角分析

引导模型从不同角度、立场或专业视角分析问题,提供全面见解。

1
2
3
4
5
6
7
8
9
10
11
12
13
分析"城市应该禁止私家车进入市中心"这一提议:

请从以下4个不同角度分析:
1. 环保专家视角
2. 经济学家视角
3. 市中心商户视角
4. 通勤居民视角

对每个视角:
- 提供支持该提议的2个论点
- 提供反对该提议的2个论点
- 分析可能的折中方案

  1. 多模态思维

结合不同表达形式进行思考,如文字描述、图表结构、代码逻辑等

1
2
3
4
5
6
7
8
9
设计一个智能家居系统的基础架构:

1. 首先用文字描述系统的主要功能和组件
2. 然后创建一个系统架构图(用ASCII或文本形式表示)
3. 接着提供用户交互流程
4. 最后简述实现这个系统可能面临的技术挑战

尝试从不同角度思考:功能性、用户体验、技术实现、安全性等。

提示词调试与优化

  1. 迭代式提示优化

通过逐步修改和完善提示词,提高输出质量

1
2
3
4
5
6
7
8
9
10
11
12
初始提示: 谈谈人工智能的影响。

[收到笼统回答后]
改进提示: 分析人工智能对医疗行业的三大积极影响和两大潜在风险,提供具体应用案例。

[如果回答仍然不够具体]
进一步改进: 详细分析AI在医学影像诊断领域的具体应用,包括:
1. 现有的2-3个成功商业化AI诊断系统及其准确率
2. 这些系统如何辅助放射科医生工作
3. 实施过程中遇到的主要挑战
4. 未来3-5年可能的技术发展方向

  1. 边界测试

通过极限情况测试模型的能力边界,找出优化空间

1
2
3
4
5
6
7
8
尝试解决以下具有挑战性的数学问题:
证明在三角形中,三条高的交点、三条中线的交点和三条角平分线的交点在同一条直线上。

如果你发现难以直接证明:
1. 说明你遇到的具体困难
2. 考虑是否有更简单的方法或特例可以探讨
3. 提供一个思路框架,即使无法给出完整证明

  1. 提示词模板化

创建结构化模板,便于针对类似任务进行一致性提示,否则每次输出的内容可能会比较大的区别,不利于调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
【专家角色】: {领域}专家
【任务描述】: {任务详细说明}
【所需内容】:
- {要点1}
- {要点2}
- {要点3}
【输出格式】: {格式要求}
【语言风格】: {风格要求}
【限制条件】: {字数、时间或其他限制}

例如:
【专家角色】: 营养学专家
【任务描述】: 为一位想减重的上班族设计一周健康饮食计划
【所需内容】:
- 七天的三餐安排
- 每餐的大致卡路里
- 准备建议和购物清单
【输出格式】: 按日分段,每餐列出具体食物
【语言风格】: 专业但友好
【限制条件】: 考虑准备时间短,预算有限

  1. 错误分析与修正

系统性分析模型回答中的错误,并针对性优化提示词,这一点在我们使用 Cursor 等 AI 开发工具生成代码时非常有用

1
2
3
4
5
6
7
8
9
10
11
我发现之前请你生成的Python代码存在以下问题:
1. 没有正确处理文件不存在的情况
2. 数据处理逻辑中存在边界条件错误
3. 代码注释不够详细

请重新生成代码,特别注意:
1. 添加完整的异常处理
2. 测试并确保所有边界条件
3. 为每个主要函数和复杂逻辑添加详细注释
4. 遵循PEP 8编码规范

AI 应用方案设计

根据需求,我们将实现一个具有多轮对话能力的 AI 视力助手应用。整体方案设计将围绕 2 个核心展开:

  • 系统提示词设计
  • 多轮对话实现
  1. 系统提示词设计

此处不再赘述,可直接在阿里云百炼平台调试系统提示词

  1. 多轮对话实现

要实现具有“记忆力”的 AI 应用,让 AI 能够记住用户之前的对话内容并保持上下文连贯性,我们可以使用 Spring AI 框架的 对话记忆能力

如何使用对话记忆能力呢?参考 Spring AI 的官方文档,了解到 Spring AI 提供了 ChatClient API 来和 AI 大模型交互。

ChatClient 特性

之前我们直接使用 Spring Boot 注入的 ChatModel 来调用大模型完成对话,而通过我们自己构造的 ChatClient,可实现功能更丰富、更灵活的 AI 对话客户端,也更推荐通过这种方式调用 AI

通过示例代码,能够感受到 ChatModel 和 ChatClient 的区别。ChatClient 支持更复杂灵活的链式调用(Fluent API):

1
2
3
4
5
6
7
8
9
10
// 基础用法(ChatModel)
ChatResponse response = chatModel.call(new Prompt("你好"));

// 高级用法(ChatClient)
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultSystem("你是恋爱顾问")
.build();

String response = chatClient.prompt().user("你好").call().content();

Spring AI 提供了多种构建 ChatClient 的方式,比如自动注入、通过建造者模式手动构造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 方式1:使用构造器注入
@Service
public class ChatService {
private final ChatClient chatClient;

public ChatService(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("你是恋爱顾问")
.build();
}
}

// 方式2:使用建造者模式
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultSystem("你是恋爱顾问")
.build();

ChatClient 支持多种响应格式,比如返回 ChatResponse 对象、返回实体对象、流式返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// ChatClient支持多种响应格式
// 1. 返回 ChatResponse 对象(包含元数据如 token 使用量)
ChatResponse chatResponse = chatClient.prompt()
.user("Tell me a joke")
.call()
.chatResponse();

// 2. 返回实体对象(自动将 AI 输出映射为 Java 对象)
// 2.1 返回单个实体
record ActorFilms(String actor, List<String> movies) {}
ActorFilms actorFilms = chatClient.prompt()
.user("Generate the filmography for a random actor.")
.call()
.entity(ActorFilms.class);

// 2.2 返回泛型集合
List<ActorFilms> multipleActors = chatClient.prompt()
.user("Generate filmography for Tom Hanks and Bill Murray.")
.call()
.entity(new ParameterizedTypeReference<List<ActorFilms>>() {});

// 3. 流式返回(适用于打字机效果)
Flux<String> streamResponse = chatClient.prompt()
.user("Tell me a story")
.stream()
.content();

// 也可以流式返回ChatResponse
Flux<ChatResponse> streamWithMetadata = chatClient.prompt()
.user("Tell me a story")
.stream()
.chatResponse();

可以给 ChatClient 设置默认参数,比如系统提示词,还可以在对话时动态更改系统提示词的变量,类似模板的概念:

1
2
3
4
5
6
7
8
9
10
11
12
// 定义默认系统提示词
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}")
.build();

// 对话时动态更改系统提示词的变量
chatClient.prompt()
.system(sp -> sp.param("voice", voice))
.user(message)
.call()
.content());

此外,还支持指定默认对话选项、默认拦截器、默认函数调用等等,后面会用到。