diff --git a/README.md b/README.md index 1184c1b..d13ba4f 100644 --- a/README.md +++ b/README.md @@ -3405,5 +3405,6230 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){ +## 大模型 + +### LLM大模型开发核心-LangChain框架实战 + +#### LLM开发框架LangChain介绍和技术生态 + +* 背景需求 + + * 大模型(如ChatGPT、DeepSeek)的局限性: + + - 无法获取训练数据外的实时信息(如今天的天气) + - 不能直接执行具体操作(发邮件/查数据库) + - 处理复杂任务时缺乏步骤规划能力 + + * 开发者的痛点 + + ```java + // 传统Java开发模式 vs AI应用开发 + String result = service.doSomething(input); // 确定性结果 + // VS + String aiResponse = llm.generate(prompt); // 非确定性输出 + ``` + +* 什么是LangChain框架(类似SpringCloud) + + * 是一个基于大型语言模型(LLM)开发应用程序的框架,专为构建与大语言模型(LLMs)相关的应用而设计。 + * 通过将多个 API、数据源和外部工具无缝集成,LangChain 能帮助开发者更高效地构建智能应用。 + * 从与 OpenAI 、DeepSeek等顶级大模型供应商的集成,到复杂的对话系统、智能搜索、推荐系统等 + * LangChain 提供了丰富的功能和灵活的接口,极大地方便了开发者的工作。 + * **通俗点:LangChain 就是对各类大模型提供的 API 的套壳,方便开发者使用这些 API和协议,搭建起来的模块和接口组合** + * 官网:https://www.langchain.com + * GIthub地址:https://github.com/langchain-ai/langchain + +* LangChain生态产品介绍 + + * LangChain + + * 提供模块化开发能力,支持LLM(如GPT、Claude等)与外部数据源(数据库、API)的集成 + * 包含链(Chain)、代理(Agent)、记忆(Memory)等核心组件,用于构建复杂AI应用 + + image-20250301141529448 + + * LangServer + + * 部署工具,可将LangChain应用快速转换为REST API,支持并行处理、流式传输和异步调用 + * 自动生成OpenAPI文档, 滚动更新支持, 内置Prometheus指标, 适用于企业级生产环境 + + image-20250301140529990 + + * LangSmith + + * 开发者调试与监控平台,支持对LLM应用的性能分析、测试和部署优化 + * 提供可视化调试界面和自动化评估工具,提升开发效率与可靠性 + + ![image-20250301140503525](/img/image-20250301140503525.png) + + * LangGraph + + * 状态管理工具,用于构建多代理系统,支持流式处理和复杂任务分解 + * 可视化流程设计器, 循环/条件分支支持,分布式状态持久化, 自动断点续跑 + + ![image-20250301140650448](/img/image-20250301140650448.png) + +* 产品矩阵对比 + + | **产品** | **核心价值** | **Java生态对标** | **适用场景** | + | :------------: | :----------------: | :------------------: | :----------------------: | + | LangSmith | 全生命周期可观测性 | Prometheus + Grafana | 生产环境监控、效果评估 | + | LangServe | 快速服务化 | Spring Boot | 模型API部署、快速原型 | + | LangGraph | 复杂流程编排 | Activiti BPMN | 业务工作流设计、状态管理 | + | LangChain Core | 基础组件库 | Spring AI | 基础AI功能开发 | + +![export_a9yct](/img/export_a9yct.png) + + + +#### Python虚拟环境evn应用讲解和实战 + +* 什么是Python的虚拟环境 + + * 类似虚拟机、沙箱机制一样,隔离不同的项目依赖的环境 + * 核心作用 + * **隔离项目依赖**:不同项目可能依赖同一库的不同版本。 + * **避免全局污染**:防止安装过多全局包导致冲突。 + * **便于协作**:通过依赖清单(如`requirements.txt`)复现环境 + +* 虚拟环境 vs 全局环境 + + | **特性** | **虚拟环境** | **全局环境** | + | :----------: | :--------------------------: | :------------------------: | + | **依赖隔离** | 项目独立,互不影响 | 所有项目共享 | + | **安全性** | 避免权限问题(无需sudo安装) | 需谨慎操作(可能影响系统) | + | **适用场景** | 开发、测试、多版本项目 | 系统级工具或少量通用库 | + +* 镜像源配置 + + * 查看系统配置的镜像源操作 + + ```shell + pip config list + + pip config get global.index-url + ``` + + * 配置国内镜像源 + + ```shell + pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple + ``` + +* 虚拟环境基础操作 + + * 创建虚拟环境 + + ```shell + # 语法:python -m venv <环境目录名> + python -m venv myenv # 创建名为myenv的虚拟环境 + ``` + + * 激活虚拟环境 + + * Windows(CMD/PowerShell) + + ```shell + myenv\Scripts\activate.bat # CMD + myenv\Scripts\Activate.ps1 # PowerShell(需管理员权限解除限制) + ``` + + * Linux/macOS: + + ```shell + source myenv/bin/activate + ``` + + * 激活后提示符变化 + + ```shell + source myenv/bin/activate + ``` + + * 退出虚拟环境 + + ```shell + deactivate + ``` + +* 依赖管理 + + * 安装库到虚拟环境 + + ```shell + # 激活环境后操作 + (myenv) pip install requests # 安装最新版本 + (myenv) pip install django==3.2 # 安装指定版本 + ``` + + * 导出依赖清单 + + ```shell + (myenv) pip freeze > requirements.txt + ``` + + * 从清单恢复依赖 + + ```shell + # 在新环境中执行 + (myenv) pip install -r requirements.txt + ``` + +* 最佳实践与案例 + + * 典型项目流程(区分Linux、Mac和Window) + + ```shell + # 创建项目目录并进入 + mkdir myproject && cd myproject + + # 创建虚拟环境 + python -m venv .venv + + # 激活环境(Windows: .venv\Scripts\activate) + source .venv/bin/activate + + # 安装依赖 + pip install django pandas + + # 导出依赖 + pip freeze > requirements.txt + + # 开发完成后退出 + deactivate + ``` + + * 协作复现环境 + + ```shell + # 克隆项目后操作 + git clone https://github.com/user/project.git + cd project + + # 创建并激活虚拟环境 + python -m venv .venv + source .venv/bin/activate + + # 安装依赖 + pip install -r requirements.txt + ``` + +* 常见问题与解决 + + * 虚拟环境激活失败 + * 现象:source: command not found + * 原因:在Windows使用Linux命令或在Linux未使用source。 + * 解决:根据操作系统选择正确激活命令。 + * 跨平台路径问题 + * 问题:Windows与Linux路径格式差异导致脚本无法运行。 + * 方案:使用/统一路径分隔符,或在代码中处理路径 + * 依赖版本冲突 + * 场景:项目A需要`numpy==1.18`,项目B需要`numpy==1.20`。 + * 解决:为每个项目创建独立虚拟环境。 + +* 案例实战: + + * LangChain框架环境搭建 + + +#### VSCode编辑器LangChain环境安装和验证 + +* Python虚拟环境和项目创建 + + * 创建虚拟环境(Windows/macOS/Linux通用) + + ```python + # 创建环境目录 + python -m venv langchain_env + ``` + + * 激活虚拟环境 + + * **Windows** + + ```python + .\langchain_env\Scripts\activate + ``` + + * **macOS/Linux** + + ```python + source langchain_env/bin/activate + ``` + + * 验证环境 + + ```python + # 查看Python路径(应显示虚拟环境路径) + which python # macOS/Linux + where python # Windows + ``` + +* LangChain环境安装 + + * 安装核心依赖包 (**版本和课程保持一致,不然很多不兼容!!!**) + + * 下载相关资料 ,使用**【wget】或者【浏览器】远程下载相关依赖包(需要替换群里最新的)** + + ```python + 原生资料下载方式(账号 - 密码 - ip地址 - 端口 需要替换群里最新的,【其他路径不变】) + wget --http-user=用户名 --http-password=密码 http://ip:端口/dcloud_pan/aipan_install_1.zip + + + #比如 命令行下 + wget --http-user=admin --http-password=xdclass.net888 http://47.115.31.28:9088/dcloud_pan/aipan_install_1.zip + + + # 比如 浏览器直接访问 + http://47.115.31.28:9088/dcloud_pan/aipan_install_1.zip + ``` + + * 解压后执行【**依赖很多,版本差异大,务必按照下面执行,否则课程无法进行下去,加我微信 xdclass6**】 + + ```python + # 安装依赖 + pip install -r requirements.txt + ``` + + * 验证安装【很多模块后续使用会验证】 + + ```python + # 执行简单测试 + from langchain_core.prompts import ChatPromptTemplate + print(ChatPromptTemplate.from_template("Hello 欢迎来到小滴课堂-AI大模型开发课程 {title}!").format(title=",干就完了")) + # 应输出: Human: Hello 欢迎来到小滴课堂-AI大模型开发课程 ,干就完了! + ``` + + +#### LangChain框架模块和大模型IO交互链路讲解 + +**简介: LangChain框架模块和大模型IO交互链路讲解** + +* 大模型IO交互链路概览 + +![image-20250301152117731](D:/学习/笔记/img/image-20250301152117731.png) + +* LangChain模块对比大家熟知的Java Spring生态 + + | LangChain模块 | Spring对应技术 | 交互方式差异 | + | :-----------: | :-------------: | :----------------: | + | Models | Spring AI | 多模型热切换支持 | + | Memory | Redis/Hazelcast | 内置对话上下文管理 | + | Chains | Activity工作流 | 动态流程重组能力 | + | Agents | Drools规则引擎 | 基于LLM的决策机制 | + +* LangChain架构六大模块(后续围绕模块逐步深入) + + * **Models(模型层)** + + - 相当于`interface LLM`,支持多种大模型(OpenAI/Gemini等) + - 示例:就像Java中的JDBC接口,可以对接不同数据库 + + * **Prompts(提示工程)** + + - 相当于模板引擎(类似Thymeleaf) + + ```python + from langchain.prompts import PromptTemplate + + template = """ + 你是一个Java专家,请用比喻解释{concept}: + 要求: + 1. 用{framework}框架做类比 + 2. 不超过2句话 + """ + prompt = PromptTemplate.from_template(template) + print(prompt.format(concept="机器学习", framework="Spring")) + ``` + + * **Chains(任务链)** + + - 类似Java的工作流引擎,将多个组件组合在一起,创建一个单一、连贯的任务 + - 包括不同的链之间组合 + + ```python + from langchain.chains import LLMChain + + # 创建任务链(类似Java的链式调用) + chain = LLMChain(llm=model, prompt=prompt) + result = chain.run(concept="多线程", framework="Spring Batch") + ``` + + * **Memory(记忆)** + + - 类似HTTP Session的会话管理 + + ```python + from langchain.memory import ConversationBufferMemory + + memory = ConversationBufferMemory() + memory.save_context({"input": "你好"}, {"output": "您好!"}) + ``` + + * **Indexes(索引)** + + - 类似数据库索引+JDBC连接 + - 对不通的文档进行结构化的方法,包括提取、切割、向量存储等,方便 LLM 能够更好的与之交互 + + ```python + from langchain.document_loaders import WebBaseLoader + + # 加载外部数据(类似JDBC读取数据库) + loader = WebBaseLoader("https://docs.spring.io/spring-boot/docs/current/reference/html/") + docs = loader.load() + ``` + + * **Agents(智能体)** + + - 类似策略模式+工厂模式,比chain更高级,可以自己选择调用链路 + - 比如下一步有多个选择, 包括不同工具、流程链路等,由程序自己选择 + + ```python + from langchain.agents import Tool, initialize_agent + + tools = [ + Tool(name="Calculator", + func=lambda x: eval(x), + description="计算数学表达式") + ] + agent = initialize_agent(tools, llm, agent="zero-shot-react-description") + ``` + +* 常见分层设计和交互如下 + + ```python + +----------------+ + | 应用层 (Agents) | + +----------------+ + | 编排层 (Chains) | + +----------------+ + | 能力层 (Tools) | + +----------------+ + | 模型层 (Models) | + +----------------+ + | 数据层 (Memory) | + +----------------+ + ``` + + +#### 大模型Model-IO链路抽象和Chat模型开发 + +**简介: 大模型Model-IO链路抽象和Chat模型实战** + +* 大模型使用开发的Model IO链路核心三要素 + + ![1](D:/学习/笔记/img/1.png) + + | 组件 | 作用 | 典型类/方法 | + | :---------: | :------------------------: | :-------------------------------------------: | + | **Prompts** | 构建模型输入的结构化模板 | `ChatPromptTemplate`, `FewShotPromptTemplate` | + | **Models** | 对接不同LLM服务的统一接口 | `ChatOpenAI` | + | **Parsers** | 将模型输出转换为结构化数据 | `StrOutputParser`, `JsonOutputParser` | + + ![image-20250301152117731](D:/学习/笔记/img/image-20250301152117731.png) + +* LangChain支持的模型类型说明 + + * 文本生成模型(Text Generation Models-逐渐少用了,Chat更强大) + * 功能:生成连贯文本,主要用于处理文本相关的任务,如自然语言理解、文本生成、情感分析、翻译 + * 典型模型:GPT-3、Claude、PaLM + * 对话模型(Chat Models,多数采用这个) + * 功能:处理多轮对话,人机交互中的对话能力,能够进行自然流畅的对话交流,适用于客户服务、智能助手 + * 典型模型:GPT-4、Claude-2 + * 嵌入模型(Embedding Models) + * 功能:生成文本向量表示,将文本转换为固定长度的向量表示,向量保留了数据的语义信息,便于后续的相似度计算、分类等任务。 + * 典型模型:text-embedding-ada-002 + * 多模态模型(Multimodal Models) + * 功能:处理文本+图像,例如文本、图像、音频等,更全面的信息理解和处理能力。 + * 典型模型:GPT-4V、Qwen-omni-turbo + * 其他更多.... + + + +* LangChain开发LLM聊天模型快速编码实战 + + ``` + from langchain_openai import ChatOpenAI + + # 调用Chat Completion API + llm = ChatOpenAI( + model_name='qwen-plus', + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-0903038424424850a88ed161845d7d4c") + response = llm.invoke('你是谁?') + print(response) + ``` + + + + + + + + + +#### 第6集 类型增强模块Typing应用和案例实战《上》 + +**简介: 类型增强模块Typing应用和案例实战《上》** + +* Python的动态类型痛点 + + ``` + # 传统动态类型代码示例 + def calculate(a, b): + return a + b # 无法直观看出参数类型和返回值类型 + + result1 = calculate(3, 5) # ✅ 正确用法 + result2 = calculate("3", 5) # ❌ 运行时才报错 + ``` + +* 什么是Typing模块 + + * 自python3.5开始,PEP484为python引入了类型注解(type hints), 为Python带来了类型提示和类型检查的能力。 + * 允许开发者在代码中添加类型注解,提高代码的可读性和可维护性。 + * 尽管Python是一种动态类型语言,但类型注解能让开发者更清晰地了解函数和变量的预期类型 + * 核心 + * **提升代码可读性**:明确参数和返回类型 + * **增强IDE支持**:智能提示与自动补全 + * **静态类型检查**:开发阶段发现潜在错误 + * **完善文档生成**:自动生成类型化API文档 + +* 核心语法快速入门 + + * 简单类型(Primitive Types) + + * Python内置的基本数据类型注解 + * 适用场景:变量、函数参数、返回值的简单类型声明 + * 类型注解不影响运行时行为 + * 兼容子类型(如int注解可接受bool值) + + ``` + age: int = 25 # 整数类型 + name: str = "Alice" # 字符串类型 + price: float = 9.99 # 浮点数类型 + is_valid: bool = True # 布尔类型 + data: bytes = b"binary" # 字节类型 + ``` + + + + * 容器类型 + + * 有多种内置的类型别名,比如`List`、`Tuple`、`Dict`等,可用于注解变量和函数的预期类型 + + * 例如 + + * `Dict[str, int]`表示键是字符串类型,值是整数类型的字典 + * `Set[int]`表示整数类型的集合 + + * `List`同质元素的序列容器 + + * 适用场景:列表类型数据,元素类型相同 + + ``` + from typing import List + + scores: List[int] = [90, 85, 95] # 整型列表 + matrix: List[List[float]] = [[1.1, 2.2], [3.3]] # 嵌套列表 + ``` + + * `Dict` 键值对映射容器 + + * 适用场景:字典类型数据,需指定键值类型 + + ``` + from typing import Dict + + person: Dict[str, str] = {"name": "Bob", "job": "dev"} # 字符串字典 + config: Dict[str, Union[int, str]] = {"timeout": 30} # 混合值类型 + + ``` + + * `Tuple`固定长度、类型的不可变序列 + + * 适用场景:坐标、数据库记录等固定结构 + * 变长声明:`Tuple[T, ...]`:元素类型相同但长度不限 , ():空元组 + + + ``` + from typing import Tuple + + point: Tuple[float, float] = (3.14, 2.71) # 二元坐标 + rgb: Tuple[int, int, int] = (255, 0, 128) # 颜色值 + flexible: Tuple[str, ...] = ("a", "b", "c") # 任意长度元组 + ``` + + * `Set` 无序不重复元素的集合 + + * 适用场景:去重数据、集合运算 + + ``` + from typing import Set + + unique_ids: Set[int] = {1, 2, 3} # 整型集合 + tags: Set[Union[str, int]] = {"urgent", 1001} # 混合类型集合 + ``` + + +​ + + * 任意类型 `Any` + + * 动态类型占位符,放弃类型检查, 应尽量避免过度使用 + * 适用场景:兼容无类型代码或动态行为 + + ``` + from typing import Any + + def debug_log(obj: Any) -> None: + print(repr(obj)) + + ``` + + * 函数类型注解 + + * 为函数添加typing模块的注解后,函数使用者就能清晰的了解函数的参数以及返回值类型 + + ``` + def greet(name: str) -> str: # 参数类型 -> 返回值类型 + return f"Hello, {name}" + + def calculate(a: int, b: int) -> int: + return a * b + + # 无返回值使用None + def show_info(info: str) -> None: + print(info) + ``` + + * `Literal` 字面量类型 + + * 精确值类型约束, 替代简单字符串枚举 + * 适用场景:枚举值的类型安全 + + ``` + from typing import Literal + + # 限定特定值 + HttpMethod = Literal["GET", "POST", "PUT", "DELETE"] + + def send_request(method: HttpMethod, url: str) -> None: + print(f"Sending {method} request to {url}") + + send_request("POST", "/api") # ✅ + send_request("PATCH", "/api") # ❌ mypy报错 + ``` + + + + + + + + + + + + + + + + + +#### 第7集 类型增强模块Typing应用和案例实战《下》 + +**简介: 类型增强模块Typing应用和案例实战《下》** + +* `Union`联合类型 + + * Union允许参数接受多种不同类型的数据。 + * 例如 `Union[int, float]`表示变量可以是int类型或float类型 + + ``` + from typing import Union + + def process_input(value: Union[int, str]) -> None: + if isinstance(value, int): + print(f"Number: {value}") + else: + print(f"String: {value}") + + process_input(42) # Number: 42 + process_input("test") # String: test + ``` + +* `Optional`可选类型 + + * `Optional`表示参数可以是指定类型或者`None` + * 让编译器识别到该参数有一个类型提示,可以使指定类型,也可以是None,且参数是可选非必传的。 + * `Optional[int]` 等价于 `Union[int, None]`,表示:既可以传指定的类型 int,也可以传 None, `Optional[ ]` 里面只能写一个数据类型 + * 适用场景:可能返回空值的操作 + * 在下面函数定义中,Optional[str] = None表示参数name的类型可以是str或None。 + * 注意 + * `= None`可省略,它表示默认参数。 + * 从 Python 3.10 开始,Optional[Type] 可以直接用 `Type | None` 替代,写法更清晰 + + ``` + from typing import Optional + + def greet1(name: Optional[str] = None) -> str: + if name: + return f"Hello, {name}!" + else: + return "Hello, world!" + + def greet2(name: Optional[str]) -> str: + if name: + return f"Hello, {name}!" + else: + return "Hello, world!" + + print(greet1()) + # print(greet2()) # 报错,必须要有参数 + print(greet1("老王")) + print(greet2("冰冰")) + ``` + + + +* 类型别名 + + * 自定义类型别名提高代码可读性。 + + ``` + from typing import Tuple + + # 基本别名 + UserId = int + Point = Tuple[float, float] + + def get_user(id: UserId) -> str: + return f"User{id}" + + def plot(points: List[Point]) -> None: + for x, y in points: + print(f"({x}, {y})") + ``` + +* `NewType`新类型创建 + + * 创建具有类型检查的语义化新类型 + * 适合 区分相同基础类型的不同用途 + + ``` + from typing import NewType + + # 创建强类型 + UserId = NewType('UserId', int) + admin_id = UserId(1001) + + def print_id(user_id: UserId) -> None: + print(user_id) + + # 正确调用 + print_id(admin_id) # ✅ + print_id(1001) # ❌ mypy报错 + ``` + +* `TypeVar`(类型变量) + + * 创建通用类型参数 + * 适用场景:泛型函数/类的类型参数化;比如创建一个函数,无论是处理整数、字符串还是自定义对象 + + ``` + from typing import TypeVar, Sequence + + T = TypeVar('T') # 无约束类型 + Num = TypeVar('Num', int, float) # 受限类型 + + def first(items: Sequence[T]) -> T: + return items[0] + + def sum(values: Sequence[Num]) -> Num: + return sum(values) + ``` + + ``` + from typing import TypeVar + + # 定义一个泛型变量T + T = TypeVar('T') + + # 创建一个泛型函数 + def get_first_item(items: list[T]) -> T: + """获取列表的第一个元素""" + if items: + return items[0] + raise ValueError("列表为空") + + # 使用示例 + numbers = [1, 2, 3, 4, 5] + words = ['apple', 'banana', 'cherry', 'fruit'] + + print(get_first_item(numbers)) # 输出: 1 + print(get_first_item(words)) # 输出: apple + ``` + + + + + + + + + + + + + + + + + + + +![logo](D:/学习/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"** +**更多高级课程请访问 xdclass.net** + +### 第三十一章 Prompt提示词工程和案例最佳实践 + + + +#### 第1集 大模型必备Prompt提示词工程 + +**简介: 大模型必备Prompt提示词工程讲解** + +* 什么是Prompt Engineering提示词工程 + + * 通过特定格式的文本输入引导AI模型生成期望输出的技术,明确地告诉模型你想要解决的问题或完成的任务 + + * 也是大语言模型理解用户需求并生成相关、准确回答或内容的基础 + + * **类比:给Java程序员的任务需求文档(越清晰明确,结果越符合预期)** + + * 为什么需要学习? + + * 大模型就是你的员工,你可以有多个助手,OpenAI、DeepSeek、千问等 + * 作为老板的你,需要正确的下达任务,描述合理和交付目标等 + + ``` + 传统编程:写代码→计算机执行 + Prompt工程:写自然语言指令→大模型生成结果 + ``` + + ![image-20250304142514844](D:/学习/笔记/img/image-20250304142514844.png) + +* Prompt设计四要素 + + * 角色设定(Role Prompting) + + * 作用:限定模型回答视角 + + ``` + [差] 写一首关于春天的诗 + [优] 你是一位擅长写现代诗的诗人,请用比喻手法创作一首8行的春天主题短诗 + ``` + + * 任务描述 + + * STAR原则:Situation 场景、Task 任务、Action 行动、Result 结果 + + ``` + (场景)用户提交了一个技术问题 + (任务)需要给出准确且易懂的解答 + (行动)分步骤说明解决方案 + (结果)最后用一句话总结要点 + ``` + + * 格式规范 + + * 常用格式指令:分点列表、指定段落数、表格呈现、代码格式 + + ``` + 用JSON格式输出包含以下字段: + { + "summary": "不超过50字的摘要", + "keywords": ["关键词1", "关键词2", "关键词3"] + } + ``` + + * 约束条件 + + * 常见约束类型: + + | 类型 | 示例 | + | :--: | :--------------------: | + | 长度 | "答案控制在200字内" | + | 风格 | "用初中生能理解的语言" | + | 内容 | "不包含专业术语" | + | 逻辑 | "先解释概念再举例说明" | + + * 汇总 + + | 要素 | 说明 | 反面案例 | 优化案例 | + | :----------: | :------------: | :----------: | :-------------------------: | + | **角色设定** | 明确模型身份 | "帮我写代码" | "你是一个资深Java架构师..." | + | **任务说明** | 具体执行要求 | "分析数据" | "使用Markdown表格对比..." | + | **输出格式** | 结构化结果定义 | 自由文本 | JSON/XML/YAML格式 | + | **约束条件** | 限制输出范围 | 无限制 | "不超过200字,不用专业术语" | + +* 模板结构设计(黄金公式) + + ``` + # 标准三段式结构 + prompt_template = """ + [角色设定] + 你是一个具有10年经验的{领域}专家,擅长{特定技能} + + [任务说明] + 需要完成以下任务: + 1. {步骤1} + 2. {步骤2} + 3. {步骤3} + + [输出要求] + 请按照以下格式响应: + {示例格式} + """ + ``` + +* 常见问题和排查原因 + + | 现象 | 可能原因 | 解决方案 | + | :--------------: | :----------------: | :----------------------: | + | 输出内容偏离主题 | 角色设定不明确 | 添加"忽略无关信息"约束 | + | 生成结果过于笼统 | 缺少具体步骤要求 | 添加"分步骤详细说明"指令 | + | 格式不符合要求 | 未提供明确格式示例 | 添加XML/JSON标记示例 | + + + + + + + + + + + + + + + + + + + + + +#### 第2集 Prompt提示词工程多案例最佳实践 + +**简介: Prompt提示词工程多案例最佳实践** + +* 需求 + + * 利用在线大模型或者本地大模型 + * 测试不同的提示词效果,分析优化前、后的Prompt工程 + * **案例实战:通用回答助手、代码生成助手、技术问答、AI数据分析 等案例实战** + +* 案例实战一:通用回答 + + * 差Prompt: + + ``` + 告诉我关于人工智能的信息 + ``` + + * 问题分析:过于宽泛,缺乏焦点、没有指定回答的深度和范围、未明确期望的格式 + * 输出结果:可能得到从历史发展到技术原理的冗长概述,缺乏针对性 + + * 好prompt + + ``` + 你是一位科技专栏作家,请用通俗易懂的方式向高中生解释: + 1. 什么是人工智能(用1个生活化比喻说明) + 2. 列举3个当前主流应用场景 + 3. 字数控制在300字以内 + 要求使用「首先」、「其次」、「最后」的结构 + ``` + + * 优化后 + * 设定回答视角(科技专栏作家) + * 明确目标受众(高中生) + * 结构化输出要求 + * 添加格式约束 + + + +* 案例实战二:代码生成 + + * 差Prompt: + + ``` + 写个Python程序 + ``` + + * 问题分析:没有具体功能描述、未指定输入输出格式、忽略异常处理需求 + * 输出结果:可能生成简单的"Hello World"程序,与真实需求不符 + + * 好Prompt: + + ``` + 编写一个Python函数,实现以下功能: + - 输入:字符串形式的日期(格式:YYYY-MM-DD) + - 输出:该日期对应的季度(1-4) + - 要求: + - 包含参数校验(不符合格式时抛出ValueError) + - 使用datetime模块 + - 编写对应的单元测试用例 + 示例: + 输入 "2024-03-15" → 返回 1 + ``` + + * 优化后 + * 明确定义输入输出 + * 指定实现方式 + * 包含测试要求 + * 提供示例验证 + + + +* 案例实战三:技术问答 + + * 差Prompt + + ``` + 如何优化网站性能? + ``` + + * 问题分析:问题范围过大、未说明技术栈、缺少评估标准 + * 输出结果:可能得到泛泛而谈的通用建议 + + * 好Prompt + + ``` + 针对使用SpringBoot+Vue3的技术栈,请给出5项可量化的性能优化方案: + 要求: + 1. 每项方案包含: + - 实施步骤 + - 预期性能提升指标(如LCP减少20%) + - 复杂度评估(低/中/高) + 2. 优先前端和后端优化方案 + 3. 引用Web Vitals评估标准 + 限制条件: + - 不涉及服务器扩容等硬件方案 + - 排除已广泛采用的方案(如代码压缩) + ``` + + * 优化点 + * 限定技术范围 + * 结构化响应要求 + * 设定评估标准 + * 排除已知方案 + +* 案例实战四:数据分析 + + * 差Prompt + + ``` + 分析这份销售数据 + ``` + + * 问题分析:未说明数据特征、没有指定分析方法、缺少可视化要求 + * 输出结果:可能得到无重点的描述性统计,缺乏洞察 + + * 好Prompt + + ``` + 你是一位资深数据分析师,请完成以下任务: + 数据集特征: + - 时间范围:2027年1-12月 + - 字段:日期/产品类别/销售额/利润率 + + 要求: + 1. 找出销售额top3的月份,分析增长原因 + 2. 识别利润率低于5%的产品类别 + 3. 生成包含趋势图的Markdown报告 + 输出格式: + ## 分析报告 + ### 关键发现 + - 要点1(数据支撑) + - 要点2(对比分析) + ### 可视化 + 趋势图描述,生成base64编码的折线图 + ``` + + * 优化点: + * 明确分析者角色 + * 描述数据集特征 + * 指定分析方法论 + * 规范输出格式 + + + + + + + + + + + + + + + +#### 第3集 LangChain 提示模板PromptTemplate介绍 + +**简介: LangChain 提示模板PromptTemplate介绍** + +* 需求 + + * 掌握LangChain 提示模板PromptTemplate常见用法 + * 掌握提示词里面的占位符使用和预置变量 + +* PromptTemplate介绍 + + * 是LangChain中用于构建结构化提示词的组件,负责将用户输入/动态数据转换为LLM可理解的格式 + * 它是一种单纯的字符模板,后续还有进阶的ChatPromptTemplate + * 主要解决 + * 动态内容组装 + * 避免Prompt硬编码 + + ![1](D:/学习/笔记/img/1-1073680.png) + + * PromptTemplate核心变量和方法 + * template 定义具体的模板格式,其中 `{变量名}` 是占位符 + * input_variables 定义模板中可以使用的变量。 + * partial_variables:前置变量,可以提前定义部分变量的值,预填充进去 + * format 使用 `format` 方法填充模板中的占位符,形成最终的文本 + +* 案例实战 + + * 创建PromptTemplate对象简单模版 + + ``` + from langchain.prompts import PromptTemplate + + # 定义模板 + template = """ + 你是一位专业的{domain}顾问,请用{language}回答: + 问题:{question} + 回答: + """ + + # 创建实例 + prompt = PromptTemplate( + input_variables=["domain", "language", "question"], + template=template + ) + + print(prompt) + + # 格式化输出 + print(prompt.format( + domain="网络安全", + language="中文", + question="如何防范钓鱼攻击?" + )) + ``` + + * 自动推断变量 + + ``` + from langchain.prompts import PromptTemplate + + # 当不显式声明 input_variables 时 + template = "请将以下文本翻译成{target_language}:{text}" + prompt = PromptTemplate.from_template(template) + + print(prompt.input_variables) # 输出: ['target_language', 'text'] + ``` + + * 默认值设置 + + ``` + from langchain.prompts import PromptTemplate + template = """分析用户情绪(默认分析类型:{analysis_type}): + 用户输入:{user_input} + 分析结果:""" + + prompt_template = PromptTemplate( + input_variables=["user_input"], + template=template, + template_format="f-string", # 新增参数 + partial_variables={"analysis_type": "情感极性分析"} # 固定值 + ) + + print(prompt_template.format(user_input="这个产品太难用了")) + + #====打印内部的变量====== + print(prompt_template.template) + print(prompt_template.input_variables) + print(prompt_template.template_format) + print(prompt_template.input_types) + print(prompt_template.partial_variables) + ``` + + + + + + + + + + + + + + + + + + + + + +#### 第4集 PromptTemplate结合LLM案例实战 + +**简介: PromptTemplate结合LLM案例实战** + +* 案例实战 + + ``` + from langchain_openai import ChatOpenAI + from langchain_core.prompts import PromptTemplate + from langchain_core.output_parsers import StrOutputParser + + #创建prompt AIGC + prompt_template = PromptTemplate( + input_variables=["product"], + template="为{product}写3个吸引人的广告语,需要面向年轻人", + ) + prompt = prompt_template.invoke({"product":"小滴课堂"}) + + + #创建模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + #调用大模型 + response = model.invoke(prompt) + + #print(response.content) + + #创建输出解析器 + out_parser = StrOutputParser() + answer = out_parser.invoke(response) + print(answer) + ``` + + + + + + + + + + + + + +#### 第5集 大模型ChatModel聊天模型和Token计算 + +**简介: 大模型里面ChatModel聊天模型介绍** + +* 什么是**ChatModel** + + * 是专为**多轮对话场景**设计的大语言模型(LLM),通过理解上下文和对话逻辑,生成连贯、符合人类交互习惯的回复。 + * 不仅是简单的文本生成工具,更是能处理复杂对话流程的智能系统 + * 核心特点 + + | 特性 | 说明 | 示例场景 | + | :--------------: | :----------------------------------------------: | :----------------------------------------------------------: | + | **上下文感知** | 追踪多轮对话历史,理解指代关系(如“它”、“这个”) | 用户:“什么是量子计算?” → AI 解释 → 用户:“它有什么应用?” → AI 能正确关联“它”指量子计算 | + | **角色扮演能力** | 可设定特定角色(如客服、教师)并保持一致性 | 设定AI为“医疗助手”时,拒绝提供诊断建议,仅提供健康信息 | + | **意图识别** | 解析用户深层需求(如咨询、投诉、闲聊) | 用户:“我的订单没收到!” → AI 识别为物流投诉,优先转接人工客服 | + | **情感分析** | 识别用户情绪(积极/消极),调整回复语气 | 用户表达不满时,AI 回复:“非常抱歉给您带来不便,我们会立刻处理...” | + | **安全过滤** | 避免生成有害、偏见或敏感内容 | 用户请求生成暴力内容时,AI 拒绝并提示:“我无法协助这个请求” | + +* ChatModel vs. 传统 Text Model + + | **对比维度** | **ChatModel** | **传统 Text Model(如 text-davinci-003)** | + | :------------: | :------------------------------------: | :----------------------------------------: | + | **核心目标** | 多轮交互式对话 | 单次文本生成(文章、代码等) | + | **输入格式** | 结构化消息序列(System/Human/AI 角色) | 纯文本提示 | + | **上下文处理** | 自动管理对话历史 | 需手动拼接历史文本 | + | **输出控制** | 内置安全审查和格式约束 | 依赖提示词工程控制 | + | **典型应用** | 客服机器人、虚拟助手 | 内容创作、数据清洗 | + +* 聊天模型(如 GPT-3.5-turbo、GPT-4)通过 **角色化消息** 实现对话控制,核心角色包括: + + | 角色类型 | 标识符 | 功能定位 | 使用场景示例 | + | :-----------: | :---------: | :------------------------: | :----------------------------------------------------------: | + | **System** | `system` | 定义AI的行为准则和角色设定 | 设定AI身份、回答规则、知识范围
`("system", "你是一位医疗助手...")` | + | **User** | `user` | 代表用户的输入信息 | 用户提问、指令、反馈
`("human", "如何缓解头痛?")` | + | **Assistant** | `assistant` | 存储AI的历史回复 | 维护对话上下文、保持回答连贯性
`("ai", "建议服用布洛芬...")` | + +* 参考案例OpenAI代码 + + ``` + # 多轮对话示例 + messages = [ + {"role": "system", "content": "你是一个电影推荐助手"}, + {"role": "user", "content": "我喜欢科幻片,推荐三部经典"}, + {"role": "assistant", "content": "1.《银翼杀手2049》... 2.《星际穿越》... 3.《黑客帝国》"}, + {"role": "user", "content": "第二部的主演是谁?"} # 基于上下文追问 + ] + + response = client.chat.completions.create( + model="gpt-3.5-turbo", + messages=messages + ) + + print(response.choices[0].message.content) + # 输出:《星际穿越》的主演是马修·麦康纳和安妮·海瑟薇... + ``` + + + +* Chat聊天多轮对话中的Token如何计算 + + * 在多轮对话场景下,上下文通常涵盖以下几部分: + + * **用户的历史输入** :之前用户说过的话,这些内容会作为上下文的一部分,帮助模型理解当前对话的背景和意图。 + * **模型的历史回复** :模型之前给出的回应,这样能让模型保持对话的连贯性和一致性。 + * **系统提示** :用于设定聊天机器人的角色、目标等,引导对话的方向和风格。 + + + * 随着上下文内容的增加,Token 数量也会相应增多。 + + + * 多轮对话的上下文 Token 累积 + + * 假设每轮对话中,用户的输入和模型的输出分别对应一定数量的 Token + * 我们以每轮对话输入 50 Token、输出 100 Token 为例来计算: + * **第 1 轮** :用户的输入为 50 Token,模型的输出为 100 Token,此时上下文中的 Token 总数为 50 + 100 = 150 Token。 + * **第 2 轮** :新的用户输入为 50 Token,模型新的输出为 100 Token,加上之前的历史上下文 150 Token,此时上下文中的 Token 总数为 50 + 100 + 150 = 300 Token。 + * **第 3 轮** :再次新增用户输入 50 Token 和模型输出 100 Token,加上之前的历史上下文 300 Token,上下文中的 Token 总数变为 50 + 100 + 300 = 450 Token + + + * 上下文窗口的限制 + + * 每个大语言模型都有一个 “上下文窗口大小”(Context Window)的限制, + + * 它决定了模型能够处理的上下文的最大 Token 数量。 + + * 常见的上下文窗口大小有: + + * 4k Tokens :例如 OpenAI GPT - 3.5,其上下文窗口大小为 4096 个 Token。 + + * 8k、32k Tokens :支持长上下文的模型,如 GPT-4 等,有更大的上下文窗口,分别为 8192 个和 32768 个 Token。 + + + + + + + + + + + + + + + + +#### 第6集 聊天模型ChatPromptTemplate讲解 + +**简介: 聊天模型ChatPromptTemplate讲解** + +* ChatPromptTemplate 核心概念 + + * 核心差异 + * 支持**消息角色**(system/user/assistant) + * 天然适配聊天模型(如GPT-3.5/4) + * 可维护对话上下文 + + ![2](D:/学习/笔记/img/2.png) + + * 消息类型体系 + + | 消息模板类 | 对应角色 | 典型用途 | + | :-------------------------: | :--------: | :--------------: | + | SystemMessagePromptTemplate | 系统消息 | 设定AI行为规则 | + | HumanMessagePromptTemplate | 用户消息 | 接收用户输入 | + | AIMessagePromptTemplate | AI回复消息 | 记录历史响应 | + | ChatPromptTemplate | 容器模板 | 组合多个消息模板 | + + + +* 掌握两个常见类方法 + + * `ChatPromptTemplate.from_template()` + * 用于创建单条消息模板,通常结合其他方法构建更复杂的对话流程。 + * 适用于单一角色的消息定义(如仅系统指令或用户输入)。 + * 需与其他模板组合使用,例如通过`from_messages`整合多个单模板 + * `ChatPromptTemplate.from_messages()` + * 用于构建多轮对话模板,支持定义不同角色(如系统、用户、AI)的消息,并允许动态插入变量。 + * 支持消息列表,每个消息可以是元组或`MessagePromptTemplate`对象。 + * 角色类型包括:`system(系统指令)、human(用户输入)、ai(模型回复)` + * 可通过占位符(如`{variable}`)动态替换内容。 + * 汇总对比 + + | 方法 | 适用场景 | 灵活性 | 代码复杂度 | + | :-------------: | :------------------------------: | :----: | :----------------: | + | `from_messages` | 多角色、多轮对话(如聊天机器人) | 高 | 较高(需定义列表) | + | `from_template` | 单角色消息模板(需组合使用) | 低 | 简单 | + +* 案例实战 + + * 使用from_messages构建多轮对话模板 + + ``` + from langchain_core.prompts import ChatPromptTemplate + + # 定义消息列表,包含系统指令、用户输入和AI回复模板, 通过元组列表定义角色和模板,动态插入name和user_input变量 + chat_template = ChatPromptTemplate.from_messages([ + ("system", "你是一个助手AI,名字是{name}。"), + ("human", "你好,最近怎么样?"), + ("ai", "我很好,谢谢!"), + ("human", "{user_input}") + ]) + + # 格式化模板并传入变量 + messages = chat_template.format_messages( + name="Bob", + user_input="你最喜欢的编程语言是什么?" + ) + print(messages) + # 输出结果示例: + # SystemMessage(content='你是一个助手AI,名字是Bob。') + # HumanMessage(content='你好,最近怎么样?') + # AIMessage(content='我很好,谢谢!') + # HumanMessage(content='你最喜欢的编程语言是什么?') + ``` + + * 结合`from_template`与`from_messages` + + ``` + from langchain_core.prompts import ( + ChatPromptTemplate, + SystemMessagePromptTemplate, + HumanMessagePromptTemplate + ) + + # 创建单条消息模板,通过细分模板类(如SystemMessagePromptTemplate)定义单条消息,再通过from_messages组合 + system_template = SystemMessagePromptTemplate.from_template( + "你是一个{role},请用{language}回答。" + ) + user_template = HumanMessagePromptTemplate.from_template("{question}") + + # 组合成多轮对话模板 + chat_template = ChatPromptTemplate.from_messages([ + system_template, + user_template + ]) + + # 使用示例 + messages = chat_template.format_messages( + role="翻译助手", + language="中文", + question="将'I love Python'翻译成中文。" + ) + print(messages) + + # 输出结果示例: + # SystemMessage(content='你是一个翻译助手,请用中文回答。') + # HumanMessage(content='将'I love Python'翻译成中文。') + ``` + + + + + + + + + + + + + + + + + + + + + + + +#### 第7集 LangChain聊天模型多案例实战 + +**简介: LangChain聊天模型多案例实战** + +* 需求 + + * 聊天模型案例实战,需要结合LLM大模型进行调用 + * 简单记执行顺序 ` from_template->from_messages->format_messages` + * 最终传递给大模型是完整构建消息列表对象 + +* 领域专家案例实战 + + ``` + from langchain_openai import ChatOpenAI + from langchain_core.messages import SystemMessage, HumanMessage + + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # 构建消息列表(类似Java的ListMessage>) + messages = [ + SystemMessage(content="你是一个Java专家,用中文回答"), + HumanMessage(content="解释volatile关键字的作用") + ] + + # 同步调用(类似Java的execute()) + response = model.invoke(messages) + print(response.content) + """ + volatile关键字主要用于: + 1. 保证变量可见性... + 2. 禁止指令重排序... + """ + ``` + +* 带参数的领域专家 + + ``` + from langchain_openai import ChatOpenAI + from langchain_core.prompts import ChatPromptTemplate + + from langchain.prompts import ( + ChatPromptTemplate, + SystemMessagePromptTemplate, + HumanMessagePromptTemplate + ) + + # 定义系统消息模板 + system_template = SystemMessagePromptTemplate.from_template( + "你是一位专业的{domain}专家,回答需满足:{style_guide}" + ) + # 定义用户消息模板 + human_template = HumanMessagePromptTemplate.from_template( + "请解释:{concept}" + ) + + # 组合聊天提示 + chat_prompt = ChatPromptTemplate.from_messages([ + system_template, + human_template + ]) + # 格式化输出 + messages = chat_prompt.format_messages( + domain="机器学习", + style_guide="使用比喻和示例说明", + concept="过拟合" + ) + + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + response = model.invoke(messages) + print(response) + ``` + +* 合规客服系统 + + ``` + from langchain_openai import ChatOpenAI + from langchain_core.prompts import ChatPromptTemplate + + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + compliance_template = ChatPromptTemplate.from_messages([ + ("system", """您是{company}客服助手,遵守: + 1. 不透露内部系统名称 + 2. 不提供医疗/金融建议 + 3. 遇到{transfer_cond}转人工"""), + ("human", "[{user_level}用户]:{query}") + ]) + + messages = compliance_template.format_messages(company="小滴课堂老王医生", transfer_cond="病情咨询、支付问题", user_level="VIP", query="感冒应该吃什么药?") + + response = model.invoke(messages) + + print(response) + ``` + + + + + + + + + + + + + + + + + +![logo](D:/学习/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"** +**更多高级课程请访问 xdclass.net** + +### 第三十二章 LCEL表达式和输出解析器多案例实战 + + + +#### 第1集 LangChain链和LLMChain链案例实战 + +**简介: LangChain链和LLMChain链案例实战** + +* 什么是Chain链 + + * 是构建语言模型应用的核心组件,用于将多个模块(如模型调用、提示模板、记忆系统等)组合成可复用的工作流程。 + * 本质:将多个处理单元(模型/工具/逻辑)按特定顺序连接起来,形成完整的任务处理流程 + * 想象Java中的责任链模式或工作流引擎中的步骤串联。 + + ``` + // 传统Java责任链模式(对比理解) + public interface Handler { + void handle(Request request); + } + + class ValidationHandler implements Handler { /* 验证逻辑 */ } + class LLMProcessingHandler implements Handler { /* 大模型处理 */ } + class DatabaseSaveHandler implements Handler { /* 存储结果 */ } + + // 构建处理链 + List chain = Arrays.asList( + new ValidationHandler(), + new LLMProcessingHandler(), + new DatabaseSaveHandler() + ); + + // 执行链式处理 + for (Handler handler : chain) { + handler.handle(request); + } + ``` + +* 常见类说明 + + * LLMChain类详解(最基础的链,少用仍保留,推荐用 LCEL) + + * 是最基础的Chain,负责结合**提示模板**和**LLM模型**,生成并执行模型的调用流程 + * 专门用于与大语言模型(如ChatGPT)交互的标准化处理单元 + * 核心功能: + * 提示模板:将用户输入动态填充到预设的模板中,生成模型的输入文本。 + * 模型调用:将模板生成的文本传递给LLM,返回生成结果。 + * 输出解析(可选):对模型输出进行后处理(如JSON解析) + * 核心参数 + + | 参数 | 类比Java场景 | 示例值 | + | :-----------: | :-------------------------------: | :-------------------------------------: | + | llm | 依赖注入的模型对象 | new OpenAI() | + | prompt | 预定义的提示词模板 | PromptTemplate("回答用户问题: {input}") | + | output_parser | 结果解析器(类似Jackson处理JSON) | new CommaSeparatedListOutputParser() | + + * 代码示例 + + ``` + from langchain_openai import ChatOpenAI + from langchain.chains import LLMChain + from langchain_core.prompts import PromptTemplate + + # 创建提示词模板(类似Java的String.format()) + prompt_template = PromptTemplate( + input_variables=["product"], + template="你是文案高手,列举三个{product}的卖点:" + ) + + # #模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # 创建LLMChain(类似构建一个Service类) + llm_chain = LLMChain( + llm=model, + prompt=prompt_template + ) + + # 执行调用(类似service.execute(input)),run方法被淘汰了,统一invoke方法调用 + result = llm_chain.invoke("智能手机") + print(result) + # 输出:"1. 高清OLED屏幕 2. 5000mAh大电池 3. 旗舰级处理器" + ``` + + * 其他常见的Chain,比如 `SequentialChain、TransformChain、RouterChain、RetrievalChain` 部分已经淘汰了 + + + + + + + + + + + + + + + + + + + +#### 第2集 新版LCEL表达式讲解和案例实战 + +**简介: 新版LCEL表达式讲解和案例实战** + +* **什么是LCEL(LangChain Expression Language**) + + * 是 LangChain 0.3+ 推出的**声明式编程语言**,用于简化 AI 流程编排,核心思想是**用管道符 `|` 连接组件**。 + + * 优势: + + * 代码简洁,支持流式响应(如 ChatGPT 逐字输出) + * 标准化接口, 所有组件输入为 `Dict`,输出为 `Dict`,支持无缝连接 + * 所有组件实现 `Runnable` 协议, 兼容同步/异步调用 + * 内置调试、重试、并行等高级功能 + + * 版本对比 + + | 特性 | 0.2 版本 | 0.3+ 版本 | + | :----------: | :-------------------: | :-------------------------------: | + | **构建方式** | 类继承 (`Chain` 子类) | LCEL 表达式语法 (`Runnable` 接口) | + | **组合方法** | `SequentialChain` 类 | 管道操作符 ` | + | **执行模式** | 同步为主 | 原生支持 Async / Stream | + | **核心模块** | `langchain.chains` | `langchain_core.runnables` | + + * **语法与结构** + + - **LLMChain(旧版)** + + - 基于类的封装,通过组合`PromptTemplate`、`LLM`和`OutputParser`等组件构建链式调用。 + - 需要显式定义每个组件并手动传递输入 + - **适用场景** + - 简单问答、单步生成任务(如生成公司名称、翻译句子)。 + - 快速原型开发,无需复杂配置 + + ``` + chain = LLMChain(llm=model, prompt=prompt) + result = chain.invoke({"text": "Hello"}) + ``` + + - **LCEL** + + - 采用声明式管道语法(`|`操作符),允许更直观的链式组合 + - 通过管道连接组件,代码更简洁且逻辑清晰 + - **适用场景** + - 复杂工作流(如多模型协作、动态路由、实时流式交互)。 + - 生产级应用开发,需高并发、稳定性和可观察性(如集成LangSmith跟踪) + + ``` + chain = prompt | model | output_parser + result = chain.invoke({"text": "Hello"}) + ``` + + + +* LCEL 核心语法 + + * 作用:将左侧组件的输出传递给右侧组件作为输入,构建了类似Unix管道运算符的设计 + * 语法规则:`chain = component_a | component_b | component_c ` + * 案例实战:构建一个简单的问答链 + + ``` + from langchain_core.prompts import ChatPromptTemplate + from langchain_openai import ChatOpenAI + from langchain_core.output_parsers import StrOutputParser + + # 定义组件 + prompt = ChatPromptTemplate.from_template("回答这个问题:{question}") + + # #模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + parser = StrOutputParser() + + # 构建 LCEL 链 + chain = prompt | model | parser + + # 调用 + result = chain.invoke({"question": "如何学习AI大模型?"}) + print(result) + ``` + + ``` + # 旧版本的写法, 创建LLMChain(类似构建一个Service类) + chain = LLMChain( + llm=model, + prompt=prompt_template + ) + + # 执行调用(类似service.execute(input)),run方法被淘汰了,统一invoke方法调用 + result = chain.invoke("智能手机") + print(result) + # 输出:"1. 高清OLED屏幕 2. 5000mAh大电池 3. 旗舰级处理器" + ``` + + + + + + + + + + + + + + + +#### 第3集 LLM大模型Stream流式输出实战 + +**简介: LLM大模型Stream流式输出实战** + +* Stream流式响应实战 + + * 前面我们的大模型输出都是一次性响应,这个容易造成体验不好 + * 支持流式响应(Streaming Response),能够显著提升应用的响应速度和用户体验。 + * 流式响应的核心在于逐步生成和返回数据,而不是等待整个结果生成后再一次性返回 + +* 案例实战:故事小助手 + + ``` + from langchain_openai import ChatOpenAI + + # #模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + for chunk in model.stream("讲一个翠花的故事。"): + print(chunk.content, end="", flush=True) + ``` + +* 案例实战:科普助手(采用LCEL表达式进行) + + ``` + from langchain_core.prompts import ChatPromptTemplate + from langchain_openai import ChatOpenAI + from langchain_core.output_parsers import StrOutputParser + + # 1. 定义提示词模版 + prompt = ChatPromptTemplate.from_template("用100字解释以下知识点:{concept}") + + # 2. 创建模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7, + streaming=True # 启用流式 + ) + + # 使用 LCEL 语法 + streaming_chain = prompt | model | StrOutputParser() + + # 2. 执行流式调用 + for chunk in streaming_chain.stream({"concept": "小滴课堂"}): + print(chunk, end="", flush=True) # 逐词输出 + ``` + +* 流式响应的优势与限制 + + * 优势 + * 提升用户体验:用户可以看到实时的生成过程,避免长时间等待。 + * 节省内存:逐步生成和返回数据,避免一次性加载大量数据。 + * 灵活性高:支持同步、异步和事件驱动的流式响应,适用于多种场景29。 + * 限制 + * 等待时间较长:涉及大语言模型的节点可能需要较长时间才能返回完整结果。 + * 复杂性较高:事件驱动的流式响应需要精细的控制和管理 + + + + + + + + + + + + + +#### 第4集 输出解析器OutputParse实战和原理讲解 + +**简介: 输出解析器OutputParse实战和原理讲解** + +* 为什么需要输出解析OutputParse? + * 大模型原始输出通常是**非结构化文本** + * 实际需要: + * 将大模型的自由文本转为结构化数据(类似Java的JSON/XML解析) + * 自动处理模型输出的格式错误 + +![image-20250301152117731](D:/学习/笔记/img/image-20250301152117731.png) + +* 解析器工作原理 + + * `输入文本 → LLM 生成 → 解析器 → 结构化数据` + * 工作原理: + - 在提示模板中预留一个占位符变量,由输出解析器负责填充。 + - 当 LLM 按照输出要求返回文本答案后,该答案会被传递给输出解析器,解析为预期的数据结构。 + - **通俗来说:就是在prompt结尾追加,告诉大模型应该返回怎样的格式** + * 解析器核心接口 + * `parse()`:解析原始文本为结构化数据。 + * `parse_with_prompt()`:结合上下文提示词解析(处理多轮对话场景)。 + * `get_format_instructions()`:生成提示词模板,指导 LLM 输出格式。 + * 文档地址:https://python.langchain.com/docs/concepts/output_parsers/ + +* 输出解析器基础结构三要素 + + ``` + from langchain.output_parsers import XxxParser + + # 要素1:创建解析器(类似Java的Gson实例) + parser = XxxParser() + + # 要素2:构建提示词模板(注意{format_instructions}占位符) + prompt = PromptTemplate( + template="请生成用户信息,按格式:{format_instructions}\n输入:{input}", + input_variables=["input"], + partial_variables={"format_instructions": parser.get_format_instructions()} + ) + + # 要素3:组合成链(类似Java的责任链模式) + chain = prompt | model | parser + ``` + + * 测试查看 多个解析器prompt提示词 + + ``` + from langchain_core.output_parsers import JsonOutputParser,CommaSeparatedListOutputParser + from langchain_core.prompts import ChatPromptTemplate + + #parser = JsonOutputParser() + parser = CommaSeparatedListOutputParser() + # 获取格式指令 + format_instructions = parser.get_format_instructions() + + # 定义提示词 + prompt = ChatPromptTemplate.from_template(""" + 分析以下商品评论,按指定格式返回结果: + + 评论内容:{review} + + 格式要求: + {format_instructions} + """) + + # 注入格式指令 + final_prompt = prompt.partial(format_instructions=format_instructions) + print(final_prompt.format_prompt(review="这个手机超级好用,超级流畅")) + ``` + + + +* 案例实战 + + ``` + from langchain_core.output_parsers import CommaSeparatedListOutputParser + from langchain.prompts import PromptTemplate + from langchain_openai import ChatOpenAI + # 1. 实例化一个 CommaSeparatedListOutputParser对象,将逗号分隔文本转为列表 + output_parser = CommaSeparatedListOutputParser() + + format_instructions = output_parser.get_format_instructions() + # 2. 创建一个 prompt template,将 output_parser.get_format_instructions()追加到的prompt里面 + prompt = PromptTemplate( + template="列举多个常见的 {subject}.{format_instructions}", + input_variables=["subject"], + partial_variables={"format_instructions": format_instructions}, + ) + #3. 创建模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + # 4. 构建链 + chain = prompt | model | output_parser + + print(chain.invoke({"subject": "水果"})) + ``` + +* !!!注意!!!! + + * 虽然有解析器,但是存在大模型返回内容不符合格式要求,则一样会报错 + * 本质解析器还是Prompt提示词内容,如果有报错则可以让大模型重试 + + + + + + + + + + + +#### 第5集 LangChain字符串和列表输出解析器实战 + +**简介: LangChain字符串和列表输出解析器实战** + +* 需求 + + * 课程讲常见的解析器,其他的可以自行拓展 + * 掌握字符串和列表输出解析器 + * 掌握解析器源码流程 + +* `StrOutputParser` + + * **功能**:将模型输出直接解析为字符串,保留原始文本输出,不做处理(默认行为) + + * **适用场景**:无需结构化处理,直接返回原始文本 + + ``` + from langchain_core.output_parsers import StrOutputParser + from langchain_openai import ChatOpenAI + from langchain_core.prompts import ChatPromptTemplate + + # 定义链 + prompt = ChatPromptTemplate.from_template("写一首关于{topic}的诗") + + #创建模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + # 创建解析器 + parser = StrOutputParser() + + chain = prompt | model | parser + + # 调用 + result = chain.invoke({"topic": "秋天"}) + print(result) # 输出:秋天的落叶轻轻飘落... + ``` + +* `CommaSeparatedListOutputParser` + + * **功能**:将逗号分隔的文本解析为列表。 + * **适用场景**:模型生成多个选项或标签。 + + ``` + from langchain_core.prompts import ChatPromptTemplate + from langchain_core.output_parsers import CommaSeparatedListOutputParser + from langchain_openai import ChatOpenAI + parser = CommaSeparatedListOutputParser() + prompt = ChatPromptTemplate.from_template("列出3个与{topic}相关的关键词:") + + #创建模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + chain = prompt | model | parser + + # 调用 + result = chain.invoke({"topic": "Java"}) + print(result) # 输出:["机器学习", "深度学习", "神经网络"] + ``` + + + + + + + + + + + + + + + + + +#### 第6集 Json输出解析器和问答系统答案提取实战 + +**简介: Json输出解析器和问答系统答案提取实战** + +* `JsonOutputParser` + + * **功能**:将模型输出解析为 JSON 对象。 + * **适用场景**:需要模型返回结构化数据(如 API 响应),**多数要结合Pydantic进行使用** + + ``` + from langchain_core.prompts import ChatPromptTemplate + from langchain_openai import ChatOpenAI + from langchain_core.output_parsers import JsonOutputParser + + prompt = ChatPromptTemplate.from_template( + "返回JSON:{{'name': '姓名', 'age': 年龄}},输入:{input}" + ) + + #创建模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + chain = prompt | model | JsonOutputParser() + result = chain.invoke({"input": "张三今年30岁,准备结婚后学AI大模型课程"}) + print(result) + # 输出:{"name": "张三", "age": 30} + ``` + +* 案例实战:问答系统答案提取 + + * 目标:从模型回答中提取答案和置信度。 + + ``` + from langchain_core.output_parsers import JsonOutputParser + from langchain_core.prompts import ChatPromptTemplate + from langchain_openai import ChatOpenAI + + # 定义 JSON 格式要求 + prompt = ChatPromptTemplate.from_template(""" + 回答以下问题,返回 JSON 格式: + {{ + "answer": "答案文本", + "confidence": 置信度(0-1) + }} + 问题:{question} + """) + + + #创建模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + parser = JsonOutputParser() + chain = prompt | model | parser + + # 调用 + result = chain.invoke({"question": "地球的半径是多少?"}) + print(result) + #{'answer': '地球的平均半径约为6,371公里。', 'confidence': 0.95} + + print(f"答案:{result['answer']},置信度:{result['confidence']}") + #答案:地球的平均半径约为6,371公里。,置信度:0.95 + ``` + + + + + + + + + + + + + +![logo](D:/学习/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"** +**更多高级课程请访问 xdclass.net** + +### 第三十三章 Pydantic模型和LLM高级解析器实战 + + + + + +#### 第1集 Python模型管理Pydantic介绍和安装 + +**简介: Python模型管理Pydantic介绍和安装** + +* 什么是Pydatic + + * Pydantic 是一个在 Python 中用于数据验证和解析的第三方库,是 Python 使用最广泛的数据验证库 + + * 声明式的方式定义数据模型和,结合Python 类型提示的强大功能来执行数据验证和序列化 + + * Pydantic 提供了从各种数据格式(例如 JSON、字典)到模型实例的转换功能 + + * 官方文档:https://pydantic.com.cn/ + + + +* 为什么要用Pydantic + + * 处理来自系统外部的数据,如API、用户输入或其他来源时,必须牢记开发中的原则:“永远不要相信用户的输入” + * AI智能体需要处理结构化数据(API请求/响应,配置文件等) + * 必须对这些数据进行严格的检查和验证,确保它们被适当地格式化和标准化 + * **解决的问题** + - **数据验证**:自动验证输入数据的类型和格式 + - **类型提示**:结合Python类型提示系统 + - **序列化**:轻松转换数据为字典/JSON + - **配置管理**:支持复杂配置项的验证 + + image-20250228151848080 + +* 对比Java开发的模型验证 + + * 典型Java方式(需手写校验逻辑) + + ``` + public class User { + private String name; + private int age; + + // 需要手写校验方法 + public void validate() throws IllegalArgumentException { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("姓名不能为空"); + } + if (age > 150) { + throw new IllegalArgumentException("年龄不合法"); + } + } + } + ``` + + * 传统Python方式(样板代码) + + ``` + class User: + def __init__(self, name: str, age: int): + if not isinstance(name, str): + raise TypeError("name必须是字符串") + if not isinstance(age, int): + raise TypeError("age必须是整数") + if age > 150: + raise ValueError("年龄必须在0-150之间") + self.name = name + self.age = age + ``` + + * pydantic方式(声明式验证) + + ``` + # 只需3行代码即可实现完整验证! + from pydantic import BaseModel, Field + class User(BaseModel): + name: str = Field(min_length=1, max_length=50) # 内置字符串长度验证 + age: int = Field(ge=0, le=150) # 数值范围验证(类似Java的@Min/@Max) + ``` + + + +* 案例实战 + + * 模块安装 Pydantic V2(需要Python 3.10+) + + ``` + pip install pydantic==2.7.4 + ``` + + * 使用 + + * Pydantic 的主要方法是创建继承自 BaseModel 的自定义类 + + ``` + from pydantic import BaseModel + + # 类似Java中的POJO,但更强大 + class UserProfile(BaseModel): + username: str # 必须字段 + age: int = 18 # 默认值 + email: str | None = None # 可选字段 + + # 实例化验证 + user1 = UserProfile(username="Alice") + print(user1) # username='Alice' age=18 email=None + + user2 = UserProfile(username="Bob", age="20") # 自动类型转换 + print(user2.age) # 20(int类型) + ``` + + * 创建实例与校验 + + ``` + try: + UserProfile(username=123) # 触发验证错误 + except ValueError as e: + print(e.errors()) + # [{ + # 'type': 'string_type', + # 'loc': ('username',), + # 'msg': 'Input should be a valid string', + # 'input': 123 + # }] + ``` + + * 字段类型验证 + + ``` + from pydantic import BaseModel,HttpUrl,ValidationError + + class WebSite(BaseModel): + url: HttpUrl #URL格式验证 + visits: int = 0 #默认值 + tags: list[str] = [] #字符串列表 + + valid_data = { + "url": "https://www.baidu.com", + "visits": 100, + "tags": ["python", "fastapi"] + } + + # try: + # website = WebSite(**valid_data) + # print(website) + # except ValidationError as e: + # print(e.errors()) + + try: + website = WebSite(url="xdclass.net",visits=100) + print(website) + except ValidationError as e: + print(e.errors()) + ``` + + * 数据解析/序列化 + + ``` + from pydantic import BaseModel + + class Item(BaseModel): + name: str + price: float + + # 从JSON自动解析(类似Jackson) + data = '{"name": "Widget", "price": "9.99"}' # 字符串数字自动转换 + item = Item.model_validate_json(data) + + # 导出为字典(类似Java的POJO转Map) + print(item.model_dump()) # {'name': 'Widget', 'price': 9.99} + ``` + + * 调试技巧:打印模型结构 + + ``` + print(Website.model_json_schema()) + # 输出完整的JSON Schema + ``` + + + + + + + + + + + + + +#### 第2集 Pydantic字段校验Field函数多案例实战 + +**简介:Pydantic字段校验Field多案例实战** + +* Filed函数讲解 + + * Field函数通常用于给模型字段添加额外的元数据或者验证条件。 + * 例如,title参数用来设置字段的标题,min_length用来限制最小长度。 + * 核心 + * **`...` 的本质**:表示“无默认值”,强制字段必填。 + * **`Field` 的常用参数**: + - `title`:字段标题(用于文档) + - `description`:详细描述 + - `min_length`/`max_length`(字符串、列表) + - `gt`/`ge`/`lt`/`le`(数值范围) + - `regex`(正则表达式) + - `example`(示例值,常用于 API 文档) + +* 案例实战 + + * 必填字段(无默认值) + + * 必填字段: + * ... 表示该字段必须显式提供值。若创建模型实例时未传入此字段,Pydantic 会抛出验证错误(ValidationError)。 + * 默认值占位 + * Field 的第一个参数是 default,而 ... 在此处的语义等价于“无默认值”。 + * 若省略 default 参数(如 Field(title="用户名")),Pydantic 会隐式使用 ...,但显式写出更明确 + + ``` + from pydantic import BaseModel, Field + class User(BaseModel): + name: str = Field(..., title="用户名", min_length=2) + # 正确用法 + user = User(name="Alice") + + # 错误用法:缺少 name 字段 + user = User() # 触发 ValidationError + ``` + + * 可选字段(有默认值) + + ``` + from pydantic import BaseModel, Field + class UserOptional(BaseModel): + name: str = Field("Guest", title="用户名") # 默认值为 "Guest" + + # 可不传 name,自动使用默认值 + user = UserOptional() + print(user.name) # 输出 "Guest" + ``` + + * 以下两种写法完全等价, 使用 `Field` 的优势在于可以附加额外参数(如 `title`、`min_length`、`description` 等)。 + + ``` + # 写法 1:省略 Field,直接类型注解 + name: str + + # 写法 2:显式使用 Field(...) + name: str = Field(...) + ``` + + * 数值类型必填 + + ``` + from pydantic import BaseModel, Field, ValidationError + + class Product(BaseModel): + price: float = Field(..., title="价格", gt=0) # 必须 > 0 + stock: int = Field(..., ge=0) # 必须 >= 0 + + # 正确 + product = Product(price=99.9, stock=10) + + # 错误:price <= 0 + try: + Product(price=-5, stock=10) + except ValidationError as e: + print(e.json()) # 提示 "price" 必须大于 0 + ``` + + * 嵌套模型必填 + + ``` + from pydantic import BaseModel, Field + class Address(BaseModel): + city: str = Field(..., min_length=1) + street: str + + class User(BaseModel): + name: str = Field(...) + address: Address # 等效于 address: Address = Field(...) + + # 正确 + user = User(name="Alice", address={"city": "Shanghai", "street": "Main St"}) + + # 错误:缺少 address + user = User(name="Bob") # 触发 ValidationError + ``` + + * 明确可选字段 + + ``` + from pydantic import BaseModel, Field + from typing import Optional + + class User(BaseModel): + name: str = Field(...) + email: Optional[str] = Field(None, title="邮箱") # 可选,默认值为 None + + # 正确:不传 email + user = User(name="Alice") + ``` + + * 混合使用默认值和必填 + + ``` + from pydantic import BaseModel, Field + + class Config(BaseModel): + api_key: str = Field(...) # 必填 + timeout: int = Field(10, ge=1) # 可选,默认 10,但必须 >=1 + + # 正确 + config = Config(api_key="secret") + assert config.timeout == 10 + + # 错误:未传 api_key + Config(timeout=5) # 触发 ValidationError + ``` + + + + + + + + + + + +#### 第3集 Pydantic自定义验证器多案例实战 + +**简介:Pydantic自定义验证器多案例实战** + +* `@field_validator`介绍 + + * 是 Pydantic 中用于为**单个字段**添加自定义验证逻辑的装饰器 + + * **适用场景**:当默认验证规则(如 `min_length`、`gt`)无法满足需求时,通过编写代码实现复杂校验。 + + * **触发时机**:默认在字段通过基础类型和规则校验后执行(可通过 `mode` 参数调整) + + * 基础语法 + + ``` + from pydantic import BaseModel, ValidationError, field_validator + + class User(BaseModel): + username: str + + # 带默认值的可选字段 + # int | None表示 age 变量的类型可以是整数 (int) 或 None,旧版本的写法:age: Union[int, None] + # Python 3.10 开始引入,替代了早期通过 Union[int, None] 的形式(仍兼容) + age: int | None = Field( + default=None, + ge=18, + description="用户年龄必须≥18岁" + ) + + @field_validator("username") + def validate_username(cls, value: str) -> str: + # cls: 模型类(可访问其他字段) + # value: 当前字段的值 + if len(value) < 3: + raise ValueError("用户名至少 3 个字符") + return value # 可修改返回值(如格式化) + ``` + +* 案例实战 + + * 字符串格式校验 + + ``` + from pydantic import BaseModel, Field,field_validator + + class User(BaseModel): + email: str + + @field_validator("email") + def validate_email(cls, v): + if "@" not in v: + raise ValueError("邮箱格式无效") + return v.lower() # 返回格式化后的值 + + # 正确 + User(email="ALICE@example.com") # 自动转为小写:alice@example.com + + # 错误 + #User(email="invalid") # 触发 ValueError + ``` + + * 验证用户名和长度 + + ``` + from pydantic import BaseModel, Field,field_validator + class User(BaseModel): + username: str = Field(..., min_length=3) + + @field_validator("username") + def validate_username(cls, v): + if "admin" in v: + raise ValueError("用户名不能包含 'admin'") + return v + + # 正确 + User(username="alice123") + + # 错误 + #User(username="admin") # 触发自定义验证错误 + ``` + + * 密码复杂性验证 + + ``` + from pydantic import BaseModel, Field,field_validator + + class Account(BaseModel): + password: str + + @field_validator("password") + def validate_password(cls, v): + errors = [] + if len(v) < 8: + errors.append("至少 8 个字符") + if not any(c.isupper() for c in v): + errors.append("至少一个大写字母") + if errors: + raise ValueError("; ".join(errors)) + return v + + # 错误:密码不符合规则 + Account(password="weak") # 提示:至少 8 个字符; 至少一个大写字母 + ``` + + * 多个字段共享验证器 + + ``` + from pydantic import BaseModel, Field,field_validator + class Product(BaseModel): + price: float + cost: float + + @field_validator("price", "cost") + def check_positive(cls, v): + if v <= 0: + raise ValueError("必须大于 0") + return v + + # 同时验证 price 和 cost 是否为正数 + + Product(price=1, cost=-2) + ``` + + + +* 注意事项 + + * 忘记返回值:验证器必须返回字段的值(除非明确要修改)。 + + ``` + @field_validator("email") + def validate_email(cls, v): + if "@" not in v: + raise ValueError("Invalid email") + # ❌ 错误:未返回 v + ``` + +* **Pydantic V2 现为 Pydantic 的当前生产发布版本** + + * 网上不少是V1版本的教程,需要注意 + * Pydantic V1和Pydantic V2的API差异 + + | Pydantic V1 | Pydantic V2 | + | :----------------------- | :----------------------- | + | `__fields__` | `model_fields` | + | `__private_attributes__` | `__pydantic_private__` | + | `__validators__` | `__pydantic_validator__` | + | `construct()` | `model_construct()` | + | `copy()` | `model_copy()` | + | `dict()` | `model_dump()` | + | `json_schema()` | `model_json_schema()` | + | `json()` | `model_dump_json()` | + | `parse_obj()` | `model_validate()` | + | `update_forward_refs()` | `model_rebuild()` | + + + + + + + + + + + + + + + +#### 第4集 重点-解析器PydanticOutputParser实战 + +**简介:重点-解析器PydanticOutputParser实战** + +* 为啥要用为什么需要Pydantic解析? + + * 结构化输出:将非结构化文本转为可编程对象 + * 数据验证:自动验证字段类型和约束条件,单纯json解析器则不会校验 + * 开发效率:减少手动解析代码 + * 错误处理:内置异常捕获与修复机制 + +* **案例实战一:大模型信息输出提取( `PydanticOutputParser` 结合Pydantic模型验证输出)** + + ``` + from langchain_openai import ChatOpenAI + from pydantic import BaseModel, Field + from langchain_core.output_parsers import PydanticOutputParser + #定义模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # Step1: 定义Pydantic模型 + class UserInfo(BaseModel): + name: str = Field(description="用户姓名") + age: int = Field(description="用户年龄", gt=0) + hobbies: list[str] = Field(description="兴趣爱好列表") + + # Step2: 创建解析器 + parser = PydanticOutputParser(pydantic_object=UserInfo) + + # Step3: 构建提示模板 + from langchain_core.prompts import ChatPromptTemplate + + prompt = ChatPromptTemplate.from_template(""" + 提取用户信息,严格按格式输出: + {format_instructions} + + 输入内容: + {input} + """) + + # 注入格式指令 + prompt = prompt.partial( + format_instructions=parser.get_format_instructions() + ) + + # Step4: 组合处理链 + chain = prompt | model | parser + + + # 执行解析 + result = chain.invoke({ + "input": """ + 我的名称是张三,年龄是18岁,兴趣爱好有打篮球、看电影。 + """ + }) + + print(type(result)) + print(result) + ``` + +* **案例实战二:电商评论情感分析系统(JsonOutputParser和pydantic结合)** + + * `JsonOutputParser`与`PydanticOutputParser`类似 + * 新版才支持从pydantic获取约束模型,该参数并非强制要求,而是可选的增强功能 + * `JsonOutputParser`可以处理流式返回的部分JSON对象。 + + ``` + from langchain_openai import ChatOpenAI + from pydantic import BaseModel, Field + from langchain_core.prompts import ChatPromptTemplate + from langchain_core.output_parsers import JsonOutputParser + #定义模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # 定义JSON结构 + class SentimentResult(BaseModel): + sentiment: str + confidence: float + keywords: list[str] + + # 构建处理链 + parser = JsonOutputParser(pydantic_object=SentimentResult) + + prompt = ChatPromptTemplate.from_template(""" + 分析评论情感: + {input} + 按以下JSON格式返回: + {format_instructions} + """).partial(format_instructions=parser.get_format_instructions()) + + chain = prompt | model | parser + + # 执行分析 + result = chain.invoke({"input": "物流很慢,包装破损严重"}) + print(result) + + # 输出: + # { + # "sentiment": "negative", + # "confidence": 0.85, + # "keywords": ["物流快", "包装破损"] + # } + + + # 2. 执行流式调用 + #for chunk in chain.stream({"input": "物流很慢,包装破损严重"}): + # print(chunk) # 逐词输出 + ``` + + + + + + + + + + + + + +#### 第5集 重点-大模型修复机制OutputFixingParser + +**简介:大模型修复机制OutputFixingParser解析器** + +* `OutputFixingParser` + + * 是LangChain中用于修复语言模型(LLM)输出格式错误的工具,通常与`PydanticOutputParser`配合使用。 + * 当原始解析器因格式问题(如JSON语法错误、字段缺失等)失败时,它能自动调用LLM修正输出,提升解析的鲁棒性。 + * 核心功能: + * 自动纠错:修复不规范的输出格式(如单引号JSON、字段顺序错误等)。 + * 兼容性:与Pydantic数据模型无缝集成,支持结构化输出验证。 + * 容错机制:避免因模型输出不稳定导致程序中断 + + export_4232z + +* 核心语法与使用步骤 + + * 基础语法 + + ``` + from langchain.output_parsers import OutputFixingParser, PydanticOutputParser + from langchain_openai import ChatOpenAI + + # 步骤1:定义Pydantic数据模型 + class MyModel(BaseModel): + field1: str = Field(description="字段描述") + field2: int + + # 步骤2:创建原始解析器 + parser = PydanticOutputParser(pydantic_object=MyModel) + + + #定义模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # 步骤3:包装为OutputFixingParser + fixing_parser = OutputFixingParser.from_llm(parser=parser, llm=model) + ``` + + * 参数说明: + * parser: 原始解析器对象(如PydanticOutputParser)。 + * llm: 用于修复错误的语言模型实例。 + * max_retries(可选): 最大重试次数(默认1) + +* 案例实战 + + * 修复机制 + * 检测到错误后,将错误信息与原始输入传递给LLM。 + * LLM根据提示生成符合Pydantic模型的修正结果。 + + ``` + from langchain.output_parsers import OutputFixingParser + from langchain_core.output_parsers import PydanticOutputParser + from langchain_openai import ChatOpenAI + from pydantic import BaseModel, Field + from typing import List + + class Actor(BaseModel): + name: str = Field(description="演员姓名") + film_names: List[str] = Field(description="参演电影列表") + + parser = PydanticOutputParser(pydantic_object=Actor) + + #定义模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # 包装原始解析器 + fixing_parser = OutputFixingParser.from_llm(parser=parser, llm=model) + + # 模拟模型输出的错误格式(使用单引号) + misformatted_output = "{'name': '小滴课堂老王', 'film_names': ['A计划','架构大课','一路向西']}" + + #在LLM 链中,chain.invoke会将 LLM 返回的文本字符串传入output_parser.invoke + #而`output_parser.invoke`最终会调用到`output_parser.parse`。 + + # try: + # parsed_data = parser.parse(misformatted_output) # 直接解析会失败 + # except Exception as e: + # print(f"解析失败:{e}") # 抛出JSONDecodeError + + # 使用OutputFixingParser修复并解析 + fixed_data = fixing_parser.parse(misformatted_output) + + print(type(fixed_data)) + print(fixed_data.model_dump()) + # 输出:{'name': '小滴课堂老王', 'film_names': ['A计划','架构大课','一路向西']} + ``` + + * 完整正常案例 + + ``` + from langchain.output_parsers import OutputFixingParser + from langchain_core.output_parsers import PydanticOutputParser + from langchain_openai import ChatOpenAI + from pydantic import BaseModel, Field + from langchain_core.prompts import PromptTemplate + from typing import List + + class Actor(BaseModel): + name: str = Field(description="演员姓名") + film_names: List[str] = Field(description="参演电影列表") + + parser = PydanticOutputParser(pydantic_object=Actor) + + #定义模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + prompt = PromptTemplate( + template="{format_instructions}\n{query}", + input_variables=["query"], + partial_variables={"format_instructions": parser.get_format_instructions()}, + ) + + # 包装原始解析器 + fixing_parser = OutputFixingParser.from_llm(parser=parser, llm=model) + chain = prompt | model | fixing_parser + response = chain.invoke({"query": "说下成龙出演过的5部动作电影? "}) + print(response) + print(type(response)) + print(response.model_dump()) + ``` + + + +* 常见问题与解决方案 + + * 修复失败的可能原因 + * 模型能力不足:升级LLM版本(如使用更高级的模型和参数量)。 + * 提示词不清晰:在提示模板中明确格式要求。 + * 网络问题:通过代理服务优化API访问 + + + + + + + + + + + + + +![logo](D:/学习/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"** +**更多高级课程请访问 xdclass.net** + +### 第三十四章 AI大模型必备之RAG和智能医生实战 + + + +#### 第1集 什么是大模型的幻觉输出 + +**简介: 什么是大模型的幻觉输出** + +* “幻觉输出”(Hallucination) + + * 是大语言模型(如GPT、Llama、DeepSeek等)生成内容时的一种常见问题 + * 指模型输出看似合理但实际错误、虚构或脱离事实的信息。 + * 这种现象类似于人类的“臆想”——模型基于不完整或错误的知识,生成逻辑通顺但内容失实的回答 + +* 表现形式 + + * 虚构事实 + * 例1:生成不存在的书籍(如称《时间简史》是鲁迅所写)。 + * 例2:编造错误的历史事件(如“秦始皇于公元前200年统一六国”)。 + * 错误推理 + * 例:回答数学问题时,步骤正确但结果错误(如“2+3=6”)。 + * 过度泛化 + * 例:将特定领域的知识错误迁移到其他领域(如用医学术语解释物理现象)。 + * 矛盾内容 + * 例:同一段回答中前后逻辑冲突(如先说“地球是平的”,后又说“地球绕太阳公转”) + + image-20250310151927722 + +* 幻觉产生的根本原因 + + * **训练数据的局限性** + - **数据噪声**:模型训练数据可能包含错误、过时或偏见信息(如互联网上的谣言)。 + - **知识截止**:模型训练后无法获取新知识(如GPT-3的数据截止到2021年)。 + * **模型生成机制** + - **概率驱动**:模型通过预测“最可能的下一词”生成文本,而非验证事实。 + - **缺乏常识判断**:无法区分“合理表达”与“真实事实”。 + * **模型策略的副作用** + - **创造性模式**:模型在开放生成任务中更易“放飞自我”(如写小说时虚构细节) + +* 典型案例分析 + + - **案例1:医疗问答** + + - **问题**:用户问“新冠疫苗会导致自闭症吗?” + - **幻觉输出**:模型可能生成“有研究表明两者存在相关性”(错误)。 + - **解决方案**:RAG检索WHO官方声明,生成“无科学证据支持此说法”。 + + - **案例2:金融咨询** + + - **问题**:“2024年比特币会涨到10万美元吗?” + + - **幻觉输出**:模型虚构专家预测或历史数据。 + + - **解决方案**:限定回答范围(如“截至2023年,比特币最高价格为…”) + + + +* 产生的影响 + + * **误导用户**:在医疗、法律等专业领域可能引发严重后果。 + * **信任危机**:用户对模型输出的可靠性产生质疑。 + * **技术瓶颈**:暴露大模型在事实性、可解释性上的不足。 + + + +* 如何缓解幻觉输出(注意:不是解决) + * **技术改进方案** + - **检索增强生成(RAG)**:通过实时检索外部知识库(如维基百科、专业数据库),为生成提供事实依据。 + - **微调对齐**:用高质量数据(如标注正确的问答对)调整模型输出偏好。 + - **强化学习(RLHF)**:通过人类反馈惩罚错误生成,奖励准确回答。 + * **生成策略优化** + - **温度参数调整**:降低随机性(`temperature=0`),减少“胡编乱造”。 + - **后处理校验**:添加事实核查模块(如调用知识图谱API验证答案)。 + * **用户侧应对** + - **提示词设计**:明确要求模型标注不确定性(如“回答需基于2023年数据”)。 + - **多源验证**:对关键信息人工交叉核对(如学术论文、权威网站)。 + + + + + + + + + +#### 第2集 带你走进RAG检索增强生成和应用场景 + +**简介: 带你走进RAG检索增强生成和应用场景** + +* 什么是RAG技术 + + * RAG(Retrieval-Augmented Generation)检索增强生成,是结合信息检索与文本生成的AI技术架构。 + * 核心思想: + * 先通过检索系统找到与问题相关的知识片段 + * 再将检索结果与问题共同输入生成模型得到最终答案 + * 类比人类解答问题的过程:遇到问题时先查资料(检索),再结合资料组织回答(生成) + + ![2](D:/学习/笔记/img/2-1580890.png) + + * 用Java伪代码描述RAG工作流程: + + ``` + public class JavaRAG { + public static void main(String[] args) { + // 1. 加载文档 + List docs = FileLoader.load("data/"); + + // 2. 创建向量库(类似建立索引) + VectorDB vectorDB = new FAISSIndex(docs); + + // 3. 处理用户问题 + String question = "如何申请报销?"; + List results = vectorDB.search(question, 3); + + // 4. 生成回答 + String context = String.join("\n", results.stream() + .map(Document::content) + .collect(Collectors.toList())); + + String answer = LLM.generate(context + "\n问题:" + question); + System.out.println(answer); + } + } + ``` + + + +* 需求背景:为什么需要RAG? + + * 传统生成模型的局限性 + * 知识过时:大模型(如GPT-3)训练数据截止于特定时间,无法覆盖实时信息。 + * 幻觉问题:生成内容可能包含不准确或虚构的信息。 + * 领域局限性:通用模型缺乏垂直领域的专业知识(如法律、医疗),企业私有数据无法写入公开模型 + + | 题类型 | 示例 | 传统模型表现 | + | :----------------: | :------------------------: | :----------------------------------: | + | 时效性问题 | "2027年诺贝尔奖得主是谁?" | 无法回答(训练数据截止到当时的数据) | + | 领域专业问题 | "如何配置Hadoop集群参数?" | 回答模糊或错误 | + | 需要引用来源的问题 | "不睡觉有哪些副作用?" | 无法提供可信出处 | + + * 检索与生成的互补性 + * 检索系统:擅长从海量数据中快速找到相关文档(实时、精准),实时从外部知识库检索信息 + * 生成模型:擅长语言理解和流畅输出。 + * 结合优势:RAG通过检索外部知识库增强生成结果,提高准确性和可信度 + * 降低训练成本:无需重新训练模型即可更新知识 + * 生成可验证性:每句回答都可追溯来源文档 + +* 技术架构 + + 1 + +* **涉及的技术链路环节: 文档加载器->文档转换器->文本嵌入模型->向量存储->检索器** + + * 关键技术组件 + + | 组件 | 常用工具 | Java类比 | + | :--------: | :-----------------------: | :------------------: | + | 文档加载器 | PyPDFLoader, Unstructured | FileInputStream | + | 文本分块器 | RecursiveTextSplitter | String.split()增强版 | + | 元数据处理 | LangChain Document类 | DTO对象封装 | + | 向量存储 | FAISS, Pinecone | 数据库索引 | + +* 典型应用场景 + + * 案例1:智能客服系统 + + * 传统问题:客服知识库更新频繁,模型无法实时同步。 + + * RAG方案: + + * 实时检索最新的产品文档、FAQ。 + * 生成个性化回答(如退货政策、故障排查)。 + + ``` + 用户问:"这个小滴手机支持老人家使用不?" + 系统检索:产品适合人群相关词条 + 生成回答:"我们这个产品适合18岁以上的成人使用,包括中老年人等" + ``` + + * 效果:减少人工干预,回答准确率提升30%+。 + + * 案例2:医疗问答助手 + + * 传统问题:通用模型缺乏专业医学知识,可能给出危险建议。 + + * RAG方案: + + * 检索权威医学数据库(如PubMed、临床指南)。 + * 生成基于循证医学的答案,标注参考文献来源。 + + ``` + 用户问:"二甲双胍的禁忌症有哪些?" + 系统检索:最新《临床用药指南》第5.3节 + 生成回答:"根据2023版用药指南,二甲双胍禁用于以下情况:1)严重肾功能不全..." + ``` + + * 效果:合规性提升,避免法律风险。 + + * 案例3:金融研究报告生成 + + * 传统问题:市场数据动态变化,模型无法实时分析。 + + * RAG方案: + + * 检索实时财报、新闻、行业数据 → 输入生成模型。 + * 自动生成带有数据支撑的投资建议。 + + ``` + 用户问:"XXX公司财报如何" + 系统检索:某某公司财报 + 生成回答:"根据公司的财报和解读,利润和负债..." + ``` + + * 效果:析师效率提升,报告更新频率加快 + + + + + + + + + + + + + +#### 第3集 LLM智能AI医生+RAG系统案例实战《上》 + +**简介: LLM智能AI医生+RAG系统案例实战** + +* 需求 + + * 快速搭建智能医生客服案例,基于LLM大模型+RAG技术 + * 方便大家可以直观的看相关效果,并方便后续拆解每个步骤 + * 效果:可以根据用户的提问,检索相关私有数据,整合大模型,最终生成返回给用户 + +* 案例实战【本集不提供课程代码,请从下面地址下载】 + + * 创建项目和安装相关环境 + + ``` + # 创建并激活虚拟环境 + python -m venv .venv + source .venv/bin/activate + + # 安装依赖 + pip install -r requirements.txt + ``` + + * 查看训练的文档数据 + + * 项目部署运行 (**版本和课程保持一致,不然很多不兼容!!!**) + + * 下载相关资料 ,使用**【wget】或者【浏览器】远程下载相关依赖包(需要替换群里最新的)** + + ``` + 原生资料下载方式(账号 - 密码 - ip地址 - 端口 需要替换群里最新的,【其他路径不变】) + wget --http-user=用户名 --http-password=密码 http://ip:端口/dcloud_pan/aipan_xd-rag.zip + + + #比如 命令行下 + wget --http-user=admin --http-password=xdclass.net888 http://47.115.31.28:9088/dcloud_pan/aipan_xd-rag.zip + + + # 比如 浏览器直接访问 + http://47.115.31.28:9088/dcloud_pan/aipan_xd-rag.zip + ``` + + * 解压后执行【**依赖很多,版本差异大,务必按照下面执行,否则课程无法进行下去,加我微信 xdclass6**】 + + ``` + # 安装依赖 + pip install -r requirements.txt + ``` + + + + +​ + + + + + + + + + +#### 第4集 LLM智能AI医生+RAG系统案例实战《下》 + +**简介: LLM智能AI医生+RAG系统案例实战** + +* 效果测试 + +![image-20250314122237372](D:/学习/笔记/img/image-20250314122237372.png) + +* 多数同学的问题 + * **为啥可以根据我们的提问,进行检索到对应的词条?而且还可以正确检索?** + * **为啥要加载文件?然后切割?什么是向量数据库?** + * **为啥检索到词条后,还可以用调整输出内容,更加友好?** + * **什么是嵌入大模型?和前面学的LLM大模型有啥区别?** + +image-20250314124004485 + + + + + + + + + +![logo](D:/学习/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"** +**更多高级课程请访问 xdclass.net** + +### 第三十五章 RAG 检索增强生成之Loader实战 + + + +#### 第1集 RAG系统链路和数据加载Loaders技术 + +**简介: RAG系统链路和数据加载Loaders技术** + +* RAG系统与LLM交互架构图 + * 注意 + * 万丈高楼平地起,基础需要打牢固,一步步进行,然后学会举一反三使用 + * 如果直接讲Agent智能体项目,那项目涉及到的很多技术就懵逼了,要学会思路 + +![0](D:/学习/笔记/img/0.png) + + + +* **涉及的技术链路环节: 文档加载器->文档转换器->文本嵌入模型->向量存储->检索器** + + * RAG数据流水线示意图 + + ``` + 原始数据 → 数据加载 → 预处理 → 向量化 → 存储 → 检索增强生成 + ↗ ↗ ↗ + PDF 文本清洗 嵌入模型 + 数据库 分块 + 网页 + ``` + + ![data_connection_diagram](D:/学习/笔记/img/data_connection-95ff2033a8faa5f3ba41376c0f6dd32a.jpg) + +* 文档加载器 (Document Loaders) + + * 外部数据多样性,包括在线,本地 或者数据库等来源 + * 将不同来源的原始数据(如PDF、网页、JSON、、HTML、数据库等)转换为统一格式的文档对象,便于后续处理。 + * **核心任务**:数据源适配与初步结构化 + + + +* LangChain里面的Loader + + * 接口文档地址【如果失效就忽略):https://python.langchain.com/docs/integrations/document_loaders/ + + ``` + from langchain_community.document_loaders import ( + TextLoader, #文本加载 + PyPDFLoader, # PDF + Docx2txtLoader, # Word + UnstructuredHTMLLoader, # HTML + CSVLoader, # CSV + JSONLoader, # JSON + SeleniumURLLoader, # 动态网页 + WebBaseLoader #网页加载 + ) + ``` + + * LangChain 设计了一个统一的接口`BaseLoader`来加载和解析文档, + + ``` + class BaseLoader(ABC): # noqa: B024 + """Interface for Document Loader. + + Implementations should implement the lazy-loading method using generators + to avoid loading all Documents into memory at once. + + `load` is provided just for user convenience and should not be overridden. + """ + + # Sub-classes should not implement this method directly. Instead, they + # should implement the lazy load method. + def load(self) -> list[Document]: + """Load data into Document objects.""" + return list(self.lazy_load()) + ``` + + * 将原始数据(如文件、API 响应、文本文件、网页、数据库等)转换为 LangChain 的 `Document` 对象 + + * `load`方法返回一个`Document`数组, 每个 `Document` 包含 + + * **`page_content`**: 文本内容 + * **`metadata`**: 元数据(如来源、创建时间、作者等) + + ``` + class Document(BaseMedia): + """Class for storing a piece of text and associated metadata. + + Example: + + .. code-block:: python + + from langchain_core.documents import Document + + document = Document( + page_content="Hello, world!", + metadata={"source": "https://example.com"} + ) + """ + + page_content: str + """String text.""" + type: Literal["Document"] = "Document" + ``` + + + +* Loader 的分类与常见类型 + + * 文件加载器(File Loaders) + + | Loader 类型 | 功能描述 | + | :----------------------: | :----------------------------------: | + | `TextLoader` | 加载纯文本文件(.txt) | + | `CSVLoader` | 解析 CSV 文件,按行生成 Document | + | `PyPDFLoader` | 提取 PDF 文本及元数据(基于 PyPDF2) | + | `Docx2txtLoader` | 读取 Word 文档(.docx) | + | `UnstructuredFileLoader` | 通用文件解析(支持多种格式) | + + * 网页加载器(Web Loaders) + + | Loader 类型 | 功能描述 | + | :--------------: | :----------------------------: | + | `WebBaseLoader` | 抓取网页文本内容 | + | `SeleniumLoader` | 处理需要 JavaScript 渲染的页面 | + + * 数据库加载器(Database Loaders) + + | Loader 类型 | 功能描述 | + | :-----------------: | :---------------------: | + | `SQLDatabaseLoader` | 执行 SQL 查询并加载结果 | + | `MongoDBLoader` | 从 MongoDB 中读取数据 | + + * 其他加载器 (自定义) ... + + + + + + + + + + + + + + + +#### 第2集 文档加载器Loaders技术多案例实战 + +**简介: 文档加载器Loaders技术多案例实战** + +* TextLoader - 加载纯文本文件 + + * ##### **通用参数** + + - **`encoding`**: 文件编码(默认 `utf-8`) + - **`autodetect_encoding`**: 自动检测编码(如处理中文乱码) + + ``` + from langchain_community.document_loaders import TextLoader + # 文本加载 + loader = TextLoader("data/test.txt") + documents = loader.load() + print(documents) + print(len(documents)) #长度 + print(documents[0].page_content[:100]) # 打印前100个字符 + print(documents[0].metadata) # 输出: {'source': 'data/test.txt'} + ``` + +* CSVLoader - 加载 CSV 文件 + + * 基础案例代码 + + ``` + from langchain_community.document_loaders import CSVLoader + + loader = CSVLoader("data/test.csv", csv_args={"delimiter": ","}) + documents = loader.load() + # 每行转换为一个 Document,metadata 包含行号 + print(len(documents)) + print(documents[0].metadata) # 输出: {'source': 'data.csv', 'row': 0} + print(documents[0].page_content) + ``` + + * 可指定列名,按行生成文档 + + ``` + from langchain_community.document_loaders import CSVLoader + + #loader = CSVLoader("data/test.csv", csv_args={"delimiter": ","}) + loader = CSVLoader("data/test.csv", csv_args={"fieldnames": ["产品名称", "销售数量", "客户名称"]}) + + documents = loader.load() + # 每行转换为一个 Document,metadata 包含行号 + print(len(documents)) + print(documents[1].metadata) # 输出: {'source': 'data.csv', 'row': 0} + print(documents[1].page_content) + ``` + + + +* JSONLoader - 加载 JSON 文件 + + * 核心参数详解 + + | 参数 | 类型 | 必填 | 说明 | + | :-------------- | :------- | :--- | :----------------------------------------------- | + | `file_path` | str | ✅ | JSON 文件路径 | + | `jq_schema` | str | ✅ | jq 查询语法,定义数据提取逻辑 | + | `content_key` | str | ❌ | 指定作为文本内容的字段(默认直接使用提取到的值) | + | `metadata_func` | Callable | ❌ | 自定义元数据处理函数 | + | `text_content` | bool | ❌ | 是否将提取内容强制转为字符串(默认 True) | + + * 必选参数 `jq_schema` + + * 必须使用 `jq_schema` 语法指定数据提取路径 + * 支持更复杂的 JSON 结构解析 + * jq 语法常用模式 + + | 场景 | jq_schema 示例 | 说明 | + | :----------- | :----------------------------------- | :--------------------------- | + | 提取根级数组 | `.[]` | 适用于 JSON 文件本身是数组 | + | 嵌套对象提取 | `.data.posts[].content` | 提取 data.posts 下的 content | + | 条件过滤 | `.users[] | select(.age > 18)` | 筛选年龄大于18的用户 | + | 多字段合并 | `{name: .username, email: .contact}` | 组合多个字段为对象 | + + * 案例实战 + + * 安装依赖包 + + ``` + pip install jq + ``` + + * 编码实战 + + ``` + from langchain_community.document_loaders import JSONLoader + loader = JSONLoader( + file_path="data/test.json", + jq_schema=".articles[]", # 提取 articles 数组中的每个元素 + content_key="content" # 指定 content 字段作为文本内容 + ) + + docs = loader.load() + print(len(docs)) + print(docs[0]) + ``` + + + + + + + + + + + + + + + + + +#### 第3集 PDF文档加载器实战和常见问题处理 + +**简介: PDF文档加载器实战和常见问题处理** + +* `PyPDFLoader` 加载PDF文件 + + * `PyPDFLoader` 是 LangChain 中专门用于加载和解析 **PDF 文件** 的文档加载器。 + + * 它能将 PDF 按页拆分为多个 `Document` 对象,每个对象包含页面文本和元数据(如页码、来源路径等)。 + + * 适用于处理多页PDF文档的文本提取任务。 + + * 使用步骤 + + * 安装依赖库 + + ``` + pip install pypdf + ``` + + * 案例代码实战 + + ``` + from langchain_community.document_loaders import PyPDFLoader + # PDF加载 + loader = PyPDFLoader("data/test.pdf") + # 加载文档并按页分割 + pages = loader.load() # 返回 Document 对象列表 + + # 查看页数 + print(f"总页数: {len(pages)}") + + # 访问第一页内容 + page_content = pages[0].page_content + metadata = pages[0].metadata + print(f"第一页内容:\n{page_content[:200]}...") # 预览前200字符 + print(f"元数据: {metadata}") + ``` + + * 按需加载, 通过 `load()` 方法的参数控制加载范围: + + ``` + # 加载指定页码范围(例如第2页到第4页) + pages = loader.load([1, 2, 3]) # 注意页码从0开始(第1页对应索引0) + ``` + + * 提取所有文本合并为单个文档, 若需将全部页面内容合并为一个字符串: + + ``` + full_text = "\n\n".join([page.page_content for page in pages]) + print(f"合并后的全文长度: {len(full_text)} 字符") + ``` + + * 常见问题与解决方案 + + * PDF无法加载或内容为空 + + * 原因:PDF为扫描版图片或加密。 + * 解决: + * 使用OCR工具(如pytesseract+pdf2image)提取图片文本。 + * 解密PDF后加载(需密码时,PyPDFLoader暂不支持直接解密) + + * 文本分块不理想 + + * 调整分块策略:选择合适的分隔符或分块大小 + + ``` + text_splitter = RecursiveCharacterTextSplitter( + separators=["\n\n", "\n", "."], # 按段落、句子分割 + chunk_size=500, + chunk_overlap=100 + ) + ``` + +* 高级技巧 + + * **批量处理PDF**:遍历文件夹内所有PDF文件。 + + ``` + import os + pdf_folder = "docs/" + all_pages = [] + for filename in os.listdir(pdf_folder): + if filename.endswith(".pdf"): + loader = PyPDFLoader(os.path.join(pdf_folder, filename)) + all_pages.extend(loader.load()) + ``` + + + + + + + + + + + + + + + + + + + + + + + +#### 第4集 Loader进阶-PDF文档里面的图片提取解析 + +**简介: Loader进阶-PDF文档里面的图片提取解析** + +* 如何提取PDF里面的图片文案? + + * `PyPDFLoader` 仅提取文本,如果没配置第三方类库则会提取不了对应的图片文案 + * 需结合其他库(如`camelot`、`pdfplumber`、`rapidocr-onnxruntime`)提取表格或图像。 + * 如果需要提取,安装好依赖库后,设置`extract_images`参数为`True`。 + +* `RapidOCR-ONNXRuntime `介绍 + + * 是一个基于 ONNX Runtime 推理引擎的轻量级 OCR(光学字符识别)工具库,专注于高效、跨平台部署。 + + * 它是 [RapidOCR](https://github.com/RapidAI/RapidOCR) 项目的一个分支,实现了更高的推理速度和更低的资源占用 + + * 特点: + + * 跨平台支持:支持 Windows、Linux、macOS,以及移动端(Android/iOS)和嵌入式设备。 + + * 多语言识别:支持中文、英文、日文、韩文等多种语言,尤其擅长中英混合文本。 + + * 轻量级:模型体积小(约几 MB),适合资源受限的环境。 + * 预处理与后处理集成:内置图像预处理(如二值化、方向校正)和文本后处理(如去除冗余字符)。 + + * RapidOCR-ONNXRuntime 与其他主流 OCR 工具的对比: + + | 工具 | 引擎 | 速度 | 准确率 | 语言支持 | 依赖项 | 适用场景 | + | :----------------------- | :----------- | :--- | :----- | :------- | :----- | :--------------------- | + | **RapidOCR-ONNXRuntime** | ONNX Runtime | ⭐⭐⭐⭐ | ⭐⭐⭐ | 多语言 | 少 | 跨平台、轻量级部署 | + | **Tesseract** | 自研引擎 | ⭐⭐ | ⭐⭐ | 多语言 | 多 | 历史项目、简单场景 | + | **EasyOCR** | PyTorch | ⭐⭐ | ⭐⭐⭐ | 多语言 | 多 | 快速原型开发 | + | **Microsoft Read API** | 云端服务 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 多语言 | 无 | 企业级、高并发云端需求 | + +* 案例实战 + + * 安装依赖包 (**耗时会有点久**) + + ``` + pip install rapidocr-onnxruntime + ``` + + * 代码实战 + + ``` + from langchain_community.document_loaders import PyPDFLoader + loader = PyPDFLoader("data/pdf-img.pdf", extract_images=True) + pages = loader.load() + print(pages[0].page_content) + ``` + + + + + + + + + + + + + + + + + + + + + + + + + +#### 第5集 网页加载器WebBaseLoader案例实战 + +**简介: Web网页加载器WebBaseLoader案例实战** + +* 什么是WebBaseLoader + + * `WebBaseLoader` 是 LangChain 中用于抓取 **静态网页内容** 的文档加载器。 + * 通过 HTTP 请求直接获取网页 HTML,并提取其中的文本内容(自动清理标签、脚本等非文本元素) + * 生成包含网页文本和元数据的 `Document` 对象 + * 适用于新闻文章、博客、文档页面等静态内容的快速提取。 + * 场景 + * 知识库构建(知识问答、企业知识库)、舆情监控(新闻/社交媒体分析) + * 竞品分析(产品功能/价格监控)、SEO 内容聚合 + +* 使用步骤 + + * 安装依赖库 + + ``` + pip install beautifulsoup4 # HTML 解析依赖(默认已包含) + pip install requests # 网络请求依赖(默认已包含) + ``` + + * 目标网页要求 + + * 无需 JavaScript 渲染(动态内容需改用 `SeleniumURLLoader` ,但是很鸡肋,少用) + * 未被反爬虫机制拦截(如需要,需配置代理或请求头) + * 如果动态网页,且内容提取好,还是需要单独针对不同的网站写代码进行提取内容 + +* 案例实战 + + * 基础用法:加载单个网页 + + ``` + import os + #代码中设置USER_AGENT, 设置USER_AGENT的代码一定要放在WebBaseLoader 这个包前面,不然还是会报错 + os.environ['USER_AGENT'] = 'Mozilla/5.0 (Windows NT 14.0; Win64; x64) AppleWebKit/567.36 (KHTML, like Gecko) Chrome/58.0.444.11 Safari/337.3' + + from langchain_community.document_loaders import WebBaseLoader + #警告日志信息:USER_AGENT environment variable not set, consider setting it to identify your requests. + + # 初始化加载器,传入目标URL列表(可多个) + urls = ["https://www.cnblogs.com"] + loader = WebBaseLoader(urls) + + # 加载文档(返回Document对象列表) + docs = loader.load() + + # 查看结果 + print(f"提取的文本长度: {len(docs[0].page_content)} 字符") + print(f"前200字符预览:\n{docs[0].page_content[:200]}...") + print(f"元数据: {docs[0].metadata}") + ``` + + * 批量加载多个网页 + + ``` + import os + #代码中设置USER_AGENT, 注意设置USER_AGENT的代码一定要放在WebBaseLoader 这个包前面,不然还是会报错 + os.environ['USER_AGENT'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3' + + from langchain_community.document_loaders import WebBaseLoader + #警告日志信息:USER_AGENT environment variable not set, consider setting it to identify your requests. + + # 初始化加载器,传入目标URL列表(可多个) + urls = [ + "https://news.baidu.com/", # 新闻 + "https://tieba.baidu.com/index.html" # 贴吧 + + ] + loader = WebBaseLoader(urls) + docs = loader.load() + + print(f"共加载 {len(docs)} 个文档") + print("各文档来源:") + for doc in docs: + print(f"- {doc.metadata['source']}") + ``` + +* 更多详细API和参数 https://python.langchain.com/docs/integrations/document_loaders/web_base/ + + + + + + + + + +#### 第6集 Word文档加载器实战和常见问题处理 + +**简介: Word文档加载器实战和常见问题处理** + +* `Docx2txtLoader`介绍 + + * 是 LangChain 中专门用于加载 **Microsoft Word 文档(.docx)** 的文档加载器。 + * 提取文档中的纯文本内容(包括段落、列表、表格文字等),忽略复杂格式(如字体、颜色),生成统一的 `Document` 对象。 + * 适用于从 Word 报告中快速提取结构化文本 + +* 使用步骤 + + * 安装依赖库 + + ``` + pip install docx2txt # 核心文本提取库 + ``` + + * 准备.docx文件:确保目标文件为 .docx 格式(旧版 .doc 需转换),且未被加密 + + * 案例代码实战 + + * 基础用法:加载单个Word文档 + + ``` + from langchain_community.document_loaders import Docx2txtLoader + + # 初始化加载器,传入文件路径 + loader = Docx2txtLoader("data/test1.docx") + + # 加载文档(返回单个Document对象) + documents = loader.load() + + # 查看内容 + print(f"文本长度: {len(documents[0].page_content)} 字符") + print(f"前200字符预览:\n{documents[0].page_content[:200]}...") + print(f"元数据: {documents[0].metadata}") + ``` + + * 批量加载文档 + + ``` + from langchain_community.document_loaders import Docx2txtLoader + import os + + folder_path = "data/" + all_docs = [] + + # 遍历文件夹内所有.docx文件 + for filename in os.listdir(folder_path): + if filename.endswith(".docx"): + file_path = os.path.join(folder_path, filename) + loader = Docx2txtLoader(file_path) + all_docs.extend(loader.load()) # 合并所有文档 + print(f"加载文件: {filename}") + + print(all_docs) + ``` + +* 常见问题与解决方案 + + * 加载 .doc 文件报错 + + * 原因:docx2txt 仅支持 .docx 格式。 + * 解决:使用 Word 将 .doc 另存为 .docx。 + + + + * 文档中的图片/图表未被提取 + + * 原因:Docx2txtLoader 仅提取文本,忽略图片。 + * 解决:使用 python-docx 单独提取图片,也可以使用其他组件,类似OCR + + + + + + + + + + + + + + + + + +![logo](D:/学习/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"** +**更多高级课程请访问 xdclass.net** + +### 第三十六章 RAG检索增强生成之文档切割 + + + + +#### 第1集 RAG系统链路构建之文档切割转换 + +**简介: RAG系统链路构建之文档切割转换** + +* 构建RAG系统:**涉及的技术链路环节: 文档加载器->文档转换器->文本嵌入模型->向量存储->检索器** + + * RAG数据流水线示意图 + + ``` + 原始数据 → 数据加载 → 预处理 → 向量化 → 存储 → 检索增强生成 + ↗ ↗ ↗ + PDF 文本清洗 嵌入模型 + 数据库 分块 + 网页 + ``` + + ![data_connection_diagram](D:/学习/笔记/img/data_connection-95ff2033a8faa5f3ba41376c0f6dd32a.jpg) + + * 需求背景,为啥要用? + + * 模型输入限制:GPT-4最大上下文32k tokens,Claude 3最高200k + + * 信息密度不均:关键信息可能分布在长文本的不同位置 + + * 格式兼容性问题:PDF/HTML/代码等不同格式的结构差异 + + + + * 文档转换器(Document Transformers) + + * 文档转换器是 LangChain 处理文档流水线的核心组件,负责对原始文档进行结构化和语义化处理, + + * 为后续的向量化存储、检索增强生成(RAG)等场景提供标准化输入。 + + * 核心任务:文本清洗、分块、元数据增强 + + * 关键操作 + + * **文本分块**:按固定长度或语义分割(防止截断完整句子) + * **去噪处理**:移除特殊字符、乱码、广告内容 + * **元数据注入**:添加来源、时间戳等上下文信息 + + * 效果 + + * **保留语义完整性**:避免因分割导致上下文断裂或信息丢失 + * **适配模型输入限制**:确保分割后的文本块长度符合大语言模型(LLM)的token限制 + * **优化向量化效果**:通过合理分块提升向量表示的语义精度,从而提高检索匹配率 + + | 问题类型 | 原始文档示例 | 转换前问题 | 转换后效果 | + | :--------: | :-----------: | :-----------------------: | :--------------------: | + | 长文本溢出 | 500页法律合同 | 直接输入导致API报错 | 分割为上下文合规的段落 | + | 信息碎片化 | 产品手册PDF | 技术参数分散在不同页面 | 按功能模块重组内容 | + | 噪音污染 | 网页抓取内容 | 包含广告/导航栏等干扰信息 | 提取纯净正文内容 | + | 格式混乱 | 代码仓库文档 | Markdown/代码片段混合 | 分离代码与说明文本 | + + * 基础类和核心参数说明 + + ``` + from langchain_text_splitters import TextSplitter + #源码 + class TextSplitter(BaseDocumentTransformer, ABC): + """Interface for splitting text into chunks.""" + + def __init__( + self, + chunk_size: int = 4000, + chunk_overlap: int = 200, + length_function: Callable[[str], int] = len, + keep_separator: Union[bool, Literal["start", "end"]] = False, + add_start_index: bool = False, + strip_whitespace: bool = True, + ) -> None: + ``` + + * 方法说明 + + * `TextSplitter`本身没有实现`split_text`,要文档分割器按自己的分割策略实现分割 + + * 关键方法调用 `split_documents()->create_documents->()->split_text()` + + * `split_text()`是基础文本分割方法 + * `create_documents()`在`split_text()`基础上封装了元数据绑定逻辑 + * `split_documents()`内部调用`create_documents()`并自动处理元数据传递 + + | 方法 | 输入类型 | 输出类型 | 元数据处理 | 典型使用场景 | + | :----------------------- | :------------------- | :--------------- | :------------------------- | :------------------------------------ | + | **`split_text()`** | **单个字符串** | `List[str]` | ❌ 不保留元数据 | 仅需分割纯文本内容时使用 | + | **`create_documents()`** | **字符串列表** | `List[Document]` | ✅ 需手动传递元数据 | 从原始文本构建带元数据的文档对象 | + | **`split_documents()`** | **Document对象列表** | `List[Document]` | ✅ 自动继承输入文档的元数据 | 分割已加载的文档对象(如PDF解析结果) | + + + + * `chunk_size` + + * 定义:每个文本块的最大长度(字符数或token数),用于控制分割后的文本块大小。 + + * 作用 + + * 防止文本过长超出模型处理限制,影响检索精度。 + * 较小的chunk_size能提高检索细粒度,会导致上下文缺失。 + + * 例子 + + ``` + # 设置chunk_size=100,分割文本为不超过100字符的块 + text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=20) + + #输入文本:"Python是一种解释型语言,适合快速开发。它支持面向对象编程,语法简洁。" + + #分割结果:["Python是一种解释型语言,适合快速开发。", "开发。它支持面向对象编程,语法简洁。"](假设每个块接近100字符) + ``` + + * `chunk_overlap` + + * 定义:相邻文本块之间的重叠字符数,用于保留上下文连贯性。 + + * 作用:避免因分割导致关键信息被切断(如句子中间被截断) + + * 案例一 + + ``` + 如果chunk_size设为1024,chunk_overlap设为128, 对一个长度为2560的文本序列,会切分成3个chunk: + chunk 1: 第1-1024个token + chunk 2: 第897-1920个token (与chunk 1重叠128个) + chunk 3: 第1793-2560个token (与chunk 2重叠128个) + ``` + + * 案例二 + + ``` + # 设置chunk_size=50,chunk_overlap=10 + text = "深度学习需要大量数据和计算资源。卷积神经网络(CNN)在图像处理中表现优异。" + text_splitter = CharacterTextSplitter(chunk_size=50, chunk_overlap=10) + + #分割结果:["深度学习需要大量数据和计算资源。卷积神经", "计算资源。卷积神经网络(CNN)在图像处理中表现优异。"] + + # 重叠部分"计算资源。"确保第二块包含前一块的结尾 + ``` + + * `separators` + + * 定义:分隔符优先级列表,用于递归分割文本。 + + * 作用:优先按自然语义边界(如段落、句子)分割,减少语义断裂。 + + * 例子 + + ``` + # 默认分隔符:["\n\n", "\n", " ", ""] + text_splitter = RecursiveCharacterTextSplitter( + separators=["\n\n", "。", ",", " "] + ) + + #输入文本:"第一段\n\n第二段。第三段,第四段" + + #分割流程:先按\n\n分割为两段,若仍超长则按。继续分割 + ``` + + + + + + + + + + + + + + + + + + + +#### 第2集 字符文档转换器TextSplitter案例实战 + +**简介: 文档转换器CharacterTextSplitter案例实战** + +* `CharacterTextSplitter` 字符分割器 + + * 核心特点 + + * 是 LangChain 中最基础的文本分割器,采用**固定长度字符切割**策略。 + * 适用于结构规整、格式统一的文本处理场景,强调**精确控制块长度** + * 适用于结构清晰的文本(如段落分隔明确的文档)。 + + * 核心参数详解 + + | 参数 | 类型 | 默认值 | 说明 | + | :------------------: | :--: | :------: | :----------------------: | + | `separator` | str | `"\n\n"` | 切割文本的分隔符 | + | `chunk_size` | int | `4000` | 每个块的最大字符数 | + | `chunk_overlap` | int | `200` | 相邻块的重叠字符数 | + | `strip_whitespace` | bool | `True` | 是否清除块首尾空格 | + | `is_separator_regex` | bool | `False` | 是否启用正则表达式分隔符 | + + * 案例代码 + + * 长文本处理 + + ``` + from langchain.text_splitter import CharacterTextSplitter + + text = "是一段 需要被分割的 长文本示例....,每个文本块的最大长度(字符数或token数)Document loaders are designed to load document objects. LangChain has hundreds of integrations with various data sources to load data from: Slack, Notion, Google Drive" + + splitter = CharacterTextSplitter( + separator=" ", + chunk_size=50, + chunk_overlap=10 + ) + + chunks = splitter.split_text(text) + print(len(chunks)) + for chunk in chunks: + print(chunk) + ``` + + * 日志文件处理 + + ``` + from langchain.text_splitter import CharacterTextSplitter + + log_data = """ + [ERROR] 2026-03-15 14:22:35 - Database connection failed + [INFO] 2026-03-15 14:23:10 - Retrying connection... + [WARNING] 2026-03-15 14:23:45 - High memory usage detected + """ + + splitter = CharacterTextSplitter( + separator="\n", + chunk_size=60, + chunk_overlap=20 + ) + log_chunks = splitter.split_text(log_data) + for chunk in log_chunks: + print(chunk) + ``` + +* 优缺点说明 + + | 特性 | 优势 | 局限性 | + | :--------: | :----------------------: | :----------------------------------------: | + | 分割速度 | ⚡️ 极快(O(n)复杂度) | 不考虑语义结构 | + | 内存消耗 | 🟢 极低 | 可能切断完整语义单元, 对语义关联性保持较弱 | + | 配置灵活性 | 🛠️ 支持自定义分隔符和重叠 | 需要预定义有效分隔符 | + | 多语言支持 | 🌍 支持任意字符集文本 | 对表意文字计算可能不准确 | + +* 适合场景 + + * 推荐使用: + - 结构化日志处理 + - 代码文件解析 + - 已知明确分隔符的文本(如Markdown) + - 需要精确控制块大小的场景 + * 不推荐使用: + - 自然语言段落(建议用RecursiveCharacterSplitter) + - 需要保持语义完整性的场景 + - 包含复杂嵌套结构的文本 + + + + + + + + + + + + + + + + + + + +#### 第3集 递归字符文档转换器TextSplitter案例实战 + +**简介: 递归字符文档转换器TextSplitter案例实战** + +* `RecursiveCharacterTextSplitter` 递归字符分割器 + + * 核心特点 + + * **递归字符分割器**采用**多级分隔符优先级切割**机制,是 LangChain 中使用最广泛的通用分割器。 + * 递归尝试多种分隔符(默认顺序:`["\n\n", "\n", " ", ""]`),优先按大粒度分割 + * 若块过大则继续尝试更细粒度分隔符,适合处理结构复杂或嵌套的文本。 + + * 核心参数说明 + + ``` + from langchain.text_splitter import RecursiveCharacterTextSplitter + + splitter = RecursiveCharacterTextSplitter( + chunk_size=1000, # 目标块大小(字符)每个块最多包含 1000 个字符 + chunk_overlap=200, # 块间重叠量,最多有200个字符重叠 + separators=["\n\n", "\n", "。", "?", "!", " ", ""], # 优先级递减的分割符 + length_function=len, # 长度计算函数 + keep_separator=True, # 是否保留分隔符 + ) + ``` + +* 案例实战 + + * 基础案例测试, 处理后chunk之间也有overlap + + ``` + from langchain_text_splitters import RecursiveCharacterTextSplitter + + splitter = RecursiveCharacterTextSplitter( + #separators=["\n\n", "\n", " ", ""], #首先按段落分割,然后按行分割,最后按空格分割,如果都不行则按字符分割 + chunk_size=20, #每个块的最大大小,不超过即可,如果超过则继续调用_split_text分隔(以字符数为单位) + chunk_overlap=4 #块与块之间的重叠部分的大小 + ) + text = "I Love English hello world, how about you? If you're looking to get started with chat models, vector stores, or other LangChain components from a specific provider, check out our supported integrations" + + + chunks = splitter.split_text(text) + + print(len(chunks)) + for chunk in chunks: + print(chunk) + ``` + + + + * 学术论文处理 + + ``` + from langchain.text_splitter import RecursiveCharacterTextSplitter + + paper_text = """ + 引言机器学习近年来取得突破性进展...(长文本)若块过大则继续尝试更细粒度分隔符,适合处理结构复杂或嵌套的文本 + + 方法我们提出新型网络架构...(技术细节)按优先级(如段落、句子、单词)递归分割文本,优先保留自然边界,如换行符、句号 + + 实验在ImageNet数据集上...处理技术文档时,使用chunk_size=800和chunk_overlap=100,数据表格 + """ + + splitter = RecursiveCharacterTextSplitter( + chunk_size=20, + chunk_overlap=4 + ) + paper_chunks = splitter.split_text(paper_text) + print(len(paper_chunks)) + for chunk in paper_chunks: + print(chunk) + + ``` + +* 避坑指南 + + ``` + # 错误示范:不合理的separators顺序 + bad_splitter = RecursiveCharacterTextSplitter( + separators=[" ", "\n"], # 空格优先会导致过早分割 + chunk_size=500 + ) + + # 正确写法:从大结构到小结构 + good_splitter = RecursiveCharacterTextSplitter( + separators=["\n\n", "\n", "。", " ", ""] + ) + ``` + + + +* 核心优势对比 + + | 特性 | CharacterTextSplitter | RecursiveCharacterTextSplitter | + | :------------: | :-------------------: | :----------------------------: | + | 分隔符策略 | 单级固定分隔符 | 多级优先级递归分隔符 | + | 语义保持能力 | ★★☆☆☆ | ★★★★☆ | + | 复杂文本适应性 | 简单结构化文本 | 混合格式/长文本/多语言 | + | 典型应用场景 | 日志/CSV | 论文/邮件/网页/混合代码文 | + +* 推荐场景 + + * 学术论文/技术文档解析 + * 多语言混合内容处理 + * 包含嵌套结构的文本(如Markdown) + * 需要保持段落完整性的问答系统 + + + + + + + + + + + + + + + + + + + +#### 第4集 分割器常见问题和优化最佳实践 + +**简介: 分割器常见问题和优化最佳实践** + +* 其他常见分割器 + + * 递归字符分割(RecursiveCharacterTextSplitter) + + * 原理:按优先级(如段落、句子、单词)递归分割文本,优先保留自然边界(如换行符、句号) + * 适用场景:通用文本处理,尤其适合逻辑紧密的长文档(如论文、技术手册) + + * 固定大小分割(CharacterTextSplitter) + + * 原理:按固定字符数分割,简单但可能打断句子结构。 + * 优化:通过重叠(chunk_overlap)和智能截断(如优先在标点处分隔)减少语义断裂 + * 适用场景:结构松散或句子独立性强的文本(如产品说明书) + + * Token分割(TokenTextSplitter) + + * 原理:基于LLM的token限制分割,避免超出模型输入长度。 + * 优势:更贴近模型处理逻辑(如GPT系列) + + * 结构化文档分割 + + * HTML/Markdown分割器:按标题层级分割,保留元数据(如`HTMLHeaderTextSplitter`) + * 适用场景:网页、技术文档等结构化内容 + * 还有很多,可以查看官方文档拓展(如果不可访问,百度搜索) + * https://python.langchain.com/docs/how_to/ + +* 关键参数`chunk_size、chunk_overlap` + + | 参数 | 作用 | 默认值 | 关键限制条件 | + | :------------------ | :----------------------------------------------------------- | :----- | :------------------------ | + | **`chunk_size`** | 定义每个文本块的最大长度(根据`length_function`计算,默认按字符数) | 1000 | 必须为正整数 | + | **`chunk_overlap`** | 定义相邻块之间的重叠长度 | 20 | 必须小于`chunk_size`的50% | + +* 重叠内容未出现的常见原因 + + * 文本总长度不足:当输入文本长度 ≤ chunk_size时,不会触发分割。 + + ``` + #解释:文本长度远小于chunk_size,不触发分割,无重叠。 + from langchain_text_splitters import CharacterTextSplitter + text = "这是一个非常短的测试文本。" + text_splitter = CharacterTextSplitter( + chunk_size=100, + chunk_overlap=20, + separator="。", # 按句号分割 + length_function=len + ) + + chunks = text_splitter.split_text(text) + print(chunks) + # 输出:['这是一个非常短的测试文本。'] + ``` + + * 递归分割策略:RecursiveCharacterTextSplitter优先保证块大小,可能牺牲重叠。 + + ``` + # 解释:当无法找到分隔符时,按字符数硬分割,强制保留重叠。 + + from langchain_text_splitters import RecursiveCharacterTextSplitter + + text = "这是一段没有标点的超长文本需要被分割成多个块但是因为没有分隔符所以分割器会尝试按字符递归分割直到满足块大小要求" + text_splitter = RecursiveCharacterTextSplitter( + chunk_size=30, + chunk_overlap=10, + separators=["", " "], # 无有效分隔符时按字符分割 + length_function=len + ) + + chunks = text_splitter.split_text(text) + for i, chunk in enumerate(chunks): + print(f"块{i+1}(长度{len(chunk)}): {chunk}") + + # 输出示例: + # 块1(长度30): 这是一段没有标点的超长文本需要被分割成多个块但是因为没有分隔 + # 块2(长度30): 个块但是因为没有分隔符所以分割器会尝试按字符递归分割直到满足 + # 块3(长度15): 字符递归分割直到满足块大小要求 + # 重叠部分:"个块但是因为没有分隔"(10字符) + ``` + + * `enumerate()` + + * 函数是一个内置函数,用于在迭代过程中同时获取元素的索引和值。 + * 它返回一个枚举对象,包含了索引和对应的元素 + + ``` + # enumerate(iterable, start=0) + #参数:iterable:必需,一个可迭代对象,如列表、元组、字符串等。 + #参数:start:可选,指定索引的起始值,默认为 0。 + + fruits = ['apple', 'banana', 'orange'] + for index, fruit in enumerate(fruits): + print(index, fruit) + ``` + + * 分隔符强制分割:在分隔符处切割时,剩余文本不足以形成重叠。 + + ``` + #解释:分隔符优先切割,每个块正好为7个字符,无法形成重叠。 + + from langchain_text_splitters import CharacterTextSplitter + text = "abcdefg.hijkllm.nopqrst.uvwxyz" + text_splitter = CharacterTextSplitter( + chunk_size=7, + chunk_overlap=3, + separator=".", # 按句号分割 + is_separator_regex=False + ) + + chunks = text_splitter.split_text(text) + print("分割块数:", len(chunks)) + for i, chunk in enumerate(chunks): + print(f"块{i+1}: {chunk}") + ``` + +* 参数调优最佳实践 + + * 通用文本处理 + + * 参数建议:`chunk_size=500-1000字符,chunk_overlap=10-20%` + * 案例: + * 处理技术文档时,使用`chunk_size=800和chunk_overlap=100` + * 确保每个块包含完整段落,同时通过重叠保留跨段落的关键术语 + + * 代码分割 + + * 参数建议:根据编程语言特性调整分隔符。 + + * 案例: + + * 分割Python代码时,`RecursiveCharacterTextSplitter.from_language(Language.PYTHON)` + * 会自动识别函数、类等结构,避免打断代码逻辑 + + ``` + from langchain_text_splitters import Language, RecursiveCharacterTextSplitter + python_splitter = RecursiveCharacterTextSplitter.from_language( + language=Language.PYTHON, chunk_size=200, chunk_overlap=50 + ) + ``` + + * 结构化文档(如Markdown) + + * 参数建议:结合标题层级分割。 + + * 案例: + + * 使用MarkdownHeaderTextSplitter按标题分割,保留元数据 + * 输入Markdown内容将按标题层级生成带元数据的块 + + ``` + headers_to_split_on = [("#", "Header 1"), ("##", "Header 2")] + markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on) + ``` + +* 常见问题与解决方案 + + * 过长导致语义模糊 + + * 表现:检索时匹配不精准。 + * 解决:缩小chunk_size,增加chunk_overlap + + * 块过短丢失上下文 + + * 表现:回答缺乏连贯性。 + * 解决:合并相邻块或使用ParentDocumentRetriever,将细粒度块与父文档关联 + + * 参数选择原则: + + * 密集文本(如论文):chunk_size较大(如1000),chunk_overlap约15%。 + + * 松散文本(如对话记录):chunk_size较小(如200),chunk_overlap约20%。 + + * **实验验证:通过AB测试对比不同参数的检索准确率与生成质量** + + + + + + + + + + + +![logo](D:/学习/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"** +**更多高级课程请访问 xdclass.net** + +### 第三十七章 人工智能和高等数学核心基础扫盲 + + + +#### 第1集 扫盲-AI大模型必备之向量-张量和应用场景 + +**简介: 扫盲-AI大模型必备之向量-张量和应用场景** + +* 什么是向量 + + * 向量就是一串有序的数字,像一条带方向的“箭头”, 在机器学习里面尤其重要 + + image-20250313142026018 + + * 世间万物使用计算机表示,用的数字化语言,让计算机能理解复杂事物, 每个数据都包含多种属性 + + * 比如气象数据(包含温度,湿度,风向等等) + * 金融数据(开盘价,收盘价,交易量等等) + * 销售数据(价格,库存量,卖出数量等等) + + * 为了表示多属性的数据,或者称为多维度的数据,向量最为合适。 + + * 向量就是有几个数字横向或者纵向排列而成,每个数字代表一个属性。 + + ``` + // 传统Java数据存储 + String[] names = {"小明", "身高", "体重"}; + Object[] person = {"张三", 175, 68.5}; + + // 向量化表示(特征向量) + float[] vector = {0.83f, 175.0f, 68.5f}; // [性别编码, 身高, 体重] + + # Python列表表示 + vector = [1.2, 3.5, 4.0, 0.8] + ``` + + * 向量能做什么? + + * 计算相似度:比如比较两个人的喜好是否接近、物品是否类似 + * 用户画像:`[年龄=25, 身高=175, 消费=5000]` → 用数字描述一个人 + * 案例:有两个水果的向量(也可以抽取更多属性) + - 苹果:[红色: 0.91, 甜度: 0.83, 圆形: 0.79] + - 草莓:[红色: 0.85, 甜度: 0.75, 圆形: 0.69] + +* 多维向量 + + * 就是维度更多的向量(比如100个数字组成的列表)。 + + * 例子: + + - 词向量:`“猫” = [0.2, -0.3, 0.7, ..., 0.1]`(300个数字表示词义,抽取多点属性,形状、毛发、行为、食物...)。 + + - 图片特征:一张猫的图片转换为 `[0.8, 0.1, 0.05, ...]`(1000个数字描述图片内容) + + - 高维向量表示用户画像 + + ``` + public class UserVector { + // 每个维度代表一个特征 + float[] features = new float[256]; + // 可能包含: + // [0-49]: 兴趣标签权重 + // [50-99]: 行为频率 + // [100-255]: 深度模型提取特征 + } + ``` + + * 为什么维度越多越好? + + - 细节更丰富 , 就像简历写得越详细,越能区分不同的人。 + - 低维:`[年龄=25, 性别=1]` → 只能简单分类。 + - 高维:`[年龄、性别、职业、兴趣1、兴趣2...]` → 精准推荐商品。 + + * 多维向量应用场景 + + - 推荐系统:用用户向量和商品向量计算匹配度。 + - 人脸识别:把照片变成向量,对比找到最相似的人。 + - 数据表示:用户画像(年龄、性别、兴趣等N个特征) + - 几何计算:三维游戏中物体的位置和移动 + + * 通俗理解高维空间 + + - 假设你在三维空间找不到两片不同的树叶,但在100维空间里,每片树叶的位置都会独一无二 + +* 张量 + + * 张量是多维数组的统称,涵盖标量、向量、矩阵及更高维结构 , + + * 阶(Rank)表示维度数: + + * **标量(0阶张量)**:单个数字(如温度25℃)。 + * **向量(1阶张量)**:一维数组(如 `[1, 2, 3]`)。 + * **矩阵(2阶张量)**:二维表格(如Excel表格)。 + * **高阶张量**:三维及以上(如视频数据:时间×宽×高×颜色通道) + + | 阶数 | 数学名称 | 典型应用 | Java存储结构 | + | :--: | :------: | :-------: | :----------: | + | 0 | 标量 | 温度值 | float | + | 1 | 向量 | 用户画像 | float[] | + | 2 | 矩阵 | Excel表格 | float[][] | + | 3 | 张量 | 彩色图片 | float[][][] | + + | **数据** | **张量形状** | **解释** | + | :----------: | :------------------: | :-------------------------: | + | 一张黑白图片 | `(28, 28)` | 28行×28列的像素矩阵 | + | 一个视频片段 | `(100, 128, 128, 3)` | 100帧,每帧128x128的RGB图片 | + | 一批用户数据 | `(500, 10)` | 500个用户,每人10个特征 | + +* 常见问题 + + * 为什么要用张量而不是数组? + + - 张量是AI框架(如PyTorch)的“通用语言”,能自动支持GPU加速和梯度计算。 + + * 维度太多会不会算不过来? + + - 会!所以需要GPU和优化算法(比如深度学习)。 + + * 我该用几维向量 + + * 看任务需求:简单任务用几十维,复杂任务(如ChatGPT)可能用上千维! + + + +* 总结:一句话记住它们! + + * 向量 → 一串数字(像GPS坐标)。 + * 多维向量 → 更长的数字串(像详细简历)。 + * 张量 → 数字的“集装箱”(像Excel表格、图片集、视频流)。 + + + + + + + + + + + +#### 第2集 扫盲-高等数学里面的求和-点积公式讲解 + +**简介: 高等数学里面的求和-点积公式讲解** + +* **什么是求和符号?** + + * 求和符号Σ(希腊字母sigma)表示**连续加法运算**,用于简化多个数的累加书写。 + + * 英文意思为Sum,Summation,汉语意思为“和”“总和”。 + + * 基本形式 + + image-20250313151603856 + + ![image-20250313151918579](D:/学习/笔记/img/image-20250313151918579.png) + + * **单变量求和示例** + + * 计算1到5的整数和: + + image-20250313151657094 + + * 计算前3个偶数的平方和: + + image-20250313151724171 + + + +* 点积(内积)的定义与计算 + + * 点积是什么 + + * 点积是**两个向量对应分量相乘后求和**的运算,结果是一个标量(数值)。 + + image-20250313161943645 + + * 几何意义 + + * 点积反映两个向量的夹角关系 + * 通过在空间中引入笛卡尔坐标系,向量之间的点积既可以由向量坐标的代数运算得出 + + image-20250313162742811 + + * 点积计算示例 + + image-20250313162827278 + + + + + + + + + + + + + +#### 第3集 扫盲-向量的相似度计算之余弦相似度 + +**简介: 扫盲-向量的相似度计算之余弦相似度** + +* 补充:三角函数 + + * **正弦(sinθ)**:对边长度与斜边长度的比值。 + * **余弦(cosθ)**:邻边长度与斜边长度的比值(即夹角的两边) + + image-20250313164754696 + +* 为啥要学余弦相似度? + + * 生活中的相似度问题,假设你需要完成以下任务: + * 网购时:找出和你刚买的衣服最搭配的裤子 + * 听歌时:发现与当前播放歌曲风格相似的曲目 + * 点外卖时:找到和你常点菜品口味接近的新店铺 + * **LLM大模型的RAG原理:用户输入自然语言query,找到最相关的文档** + * 这些场景的共同点:需要量化两个事物的相似程度,向量空间中的"方向感" + + image-20250313170154488 + + + +* 什么是余弦相似度? + + * 基础定义:余弦相似度(Cosine Similarity)用于衡量两个向量在方向上的相似程度,忽略其绝对长度 + + * **值的范围为[-1,1],-1为完全不相似,1为完全相似。** + + * 公式(向量的模长等于其各分量平方和的平方根) + + ![image-20250315152627015](D:/学习/笔记/img/image-20250315152627015.png) + + image-20250313170520819 + + * **直观理解** + + - 几何意义:两个向量的夹角越小,方向越一致,余弦值越接近1。 + - 1 → 完全相同方向 + - 0 → 完全无关(正交) + - -1 → 完全相反方向 + - 两条线段之间形成一个夹角, + - 如果夹角为0度,意味着方向相同、线段重合,这是表示两个向量代表的文本完全相等; + - 如果夹角为90度,意味着形成直角,方向完全不相似; + - 如果夹角为180度,意味着方向正好相反。 + - **因此可以通过夹角的大小,来判断向量的相似程度,夹角越小,就代表越相似。** + + * **为何选择余弦相似度?** + + - **高维数据友好**:适用于文本、图像等高维稀疏数据。 + - **长度不变性**:只关注方向,忽略向量长度差异(如文档长短)。 + - **计算高效**:适合大规模向量检索 + +* 案例 + + * 以二维向量为例:向量A = [3, 4],向量B = [1, 2] + * 结论:两个向量方向高度相似! + + image-20250313170904153 + +* 大模型中的核心应用场景 + + * 语义搜索(Semantic Search) + * 问题:用户输入自然语言query,找到最相关的文档。 + * 实现步骤: + * 将query和所有文档编码为向量。 + * 计算query向量与文档向量的余弦相似度。 + * 返回Top-K相似度最高的文档。 + * 推荐系统(User-Item Matching) + * 原理:用户向量和物品向量的余弦相似度 → 推荐得分。 + * 示例: + * 用户向量:[0.3, 0.5, -0.2](表示对科技、体育、艺术的兴趣) + * 物品向量:[0.4, 0.1, 0.0](科技类文章) + * 相似度 ≈ 0.3×0.4 + 0.5×0.1 = 0.17 → 推荐该文章。 + + + +* 为什么重要:掌握余弦相似度,就是掌握了**连接数据与智能的钥匙**! + +![1](D:/学习/笔记/img/1-1859494.png) + + + + + + + + + + + + + +#### 第4集 科学计算核心库NumPy和推荐系统案例 + +**简介: 科学计算核心库NumPy和推荐系统案例** + +* 什么是 NumPy? + + * NumPy(Numerical Python)是 Python 的科学计算核心库,专门处理多维数组(矩阵)的高效运算。 + * 核心功能:提供高性能的数组对象 ndarray,支持向量化操作(无需循环直接计算)。 + * 江湖地位:几乎所有 Python 科学计算库(如 Pandas、SciPy、TensorFlow)都依赖 NumPy。 + * 设计目标:用简洁的语法替代传统循环,提升大规模数据计算效率。 + +* 为什么需要 NumPy?对比原生 Python + + * 原生 Python 列表的痛点 + + ``` + # 计算两个列表元素相加 + a = [1, 2, 3] + b = [4, 5, 6] + result = [a[i] + b[i] for i in range(len(a))] # 需要循环逐个计算 + ``` + + * NumPy 的解决方案 + + ``` + import numpy as np + a = np.array([1, 2, 3]) + b = np.array([4, 5, 6]) + result = a + b # 直接向量化运算 → 输出 [5,7,9] + ``` + + * 性能对比(关键优势) + + | 操作类型 | Python 列表耗时 | NumPy 耗时 | 速度提升倍数 | + | :------------ | :--------------- | :--------- | :----------- | + | 100万元素求和 | 15 ms | 0.5 ms | **30倍** | + | 矩阵乘法 | 手动循环实现复杂 | 单行代码 | **100倍+** | + +* 基础安装 + + ``` + # 常规安装 + pip install numpy + + # 验证安装, 应输出版本号 + python -c "import numpy as np; print(np.__version__)" + + # 升级版本 + pip install numpy --upgrade + ``` + +* 余弦相似度案例实战 + + * 案例1:基础向量计算 + + ``` + import numpy as np + + def cos_sim(v1, v2): + """ + 计算两个向量的余弦相似度 + 余弦相似度用于衡量两个向量方向的相似性,结果范围从-1到1 + -1表示完全相反,1表示完全相同,0表示两者正交(即无相似性) + + 参数: + v1: numpy数组,第一个向量 + v2: numpy数组,第二个向量 + + 返回: + 两个向量的余弦相似度 + """ + # 计算向量点积 + dot = np.dot(v1, v2) + # 计算向量的模的乘积 + norm = np.linalg.norm(v1) * np.linalg.norm(v2) + # 返回余弦相似度 + return dot / norm + + # 测试向量(支持任意维度) + vec_a = np.array([0.2, 0.5, 0.8]) # 文本A的嵌入向量 + vec_b = np.array([0.3, 0.6, 0.7]) # 文本B的嵌入向量 + # 输出两向量的余弦相似度 + print(f"相似度:{cos_sim(vec_a, vec_b):.4f}") # 输出:0.9943 + ``` + + * 案例2:推荐系统 + + ``` + import numpy as np + + def cosine_similarity(a, b): + # 将列表转换为NumPy数组 + a = np.array(a) + b = np.array(b) + # 计算点积 + dot_product = np.dot(a, b) + # 计算模长 + norm_a = np.linalg.norm(a) + norm_b = np.linalg.norm(b) + # 计算余弦相似度 + return dot_product / (norm_a * norm_b) if norm_a * norm_b != 0 else 0 + + # 用户嵌入向量(根据浏览行为生成) + user_embedding = [0.7, -0.2, 0.5, 0.1] + + # 商品嵌入库 + products = { + "item1": [0.6, -0.3, 0.5, 0.2], + "item2": [0.8, 0.1, 0.4, -0.1], + "item3": [-0.5, 0.7, 0.2, 0.3] + } + + # 计算相似度并推荐 + recommendations = [] + for item_id, vec in products.items(): + sim = cosine_similarity(user_embedding, vec) + recommendations.append((item_id, round(sim, 3))) # 保留3位小数 + + # 按相似度降序排序 + recommendations.sort(key=lambda x: x[1], reverse=True) + print("推荐排序:", recommendations) + ``` + + + + + + + + + + + + + + + + + + + +![logo](D:/学习/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"** +**更多高级课程请访问 xdclass.net** + +### 第三十八章 RAG系统必备之嵌入大模型Embedding + + + +#### 第1集 嵌入大模型Embedding和LLM大模型对比 + +**简介: 嵌入大模型Embedding和LLM大模型对比** + +* 什么是文本嵌入Text Embedding + + * 文本嵌入(Text Embedding)是将文本(单词、短语、句子或文档)映射到高维向量空间的技术。 + + * 类比 + + * 假设你是一个Java工程师,现在需要将一段文字(比如用户评论)存入数据库。 + * 传统方式可能是存字符串,但计算机无法直接“理解”语义。 + * Embedding的作用 + * 把文字转换成一个固定长度的数字数组(向量),比如 `[0.2, -0.5, 0.8, ...]`,这个数组能“编码”文字的含义。 + * 想象每个词或句子是一个点,而Embedding就是给这些点在地图上标坐标。 + * 语义相近的词(如“猫”和“狗”)坐标距离近,无关的词(如“猫”和“汽车”)坐标距离远。 + + * **通俗来说** + + * 将不可计算、非结构化 的词 转化为 可计算,结构化的 向量,把文字变成一串有含义的数字密码 + * 就像给每个句子/词语颁发"数字身份证"。这个身份证号码能表达文字的**核心含义** + * 例:"猫" → [0.2, -0.5, 0.8,...](300个数字组成的向量) + + * 核心特点 + + * **语义感知**:相似的文字数字也相似 + + ``` + # "狗"和"犬"的嵌入向量距离近 + # "苹果"(水果)和"苹果"(手机)的嵌入距离远 + ``` + + * 降维表示:将离散的文本转化为连续的向量 + + * 维度固定:无论输入多长,同个嵌入大模型 输出数字个数相同(如384/768维) + + * 案例【采用二维方式,正常的向量化后都是上千个维度】 + + * 句子 + + * 句子1:老王喜欢吃香蕉 + * 句子2:冰冰喜欢吃苹果 + * 句子3:老帆在打篮球 + + * 向量化后续图形表示【二维】 + + * 句子1和2相近,因为维度大体相同 + + image-20250313210027704 + +* 应用场景 + + * 语义搜索: 找含义相近的内容,不依赖关键词匹配 + + ``` + # 搜索"如何养小猫咪" → 匹配到"幼猫护理指南" + ``` + + * 智能分类:自动识别用户评论的情绪/类型 + + * 问答系统:快速找到与问题最相关的答案段落 + +* 什么是嵌入Embedding大模型 + + * Embedding 模型的主要任务是将文本转换为数值向量表示 + * 这些向量可以用于计算文本之间的相似度、进行信息检索和聚类分析 + * 文本嵌入的整体链路 `原始文本 → Embedding模型 → 数值向量 → 存储/比较` + +![image-20250313212722723](D:/学习/笔记/img/image-20250313212722723.png) + +* LLM 大模型 vs Embedding 大模型 + +| 维度 | LLM (如GPT-4、Claude) | Embedding 模型 (如BERT、text-embedding-3) | +| :----------: | :------------------------: | :---------------------------------------: | +| **核心能力** | 理解并生成人类语言 | 将文本转化为数学向量 | +| **输出形式** | 自然文本(对话/文章/代码) | 数值向量(如1536维浮点数) | +| **典型交互** | 多轮对话、持续创作 | 单次转换、批量处理 | + +* 关键联系 + + * 数据基础相同: + * 都通过海量文本训练,理解语言规律 + * 就像作家和图书管理员都读过很多书 + + * 协作关系 + * 常组合使用,Embedding快速筛选相关信息,LLM精细加工生成最终结果 + * 就像先让图书管理员找到资料,再让作家整理成报告 + +* 常见误区 + + * ❌ 误区1:"Embedding是简化版LLM"→ 其实它们是不同工种,就像厨师和营养师的关系 + * ❌ 误区2:"LLM可以直接做Embedding的事"→ 虽然理论上可以,但就像用跑车送外卖——又贵又慢 + * ❌ 误区3:"Embedding模型不需要训练"→ 好的Embedding也需要大量训练,就像图书管理员需要学习分类方法 + +* 一句话总结 + + * LLM是内容生产者,Embedding是内容组织者 + * 它们就像餐厅里的厨师和配菜员,一个负责烹饪(生成内容),一个负责准备食材(组织信息) + +* 组合应用场景 + + * 场景1:智能客服系统 + * Embedding:把用户问题"我的订单怎么还没到?"转换成向量,快速匹配知识库中相似问题 + * LLM:根据匹配到的问题模板,生成具体回答:"您的订单已发货,预计明天送达" + * 场景2:论文查重 + * Embedding:把论文段落转为向量,计算与数据库的相似度 + * LLM:若发现高相似度,自动改写重复段落并给出修改建议 + + + + + + + + + + + + + + + + + + + + + + + +#### 第2集 LangChain框架文本嵌入Embedding实战 + +**简介: LangChain框架文本嵌入Embedding实战** + +* LangChain框架中的Embedding + + * 通过标准化接口集成了多种嵌入模型,支持开发者灵活调用 + + * **功能**:对接各类文本嵌入模型的标准化接口 + + * **作用**:将文本转换为向量,供后续检索/比较使用 + + * **类比**:不同品牌手机充电器 → LangChain是万能充电头 + + * 源码查看 + + ``` + from langchain.embeddings import OpenAIEmbeddings + + from abc import ABC, abstractmethod + from langchain_core.runnables.config import run_in_executor + class Embeddings(ABC): + @abstractmethod + def embed_documents(self, texts: list[str]) -> list[list[float]]: + """Embed search docs. + Args: + texts: List of text to embed. + Returns: + List of embeddings. + """ + @abstractmethod + def embed_query(self, text: str) -> list[float]: + """Embed query text. + Args: + text: Text to embed. + Returns: + Embedding. + """ + ``` + + * 支持的嵌入模型类型【不同嵌入模型维度和精度不一样】 + + | 类型 | 代表模型 | 特点 | + | :------------: | :-----------------------------: | :--------------------: | + | 云端API | OpenAI, Cohere, HuggingFace Hub | 无需本地资源,按量付费 | + | 本地开源模型 | Sentence-Transformers, FastText | 数据隐私高,可离线运行 | + | 自定义微调模型 | 用户自行训练的模型 | 领域适配性强 | + + * 核心API与属性 + + | 方法 | 作用 | 示例场景 | + | :----------------------: | :----------------------------: | :----------------: | + | `embed_query(text)` | 对单个文本生成向量 | 用户提问的实时嵌入 | + | `embed_documents(texts)` | 批量处理多个文本,返回向量列表 | 处理数据库文档 | + | `max_retries` | 失败请求重试次数 | 应对API不稳定 | + | `request_timeout` | 单次请求超时时间(秒) | 控制长文本处理时间 | + + + +* 案例实战 + + * 在线嵌入模型使用,也可以使用其他的厂商 + * 地址:https://bailian.console.aliyun.com/ + + ![image-20250313215608473](D:/学习/笔记/img/image-20250313215608473.png) + + ``` + from langchain_community.embeddings import DashScopeEmbeddings + + # 初始化模型 + ali_embeddings = DashScopeEmbeddings( + model="text-embedding-v2", # 第二代通用模型 + max_retries=3, + dashscope_api_key="sk-005c3c25f6d042848b29d75f2f020f08" + ) + + # 分析商品评论情感倾向 + comments = [ + "衣服质量很好,但是物流太慢了", + "性价比超高,会回购!", + "尺寸偏小,建议买大一号" + ] + + # 生成嵌入向量 + embeddings = ali_embeddings.embed_documents(comments) + print(embeddings) + print(len(embeddings)) # 5 + print(len(embeddings[0])) # 1536 + ``` + + + + + + + + + + + + + + + +#### 第3集 本地私有化部署嵌入大模型Embedding实战 + +**简介: 本地私有化部署嵌入模型Embedding实战** + +* 为什么要本地部署嵌入大模型 + + * **对比云端风险**:第三方API可能造成数据泄露(如某云服务商API密钥泄露事件) + + | 需求类型 | 典型场景 | 案例说明 | + | :------------: | :----------------: | :------------------------------: | + | **数据安全** | 政府/金融/医疗行业 | 医院病历分析需符合《数据安全法》 | + | **定制化需求** | 垂直领域术语适配 | 法律文书嵌入需理解专业法条词汇 | + | **成本控制** | 长期高频使用场景 | 电商评论分析每日百万次调用 | + | **网络限制** | 内网隔离环境 | 军工企业研发内部知识库 | + + * 本地部署嵌入大模型数据闭环 + + ``` + 用户数据 → 企业内网服务器 → 本地模型处理 → 结果存于本地数据库 + ``` + + ![data_connection_diagram](D:/学习/笔记/img/data_connection-95ff2033a8faa5f3ba41376c0f6dd32a.jpg) + +* 部署实战 + + * 使用ollama下载嵌入大模型 + + * 地址:https://ollama.com/search?c=embed + + ``` + #下载嵌入模型 + ollama run mofanke/acge_text_embedding + + # 后台启动服务(默认端口11434) + ollama serve & + + #查看运行的模型 + ollama ps + ``` + + * 嵌入模型请求测试 + + ``` + curl http://localhost:11434/api/embeddings -d '{"model": "mofanke/acge_text_embedding", "prompt": "小滴课堂AI大模型课程"}' + ``` + + * 编码实战 + + * 由于 LangChain 0.3.x 尚未原生支持 Ollama 嵌入模型,需自定义接口类 + * `pip install requests` + + ``` + from typing import List, Optional + from langchain.embeddings.base import Embeddings + import requests + + class OllamaEmbeddings(Embeddings): + def __init__(self, model: str = "llama2", base_url: str = "http://localhost:11434"): + self.model = model + self.base_url = base_url + + def _embed(self, text: str) -> List[float]: + try: + response = requests.post( + f"{self.base_url}/api/embeddings", + json={ + "model": self.model, + "prompt": text # 注意:某些模型可能需要调整参数名(如"prompt"或"text") + } + ) + response.raise_for_status() + return response.json().get("embedding", []) + except Exception as e: + raise ValueError(f"Ollama embedding error: {str(e)}") + + def embed_query(self, text: str) -> List[float]: + return self._embed(text) + + def embed_documents(self, texts: List[str]) -> List[List[float]]: + return [self._embed(text) for text in texts] + ``` + + * 使用自定义嵌入模型处理文档 + + ``` + embeddings = OllamaEmbeddings( + model="mofanke/acge_text_embedding", + base_url="http://localhost:11434" + ) + + + # 分析商品评论情感倾向 + comments = [ + "衣服质量很好,但是物流太慢了", + "性价比超高,会回购!", + "尺寸偏小,建议买大一号" + ] + + # 生成嵌入向量 + embeddings = embeddings.embed_documents(comments) + print(embeddings) + print(len(embeddings)) # 3 + print(len(embeddings[0])) # 1024 + ``` + + + + + + + + + + + + + + + + + +#### 第4集 【面试题】RAG系统构建之嵌入模型性能优化 + +**简介: RAG系统构建之嵌入模型性能优化** + +* 需求背景(面试高频题目) + + * 嵌入计算的痛点 + * 嵌入生成成本高:每次调用模型API都需要计算资源 + * 重复计算浪费:相同文本多次生成嵌入浪费资源 + * API调用限制:商业API有调用次数限制和成本 + * 响应速度瓶颈:实时场景需要快速响应 + * 解决方案:缓存 + * 降低计算成本:相同文本只需计算一次 + * 提升响应速度:缓存读取比模型计算快10-100倍 + * 突破API限制:本地缓存不受远程API配额限制 + * 支持离线场景:网络不可用时仍能获取历史嵌入 + +* `CacheBackedEmbeddings` 介绍 + + * 技术架构图 + + ``` + [应用程序] → 检查缓存 → 命中 → 返回缓存嵌入 + ↓ + 未命中 → 调用模型 → 存储结果 → 返回新嵌入 + ``` + + * 是LangChain提供的缓存装饰器,支持 本地文件系统、Redis、数据库等存储方式,自动哈希文本生成唯一缓存键 + + ``` + from langchain.embeddings import CacheBackedEmbeddings + ``` + + * 核心语法与参数 + + ``` + from langchain.storage import LocalFileStore + from langchain.embeddings import CacheBackedEmbeddings + + # 初始化 + cache = CacheBackedEmbeddings( + underlying_embeddings=embedding_model, # 原始嵌入模型 + document_embedding_store=storage, # 缓存存储对象 + namespace="custom_namespace" # 可选命名空间(隔离不同项目) + ) + ``` + + | 参数 | 类型 | 作用 | + | :------------------------: | :--------: | :--------------------------------: | + | `underlying_embeddings` | Embeddings | 原始嵌入模型(如OpenAIEmbeddings) | + | `document_embedding_store` | BaseStore | 缓存存储实现类(如LocalFileStore) | + | `namespace` | str | 缓存命名空间(避免键冲突) | + + * 存储支持多类型 + + ``` + #这个包里面 + from langchain.storage + + __all__ = [ + "create_kv_docstore", + "create_lc_store", + "EncoderBackedStore", + "InMemoryByteStore", + "InMemoryStore", + "InvalidKeyException", + "LocalFileStore", + "RedisStore", + "UpstashRedisByteStore", + "UpstashRedisStore", + ] + ``` + +* 应用案例:智能客服知识库加速 + + * 场景需求 + + * 知识库包含10万条QA对 + * 每次用户提问需要检索最相关答案 + * 传统方式每次请求都要计算所有问题嵌入 + + * 缓存方案 + + * 首次加载时全量预计算并缓存 + * 后续请求直接读取缓存 + * 新增问题时动态更新缓存 + + * 代码案例 + + * 基础版本(无缓存) + + ``` + from langchain.embeddings import OpenAIEmbeddings + + # 初始化模型 + embedder = OpenAIEmbeddings(openai_api_key="sk-xxx") + + # 生成嵌入 + vector = embedder.embed_documents("如何重置密码?") + print(f"向量维度:{len(vector)}") + ``` + + * 带缓存版本 + + ``` + from langchain.storage import LocalFileStore + from langchain.embeddings import CacheBackedEmbeddings + + # 创建文件缓存 + fs = LocalFileStore("./embedding_cache/") + + # 组合缓存与模型(Java的装饰器模式模式类似) + cached_embedder = CacheBackedEmbeddings.from_bytes_store( + underlying_embeddings=embedder, + document_embedding_store=fs, + namespace="openai-embeddings" # 区分不同模型版本 + ) + + # 首次调用(写入缓存) + vector1 = cached_embedder.embed_documents("如何重置密码?") + + # 二次调用(读取缓存) + vector2 = cached_embedder.embed_documents("如何重置密码?") + + print(f"结果一致性:{vector1 == vector2}") # 输出True + ``` + + * 高级配置示例(分布式案例-存储Redis) + + ``` + # 带TTL的Redis缓存 + from redis import Redis + from langchain.storage import RedisStore + + redis_client = Redis(host="localhost", port=6379) + redis_store = RedisStore(redis_client, ttl=86400) # 24小时过期 + + cached_embedder = CacheBackedEmbeddings.from_bytes_store( + underlying_embeddings=embedder, + document_embedding_store=redis_store, + namespace="openai-v3" + ) + ``` + + + + + + + + + + + + + + + + + + + +#### 第5集 嵌入大模型CacheBackedEmbeddings案例实战 + +**简介: 嵌入模型CacheBackedEmbeddings案例实战** + +* **案例实战:对比嵌入大模型使用缓存前后性能区别** + +* 注意:API设计区别 + + * `embed_documents`:面向批量文档预处理(适合缓存) + + * `embed_query`:面向实时查询处理(默认不缓存) + + * 设计考量因素 + + | 维度 | `embed_documents` | `embed_query` | + | :------: | :--------------------------------: | :------------------------------: | + | 使用场景 | 预处理大量重复文档(如知识库构建) | 实时响应用户查询 | + | 数据特征 | 高重复率(法律条款、产品描述) | 低重复率(用户提问多样化) | + | 性能损耗 | 批量处理可分摊缓存读写开销 | 单次查询增加延迟(缓存命中率低) | + | 存储效率 | 缓存大量重复文本收益显著 | 缓存大量唯一查询浪费资源 | + +* 编码实战 + + ``` + from langchain.embeddings import CacheBackedEmbeddings,DashScopeEmbeddings + import time + # 初始化模型和缓存 + from langchain.storage import LocalFileStore + + # 初始化模型 + embedding_model = DashScopeEmbeddings( + model="text-embedding-v2", # 第二代通用模型 + max_retries=3, + dashscope_api_key="sk-005c3c25f6d042848b29d75f2f020f08" + ) + + storage = LocalFileStore("./embedding_cache/") # 本地缓存目录 + + cached_embeddings = CacheBackedEmbeddings.from_bytes_store( + embedding_model, + storage, + namespace="openai_emb" # 命名空间隔离不同模型 + ) + + texts = ["小滴课堂AI大模型开发实战", "小滴课堂AI大模型开发实战"] # 故意重复 + + start_time = time.time() + # 第一次调用(写入缓存) + emb1 = cached_embeddings.embed_documents(texts) + print(f"首次调用嵌入维度: {len(emb1[0])}") + embedded1_end_time = time.time() + print(f"首次调用耗时: {embedded1_end_time - start_time}") + + # 第二次调用(读取缓存) + emb2 = cached_embeddings.embed_documents(texts) + print(f"二次调用结果相等: {emb1 == emb2}") + embedded2_end_time = time.time() + print(f"二次调用耗时: {embedded2_end_time - embedded1_end_time}") + + ``` + +* 最佳实践建议 + + * **适用场景** + + - 处理大量重复文本(如商品描述、法律条款) + - 需要控制API调用成本的商业应用 + - 本地模型加速重复计算 + + * **存储选择策略** + + | 存储类型 | 优点 | 缺点 | 适用场景 | + | :------------: | :----------------: | :-----------: | :-----------: | + | LocalFileStore | 零配置、易调试 | 不适合分布式 | 本地开发/测试 | + | RedisStore | 高性能、支持分布式 | 需要运维Redis | 生产环境集群 | + | InMemoryStore | 最快速度 | 重启丢失数据 | 临时测试 | + + + + + + + + + + + + + + + + + + + +![logo](D:/学习/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"** +**更多高级课程请访问 xdclass.net** + +### 第三十九章 大模型必备技术Milvus向量数据库 + + + +#### 第1集 向量数据库介绍和技术选型思考 + +**简介: 向量数据库介绍和技术选型思考** + +* 为什么要用向量数据库,不能用MySQL存储? + + * 文档块通过嵌入模型处理后得到对应向量,下一步就是将向量存储到数据库中,方便后续进行检索使用 + + ![data_connection_diagram](D:/学习/笔记/img/data_connection-95ff2033a8faa5f3ba41376c0f6dd32a.jpg) + + * 传统数据库的局限性 + + * **维度灾难**:传统索引(B-Tree/Hash)在100+维度时效率断崖式下降,无法高效处理高维向量(常达768-1536维) + * **相似度计算**:无法高效处理余弦相似度/Euclidean距离等复杂运算 + * **实时性要求**:亿级向量场景下传统方案响应延迟高达秒级,暴力搜索时间复杂度O(N) + + ``` + // 传统关系型数据库查询(精确匹配) + SELECT * FROM products WHERE category = 'electronics'; + + // 向量数据库查询(相似度匹配) + Find top5 similar_products where description ≈ '高性能游戏本' + ``` + + * 向量数据库的核心能力 + + * **相似性搜索**:余弦相似度/欧式距离 + * **混合查询**:向量搜索 + 传统条件过滤 + * **动态扩展**:支持实时数据更新 + * **高效存储**:压缩向量存储技术 + + * 向量数据库典型应用场景 + + | 场景 | 案例 | 核心需求 | + | :--------: | :-------------: | :------------: | + | 推荐系统 | 电商商品推荐 | 高并发低延迟 | + | 语义搜索 | 法律条文检索 | 高精度召回 | + | AI代理记忆 | GPT长期记忆存储 | 快速上下文检索 | + | 图像检索 | 以图搜图系统 | 多模态支持 | + +* 主流的向量数据库介绍 + + * 开源向量数据库 + + * **Milvus** + - **核心优势**:分布式架构支持千亿级向量规模,QPS超百万级,提供HNSW、IVF-PQ等多样化索引算法,支持高并发场景如金融风控、生物医药分子库检索。 + - **优点**:高扩展性、多租户支持、完整的API生态(Python/Java/Go等)。 + - **缺点**:部署复杂度高,运维成本较大,适合有专业团队的企业。 + * **Qdrant** + - **核心优势**:基于Rust开发,支持稀疏向量检索(速度提升16倍),内置标量量化和产品量化技术优化存储效率,适合电商推荐、广告投放等高并发场景。 + - **优点**:高性能过滤、云原生设计,支持地理空间和混合数据类型。 + - **缺点**:社区生态较新,文档和案例相对较少。 + + * 云原生服务 + + * **Pinecone** + - **核心优势**:全托管服务,实时数据更新延迟低于100ms,支持Serverless计费(按查询付费),适合SaaS快速集成和中小型企业。 + - **优点**:零运维、低延迟、无缝集成LangChain生态。 + - **缺点**:成本较高,大规模数据场景费用显著。 + * **腾讯云VectorDB** + - **核心优势**:国产化方案,单索引支持千亿向量,集成AI套件实现文档自动向量化,适合政务、金融等数据主权敏感场景。 + - **优点**:端到端RAG解决方案,与腾讯云生态深度整合。 + - **缺点**:依赖腾讯云生态,跨云部署受限。 + + * 轻量级工具 + + * **Chroma** + - **核心优势**:没有深厚数据库背景的开发者也能快速上手,5分钟完成单机部署,适合学术研究、初创团队验证。 + - **优点**:开发友好、快速原型验证,支持LangChain集成。 + - **缺点**:不适合生产级大规模应用,功能有限。 + * **Faiss** + - **核心优势**:Meta开源的GPU加速检索库,百万级向量查询延迟低于10ms,常作为其他数据库的底层引擎。 + - **优点**:性能标杆、算法丰富,支持混合检索架构。 + - **缺点**:无托管服务,需自行处理分布式与高可用性。 + + * 传统数据库扩展 + + * **MongoDB Atlas** + + - **核心优势**:文档数据库嵌入向量索引,支持每个文档存储16MB向量数据,适合已有MongoDB基础设施的企业智能化升级。 + - **优点**:事务处理与向量检索一体化,兼容现有业务逻辑。 + - **缺点**:向量检索性能弱于专用数据库,扩展性受限。 + + * 其他:PostgreSQL、ElasticSearch等 + + + + * 综合选型对比 + + | 维度\产品 | Pinecone | Milvus | Qdrant | Chroma | + | :----------: | :--------: | :-----------: | :-----------: | :------: | + | **部署模式** | 全托管 | 自建/云 | 自建/云 | 嵌入式 | + | **学习曲线** | 简单 | 复杂 | 中等 | 极简 | + | **扩展能力** | 自动扩展 | 手动分片 | 自动分片 | 单机 | + | **典型场景** | 生产级SaaS | 企业私有云 | 高性能需求 | 本地开发 | + | **成本模型** | 按用量付费 | 基础设施+运维 | 基础设施+运维 | 免费 | + +* 技术选型建议 + + * **数据规模** + + - **十亿级以上**:选择Milvus、腾讯云VectorDB等分布式方案。 + + - **百万级以下**:轻量级工具如Chroma或Faiss。 + + * **部署复杂度** + - **云服务优先**:中小企业选Pinecone或腾讯云VectorDB,省去运维成本。 + - **私有化部署**:大型企业选Milvus、Qdrant,需专业团队支持。 + * **成本考量** + - **开源方案**:Milvus、Qdrant适合长期可控成本。 + - **按需付费**:小规模试用选Pinecone Serverless。 + * **生态兼容性** + - **国产化需求**:腾讯云VectorDB或华为云等国产方案。 + - **现有技术栈**:PostgreSQL/MongoDB扩展适合渐进式改造。 + + + +* **总结(花钱的多数更轻松方便)** + + * 向量数据库的选择需结合数据规模、业务场景、团队能力和成本预算综合评估。 + * 对于AI驱动的应用(如RAG、多模态搜索),建议优先考虑云原生或分布式开源方案(如Milvus、Pinecone); + * 传统业务系统可尝试数据库扩展插件以降低迁移成本,具体案例可参考各数据库的官方文档 + + + + + + + + + + + + + +#### 第2集 向量数据库Milvus介绍和架构讲解 + +**简介: 向量数据库Milvus介绍和和架构讲解** + +* 什么是Milvus向量数据库 + + * 地址:https://milvus.io/ + * 一种高性能、高扩展性的向量数据库,可在从笔记本电脑到大规模分布式系统等各种环境中高效运行。 + * 可以开源软件的形式提供,也可以云服务的形式提供 + * 核心能力:高性能、可扩展、低延迟,支持多种相似度计算方式(如欧氏距离、余弦相似度)。 + + | 维度 | 指标/能力 | + | :--------: | :-----------------------------------------: | + | 数据规模 | 支持**千亿级向量**,PB级存储 | + | 查询性能 | 亿级向量**亚秒级响应**(GPU加速) | + | 扩展性 | 水平扩展,支持**动态增删节点** | + | 查询类型 | 相似度搜索、混合查询、多向量联合查询 | + | 生态兼容性 | 支持Python/Java/Go/REST API,整合主流AI框架 | + + * 适用场景:推荐系统、图像检索、自然语言处理(NLP)等 + * 全球大厂使用者 + + ![Milvus Adopters](D:/学习/笔记/img/milvus-adopters.png) + +* 支持部署的架构 + + * Milvus提供多种部署选项,包括本地部署、Docker、Kubernetes on-premises、云SaaS和面向企业的自带云(BYOC) + * Milvus Lite 是一个 Python 库,轻量级版本,适合在 Jupyter Notebooks 中进行快速原型开发,或在资源有限的边缘设备上运行 + * Milvus Standalone 是单机服务器部署,所有组件都捆绑在一个 Docker 镜像中,方便部署 + * Milvus Distributed 可部署在 K8S 集群上,采用云原生架构,适合十亿规模甚至更大的场景,该架构可确保关键组件的冗余。 + + image-20250318151846918 + + image-20250318155633751 + +* Milvus 架构解析 + + * 数据处理流程 :插入数据 → 生成日志 → 持久化到存储层 → 构建索引 → 支持查询。 + + export_qtyb85 + + | 组件名称 | 核心职责 | 关键特性 | + | :-------------: | :----------------------------------------------------------: | :---------------------------: | + | **Proxy** | 客户端请求入口,路由转发,负载均衡与协议转换(gRPC/RESTful) | 支持负载均衡、连接池管理 | + | **Query Node** | 执行向量搜索,标量过滤,向量相似度计算与混合过滤 | 内存索引加载,GPU加速支持 | + | **Data Node** | 处理数据插入、日志流处理与数据持久化存储 | 写入日志(WAL)保障数据一致性 | + | **Index Node** | 负责索引构建与优化 | 支持后台异步构建索引 | + | **Coordinator** | 集群元数据管理、任务调度 | 高可用部署(etcd存储元数据) | + + + +* 极速认知存储内容 + + * Collection 是一个二维表,具有固定的列和变化的行。 + * 每列代表一个字段,每行代表一个实体。 + * 下图显示了一个有 8 列和 6 个实体的 Collection + + ![Collection explained](D:/学习/笔记/img/collection-explained.png) + + + + + + + + + +#### 第3集 Milvus核心概念和数据结构讲解 + +**简介: Milvus核心概念和数据结构讲解** + +* **向量数据库对比关系型数据库** + + | Milvus 向量数据库 | 关系型数据库 | + | :---------------- | :----------- | + | Collection | 表 | + | Entity | 行 | + | Field | 表字段 | + +* **基础数据结构** + + * **Collection(集合)** + + - 类比关系型数据库的“表”,用于存储和管理一组具有相同结构的实体(Entity) + - Schema 定义字段结构(主键、向量、标量字段),支持动态字段(Milvus 2.3+),自动生成唯一ID(可选) + - 例子 + + ``` + pip install pymilvus==2.5.5 + + from pymilvus import FieldSchema, CollectionSchema, DataType + + # 定义字段 + fields = [ + FieldSchema(name="id", dtype=DataType.INT64, is_primary=True), + FieldSchema(name="vector", dtype=DataType.FLOAT_VECTOR, dim=768), + FieldSchema(name="category", dtype=DataType.VARCHAR, max_length=50) + ] + + # 创建集合 + schema = CollectionSchema(fields, description="商品向量库") + collection = Collection(name="products", schema=schema) + ``` + + * **Entity(实体)** + + - 数据的基本单位,包含多个字段(Field),如主键、标量字段(结构化数据)和向量字段。 + - 主键(Primary Key):唯一标识实体,支持整数或字符串类型。 + - 注意:Milvus 目前不支持主键去重,允许重复主键存在 + - 组成要素 + - **主键**:唯一标识(支持整数/字符串) + - **向量**:浮点数组(维度需固定) + - **标量字段**:元数据(文本/数值/布尔) + - 数据存储示意图 + + | ID (INT64) | Vector (FLOAT_VECTOR[768]) | Category (VARCHAR) | Price (FLOAT) | + | ---------- | -------------------------- | ------------------ | ------------- | + | 1001 | [0.12, 0.34, ..., 0.98] | "Electronics" | 299.99 | + | 1002 | [0.55, 0.21, ..., 0.11] | "Clothing" | 89.99 | + + * **字段(Field)** + + * 标量字段:存储数值、字符串等结构化数据,支持过滤查询(如 price < 100)。 + * 向量字段:存储高维向量(如512维浮点数组),支持相似性搜索 + + + +* **查询方式(Query)** + + * 向量搜索:输入一个向量,返回最相似的 Top-K 结果。 + + * 混合查询:结合向量相似度和标量过滤条件(如“价格 < 100”) + + + +* **数据组织与扩展** + + * **分区(Partition)** + + - 逻辑划分集合数据,用于优化查询性能(如按时间或地域分区)。 + - 每个分区可包含多个分片(Sharding)和段(Segment),查询时按分区减少扫描范围 + + * **分片(Sharding)** + + - 数据写入时分散到不同节点,实现并行写入。 + - 默认每个集合分2个分片,基于主键哈希分配 + + * **段(Segment)** + + - 物理存储单元,自动合并插入数据形成数据文件。 + + - 段分为“增长段”(持续写入)和“密封段”(持久化存储),查询时合并所有段结果 + + + +* **索引**(类似MySQL有多个不同索引类型) + + * 一种特殊的数据结构,用于快速查找和访问数据,存储在内存中. 本身不存储数据,是存储指向数据存储位置的指针或键值对。 + + * Milvus索引基于原始数据构建,提高对 collection 数据搜索的速度,一个向量字段仅支持一种索引类型。 + + * 为提高查询性能,Milvus 支持多种索引类型,可以为每个向量字段指定一种索引类型。 + + * 索引类型(先大体知道即可) + + | 索引类型 | 适用场景 | 内存消耗 | 精度 | 构建速度 | + | :------: | :--------------: | :------: | :--: | :------: | + | FLAT | 小数据集精准搜索 | 高 | 100% | 快 | + | IVF_FLAT | 平衡型场景 | 中 | 98% | 较快 | + | HNSW | 高召回率需求 | 高 | 99% | 慢 | + | IVF_PQ | 超大规模数据 | 低 | 95% | 快 | + +* **相似度计算** + + * **欧氏距离(Euclidean Distance L2)** + + * 数值越小越相似,理解为两个向量为两个点,欧式距离就是这两个点的直线距离 + * 两点之间的距离,最小值为0,最大值不确定 + * 两个点的距离,距离越近,则相似度越高,距离越大,则差异性越大 + + * **内积(Inner Product 简写 IP)** + + * 内积又称之为点积,数值越大越相似 `a·b=|a||b|cosθ ` + + ``` + # 向量A = [a1, a2,..., an] + # 向量B = [b1, b2,..., bn] + IP = a1*b1 + a2*b2 + ... + an*bn + ``` + + * **余弦相似度(Cosine)** + + * 基于向量夹角的相似度。 + + image-20250313170520819 + + + + + + + + + + + +#### 第4集 Milvus的分区-分片-段结构和最佳实践 + +**简介: Milvus的分区-分片-段结构和最佳实践** + +* 分区-分片-段 很多同学懵逼,用图书馆比喻理解三者关系 + + * 想象管理一个超大型图书馆(类比 **Collection** 集合),里面存放了上亿本书。 + * 为了更好地管理图书,用了三种组织方式 + * **分区(Partition)**:按书籍**主题**划分区域 + * 比如:1楼科技区、2楼文学区、3楼艺术区 + * **作用**:快速定位某一类书籍,避免全馆搜索 + * **类比**:电商平台按商品类别(电器/服装/食品)分区存储 + * **分片(Shard)**:每个主题区内设置**多个平行书架** + * 比如:科技区分成10个相同结构的书架,每个书架存100万本 + * **作用**:多人同时查找时,不同书架可并行工作 + * **类比**:分布式系统中用分片实现水平扩展 + * **段(Segment)**:每个书架上的**可拆卸书盒** + * 比如:每个书架由多个书盒组成,新书先放临时盒,写满后密封成固定盒 + * **作用**:优化存储空间,旧书盒可压缩归档 + * **类比**:数据库将数据分块存储,便于后台合并优化 + + + +* 三者协作关系 + +0 + +| 维度 | 分区(Partition) | 分片(Shard) | 段(Segment) | +| :--------: | :---------------: | :--------------------: | :----------------: | +| **层级** | 逻辑划分 | 物理分布 | 物理存储单元 | +| **可见性** | 用户主动创建管理 | 系统自动分配 | 完全由系统管理 | +| **目的** | 业务数据隔离 | 负载均衡与扩展 | 存储优化与查询加速 | +| **类比** | 图书馆的不同楼层 | 楼层内的多个相同书架 | 书架上的可替换书盒 | +| **操作** | 手动指定查询分区 | 自动路由请求到不同节点 | 自动合并/压缩 | + +* 实际工作流程例子 + + * **场景**:用户上传10万条商品数据到电商平台 + + * **分区阶段** + + * 按业务维度划分(如用户ID、时间范围), 示例:`partition_2024Q1`, `vip_users` + + ``` + # 按商品类别创建分区 + + # 电子产品存入electronics分区 + collection.create_partition("electronics") + + # 服装类存入clothing分区 + collection.create_partition("clothing") + ``` + + * **分片阶段(自动完成)** + + * 系统自动将数据均分到3个分片(假设集群有3个节点) + + 2 + + * **段阶段(自动完成)** + + * 分片内数据按512MB大小自动切割成多个段 + + 4 + +* 三者协作原理 + + * **写入过程** + + ``` + 新数据 → 选择分区 → 分配到分片 → 写入活跃段 → 段写满后冻结 → 生成新段 + ``` + + * **查询过程** + + ``` + 用户请求 → 定位分区 → 并行查询所有相关分片 → 各分片扫描所有段 → 合并结果排序 + ``` + + * **合并优化** + + ``` + # 自动将小段合并成大段(类似HBase Compaction) + [Segment1(100MB)] + [Segment2(100MB)] → [SegmentMerged(200MB)] + ``` + + * 注意: 分区的意义是通过划定分区减少数据读取,而分片的意义在于多台机器上并行写入操作。 + + + +* **开发注意事项** + + * **分区使用技巧** + + - 按时间分区:`2023Q1`, `2023Q2` + - 按业务线分区:`user_profiles`, `product_info` + - **错误示范**:创建超过1000个分区(影响元数据性能) + + ``` + # 好的实践:按时间分区 + client.create_partition( + collection_name="logs", + partition_name="2024-01" + ) + + # 坏的实践:每个用户一个分区(容易超过限制) + ``` + + * **分片配置建议** + + * ❌ 8核机器设置128分片 → 线程频繁切换导致性能下降 + * ✅ 使用公式:`分片数 = 节点数 × CPU核心数` + + | 分片数少 | 分片数多 | + | :------------: | :------------: | + | 单分片数据量大 | 单分片数据量小 | + | 写入吞吐低 | 写入吞吐高 | + | 易成性能瓶颈 | 资源消耗大 | + + ``` + # 创建集合时指定 + collection = Collection( + name="product_images", + shards_num=64, # 分片数 = 8台 × 8核 = 64 + partitions=[ + "electronics", + "clothing", + "home_appliances" + ] + ) + + # 调整段配置 + client.set_property("dataCoord.segment.maxSize", "1024") # 1GB + client.set_property("dataCoord.segment.sealProportion", "0.7") + ``` + + * 段优化策略 + + * 监控段大小:`collection.get_segment_info()` + * 手动触发合并:`collection.compact()` + * 设置段容量阈值:`storage.segmentSize=1024` (单位MB) + * 根据数据特性调整 + + ``` + if 向量维度 > 1024: + maxSize = 512 # 降段大小缓解内存压力 + else: + maxSize = 1024 + ``` + + + + + + + + + + + + + +#### 第5集 Milvus部署架构选择和Docker部署实战 + +**简介: Milvus部署架构选择和Docker部署实战 ** + +* 部署架构选择 + + * 选择取决于项目的阶段和规模,Milvus 为从快速原型开发到大规模企业部署的各种需求提供了灵活而强大的解决方案。 + + - **Milvus Lite**建议用于较小的数据集,多达几百万个向量, 不支持WINDOWS系统。 + - **Milvus Standalone**适用于中型数据集,可扩展至 1 亿向量。 + - **Milvus Distributed 专为**大规模部署而设计,能够处理从一亿到数百亿向量的数据集。 + + ![Select deployment option for your use case](D:/学习/笔记/img/select-deployment-option.jpeg) + +* Milvus分层架构(Docker部署都包括了) + + ``` + ┌───────────────────────────────┐ + │ Coordinator │ ← 管理元数据、负载均衡 + ├───────────────┬───────────────┤ + │ Query Node │ Data Node │ ← 处理查询与数据存储 + ├───────────────┴───────────────┤ + │ Object Storage (S3) │ ← 持久化存储(可选MinIO、AWS S3) + └───────────────────────────────┘ + ``` + + * Milvus Standalone 是单机服务器部署,所有组件都打包到一个Docker 镜像中,部署方便。 + * 此外,Milvus Standalone 通过主从复制支持高可用性。 + + + +* 有钱的当我没说,直接购买云厂商的服务:https://help.aliyun.com/zh/milvus + + + +* LInux服务器部署Milvus实战 + + * 阿里云网络安全组记得开放端口 `2379`、`9091`, `19530` + + * 注意: 默认没加权限校验,生产环境使用一般都是内网,结合配置IP白名单 + + * 版本和课程保持一致,不然很多不兼容!!! + + * 下载相关资料 ,使用**【wget】或者【浏览器】远程下载相关依赖包(需要替换群里最新的)** + + ``` + 原生资料下载方式(账号 - 密码 - ip地址 - 端口 需要替换群里最新的,【其他路径不变】) + wget --http-user=用户名 --http-password=密码 http://ip:端口/dcloud_pan/standalone_embed.sh + + + #比如 命令行下 + wget --http-user=admin --http-password=xdclass.net888 http://47.115.31.28:9088/dcloud_pan/standalone_embed.sh + + + # 比如 浏览器直接访问 + http://47.115.31.28:9088/dcloud_pan/standalone_embed.sh + ``` + + * 解压后执行【**依赖很多,版本差异大,务必按照下面执行,否则课程无法进行下去,加我微信 xdclass6**】 + + ``` + #启动 + bash standalone_embed.sh start + + #停止 + bash standalone_embed.sh stop + + #删除 + bash standalone_embed.sh delete + + #升级 + bash standalone_embed.sh upgrade + ``` + + * 运行安装脚本后 + + - 一个名为 Milvus 的 docker 容器已在**19530** 端口启动。 + - 嵌入式 etcd 与 Milvus 安装在同一个容器中,服务端口为**2379**。 + - Milvus 数据卷被映射到当前文件夹中的**volumes/milvus** + - 访问 `http://${MILVUS_PROXY_IP}:9091/webui` 例子: http://47.119.128.20:9091/webui/ + +* 注意 + + * Milvus Web UI 与 Attu等可视化工具 不同,它是一个内置工具,只是提供简单直观的界面,查看系统的基本信息 + + * 主要功能:运行环境、数据库/ Collections 详情、任务和慢查询请求,不支持数据库管理和操作任务 + + * 参考:https://milvus.io/docs/zh/milvus-webui.md + + + + + + + + + + + + + + + + + +#### 第6集 Milvus可视化客户端安装实战 + +**简介: Milvus可视化客户端安装实战 ** + +* Attu 可视化客户端介绍 + + * 是一款专为 **Milvus 向量数据库**设计的开源图形化管理工具,通过直观的界面简化了数据库的日常操作与维护流程 + * **跨平台支持**:提供 Docker 镜像,适配 Windows、Linux 和 macOS + * **开箱即用**:无需编写代码即可完成 Milvus 的日常管理,降低学习成本 + * **社区与生态**:由 Zilliz 团队维护,与 Milvus 深度集成,持续更新功能 + * **版本兼容性**:注意 Attu 与 Milvus 版本的匹配,避免接口不兼容问题【**当前安装的Milvus版本 V2.5X**】 + * GitHub地址:https://github.com/zilliztech/attu + + ![image-20250318190605311](D:/学习/笔记/img/image-20250318190605311.png) + +* 核心功能 + + * 数据库与集合管理 + * 数据库管理:支持创建、删除数据库,默认提供 default 数据库且不可删除。 + * 集合(Collection)操作:可创建集合、定义字段(主键、标量字段、向量字段)、构建索引,并支持数据导入/导出。 + * 分区与分片:支持按业务需求划分分区(如按时间或用户组),优化查询效率;默认分片数为 2,支持水平扩展。 + * 向量检索与混合查询 + * 相似性搜索:输入向量即可快速检索 Top-K 相似结果,支持欧氏距离(L2)、余弦相似度等度量方式。 + * 标量过滤:通过 Advanced Filter 功能结合标量字段(如价格、标签)进行条件筛选,提升搜索精准度。 + * 数据加载与释放:可将数据加载至内存加速检索,或释放内存以优化资源占用。 + * 用户与权限管理 + * 多角色权限控制:支持创建用户与角色,并分配细粒度权限(如全局权限、集合操作权限)。 + * 权限类型:涵盖数据插入、删除、查询等操作, + * 例如: + * 全局权限:创建/删除数据库、管理资源组。 + * 集合权限:加载/释放数据、构建索引、执行搜索。 + * 用户权限:更新用户凭证、查询用户信息 + +* 安装实战(根据系统选择对应的客户端下载,输入ip+端口) + + ![image-20250318201255473](D:/学习/笔记/img/image-20250318201255473.png) + + + + + +#### + diff --git a/src/main/java/org/ycloud/aipan/component/OSSFileStoreEngine.java b/src/main/java/org/ycloud/aipan/component/OSSFileStoreEngine.java index fcdc639..7615922 100644 --- a/src/main/java/org/ycloud/aipan/component/OSSFileStoreEngine.java +++ b/src/main/java/org/ycloud/aipan/component/OSSFileStoreEngine.java @@ -1,68 +1,160 @@ package org.ycloud.aipan.component; -import com.amazonaws.services.s3.model.Bucket; -import com.amazonaws.services.s3.model.S3ObjectSummary; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.*; +import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.tomcat.util.http.fileupload.IOUtils; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; +import java.io.File; +import java.io.IOException; +import java.util.Date; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +@Slf4j +@Component +@Primary +public class OSSFileStoreEngine implements StoreEngine { + @Resource + private AmazonS3Client amazonS3Client; + + // 缓存 bucket 存在性 + private final Map bucketCache = new ConcurrentHashMap<>(); -//@Component -public class OSSFileStoreEngine implements StoreEngine{ @Override public boolean bucketExists(String bucketName) { - return false; + return bucketCache.computeIfAbsent(bucketName, key -> amazonS3Client.doesBucketExistV2(key)); } @Override public boolean removeBucket(String bucketName) { + if (bucketExists(bucketName)) { + try { + amazonS3Client.deleteBucket(bucketName); + bucketCache.remove(bucketName); + return true; + } catch (Exception e) { + log.error("删除 bucket {} 失败: {}", bucketName, e.getMessage(), e); + } + } return false; } @Override public void createBucket(String bucketName) { - + log.info("创建 bucket: {}", bucketName); + if (!bucketExists(bucketName)) { + try { + amazonS3Client.createBucket(bucketName); + bucketCache.put(bucketName, true); + } catch (Exception e) { + log.error("创建 bucket {} 失败: {}", bucketName, e.getMessage(), e); + } + } else { + log.info("bucket 已存在"); + } } @Override public List getAllBucket() { - return List.of(); + return amazonS3Client.listBuckets(); } @Override public List listObjects(String bucketName) { + if (bucketExists(bucketName)) { + return amazonS3Client.listObjects(bucketName).getObjectSummaries(); + } return List.of(); } @Override public boolean doesObjectExist(String bucketName, String objectKey) { + if (bucketExists(bucketName)) { + return amazonS3Client.doesObjectExist(bucketName, objectKey); + } return false; } @Override public boolean upload(String bucketName, String objectKey, String localFileName) { + if (bucketExists(bucketName)) { + try { + amazonS3Client.putObject(bucketName, objectKey, new File(localFileName)); + return true; + } catch (Exception e) { + log.error("上传文件到 bucket {} 失败: {}", bucketName, e.getMessage(), e); + } + } return false; } @Override public boolean upload(String bucketName, String objectKey, MultipartFile file) { + if (bucketExists(bucketName)) { + try { + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentType(file.getContentType()); + objectMetadata.setContentLength(file.getSize()); + amazonS3Client.putObject(bucketName, objectKey, file.getInputStream(), objectMetadata); + return true; + } catch (Exception e) { + log.error("上传文件到 bucket {} 失败: {}", bucketName, e.getMessage(), e); + } + } return false; } @Override public boolean delete(String bucketName, String objectKey) { + if (bucketExists(bucketName)) { + try { + amazonS3Client.deleteObject(bucketName, objectKey); + return true; + } catch (Exception e) { + log.error("删除 bucket {} 中的对象 {} 失败: {}", bucketName, objectKey, e.getMessage(), e); + } + } return false; } @Override - public String getDownloadUrl(String bucketName, String remoteFileName, long timeout, TimeUnit unit) { - return ""; + public String getDownloadUrl(String bucketName, String objectKey, long timeout, TimeUnit unit) { + try { + Date expiration = new Date(System.currentTimeMillis() + unit.toMillis(timeout)); + return amazonS3Client.generatePresignedUrl(bucketName, objectKey, expiration).toString(); + } catch (Exception e) { + log.error("生成 bucket {} 中对象 {} 的下载 URL 失败: {}", bucketName, objectKey, e.getMessage(), e); + return null; + } } @Override public void download2Response(String bucketName, String objectKey, HttpServletResponse response) { - + try (S3Object s3Object = amazonS3Client.getObject(bucketName, objectKey)) { + response.setHeader("Content-Disposition", "attachment;filename=" + objectKey.substring(objectKey.lastIndexOf("/") + 1)); + response.setContentType("application/force-download"); + response.setCharacterEncoding("UTF-8"); + IOUtils.copy(s3Object.getObjectContent(), response.getOutputStream()); + } catch (IOException e) { + log.error("下载 bucket {} 中对象 {} 失败: {}", bucketName, objectKey, e.getMessage(), e); + } } + + // 拼接路径 +// public static void main(String[] args) { +// String fileSeparator = System.getProperty("file.separator"); +// System.out.println("dir" + fileSeparator + "time" + fileSeparator + "index.html"); +// String files = String.join("/", List.of("dir", "time", "index.html")); +// System.out.println(files); +// Path file = Paths.get("dir", "time", "index.html"); +// System.out.println(file); +// } } \ No newline at end of file diff --git a/src/main/java/org/ycloud/aipan/config/AliOssConfig.java b/src/main/java/org/ycloud/aipan/config/AliOssConfig.java new file mode 100644 index 0000000..c81c713 --- /dev/null +++ b/src/main/java/org/ycloud/aipan/config/AliOssConfig.java @@ -0,0 +1,28 @@ +package org.ycloud.aipan.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "oss") +public class AliOssConfig { + + @Value("endpoint") + private String endpoint; + + @Value("access-key") + private String accessKey; + + @Value("access-secret") + private String accessSecret; + + @Value("bucket-name") + private String bucketName; + + + // 预签名url过期时间(ms) + private Long PRE_SIGN_URL_EXPIRE = 60 * 10 * 1000L; +} diff --git a/src/main/java/org/ycloud/aipan/config/AmazonS3Config.java b/src/main/java/org/ycloud/aipan/config/AmazonS3Config.java index 4a67685..b21351d 100644 --- a/src/main/java/org/ycloud/aipan/config/AmazonS3Config.java +++ b/src/main/java/org/ycloud/aipan/config/AmazonS3Config.java @@ -5,6 +5,7 @@ import com.amazonaws.Protocol; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.auth.BasicSessionCredentials; import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.regions.Regions; import com.amazonaws.services.s3.AmazonS3; @@ -20,8 +21,10 @@ import org.springframework.context.annotation.Configuration; public class AmazonS3Config { // 注入Minio配置类,用于获取访问密钥和Endpoint等信息 +// @Resource +// private MinioConfig minioConfig; @Resource - private MinioConfig minioConfig; + private AliOssConfig aliOssConfig; /** * 创建并配置Amazon S3客户端 @@ -37,20 +40,19 @@ public class AmazonS3Config { // 设置网络访问超时时间 config.setConnectionTimeout(5000); config.setUseExpectContinue(true); - - // 使用Minio配置中的访问密钥和秘密密钥创建AWS凭证 - AWSCredentials credentials = new BasicAWSCredentials(minioConfig.getAccessKey(), minioConfig.getAccessSecret()); - + // 使用配置中的访问密钥和秘密密钥创建AWS凭证 + AWSCredentials credentials = new BasicAWSCredentials(aliOssConfig.getAccessKey(), aliOssConfig.getAccessSecret()); // 设置Endpoint AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder - .EndpointConfiguration(minioConfig.getEndpoint(), Regions.US_EAST_1.name()); - + .EndpointConfiguration(aliOssConfig.getEndpoint(), Regions.US_EAST_1.name()); // 使用以上配置创建并返回Amazon S3客户端实例 return AmazonS3ClientBuilder.standard() .withClientConfiguration(config) .withCredentials(new AWSStaticCredentialsProvider(credentials)) .withEndpointConfiguration(endpointConfiguration) - .withPathStyleAccessEnabled(true).build(); - } + .withPathStyleAccessEnabled(false) // mino设置为true,oss设置为false + .build(); + + } } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index dfe9808..0e15a7c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -44,4 +44,12 @@ minio: access-key: minio_root access-secret: minio_123456 bucket-name: ai-pan - avatar-bucket-name: avatar \ No newline at end of file + avatar-bucket-name: avatar + + +oss: + endpoint: oss-cn-hangzhou.aliyuncs.com + access-key: LTAI5tRQFFPQWHPZksM9XGHG + access-secret: z4ZSJffdH525Konxz7LBxOSAZP2BXN + bucket-name: forward-tech + diff --git a/src/test/java/org/ycloud/aipan/AmazonS3ClientTests.java b/src/test/java/org/ycloud/aipan/AmazonS3ClientTests.java index 9900cae..e77292d 100644 --- a/src/test/java/org/ycloud/aipan/AmazonS3ClientTests.java +++ b/src/test/java/org/ycloud/aipan/AmazonS3ClientTests.java @@ -32,7 +32,7 @@ class AmazonS3ClientTests { */ @Test public void testBucketExists() { - boolean bucketExist = amazonS3Client.doesBucketExist("ai-pan1"); + boolean bucketExist = amazonS3Client.doesBucketExist("forward-tech"); log.info("bucket是否存在:{}", bucketExist); } @@ -51,7 +51,7 @@ class AmazonS3ClientTests { */ @Test public void testDeleteBucket() { - String bucketName = "ai-pan1"; + String bucketName = "tjcz-app"; amazonS3Client.deleteBucket(bucketName); } @@ -171,7 +171,7 @@ class AmazonS3ClientTests { // 计算预签名url的过期日期 Date expireDate = DateUtil.offsetMillisecond(new Date(), (int) PRE_SIGN_URL_EXPIRE); // 创建生成预签名url的请求,并设置过期时间和HTTP方法, withMethod是生成的URL访问方式 - GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest("avatar", "/2025/3/22/999ae223-cf98-4891-a7da-e3ab1d618719.jpg") + GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest("forward-tech", "confluence/confluence_01.png") .withExpiration(expireDate).withMethod(HttpMethod.GET); // 生成预签名url URL preSignedUrl = amazonS3Client.generatePresignedUrl(request);