{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 05 LLM 链\n", "\n", "## 学习目标\n", "1. 理解 LLMChain 的本质:Prompt 模板 + 模型 + 输出解析的组合\n", "2. 掌握使用 LCEL(LangChain Expression Language)管道符构建链\n", "3. 理解数据在链中的流动过程:输入 -> Prompt -> LLM -> 输出\n", "4. 学会使用链进行单条调用、批量调用和流式输出" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. 什么是 LLMChain\n", "\n", "LLMChain 是 LangChain 中最基础、最常用的链类型。它表示一个简单的处理流程:\n", "\n", "```\n", "用户输入 -> Prompt模板 -> 大模型 -> 输出结果\n", "```\n", "\n", "在 LangChain 的新版(LCEL)中,我们不再使用专门的 `LLMChain` 类,而是直接用 `|` 管道符将组件串联起来:\n", "\n", "```python\n", "chain = prompt | llm\n", "```\n", "\n", "这种方式更直观、更灵活,也是本课程推荐的标准写法。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. 构建第一个 LLMChain\n", "\n", "我们需要三个基本组件:\n", "1. **Prompt 模板**:定义输入格式和系统提示\n", "2. **LLM 模型**:实际调用的大模型\n", "3. **链**:将两者用 `|` 连接" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from langchain_openai import ChatOpenAI\n", "from langchain_core.prompts import ChatPromptTemplate\n", "from dotenv import load_dotenv\n", "\n", "load_dotenv()\n", "\n", "\n", "# 1. 创建模型\n", "llm = ChatOpenAI(\n", " model='qwen3.6-35b-A3b',\n", " temperature=0.7\n", ")\n", "\n", "# 2. 创建 Prompt 模板\n", "prompt = ChatPromptTemplate.from_messages([\n", " ('system', '你是一个专业的翻译助手,只输出翻译结果,不做解释。'),\n", " ('user', '请将以下文本翻译成英文:{text}')\n", "])\n", "\n", "# 3. 构建链\n", "chain = prompt | llm\n", "\n", "# 4. 运行链\n", "result = chain.invoke({'text': '人工智能正在改变世界'})\n", "print(result.content)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 代码解析\n", "\n", "| 步骤 | 代码 | 作用 |\n", "| --- | --- | --- |\n", "| 1 | `ChatOpenAI(...)` | 创建模型实例,指定模型名称和温度 |\n", "| 2 | `ChatPromptTemplate.from_messages(...)` | 创建提示模板,包含 system 和 user 消息 |\n", "| 3 | `prompt \\| llm` | 用管道符连接,形成处理链 |\n", "| 4 | `chain.invoke({'text': '...'})` | 传入变量,执行链并获取结果 |\n", "\n", "`invoke()` 返回的是 `AIMessage` 对象,用 `.content` 获取文本内容。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. 数据流详解\n", "\n", "当调用 `chain.invoke({'text': '人工智能正在改变世界'})` 时,数据在链中是这样流动的:\n", "\n", "```text\n", "输入: {'text': '人工智能正在改变世界'}\n", " |\n", " v\n", "prompt: 将变量填入模板\n", " system: 你是一个专业的翻译助手,只输出翻译结果,不做解释。\n", " user: 请将以下文本翻译成英文:人工智能正在改变世界\n", " |\n", " v\n", "llm: 调用大模型 API\n", " |\n", " v\n", "输出: AIMessage(content='AI is changing the world.')\n", "```\n", "\n", "链的每个环节都是 **Runnable**(可运行对象),它们都实现了统一的接口:\n", "- `.invoke(input)` — 单条调用\n", "- `.batch(inputs)` — 批量调用\n", "- `.stream(input)` — 流式输出\n", "- `.ainvoke(input)` — 异步调用" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. 多变量输入\n", "\n", "Prompt 模板中可以定义多个变量,invoke 时传入字典即可:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from langchain_openai import ChatOpenAI\n", "from langchain_core.prompts import ChatPromptTemplate\n", "\n", "llm = ChatOpenAI(model='qwen3.6-35b-A3b', temperature=0.7)\n", "\n", "# 多变量 Prompt 模板\n", "prompt = ChatPromptTemplate.from_messages([\n", " ('system', '你是一位{style}作家。'),\n", " ('user', '请用{style}的风格写一段关于{topic}的{format},约{length}字。')\n", "])\n", "\n", "chain = prompt | llm\n", "\n", "result = chain.invoke({\n", " 'style': '浪漫主义',\n", " 'topic': '星空',\n", " 'format': '散文',\n", " 'length': '100'\n", "})\n", "print(result.content)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. 批量调用(Batch)\n", "\n", "当需要处理多条输入时,使用 `.batch()` 可以一次性发送多个请求,效率更高:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from langchain_openai import ChatOpenAI\n", "from langchain_core.prompts import ChatPromptTemplate\n", "\n", "llm = ChatOpenAI(model='qwen3.6-35b-A3b', temperature=0.7)\n", "\n", "prompt = ChatPromptTemplate.from_messages([\n", " ('system', '你是一个简洁的助手,只输出结果。'),\n", " ('user', '将以下中文翻译成英文:{text}')\n", "])\n", "\n", "chain = prompt | llm\n", "\n", "# 批量输入\n", "inputs = [\n", " {'text': '你好'},\n", " {'text': '谢谢'},\n", " {'text': '再见'},\n", " {'text': '人工智能'}\n", "]\n", "\n", "results = chain.batch(inputs)\n", "for i, result in enumerate(results):\n", " print(f'{i+1}. {result.content}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6. 流式输出(Stream)\n", "\n", "链也支持流式输出,模型会逐字返回结果:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from langchain_openai import ChatOpenAI\n", "from langchain_core.prompts import ChatPromptTemplate\n", "\n", "llm = ChatOpenAI(model='qwen3.6-35b-A3b', temperature=0.7)\n", "\n", "prompt = ChatPromptTemplate.from_messages([\n", " ('system', '你是一个诗人。'),\n", " ('user', '写一首关于{topic}的短诗。')\n", "])\n", "\n", "chain = prompt | llm\n", "\n", "print('生成中:', end='')\n", "for chunk in chain.stream({'topic': '秋天'}):\n", " print(chunk.content, end='')\n", "print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 7. 链的重用与组合\n", "\n", "定义好的链可以像函数一样多次复用,也可以作为更大链的一部分继续组合。本节演示三个核心概念:\n", "\n", "### 核心概念\n", "\n", "1. **StrOutputParser()**:输出解析器,自动把模型的 AIMessage 对象转换成纯字符串\n", "2. **链的复用**:同一个 chain 变量可以多次调用 invoke(),传入不同输入\n", "3. **链的组合**:把多个链用 `|` 连接起来,前一个链的输出作为后一个链的输入\n", "\n", "### 组合链的数据流\n", "\n", "```\n", "输入: {'text': '大语言模型正在改变软件开发的方式'}\n", " |\n", " v\n", "translate_chain: 中文 -> 英文\n", " 输出: 'Large language models are changing the way software is developed.'\n", " |\n", " v\n", "lambda x: {'text': x}: 把字符串包装成 summarize_chain 需要的字典格式\n", " 输出: {'text': 'Large language models are changing...'}\n", " |\n", " v\n", "summarize_chain: 总结成一句话\n", " 输出: '大型语言模型正在改变软件开发的方式。'\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from langchain_openai import ChatOpenAI\n", "from langchain_core.prompts import ChatPromptTemplate\n", "from langchain_core.output_parsers import StrOutputParser\n", "\n", "# 创建共享的 LLM 实例\n", "llm = ChatOpenAI(model='qwen3.6-35b-A3b', temperature=0.7)\n", "\n", "# ========== 1. 定义可复用的翻译链 ==========\n", "# 先创建翻译用的 Prompt 模板\n", "translate_prompt = ChatPromptTemplate.from_messages([\n", " ('system', '你是一个翻译助手,将中文翻译成英文。'),\n", " ('user', '{text}') # {text} 是输入变量\n", "])\n", "\n", "# 构建翻译链:Prompt -> LLM -> 字符串输出解析器\n", "# StrOutputParser() 的作用:把模型返回的 AIMessage 对象直接转成 str\n", "translate_chain = translate_prompt | llm | StrOutputParser()\n", "\n", "# ========== 2. 链的复用 ==========\n", "# 同一个 translate_chain 可以像函数一样多次调用\n", "# 每次 invoke() 传入不同的 {'text': '...'},得到对应的英文翻译\n", "print(translate_chain.invoke({'text': '苹果'}))\n", "print(translate_chain.invoke({'text': '香蕉'}))\n", "print(translate_chain.invoke({'text': '桃子'}))\n", "\n", "# ========== 3. 链的组合 ==========\n", "# 再定义一个总结链,同样接收 {text} 变量\n", "summarize_prompt = ChatPromptTemplate.from_messages([\n", " ('system', '请用一句话总结以下内容。'),\n", " ('user', '{text}')\n", "])\n", "summarize_chain = summarize_prompt | llm | StrOutputParser()\n", "\n", "# 组合链:翻译后的英文传给总结链\n", "# translate_chain 的输出是字符串,但 summarize_chain 需要字典 {'text': ...}\n", "# 所以中间加一个 lambda 函数做格式转换\n", "full_chain = translate_chain | (lambda x: {'text': x}) | summarize_chain\n", "\n", "# 运行组合链:输入中文 -> 翻译成英文 -> 总结成中文\n", "result = full_chain.invoke({'text': '大语言模型写代码的能力越来越强。使用ai开发代码让传统的软件开发方式被改变。'})\n", "print(f'\\n翻译+总结:{result}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 8. 链的调试与查看中间结果\n", "\n", "使用 `StrOutputParser()` 可以自动将模型输出转换为字符串,省去手动 `.content` 的步骤。\n", "\n", "如果想查看链运行过程中的中间状态(比如格式化后的 Prompt 是什么样的),可以使用回调或调试工具:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from langchain_openai import ChatOpenAI\n", "from langchain_core.prompts import ChatPromptTemplate\n", "\n", "llm = ChatOpenAI(model='qwen3.6-35b-A3b', temperature=0.7)\n", "\n", "prompt = ChatPromptTemplate.from_messages([\n", " ('system', '你是一个助手。'),\n", " ('user', '你好,我叫{name}')\n", "])\n", "\n", "# 查看格式化后的 Prompt\n", "formatted = prompt.format_messages(name='小明')\n", "print('格式化后的 Prompt:')\n", "for msg in formatted:\n", " print(f' {msg.type}: {msg.content}')\n", "\n", "chain = prompt | llm\n", "result = chain.invoke({'name': '小明'})\n", "print(f'\\n模型回复:{result.content}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9. 本节课练习\n", "\n", "1. 创建一个 LLMChain,将用户的输入翻译成法文(修改 system 提示和 user 模板)\n", "2. 使用多变量输入,创建一个可以根据「风格」和「主题」生成不同文章的链\n", "3. 使用 `.batch()` 一次性翻译 5 个不同的中文句子\n", "4. 使用 `.stream()` 让模型逐字生成一段关于「未来科技」的短文\n", "5. 尝试在链末尾加上 `| StrOutputParser()`,观察返回结果类型的变化" ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.3" } }, "nbformat": 4, "nbformat_minor": 4 }