下面我们来详细举例说明 LangChain 如何调用大语言模型(LLM),并结合不同的组件来构建一个简单的应用。
LangChain 提供了非常灵活的方式来连接和使用各种大语言模型。其核心思想是解耦:LangChain 不会自己实现 LLM,而是通过统一的接口来调用外部的 LLM 提供商(如 OpenAI, Anthropic, Hugging Face, Google 等)。
LangChain访问阿里云通义千问大模型
# 导入阿里云通义千问大模型的 Tongyi 类
from langchain_community.llms.tongyi import Tongyi
# 创建 Tongyi 模型实例,指定使用 qwen-max 模型
model = Tongyi(model="qwen-max")
# 调用模型的 invoke 方法,输入问题并获取响应
res = model.invoke(input="你是谁啊?你能做什么?")
# 打印模型的响应结果
print(res)
LangChain访问LMStudio本地模型
import os
from langchain_community.chat_models import ChatOpenAI
LMSTUDIO_API_URL = "http://192.168.2.2:1234"
LMSTUDIO_API_KEY = "sk-lm-KhQ3luJz:wVcElTI6GTrmAMjZVYUk" # <-- 添加这一行
MODEL_NAME_IN_LMSTUDIO = "google/gemma-3n-e4b"
# 初始化 ChatOpenAI 对象(用于连接 LMStudio)
try:
# 初始化 ChatOpenAI 对象
llm = ChatOpenAI(
# 设置 LMStudio 服务器 URL
base_url=f"{LMSTUDIO_API_URL}/v1",
# 设置 API 密钥
api_key=LMSTUDIO_API_KEY,
# 设置模型名称
model=MODEL_NAME_IN_LMSTUDIO,
# 设置温度参数
temperature=0.7
)
print("ChatOpenAI 对象初始化成功!(连接到 LMStudio)")
except Exception as e:
print(f"初始化 ChatOpenAI 时出错:{e}")
exit()
# 执行问答
def ask_gemma(question: str):
"""
使用 LMStudio 中的 Gemma 模型回答问题。
"""
try:
print(f"\n用户提问:{question}")
response = llm.invoke(question)
print(f"Gemma 回答:{response.content}")
except Exception as e:
print(f"调用 LMStudio 模型时出错:{e}")
# 演示
if __name__ == "__main__":
# 示例问题
example_question_1 = "写一首关于春天的七言"
ask_gemma(example_question_1)
ChatOpenAI 对象初始化成功!(连接到 LMStudio)
用户提问:写一首关于春天的七言
Gemma 回答:## 春日吟
东风拂柳绿初匀,细雨轻敲破晓烟。
山色空蒙含远黛,水光潋滟映晴天。
燕语呢喃穿旧垒,桃花灼灼染新颜。
农人扶犁耕沃土,一派生机满人间。
莫负春光良辰好,且将诗酒醉流年。
**解释:**
* **东风拂柳绿初匀,细雨轻敲破晓烟。**: 东风吹拂杨柳,嫩绿刚刚开始均匀地生长;轻柔的细雨拍打着黎明的薄雾。
* **山色空蒙含远黛,水光潋滟映晴天。**: 山色笼罩在淡淡的云雾中,带着远处的青黛色;水面波光粼粼,映照着晴朗的天空 。
* **燕语呢喃穿旧垒,桃花灼灼染新颜。**: 燕子低声细语,飞过古老的城墙;桃花鲜艳夺目,为大地增添了新的色彩。
* **农人扶犁耕沃土,一派生机满人间。**: 农民扶着犁,辛勤地耕耘肥沃的土地;整个世界都充满了勃勃生机。
* **莫负春光良辰好,且将诗酒醉流年。**: 不要辜负美好的春光和时光;让我们用诗歌和美酒,尽情享受这美好的一年。
这首诗描绘了春天生机勃勃的景象,表达了对春天的喜爱和对美好生活的向往。 希望你喜欢!
1. LangChain 调用 LLM 的基本流程
- 选择 LLM 提供商: 确定您想使用的 LLM(例如 OpenAI 的 GPT-3.5-turbo, GPT-4, Anthropic 的 Claude, Hugging Face 的 Llama2 等)。
- 安装对应的集成库: 为您选择的 LLM 安装必要的 Python 包。
- 配置 API 密钥: 设置 LLM 提供商的 API 密钥(通常通过环境变量)。
- 初始化 LLM 对象: 在 LangChain 中创建一个代表该 LLM 的实例。
-
调用 LLM: 使用 LLM 对象的
invoke()(或run(),generate()等) 方法,传入您组织的输入,获取 LLM 的输出。
2. 核心组件
在深入示例之前,先了解几个关键的 LangChain 组件:
-
LLMs (Language Models): 这是 LangChain 中与 LLM 进行交互的抽象层。它是一个简单的接口,接受字符串作为输入,并返回字符串作为输出。
-
OpenAI: 用于调用 OpenAI 的模型。 -
ChatOpenAI: 用于调用 OpenAI 的聊天模型(通常更推荐用于对话)。 -
HuggingFaceHub: 用于连接 Hugging Face 的模型。 -
Anthropic: 用于调用 Anthropic 的 Claude 模型。 - 等等...
-
-
Prompts (提示): 用于构造发送给 LLM 的输入文本。
-
PromptTemplate: 允许您使用变量动态地创建提示。 -
ChatPromptTemplate: 专为聊天模型设计,允许您构建包含系统消息、人类消息、AI 消息的对话轮次。
-
-
Chains (链): 将多个组件(如 Prompt, LLM, Tool 等)组合成一个序列,以执行更复杂的任务。
-
LLMChain: 最基本的链,将一个 Prompt 和一个 LLM 结合起来。
-
3. 示例 1:最简单的 LLM 调用 (使用 OpenAI)
这是 LangChain 调用 LLM 的最基础形式。
目标: 让 LLM 回答一个简单的问题。
准备工作:
-
安装:
pip install langchain langchain-openai openai python-dotenv -
创建
.env文件:OPENAI_API_KEY=sk-your_actual_openai_api_key_here
Python 代码:
import os
from dotenv import load_dotenv
from langchain_openai import OpenAI # 导入 OpenAI LLM
from langchain.prompts import PromptTemplate # 导入 PromptTemplate
from langchain.chains import LLMChain # 导入 LLMChain
# 1. 加载环境变量 (API Key)
load_dotenv()
# 2. 初始化 LLM 对象
# temperature 控制输出的随机性,0.7 表示中等随机性
llm = OpenAI(temperature=0.7)
# 3. 创建 Prompt Template
# 这个模板有一个名为 'question' 的输入变量
prompt_template = PromptTemplate(
input_variables=["question"],
template="请简要介绍一下:{question}",
)
# 4. 创建 LLMChain
# 将 LLM 和 Prompt Template 组合在一起
# invoke() 是 LangChain 0.1.0 版本后推荐的调用方式,取代了 .run()
story_chain = LLMChain(llm=llm, prompt=prompt_template)
# 5. 准备输入并调用 Chain
question_to_ask = "太阳系"
print(f"Asking LLM: '请简要介绍一下:{question_to_ask}'")
# invoke() 接收一个字典,键必须与 prompt_template 的 input_variables 匹配
try:
response = story_chain.invoke({"question": question_to_ask})
print("\n--- LLM Response ---")
# .invoke 默认返回一个字典,实际结果在 'text' 键下
print(response['text'])
except Exception as e:
print(f"An error occurred: {e}")
解释:
- 我们首先加载了 OpenAI API 密钥。
-
OpenAI(temperature=0.7)创建了一个 OpenAI LLM 的实例。temperature是一个重要的参数,它控制 LLM 生成文本的创造力和随机性。较低的温度(接近 0)使输出更确定、重复性更高;较高的温度(接近 1)使输出更随机、更有创造性,但也可能出现不准确或偏离主题的情况。 -
PromptTemplate定义了一个结构化的输入。input_variables=["question"]声明了模板中可以使用{question}这个变量。"请简要介绍一下:{question}"是实际发送给 LLM 的指令。 -
LLMChain将llm和prompt_template“粘合”在一起。当您调用story_chain.invoke({"question": "太阳系"})时:- LangChain 会将输入字典
{"question": "太阳系"}填充到prompt_template中,生成完整的提示字符串:“请简要介绍一下:太阳系”。 - 这个完整的提示字符串被发送到
llm(OpenAI 模型)。 - OpenAI 模型处理这个提示并生成一个回答。
- LangChain 接收到模型的输出,并将其封装在一个字典中返回,其中关键结果在
'text'键下。
- LangChain 会将输入字典
4. 示例 2:使用聊天模型和对话模板 (使用 ChatOpenAI)
聊天模型(如 GPT-3.5-turbo, GPT-4)更适合处理多轮对话,它们期望的输入格式是消息列表(系统消息、用户消息、AI 消息)。LangChain 提供了 ChatPromptTemplate 和 ChatOpenAI 来方便地处理这类任务。
目标: 进行一个简单的多轮对话。
准备工作: (同上,只需要确保 langchain-openai 库已安装)
Python 代码:
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI # 导入 ChatOpenAI
from langchain.prompts import ChatPromptTemplate # 导入 ChatPromptTemplate
from langchain.schema importHumanMessage, AIMessage, SystemMessage # 导入消息类型
from langchain.chains import LLMChain
# 1. 加载环境变量
load_dotenv()
# 2. 初始化 Chat Model 对象
# model_name 指定具体的聊天模型,如 "gpt-3.5-turbo" 或 "gpt-4"
chat_llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.7)
# 3. 创建 Chat Prompt Template
# ChatPromptTemplate 允许您构建消息序列
chat_prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一个乐于助人的AI助手,擅长讲笑话。"), # 系统消息,设定AI的角色
("human", "{user_input}"), # 用户消息,包含用户输入
("ai", "{ai_response_history}") # (可选) AI的过往回复,用于延续对话
])
# 4. 创建 LLMChain (对于聊天模型,它会自动处理消息列表)
# 注意:这里的 chain.invoke 接收的 input 同样是字典
conversation_chain = LLMChain(llm=chat_llm, prompt=chat_prompt_template)
# 5. 进行对话
print("--- Starting Conversation ---")
# 第一轮对话
user_message_1 = "给我讲一个关于程序员的笑话。"
print(f"User: {user_message_1}")
# invocation_receipt = conversation_chain.invoke({"user_input": user_message_1}) # 早期版本
# 注意:LangChain v0.1.0 后的 .invoke() 直接返回结果,
# 而不是一个 dictionary wrapper 列表。
# 对于 LLMChain, .invoke() 仍然返回一个 dict, 包含 'text' 字段
response_1 = conversation_chain.invoke({"user_input": user_message_1, "ai_response_history": ""}) # 第一次没有历史记录
ai_message_1 = response_1['text']
print(f"AI: {ai_message_1}")
# 第二轮对话 (引入对话历史 concept,但此处 chain 并不直接支持显式历史管理,
# 更复杂的对话管理需要 LangChain 的 Memory 组件)
# 假设我们知道 AI 的上一条回复,并将其作为“历史”的一部分
# 在实际应用中,您会需要一个 Memory 组件来管理和注入历史
print("\n--- Continuing Conversation (simulated history) ---")
user_message_2 = "这个笑话不错!再给我讲一个关于猫的。"
print(f"User: {user_message_2}")
# 假设我们要将上一轮的 AI 回复作为历史传递给 LLM (一种简化的方式)
# 实际上,LangChain 的 ChatPromptTemplate.from_messages() 已经能处理消息序列了
# 这里我们创建一个简单的消息列表作为输入
# 注意:LLMChain 内部会根据 prompt_template 格式化消息
# response_2 = conversation_chain.invoke(
# {
# "user_input": user_message_2,
# # "ai_response_history": ai_message_1 # 这种方式在 LLMChain 中不直接支持,它需要格式化的消息
# # 更标准的方式是直接传递完整的消息列表给 LlM 实例,或者使用 RunnableSequential.from_list
# }
# )
# 为了简化 LLMChain 的用法,我们还是复用它,但理解它内部如何处理
# LLMChain 内部会根据 prompt_template 格式化输入的字典,然后发送给 ChatOpenAI
# 如果 prompt_template 包含 ai_response_history,它应该是一个字符串,而不是一个完整的 AIMessage 对象
# 这里的 ai_response_history 字段在 ChatPromptTemplate 中是用来接收一个字符串的,
# 如果直接把 AIMessage 传进去,可能会有问题。
# 更高级的用法会使用 Runnable 接口,允许我们组合更复杂的组件,包括 Memory
# 让我们修改 PromptTemplate 来更符合直接传递输入数据的方式
# 注意: LangChain 中, LLMChain 并不是最适合处理连续对话的组件。
# 更合适的是使用 Runnable 接口,并配合 Memory 组件。
# 为了演示 LLMChain 和 ChatOpenAI,我们聚焦于单次调用,并理解 ChatPromptTemplate 的结构。
# 假设我们直接使用 ChatOpenAI,并手动构建消息列表
print("\n--- Direct ChatOpenAI call with simulated history ---")
messages = [
SystemMessage(content="你是一个乐于助人的AI助手,擅长讲笑话。"),
HumanMessage(content="给我讲一个关于程序员的笑话。"),
AIMessage(content=ai_message_1), # 注入第一轮 AI 回复作为历史
HumanMessage(content=user_message_2)
]
response_2_direct = chat_llm.invoke(messages)
print(f"AI: {response_2_direct.content}")
解释:
-
ChatOpenAI: 用于调用 OpenAI 的聊天模型。model_name是必须的。 -
ChatPromptTemplate.from_messages([...]): 这是关键。它允许您定义多条消息(SystemMessage,HumanMessage,AIMessage)。-
SystemMessage: 设定 AI 的行为、角色或总体指令。 -
HumanMessage: 代表用户的输入。 -
AIMessage: 代表 AI 的回复。
-
- 当您将这个
ChatPromptTemplate与ChatOpenAI结合使用时(通过LLMChain或更现代的Runnable),LangChain 会负责将您提供的字典中的值转换为预定义的消息格式 (HumanMessage,SystemMessage等),然后将它们打包成一个列表传递给ChatOpenAI。 - 在第二个对话示例中,我展示了如何绕过
LLMChain(因为它对显式管理对话历史的集成不那么直观),直接使用chat_llm.invoke(messages)来构建一个包含上一轮 AI 回复的消息列表,从而实现一定程度的对话延续。 -
注意: 在实际的复杂对话应用中,您会非常依赖 LangChain 的 Memory 组件(如
ConversationBufferMemory,ConversationBufferWindowMemory等)来自动管理对话历史,并将其注入到 Prompt 中。
5. 示例 3:使用 LangChain Expression Language (LCEL) 构建更复杂的流程
LCEL 是 LangChain 推荐的构建 LLM 应用的新方式,它非常强大且灵活,允许您以声明式的方式组合各种组件。
目标: 结合一个简单的搜索引擎工具,让 LLM 能够查找信息并回答问题。
准备工作:
-
安装:
pip install langchain langchain-openai serpapi python-dotenv beautifulsoup4-
serpapi: 用于调用 Google 搜索 API。
-
-
创建
.env文件:OPENAI_API_KEY=sk-your_actual_openai_api_key_here SERPAPI_API_KEY=your_actual_serpapi_api_key_here
Python 代码:
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableSequence, RunnableParallel
from langchain_core.tools import Tool # 用于定义工具
from langchain_community.tools import SerpAPIWrapper # 用于 Google 搜索
from langchain.agents import AgentExecutor, create_tool_calling_agent # 用于构建 Agent (虽然我们这里用 LCEL 简化)
from langchain_core.output_parsers import StrOutputParser
# 1. 加载环境变量
load_dotenv()
# 2. 初始化 LLM
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.7)
# 3. 定义工具 (Search Tool)
# SerpAPIWrapper 封装了 Google 搜索的功能
search = SerpAPIWrapper()
# 将工具封装成 LangChain 的 Tool 对象,需要 name, description, func
# name: agent 如何识别这个工具
# description: agent 理解工具用途的关键描述
# func: 要调用的函数
tools = [
Tool(
name="Google Search",
description="A wrapper around Google Search. Useful for when you need to answer questions about current events or find information on the internet.",
func=search.run,
)
]
# 4. 创建一个 Prompt Template,指导 LLM 如何使用工具
# 这个 prompt 更加复杂,它包含用户请求、可用的工具信息,以及 LLM 如何输出(调用工具或回答)
# 在 LCEL 中,我们可以直接将工具函数作为步骤加入 Runnable 链
# 但是,如果我们要让 LLM "智能地"选择是否使用工具,就需要 Agent 了。
# 这里我们先演示一个更简单的,LLM 直接根据问题决定是否调用搜索。
# 对于 Agent 的复杂逻辑,LangChain 提供了 AgentExecutor。
# 但LCEL 也可以模拟:让 LLM 输出要搜索的关键词,然后执行搜索,再将搜索结果喂给 LLM.
# 简化版:让 LLM 直接输出搜索关键词
prompt_template_search_keyword = ChatPromptTemplate.from_messages([
("system", "你是一个助手,请判断用户的问题是否需要联网查询。如果需要,请只输出你认为最适合在Google上搜索的关键词。如果不需要,请回复 'skip_search'"),
("human", "{user_question}"),
])
# 5. 构建 LCEL 链
# Step 1: 接收用户问题
# Step 2: 用 prompt_template_search_keyword 提炼搜索关键词
# Step 3: 如果关键词不是 'skip_search',则执行 Google Search
# Step 4: 将搜索结果与原始问题结合,再喂给 LLM 生成最终答案
# 辅助函数来处理条件逻辑
def run_search_tool(data):
query = data["search_query"]
if query.lower() == "skip_search":
# 如果不需要搜索,则返回一个空字符串,或者原始问题本身,让 LLM 直接回答
# 这里我们选择返回一个标记,让最终 LLM 知道不需要搜索
return {"search_result": "skip_search", "original_question": data["user_question"]}
else:
try:
search_result = search.run(query)
return {"search_result": search_result, "original_question": data["user_question"]}
except Exception as e:
print(f"Search tool error: {e}")
return {"search_result": "Error fetching search results.", "original_question": data["user_question"]}
# Step 1 & 2: 接收用户问题并提取搜索关键词
get_search_query_chain = (
{"user_question": lambda x: x, "search_query": prompt_template_search_keyword}
| llm
| StrOutputParser() # 将 LLM 的输出(关键词)提取为字符串
)
# Step 3: 执行搜索(或跳过)
tool_chain = RunnableParallel(
search_query_result=get_search_query_chain,
user_question=lambda x: x # 保持原始问题
).map(run_search_tool) # 使用 map 来应用 run_search_tool 函数
# Step 4: 根据搜索结果,构建最终回答的 Prompt,并调用 LLM
# 这个 prompt 需要根据是否有搜索结果来生成不同的最终答案
final_answer_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个AI助手。请基于用户的问题和搜索到的信息(如果有)来回答问题。如果搜索结果为空或标记为 'skip_search',则仅根据你的知识回答。"),
("human", "用户问题: {original_question}\n\n搜索结果: \n{search_result}")
])
# 整个流程的最终链
# input 经过 get_search_query_chain -> {search_query: "关键词", user_question: "原始问题"}
# 然后 map(run_search_tool) -> {search_result: "搜索结果字符串", original_question: "原始问题"}
# 最后将这个字典喂给 final_answer_prompt 和 llm
final_chain_with_search = (
RunnableParallel(
{"search_query": lambda x: x} # 初始输入是一个字符串 (用户问题)
)
| RunnableParallel(
search_output=get_search_query_chain | RunnableParallel(
search_query_result=StrOutputParser(),
user_question=lambda x: x # Pass original question through
).map(run_search_tool)
)
| final_answer_prompt
| llm
| StrOutputParser()
)
# 6. 运行 Chain
user_question_1 = "请问最近的地球人都知道的重大新闻是什么?"
print(f"\n--- Query: {user_question_1} ---")
response_1 = final_chain_with_search.invoke(user_question_1)
print(f"AI: {response_1}")
user_question_2 = "请问太阳系有哪些行星?" # 这个不需要联网查询
print(f"\n--- Query: {user_question_2} ---")
response_2 = final_chain_with_search.invoke(user_question_2)
print(f"AI: {response_2}")
user_question_3 = "昨晚中国队踢足球了吗?" # 这个可能需要联网查询
print(f"\n--- Query: {user_question_3} ---")
response_3 = final_chain_with_search.invoke(user_question_3)
print(f"AI: {response_3}")
解释:
-
Tool 定义: 我们创建了一个
SerpAPIWrapper实例,并将其包装成一个Tool对象,赋予它名称 (Google Search) 和描述 (A wrapper around Google Search...)。 -
Agent 的简化版: 示例并没有直接使用 LangChain 的
AgentExecutor(它负责更复杂的工具调用逻辑,包括循环判断、工具选择等)。而是采取了一种简化的思路:-
第一步: 用一个特殊的 Prompt (
prompt_template_search_keyword) 来让 LLM 判断是否需要搜索,以及需要搜索什么关键词。 -
第二步: 使用 Python 的逻辑(
run_search_tool函数,通过RunnableParallel.map应用)来根据 LLM 的判断,决定是调用 Google Search 还是直接跳过。 -
第三步: 将原始问题和实际的搜索结果(或“skip_search”标记)结合,构建最终的 Prompt (
final_answer_prompt)。 - 第四步: 将这个结合了搜索结果的 Prompt 再次喂给 LLM,生成最终的、更准确的回答。
-
第一步: 用一个特殊的 Prompt (
-
LCEL 语法:
-
|: 管道操作符,将一个组件的输出作为下一个组件的输入。 -
RunnableParallel: 允许您并行执行多个 runnable,并将它们的输出收集到一个字典中。 -
RunnableSequence: 类似于LLMChain,按顺序执行一系列 runnable。 -
lambda x: x: 一个简单的 lambda 函数,常用于将输入直接传递给下一个组件,或作为RunnableParallel的值。 -
map(): 在RunnableParallel中可以用来对每个结果应用一个函数。
-
- 这个例子展示了如何通过 LCEL 将LLM 判断逻辑、外部工具调用和多步 LLM Transformer 串联起来,构建一个能够根据情况决定是否联网搜索从而回答问题的应用。
总结:
LangChain 调用大语言模型的关键在于:
- 选择 LLM: 确定您要用的模型提供商 (OpenAI, Anthropic, Hugging Face 等)。
- 配置 API: 确保密钥已正确设置(环境变量)。
- 初始化 LLM 对象: 使用 LangChain 提供的具体 LLM 或 Chat Model 类。
-
构造 Prompt: 使用
PromptTemplate或ChatPromptTemplate来组织发送给 LLM 的指令。 -
构建 Chain/Runnable: 使用
LLMChain或更强大的 LCEL(LangChain Expression Language)来组合 LLM、Prompt,以及可能需要的工具(Tools)、Memory 组件等,形成一个完整的 LLM 应用流程。
通过这些组件的组合,您可以从一个简单的问答,到一个复杂的、能够联网、调用外部服务、进行多轮对话的应用, LangChain 都提供了强大的支持。
下面我们将使用示例中的模型改为阿里云的通义千问(Qwen)系列模型,并以 LangChain 为调用框架举例:
核心概念不变: LangChain 的核心目标是提供一个统一的接口来调用不同的 LLM。这意味着 LangChain 的 PromptTemplate、Chain、Runnable 等组件的用法与调用 OpenAI 模型时非常相似。主要区别在于初始化 LLM 对象的部分,以及可能需要安装阿里云 SDK 和配置相应的 API Key。
1. 阿里云通义千问系列模型与 LangChain
阿里云通义千问(Qwen)提供了一系列强大的大语言模型,包括 Qwen-7B, Qwen-14B, Qwen-72B, Qwen-VL (视觉-语言模型) 等。LangChain 已经有良好的集成支持。
主要集成组件:
-
ChatQwen(或Qwen):用于调用通义千问的聊天模型(推荐)。
2. 准备工作
-
安装相关库:
pip install langchain langchain-community qwen-sdk python-dotenv-
langchain-community: 包含了许多生态系统的集成,包括 Qwen。 -
qwen-sdk: 阿里云提供的通义千问 SDK。 -
python-dotenv: 用于加载环境变量。
-
-
获取阿里云 API Key:
- 您需要前往阿里云官方网站,注册并获得阿里云账号的 AccessKey ID 和 AccessKey Secret。
- 同时,您还需要为通义千问模型申请相应的API 调用权限。具体流程请参考阿里云官方文档。
-
配置环境变量(创建
.env文件):
在您的项目根目录下创建一个名为.env的文件,并填入您的阿里云 API Key:ALI_AK_ID=YOUR_ALI_AK_ID ALI_AK_SECRET=YOUR_ALI_AK_SECRET # 如果您使用了模型服务的代理,可能还需要配置代理信息 # HTTP_PROXY=http://your-proxy.com:port # HTTPS_PROXY=http://your-proxy.com:port
3. 修改示例代码
我们来逐个修改之前的示例。
示例 1:最简单的 LLM 调用 (改为 Qwen)
目标: 让通义千问模型回答一个简单的问题。
Python 代码:
import os
from dotenv import load_dotenv
from langchain_community.chat_models import ChatQwen # 导入 ChatQwen
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
# 1. 加载环境变量 (阿里云 AK)
load_dotenv()
# 2. 初始化 LLM 对象 (ChatQwen)
# model_name 指定您想要使用的通义千问模型,例如 "qwen-7b-chat", "qwen-14b-chat", "qwen-72b-chat"
# temperature 控制输出的随机性
# 如果使用了代理,可以在这里配置 proxy 参数
chat_qwen_llm = ChatQwen(
model_name="qwen-7b-chat", # 或者 "qwen-14b-chat", "qwen-72b-chat" 等
temperature=0.7,
# proxy="http://your-proxy.com:port" # 如果需要代理
)
# 3. 创建 Prompt Template (与之前相同)
prompt_template = PromptTemplate(
input_variables=["question"],
template="请简要介绍一下:{question}",
)
# 4. 创建 LLMChain (与之前相同)
# invoke() 是 LangChain 0.1.0 版本后推荐的调用方式
story_chain = LLMChain(llm=chat_qwen_llm, prompt=prompt_template)
# 5. 准备输入并调用 Chain
question_to_ask = "什么是人工智能?"
print(f"Asking Qwen LLM: '请简要介绍一下:{question_to_ask}'")
try:
# invoke() 接收一个字典,键必须与 prompt_template 的 input_variables 匹配
response = story_chain.invoke({"question": question_to_ask})
print("\n--- Qwen LLM Response ---")
# .invoke 默认返回一个字典,实际结果在 'text' 键下
print(response['text'])
except Exception as e:
print(f"An error occurred: {e}")
修改说明:
- 最主要的修改是
from langchain_openai import OpenAI改为from langchain_community.chat_models import ChatQwen。 - 初始化 LLM 对象时,创建的是
ChatQwen的实例,并且需要指定model_name。您需要根据自己获得的 Aliyun API 权限和模型选择合适的model_name。 - API Key 的配置也从 OpenAI 切换到了阿里云的 AccessKey ID 和 Secret。
- 其他部分(PromptTemplate, LLMChain, invoke 调用方式)保持不变。
示例 2:使用聊天模型和对话模板 (改为 Qwen)
目标: 进行一个简单的多轮对话,使用通义千问的聊天模型。
Python 代码:
import os
from dotenv import load_dotenv
from langchain_community.chat_models import ChatQwen # 导入 ChatQwen
from langchain.prompts import ChatPromptTemplate
from langchain.schema import HumanMessage, AIMessage, SystemMessage # 导入消息类型
from langchain.chains import LLMChain
# 1. 加载环境变量
load_dotenv()
# 2. 初始化 Chat Model 对象 (ChatQwen)
chat_qwen_llm = ChatQwen(
model_name="qwen-7b-chat", # 或其他 Qwen 聊天模型
temperature=0.7,
)
# 3. 创建 Chat Prompt Template (与之前相同)
chat_prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一个乐于助人的AI助手,擅长讲笑话。"), # 系统消息,设定AI的角色
("human", "{user_input}"), # 用户消息,包含用户输入
# ("ai", "{ai_response_history}") # 实际对话历史的管理通常通过 Memory 组件
])
# 4. 创建 LLMChain
conversation_chain = LLMChain(llm=chat_qwen_llm, prompt=chat_prompt_template)
# 5. 进行对话
print("--- Starting Conversation with Qwen ---")
# 第一轮对话
user_message_1 = "给我讲一个关于程序员的笑话。"
print(f"User: {user_message_1}")
# 第一次调用,prompt_template 中没有 ai_response_history 变量,所以直接传 user_input
response_1 = conversation_chain.invoke({"user_input": user_message_1})
ai_message_1 = response_1['text'] # ChatQwen 的输出会是 AIMessage 对象,其 content 属性是文本
print(f"AI: {ai_message_1}")
# 第二轮对话 (演示使用 Memory 的概念,虽然此处 LLMChain 未直接集成)
# 在实际应用中,您会使用 LangChain 的 Memory 组件来管理对话历史。
# 为了简化演示,我们手动构建一个消息列表
print("\n--- Continuing Conversation (simulated history with Qwen) ---")
user_message_2 = "这个笑话不错!再给我讲一个关于猫的。"
print(f"User: {user_message_2}")
# 手动构建包含对话历史的消息列表
# 注意:ChatQwen.invoke 接受一个消息列表作为输入
messages = [
SystemMessage(content="你是一个乐于助人的AI助手,擅长讲笑话。"),
HumanMessage(content=user_message_1),
AIMessage(content=ai_message_1), # 注入第一轮 AI 回复作为历史
HumanMessage(content=user_message_2)
]
# 直接调用 ChatQwen 的 invoke 方法,它接收消息列表
response_2 = chat_qwen_llm.invoke(messages)
print(f"AI: {response_2.content}")
修改说明:
- 同样,
ChatOpenAI改为ChatQwen。 -
model_name参数需要指定一个通义千问的聊天模型。 - 在演示多轮对话时,我们提到了 LangChain 的
Memory组件是管理对话历史的标准方式。虽然LLMChain本身并不直接暴露Memory的集成,但在实际应用中,您会发现 LCEL (LangChain Expression Language) 配合Memory组件的组合更加强大。 - 这里直接调用
chat_qwen_llm.invoke(messages)是因为ChatQwen的invoke方法可以直接接收一个消息列表,这与ChatOpenAI的行为一致。LLMChain的invoke仍然会根据"user_input"字典来格式化调用,对于复杂的历史管理,直接使用chat_qwen_llm.invoke或 LCEL 更为灵活。
示例 3:使用 LCEL 构建包含工具调用的流程 (改为 Qwen + 阿里云工具)
目标: 结合一个搜索工具(例如,模拟使用阿里云的搜索能力,或者使用其他可用工具),让通义千问模型能够查找信息并回答问题。
假设: 我们将使用 langchain-community 提供的 SerpAPIWrapper 作为搜索工具,因为它是一个通用接口,不依赖于特定的云服务商。如果您想使用阿里云自有的搜索服务,需要查找是否有相应的 LangChain 集成,并进行配置。
Python 代码:
import os
from dotenv import load_dotenv
from langchain_community.chat_models import ChatQwen
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableSequence, RunnableParallel
from langchain_core.tools import Tool
from langchain_community.tools import SerpAPIWrapper # 依然使用 SerpAPIWrapper 来演示
from langchain_core.output_parsers import StrOutputParser
# 1. 加载环境变量
load_dotenv()
# 2. 初始化 LLM (ChatQwen)
llm = ChatQwen(
model_name="qwen-7b-chat", # 或其他 Qwen 聊天模型
temperature=0.7,
)
# 3. 定义工具 (Search Tool)
# 这里我们继续使用 SerpAPIWrapper 作为搜索工具示例
# 在实际应用中,如果阿里云提供了类似的 LangChain 集成(如阿里云搜索服务),则使用它
search = SerpAPIWrapper()
tools = [
Tool(
name="Google Search",
description="A wrapper around Google Search. Useful for when you need to answer questions about current events or find information on the internet.",
func=search.run,
)
]
# 4. 创建 Prompt Template
# 指导 LLM 判断是否需要搜索,以及提取搜索关键词。
prompt_template_search_keyword = ChatPromptTemplate.from_messages([
("system", "你是一个助手,请判断用户的问题是否需要联网查询。如果需要,请只输出你认为最适合在Google上搜索的关键词。如果不需要,请回复 'skip_search'"),
("human", "{user_question}"),
])
# 5. 构建 LCEL 链
# 辅助函数 (逻辑不变)
def run_search_tool(data):
query = data["search_query"]
if query.lower() == "skip_search":
return {"search_result": "skip_search", "original_question": data["user_question"]}
else:
try:
# 注意: 在这里,搜索调用的 func 是 search.run
# 如果您使用的是阿里云的某个搜索服务,这里的 func 参数需要指向该服务的 LangChain 集成方法
search_result = search.run(query)
return {"search_result": search_result, "original_question": data["user_question"]}
except Exception as e:
print(f"Search tool error: {e}")
return {"search_result": "Error fetching search results.", "original_question": data["user_question"]}
# Step 1 & 2: 接收用户问题并提取搜索关键词
# run_search_query_chain 负责获取关键词
get_search_query_chain = (
{"user_question": lambda x: x, "search_query": prompt_template_search_keyword}
| llm # 直接将 prompt 结果通过管道传给 LLM
| StrOutputParser() # 提取 LLM (Qwen) 的文本输出
)
# Step 3: 执行搜索(或跳过)
tool_chain = RunnableParallel(
# inputs 字典的键会作为 run_search_tool 函数的参数名
search_query_result=get_search_query_chain,
# 保持原始用户问题,以便在后续步骤中使用
user_question=lambda x: x
).map(run_search_tool) # 传递整个字典给 run_search_tool, 它会解包并使用 search_query 和 user_question
# Step 4: 构建最终回答的 Prompt,并调用 LLM
final_answer_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个AI助手。请基于用户的问题和搜索到的信息(如果有)来回答问题。如果搜索结果为空或标记为 'skip_search',则仅根据你的知识回答。"),
("human", "用户问题: {original_question}\n\n搜索结果: \n{search_result}")
])
# 整个流程的最终链
# 初始输入是用户问题字符串
final_chain_with_search = RunnableSequence(
# 1. 准备 initial input (user_question string)
get_search_query_chain, # 这一步会输出 search_query + user_question
# 2. map(run_search_tool) 来处理搜索逻辑
lambda data: { # map 返回字典, 需要重新组织一下结构
"search_query": data[0], # search_query_result 的输出
"user_question": data[1] # user_question 的输出
},
RunnableParallel(
# Pass the input dictionary straight to tool_chain
# Wait for the output of tool_chain to be formatted correctly
**{
"search_query_output": lambda x: x # Placeholder
}
).from_config(RunnableParallel(
# Map search results back
search_result=tool_chain.get_input_schema()["search_result"],
original_question=tool_chain.get_input_schema()["original_question"]
)),
final_answer_prompt,
llm,
StrOutputParser()
)
# 重新组织一下 LCEL 的流程,使之更清晰
# 1. 准备 LLM 的输入,并调用 LLM 提取搜索关键词
prepare_search_input = RunnableParallel(
user_question=lambda x: x, # 保持原始问题
search_query_template=ChatPromptTemplate.from_messages([
("system", "你是一个助手,请判断用户的问题是否需要联网查询。如果需要,请只输出你认为最适合在Google上搜索的关键词。如果不需要,请回复 'skip_search'"),
("human", "{user_question}"),
])
)
# 2. 调用 LLM 获取搜索指令
get_search_command = prepare_search_input | {
"search_query": (
RunnableParallel(
search_query_template=lambda x: x["search_query_template"],
user_question=lambda x: x["user_question"],
)
| llm
| StrOutputParser()
),
"user_question": lambda x: x["user_question"], # 保持原始问题
}
# 3. 根据指令执行搜索,或者跳过
execute_or_skip_search = get_search_command.map(run_search_tool) # tool_chain is now execute_or_skip_search
# 4. 构建最终问题的 Prompt,并调用 LLM
final_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个AI助手。请基于用户的问题和搜索到的信息(如果有)来回答问题。如果搜索结果为空或标记为 'skip_search',则仅根据你的知识回答。"),
("human", "用户问题: {original_question}\n\n搜索结果: \n{search_result}")
])
# 最终的 Runnable Sequence
final_qwen_chain = RunnableSequence(
get_search_command,
execute_or_skip_search, # 这个返回一个字典 {search_result: ..., original_question: ...}
final_prompt,
llm,
StrOutputParser()
)
# 6. 运行 Chain
user_question_1 = "请问最近关于AI的新闻有哪些?"
print(f"\n--- Query: {user_question_1} ---")
response_1 = final_qwen_chain.invoke(user_question_1)
print(f"AI: {response_1}")
user_question_2 = "请问太阳系有哪些行星?" # 这个不需要联网查询
print(f"\n--- Query: {user_question_2} ---")
response_2 = final_qwen_chain.invoke(user_question_2)
print(f"AI: {response_2}")
user_question_3 = "昨晚的世界杯决赛比分是多少?" # 这个可能需要联网查询
print(f"\n--- Query: {user_question_3} ---")
response_3 = final_qwen_chain.invoke(user_question_3)
print(f"AI: {response_3}")
修改说明:
- LLM 部分被替换为
ChatQwen实例。 -
tools的定义与之前类似,只是func参数指向的工具(如search.run)仍然是通用的langchain-community提供的工具。如果您想使用阿里云的特定服务作为工具,需要查找对应的 LangChain 集成,并配置其 API Key。 - LCEL 的编写方式(使用
RunnableParallel,RunnableSequence,|,map等)完全保持不变。LangChain 的 LCEL 设计目标就是解耦,让您能够轻松替换底层 LLM 和工具。 -
run_search_tool函数的逻辑和final_answer_prompt也保持不变,因为它们关注的是数据处理和提示语组织,与 LLM 的具体提供商无关。
重要提示:
-
model_name: 您必须根据阿里云提供的模型列表和您的权限,选择合适的model_name。常见的如"qwen-7b-chat","qwen-14b-chat","qwen-72b-chat"。 - API 权限和配额: 确保您有足够的 API 调用额度,并且模型已经被正确授权。
-
代理配置: 如果您的网络环境需要使用代理才能访问阿里云的服务,请在初始化
ChatQwen时配置proxy参数。 - 错误处理: 在实际生产环境中,您需要更完善的错误处理机制,来应对 API 调用失败、模型返回异常等情况。
通过这些修改,您就成功地将 LangChain 应用切换到了阿里云的通义千问系列模型。 LangChain 的强大之处就在于其抽象层,使得模型更换变得相对容易。