From 55c59f21740bfc69616176096e207cb8f69433e6 Mon Sep 17 00:00:00 2001 From: "yuanjs@qutke.com" Date: Thu, 15 May 2025 14:38:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4672 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4672 insertions(+) diff --git a/README.md b/README.md index c804afb..b845933 100644 --- a/README.md +++ b/README.md @@ -10907,3 +10907,4675 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){ * **调试技巧**:使用 `RunnableLambda` 插入日志或数据检查点。 * **容错设计**:结合 `RunnableBranch` 和 提升健壮性 +### 进阶LLM之Agent智能体和Tool工具实战 + +#### 大模型Agent智能体介绍和应用场景 + +* 什么是智能体Agent + + * 是一种**具备自主决策能力的AI系统**,通过感知环境、分析信息、调用工具、执行动作的闭环过程完成任务 + + * 智能体 = 大语言模型(LLM) + 工具(Tools) + 记忆(Memory) + + * 核心架构 + + ``` + 用户输入 → 大模型推理 → 工具选择 → 执行工具 → 结果验证 → 输出响应 + ↑ ↓ ↑ + 记忆系统 ↔ 工具库 ↔ 知识库 + ``` + + * 类比:一个具备自主决策能力的虚拟助手,能根据目标自主调用工具完成任务 + + 1 + + + + * 与传统LLM的关键区别 + + | 维度 | 常规LLM | Agent | + | :------: | :-----------: | :---------------: | + | 交互方式 | 单轮问答 | 多轮决策链 | + | 能力范围 | 文本生成 | 工具调用+环境交互 | + | 记忆机制 | 短期上下文 | 长期记忆存储 | + | 输出形式 | 自然语言 | 结构化动作序列 | + | 应用场景 | 内容创作/问答 | 复杂任务自动化 | + + 3 + + * 常规大模型和Agent案例场景对比 + + | 测试用例 | 传统模式响应 | Agent模式响应 | + | :--------------: | :----------: | :---------------------------: | + | "北京天气" | 温度数据 | "北京当前晴,12℃,建议穿外套" | + | "明天需要带伞吗" | 无法处理 | 调用天气API分析降水概率 | + | "上周三天气如何" | 报错 | 自动切换历史天气数据库工具 | + + + +* 典型应用场景 + + * 医疗行业 - 诊断辅助Agent + + * 传统系统痛点: + + * 基于固定规则的专家系统、无法处理复杂症状组合、知识更新依赖人工维护 + + * Agent方案关键能力 + + * 结合最新医学论文(通过工具实时检索,常规大模型没法获取最新数据) + * 自动生成检查建议清单 + * 保留患者完整诊疗历史 + + ```python + medical_agent = AgentExecutor( + tools=[ + SymptomAnalyzerTool, + MedicalLiteratureTool, + LabTestRecommenderTool + ], + memory=PatientHistoryMemory() + ) + + # 交互示例 + response = medical_agent.invoke({ + "input": "患者女35岁,持续低烧两周,伴有关节痛", + "history": "既往有类风湿病史" + }) + # 输出:建议进行抗核抗体检测+推荐专科医生 + ``` + + * 教育行业 - 个性化学习Agent + + * 传统在线教育 + + ```python + // 固定学习路径 + public class LearningService { + public String getNextStep(String userId) { + int score = db.getUserScore(userId); + if (score < 60) { + return "重新学习第三章"; + } + return "进入第四章"; + } + } + ``` + + * Agent方案关键能力 + + * 动态调整学习路径(基于实时掌握程度) + * 多模态教学内容推荐(视频/图文/交互实验) + * 自动生成错题分析报告 + + ```python + class TutorAgent: + tools = [ + KnowledgeGraphTool, + ExerciseRecommenderTool, + LearningStyleAnalyzerTool + ] + + def guide_student(self, studentQuery): + # 动态决策: + # 1. 分析学生知识薄弱点 + # 2. 根据学习风格推荐资料 + # 3. 生成个性化练习计划 + return self.agent_executor.invoke(studentQuery) + + ``` + +* Agent智能体案例(伪代码) + + ```python + from langchain.agents import AgentExecutor, create_react_agent + from langchain import hub + + # 定义工具集 + tools = [ + Tool( + name="WeatherCheck", + func=get_weather_api_data, + description="查询实时天气数据" + ), + Tool( + name="CalendarAccess", + func=read_google_calendar, + description="访问用户日历信息" + ) + ] + + # 构建Agent + prompt = hub.pull("hwchase17/react") + agent = create_react_agent( + llm=ChatOpenAI(temperature=0), + tools=tools, + prompt=prompt + ) + + # 执行示例 + agent_executor = AgentExecutor(agent=agent, tools=tools) + result = agent_executor.invoke({ + "input": "帮我安排明天北京的户外会议,需要考虑天气情况" + }) + print(result["output"]) + + + #典型输出示例 + 思考过程: + 1. 需要确定明天北京的天气(调用WeatherCheck) + 2. 查询明天下午2点的天气预报 + 3. 如果天气适宜,查找明天下午的空闲时段(调用CalendarAccess) + 4. 综合结果建议会议时间 + 最终输出:建议将会议安排在明天下午15:00,天气预报显示晴,气温22℃。 + ``` + + +#### 大模型痛点和LangChain工具Tool实战 + +* 需求背景: + + * **大模型的短板**:虽然大语言模型(LLM)擅长文本生成,但缺乏: + - 实时数据获取能力(如天气/股票)、精确数学计算能力 + - 专业领域知识(如法律/医疗)、外部系统对接能力 + + * Tool工具就是解决这类问题的,通过Tool机制,好比给大模型插入翅膀 + +* 大模型的Tool工具 + + * Tool是LLM与外部世界交互的接口,让大模型能调用外部功能(如API、函数、数据库) + + * 核心 + + * 突破大模型静态知识库限制 + * 实时获取外部数据(如天气/股票) + * 执行复杂计算业务逻辑 + * 连接现有软件系统(如CRM、各个系统API) + + * 工具生命周期 + + ``` + 工具定义 → 2. Agent注册 → 3. 自动调用 → 4. 结果处理 + ``` + +* LangChain里面创建工具 + + * @tool装饰器 + * 通过简单的@tool装饰器或StructuredTool即可实现,适用于大多数用例, + * @tool但不能同时有同步和异步的方法,只能单独使用 + * LangChain Runnables + * 接受字符串或字典输入的LangChain Runnables使用as_tool方法转换为工具 + * 允许为参数指定名称、描述和其他模式信息; + * 继承BaseTool类: + * 通过从BaseTool进行子类化来定义自定义工具,提供了对工具定义的最大控制,但需要编写更多的代码。 + + + +* LangChain里面Tool实战 + + * **@tool 装饰器**:用来定义一个简单的工具函数,, 可以直接给函数加上这个装饰器,让函数成为可调用的工具 + + * 简单定义, **需要加上文档字符串注释描述,AI才知道工具的用途** + + ``` + from langchain_core.tools import tool + + @tool + def multiply(a: int, b: int) -> int: + """把传递的两个参数相乘""" + return a * b + + print("工具名称:", multiply.name) + print("工具描述:", multiply.description) + print("工具参数:", multiply.args) + print("工具返回值:", multiply.return_direct) + print("工具详细的schema:",multiply.args_schema.model_json_schema()) + + print(multiply.invoke({"a": 2, "b": 3})) + #定义了一个 `multiply` 工具,用于两个数字相乘,并在调用时显示该工具的名称、描述和参数列表。 + ``` + + * 配置参数 + + ```python + from pydantic import BaseModel, Field + from langchain_core.tools import tool + + class CalculatorInput(BaseModel): + a: int = Field(description="第一个参数") + b: int = Field(description="第二个参数") + + @tool("multiplication-tool", args_schema=CalculatorInput, return_direct=True) + def multiply(a: int, b: int) -> int: + """Multiply two numbers.""" + return a * b + + + # Let's inspect some of the attributes associated with the tool. + print("工具名称:", multiply.name) + print("工具描述:", multiply.description) + print("工具参数:", multiply.args) + print("工具返回值:", multiply.return_direct) + print("工具详细的schema:",multiply.args_schema.model_json_schema()) + ``` + + * 核心组件 + + | **组件** | **作用** | **示例** | + | :---------------------------- | :--------------------------------------------------------- | :----------------------------- | + | **名称(name)** | 工具唯一标识符,代理通过名称匹配调用工具 | `wikipedia`、`google_search` | + | **描述(description)** | 工具功能的自然语言描述,代理根据描述决定是否调用工具 | "查询维基百科内容" | + | **输入参数(args_schema)** | 定义工具的参数格式(Pydantic模型),用于参数校验与提示生成 | `query: str` | + | **执行函数(func)** | 实际执行操作的函数(如调用API、运行Shell命令) | `def run(query: str): ...` | + | **返回模式(return_direct)** | 若为`True`,代理直接返回工具结果,不再生成额外文本 | 适用于无需进一步推理的简单任务 | + +* `StructuredTool ` 介绍 + + * 是LangChain中用于定义**结构化参数工具**的基类,相比普通`@tool`装饰器,它支持: + - **严格的参数模式定义**(基于Pydantic模型) + - **多参数输入校验** + - **自动生成工具调用示例** + * **适用场景**:需要多个输入参数或复杂参数类型的工具 + + | **特性** | **普通@tool装饰器** | **StructuredTool** | + | :------------- | :--------------------------- | :------------------------------- | + | **参数定义** | 简单参数(单个字符串或字典) | 基于Pydantic模型的严格参数模式 | + | **参数校验** | 弱校验(依赖代码逻辑) | 强校验(自动类型检查和格式验证) | + | **多参数支持** | 需手动解析字典参数 | 直接映射多个命名参数 | + | **使用复杂度** | 快速定义简单工具 | 适合复杂业务逻辑的工具 | + + * 案例实战 + + ```python + from pydantic import BaseModel, Field + from langchain_core.tools import StructuredTool + + # 定义输入参数的数据结构 + class CalculatorInput(BaseModel): + a: int = Field(description="第一个数字") + b: int = Field(description="第二个数字") + + # 定义计算函数 + def multiply(a: int, b: int) -> int: + """Multiply two numbers.""" + return a * b + + # 封装工具 + calculator = StructuredTool.from_function( + func=multiply, + name="Calculator", + description="用于计算两个数字的乘积", + args_schema=CalculatorInput, + return_direct=True, + ) + + print("工具名称:", calculator.name) + print("工具描述:", calculator.description) + print("工具参数:", calculator.args) + print("工具返回值:", calculator.return_direct) + print("工具详细的schema:",calculator.args_schema.model_json_schema()) + + # 调用工具 + print("工具调用结果:", calculator.invoke({"a": 2, "b": 3})) + ``` + +* 使用继承`BaseTool`子类进行创建工具 + + ```python + from pydantic import BaseModel, Field + from typing import Type + from langchain_core.tools import BaseTool + from pydantic import BaseModel + + + class CalculatorInput(BaseModel): + a: int = Field(description="第一个参数") + b: int = Field(description="第二个参数") + + + class CustomCalculatorTool(BaseTool): + name: str = "Calculator" + description: str = "当你需要计算数学问题时候使用" + args_schema: Type[BaseModel] = CalculatorInput + return_direct: bool = True + + def _run( + self, a: int, b: int + ) -> str: + """使用工具.""" + return a * b + + + calculator = CustomCalculatorTool() + print("工具名称:", calculator.name) + print("工具描述:", calculator.description) + print("工具参数:", calculator.args) + print("工具返回值:", calculator.return_direct) + print("工具详细的schema:",calculator.args_schema.model_json_schema()) + + print(calculator.invoke({"a": 2, "b": 3})) + ``` + + +#### LLM大模型绑定工具Tool案例实战 + +* 需求 + + * 定义了工具,需要把工具绑定给大模型, 大模型会在合适的时候,选择对应的工具函数 + * **解决痛点**:如天气查询/股票数据/订单处理等需要实时数据的场景 + * **类比理解**:给大模型装"手和脚",像钢铁侠的AI助手贾维斯可操作战甲 + * 注意 + * 虽然“工具调用”这个名称暗示模型直接执行某些操作,但实际上并非如此! + * 模型仅生成工具的参数,实际运行工具(或不运行)取决于用户的需求 + + +| 要素 | 作用 | 示例 | +| :------: | :------------------------------: | :------------------------------: | +| 工具描述 | 告诉模型工具的功能和使用方法 | 天气查询API的输入输出说明 | +| 参数解析 | 从自然语言中提取结构化参数 | 提取"北京今天温度"中的城市和日期 | +| 执行反馈 | 将工具返回结果重新组织成自然语言 | 把JSON天气数据转为口语化描述 | + + * 技术实现流程 + + ``` + 用户输入 → 大模型分析意图 → 选择工具 → 提取参数 → 调用工具 → 结果格式化 → 最终回复 + ``` + +* 大模型绑定工具api + + * 支持工具调用功能的聊天模型, 直接使用`.bind_tools( )`方法,传入工具列表即可 + + ```python + def bind_tools( + self, + tools: Sequence[Union[Dict[str, Any], Type, Callable, BaseTool]], + *, + tool_choice: Optional[ + Union[dict, str, Literal["auto", "none", "required", "any"], bool] + ] = None, + strict: Optional[bool] = None, + parallel_tool_calls: Optional[bool] = None, + **kwargs: Any, + ) -> Runnable[LanguageModelInput, BaseMessage]: + ``` + + * 注意:不是全部大模型都是支持绑定工具列表, + + * 大模型绑定工具的伪代码参考 + + ```python + tools = [add, multiply] + from langchain_openai import ChatOpenAI + llm = ChatOpenAI(model="gpt-4o-mini") + llm_with_tools = llm.bind_tools(tools) + query = "What is 3 * 12?" + resp = llm_with_tools.invoke(query) + ``` + + + +* 工具调用 + + * 第一步 + + * 大模型如果需要调用工具,则生成响应里面包括了工具调用信息,本身的content内容为空 + + * 大模型响应的消息 或消息块 作为工具调用对象的列表,位于`.tool_calls`属性中。 + + * 聊天模型可以同时调用多个工具, 包含 工具名称、参数值字典和(可选)标识符的类型字典。 + + * 没有工具调用的消息 默认将此属性设置为空列表 + + ```python + # 使用绑定工具的模型处理用户查询 + ai_msg = llm_with_tools.invoke(messages) + + # 打印ai_msg对象的tool_calls属性,显示AI消息中包含的工具调用信息 + print(ai_msg.tool_calls) + + {'tool_calls': + [ + { + 'id': 'call_ea723d86cf804a088b946a', + 'function': {'arguments': '{"a": 3, "b": 12}', 'name': 'multiply'}, + 'type': 'function', 'index': 0} + ] + } + ``` + + * 第二步 + + * 提取大模型响应信息里面的选择的工具,代码编写 选择对应的工具进行执行 + + ```python + # 遍历AI消息中的工具调用 + for tool_call in ai_msg.tool_calls: + # 根据工具调用的名称选择相应的工具函数 + selected_tool = {"add": add, "multiply": multiply}[tool_call["name"].lower()] + print(f"selected_tool:{selected_tool}") + # 调用选中的工具函数并获取结果 + tool_msg = selected_tool.invoke(tool_call) + print(f"tool_msg:{tool_msg}") + # 将工具调用的结果添加到messages列表中 + messages.append(tool_msg) + ``` + + * `invoke()`执行后,会生成执行结果对象 `ToolMessage`, 包括与模型生成的原始工具调用中的 `id` 匹配的 `tool_call_id` + + ``` + ToolMessage(content='36', name='multiply', tool_call_id='call_1319a58494c54998842092')] + ``` + + * 第三步 + + * 将工具调用的结果添加到消息messages列表中,再传递给大模型,大模型会重新进行执行,组织对应的语言返回 + + ```python + # 再次使用绑定工具的模型处理更新后的messages列表 + reslut = llm_with_tools.invoke(messages) + # 打印最终结果 + print(f"最终结果:{reslut}") + + + 最终结果: content='3 乘以 12 的结果是 36。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 272, 'total_tokens': 288, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-b499cd0b-29d4-9d83-8473-e41d7214223d', 'finish_reason': 'stop', 'logprobs': None} id='run-8046aa25-091a-4df3-a49a-aa36811c8d44-0' usage_metadata={'input_tokens': 272, 'output_tokens': 16, 'total_tokens': 288, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}} + ``` + + ![Diagram of a tool call invocation](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/tool_invocation-7f277888701ee431a17607f1a035c080-20250328134624442.png) + + ![Diagram of a tool call result](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/tool_results-71b4b90f33a56563c102d91e7821a993.png) + +* 完整案例实战 + + ```python + from langchain_core.tools import tool + from langchain_openai import ChatOpenAI + from langchain_core.messages import HumanMessage + + # 定义一个加法工具函数 + @tool + def add(a: int, b: int) -> int: + """Adds a and b.""" + return a + b + + # 定义一个乘法工具函数 + @tool + def multiply(a: int, b: int) -> int: + """Multiplies a and b.""" + return a * b + + # 定义模型 + llm = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # 将前面定义的工具函数整合到一个列表中 + tools = [add, multiply] + + # 将工具函数绑定到语言模型 + llm_with_tools = llm.bind_tools(tools) + + # 定义用户查询 + query = "3 * 12 结果是多少" + + # 创建一个包含用户查询的messages列表 + messages = [HumanMessage(query)] + + # 使用绑定工具的模型处理用户查询 + ai_msg = llm_with_tools.invoke(messages) + + # 打印ai_msg对象,以便用户可以看到AI的消息响应 + print(ai_msg) + + # 打印ai_msg对象的tool_calls属性,显示AI消息中包含的工具调用信息 + print(ai_msg.tool_calls) + + # 将AI的消息添加到messages列表中 + messages.append(ai_msg) + + # 遍历AI消息中的工具调用 + for tool_call in ai_msg.tool_calls: + # 根据工具调用的名称选择相应的工具函数 + selected_tool = {"add": add, "multiply": multiply}[tool_call["name"].lower()] + print(f"selected_tool:{selected_tool}") + # 调用选中的工具函数并获取结果 + tool_msg = selected_tool.invoke(tool_call) + + print(f"tool_msg:{tool_msg}") + # 将工具调用的结果添加到messages列表中 + messages.append(tool_msg) + + # 打印更新后的messages列表 + print(f"打印更新后的messages列表:{messages}") + + # 再次使用绑定工具的模型处理更新后的messages列表 + reslut = llm_with_tools.invoke(messages) + # 打印最终结果 + print(res + ``` + + +#### LangChain内置工具包和联网搜索实战 + +* LangChain工具包 + + * 方便开发者快速使用各种主流工具,LangChain官方加入了很多内置工具,开箱即用 + + * 所有工具都是 BaseTool 的子类,且工具是 Runnable可运行组件,支持 invoke、stream 等方法进行调用 + + * 也可以通过 name、 description、args、 returu_direct 等属性来获取到工具的相关信息 + + * 如果内置工具包不满足,即可以自定义工具 + + * 地址(失效忽略即可):https://python.langchain.com/docs/integrations/tools/ + + 1 + +* 如何使用内置工具包【联网搜索例子】 + + * 选择对应的工具->安装依赖包->编写代码实战 + + * 案例实战 + + * 搜索工具:选择 SearchApi,注册时100次免费搜索,注册账号获取 APIKEY + + image-20250328123503960 + + * 编写代码 + + ```python + # 导入操作系统接口模块,用于与环境变量交互 + import os + # 从langchain_community.utilities模块中导入SearchApiAPIWrapper类,用于封装搜索API + from langchain_community.utilities import SearchApiAPIWrapper + + # 设置环境变量SEARCHAPI_API_KEY,用于认证搜索API的密钥 + os.environ["SEARCHAPI_API_KEY"] = "qQBHMQo4Rk8SihmpJjCs7fML" + + # 实例化SearchApiAPIWrapper对象,用于调用搜索API + search = SearchApiAPIWrapper() + + # 调用run方法执行搜索操作,参数为查询腾讯股价的中文字符串 + result = search.run("今天腾讯的股价是多少") + + # 输出搜索结果 + print(result) + ``` + + * 拓展 + + * SearchApi 包装器可以自定义为使用不同的引擎,如 Google News、Google Jobs、Google Scholar + + * 其他可以在 SearchApi 文档中找到的引擎。执行查询时可以传递 SearchApi 支持的所有参数。 + + ``` + search = SearchApiAPIWrapper(engine="google_jobs") + ``` + + * 获取元数据 + + ```python + result_meta = search.results("今天腾讯的股价是多少") + print(result_meta) + ``` + + +#### 兜底降级-LangChain调用工具Tool异常处理 + +* 需求背景 + + * 智能体(Agent)在调用外部工具(如 API、数据库、搜索引擎)时,会遇到各种不可预知的错误 + * 例如: + - 网络请求失败(如 API 无响应) + - 权限不足(如访问密钥失效) + - 输入参数不合法(如格式错误) + - 资源限制(如 API 调用次数超限) + * 如果智能体不处理这些错误,会导致: + * **程序崩溃**:直接抛出未捕获的异常。 + * **用户困惑**:返回难以理解的错误堆栈信息。 + * **无法恢复**:智能体无法根据错误调整策略(如重试或切换工具) + +* 解决方案 `ToolException`: + + * 通过 ToolException 统一捕获和处理工具调用中的错误,使智能体具备容错能力和用户友好的错误反馈 + * ToolException 的核心作用 + * 统一错误格式:将不同工具的异常转换为标准格式,方便智能体解析。 + * 错误上下文传递:保留错误原因、工具名称等关键信息。 + * 使用场景举例 + * **API 调用失败**:如天气查询接口超时。 + * **权限校验失败**:如访问数据库的密钥过期。 + * **输入参数校验**:如用户输入的城市名不存在。 + * **资源限制**:如每日调用次数用尽。 + +* 案例实战 + + * 方式一:配置响应 `handle_tool_error=True` 默认是false + + ```python + from langchain_core.tools import tool, ToolException, StructuredTool + + def search(query: str) -> str: + """ + 执行搜索查询 + """ + # 引发一个ToolException来模拟搜索结果为空的情况 + raise ToolException(f"相关搜索结果为空:{query}") + + # 使用StructuredTool从函数创建一个工具实例 + # handle_tool_error参数设置为True,表示工具将处理内部异常 + search_tool = StructuredTool.from_function( + func=search, + name="search", + description="搜索工具", + handle_tool_error=True + ) + + # 调用search_tool的invoke方法来执行搜索工具, 传递一个包含查询参数的字典 + resp = search_tool.invoke({"query": "腾讯的股价多少"}) + # 打印搜索工具的响应结果 + print(resp) + ``` + + * 方式二:配置响应 `handle_tool_error=”错误信息“` + + ```python + from langchain_core.tools import tool, ToolException, StructuredTool + def search(query: str) -> str: + """ + 执行搜索查询 + """ + # 引发一个ToolException来模拟搜索结果为空的情况 + raise ToolException(f"相关搜索结果为空:{query}") + + # 使用StructuredTool从函数创建一个工具实例 + # handle_tool_error参数设置为True,表示工具将处理内部异常 + search_tool = StructuredTool.from_function( + func=search, + name="search", + description="搜索工具", + handle_tool_error="搜索结果失败,请重试" + ) + # 调用search_tool的invoke方法来执行搜索工具,传递一个包含查询参数的字典 + resp = search_tool.invoke({"query": "腾讯的股价多少"}) + # 打印搜索工具的响应结果 + print(resp) + ``` + + * 方式三:配置自定义处理函数-兜底降级 + + ```python + from langchain_core.tools import tool, ToolException, StructuredTool + def search(query: str) -> str: + """ + 执行搜索查询 + """ + # 引发一个ToolException来模拟搜索结果为空的情况 + raise ToolException(f"相关搜索结果为空:{query}") + + #自定义异常处理函数 + def _handle_tool_error(error: ToolException) -> str: + """ + 自定义异常处理函数 + """ + return f"搜索结果失败,自定义异常,请重试:{error}" + + + # 使用StructuredTool从函数创建一个工具实例 + # handle_tool_error参数设置为True,表示工具将处理内部异常 + search_tool = StructuredTool.from_function( + func=search, + name="search", + description="搜索工具", + handle_tool_error=_handle_tool_error + ) + # 调用search_tool的invoke方法来执行搜索工具,传递一个包含查询参数的字典 + resp = search_tool.invoke({"query": "腾讯的股价多少"}) + # 打印搜索工具的响应结果 + print(resp) + ``` + + +#### 插上翅膀-LLM增加联网搜索功能实 + +* 需求说明 + + * 实现了一个结合大语言模型(LLM)和工具调用的智能问答系统。 + * 用户可以通过自然语言输入问题,系统会根据问题内容判断是否需要调用外部工具(如搜索引擎或计算工具),返回最终答案。 + +* 交互流程图 + + 1 + +* 步骤思路 + + * 配置SearchAPI的API密钥, + * 实例化`SearchApiAPIWrapper`对象,用于调用搜索API。 + * 定义多个工具函数,供系统在回答问题时调用。 + - **web_search**: 用于搜索实时信息、最新事件或未知领域知识。 + - 输入参数:`query`(字符串类型,搜索关键词)。 + - 返回值:搜索结果的标题和摘要。 + - **multiply**: 用于计算两个整数的乘积。 + - 输入参数:`a`和`b`(均为整数类型)。 + - 返回值:两数相乘的结果。 + * 初始化大语言模型(LLM),并将其与工具绑定,形成一个智能问答Agent。 + - **关键点** + - 使用`ChatOpenAI`类初始化LLM,指定模型名称、API密钥、温度等参数。 + - 创建聊天提示模板(`ChatPromptTemplate`),定义系统角色和用户输入格式。 + - 将工具列表与LLM绑定,具备工具调用能力的Agent。 + * 构建运行链(`chain`),将用户输入传递给提示模板和Agent,生成响应。 + - **关键点** + - 用户输入通过`RunnablePassthrough`传递到提示模板。 + - 提示模板生成的消息进一步传递给Agent处理。 + * 根据据LLM生成的响应,判断是否要调用工具。如果需要调用工具并将结果合并到历史消息中,重新传递给LLM生成最终答案。 + - **关键点** + - 通过`resp.tool_calls`判断是否需要调用工具。 + - 调用工具后,将结果以`ToolMessage`形式添加到历史消息中。 + - 最终结果由LLM根据更新的历史消息生成。 + +* 案例代码实战 + + ```python + # 基于LangChain 0.3.x的SearchApi工具实战(需安装依赖) + # pip install langchain-core langchain-openai langchain-community + + from langchain_community.utilities import SearchApiAPIWrapper + from langchain_core.tools import tool + from langchain_openai import ChatOpenAI + from langchain_core.messages import ToolMessage + from langchain_core.runnables import RunnablePassthrough + from langchain_core.prompts import ChatPromptTemplate + + import os + from langchain_core.tools import tool + from pydantic import Field + # ====================== + # 第一步:配置搜索工具 + # ====================== + # 注册SearchAPI获取密钥:https://www.searchapi.io/ + + # 设置SearchAPI的API密钥 + os.environ["SEARCHAPI_API_KEY"] = "qQBHMQo4Rk8SihmpJjCs7fML" + # 实例化SearchApiAPIWrapper对象,用于调用搜索API + search = SearchApiAPIWrapper() + # ====================== + # 第二步:定义搜索工具 + # ====================== + @tool("web_search", return_direct=True) + def web_search(query: str) -> str: + """ + 当需要获取实时信息、最新事件或未知领域知识时使用,输入应为搜索关键词 + """ + try: + results = search.results(query) # 获取前3条结果 + return "\n\n".join([ + f"来源:{res['title']}\n内容:{res['snippet']}" + for res in results['organic_results'] + ]) + except Exception as e: + return f"搜索失败:{str(e)}" + + @tool("multiply") + def multiply(a: int, b: int) -> int: + """ + 把传递的两个参数相乘 + """ + return a * b + + # ====================== + # 第三步:绑定LLM创建Agent + # ====================== + # 初始化大模型 + llm = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # 创建聊天提示模板 + prompt = ChatPromptTemplate.from_messages( + [ + ("system", "你是一个AI助手,名称叫老王,请根据用户输入的查询问题,必要时可以调用工具帮用户解答"), + ("human", "{query}"), + ] + ) + + # 定义工具字典 + tool_dict = { + "web_search": web_search, + "multiply": multiply + } + + # 从字典中提取工具列表 + tools = [ tool_dict[tool_name] for tool_name in tool_dict ] + + # 绑定工具到大模型 + llm_with_tools = llm.bind_tools(tools=tools) + + # 创建运行链 + chain = {"query":RunnablePassthrough()} | prompt | llm_with_tools + + # 定义查询 + query = "你是谁?" + + # 执行链并获取响应 + resp = chain.invoke({"query":query}) + print(resp) + + #判断是否需要调用工具 content=''不一定需要调用,根据tool_calls进行判断 + ``` + + + +* 编码实战 + + ```python + #判断是否需要调用工具 content=''不一定需要调用,根据tool_calls进行判断 + + # ====================== + # 第四步:获取工具调用 + # ====================== + tool_calls = resp.tool_calls + if len(tool_calls) <= 0: + print(f"不需要调用工具:{resp.content}") + else: + #将历史消息合并,包括用户输入和AI输出 + history_messages = prompt.invoke(query).to_messages() + history_messages.append(resp) + print(f"历史消息:{history_messages}") + #循环调用工具 + for tool_call in tool_calls: + tool_name = tool_call.get("name") + tool_args = tool_call.get("args") + tool_resp = tool_dict[tool_name].invoke(tool_args) + print(f"一次调用工具:{tool_name},参数:{tool_args},结果:{tool_resp}") + #将工具调用结果添加到历史消息中 + history_messages.append( + ToolMessage( + tool_call_id=tool_call.get("id"), + name=tool_name, + content=tool_resp + ) + ) + print(f"历史消息:{history_messages}") + resp = llm_with_tools.invoke(history_messages) + print(f"最终结果:{resp}") + print(f"调用工具后的结果:{resp.content}") + ``` + +* 案例测试实战 + + ``` + #query = "9*3是多少" + #query = "今天是腾讯股价是多少" + ``` + +* 扩展功能建议 + + - **多语言支持**:增加对多种语言的输入和输出支持。 + - **更多工具集成**:集成更多类型的工具,如翻译工具、图像识别工具等。 + - **用户反馈机制**:允许用户对系统生成的答案进行评价,优化模型性能。 + +### 大模型链路调试平台之LangSmith实战 + +#### 大模型LLM调用链路分析和LangSmith介绍 + +* 需求背景 + + * 开发基于大语言模型(LLM)的智能体时,会遇到以下问题: + * 调试困难 + * LLM 的输出不可预测,难以追踪中间步骤(如思维链、工具调用)。 + * 错误定位耗时(如工具返回异常,但不知道具体哪一步出错)。 + * 测试复杂 + * 需要验证不同输入场景下的输出稳定性。 + * 手动测试效率低,缺乏自动化验证。 + * 监控缺失 + * 生产环境中的智能体行为难以追踪(如 API 调用延迟、错误率)。 + * 无法分析用户高频问题或模型性能瓶颈。 + +* **LangSmith 是什么** + + * **LangChain官方出品**的LLM应用开发调试与监控平台 + + * 地址:https://smith.langchain.com/ + + * 解决大模型应用开发中的**调试困难**、**效果追踪**、**生产监控**三大痛点 + + * 类似:Java中的Spring Boot Actuator(监控)、ELK Stack(日志分析) + + * 核心功能说明 + + * 调试与追踪 + - 记录智能体完整执行链路(LLM 调用、工具调用、中间结果)。 + - 可视化展示每一步的输入输出和耗时。 + - 统计成功率、延迟等关键指标。 + * 生产监控 + - 实时监控 API 调用异常(如超时、错误响应)。 + - 分析用户高频请求和模型性能趋势 + + * 功能矩阵 + + | 功能模块 | 作用描述 | 典型应用场景 | + | :----------: | :---------------------------------------: | :---------------------: | + | 运行追踪 | 记录LLM调用链的完整执行过程 | 调试复杂Chain/Agent逻辑 | + | 提示工程分析 | 对比不同Prompt模板的实际效果 | 优化系统提示词 | + | 版本对比 | 比对不同模型版本或参数的表现 | 模型升级效果验证 | + | 生产监控 | 实时监控API调用指标(延迟、成本、错误率) | 服务健康状态跟踪 | + | 团队协作 | 共享调试结果和监控仪表盘 | 多人协作开发LLM应用 | + +* LangSmith 系统架构 + + * SDK捕获所有LLM相关操作 + * 加密传输到LangSmith服务端 + * 数据存储与索引 + * 可视化界面动态查询 + + ``` + [Your Application] + │ + ▼ + [LangChain SDK] → [LangSmith API] + │ │ + ▼ ▼ + [LLM Providers] [Trace Storage] + │ │ + ▼ ▼ + [External Tools] [Analytics Engine] + ``` + + + +* 相关环境实战 + + * 注册地址:https://smith.langchain.com/ + + ![image-20250329170410782](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20250329170410782.png) + + * 配置项目前准备 + + * 项目安装依赖 + + ``` + pip install langsmith==0.3.19 + ``` + + * 生成密钥,记得保存 + + * 配置项目名称(不存在的话会自动新建) + + ![image-20250329170551464](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20250329170551464.png) + + +#### 大模型调用接入LangSmith分析实战 + +**简介: 大模型调用接入LangSmith分析实战** + +* LangChain接入调用链路分析实战 + + * 项目配置 + + ``` + #示例配置 + import os + os.environ["LANGCHAIN_TRACING_V2"] = "true" + os.environ["LANGCHAIN_API_KEY"] = "ls_xxxxx" # 替换为实际Key + os.environ["LANGCHAIN_PROJECT"] = "My_Project" # 自定义项目名 + os.environ["LANGSMITH_ENDPOINT"] = "https://api.smith.langchain.com" #后端接口路径 + ``` + + ```python + #真正配置 + import os + os.environ["LANGCHAIN_TRACING_V2"] = "true" + os.environ["LANGCHAIN_API_KEY"] = "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" + os.environ["LANGSMITH_ENDPOINT"] = "https://api.smith.langchain.com" + os.environ["LANGSMITH_PROJECT"] = "agent_v1" + ``` + + * 如果需要本地私有化部署 langfuse + +* 案例实战分析 + + * 单独案例测试 + + ```python + import os + os.environ["LANGCHAIN_TRACING_V2"] = "true" + os.environ["LANGCHAIN_API_KEY"] = "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" + os.environ["LANGSMITH_ENDPOINT"] = "https://api.smith.langchain.com" + os.environ["LANGSMITH_PROJECT"] = "xdclass_test_v1" + + from langchain_openai import ChatOpenAI + import logging + logging.basicConfig(level=logging.DEBUG) + + # 初始化大模型 + llm = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + resp = llm.invoke("什么是智能体") + print(resp.content) + ``` + + ![image-20250329175530690](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20250329175530690.png) + + * 进阶案例测试【选择之前的案例代码-43.2】 + + ![image-20250329181733895](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20250329181733895.png) + + +### 模型Agent智能体进阶开发实战 + +#### 大模型智能体之CoT思维链和ReAct推理行动 + +* 需求背景:为什么需要CoT/ReAct? + + * 问题场景:传统大模型直接输出结果,但复杂任务(数学推理/多步骤决策)易出错 + + * 核心需求:让模型展示思考过程 + 动态调整策略 + + * 类比理解: + + * 普通模型 → 考试直接写答案 + + * CoT → 在试卷上写解题步骤 + + * ReAct → 边查公式手册边解题 + + + +* 什么是CoT(思维链,Chain of Thought) + + * **核心机制**:通过显式生成推理步骤得出最终答案 + + ```python + """ + 问题:...[输入问题]... + 思考步骤: + 1. 第一步分析(如提取关键数据) + 2. 第二步计算(如公式应用) + 3. ...(更多步骤) + 最终答案:...[结论]... + """ + + 比如问题:A超市苹果每斤5元,买3斤送1斤;B超市同品质苹果每斤6元但买二送一。买8斤哪家更划算? + CoT推理: + 1. A超市实际获得:3+1=4斤,花费3×5=15元 → 单价15/4=3.75元/斤 + 2. 买8斤需要购买两次活动:4×2=8斤 → 总价15×2=30元 + 3. B超市每3斤花费6×2=12元 → 单价12/3=4元/斤 + 4. 买8斤需购买3次活动(3×3=9斤)花费12×3=36元 → 实际单价36/9=4元/斤 + 5. 结论:A超市更优惠 + ``` + + * 是一种促使大语言模型产生一系列中间推理步骤,进而得出最终答案的技术。 + + * 以往的大模型通常直接给出问题的答案,而 CoT 鼓励模型展示其思考过程,就像人类在解决复杂问题时逐步推导一样。 + + * 这种方法能增强模型在处理复杂推理任务时的性能,尤其是涉及算术、常识推理和符号操作的任务。 + + * 局限性 + + * **依赖模型知识**:若模型内部知识错误,推理链条也可能错误(如“太阳绕地球转”)。 + * **不适用于动态信息**:无法处理需要实时数据的问题(如“今天天气如何”)。 + * **步骤冗余**:简单问题可能因分步反而降低效率。 + + * 案例说明:算术问题 + + * 问题:小明有 5 个苹果,小红给他 3 个,然后他吃了 2 个,还剩下多少个苹果? + * 无 CoT 回答:“6 个”。 + * CoT 回答: + * 首先,小明原本有 5 个苹果,小红又给了他 3 个,那么此时他拥有的苹果数为 5 + 3 = 8 个。 + * 接着,他吃了 2 个苹果,所以剩下的苹果数是 8 - 2 = 6 个。 + * 因此,小明最后还剩下 6 个苹果。 + + * 应用场景 + + * 教育领域 + * 用于辅导学生学习数学、科学等学科的推理问题。 + * 通过展示推理过程,学生能更好地理解解题思路,提高学习效果。 + * 智能客服 + * 在回答客户复杂问题时,展示推理过程能让客户更清楚解决方案的由来,增强客户对服务的信任。 + + + +* 什么是ReAct(推理与行动,Reasoning and Acting) + + * **交互逻辑**:循环执行"思考-行动-观察"步骤 + + ```python + """ + 问题:...[输入问题]... + 思考:当前需要解决的问题是... + 行动:调用__工具名称__(参数) + 观察:工具返回结果... + 思考:根据结果分析... + (循环直至得出结论) + 答案:...[最终结果]... + """ + + 比如问题:2027年诺贝尔文学奖得主的代表作是什么? + ReAct流程: + [思考] 需要先确定最新获奖者 → 调用搜索工具 + [行动] 使用DuckDuckGo搜索"2027诺贝尔文学奖获得者" + [观察] 结果显示:法国作家安妮·艾诺 + [思考] 需要确认其代表作品 → 调用维基百科工具 + [行动] 查询维基百科"安妮·艾诺" + [观察] 主要作品:《悠悠岁月》《位置》等 + [结论] 代表作是《悠悠岁月》 + ``` + + * ReAct 是一种让大模型结合推理和行动的范式,不是前端框架React + + * 模型不仅要进行推理,还能根据推理结果调用外部工具(如搜索引擎、数据库等)来获取额外信息,从而更有效地解决问题。 + + * 它使模型能够与外部环境交互,拓展了模型的能力边界。 + + * ReAct的优势 + + * 减少幻觉:通过实时调用外部工具验证信息,降低模型编造错误答案的概率。 + * 处理复杂任务:适合多步骤问题(如数学计算、事实核查),传统单次生成容易出错。 + * 透明可解释:模型的推理过程以自然语言呈现,便于人类理解其决策逻辑。 + + * 案例说明: + + * 问题:2027 年 NBA 总冠军是哪个队伍? + * 无 ReAct 处理:由于模型训练数据可能存在时效性问题,可能无法给出准确答案或给出过时的信息。 + * ReAct 处理: + * 推理:模型意识到自己可能没有最新的 2026 年 NBA 总冠军信息,需要调用外部资源获取。 + * 行动:调用体育新闻网站的 API 或搜索引擎查询相关信息。 + * 结果:获取到最新信息后,给出准确回答,如 “2026年 NBA 总冠军是 [具体队伍名称]。 + + * 应用场景 + + * 知识问答系统 + * 处理需要实时信息或特定领域最新数据的问题,如财经数据、体育赛事结果、科技动态等。 + + * 任务自动化 + * 在自动化流程中,根据任务需求调用不同的工具和服务,如预订机票、查询物流信息、控制智能家居设备等。 + +* 技术框架对比 + + | 维度 | CoT | ReAct | + | :---------------: | :-------------------------------: | :---------------------------------: | + | **核心组件** | 纯提示工程,依赖模型知识 | 代理(Agent)+ 工具调用 | + | **数据依赖** | 仅依赖内部知识 | 可接入外部数据源/API | + | **错误修复** | 单次推理完成 | 可通过多次行动修正结果 | + | **适用场景** | 理论推导/数学计算 | 实时信息查询/复杂任务分解 | + | **计算开销** | 低 | 中高(涉及外部调用) | + | **LangChain组件** | `ChatPromptTemplate` + `LLMChain` | `Agent` + `Tools` + `AgentExecutor` | + | **优势** | 简单、透明 | 实时交互,扩展性强 | + | **缺点** | 无法获取最新信息 | 依赖工具API的稳定性 | + +* 应用场景指南 + + | 场景类型 | 推荐方法 | 原因说明 | + | :--------------: | :------: | :------------------------: | + | 数学/逻辑推理 | CoT | 无需外部数据,单步推理高效 | + | 实时数据获取 | ReAct | 需要调用搜索/API工具 | + | 多步骤决策任务 | ReAct | 支持动态调整执行路径 | + | 知识密集型问答 | CoT | 依赖模型内部知识库 | + | 复杂问题分解执行 | 混合架构 | 兼顾推理与执行效率 | + +* **开发建议**: + + * **方法选择原则**: + - 纯推理 → CoT + - 需实时数据 → ReAct + * **提示工程技巧**: + - CoT需明确定义步骤格式 + - ReAct要严格规范行动命名(如`调用API名称_参数`) + * **常见陷阱**: + - CoT可能产生错误中间步骤 + - ReAct需处理API调用失败情况 + * 通过`verbose=True`参数观察两种方法的执行过程差异 + * LangChain框架的模块化设计,可以灵活组合这两种技术,适应不同场景需求 + + +#### 大模型的Zero-Shot和Few-Shot案例实战 + +**简介: 大模型的Zero-Shot和Few-Shot案例讲解** + +* **零样本(Zero-Shot)学习** + + * 模型在**没有特定任务训练数据**的情况下,直接通过预训练知识和自然语言理解能力完成任务。 + * 例如,直接要求模型生成从未见过的任务结果(如翻译、分类)。 + * **核心原理**:依赖大模型在预训练阶段学习到的通用知识泛化能力 + * 示例 + - 用户输入:“将这句话翻译成中文:Hello, how are you?” + - 模型输出:“你好” + +* **少量样本(Few-Shot)学习【照猫画虎】** + + - 模型通过**少量示例(通常3-5个)** 快速理解任务格式和需求,提升任务表现。 + + - 例如,提供几个问答示例后,模型能模仿格式回答新问题。 + + - **核心原理**:通过示例激发模型的上下文学习(In-Context Learning)能力,提升输出准确性和一致性。 + + - 示例 + + ``` + 输入:“苹果 -> 水果”,输出:“香蕉 -> 水果” + 输入:“汽车 -> 交通工具”,输出:“飞机 -> 交通工具” + 输入:“猫 ->”,输出:“动物” + ``` + +* 应用场景对比 + + | **场景** | **零样本(Zero-Shot)** | **少量样本(Few-Shot)** | + | :----------------: | :------------------------: | :------------------------------------------: | + | **适用任务复杂度** | 简单任务(如翻译、分类) | 复杂任务(如逻辑推理、特定格式生成) | + | **数据依赖** | 无需示例 | 需要少量高质量示例 | + | **输出可控性** | 较低(依赖模型预训练知识) | 较高(通过示例明确格式和规则) | + | **典型用例** | 快速原型开发、通用问答 | 领域特定任务(如法律文档解析、医疗术语抽取) | + +* 案例实战 + + * 零样本学习案例:直接通过指令调用大模型生成答案 + + ```python + from langchain_openai import ChatOpenAI + from langchain_core.prompts import ChatPromptTemplate + + # 零样本提示模板 + zero_shot_prompt = ChatPromptTemplate.from_messages([ + ("system", "你是一个专业翻译,将文本从{source_lang}翻译到{target_lang}"), + ("human", "{text}") + ]) + + # 初始化大模型 + llm = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + # 链式调用 + chain = zero_shot_prompt | llm + + # 执行翻译(中文→英文) + response = chain.invoke({ + "source_lang": "中文", + "target_lang": "英文", + "text": "今天天气不错,我要学习AI大模型开发" + }) + + print(response.content) + + ``` + + * 少量样本学习案例: 通过示例指导模型理解任务格式 + + ```python + from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate + from langchain_openai import ChatOpenAI + # 初始化大模型 + llm = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + # 示例数据 + examples = [ + { + "input": "苹果公司的总部在哪里?", + "output": "根据我的大量思考:苹果公司的总部位于美国加利福尼亚州的库比蒂诺(Cupertino)。" + }, + { + "input": "OpenAI的CEO是谁?", + "output": "根据我的大量思考:OpenAI的现任CEO是萨姆·阿尔特曼(Sam Altman)。" + } + ] + # 定义单条示例模板 + example_template = """ + 输入:{input} + 输出:{output} + """ + example_prompt = PromptTemplate( + template=example_template + input_variables=["input", "output"], + ) + + # 构建Few-Shot提示模板 + few_shot_prompt = FewShotPromptTemplate( + examples=examples, + example_prompt=example_prompt, + suffix="输入:{question}\n输出:", + input_variables=["question"] + ) + + # 创建Chain并调用 + few_shot_chain = few_shot_prompt | llm + response = few_shot_chain.invoke({"question": "亚马逊的创始人是谁?"}) + print(response.content) # 输出:根据我的大量思考:亚马逊的创始人是杰夫·贝索斯(Jeff Bezos)。 + ``` + +* 总结 + + * **零样本 vs 少量样本**: + - 零样本依赖模型预训练知识,适合通用任务。 + - 少量样本通过示例提升任务适配性,适合专业场景。 + * **LangChain 实现要点**: + - **零样本**:使用 `AgentType.ZERO_SHOT_REACT_DESCRIPTION` 初始化智能体。 + - **少量样本**:通过 `PromptTemplate` 设计含示例的提示词。 + * **设计原则**: + - **清晰指令**:明确任务目标和输出格式。 + - **示例质量**:少量样本的示例需覆盖典型场景。 + +#### LangChain智能体执行引擎AgentExecutor + +* 需求背景:为什么需要 AgentExecutor? + + * 问题:当智能体(Agent)需要执行多步操作(如多次调用工具、循环推理)时,开发者需手动处理: + * 执行循环:根据模型输出决定是否继续调用工具。 + * 错误处理:捕获工具调用或模型解析中的异常。 + * 流程控制:限制最大迭代次数,防止无限循环。 + * 日志记录:追踪每一步的输入、输出和中间状态。 + * 痛点: + * 代码冗余:重复编写循环和错误处理逻辑。 + * 维护成本高:复杂任务中难以保证流程稳定性。 + * 可观测性差:难以调试多步骤执行过程。 + +* `AgentExecutor` 介绍 + + * LangChain 提供的智能体执行引擎,封装了执行循环、错误处理和日志追踪,让开发者聚焦业务逻辑。 + + * 核心功能 + + | **功能** | **说明** | + | :--------------: | :----------------------------------------------------------: | + | **执行循环控制** | 自动迭代执行智能体的 `思考 -> 行动 -> 观察` 流程,直到满足终止条件。 | + | **错误处理** | 捕获工具调用异常、模型解析错误,支持自定义重试或回退逻辑。 | + | **迭代限制** | 通过 `max_iterations` 防止无限循环(如模型陷入死循环)。 | + | **日志与追踪** | 记录每一步的详细执行过程(需设置 `verbose=True`),支持集成 LangSmith。 | + | **输入输出处理** | 统一格式化最终结果,隐藏中间步骤细节(除非显式要求输出)。 | + + * 关键参数 + + | **参数** | **作用** | + | :-------------------------: | :----------------------------------------------------------: | + | `agent` | 绑定的智能体实例(如 `create_react_agent` 的返回值)。 | + | `tools` | 智能体可调用的工具列表。 | + | `verbose` | 是否打印详细执行日志(调试时建议开启)。 | + | `max_iterations` | 最大迭代次数,防止死循环。 | + | `handle_parsing_errors` | 自动处理模型输出解析错误(如返回无效工具名),可设置为 `True` 或自定义函数。 | + | `return_intermediate_steps` | 是否返回中间步骤结果(用于调试或展示完整链路)。 | + +* 使用场景:何时需要 `AgentExecutor` + + * **多步骤任务**: + + - 例如:先调用搜索工具获取数据,再调用计算工具处理结果 + + ```python + # 示例:计算商品折扣价 + "思考:需要先获取商品价格,再计算折扣。" + "行动1:调用 get_price(商品A)" + "观察1:价格=100元" + "行动2:调用 calculate_discount(100, 20%)" + "观察2:折扣价=80元" + ``` + + * **工具依赖场景**: + + - 例如:预订航班需要先查询航班号,再调用支付接口。 + + * **复杂推理任务**: + + - 例如:解决数学问题需多次尝试不同公式。 + + * **生产环境部署**: + + - 需保证服务稳定性(自动处理超时、限流等异常) + +* 伪代码参考 + + ```python + from langchain.agents import AgentExecutor + + agent_executor = AgentExecutor( + agent=agent, + tools=tools, + verbose=True, + max_iterations=3, + handle_parsing_errors=True + ) + + # 执行查询(自动处理循环和错误) + response = agent_executor.invoke({"input": "北京的气温是多少华氏度?"}) + print(response["output"]) # 输出:25℃ = 77℉ + ``` + + +#### LangChain智能体之initialize_agent开发实战 + +* 需求背景 + + | 手工调用工具的问题 | `initialize_agent`解决方案 | + | :----------------------: | :--------------------------: | + | 需要手动编写工具选择逻辑 | 自动根据输入选择最合适的工具 | + | 缺乏错误重试机制 | 内置异常处理和重试策略 | + | 输出格式不统一 | 标准化响应格式 | + | 难以处理多工具协作场景 | 自动编排工具调用顺序 | + + * 手动执行需要开发者自己处理任务分解、工具选择、参数生成、结果整合等步骤 + + * Agent利用大模型的推理能力自动完成这些步骤,提高了开发效率和系统的灵活性。 + + * 解决方案: + + * langchain内置多个方法快速创建智能体,能减少手动编码的复杂性,提升系统的智能性和适应性。 + * 包括自动化任务分解、动态工具选择、错误处理、结果整合等 + * 主要包含以下核心方法: + * `initialize_agent`:通用初始化方法,快速构建智能体(兼容旧版)返回AgentExecutor。 + * `create_react_agent`:基于 ReAct 框架的智能体,支持多步推理。 + * `create_tool_calling_agent`:专为工具调用优化的智能体,支持结构化输出。 + * 其他方法:如 `create_json_agent`(处理 JSON )、`create_openai_tools_agent`(适配 OpenAI 工具调用格式) + + + ![12](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/12.png) + +* `initialize_agent` 方法介绍 + + * 通过封装智能体的决策逻辑和工具调度,旧版方法,未来可能被弃用 + + * **适用场景**:快速原型开发、简单任务处理 + + * 实现 + + * 自动化工具选择:根据输入动态调用合适的工具。 + * 流程标准化:统一处理错误、重试、结果格式化。 + * 简化开发:一行代码完成智能体初始化。 + + * 语法与参数详解 + + ```python + from langchain.agents import initialize_agent + + def initialize_agent( + tools: Sequence[BaseTool], # 可用工具列表 + llm: BaseLanguageModel, # 大模型实例 + agent: Optional[AgentType] = None, # Agent类型 + verbose: bool = False, # 是否显示详细过程 + ) -> AgentExecutor: + ``` + + * **关键参数说明**:`agent_type` + + | AgentType | 适用场景 | 特点 | + | :-----------------------------------------: | :------------------------------------: | :-----------------------: | + | ZERO_SHOT_REACT_DESCRIPTION | 通用任务处理 | 基于ReAct框架,零样本学习 | + | STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION | 结构化输入输出 | 支持复杂参数类型 | + | CONVERSATIONAL_REACT_DESCRIPTION | 多轮对话场景 | 保留对话历史上下文 | + | SELF_ASK_WITH_SEARCH | 结合自问自答和搜索工具,适合问答场景。 | 自动生成中间问题并验证 | + +* 案例实战 + + ```python + from langchain.agents import initialize_agent, AgentType + from langchain_openai import ChatOpenAI + from langchain_core.tools import tool + from langchain.agents import initialize_agent + from langchain_community.utilities import SearchApiAPIWrapper + import os + + # 设置SearchAPI的API密钥 + os.environ["SEARCHAPI_API_KEY"] = "qQBHMQo4Rk8SihmpJjCs7fML" + # 实例化SearchApiAPIWrapper对象,用于调用搜索API + search = SearchApiAPIWrapper() + #定义搜索工具 + @tool("web_search", return_direct=True) + def web_search(query: str) -> str: + """ + 当需要获取实时信息、最新事件或未知领域知识时使用,输入应为搜索关键词 + """ + try: + results = search.results(query) # 获取前3条结果 + return "\n\n".join([ + f"来源:{res['title']}\n内容:{res['snippet']}" + for res in results['organic_results'] + ]) + except Exception as e: + return f"搜索失败:{str(e)}" + + # 使用 @tool 定义进行数学计算的工具 + @tool("math_calculator", return_direct=True) + def math_calculator(expression: str) -> str: + """用于进行数学计算,输入应该是一个有效的数学表达式,如 '2 + 3' 或 '5 * 4'""" + try: + result = eval(expression) + return str(result) + except Exception as e: + return f"计算出错: {str(e)}" + + #不同大模型效果不一样,有些会报错,不支持多个输入参数的工具 + @tool("multiply") + def multiply(a: int, b: int) -> int: + """ + 把传递的两个参数相乘 + """ + return a * b + + # 创建工具列表 + tools = [math_calculator,web_search] + + # 初始化大模型 + llm = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + + # 使用 initialize_agent 创建代理 + agent_chain = initialize_agent( + tools=tools, # 使用的工具列表 + llm=llm, # 使用的语言模型 + agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, # 指定代理类型 + verbose=True, # 打开详细日志,便于查看代理的思考过程 + handle_parsing_errors=True #自动处理解析错误,提高Agent的稳定性。 + + ) + #打印对应的智能体,更清晰底层逻辑 + print(agent_chain.agent.llm_chain) + print(agent_chain.agent.llm_chain.prompt.template) + print(agent_chain.agent.llm_chain.prompt.input_variables) + + # 测试代理 + # 需求 1: 计算 5 乘以 6 的结果 + result1 = agent_chain.invoke({"input":"计算 5 乘以 6 的结果"}) + print("计算 5 乘以 6 的结果:", result1) + + # 需求 2: 获取当前日期 + #result2 = agent_chain.run("腾讯最新的股价") + #result2 = agent_chain.invoke({"input":"腾讯最新的股价"}) + #print("腾讯最新的股价:", result2 + ``` + + +#### 个人AI助理智能体之tool_calling_agent实战 + +* `create_tool_calling_agent `方法介绍 + + * 是 LangChain 0.3 新增的智能体创建方法, 要求模型直接返回工具调用参数(如 JSON 格式),减少中间解析错误。 + + * **结构化工具调用**:显式调用工具并传递结构化参数(支持复杂数据类型) + + * **多步骤任务处理**:适合需要按顺序调用多个工具的场景 + + * **精准控制**:通过自定义 Prompt 模板指导 Agent 行为 + + * 方法参数 + + ```python + from langchain.agents import create_tool_calling_agent + + agent = create_tool_calling_agent( + llm: BaseLanguageModel, # 语言模型实例(需支持结构化输出) + tools: List[BaseTool], # 工具列表 + prompt: ChatPromptTemplate # 提示模板(需明确工具调用规则) + ) + ``` + + | 参数 | 类型 | 必填 | 说明 | + | :----: | :----------------: | :--: | :----------------------------------------------------------: | + | llm | BaseLanguageModel | 是 | 支持工具调用的模型(如 `ChatOpenAI`) | + | tools | Sequence[BaseTool] | 是 | 工具列表,每个工具需用 `@tool` 装饰器定义 | + | prompt | ChatPromptTemplate | 是 | 控制 Agent 行为的提示模板,需包含 `tools` 和 `tool_names` 的占位符 | + +* 适用场景 + + * API 集成:如调用天气查询、支付接口等需要严格参数格式的场景。 + * 多工具协作:模型需根据输入动态选择多个工具并传递参数。 + * 高精度任务:如金融计算、医疗诊断等容错率低的场景。 + +* 案例实战: 个人AI助理智能体 + + ```python + from langchain_openai import ChatOpenAI + from langchain.agents import create_tool_calling_agent, Tool, AgentExecutor + from langchain.tools import tool + from datetime import datetime + from langchain_core.prompts import ChatPromptTemplate + + # 定义获取当前日期的工具函数 + @tool + def get_current_date() -> str: + """获取当前日期""" + formatted_date = datetime.now().strftime("%Y-%m-%d") + return f"The current date is {formatted_date}" + + # 定义搜索航班的工具函数 + @tool + def search_flights(from_city: str, to_city: str, date: str) -> str: + """根据城市和日期搜索可用航班""" + return f"找到航班:{from_city} -> {to_city},日期:{date},价格:¥1200" + + # 定义预订航班的工具函数 + @tool + def book_flight(flight_id: str, user: str) -> str: + """预订指定航班""" + return f"用户 {user} 成功预订航班 {flight_id}" + + # 定义获取股票价格的函数 + def get_stock_price(symbol) -> str: + return f"The price of {symbol} is $100." + + # 创建工具列表,包括获取股票价格、搜索航班、预订航班和获取当前日期的工具 + tools = [ + Tool( + name="get_stock_price", + func=get_stock_price, + description="获取指定的股票价格" + ), + search_flights, + book_flight, + get_current_date + ] + + # 初始化大模型 + llm = ChatOpenAI( + model_name="qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # 定义聊天提示模板 + prompt = ChatPromptTemplate.from_messages( + [ + ("system", "你是一个AI助手,必要时可以调用工具回复问题"), + ("human", "我叫老王,经常出差,身份证号是 4414231993210223213332"), + #("placeholder", "{chat_history}"), + ("human", "{input}"), + ("placeholder", "{agent_scratchpad}"), + ] + ) + + # 创建代理 + agent = create_tool_calling_agent(llm, tools, prompt) + + # 初始化代理执行器 , verbose=True可以看到思考明细, return_intermediate_steps返回中间结果 + agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True,return_intermediate_steps=True) + + + # 运行代理并获取结果 + result = agent_executor.invoke({"input": "苹果股票是多少?根据我的行程,帮我查询下明天的航班,从广州去北京,并定机票"}) + print(f"最终结果:{result}" + ``` + + +#### 旅游规划智能体之react_agent实战 + +* **ReAct 框架**: + + * **Re**asoning + **Act**ing 是一种结合推理和行动的智能体设计模式 + + * 由以下步骤循环组成: + + * **推理(Reason)**:分析当前状态,决定下一步行动(如调用工具或直接回答)。 + * **行动(Act)**:执行选定的操作(如调用工具、查询数据)。 + * **观察(Observe)**:获取行动结果,更新状态,进入下一轮循环。 + + ``` + [Thought] <-- 推理阶段(分析当前状况) + [Action] <-- 行动阶段(调用工具) + [Observation] <-- 环境反馈(工具返回结果) + ``` + +* **`create_react_agent`** + + * LangChain 提供的专用方法,用于创建基于 ReAct 框架的智能体 + + * 适合需要**多步动态决策**的任务(如复杂问答、数学问题求解)。 + + * 方法参数与语法 + + ```python + from langchain.agents import create_react_agent + + agent = create_react_agent( + llm: BaseLanguageModel, # 必须支持 ReAct 格式 + tools: Sequence[BaseTool],# 工具集(需详细文档) + prompt: ChatPromptTemplate # 必须包含 ReAct 特殊标记 + ) -> Runnable + ``` + + * 参考使用的提示模板结构 + + ```python + # 官方推荐模板(从 hub 获取) + prompt = hub.pull("hwchase17/react") + + # 模板核心内容示例: + """ + Answer the following questions using the following tools: + {tools} + Use the following format: + + Question: the input question + Thought: 需要始终进行的思考过程 + Action: 要执行的动作,必须是 [{tool_names}] 之一 + Action Input: 动作的输入 + Observation: 动作的结果 + ...(重复思考/行动循环) + Final Answer: 最终答案 + """ + ``` + + * 与其它方法的对比选型 + + | 场景特征 | 推荐方法 | 原因说明 | + | :--------------: | :-----------------------: | :--------------------: | + | 需要逐步推理 | create_react_agent | 显式思考链支持复杂逻辑 | + | **严格参数传递** | create_tool_calling_agent | 结构化输入更可靠 | + | 快速简单任务 | initialize_agent | 开箱即用最简配置 | + | 需要自我修正 | create_react_agent | 错误后可重新推理 | + | 与人类协作调试 | create_react_agent | 完整思考链易读性好 | + + * 适用场景 + + * 多步骤任务:例如:“查询北京的气温,并计算对应的华氏度。” + * 动态工具选择:根据中间结果决定下一步调用哪个工具。 + * 复杂推理任务:例如:“如果明天下雨,推荐室内活动;否则推荐户外活动。 + +* 案例实战:智能推荐助手 + + ```python + from langchain_openai import ChatOpenAI + from langchain.tools import tool + from langchain_community.utilities import SearchApiAPIWrapper + import os + from langchain_core.prompts import PromptTemplate + from langchain.agents import create_react_agent, AgentExecutor + @tool + def get_weather(city: str) -> str: + """获取指定城市的天气""" + # 模拟数据 + weather_data = { + "北京": "晴,25℃", + "上海": "雨,20℃", + "广州": "多云,28℃" + } + return weather_data.get(city, "暂不支持该城市") + + @tool + def recommend_activity(weather: str) -> str: + """根据天气推荐活动""" + if "雨" in weather: + return "推荐室内活动:博物馆参观。" + elif "晴" in weather: + return "推荐户外活动:公园骑行。" + else: + return "推荐一般活动:城市观光。" + + # 定义搜索工具 + os.environ["SEARCHAPI_API_KEY"] = "qQBHMQo4Rk8SihmpJjCs7fML" + @tool("web_search", return_direct=True) + def web_search(query: str) -> str: + """ + 当需要获取实时信息、最新事件或未知领域知识时使用,输入应为搜索关键词 + """ + try: + search = SearchApiAPIWrapper() + results = search.results(query) # 获取前3条结果 + return "\n\n".join([ + f"来源:{res['title']}\n内容:{res['snippet']}" + for res in results['organic_results'] + ]) + except Exception as e: + return f"搜索失败:{str(e)}" + + tools = [get_weather, recommend_activity, web_search] + + + # 初始化大模型 + llm = ChatOpenAI( + model_name="qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + + # 使用 LangChain 预定义的 ReAct 提示模板,https://smith.langchain.com/hub/hwchase17/react + #from langchain import hub + #prompt = hub.pull("hwchase17/react") + + # 模板内容示例: + template = """ + Answer the following questions as best you can. You have access to the following tools: + {tools} + Use the following format: + Question: the input question you must answer + Thought: you should always think about what to do + Action: the action to take, should be one of [{tool_names}] + Action Input: the input to the action + Observation: the result of the action + ... (this Thought/Action/Action Input/Observation can repeat N times) + Thought: I now know the final answer + Final Answer: the final answer to the original input question + + Begin! + + Question: {input} + Thought:{agent_scratchpad} + """ + + prompt = PromptTemplate.from_template(template) + + # 创建 ReAct 智能体 + agent = create_react_agent(llm, tools, prompt) + agent_executor = AgentExecutor( + agent=agent, + tools=tools, + verbose=True, # 打印详细执行过程 + max_iterations=3, # 限制最大迭代次数, + handle_parsing_errors=lambda _: "请检查输入格式", # 错误处理 + return_intermediate_steps=True # 保留中间步骤 + ) + + # 执行查询 + response = agent_executor.invoke({ + "input": "我在北京玩3天,根据天气推荐活动, 顺便查询腾讯的股价是多少" + }) + print(response) + ``` + + + + +#### LLM综合实战-文档网络智能问答助手开发 + +* 需求说明 + + * 智能体核心功能 + + * **多模态知识整合** + * 深度结合 **Milvus 向量数据库** 的本地文档知识与 **实时网络搜索能力**,实现对复杂问题的分步解析与回答。 + * 支持同时处理 **结构化文档问答**(如技术框架详解)和 **实时信息查询**(如股价、日期等动态数据)。 + + * **智能工具决策** + - 自动判断问题类型: + - **文档相关问题**(如 "什么是Milvus?")调用 **Milvus 向量检索工具**,精准匹配本地知识库内容。 + - **实时信息需求**(如 "腾讯股价")触发 **Web 搜索工具**,获取最新数据。 + - 支持 **多问题串联回答**,例如一次性处理包含技术解释、时间查询、股价查询的复合任务。 + * **交互式回答增强** + - 通过 **LangChain Agent 框架** 的动态决策流程,展示问题拆解与工具调用的全过程(如中间思考步骤)。 + - 提供 **结构化输出**,清晰标注每个答案的来源(如 "来自 Milvus 文档" 或 "来自实时搜索")。 + +* 技术亮点: + + * 工具链集成:无缝融合 Milvus 向量检索 + 网络搜索API + 大模型推理(如 Qwen-Plus)。 + * 可视化流程:通过 LangSmith 追踪 Agent 的决策路径 + * 自定义提示模板:支持灵活调整 Agent 的行为逻辑(如切换 create_tool_calling_agent 或 create_react_agent 模式)。 + +* 应用场景 + + * 技术文档问答:快速解析 Milvus 版本、功能、与 LangChain 的集成方法。 + * 实时数据查询:动态获取股票价格、当前日期等时效性信息。 + * 多步骤问题处理:如 "解释Milvus的工作原理,同时比较其与Faiss的优劣,最后给出GitHub最新教程链接"。 + +* 功能效果演示 + + ![image-20250331231807616](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20250331231807616.png) + +* 编码实战 + +```python +from langchain_community.utilities import SearchApiAPIWrapper +from langchain_core.tools import tool +from langchain.agents import AgentExecutor, create_tool_calling_agent,create_react_agent + +from langchain.tools.retriever import create_retriever_tool +from langchain_milvus import Milvus + +from langchain.prompts import PromptTemplate,ChatPromptTemplate +from langchain_community.embeddings import DashScopeEmbeddings +from langchain_openai import ChatOpenAI +import os +from pydantic import Field + +os.environ["LANGCHAIN_TRACING_V2"] = "true" +os.environ["LANGCHAIN_API_KEY"] = "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" +os.environ["LANGSMITH_ENDPOINT"] = "https://api.smith.langchain.com" +os.environ["LANGSMITH_PROJECT"] = "agent_v1" + + +# 配置搜索工具 +os.environ["SEARCHAPI_API_KEY"] = "qQBHMQo4Rk8SihmpJjCs7fML" +search = SearchApiAPIWrapper() +@tool("web_search") +def web_search(query: str) -> str: + """当需要获取实时信息、最新事件或未知领域知识时使用,输入应为搜索关键词""" + try: + results = search.results(query) # 获取前3条结果 + return "\n\n".join([ + f"来源:{res['title']}\n内容:{res['snippet']}" + for res in results['organic_results'] + ]) + except Exception as e: + return f"搜索失败:{str(e)}" + + +#初始化嵌入模型 +embeddings = DashScopeEmbeddings( + model="text-embedding-v2", # 第二代通用模型 + max_retries=3, + dashscope_api_key="sk-005c3c25f6d042848b29d75f2f020f08" +) + +vector_store = Milvus( + embeddings, + connection_args={"uri": "http://47.119.128.20:19530"}, + collection_name='doc_qa_db', +) + +#获取检索器 +retriever = vector_store.as_retriever() +retriever_tool = create_retriever_tool( + retriever, + "milvus_retriever", + "搜索有关 Milvus 的信息。对于任何有关 Milvus 的问题,你必须使用这个工具!", +) + + +# 初始化大模型 +llm = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 +) + +tools = [web_search, retriever_tool] + +# #构建提示模板 ,采用create_tool_calling_agent的提示词模版 +``` + + + +* 编码实战 + + ```python + # #构建提示模板 ,采用create_tool_calling_agent的提示词模版 + # prompt = ChatPromptTemplate.from_messages([ + # ("system", "你是一个AI文档助手,拥有多个工具,必要时可以利用工具来回答问题。"), + # ("user", "{input}"), + # ("placeholder", "{agent_scratchpad}") + # ]) + # # 创建Agent执行器 + # agent = create_tool_calling_agent(llm, tools,prompt) + + #构建提示模板 ,采用create_react_agent的提示词模版 + prompt = PromptTemplate.from_template('''Answer the following questions as best you can. You have access to the following tools: + {tools} + + Use the following format: + + Question: the input question you must answer + Thought: you should always think about what to do + Action: the action to take, should be one of [{tool_names}] + Action Input: the input to the action + Observation: the result of the action + ... (this Thought/Action/Action Input/Observation can repeat N times) + Thought: I now know the final answer + Final Answer: the final answer to the original input question + + Begin! + + Question: {input} + Thought:{agent_scratchpad}''') + agent = create_react_agent(llm, tools,prompt) + + #创建Agent执行器 + agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True,handle_parsing_errors=True,return_intermediate_steps=True) + + def run_question(question: str): + print(f"\n问题:{question}") + result = agent_executor.invoke({"input": question}) + print(f"\nllm-result:{result}") + print(f"\n答案:{result['output']}\n{'='*50}") + ``` + +* 案例测试 + + * 提问输入问题 + + ``` + run_question("中文回答下面3个问题:第一个:什么是Milvus,最新的版本是多少,如何整合LangChain框架,其与Faiss的优劣,最后给出GitHub最新教程链接; 第二个:现在的日期是几号;第三个:腾讯的股价是多少") + ``` + + * LangSmith调用链路分析 + + ![image-20250401104847864](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20250401104847864.png) + + * 切换不同创建agent方法和提示词进行测试 + +* 打印输出中间思考步骤 + + * 使用 AgentExecutor 的 `return_intermediate_steps` 参数 + + ```python + #print("最终结果:", result["output"]) + #print("中间步骤:", result["intermediate_steps"]) + + def run_question(question: str): + print(f"\n问题:{question}") + result = agent_executor.invoke({"input": question}) + print(f"\nllm-result:{result}") + print(f"\n答案:{result['output']}\n{'='*50}") + # 输出完整执行轨迹 + print("=== 完整执行链 ===") + for step in result["intermediate_steps"]: + print(f"Action: {step[0].tool}") + print(f"Input: {step[0].tool_input}") + print(f"Observation: {step[1]}\n") + ``` + +* 总结说明 + + * 在AI智能体开发中,不同的任务需要不同的交互逻辑,因此需要不同类型的智能体来高效处理不同任务。 + * 例如: + - 有些任务需要智能体先“思考”再行动(如解数学题)。 + - 有些任务需要直接调用工具完成(如查天气、调用计算器) + * **关键选择点**: + * 任务是否需要分解步骤 → 选`create_react_agent`; + * 任务是否单一明确 → 选`create_tool_calling_agent`。 + * **进阶技巧**: + * 混合使用两种智能体(如先用React分解任务,再调用Tool-Calling执行子任务) + * 共同点 + - 均基于大模型(如LLM大模型)驱动。 + - 依赖预定义的工具集(Tools)。 + * **底层关系**: + * `create_tool_calling_agent` 可视为 `create_react_agent` 的简化版(跳过显式推理步骤)。 + + + +### 老王忘不了的痛-大模型存储记忆实战 + +#### LLM大模型存储记忆功能介绍和应用场景 + +* 需求背景:为什么需要存储记忆功能? + + * 长对话上下文遗忘问题 + + ``` + # 示例:第二次提问时模型已“失忆” + user_input1 = "我叫张三" + ai_response1 = "你好张三!" + user_input2 = "我叫什么名字?" + ai_response2 = "抱歉,我不知道您的名字。" # 期望回答“张三” + ``` + + * 大模型(如 GPT-4)单次对话的上下文窗口有限(通常为 4k-128k tokens),导致多轮对话中容易丢失早期信息。 + * 例如,用户询问 “如何制作蛋糕” 后接着问 “需要烤箱吗” + * 模型若无法记住前一轮对话,可能回答 “需要烤箱” 但忘记蛋糕配方的关键步骤。 + + * 个性化服务与用户偏好记忆 + + * 在客服、教育、医疗等场景中,用户需要模型记住个人信息(如姓名、病史)或历史行为(如订单记录、学习进度)。 + * 例如,医疗助手需根据患者的历史诊断结果提供建议。 + + * 复杂任务的状态管理 + + * 涉及多步骤的任务(如旅行规划、代码调试)需要模型跟踪中间状态。 + + * 例如,用户要求 “规划上海到北京的三天行程”,模型需记住已推荐的景点、交通方式等。 + +* LangChain方法 + + * 短期记忆:通过 Memory 模块存储对话历史,确保模型在多轮交互中保持连贯 + + * 长期记忆:将用户数据存储在外部数据库或向量数据库(如 Milvus、Pinecone),实现跨会话的长期记忆 + +* 记忆功能的核心设计 + + * 两种类型 + + | 维度 | 短期记忆 | 长期记忆 | + | :------------: | :----------------------------------: | :-----------------------------------------: | + | **存储方式** | 内存缓存, 模型输入中的历史消息 | 数据库持久化存储, 向量库/文件 | + | **容量限制** | 受上下文窗口限制(如4k-128k tokens) | 理论上无上限 | + | **访问速度** | 毫秒级 | 百毫秒级(依赖检索算法) | + | **典型应用** | 对话连贯性保持, 即时对话、单次任务 | 个性化服务、用户画像构建,跨会话记忆、知识库 | + | **实现复杂度** | 低 | 高 | + | **成本** | 低(无额外存储开销) | 中高(需维护存储系统) | + | **示例** | 聊天中记住前3轮对话 | 用户资料、项目历史记录 | + + * 记忆的实现方式 + + * **短期记忆**:通过拼接历史消息实现(`[用户: 你好][AI: 你好!]`)。 + * 长期记忆 + - **结构化存储**:用数据库记录关键信息(如用户喜好)。 + - **向量化存储**:将文本转为向量存入向量库(如Milvus)。 + - **混合模式**:短期记忆 + 长期检索增强(RAG) + +* 应用场景与案例 + + * 个性化教育助手 (伪代码) + + ```python + from langchain.memory import VectorStoreRetrieverMemory + + # 初始化记忆系统 + retriever = vectorstore.as_retriever(search_kwargs={"k": 3}) + memory = VectorStoreRetrieverMemory(retriever=retriever) + + # 运行示例 + memory.save_context( + {"input": "我的学习目标是掌握微积分"}, + {"output": "目标已记录,将推荐相关资源"} + ) + memory.save_context( + {"input": "请解释洛必达法则"}, + {"output": f"{explanation} 已添加到你的学习清单"} + ) + + # 后续对话 + query = "根据我的目标推荐学习资料" + relevant_memories = memory.load_memory_variables({"query": query}) + # 返回微积分相关记忆 + + ``` + + * 电商推荐引擎 + + ```python + from langchain.retrievers import TimeWeightedVectorStoreRetriever + + # 带时间权重的记忆系统 + retriever = TimeWeightedVectorStoreRetriever( + vectorstore=vectorstore, + decay_rate=0.95, # 记忆衰减系数 + k=5 + ) + + # 记忆示例数据 + retriever.add_documents([ + Document(page_content="用户2026-12-01购买手机", metadata={"type": "purchase"}), + Document(page_content="用户2027-03-15浏览笔记本电脑", metadata={"type": "browse"}), + Document(page_content="用户2027-06-20退货耳机", metadata={"type": "return"}) + ]) + + # 获取最新加权记忆 + relevant_memories = retriever.get_relevant_documents("用户兴趣分析") + ``` + + +* 大模型长短期记忆选择决策树 + * 基础用法:短期对话记忆 + * 进阶用法:长期记忆 + 向量数据库 + * 高级用法:多用户隔离记忆(会话级) + +![1](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/1-3479667.png) + + + +#### LLM存储记忆功能之BaseChatMemory实战 + +* `BaseChatMemory` 介绍 + + * 是 LangChain 中所有**聊天型记忆模块的基类**,定义了记忆存储和检索的通用接口。 + + ``` + from langchain.memory.chat_memory import BaseChatMemory + ``` + + * 可通过继承此类实现自定义记忆逻辑(如过滤敏感信息、动态清理策略) + + * 注意:部分API虽然过期,但也需要知道核心思想, 新旧我们都有讲 + + * 核心作用 + + * **标准化接口**:统一 `save_context()`(保存上下文)和 `load_memory_variables()`(加载记忆)方法。 + * **状态管理**:维护对话历史(`chat_memory` 属性),支持消息的增删改查。 + * **扩展性**:允许开发者覆盖默认行为(如自定义存储格式、加密数据) + + * 关键属性 + + * `chat_memory` + * 存储对话消息的容器,类型为 ChatMessageHistory,包含 messages 列表 + * 每条消息为 BaseMessage 对象,如 HumanMessage、AIMessage + + * 核心方法 + + * `save_context` 保存用户输入和模型输出到 chat_memory。 + + ``` + memory.save_context({"input": "你好"}, {"output": "你好!有什么可以帮您?"}) + # 等价于: + memory.chat_memory.add_user_message("你好") + memory.chat_memory.add_ai_message("你好!有什么可以帮您?") + ``` + + * `load_memory_variables` 返回当前记忆内容(通常为 {"history": "对话历史字符串"} + + ``` + variables = memory.load_memory_variables({}) + print(variables["history"]) # 输出:Human: 你好\nAI: 你好!有什么可以帮您? + ``` + + * `clear()` 清空所有记忆 + + ``` + #调用 + chat_memory.clear() + ``` + +* `BaseChatMemory`的子类 + + | **类名** | **说明** | + | :------------------------------: | :----------------------------------------------------------: | + | `ConversationBufferMemory` | 直接存储原始对话历史(继承 `BaseChatMemory`)。 | + | `ConversationBufferWindowMemory` | 仅保留最近 N 轮对话(通过 `k` 参数控制,覆盖消息存储逻辑)。 | + | `ConversationSummaryMemory` | 存储模型生成的对话摘要(覆盖 `load_memory_variables` 生成摘要)。 | + | 自定义类 | 继承 `BaseChatMemory`,按需重写 `save_context` 或 `load_memory_variables` | + +* 案例实战 + + * `ConversationBufferMemory` + + ```python + from langchain.memory import ConversationBufferMemory + + # 初始化对话缓冲记忆 + memory = ConversationBufferMemory( + memory_key="chat_history", #存储进去的Key,和获取的时候需要保持一致 + return_messages=True, + ) + + # 添加用户消息和 AI 消息 + memory.chat_memory.add_user_message("你的名字是什么") + memory.chat_memory.add_ai_message("二当家") + + # 加载记忆内容 + memory_variables = memory.load_memory_variables({}) + print("记忆内容:", memory_variables) + ``` + + * `ConversationBufferWindowMemory ` + + * 滑动窗口式对话记忆, 此类在对话记录中引入窗口机制,仅保存最近的 `k` 条对话历史。 + * **k 参数**:定义窗口大小,表示最多保留的对话记录条数, 适合长对话或对历史要求不高的应用。 + + ```python + from langchain.memory import ConversationBufferWindowMemory + + # 初始化窗口记忆,设置窗口大小为 2 + memory = ConversationBufferWindowMemory(k=2, memory_key="chat_history") + + # 保存一些消息 + memory.save_context({"input": "你叫什么名字"}, {"output": "你好,我是三当家"}) + memory.save_context({"input": "小滴课堂怎么样"}, {"output": "小滴课堂是适合初学者的计算机学习平台"}) + memory.save_context({"input": "好的,我去了解下"}, {"output": "希望对你有帮助!"}) + + # 获取窗口内的对话历史 + window_history = memory.load_memory_variables({}) + print("当前窗口内的对话历史:", window_history) + ``` + + + + + + +#### 【面试题】LLM存储优化-大量长对话如何解决 + +* 面试官 + * 传统对话系统每次交互独立,模型无法感知历史对话内容,如何解决? + * 长对话超出模型的Token处理能力,导致信息截断或性能下降,如何解决? + +* **大模型场景题目的需求背景【重要】** + + * **大模型的上下文限制** + * 大语言模型(如GPT-4、DeepSeek等虽然能处理复杂的对话任务 + * 但其输入长度存在限制(如Token上限),无法直接存储长期对话历史 + * **对话连贯性需求** + * 实际应用中(如客服系统、智能助手),用户问题常依赖上下文。 + * 例如,用户先问“人工智能的定义”,再要求“详细说明”,模型需基于历史回答才能生成合理响应 + * 资源优化需求 + * 直接存储完整对话历史会占用大量内存或数据库资源,且频繁传递完整上下文会增加计算成本。 + + | 问题类型 | 具体表现 | 后果 | + | :------: | :----------------------------: | :------------------------: | + | 技术限制 | 长对话超出模型上下文窗口 | 关键信息丢失,回答质量下降 | + | 效率瓶颈 | 全量历史数据检索耗时(>500ms) | 响应延迟影响用户体验 | + | 业务需求 | 需快速定位历史问题关键点 | 客服质检、争议溯源效率低 | + | 合规风险 | 存储用户敏感对话原文 | 数据泄露风险增加 | + +* 面试回答要点 + + * **核心目标** + * 通过摘要存储实现对话上下文的长期维护,解决大模型Token限制与对话连贯性问题。 + * 技术实现 + * 记忆模块:LangChain提供ConversationBufferMemory(完整历史)和ConversationSummaryMemory(摘要存储)等 + * 摘要生成:调用LLM对历史对话生成摘要,后续交互仅传递摘要而非完整历史 + * **优势** + - 减少Token消耗,适配模型输入限制。 + - 提升对话系统的长期记忆能力。 + - 支持分布式存储(如MongoDB、Milvus),扩展性强 + +* `ConversationSummaryMemory` + + * 通过模型生成对话的摘要,帮助保留重要信息,而不保存完整历史,适合需要长期记忆的场景。 + + * 原理就是提示词,让AI帮出来摘要,而且可以定制 + + ```python + 请将以下对话压缩为简短摘要,保留用户需求和关键结果: + 对话历史: + {history} + + 当前对话: + Human: {input} + AI: {output} + 摘要: + """ + ``` + + * **load_memory_variables**:返回当前会话的摘要信息。 + + ```python + from langchain.memory import ConversationSummaryMemory + from langchain_openai import ChatOpenAI + + # 初始化大模型 + llm = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + # 初始化摘要记忆 + memory = ConversationSummaryMemory(llm=llm) + # 模拟对话 + memory.save_context({"input": "你叫什么名字?"}, {"output": "你好,我是三当家。"}) + memory.save_context({"input": "你能告诉我机器学习吗?"}, {"output": "机器学习是人工智能的一个分支。"}) + # 获取摘要 + summary = memory.load_memory_variables({}) + print("当前对话摘要:", summary) + ``` + + + +#### 基于LangChain的带摘要存储对话系统实战 + +* 实战案例 + + * 基于LangChain的带摘要存储对话系统 + + ```python + from langchain.memory import ConversationSummaryMemory + from langchain_openai import ChatOpenAI + from langchain_core.runnables import RunnablePassthrough + from langchain_core.output_parsers import StrOutputParser + from langchain_core.prompts import ChatPromptTemplate + from langchain.chains import LLMChain + + # 初始化大模型 + llm = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # 初始化模型和记忆模块 + memory = ConversationSummaryMemory( + llm=llm, + memory_key="chat_history", # 与prompt中的变量名一致 + return_messages=True + ) + + # 定义提示模板(必须包含chat_history占位符) + prompt = ChatPromptTemplate.from_messages([ + ("system", "你是一个助手,需基于对话历史回答问题。当前摘要:{chat_history}"), + ("human", "{input}") + ]) + + # # 创建链并绑定记忆模块 + # chain = LLMChain( + # llm=llm, + # prompt=prompt, + # memory=memory, + # verbose=True # 调试时查看详细流程 + # ) + # 定义LCEL链式流程 + chain = ( + # 注入输入和记忆 + RunnablePassthrough.assign( + chat_history=lambda _: memory.load_memory_variables({})["chat_history"] + ) + | prompt # 将输入传递给提示模板 + | llm # 调用模型 + | StrOutputParser() # 解析模型输出为字符串 + ) + + # 模拟多轮对话 + user_inputs = [ + "人工智能的定义是什么?", + "小滴课堂上面有什么课程可以学习", + "人工智能它在医疗领域有哪些应用?" + ] + + for query in user_inputs: + # 执行对话 + response = chain.invoke({"input": query}) + + # 打印结果 + print(f"\n用户:{query}") + print(f"AI:{response}") + #使用 LLMChain 而非原始LCEL链时,每次 invoke() 会自动调用 memory.save_context()。 + #手动调用场景需显式保存: + memory.save_context({"input": query}, {"output": response}) + print("当前记忆摘要:", memory.load_memory_variables({})["chat_history"]) + ``` + + * 示例输出 + + ```markdown + 用户:人工智能的定义是什么? + AI:人工智能是模拟人类智能的计算机系统... + 当前记忆摘要: 用户询问人工智能的定义,助手解释了其核心是通过算法模拟人类认知能力。 + + 用户:小滴课堂上面有什么课程可以学习 + AI:如果“小滴课堂”是一个特定的学习平台,建议直接查询该平台提供的课程列表以获取最新信息 + 当前记忆摘要: 首先询问了人工智能的定义,AI解释了人工智能是计算机科学的一个分支,随后,人类问到“小滴课堂”上有什么课程可以学习 + + 用户:它在医疗领域有哪些应用? + AI:在医疗影像分析、药物研发... + 当前记忆摘要: 用户问及医疗应用,助手提到影像分析、药物研发和个性化诊疗是主要方向。 + ``` + + * 关键步骤讲解 + + * **链式组合 (`|` 操作符)** + 通过管道符连接多个组件: + - `RunnablePassthrough`:传递原始输入 + - `prompt`:格式化提示词 + - `llm`:调用大模型 + - `StrOutputParser`:解析输出为字符串 + * **记忆管理** + - `memory.load_memory_variables()`:加载当前摘要到提示词 + - `memory.save_context()`:手动保存对话记录(LCEL需要显式保存) + * **变量绑定** + 使用 `RunnablePassthrough.assign` 动态注入 `chat_history` 变量,确保与提示模板匹配 + + * 注意: 变量名一致性 + + - `memory_key` 必须与提示模板中的变量名一致(示例中均为 `chat_history`)。 + - 错误示例:如果模板用 `{summary}` 但 `memory_key` 设为 `history`,会导致变量未注入 + +* LCEL与 LLMChain 的核心区别 + + | 特性 | LCEL 实现 | LLMChain 实现 | + | :------------- | :--------------------------- | :------------------ | + | **记忆管理** | 需手动调用 `save_context` | 自动保存上下文 | + | **链式组合** | 支持任意步骤组合 | 固定结构 | + | **调试灵活性** | 可插入日志中间件 | 依赖 `verbose=True` | + | **扩展性** | 容易添加路由、分支等复杂逻辑 | 适合简单线性流程 | + +#### MessagesPlaceholder和多轮AI翻译助手实战 + +* `MessagesPlaceholder `介绍 + + * 是 LangChain 中用于在 **聊天型提示模板(ChatPromptTemplate)** 中动态插入消息列表的占位符。 + + * 允许开发者将历史对话记录、系统消息等结构化地嵌入到 Prompt 中,从而支持多轮对话场景的上下文管理 + + * 适用场景 + + * 多角色对话:在聊天机器人中,区分系统指令、用户输入和AI响应。 + * 历史对话注入:将历史消息作为上下文传递给模型,确保对话连贯性。 + * 模块化Prompt设计:灵活组合不同来源的消息(如系统消息、检索结果等) + + * 与普通PromptTemplate的区别 + + * PromptTemplate:用于单字符串模板,适合简单问答。 + + * ChatPromptTemplate:专为多角色消息设计,必须使用 MessagesPlaceholder 处理消息列表 + + * 例如 + + ```python + ChatPromptTemplate.from_messages([ + ("system", "你是一个助手"), + MessagesPlaceholder(variable_name="history"), + ("human", "{input}") + ]) + ``` + + * 核心功能对比 + + | 功能特性 | MessagesPlaceholder | 传统列表存储 | + | :--------------: | :--------------------------: | :----------------: | + | **动态插入** | ✅ 支持运行时动态调整消息顺序 | ❌ 固定顺序 | + | **消息类型感知** | ✅ 区分 system/human/AI | ❌ 统一存储为字符串 | + | **内存集成** | ✅ 自动与Memory组件同步 | ❌ 需手动管理 | + | **结构化操作** | ✅ 支持消息元数据 | ❌ 纯文本存储 | + +* 使用步骤 + + * 定义模板与占位符 + + * 在 ChatPromptTemplate 中通过 MessagesPlaceholder 声明占位位置 + * **并指定变量名(需与 Memory 模块的 memory_key 一致)** + + ```python + from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder + + prompt = ChatPromptTemplate.from_messages([ + ("system", "你是一个翻译助手"), + MessagesPlaceholder(variable_name="chat_history"), + ("human", "{input}") + ]) + ``` + + * 绑定记忆模块 + + * 使用 ConversationBufferMemory 或 ConversationSummaryMemory 存储对话历史, + * 并确保 memory_key 与占位符变量名匹配 + + ```python + from langchain.memory import ConversationBufferMemory + + memory = ConversationBufferMemory( + memory_key="chat_history", # 必须与MessagesPlaceholder的variable_name一致 + return_messages=True # 返回消息对象而非字符串 + ) + ``` + + * 链式调用与历史管理 + + * 在链式调用中自动注入历史消息,需使用 LLMChain 或 `RunnableWithMessageHistory` + + ```python + from langchain.chains import LLMChain + from langchain_community.chat_models import ChatOpenAI + + llm = ChatOpenAI() + chain = LLMChain(llm=llm, prompt=prompt, memory=memory) + ``` + +* 案例测试 + + ```python + from langchain.memory import ConversationBufferMemory + from langchain_community.chat_models import ChatOpenAI + from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder + from langchain.chains import LLMChain + from langchain_openai import ChatOpenAI + + # 1. 初始化模型与记忆模块 + # 初始化大模型 + llm = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + memory = ConversationBufferMemory( + memory_key="chat_history", + return_messages=True + ) + + # 2. 定义包含MessagesPlaceholder的Prompt模板 + prompt = ChatPromptTemplate.from_messages([ + ("system", "你是一个翻译助手,需参考历史对话优化翻译结果。"), + MessagesPlaceholder(variable_name="chat_history"), + ("human", "请翻译以下内容:{input}") + ]) + + # 3. 创建链并绑定记忆 + chain = LLMChain(llm=llm, prompt=prompt, memory=memory) + + # 4. 模拟多轮对话 + user_inputs = [ + "Translate 'Hello' to Chinese", + "Use the translation in a sentence", + "Now translate 'Goodbye'" + ] + + for query in user_inputs: + response = chain.invoke({"input": query}) + print(f"用户:{query}") + print(f"AI:{response['text']}\n") + print("当前对话历史:", memory.load_memory_variables({})["chat_history"], "\n") + ``` + + * LCEL表达式案例 + + ```python + from langchain.memory import ConversationBufferMemory + from langchain_community.chat_models import ChatOpenAI + from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder + from langchain.chains import LLMChain + from langchain_openai import ChatOpenAI + from langchain_core.runnables import RunnablePassthrough + from langchain_core.output_parsers import StrOutputParser + + # 1. 初始化模型与记忆模块 + # 初始化大模型 + llm = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + memory = ConversationBufferMemory( + memory_key="chat_history", + return_messages=True + ) + + # 2. 定义包含MessagesPlaceholder的Prompt模板 + prompt = ChatPromptTemplate.from_messages([ + ("system", "你是一个翻译助手,需参考历史对话优化翻译结果。"), + MessagesPlaceholder(variable_name="chat_history"), + ("human", "请翻译以下内容:{input}") + ]) + + + # 3. 创建链并绑定记忆 + #chain = LLMChain(llm=llm, prompt=prompt, memory=memory) + #定义LECL表达式,构建chain + chain = ( + RunnablePassthrough.assign( + chat_history = lambda _ : memory.load_memory_variables({})['chat_history'] + ) + | prompt + | llm + | StrOutputParser() + ) + + # 4. 模拟多轮对话 + user_inputs = [ + "Translate 'Hello' to Chinese", + "Use the translation in a sentence", + "Now translate 'Goodbye'" + ] + + for query in user_inputs: + response = chain.invoke({"input": query}) + print(f"用户:{query}") + print(f"AI:{response}\n") + memory.save_context({"input": query}, {"output": response}) + print("当前对话历史:", memory.load_memory_variables({})["chat_history"], "\n") + ``` + + + + + + + + + + + + + + + + + + + +#### 第6集 LLM复杂记忆存储-多会话隔离案例实战 + +**简介:LLM复杂记忆存储-多会话隔离案例实战** + +* 背景与需求 + + * 当多个会话同时与对话系统交互时,需确保每个会话的对话历史独立存储,避免以下问题: + + - **数据混淆**:会话A的对话内容泄露给会话B。 + - **上下文丢失**:不同会话的对话历史互相覆盖。 + - **隐私安全**:敏感信息因隔离不当导致泄露。 + + * **典型场景** + + - **客服系统**:不同会话咨询需独立记录。 + - **教育应用**:每个学生与AI助教的对话需单独存档。 + - **医疗助手**:患者健康信息需严格隔离。 + + * **解决方案:为每个会话分配唯一ID,通过ID隔离的对话历史。** + + + +* `RunnableWithMessageHistory` 介绍 + + * 是 LangChain 中用于**动态管理多用户对话历史**的高级封装类,主要解决以下问题 + + * **会话隔离**:为不同用户/会话(通过 `session_id`)独立存储对话历史。 + * **记忆注入**:自动将历史消息注入到链的每次执行中,无需手动传递。 + * **灵活存储**:支持自定义历史存储后端(内存、数据库、Redis 等)。 + + * 核心参数 + + ``` + from langchain_core.runnables.history import RunnableWithMessageHistory + ``` + + | 参数 | 类型 | 必填 | 说明 | + | :--------------------: | :-------------------------------------: | :--: | :-------------------------------------: | + | `runnable` | Runnable | 是 | 基础处理链(需支持消息历史输入) | + | `get_session_history` | Callable[[str], BaseChatMessageHistory] | 是 | 根据session_id获取历史存储实例的函数 | + | `input_messages_key` | str | 否 | 输入消息在字典中的键名(默认"input") | + | `history_messages_key` | str | 否 | 历史消息在字典中的键名(默认"history") | + + * 使用场景 + + * **多用户对话系统**:为每个用户维护独立的对话历史(如客服系统)。 + * **长期会话管理**:结合数据库存储历史,支持跨设备会话恢复。 + +* 案例实战 + + ``` + from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder + from langchain_openai.chat_models import ChatOpenAI + from langchain_core.messages import HumanMessage + from langchain_core.runnables.history import RunnableWithMessageHistory + from langchain_community.chat_message_histories import ChatMessageHistory + + # 存储会话历史的字典,可以改其他存储结构 + store = {} + # 获取会话历史的函数 如果给定的session_id不在store中,则为其创建一个新的ChatMessageHistory实例 + def get_session_history(session_id): + if session_id not in store: + store[session_id] = ChatMessageHistory() + return store[session_id] + + # 初始化大模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # 构建聊天提示模板,包含系统消息、历史消息占位符和人类消息 + prompt = ChatPromptTemplate.from_messages( + [ + ( + "system", + "你是一个小滴课堂AI助手,擅长能力{ability}。用30个字以内回答", + ), + MessagesPlaceholder(variable_name="history"), + ("human", "{input}"), + ] + ) + + # 创建Runnable管道,将提示模板和模型结合在一起 + runnable = prompt | model + + # 创建带有会话历史的Runnable ,使用get_session_history函数来管理会话历史 + with_message_history = RunnableWithMessageHistory( + runnable, + get_session_history, + input_messages_key="input", #输入消息在字典中的键名(默认"input") + history_messages_key="history", #历史消息在字典中的键名(默认"history") + ) + + # 第一次调用带有会话历史的Runnable,提供用户输入和会话ID + response1=with_message_history.invoke( + {"ability": "Java开发", "input": HumanMessage("什么是jvm")}, + config={"configurable": {"session_id": "user_123"}},#历史信息存入session_id + ) + print(f"response1:{response1.content}",end="\n\n") + + # 第二次调用带有会话历史的Runnable,用户请求重新回答上一个问题 + response2=with_message_history.invoke( + {"ability": "Java开发", "input": HumanMessage("重新回答一次")}, + config={"configurable": {"session_id": "user_123"}},#历史信息存入session_id,如果改为其他session_id,则不会关联到之前的会话历史 + ) + print(f"response2:{response2.content}",end="\n\n") + + # 打印存储的会话历史 + print(f"存储内容:{store}") + ``` + + + + + + + + + + + + + +#### 第7集 再进阶复杂存储-多租户多会话隔离案例实战 + +**简介:再进阶复杂存储-多用户多会话隔离案例实战** + +* 需求背景 + + * 系统需要支持多个用户访问,每个用户拥有唯一的标识符(`user_id`)。 + * 用户之间的会话历史完全隔离,避免数据混淆。 + * 业务场景中,存在一个用户多个会话的场景 + * 存储里面需要支持多用户,多会话的方案, 支持不同用户在多个会话中与AI助手交互。 + +* 目标: + + * **多租户支持**:每个用户拥有独立的标识符(`user_id`),确保不同用户之间的数据隔离。 + * **多会话支持**:每个用户可以同时维护多个会话(`session_id`),每个会话的历史记录相互独立。 + * **灵活扩展性**:支持自定义存储结构和参数配置,便于未来扩展到分布式存储或其他存储介质 + +* 知识点 + + * `history_factory_config` + + - 是一个配置列表,用于定义额外的可配置字段,(如 `user_id` 和 `session_id`),这些字段可以影响会话历史的生成逻辑。 + - 通过自定义参数来增强系统的灵活性,例如指定用户标识符或对话标识符,从而实现多租户或多会话的支持 + + * `ConfigurableFieldSpec` + + * 是一个类,用于定义一个可配置字段的元信息,包括字段的 ID、类型、名称、描述、默认值等。 + + ``` + from langchain_core.runnables import ConfigurableFieldSpec + ``` + + * 为 `RunnableWithMessageHistory` 提供灵活的参数支持,使得开发者可以通过配置动态调整行为。 + + | 参数名 | 类型 | 描述 | 示例值 | + | :------------ | :----- | :----------------------------------------------------------- | :--------------------- | + | `id` | `str` | 字段的唯一标识符,用于在配置中引用该字段。 | `"user_id"` | + | `annotation` | `type` | 字段的数据类型,用于类型检查和验证。 | `str` | + | `name` | `str` | 字段的名称,通常用于UI展示或调试信息。 | `"用户ID"` | + | `description` | `str` | 字段的描述信息,用于说明该字段的用途。 | `"用户的唯一标识符。"` | + | `default` | `any` | 字段的默认值,如果未提供值时使用。 | `""` | + | `is_shared` | `bool` | 是否为共享字段。如果为 `True`,则该字段在整个运行过程中保持不变。 | `True` | + +* 解决方案 + + * 使用 `user_id` 和 `session_id` 组合作为键值,存储在全局字典 `store` 中。 + * 在调用 `get_session_history` 函数时,传入 `user_id` 和 `session_id`,确保获取正确的会话历史。 + +* 案例实战 + + ``` + from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder + from langchain_openai.chat_models import ChatOpenAI + from langchain_core.messages import HumanMessage + from langchain_core.runnables.history import RunnableWithMessageHistory + from langchain_community.chat_message_histories import ChatMessageHistory + from langchain_core.runnables import ConfigurableFieldSpec + + # 存储会话历史的字典,可以改其他存储结构 + store = {} + # 获取会话历史的函数 如果给定的session_id不在store中,则为其创建一个新的ChatMessageHistory实例 + def get_session_history(user_id: str,session_id: str): + if (user_id, session_id) not in store: + store[(user_id, session_id)] = ChatMessageHistory() + return store[(user_id, session_id)] + + + # 初始化大模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # 构建聊天提示模板,包含系统消息、历史消息占位符和人类消息 + prompt = ChatPromptTemplate.from_messages( + [ + ( + "system", + "你是一个小滴课堂AI助手,擅长能力{ability}。用30个字以内回答", + ), + MessagesPlaceholder(variable_name="history"), + ("human", "{input}"), + ] + ) + + # 创建Runnable管道,将提示模板和模型结合在一起 + runnable = prompt | model + + # 创建带有会话历史的Runnable ,使用get_session_history函数来管理会话历史 + with_message_history = RunnableWithMessageHistory( + runnable, + get_session_history, + input_messages_key="input", #输入消息在字典中的键名(默认"input") + history_messages_key="history", #历史消息在字典中的键名(默认"history") + # 定义一些自定义的参数 + history_factory_config=[ + ConfigurableFieldSpec( + id="user_id", + annotation=str, + name="用户ID", + description="用户的唯一标识符。", + default="", + is_shared=True, + ), + ConfigurableFieldSpec( + id="session_id", + annotation=str, + name="对话 ID", + description="对话的唯一标识符。", + default="", + is_shared=True, + ), + ] + ) + + # 第一次调用带有会话历史的Runnable,提供用户输入和会话ID + response1=with_message_history.invoke( + {"ability": "Java开发", "input": HumanMessage("什么是jvm")}, + config={'configurable' : { "user_id" : "1" , "session_id" : "1" }} + ) + print(f"response1:{response1.content}",end="\n\n") + + # 第二次调用带有会话历史的Runnable,用户请求重新回答上一个问题 + response2=with_message_history.invoke( + {"ability": "Java开发", "input": HumanMessage("重新回答一次")}, + #历史信息存入session_id,如果改为其他session_id,则不会关联到之前的会话历史 + config={'configurable' : { "user_id" : "1" , "session_id" : "2" }}, + + ) + print(f"response2:{response2.content}",end="\n\n") + # 打印存储的会话历史 + print(f"存储内容:{store}") + ``` + + + + + + + + + + + + + + + + + +#### 第8集 大模型长期记忆解决方案和案例实战《上》 + +**简介:大模型长期记忆解决方案和案例实战** + +* 长期记忆的核心需求 + + * 大模型(LLM)本身不具备长期记忆能力,需通过外部系统实现以下目标: + * **跨会话记忆持久化**:用户多次对话中产生的关键信息需永久存储。 + * **多用户隔离**:不同用户的对话历史严格隔离,避免数据泄露。 + * **高效检索**:快速提取历史信息辅助当前对话生成。 + * **动态更新**:支持记忆的增量添加和过期清理。 + * 解决思路: + * 持久化存储:将会话历史保存到数据库/缓存,支持跨会话读取。 + * 高效检索:通过语义搜索或键值查询快速定位历史信息。 + * 动态更新:根据新交互持续补充用户画像。 + +* LangChain提供的存储方案 + + * 地址(失效忽略即可):https://python.langchain.com/docs/integrations/memory/ + + * `RedisChatMessageHistory` 存储介绍 + + ![1](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/1-3677655.png) + + * 内置的 Redis 消息历史存储工具,将会话记录以结构化形式保存至 Redis + + | 特性 | 价值 | + | :-------------: | :----------------------------------------------------------: | + | 低延迟(<1ms) | 实时响应交互需求 | + | 丰富数据结构 | 灵活存储消息/元数据/关系, 使用 JSON 格式存储,支持复杂消息类型(如带元数据的消息) | + | 持久化选项 | RDB+AOF保障数据安全 | + | 集群支持 | 横向扩展应对高并发 | + | 自动过期(TTL) | 合规数据自动清理, 通过 `expire` 参数设置历史记录保存时间(如 30 天自动删除 | + + * LangChain 的 `RedisChatMessageHistory` 等组件默认依赖 JSON 格式存储对话历史,ReJSON 提供天然的兼容性 + * 数据存储结构 + + image-20250404162459285 + + image-20250404162420533 + + + +* 什么是Redis-Stack + + * 是Redis 官方推出的 集成化发行版,预装了多个高性能模块和工具,专为现代应用设计。 + + * **开箱即用**:无需手动安装模块,直接支持搜索、JSON、图数据库等高级功能。 + + * **开发友好**:更适合需要复杂查询、实时分析、AI 应用的场景(如大模型上下文管理、聊天记录检索)。 + + * **统一体验**:通过 Redis Stack 命令行或客户端库统一访问所有功能。 + + | 模块/工具 | 功能 | + | :------------------ | :----------------------------------- | + | **RediSearch** | 全文搜索、二级索引 | + | **RedisJSON** | 原生 JSON 数据支持 | + | **RedisGraph** | 图数据库(基于属性图模型) | + | **RedisTimeSeries** | 时间序列数据处理 | + | **RedisBloom** | 概率数据结构(布隆过滤器、基数估算) | + | **RedisInsight** | 图形化管理工具 | + + * 何时选择 Redis Stack? + + * 全文检索(如聊天记录搜索)。 + * 直接操作 JSON 数据(如存储大模型的对话上下文)。 + * 时间序列分析(如监控聊天频率)。 + * 图关系查询(如社交网络分析)。 + * 简化开发流程:避免手动配置多个模块的兼容性和依赖。 + + * LLM整合如果直接使用之前的Redis服务端会报错,新版单独安装 RedisStack 服务端,才支持相关Redis搜索 + + * 之前部署的Redis可以卸载,或者更改端口,也可以配置密码,使用方式一样,redis-stack 7.X版本都可以 + + ``` + docker run -d \ + --name redis-stack \ + -p 6379:6379 \ + -p 8001:8001 \ + redis/redis-stack:latest + ``` + + + + + + + + + + + + + + + + + + + + + +#### 第9集 大模型长期记忆解决方案和案例实战《下》 + +**简介:大模型长期记忆解决方案和案例实战** + +* 案例实战 + + * 安装依赖 + + ``` + pip install langchain-redis==0.2.0 redis==5.2.1 + ``` + + * 案例测试 + + ``` + from langchain_redis import RedisChatMessageHistory + import os + + REDIS_URL = os.getenv("REDIS_URL", "redis://47.119.128.20:6379") + #配置了密码 REDIS_URL = "redis://:your_password@your_ip:6379" + #REDIS_URL = os.getenv("REDIS_URL", "redis://:abc123456@39.108.115.28:6379") + + # 初始化 RedisChatMessageHistory + history = RedisChatMessageHistory(session_id="user_123", redis_url=REDIS_URL) + + # 新增消息 + history.add_user_message("Hello, AI assistant!") + history.add_ai_message("Hello! How can I assist you today?") + + # 检索消息 + print("Chat History:") + for message in history.messages: + print(f"{type(message).__name__}: {message.content}") + ``` + + * 编码实战 + + ``` + from langchain_core.chat_history import BaseChatMessageHistory + from langchain_core.messages import AIMessage, HumanMessage + from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder + from langchain_core.runnables.history import RunnableWithMessageHistory + from langchain_openai import ChatOpenAI + from langchain_redis import RedisChatMessageHistory + + #存储历史会话的字典 + + REDIS_URL="redis://47.119.128.20:6379" + #定义函数,用于获取会话历史 + def get_redis_history(session_id: str): + return RedisChatMessageHistory(session_id, redis_url=REDIS_URL) + + # 初始化大模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # 构建聊天提示模板,包含系统消息、历史消息占位符和人类消息 + prompt = ChatPromptTemplate.from_messages( + [ + ("system","你是一个小滴课堂AI助手,擅长能力{ability}。用30个字以内回答",), + MessagesPlaceholder(variable_name="history"), + ("human", "{input}"), + ] + ) + + #创建基础链 + chain = prompt | model + + with_message_history = RunnableWithMessageHistory( + chain, get_redis_history, input_messages_key="input", history_messages_key="history" + ) + + #第一次调用,提供用户输入和会话ID + resp1 = with_message_history.invoke( + {"ability":"Java开发", "input":HumanMessage("什么是JVM")}, #替换提示词 + config={"configurable":{"session_id":"user_123"} }) #会话唯一ID + + print(f"resp1={resp1.content}",end="\n\n") + + resp2 = with_message_history.invoke( + {"ability":"Java开发", "input":HumanMessage("重新回答一次")}, #替换提示词 + config={"configurable":{"session_id":"user_123"} }) #会话唯一ID + + print(f"resp2={resp2.content}",end="\n\n") + ``` + + * 注意: 安全起见,RedisStack还是需要配置密码,避免入侵挖矿 + + ``` + docker run -d \ + --name redis-stack \ + -p 6379:6379 \ + -p 8001:8001 \ + -e REDIS_ARGS="--requirepass abc123456" \ + redis/redis-stack:latest + ``` + + * 多租户多会话方案 + + ``` + from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder + from langchain_openai.chat_models import ChatOpenAI + from langchain_core.messages import HumanMessage + from langchain_core.runnables.history import RunnableWithMessageHistory + from langchain_community.chat_message_histories import ChatMessageHistory + from langchain_core.runnables import ConfigurableFieldSpec + from langchain_redis import RedisChatMessageHistory + + #存储历史会话的字典 + + REDIS_URL="redis://:abc123456@47.119.128.20:6379" + + # 获取会话历史的函数 如果给定的session_id不在store中,则为其创建一个新的ChatMessageHistory实例 + def get_session_history(user_id: str,session_id: str): + uni_key = user_id+"_"+session_id + + return RedisChatMessageHistory( + session_id=uni_key, + redis_url=REDIS_URL) + + + + # 初始化大模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7 + ) + + # 构建聊天提示模板,包含系统消息、历史消息占位符和人类消息 + prompt = ChatPromptTemplate.from_messages( + [ + ( + "system", + "你是一个小滴课堂AI助手,擅长能力{ability}。用30个字以内回答", + ), + MessagesPlaceholder(variable_name="history"), + ("human", "{input}"), + ] + ) + + # 创建Runnable管道,将提示模板和模型结合在一起 + runnable = prompt | model + + # 创建带有会话历史的Runnable ,使用get_session_history函数来管理会话历史 + with_message_history = RunnableWithMessageHistory( + runnable, + get_session_history, + input_messages_key="input", #输入消息在字典中的键名(默认"input") + history_messages_key="history", #历史消息在字典中的键名(默认"history") + # 定义一些自定义的参数 + history_factory_config=[ + ConfigurableFieldSpec( + id="user_id", + annotation=str, + name="用户ID", + description="用户的唯一标识符。", + default="", + is_shared=True, + ), + ConfigurableFieldSpec( + id="session_id", + annotation=str, + name="对话 ID", + description="对话的唯一标识符。", + default="", + is_shared=True, + ), + ] + ) + + # 第一次调用带有会话历史的Runnable,提供用户输入和会话ID + response1=with_message_history.invoke( + {"ability": "Java开发", "input": HumanMessage("什么是jvm")}, + config={'configurable' : { "user_id" : "user_1" , "session_id" : "session_1" }} + ) + print(f"response1:{response1.content}",end="\n\n") + # 打印存储的会话历史 + print(get_session_history("user_1","session_1").messages) + + # 第二次调用带有会话历史的Runnable,用户请求重新回答上一个问题 + response2=with_message_history.invoke( + {"ability": "Java开发", "input": HumanMessage("重新回答一次")}, + config={'configurable' : { "user_id" : "user_2" , "session_id" : "session_1" }}, + ) + print(f"response2:{response2.content}",end="\n\n") + # 打印存储的会话历史 + print(get_session_history("user_2","session_1").messages) + ``` + + + + + + + + + + + +![logo](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"** +**更多高级课程请访问 xdclass.net** + +### 第四十八章 大模型服务必备FastAPI Web框架 + + + +#### 第1集 Python Web框架Fast API技术介绍 + +**简介: Python Web框架Fast API技术介绍** + +* 需求背景:现代Web开发痛点 + + * 传统框架(如Flask、Django)对异步支持弱,性能受限。 + * 手动编写API文档耗时且易过时、缺乏自动化数据验证,易出现安全漏洞。 + +* FastAPI是什么? + + - 一个基于 Python 3.8+ 的现代 Web 框架,专为构建高性能 API 设计。 + - 官方地址: + - https://github.com/fastapi/fastapi + - https://fastapi.tiangolo.com/ + - 关键特性: + - ⚡ 高性能:基于Starlette(异步)和Pydantic(数据验证) + - 📚 自动文档:集成Swagger UI和Redoc,代码即文档。 + - ✅ 强类型安全:Python类型提示 + Pydantic模型,减少Bug。 + - 🚀 易学易用:代码简洁,适合快速开发API。 + - 适用场景 + * 构建 RESTful API 或 大模型 服务,微服务架构中的独立服务模块。 + * 需要自动生成 API 文档的项目。 + * **核心价值**:FastAPI = 高性能 + 强类型 + 自动文档。 + - **优点**: + - 开发效率高(代码量减少 40%+) + - 严格的类型提示减少运行时错误。 + - **缺点**: + - 生态相对较新(插件和社区资源少于 Django/Flask)。 + - 学习曲线略高(需熟悉 异步编程)。 + +* 什么是 Uvicorn(类似Java 生态的Tomcat) + + * 是一个轻量级、高性能的 **ASGI(Asynchronous Server Gateway Interface)服务器** + * 专门运行异步 Python Web 应用(如 FastAPI、Starlette 或 Django Channels)。 + * 类似传统 WSGI 服务器 的异步升级版,支持 HTTP/1.1、WebSockets 和 ASGI 标准。 + * **核心功能** + * **异步处理**:基于 `asyncio` 库,原生支持 `async/await`,适合高并发场景(如实时通信、高频 API 调用)。 + * **高性能**:使用 C 语言编写的 `uvloop` 和 `httptools` 加速网络请求处理。 + * **自动重载**:开发时通过 `--reload` 参数监听代码变动,自动重启服务。 + * **协议支持**: HTTP/1.1 、WebSocket、实验性 HTTP/2(需额外配置) + * 与 FastAPI 的关系 + * 依赖关系:FastAPI 本身不包含服务器,需通过 Uvicorn(或其他 ASGI 服务器)启动。 + * 协同工作流程: + * 用户通过 Uvicorn 启动 FastAPI 应用。 + * Uvicorn 监听 HTTP 请求,将请求转发给 FastAPI 处理。 + * FastAPI 处理请求并返回响应,由 Uvicorn 发送给客户端 + +* 类似产品对比 + + | 框架 | 语言 | 特点 | 适用场景 | + | :-------------- | :------ | :----------------------------------- | :---------------------- | + | **Flask** | Python | 轻量级、灵活,但同步且无内置数据验证 | 小型应用、快速原型开发 | + | **Django** | Python | 全功能(ORM、Admin),但重量级、同步 | 复杂 Web 应用(如 CMS) | + | **Express** | Node.js | 高并发,但动态类型易出错 | JS 生态的 API 开发 | + | **Spring Boot** | Java | 企业级功能,但配置复杂、启动慢 | 大型分布式系统 | + | **FastAPI** | Python | 高性能、异步、类型安全、自动文档 | 高并发 API、微服务 | + + + + + + + + + + + + + +#### 第2集 FastAPI环境安装和基础案例实战 + +**简介: FastAPI环境安装和基础案例实战** + +* 环境安装 + + * FastAPI 依赖 Python 3.8 及更高版本, 安装fastapi版本 `0.115.12` + * 安装命令 + * 安装依赖库. `pip install "fastapi[standard]"` + * 安装uvicorn服务器 `pip install "uvicorn[standard]"` + +* 案例实战 + + * 创建第一个Fast API应用 + + ``` + from typing import Union + + from fastapi import FastAPI + + # 创建FastAPI实例 + app = FastAPI() + + #http请求方式类型:get、post、put、update、delete + #不带参数访问路由 + @app.get("/") + def read_root(): + return {"Hello": "World11"} + + + # 带参数访问,比如 http://127.0.0.1:8000/items/5?q=xd + # q参数通过 Union[str, None] 表示可以是字符串类型或空,这样就允许在请求中不提供 q 参数。 + @app.get("/items/{item_id}") + def read_item(item_id: int, q: Union[str, None] = None): + return {"item_id": item_id, "q": q} + ``` + + * 启动服务 + + ``` + #语法:uvicorn 文件的相对路径:实例名 --reload + #--reload:代码修改后自动重启(仅开发环境) + uvicorn main:app --reload + + #指定程序监听端口 + uvicorn main:app --port 8002 --reload + + #下面这个方式也可以 + fastapi dev main.py + ``` + + * 访问API与文档 + + * API地址:`http://localhost:8000` + * 交互文档:`http://localhost:8000/docs` + * 备用文档( 另一种交互式文档界面,具有清晰简洁的外观 ):`http://localhost:8000/redoc` + + + + + + + + + + + + + + + + + +#### 第3集 进阶FastAPI Web多案例参数请求实战 + +**简介: 进阶FastAPI Web多案例参数请求实战** + +* 路由与 HTTP 方法 + + ``` + from fastapi import FastAPI + + app = FastAPI() + + @app.get("/items/{item_id}") + def read_item(item_id: int): # 自动类型转换和验证 + return {"item_id": item_id} + + @app.post("/items/") + def create_item(item: dict): + return {"item": item} + + @app.put("/items/{item_id}") + def update_item(item_id: int, item: dict): + return {"item_id": item_id, "updated_item": item} + + @app.delete("/items/{item_id}") + def delete_item(item_id: int): + return {"status": "deleted", "item_id": item_id} + + ``` + +* 请求头Header操作 + + ``` + from fastapi import Header + + @app.get("/header") + def read_item(item_id: int, token: str = Header("token")): + return {"token": token,"item_id": item_id} + + ``` + +* 获取多个请求头 + + ``` + from fastapi import Request + @app.get("/all-headers/") + def get_all_headers(request: Request): + return dict(request.headers) + ``` + +* 自定义响应头 + + ``` + from fastapi.responses import JSONResponse + @app.get("/custom-header/") + def set_custom_header(): + content = {"message": "Hello World"} + headers = { + "X-Custom-Header": "my_value xdclass.net", + "Cache-Control": "max-age=3600" + } + return JSONResponse(content=content, headers=headers) + + ``` + +* 自定义响应码 + + ``` + from fastapi import FastAPI, status,Response + + @app.get("/status_code", status_code=200) + def create_item(name: str): + if name == "Foo": + return Response(status_code=status.HTTP_404_NOT_FOUND) + return {"name": name} + ``` + + + + + + + + + + + +#### 第4集 FastAPI异步编程async-await多案例实战 《上》 + +**简介: FastAPI异步编程async-await多案例实战** + +* 需求背景 + + * 什么是同步和异步? 用一个奶茶店买饮料的场景帮理解同步和异步的区别 + + * 🧋 **同步(Synchronous)就像传统奶茶店** + + ``` + 你: 老板,我要一杯珍珠奶茶 + 店员: 开始制作(5分钟) + 你: 干站着等,不能做其他事 + 店员: 做好了!请取餐 + 下一位顾客: 必须等前一个人完成才能点单 + 特点: 必须按顺序处理,前一个任务没完成,后面全卡住 + ``` + + * 🚀 异步(Asynchronous)像智能奶茶店 + + ``` + 你: 扫码下单珍珠奶茶 + 系统: 收到订单(生成取餐号) + 你: 去旁边座位玩手机(不用干等) + 店员: 同时处理多个订单 + 系统: 奶茶做好后叫号通知你 + 特点: 下单后可以继续做其他事,系统并行处理多个任务 + ``` + + * 对应到代码中 + + ``` + # 同步代码(传统奶茶店模式) + def make_tea(): + print("开始煮茶") # 👨🍳 店员开始工作 + time.sleep(3) # ⏳ 你干等着 + print("加珍珠") + return "奶茶好了" + + # 异步代码(智能奶茶店模式) + async def async_make_tea(): + print("开始煮茶") + await asyncio.sleep(3) # 🚶♂️ 你可以去做其他事 + print("加珍珠") + return "奶茶好了" + ``` + +* 同步和异步关键区别总结【伪代码】 + + * 同步版本(存在性能瓶颈) + + ``` + import requests + + @app.get("/sync-news") + def get_news_sync(): + # 顺序执行(总耗时=各请求之和) + news1 = requests.get("https://api1.com").json() # 2秒 + news2 = requests.get("https://api2.com").json() # 2秒 + return {"total_time": 4} + ``` + + * 异步版本(高效并发) + + ``` + import httpx + + @app.get("/async-news") + async def get_news_async(): + async with httpx.AsyncClient() as client: + # 并行执行(总耗时≈最慢的请求) + start = time.time() + task1 = client.get("https://api1.com") + task2 = client.get("https://api2.com") + res1, res2 = await asyncio.gather(task1, task2) + return { + "total_time": time.time() - start # ≈2秒 + } + ``` + +* 异步编程常见类库介绍 + +* * `asyncio` 类库 + + * Python 中用于编写**单线程并发代码**的库,基于**协程(Coroutine)**和**事件循环(Event Loop)**实现异步编程。 + + * 专为处理 I/O 密集型任务(如网络请求、文件读写、数据库操作)设计,提高程序的吞吐量和资源利用率 + + * **协程(Coroutine)** + + - 使用 `async def` 定义的函数称为协程,返回一个协程对象,不会立即执行。 + + - 协程通过 `await` 关键字挂起自身,将控制权交还给事件循环,直到异步操作完成 + + ``` + async def my_coroutine(): + await asyncio.sleep(1) + print("Done!") + ``` + + * **事件循环(Event Loop)** + + - 事件循环是异步程序的核心,负责调度和执行协程、处理 I/O 事件、管理回调等。 + + - 通过 `asyncio.run()` 或手动创建事件循环来启动。 + + ``` + # 启动事件循环并运行协程 + asyncio.run(my_coroutine()) + ``` + + * **任务(Task)** + + - 任务是对协程的封装,用于在事件循环中并发执行多个协程。 + + - 通过 `asyncio.create_task()` 创建任务 + + ``` + async def main(): + task = asyncio.create_task(my_coroutine()) + await task + ``` + + * **Future** + + - `Future` 是一个底层对象,表示异步操作的最终结果。通常开发者直接使用 `Task`(它是 `Future` 的子类)。 + + + + * `httpx` 类库 + + * Python 中一个现代化、功能丰富的 HTTP 客户端库,支持同步和异步请求 + + * `httpx` 是 `requests` 的现代替代品,结合了易用性与强大功能,尤其适合需要异步或 HTTP/2 支持的场景。 + + * 同步请求案例 + + ``` + import httpx + # GET 请求 + response = httpx.get("https://httpbin.org/get") + print(response.status_code) + print(response.json()) + ``` + + * 异步请求案例 + + ``` + import httpx + import asyncio + + async def fetch_data(): + async with httpx.AsyncClient() as client: + response = await client.get("https://httpbin.org/get") + print(response.json()) + + asyncio.run(fetch_data()) + ``` + + * 使用客户端实例(推荐) + + ``` + # 同步客户端 + with httpx.Client() as client: + response = client.get("https://httpbin.org/get") + + # 异步客户端 + async with httpx.AsyncClient() as client: + response = await client.get("https://httpbin.org/get") + ``` + + + + + + + + + + + + + +#### 第5集 FastAPI异步编程async-await多案例实战 《下》 + +**简介: FastAPI异步编程async-await多案例实战** + +* 案例实战 + + * 基础同步和异步编程 + + ``` + from fastapi import FastAPI + import asyncio + import time + + app = FastAPI() + + # 同步路由执行(顺序阻塞) + @app.get("/sync_func") + def sync_endpoint(): + # 模拟耗时操作 + print("开始任务1") # 立即执行 + time.sleep(3) # 阻塞3秒 + print("开始任务2") # 3秒后执行 + return {"status": "done"} + + # 异步异步执行(非阻塞切换) + @app.get("/async_func") + async def async_endpoint(): # 异步接口 + # 必须使用异步库 + print("开始任务A") # 立即执行 + await asyncio.sleep(3) # 释放控制权,异步等待 + print("开始任务B") # 3秒后恢复执行 + return {"status": "done"} + ``` + + * 综合案例实战:并发调用外部API + + ``` + import asyncio + import httpx + from fastapi import FastAPI + + app = FastAPI() + + """ + 异步函数:fetch_data + 功能:通过HTTP GET请求从指定URL获取数据并返回JSON格式的响应。 + 参数: + url (str): 目标API的URL地址。 + 返回值: + dict: 从目标URL获取的JSON格式数据。 + """ + async def fetch_data(url: str): + async with httpx.AsyncClient() as client: + response = await client.get(url) + return response.json() + + + + """ + 路由处理函数:get_news + 功能:定义一个FastAPI的GET路由,用于并发获取多个API的数据并返回整合后的结果。 + """ + @app.get("/xdclass") + async def get_news(): + start = time.time() + # 定义需要请求的API URL列表 + urls = [ + "https://api-v2.xdclass.net/api/funny/v1/get_funny", + "https://api-v2.xdclass.net/api/banner/v1/list?location=home_top_ad", + "https://api-v2.xdclass.net/api/rank/v1/hot_product", + "http://localhost:8000/sync1", + "http://localhost:8000/sync2", + ] + + # 创建并发任务列表,每个任务调用fetch_data函数获取对应URL的数据 + tasks = [fetch_data(url) for url in urls] + + # 使用asyncio.gather并发执行所有任务,并等待所有任务完成 + results = await asyncio.gather(*tasks) + print(f"共耗时 {time.time() - start} 秒") + # 返回整合后的结果 + return results + ``` + + + +* 常见错误模式演示 + + ``` + # 错误示例:在async函数中使用同步阻塞 + @app.get("/wrong") + async def bad_example(): + time.sleep(5) # 会阻塞整个事件循环! + + # 正确方案1:改用异步等待 + @app.get("/right1") + async def good_example(): + await asyncio.sleep(5) + ``` + + + +* 最佳实践指南 + + * **必须使用async的三种场景** + + * 需要await调用的异步库操作 + * WebSocket通信端点 + * 需要后台任务处理的接口 + + * **以下情况适合同步路由** + + - 纯CPU计算(如数学运算) + - 使用同步数据库驱动 + - 快速返回的简单端点 + + * **路由声明规范** + + * 所有路由优先使用`async def` + * 仅在必须使用同步操作时用普通 `def` + + * **性能优化建议** + + - 保持async函数轻量化 + - 长时间CPU密集型任务使用后台线程 + - 使用连接池管理数据库/HTTP连接 + + + + + + + + + + + + + + + +#### 第6集 进阶FastAPI模型Pydantic 案例实战 + +**简介: 进阶FastAPI模型Pydantic 案例实战** + +* 什么是数据模型 + + * 客户端能发送什么数据 + * 服务端会返回什么数据 + * 数据验证规则 + +* 基础模型定义 + + ``` + from pydantic import BaseModel + + # 用户注册模型 + class UserRegister(BaseModel): + username: str # 必填字段 + email: str | None = None # 可选字段 + age: int = Field(18, gt=0) # 带默认值和验证 + ``` + +* 案例实战 + + * 请求体(POST/PUT数据) + + ``` + from pydantic import BaseModel,Field + class Product(BaseModel): + name: str + price: float = Field(..., gt=0) + tags: list[str] = [] + + @app.post("/products") + async def create_product(product: Product): + return product.model_dump() + + + #测试数据 + { + "name":"小滴", + "price":111, + "tags":["java","vue"] + } + ``` + + * 返回 Pydantic 模型 + + ``` + from pydantic import BaseModel + class Item(BaseModel): + name: str + description: str = None + price: float + tax: float = None + + @app.post("/items") + async def create_item(item: Item): + return item + + #测试数据 + { + "name":"小滴", + "price":111, + "description":"小滴课堂是在线充电平台" + } + ``` + + * 密码强度验证 + + ``` + from pydantic import BaseModel, field_validator + + class UserRegistration(BaseModel): + username: str + password: str + + @field_validator('password') + def validate_password(cls, v): + if len(v) < 8: + raise ValueError('密码至少8位') + if not any(c.isupper() for c in v): + raise ValueError('必须包含大写字母') + if not any(c.isdigit() for c in v): + raise ValueError('必须包含数字') + return v + + @app.post("/register/") + async def register(user: UserRegistration): + return {"username": user.username} + + + #测试数据 + { + "username":"小滴", + "password":"123" + } + ``` + + + + + + + + + + + + + + + + + + + + + + + +![logo](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"** +**更多高级课程请访问 xdclass.net** + +### 第四十九章 FastAPI框架高级技能和综合项目实战 + + + +#### 第1集 FastAPI路由管理APIRouter案例实战 + +**简介: FastAPI 路由管理APIRouter案例实战** + +* 需求背景 + + * 代码混乱问题, 未使用路由管理(所有接口堆在main.py中) + + * 所有接口混杂在一个文件中,随着功能增加,文件会变得臃肿难维护。 + + ``` + # main.py + from fastapi import FastAPI + + app = FastAPI() + + # 用户相关接口 + @app.get("/users") + def get_users(): ... + + # 商品相关接口 + @app.get("/items") + def get_items(): ... + + # 订单相关接口 + @app.get("/orders") + def get_orders(): ... + ``` + + * 重复配置问题, 未使用路由管理(重复写前缀和标签) + + * 每个接口都要重复写`/api/v1`前缀和`tags`,容易出错且难以统一修改 + + ``` + # 用户接口 + @app.get("/api/v1/users", tags=["用户管理"]) + def get_users_v1(): ... + + # 商品接口 + @app.get("/api/v1/items", tags=["商品管理"]) + def get_items_v1(): ... + ``` + + * 权限控制问题, 未使用路由管理(每个接口单独添加认证) + + * 需要在每个管理员接口重复添加认证依赖。 + + ``` + @app.get("/admin/stats", dependencies=[Depends(admin_auth)]) + def get_stats(): ... + + @app.post("/admin/users", dependencies=[Depends(admin_auth)]) + def create_user(): ... + ``` + + + +* **使用路由管理(模块化拆分)** + + * 按业务模块拆分,每个文件职责单一。 + + ``` + # 文件结构 + routers/ + ├── users.py # 用户路由 + ├── items.py # 商品路由 + └── orders.py # 订单路由 + + # main.py + from routers import users, items, orders + app.include_router(users.router) + app.include_router(items.router) + app.include_router(orders.router) + ``` + + * 使用路由管理 + + ``` + # 创建管理员专属路由组 + admin_router = APIRouter( + prefix="/admin", + dependencies=[Depends(admin_auth)], # 👈 统一认证 + tags=["管理员"] + ) + + @admin_router.get("/stats") + def get_stats(): ... + + @admin_router.post("/users") + def create_user(): ... + ``` + + + +* `APIRouter`核心概念 + + * 基础使用模板 + + ``` + from fastapi import APIRouter + + router = APIRouter( + prefix="/users", # 路由前缀 + tags=["用户管理"], # OpenAPI分组 + responses={404: {"description": "资源未找到"}} # 默认响应 + ) + + @router.get("/", summary="获取用户列表") + async def list_users(): + return [{"id": 1, "name": "张三"}] + ``` + + * 核心配置参数 + + | 参数 | 作用 | 示例值 | + | :----------: | :--------------: | :---------------------: | + | prefix | 路由统一前缀 | "/api/v1" | + | tags | OpenAPI文档分组 | ["认证相关"] | + | dependencies | 路由组公共依赖项 | [Depends(verify_token)] | + | responses | 统一响应定义 | {400: {"model": Error}} | + +* 案例实战 + + * 创建文件` app/users.py` + + ``` + from fastapi import APIRouter, HTTPException + from pydantic import BaseModel + router = APIRouter( + prefix="/users", # 路由前缀 + tags=["用户管理"], # OpenAPI文档分组 + dependencies=[] # 模块级依赖 + ) + + @router.get("/", summary="获取用户列表") + async def list_users(): + return [{"id": 1, "name": "Alice"}] + + @router.post("/", summary="创建新用户") + async def create_user(): + return {"id": 2, "name": "Bob"} + + class UserCreate(BaseModel): + username: str + password: str + + @router.post("/register", status_code=201) + async def register(user: UserCreate): + """用户注册接口""" + # 实际应保存到数据库 + return {"message": "用户创建成功", "username": user.username} + + @router.get("/{user_id}") + async def get_user(user_id: int): + if user_id > 100: + raise HTTPException(404, "用户不存在") + return {"user_id": user_id, "name": "虚拟用户"} + ``` + + * 创建文件 `app/products.py` + + ``` + # app/api/v1/products.py + from fastapi import APIRouter + router = APIRouter( + prefix="/products", + tags=["商品管理"], + dependencies=[] + ) + + @router.get("/search", summary="商品搜索") + async def search_products( + q: str, + min_price: float = None, + max_price: float = None + ): + # 实现搜索逻辑 + return {"message": "搜索成功"} + + @router.get("/{product_id}", summary="获取商品详情") + async def get_product_details(product_id: int): + return {"id": product_id} + + ``` + + * 创建入口文件 `main.py` + + ``` + from fastapi import FastAPI + from app import users, products + import uvicorn + + # 创建FastAPI应用实例 + app = FastAPI() + + # 注册用户模块的路由 + app.include_router(users.router) + + # 注册产品模块的路由 + app.include_router(products.router) + + # 访问路径: + # GET /users/ → 用户列表 + # POST /users/ → 创建用户 + #... + + # 打印所有注册的路由信息,便于开发者查看和调试 + for route in app.routes: + print(f"{route.path} → {route.methods}") + + # 定义根路径的GET请求处理函数 + """ + 处理根路径的GET请求。 + + 返回值: + dict: 返回一个JSON对象,包含欢迎消息。 + """ + @app.get("/") + async def root(): + return {"message": "Hello 小滴课堂"} + + + # 主程序入口,用于启动FastAPI应用 + if __name__ == '__main__': + # 使用uvicorn运行FastAPI应用,默认监听本地地址和端口 + uvicorn.run(app,port=8001) + ``` + +* 模块化结构参考 + + ``` + #整体项目结构 + project/ + ├── main.py + └── routers/ + ├── __init__.py + ├── users.py + ├── items.py + ├── admin/ + │ ├── dashboard.py + │ └── audit.py + └── v2/ + └── users.py + ``` + +* 总结 + + | 实践要点 | 说明 | + | :----------: | :------------------------------------: | + | 模块化组织 | 按业务功能拆分路由模块 | + | 统一前缀管理 | 使用`prefix`参数避免路径重复 | + | 文档友好 | 合理使用`tags`和`summary`优化API文档 | + | 依赖分层 | 模块级依赖处理认证,路由级处理业务逻辑 | + + + + + + + + + + + + + + + + + + + + + +#### 第2集 FastAPI依赖注入和常见项目结构设计 + +**简介: FastAPI依赖注入和常见项目结构设计** + +* 什么是依赖注入 + + * 用于将重复逻辑(如鉴权、数据库连接、参数校验)抽象为可复用的组件, + + * 通过依赖注入(Dependency Injection)自动注入到路由处理函数中,让代码更简洁、模块化且易于测试 + + * 核心 + + * **代码复用**:避免在多个路由中重复相同逻辑(如权限检查)。 + * **解耦**:将业务逻辑与基础设施(如数据库、认证)分离。 + * **层级化**:支持嵌套依赖,构建多层逻辑(如先验证用户,再验证权限) + + * 与 Java Spring 对比 + + | **功能** | **FastAPI** | **Spring (Java)** | + | :----------- | :-------------------- | :----------------------------- | + | **依赖注入** | 函数/类 + `Depends()` | `@Autowired` + 容器管理 | + | **作用域** | 默认每次请求 | Singleton/Prototype/Request 等 | + + * 语法案例 + + * **定义依赖项函数** + + * 依赖项可以是任何可调用对象(如函数、类),通过 `Depends()` 声明依赖关系 + + ``` + from fastapi import Depends, FastAPI + + app = FastAPI() + + # 定义一个依赖项(函数) + def common_params(query: str = None, page: int = 1): + return {"query": query, "page": page} + + # 在路由中使用依赖项 + @app.get("/read_items") + async def read_items(params: dict = Depends(common_params)): + return params + + #说明:common_params 会被自动调用,结果注入到 params 参数。 + #访问结果:read_items 中可直接使用 params["query"] 和 params["page"]。 + ``` + + * 类作为依赖项 + + * 依赖项也可以是类,适合需要初始化或状态管理的场景, 通过依赖注入机制,将复杂逻辑解耦为可复用的模块 + + ``` + #案例一 + class DatabaseSession: + def __init__(self): + self.session = "模拟数据库连接" + + def close(self): + print("关闭数据库连接") + + def get_db(): + db = DatabaseSession() + try: + yield db + finally: + db.close() + + @app.get("/users/") + async def get_users(db: DatabaseSession = Depends(get_db)): + return {"db_session": db.session} + + + #案例二 + class Pagination: + def __init__(self, page: int = 1, size: int = 10): + self.page = page + self.size = size + + @app.get("/articles/") + async def get_articles(pagination: Pagination = Depends()): + return {"page": pagination.page, "size": pagination.size} + ``` + + * 全局依赖项 + + * 为所有路由添加公共依赖项(如统一认证) + + ``` + app = FastAPI(dependencies=[Depends(verify_token)]) + + # 或针对特定路由组: + router = APIRouter(dependencies=[Depends(log_request)]) + + #在部分管理员接口添加认证依赖。 + @app.get("/admin/stats", dependencies=[Depends(admin_auth)]) + def get_stats(): ... + + @app.post("/admin/users", dependencies=[Depends(admin_auth)]) + def create_user(): ... + ``` + + + +* FastAPI 项目结构设计原则 + + * 基础分层结构(适合小型项目) + + ``` + myproject/ + ├── main.py + ├── routers/ + │ ├── users.py + │ └── items.py + ├── models/ + │ └── schemas.py + └── dependencies.py + ``` + + * 模块化拆分结构一(推荐中型项目) + + ``` + src/ + ├── app/ + │ ├── core/ # 核心配置 + │ │ ├── config.py + │ │ └── security.py + │ ├── api/ # 路由入口 + │ │ ├── v1/ # 版本控制 + │ │ │ ├── users/ + │ │ │ │ ├── endpoints.py + │ │ │ │ └── schemas.py + │ │ │ └── items/ + │ ├── models/ # 数据模型 + │ ├── services/ # 业务逻辑 + │ └── utils/ # 工具类 + ├── tests/ # 测试目录 + └── requirements.txt + + ``` + + * 模块化拆分结构二(推荐中型项目) + + ``` + myproject/ + ├── app/ # 应用核心目录 + │ ├── core/ # 全局配置和工具 + │ │ ├── config.py # 配置管理 + │ │ └── security.py # 安全相关工具 + │ ├── api/ # 路由端点 + │ │ ├── v1/ # API版本目录 + │ │ │ ├── users.py + │ │ │ ├── items.py + │ │ │ └── ai.py + │ │ └── deps.py # 公共依赖项 + │ ├── models/ # Pydantic模型 + │ │ ├── user.py + │ │ └── item.py + │ ├── services/ # 业务逻辑层 + │ │ ├── user_service.py + │ │ └── ai_service.py + │ ├── db/ # 数据库相关 + │ │ ├── session.py # 数据库会话 + │ │ └── models.py # SQLAlchemy模型 + │ └── utils/ # 工具函数 + │ └── logger.py + ├── tests/ # 测试目录 + │ ├── test_users.py + │ └── conftest.py + ├── static/ # 静态文件 + ├── main.py # 应用入口 + ├── requirements.txt + └── .env # 环境变量 + + ``` + +* 推荐原则【遵循团队规范即可】 + + | 原则 | 实施方法 | + | :------: | :--------------------------------: | + | 单一职责 | 每个文件/类只做一件事 | + | 依赖倒置 | 通过依赖注入解耦组件 | + | 分层清晰 | 严格区分路由层、服务层、数据访问层 | + | 版本控制 | 通过URL路径实现API版本管理 | + | 文档友好 | 为每个路由添加summary和description | + + + + + + + + + + + + + + + + + + + + + +#### 第3集 FastAPI+大模型流式AI问答助手实战《上》 + +**简介: FastAPI+大模型流式AI问答助手实战《上》** + +* 需求 + + * 开发一个基于AI的问答工具,能够根据用户提供的知识点或主题生成简洁的介绍或解释。 + * 使用了大语言模型(LLM)来实现流式生成文本的功能,适用于教育、内容创作等场景 + * FastAPI框架整合LLM大模型,提供HTTP服务 + +* `StreamingResponse` 介绍 + + * FastAPI 的提供了 `StreamingResponse` 是一个用于处理流式传输数据的工具,适用于需要逐步发送大量数据或实时内容的场景。 + + * 允许通过生成器逐块发送数据,避免一次性加载全部内容到内存,提升性能和资源利用率。 + + ``` + from fastapi.responses import StreamingResponse + ``` + + * 核心功能 + + * 流式传输:逐步发送数据块,适用于大文件(如视频、日志)、大模型实时生成内容(如LLM响应、服务器推送事件)或长时间运行的任务。 + * 内存高效:无需将完整数据加载到内存,减少服务器负载。 + * 异步支持:兼容同步和异步生成器,灵活适配不同场景。 + + * 基本用法 + + * 在路由中返回 `StreamingResponse` 实例,并传入生成器作为数据源 + + * 参数说明 + + * `content`: 生成器函数,产生字节或字符串数据块。 + * `media_type`: 指定 MIME 类型(如 `"text/event-stream"`、`"application/json"`)。 + * `headers`: 自定义响应头(如 `{"Content-Disposition": "attachment; filename=data.csv"}`)。 + * `status_code`: 设置 HTTP 状态码(默认为 `200`)。 + + * 案例实操 + + ``` + async def ai_qa_stream_generator(query: str): + """生成A回答的流式响应""" + try: + async for chunk in ai_writer.run_stream(query): + json_data = json.dumps({"text": chunk}) + yield f"data: {json_data}\n\n" + except Exception as e: + error_msg = json.dumps({"error": str(e)}) + yield f"data: {error_msg}\n\n" + + + @app.get("/ai_write") + async def ai_writer_endpoint(query: str): + """AI写作接口,返回流式响应""" + return StreamingResponse( + ai_qa_stream_generator(query), + media_type="text/event-stream", + headers={ + "Cache-Control": "no-cache", + "Connection": "keep-alive" + } + ) + ``` + + + +* 编码实战 `app/ai_writer.py` + + ``` + from langchain_openai import ChatOpenAI + from langchain_core.prompts import ChatPromptTemplate + from langchain_core.output_parsers import StrOutputParser + from typing import AsyncGenerator + + + # 封装AI问答的类 + class AIWriter: + def __init__(self): + # 初始化语言模型 + self.llm = self.llm_model() + + # 定义一个返回自定义语言模型的方法 + def llm_model(self): + #创建模型 + model = ChatOpenAI( + model_name = "qwen-plus", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_key="sk-005c3c25f6d042848b29d75f2f020f08", + temperature=0.7, + streaming=True + ) + return model + + async def run_stream(self, query: str) -> AsyncGenerator[str, None]: + """运行AI问答并返回流式响应""" + try: + # 定义提示模板,要求对文档进行摘要总结 + prompt_template = "用100个字解释下面的知识点或者介绍:{concept}" + # 使用提示模板类创建模板 + prompt = ChatPromptTemplate.from_template(prompt_template) + + # 定义LLM链,将自定义语言模型和提示模板结合 + chain = prompt | self.llm | StrOutputParser() + + # 使用流式输出 + async for chunk in chain.astream({"concept": query}): + if isinstance(chunk, str): + yield chunk + elif isinstance(chunk, dict) and "content" in chunk: + yield chunk["content"] + else: + yield str(chunk) + except Exception as e: + yield f"发生错误: {str(e)}" + + + + async def chat(self, query: str): + """处理用户消息并返回流式响应""" + try: + async for chunk in self.run_stream(query): + yield chunk + except Exception as e: + yield f"发生错误: {str(e)}" + ``` + + + + + + + + + + + + + + + + + + + + + +#### 第4集 FastAPI+大模型流式AI问答助手实战《下》 + +**简介: FastAPI+大模型流式AI问答助手实战《下》** + +* 编码实战 `writer_app.py` + + ``` + import uvicorn + from fastapi import FastAPI + from fastapi.responses import StreamingResponse + from app.ai_writer import AIWriter + import json + + app = FastAPI() + + # 初始化智能体 + ai_writer = AIWriter() + + async def ai_qa_stream_generator(query: str): + """生成A回答的流式响应""" + try: + async for chunk in ai_writer.run_stream(query): + json_data = json.dumps({"text": chunk}, ensure_ascii=False) + yield f"data: {json_data}\n\n" + except Exception as e: + error_msg = json.dumps({"error": str(e)}) + yield f"data: {error_msg}\n\n" + + @app.get("/ai_write") + async def ai_writer_endpoint(query: str): + """AI写作接口,返回流式响应""" + return StreamingResponse( + ai_qa_stream_generator(query), + media_type="text/event-stream", + headers={ + "Cache-Control": "no-cache", + "Connection": "keep-alive" + } + ) + + + # 启动服务器的命令 + if __name__ == "__main__": + uvicorn.run(app, port=8003) + ``` + +* 如何调试 + + * Apifox 提供了专门的 SSE(Server-Sent Events)调试功能,适合处理 AI 大模型的流式响应场景 + * 配置 SSE,选择接口的请求方法,在请求头中添加 `Accept: text/event-stream` 来启用 SSE。