Files
ai-agent-dev/05_LLM链.ipynb
2026-07-08 10:09:42 +08:00

394 lines
13 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 05 LLM 链\n",
"\n",
"## 学习目标\n",
"1. 理解 LLMChain 的本质Prompt 模板 + 模型 + 输出解析的组合\n",
"2. 掌握使用 LCELLangChain 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
}