在这里插入图片描述

大家好,我是飞哥!👋

欢迎来到吴恩达《LangChain LLM 应用开发》系列课程的第二讲。这一讲我们深入 LangChain 的三大基石。

根据 Harrison Chase 的讲解,这三个概念是构建所有 AI 应用的基础。


1. 为什么:从“手搓代码”到“标准化组件”

Harrison 在视频开头通过一段代码展示了直接调用 OpenAI API 的方式。虽然看起来也不难,但当你想开发一个复杂的应用时,会有两个痛点:

  1. 复用性差:每次都要手写 Prompt 字符串。
  2. 结构化难:模型返回的是一堆文本,而你的程序可能需要一个 JSON 对象(比如 {"name": "Jack", "age": 18})。

LangChain 的出现,就是为了把这些操作标准化


2. 是什么:三大核心组件

2.1 Models (模型)

指的就是底层的语言模型(LLM),比如 OpenAI 的 gpt-3.5-turbo。LangChain 提供了一个统一的接口,让你切换模型就像换电池一样简单。飞哥在学习过程中就换成了deepseek。

2.2 Prompts (提示)

指的是构建输入的方式。LangChain 使用 PromptTemplate 来管理提示词,让你可以像填空题一样动态生成 Prompt。

2.3 Parsers (解析器)

指的是处理输出的方式。模型吐出的是文本,但我们需要的是结构化数据(JSON)。OutputParser 就是那个负责“翻译”的组件。


3. 环境准备:API Key (DeepSeek) 与 .env 🛠️

在开始写代码之前,我们需要先搞定“入场券”。考虑到 OpenAI 的额度限制和国内访问的不便,我们这里推荐使用 DeepSeek (深度求索)

为什么选择 DeepSeek?

  • 兼容性强:DeepSeek 完美兼容 OpenAI 的 API 格式,代码几乎不用改。
  • 性价比高:价格极其亲民,且赠送额度大方。
  • 访问稳定:作为国产模型,国内直连速度快,无需魔法。

3.1 获取 DeepSeek API Key 🔑

  1. 注册/登录:访问 DeepSeek 开放平台
  2. 创建 Key:点击左侧菜单的 API keys -> Create API key
  3. 复制保存:生成的 sk-... 开头的字符串就是你的 Key。注意:只会显示一次,一定要马上复制保存!

3.2 配置 .env 文件 ⚙️

为了不把敏感的 Key 直接写在代码里(容易泄露),我们通常使用 .env 文件来管理环境变量。

  1. 创建文件:在你的项目根目录下(建议放在 09_吴恩达_LangChain_全系列 根目录或者当前脚本同级目录),创建一个名为 .env 的文件(注意前面有个点)。
    • 统一配置:如果你希望所有项目共用一个 Key,可以把 .env 放在 09_吴恩达_LangChain_全系列/env/ 目录下。
  2. 写入内容
    你需要配置 Key、API 地址 (Base URL) 和模型名称。
    # DeepSeek 配置
    OPENAI_API_KEY=sk-你的DeepSeek密钥在这
    OPENAI_API_BASE=https://api.deepseek.com
    OPENAI_MODEL_NAME=deepseek-chat
    
  3. 代码调用
    Python 的 dotenv 库会自动寻找并读取这个文件。
    import os
    from dotenv import load_dotenv, find_dotenv
    
    # 方式一:自动寻找当前或父级目录下的 .env
    # _ = load_dotenv(find_dotenv()) 
    
    # 方式二:指定路径加载(适合统一管理 Key)
    # 假设你的 .env 在 ../../env/.env
    env_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../env/.env'))
    if os.path.exists(env_path):
        _ = load_dotenv(env_path)
    else:
        _ = load_dotenv(find_dotenv())
    
    # 读取配置
    openai.api_key = os.environ['OPENAI_API_KEY']
    openai.api_base = os.environ.get('OPENAI_API_BASE', 'https://api.openai.com/v1')
    model_name = os.environ.get('OPENAI_MODEL_NAME', 'gpt-3.5-turbo')
    

3.3 安装依赖库 📦

在运行代码之前,请确保你已经安装了以下 Python 库:

pip install python-dotenv openai langchain langchain-openai langchain-core

飞哥提示:一定要把 .env 加入到 .gitignore 文件中,千万别推送到 GitHub 上,否则你的钱包可能会被刷爆!💸


4. 怎么用:实战代码 (Models_Prompts_Parsers.py)

代码已整理为可运行的 Python 脚本,位于本目录下的 Models_Prompts_Parsers.py

让我们跟着视频里的案例,一步步实现这个功能。

4.1 传统方式 (Direct API Call)

首先,Harrison 定义了一个辅助函数 get_completion,这是我们熟悉的直接调用方式:

import os
import openai
from dotenv import load_dotenv, find_dotenv

# 加载环境变量
# 1. 自动寻找 .env 文件
# _ = load_dotenv(find_dotenv()) 

# 2. 指定路径加载 .env 文件 (推荐)
env_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '.env'))
if os.path.exists(env_path):
    _ = load_dotenv(env_path)
else:
    # 如果找不到统一的 env 文件,尝试自动寻找
    _ = load_dotenv(find_dotenv())

openai.api_key = os.environ['OPENAI_API_KEY']
openai.api_base = os.environ.get('OPENAI_API_BASE', 'https://api.openai.com/v1')
model_name = os.environ.get('OPENAI_MODEL_NAME', 'gpt-3.5-turbo')

from openai import OpenAI

client = OpenAI(
    api_key=openai.api_key,
    base_url=openai.api_base
)

def get_completion(prompt, model=model_name):
    messages = [{"role": "user", "content": prompt}]
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0, 
    )
    return response.choices[0].message.content

# 模拟一封客户邮件
# 这封邮件是用“海盗英语”写的,非常口语化且带有强烈的情绪
customer_email = """
Arrr, I be fuming that me blender lid flew off and splattered my kitchen walls with smoothie! 
And to make matters worse, the warranty don't cover the cost of cleaning up my kitchen. 
I need your help right now, matey!
"""
# 中文翻译:
# 啊!我气炸了!我的搅拌机盖子飞了出去,果昔溅得厨房墙上到处都是!
# 更糟糕的是,保修竟然不包清理厨房的费用。
# 伙计,我现在就需要你的帮助!

# 定义 Prompt:把这封邮件翻译成美式英语,语气要平静、尊敬
# 目标:将愤怒的海盗口吻转换为平静、尊敬的美式英语
style = """American English in a calm and respectful tone"""
# 中文含义:平静且尊重的语气的美式英语

prompt = f"""Translate the text that is delimited by triple backticks 
into a style that is {style}.
text: ```{customer_email}```
"""
# 中文含义:将用三个反引号分隔的文本翻译成{style}风格。

print(get_completion(prompt))

运行结果示例

I am quite upset that my blender lid flew off and splattered smoothie all over my kitchen walls. To make matters worse, the warranty does not cover the cost of cleaning up the mess. I would really appreciate your help with this as soon as possible.

这一步展示了如果不复用 Prompt,每次都要拼接字符串,很麻烦。

4.2 LangChain 方式:Prompt Templates

现在我们用 LangChain 来重构。

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

# 1. 初始化模型
chat = ChatOpenAI(
    temperature=0.0,
    model=model_name,
    api_key=openai.api_key, # 使用 api_key 而不是 openai_api_key
    base_url=openai.api_base # 使用 base_url 而不是 openai_api_base
)

# 2. 定义模板 (填空题)
# 使用 PromptTemplate 可以让我们复用 Prompt 结构,只需要填入不同的变量
template_string = """Translate the text that is delimited by triple backticks \
into a style that is {style}. \
text: ```{text}```
"""
# 中文含义:将用三个反引号分隔的文本翻译成{style}风格。

prompt_template = ChatPromptTemplate.from_template(template_string)

# 3. 看看模板长什么样
print(prompt_template.messages[0].prompt.template)

运行结果示例

Translate the text that is delimited by triple backticks into a style that is {style}. text: ```{text}```
# 4. 填入变量 (生成真正的 Prompt)
customer_style = """American English in a calm and respectful tone"""
# 中文含义:平静且尊重的语气的美式英语

customer_email = """Arrr, I be fuming that me blender lid flew off..."""
# 中文含义:(海盗英语片段) 啊,我气炸了,我的搅拌机盖子飞出去了...

customer_messages = prompt_template.format_messages(
    style=customer_style,
    text=customer_email
)
# format_messages 方法会将变量填入模板,并返回一个消息对象列表 (List[BaseMessage])
# 这里返回的是一个包含单个 HumanMessage 的列表

print(type(customer_messages[0]))
print(customer_messages[0].content)

运行结果示例

<class 'langchain_core.messages.human.HumanMessage'>
Translate the text that is delimited by triple backticks into a style that is American English in a calm and respectful tone. text: ```Arrr, I be fuming that me blender lid flew off...```
# 5. 调用模型
customer_response = chat(customer_messages)
print(customer_response.content)

运行结果示例

I am quite upset that my blender lid flew off and splattered smoothie all over my kitchen walls. To make matters worse, the warranty does not cover the cost of cleaning. I would really appreciate your help with this as soon as possible.

中文翻译

我非常生气,我的搅拌机盖子飞了出去,把果昔溅得厨房墙上到处都是。更糟糕的是,保修不包括清洁费用。我真的非常希望能尽快得到您的帮助。

4.3 LangChain 方式:Output Parsers

接下来,Harrison 展示了一个更高级的需求:从商品评论中提取 JSON 数据

假设我们希望 AI 分析评论,并返回这样的 JSON:

{
  "gift": false,
  "delivery_days": 5,
  "price_value": "unknown"
}

如果直接问 AI,它可能会回一大段话。我们需要它回 JSON。

from langchain.output_parsers import ResponseSchema, StructuredOutputParser

# 1. 定义我们想要的字段 (Schema)
# gift: 这是一个布尔值,表示商品是否作为礼物购买
gift_schema = ResponseSchema(
    name="gift",
    description="Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.",
)
# 中文含义:这件商品是买来送给别人的礼物吗?如果是回答 True,如果不是或未知回答 False。

# delivery_days: 这是一个数字,表示送货天数
delivery_days_schema = ResponseSchema(
    name="delivery_days",
    description="How many days did it take to arrive? If this information is not found, output -1.",
)
# 中文含义:这件商品用了几天才送达?如果找不到这个信息,输出 -1。

# price_value: 这是一个列表,提取关于价格或价值的句子
price_value_schema = ResponseSchema(
    name="price_value",
    description="Extract any sentences about the value or price, and output them as a comma separated Python list.",
)
# 中文含义:提取任何关于价值或价格的句子,并将它们作为一个逗号分隔的 Python 列表输出。

response_schemas = [gift_schema, delivery_days_schema, price_value_schema]

# 2. 创建解析器
# StructuredOutputParser 会自动生成一段 Prompt 指令,告诉模型如何输出 JSON
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)

# 3. 获取格式说明 (这段神奇的代码会自动生成一段 Prompt,告诉 AI 要怎么输出 JSON)
format_instructions = output_parser.get_format_instructions()
print(format_instructions) 

运行结果示例

The output should be a markdown code snippet formatted in the following schema, including the leading and trailing "```json" and "```":
{
	"gift": string  // Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.
	"delivery_days": string  // How many days did it take to arrive? If this information is not found, output -1.
	"price_value": string  // Extract any sentences about the value or price, and output them as a comma separated Python list.
}
# 你会看到一段很长的 Prompt,告诉 AI 必须输出 markdown code snippet formatted as a JSON object...
# 4. 把格式说明加到 Prompt 里
review_template = """\
For the following text, extract the following information:

gift: Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.

delivery_days: How many days did it take to arrive? If this information is not found, output -1.

price_value: Extract any sentences about the value or price, and output them as a comma separated Python list.

text: {text}

{format_instructions}
"""
# 中文含义:
# 对于以下文本,提取以下信息:
# gift: 商品是买来送给别人的礼物吗?如果是回答 True,否则回答 False。
# delivery_days: 几天送达?没找到输出 -1。
# price_value: 提取关于价格的句子。
# text: {text}
# {format_instructions}

prompt = ChatPromptTemplate.from_template(review_template)

# 5. 调用并解析
# 这里我们输入一段用户的评论,看看模型能否提取出结构化数据
# 评论内容:
# 这款吹叶机太棒了。它有四种设置:吹蜡烛、微风、风城和龙卷风。
# 它两天就到了,正好赶上我妻子的周年纪念礼物。
# 我想我妻子太喜欢它了,以至于说不出话来。
# 到目前为止,我是唯一一个使用它的人,我每隔一天早上用它来清理草坪上的树叶。
# 它比外面的其他吹叶机稍微贵一点,但我认为为了这些额外的功能是值得的。
messages = prompt.format_messages(
    text="This leaf blower is pretty amazing. It has four settings: candle blower, gentle breeze, windy city, and tornado. It arrived in two days, just in time for my wife's anniversary present. I think my wife liked it so much she was speechless. So far I've been the only one using it, and I've been using it every other morning to clear the leaves on our lawn. It's slightly more expensive than the other leaf blowers out there, but I think it's worth it for the extra features.",
    format_instructions=format_instructions
)

response = chat(messages)
print(response.content)

运行结果示例

{
    "gift": "True",
    "delivery_days": "2",
    "price_value": "[\"It's slightly more expensive than the other leaf blowers out there, but I think it's worth it for the extra features.\"]"
}
output_dict = output_parser.parse(response.content)

print(output_dict)
print(type(output_dict)) # <class 'dict'> 成功变成了 Python 字典!

运行结果示例

{'gift': 'True', 'delivery_days': '2', 'price_value': '["It\'s slightly more expensive than the other leaf blowers out there, but I think it\'s worth it for the extra features."]'}
<class 'dict'>

📝 飞哥总结

这一讲的核心在于“规范化”:

  1. PromptTemplate:把“怎么问”固化成模板,以后只需要换变量。
  2. OutputParser:把“怎么答”固化成代码,让 AI 输出机器可读的 JSON。

一句话记住它:LangChain 帮你把 AI 那个随意的“嘴”,变成了严谨的“接口”。

下一讲,我们来解决 AI “健忘” 的问题 —— Memory (记忆)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐