19.RAG开发-06-LangChain调用大语言模型

下面我们来详细举例说明 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 的基本流程

  1. 选择 LLM 提供商: 确定您想使用的 LLM(例如 OpenAI 的 GPT-3.5-turbo, GPT-4, Anthropic 的 Claude, Hugging Face 的 Llama2 等)。
  2. 安装对应的集成库: 为您选择的 LLM 安装必要的 Python 包。
  3. 配置 API 密钥: 设置 LLM 提供商的 API 密钥(通常通过环境变量)。
  4. 初始化 LLM 对象: 在 LangChain 中创建一个代表该 LLM 的实例。
  5. 调用 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 回答一个简单的问题。

准备工作:

  1. 安装:
    pip install langchain langchain-openai openai python-dotenv
    
  2. 创建 .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 的指令。
  • LLMChainllmprompt_template “粘合”在一起。当您调用 story_chain.invoke({"question": "太阳系"}) 时:
    1. LangChain 会将输入字典 {"question": "太阳系"} 填充到 prompt_template 中,生成完整的提示字符串:“请简要介绍一下:太阳系”。
    2. 这个完整的提示字符串被发送到 llm (OpenAI 模型)。
    3. OpenAI 模型处理这个提示并生成一个回答。
    4. LangChain 接收到模型的输出,并将其封装在一个字典中返回,其中关键结果在 'text' 键下。

4. 示例 2:使用聊天模型和对话模板 (使用 ChatOpenAI)

聊天模型(如 GPT-3.5-turbo, GPT-4)更适合处理多轮对话,它们期望的输入格式是消息列表(系统消息、用户消息、AI 消息)。LangChain 提供了 ChatPromptTemplateChatOpenAI 来方便地处理这类任务。

目标: 进行一个简单的多轮对话。

准备工作: (同上,只需要确保 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 的回复。
  • 当您将这个 ChatPromptTemplateChatOpenAI 结合使用时(通过 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 能够查找信息并回答问题。

准备工作:

  1. 安装:
    pip install langchain langchain-openai serpapi python-dotenv beautifulsoup4
    
    • serpapi: 用于调用 Google 搜索 API。
  2. 创建 .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(它负责更复杂的工具调用逻辑,包括循环判断、工具选择等)。而是采取了一种简化的思路
    1. 第一步: 用一个特殊的 Prompt (prompt_template_search_keyword) 来让 LLM 判断是否需要搜索,以及需要搜索什么关键词
    2. 第二步: 使用 Python 的逻辑(run_search_tool 函数,通过 RunnableParallel.map 应用)来根据 LLM 的判断,决定是调用 Google Search 还是直接跳过。
    3. 第三步: 将原始问题和实际的搜索结果(或“skip_search”标记)结合,构建最终的 Prompt (final_answer_prompt)。
    4. 第四步: 将这个结合了搜索结果的 Prompt 再次喂给 LLM,生成最终的、更准确的回答。
  • LCEL 语法:
    • |: 管道操作符,将一个组件的输出作为下一个组件的输入。
    • RunnableParallel: 允许您并行执行多个 runnable,并将它们的输出收集到一个字典中。
    • RunnableSequence: 类似于 LLMChain,按顺序执行一系列 runnable。
    • lambda x: x: 一个简单的 lambda 函数,常用于将输入直接传递给下一个组件,或作为 RunnableParallel 的值。
    • map(): 在 RunnableParallel 中可以用来对每个结果应用一个函数。
  • 这个例子展示了如何通过 LCEL 将LLM 判断逻辑外部工具调用多步 LLM Transformer 串联起来,构建一个能够根据情况决定是否联网搜索从而回答问题的应用。

总结:

LangChain 调用大语言模型的关键在于:

  1. 选择 LLM: 确定您要用的模型提供商 (OpenAI, Anthropic, Hugging Face 等)。
  2. 配置 API: 确保密钥已正确设置(环境变量)。
  3. 初始化 LLM 对象: 使用 LangChain 提供的具体 LLM 或 Chat Model 类。
  4. 构造 Prompt: 使用 PromptTemplateChatPromptTemplate 来组织发送给 LLM 的指令。
  5. 构建 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. 准备工作

  1. 安装相关库:

    pip install langchain langchain-community qwen-sdk python-dotenv
    
    • langchain-community: 包含了许多生态系统的集成,包括 Qwen。
    • qwen-sdk: 阿里云提供的通义千问 SDK。
    • python-dotenv: 用于加载环境变量。
  2. 获取阿里云 API Key:

    • 您需要前往阿里云官方网站,注册并获得阿里云账号的 AccessKey ID 和 AccessKey Secret
    • 同时,您还需要为通义千问模型申请相应的API 调用权限。具体流程请参考阿里云官方文档。
  3. 配置环境变量(创建 .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) 是因为 ChatQweninvoke 方法可以直接接收一个消息列表,这与 ChatOpenAI 的行为一致。LLMChaininvoke 仍然会根据 "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 的强大之处就在于其抽象层,使得模型更换变得相对容易。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容