【大模型学习笔记】一篇文档读懂 LangChain 常用用法

轻松一刻:程序员最怕听到的四句话,按恐怖程度排序:“这个需求很简单。”,“用户不会那样操作的。”,“明天能上线吗?”,“你这代码我看不懂,重写一遍吧。”

在学习LangChain框架之前,先看一段不用框架调取大模型的代码demo.py(去掉了界面搭建部分,觉得太长可以先跳过,在后面与LangChain框架对比时会再分部分提及):

import gradio as gr
import json
import time
from datetime import datetime
import os
from typing import List, Dict, Optional
import requests
from dotenv import load_dotenv

load_dotenv()  # .env文件里面的 DEEPSEEK_API_KEY = "sk******"
class DeepSeekChat:
    def __init__(self, api_key: str = None, base_url: str = "https://api.deepseek.com"):
        """
        初始化DeepSeek聊天类
        Args:
            api_key: DeepSeek API密钥
            base_url: API基础URL
        """
        self.api_key = api_key or os.getenv("DEEPSEEK_API_KEY")
        if not self.api_key:
            raise ValueError("请设置DEEPSEEK_API_KEY环境变量或传入api_key参数")

        self.base_url = base_url
        self.conversation_history: List[Dict] = []
        self.system_prompt = "你是一个有帮助的AI助手。"
        self.temperature = 0.7
        self.max_tokens = 2000
        self.top_p = 0.95

    def chat(self, message: str, stream: bool = False) -> str:
        """
        发送聊天消息
        Args:
            message: 用户消息
            stream: 是否使用流式输出
        Returns:
            AI响应内容
        """
        # 构建消息历史
        messages = [{"role": "system", "content": self.system_prompt}]
        messages.extend(self.conversation_history)
        messages.append({"role": "user", "content": message})

        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }

        data = {
            "model": "deepseek-chat",
            "messages": messages,
            "temperature": self.temperature,
            "max_tokens": self.max_tokens,
            "top_p": self.top_p,
            "stream": stream
        }

        try:
            response = requests.post(
                f"{self.base_url}/v1/chat/completions",
                headers=headers,
                json=data,
                stream=stream,
                timeout=60
            )

            if stream:
                return self._handle_stream_response(response)
            else:
                return self._handle_normal_response(response)

        except requests.exceptions.RequestException as e:
            return f"请求失败: {str(e)}"
        except Exception as e:
            return f"发生错误: {str(e)}"

    def _handle_normal_response(self, response) -> str:
        """处理普通响应"""
        if response.status_code == 200:
            result = response.json()
            assistant_message = result["choices"][0]["message"]["content"]
            # 更新对话历史
            self.conversation_history.append({"role": "assistant", "content": assistant_message})
            return assistant_message
        else:
            return f"API错误: {response.status_code} - {response.text}"

    def _handle_stream_response(self, response) -> str:
        """处理流式响应"""
        full_response = ""
        for line in response.iter_lines():
            if line:
                line = line.decode('utf-8')
                if line.startswith('data: '):
                    data = line[6:]
                    if data != '[DONE]':
                        try:
                            chunk = json.loads(data)
                            if 'choices' in chunk and len(chunk['choices']) > 0:
                                delta = chunk['choices'][0].get('delta', {})
                                if 'content' in delta:
                                    content = delta['content']
                                    full_response += content
                                    yield content
                        except json.JSONDecodeError:
                            continue

        # 保存完整响应到历史
        self.conversation_history.append({"role": "assistant", "content": full_response})

    def clear_history(self):
        """清空对话历史"""
        self.conversation_history = []

    def get_history(self) -> List[Dict]:
        """获取对话历史"""
        return self.conversation_history

    def set_system_prompt(self, prompt: str):
        """设置系统提示"""
        self.system_prompt = prompt

    def set_parameters(self, temperature: float = None, max_tokens: int = None, top_p: float = None):
        """设置生成参数"""
        if temperature is not None:
            self.temperature = max(0, min(2, temperature))
        if max_tokens is not None:
            self.max_tokens = max(1, min(8192, max_tokens))
        if top_p is not None:
            self.top_p = max(0, min(1, top_p))

1. 为什么要学 LangChain

在demo中聊天流程靠request手动搭建

# 手动拼 messages
messages = [{"role":"system","content":prompt}]
messages.extend(history)
messages.append({"role":"user","content":msg})

# 手动发请求
response = requests.post(url, headers=headers, json=data)
reply = response.json()["choices"][0]["message"]["content"]

# 手动存历史
self.conversation_history.append({"role":"assistant","content":reply})

这段写法用LangChain写的话如下:

# 模板自动拼
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是AI助手"),
    MessagesPlaceholder("history"),
    ("human", "{input}"),
])

# 管道符串联,一行搞定
chain = prompt | llm

# 自动管历史
reply = chain_with_memory.invoke(
    {"input": msg},
    config={"configurable": {"session_id": sid}}
)

LangChain 的核心价值

  • 标准化:换模型只改 base_url 和 model,其余代码不用动
  • 可组合:用 | 管道符串联组件,像搭积木一样
  • 自动记忆RunnableWithMessageHistory 替代手动管 conversation_history
  • 生态丰富:RAG、向量库、工具调用、文档加载等都有现成封装
  • 面试加分:JD 里写 "熟悉 LangChain" 的比比皆是

2. 核心概念速览

LangChain 的所有功能围绕 Runnable(可运行组件)构建。每个组件都是一个 Runnable,可以用 | 串联:

组件作用你的对照
ChatOpenAI调用大模型替代 requests.post()
ChatPromptTemplate管理提示词模板替代手动拼 messages
OutputParser解析模型输出替代手动取 response["choices"][0]
Memory管理对话历史替代 conversation_history 列表
Retriever从向量库检索RAG 的核心组件
Tool包装工具函数Function Calling

一句话理解:LangChain = 把 "调用大模型" 这件事拆成标准化组件,用 | 管道符串联,像流水线一样运转。

3. ChatOpenAI —— 模型调用层

3.1 基本用法

ChatOpenAI 是 LangChain 对 OpenAI API 的包装。因为 DeepSeek 和通义千问都兼容 OpenAI API 格式,所以同一个类可以调三个不同的模型——只改参数就行。

from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
import os

load_dotenv()

# 创建 LLM 实例 —— 对比你 use_deepseek_test.py 里的写法
llm = ChatOpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",  # DeepSeek
    model="deepseek-chat",
    temperature=0.7,
    max_tokens=2000,
)

# 最简单的调用
result = llm.invoke("你好,你是谁?")
print(result.content)  # .content 就是回复文本
print(result.usage_metadata)  # token 用量

返回值说明:llm.invoke() 返回一个 AIMessage 对象,不是字符串。要取回复文本用 .content。这和 OpenAI SDK 的 response.choices[0].message.content 作用一样,但更简洁。

3.2 切换模型 —— 只改参数

# DeepSeek
llm_deepseek = ChatOpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",
    model="deepseek-chat",
)

# 通义千问 (阿里云 DashScope)
llm_qwen = ChatOpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    model="qwen-plus",
)

# OpenAI 原版
llm_openai = ChatOpenAI(
    api_key=os.getenv("OPENAI_API_KEY"),
    model="gpt-4o",
)

# 三个模型用法完全一样,因为都返回 AIMessage
for llm in [llm_deepseek, llm_qwen, llm_openai]:
    print(llm.invoke("1+1=?").content)

注意:ChatOpenAI 这个类名容易误解。它不是只能调 OpenAI,而是"兼容 OpenAI API 格式的模型都能调"。DeepSeek、通义千问、Moonshot 都兼容这个格式。

3.3 常用参数

参数作用推荐值
temperature随机性,越高越有创造力聊天 0.7 / 代码 0.1 / 分类 0
max_tokens最大生成 token 数2000-4096
model模型名称deepseek-chat / qwen-plus / gpt-4o
streaming是否流式输出True 时用 stream() 而非 invoke()

4. PromptTemplate —— 提示词模板

在 demo.py 里手动拼 messages:先加 system,再遍历 history,最后加 user。ChatPromptTemplate 把这件事模板化了。

4.1 基本模板

from langchain_core.prompts import ChatPromptTemplate

# 用 {变量名} 占位,调用时传值
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个{role},请用{style}的语气回答问题。"),
    ("human", "{question}"),
])

# 填充变量 —— 返回格式化后的 ChatPromptValue
formatted = prompt.invoke({
    "role": "翻译官",
    "style": "正式",
    "question": "Hello World",
})
print(formatted)  # 看看格式化后的 messages 长什么样

4.2 带对话历史的模板

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages([
    # 1. 系统提示 —— 定义 AI 的人设
    ("system", "你是一个有帮助的AI助手。用简洁的中文回答。"),

    # 2. 历史对话占位符 —— 自动插入之前的对话
    MessagesPlaceholder(variable_name="history"),

    # 3. 用户当前输入
    ("human", "{input}"),
])

# MessagesPlaceholder 等价于 demo.py 里的:
#   messages.extend(self.conversation_history)
# 但它是自动的,不用手动 extend

4.3 Few-Shot 模板(给几个例子让模型学)

from langchain_core.prompts import FewShotChatMessagePromptTemplate

# 例子:教模型做情感分析
examples = [
    {"input": "这个产品太好用了!", "output": "正面"},
    {"input": "质量很差,退货了", "output": "负面"},
    {"input": "还可以吧,一般般", "output": "中性"},
]

example_prompt = ChatPromptTemplate.from_messages([
    ("human", "{input}"),
    ("ai", "{output}"),
])

few_shot_prompt = FewShotChatMessagePromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
)

# 组合成完整 prompt
final_prompt = ChatPromptTemplate.from_messages([
    ("system", "判断用户评价的情感倾向,只回答: 正面/负面/中性"),
    few_shot_prompt,
    ("human", "{input}"),
])

什么时候用 Few-Shot?:当模型对任务理解不够好(比如自定义分类、特定格式输出),给 2-5 个例子比写一堆规则更有效。这是 prompt engineering 的核心技巧。

5. LCEL 管道符 —— 核心语法

LCEL(LangChain Expression Language)是 LangChain 的设计核心。| 管道符把多个 Runnable 串联成一条链,上一步的输出自动喂给下一步。

5.1 最简单的链

# prompt 的输出 → 喂给 llm
chain = prompt | llm

# 调用
result = chain.invoke({"input": "什么是RAG?"})
print(result.content)

5.2 加 OutputParser

from langchain_core.output_parsers import StrOutputParser

# 再加一个 |,链就变长了
chain = prompt | llm | StrOutputParser()

# StrOutputParser 把 AIMessage 对象变成纯字符串
result = chain.invoke({"input": "什么是RAG?"})
print(result)  # 直接是字符串,不用 .content 了

5.3 LCEL 的三种调用方式

# 1. invoke —— 普通调用,等完整结果
result = chain.invoke({"input": "你好"})

# 2. stream —— 流式调用,逐块返回(打字机效果)
for chunk in chain.stream({"input": "你好"}):
    print(chunk, end="", flush=True)

# 3. batch —— 批量调用,并行处理多个输入
results = chain.batch([
    {"input": "什么是AI"},
    {"input": "什么是ML"},
    {"input": "什么是DL"},
])
print(results)  # 返回 3 个结果

5.4 用 RunnablePassthrough 传额外数据

from langchain_core.runnables import RunnablePassthrough

# RunnablePassthrough 像一条"直通管道",把输入原样传到输出
# 在 RAG 中非常有用:一边检索,一边把用户问题透传给 prompt

chain = RunnablePassthrough.assign(
    context=lambda x: retriever.invoke(x["question"])
) | prompt | llm | StrOutputParser()

# 上面等价于:
# 1. 接收 {"question": "..."}
# 2. 用 question 去检索,结果存到 context 字段
# 3. 把 {question, context} 传给 prompt
# 4. prompt 输出喂给 llm
# 5. llm 输出喂给 parser

理解 LCEL 的关键:| 左边的组件输出,必须和右边的组件输入兼容。比如 prompt 输出的是 ChatPromptValuellm 接收的就是 ChatPromptValue,所以能接上。llm 输出的是 AIMessageStrOutputParser 接收 AIMessage,所以也能接上。

6. OutputParser —— 输出解析

LLM 的原始输出是 AIMessage 对象,但实际使用时可能要:取纯文本、解析 JSON、解析列表。OutputParser 负责这些。

6.1 StrOutputParser —— 取纯文本

from langchain_core.output_parsers import StrOutputParser

chain = prompt | llm | StrOutputParser()
# 输出直接是字符串,不需要 .content

6.2 JsonOutputParser —— 解析 JSON

from langchain_core.output_parsers import JsonOutputParser

parser = JsonOutputParser()

# 在 prompt 里告诉模型输出 JSON 格式
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是信息提取助手。输出JSON格式:\n{name, age, city}"),
    ("human", "{input}"),
])

chain = prompt | llm | parser

result = chain.invoke({"input": "张三,25岁,在北京工作"})
print(result)
# {'name': '张三', 'age': 25, 'city': '北京'}
print(type(result))  # <class 'dict'>

6.3 PydanticOutputParser —— 结构化解析(推荐)

from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field

# 1. 定义你想要的数据结构
class MovieInfo(BaseModel):
    title: str = Field(description="电影名称")
    rating: float = Field(description="评分 0-10")
    genre: list[str] = Field(description="类型标签")

# 2. 创建 parser,它会自动生成格式说明
parser = PydanticOutputParser(pydantic_object=MovieInfo)

# 3. 把格式说明塞进 prompt
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是电影信息提取助手。\n{format_instructions}"),
    ("human", "{input}"),
])
prompt = prompt.partial(format_instructions=parser.get_format_instructions())

chain = prompt | llm | parser

result = chain.invoke({"input": "我不是药神,评分9.0,剧情/现实题材"})
print(result)
# MovieInfo(title='我不是药神', rating=9.0, genre=['剧情', '现实题材'])
print(result.title)   # 我是药神
print(result.rating)  # 9.0

为什么要用 PydanticOutputParser?:它做了一件很聪明的事:自动把你的数据结构变成 JSON Schema 格式的提示词,让模型知道该输出什么格式。比手写"请输出JSON格式"靠谱得多。

7. Memory —— 对话记忆

你在 demo.py 里手动维护 conversation_history 列表。LangChain 用 RunnableWithMessageHistory 自动做这件事。

7.1 基本用法

from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

# 存储每个 session 的历史
chat_histories = {}

def get_history(session_id: str):
    if session_id not in chat_histories:
        chat_histories[session_id] = InMemoryChatMessageHistory()
    return chat_histories[session_id]

# 把 chain 包装成"有记忆的 chain"
chain_with_memory = RunnableWithMessageHistory(
    chain,              # 你之前的 chain (prompt | llm)
    get_history,       # 获取历史的函数
    input_messages_key="input",       # 用户输入的字段名
    history_messages_key="history",   # prompt 里 history 占位符的名字
)

# 调用时传入 session_id
reply1 = chain_with_memory.invoke(
    {"input": "我叫小明"},
    config={"configurable": {"session_id": "user_001"}}
)
# LangChain 自动: 把 "我叫小明" 存入 user_001 的历史

reply2 = chain_with_memory.invoke(
    {"input": "我叫什么名字?"},
    config={"configurable": {"session_id": "user_001"}}
)
# LangChain 自动: 从历史里读出 "我叫小明",模型知道你叫小明

与demo.py对比

demo.py 手动管理

# 手动存
self.conversation_history.append(
    {"role":"user",
     "content":message}
)
self.conversation_history.append(
    {"role":"assistant",
     "content":reply}
)
# 手动读
messages.extend(
    self.conversation_history
)
# 手动清
def clear_history(self):
    self.conversation_history = []

LangChain 自动管理

# 自动存 + 自动读
chain_with_memory.invoke(
    {"input": msg},
    config={"configurable":
        {"session_id": sid}}
)
# 手动清
get_history(sid).clear()
# 不同用户互不干扰
# session_id 就是隔离键

7.2 多用户隔离

# user_A 和 user_B 的历史互不干扰
chain_with_memory.invoke(
    {"input": "我叫张三"},
    config={"configurable": {"session_id": "user_A"}}
)

chain_with_memory.invoke(
    {"input": "我叫李四"},
    config={"configurable": {"session_id": "user_B"}}
)

# user_A 问 "我叫什么"
chain_with_memory.invoke(
    {"input": "我叫什么名字?"},
    config={"configurable": {"session_id": "user_A"}}
)
# 回答 "张三",因为只读了 user_A 的历史

7.3 历史过长怎么办?—— 总结式记忆

# 对话太长时,历史会占满 token。
# 思路:把旧对话让 LLM 总结成一段摘要,只保留摘要 + 最近几轮

# 在实际项目中可以用 RunnableWithMessageHistory + 自定义缩减逻辑
# 或者用 langchain-community 里的 ConversationSummaryBufferMemory
# 核心思路是:当 history 超过阈值时,用 llm 把旧消息压缩成摘要

# 简单版:手动限制历史长度
def get_history(session_id: str):
    if session_id not in chat_histories:
        chat_histories[session_id] = InMemoryChatMessageHistory()
    history = chat_histories[session_id]
    # 只保留最近 10 条消息
    if len(history.messages) > 10:
        history.messages = history.messages[-10:]
    return history

生产环境注意:InMemoryChatMessageHistory 存在内存里,重启就没了。生产环境要用 Redis 或数据库。LangChain 也支持 RedisChatMessageHistory,用法一样,只是换一个 get_history 函数。

8. 常用 Chain 模式

8.1 基础对话链

chain = prompt | llm | StrOutputParser()
result = chain.invoke({"input": "你好"})

8.2 带记忆的对话链

chain = prompt | llm
chain_with_memory = RunnableWithMessageHistory(
    chain, get_history,
    input_messages_key="input",
    history_messages_key="history",
)

8.3 结构化输出链

chain = prompt | llm | PydanticOutputParser(pydantic_object=MyModel)
result = chain.invoke({"input": "..."})
# result 是 MyModel 实例,不是字符串

8.4 RAG 链(检索 + 生成)

rag_chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

8.5 条件分支链 —— 根据输入走不同路径

from langchain_core.runnables import RunnableLambda

# 根据输入类型选择不同的处理方式
def route(input):
    if "代码" in input["input"]:
        return "code"
    return "chat"

chain = RunnableLambda(route).with_fallbacks(...)
# 或者用 RunnableBranch 做更复杂的分支

8.6 并行链 —— 同时做多件事

from langchain_core.runnables import RunnableParallel

# 同时翻译成英文和日文
parallel = RunnableParallel(
    english=english_chain,
    japanese=japanese_chain,
)
result = parallel.invoke({"input": "你好世界"})
print(result["english"])   # Hello World
print(result["japanese"])  # こんにちは世界

9. RAG —— 检索增强生成

RAG 是 LangChain 最常用的场景。核心思路:先从知识库里检索相关内容,再把内容塞进 prompt 让 LLM 基于它回答。

9.1 完整 RAG 代码

from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

# ① 加载文档
loader = TextLoader("my_doc.txt")
docs = loader.load()

# ② 切分文档 —— 把长文档切成小块
splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,        # 每块约 500 字符
    chunk_overlap=50,       # 块之间重叠 50 字符(避免切断语义)
)
chunks = splitter.split_documents(docs)

# ③ 向量化 + 存入 Chroma 向量库
embeddings = OpenAIEmbeddings(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",  # DeepSeek 也支持 embedding
)
vectorstore = Chroma.from_documents(chunks, embeddings)

# ④ 创建检索器
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})  # 每次检索 3 个最相关的块

# ⑤ 构建 RAG prompt
rag_prompt = ChatPromptTemplate.from_messages([
    ("system", """根据以下检索到的上下文回答用户问题。
如果上下文中没有相关信息,请说明"我没有找到相关信息"。

上下文:
{context}"""),
    ("human", "{question}"),
])

# ⑥ 构建 RAG 链
rag_chain = (
    {
        "context": retriever,                              # 检索相关文档
        "question": RunnablePassthrough()                  # 原样透传用户问题
    }
    | rag_prompt
    | llm
    | StrOutputParser()
)

# ⑦ 使用
answer = rag_chain.invoke("这个文档讲了什么?")
print(answer)

RAG 链的数据流:用户输入字符串 → 并行做两件事:retriever 检索文档(存入 context),RunnablePassthrough 原样传递(存入 question) → rag_prompt 接收 {context, question} 格式化 → llm 生成 → parser 取纯文本。整条链用 | 串联,每一步各司其职。

9.2 文档加载器

加载器用途安装
TextLoader纯文本内置
PyPDFLoaderPDF 文件pip install pypdf
WebBaseLoader网页pip install bs4
CSVLoaderCSV 文件内置
DirectoryLoader批量加载文件夹内置

9.3 向量库对比

向量库特点适用场景
Chroma纯 Python,内存/本地文件,零配置学习/原型/小项目
FAISSMeta 出品,高性能,本地大规模本地检索
Milvus分布式,云原生生产环境/百万级
Pinecone云托管,免运维SaaS 产品

学习建议:先用 Chroma 跑通 RAG 全链路,理解原理后再考虑 FAISS/Milvus。Chroma 零配置,pip 装完就能用,对学习阶段最友好。

10. Agent —— 工具调用

在 上一篇文章里手写了 Agent 循环(for 循环 + tool_calls 判断 + 工具执行)。LangChain 把这个过程也封装了。

10.1 用 LangChain 创建工具

from langchain_core.tools import tool

# 用 @tool 装饰器把普通函数变成 LangChain 工具
# 函数的 docstring 会自动变成工具描述,给 LLM 看的

@tool
def get_weather(city: str) -> str:
    """查询指定城市的天气"""
    # 你的实现
    return f"{city}: 晴天 28°C"

@tool
def calculate(expression: str) -> str:
    """计算数学表达式"""
    return str(eval(expression))

tools = [get_weather, calculate]

@tool 装饰器 vs 上一篇文章手写的 JSON Schema

在上一篇文章里手写了 TOOLS 列表,每个工具一大段 JSON。@tool 装饰器自动从函数签名和 docstring 生成 JSON Schema,省了很多模板代码。

10.2 创建 Agent

from langchain.agents import create_tool_calling_agent, AgentExecutor

# 1. 创建 Agent prompt
agent_prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个有用的助手。可以使用工具。"),
    MessagesPlaceholder("chat_history", optional=True),
    ("human", "{input}"),
    MessagesPlaceholder("agent_scratchpad"),  # Agent 的中间思考
])

# 2. 创建 Agent
agent = create_tool_calling_agent(llm, tools, agent_prompt)

# 3. 创建 AgentExecutor —— 自动处理循环
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# 4. 使用
result = agent_executor.invoke({"input": "北京天气怎么样?"})
print(result["output"])

对比 上一篇文章手写的 Agent 循环

上一篇文章手写了 for i in range(max_iterations) 循环,手动判断 msg.tool_calls,手动执行工具,手动把结果喂回。AgentExecutor 把这些全封装了——你只需要传工具和 prompt,它自动跑循环。

10.3 工具从函数到 JSON Schema 的自动转换

# 你 Day1 手写的
TOOLS = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "查询城市天气",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "城市名"}
            },
            "required": ["city"]
        }
    }
}]

# LangChain 的写法 —— 一个装饰器搞定
@tool
def get_weather(city: str) -> str:
    """查询指定城市的天气"""
    ...

# 工具的 JSON Schema 自动生成
print(get_weather.args_schema.model_json_schema())

11. 流式输出

在 demo.py 里手写了 _handle_stream_response,手动解析 SSE 数据。LangChain 用 stream() 一行搞定。

# 普通链的流式
chain = prompt | llm | StrOutputParser()

for chunk in chain.stream({"input": "写一首诗"}):
    print(chunk, end="", flush=True)
    # 每个 chunk 是一小段文字,逐块返回

# 带记忆的链也能流式
for chunk in chain_with_memory.stream(
    {"input": "写一首诗"},
    config={"configurable": {"session_id": "user_001"}}
):
    print(chunk, end="", flush=True)

配合 Gradio 实现打字机效果

import gradio as gr

def chat_stream(message, history):
    # Gradio 6.0 的流式写法 —— 用 yield
    partial = ""
    for chunk in chain_with_memory.stream(
        {"input": message},
        config={"configurable": {"session_id": "user_001"}}
    ):
        partial += chunk
        yield partial  # 每次返回累积的文本,Gradio 自动刷新

gr.ChatInterface(chat_stream).launch()

对比 demo.py 的手写流式

demo.py 的 _handle_stream_response 手动解析 data: 前缀、手动拼 full_response、手动处理 [DONE] 标记。LangChain 的 stream() 把这些全封装了,你只需要 for chunk in chain.stream()

12. 速查表

常用导入

# 模型
from langchain_openai import ChatOpenAI, OpenAIEmbeddings

# Prompt
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, FewShotChatMessagePromptTemplate

# Parser
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser, PydanticOutputParser

# Runnable
from langchain_core.runnables import RunnablePassthrough, RunnableParallel, RunnableLambda

# Memory
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

# 文档处理
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader, PyPDFLoader

# 向量库
from langchain_community.vectorstores import Chroma

# Agent
from langchain_core.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor

常用模式速查

场景代码
基础调用llm.invoke("你好").content
基础链chain = prompt | llm | StrOutputParser()
带记忆链RunnableWithMessageHistory(chain, get_history, ...)
RAG 链{"context": retriever, "question": RunnablePassthrough()} | prompt | llm | parser
流式for chunk in chain.stream(input): ...
批量results = chain.batch([input1, input2, input3])
并行RunnableParallel(a=chain1, b=chain2).invoke(input)
定义工具@tool def my_func(x: str) -> str: ...
AgentAgentExecutor(agent=create_tool_calling_agent(llm, tools, prompt), tools=tools)

学习路线建议

  1. 先跑通基础链prompt | llm | StrOutputParser(),理解数据怎么在链里流动
  2. 加记忆:用 RunnableWithMessageHistory,对比你 demo2.py 的手动管理
  3. 学 RAG:用 Chroma 跑通"加载→切分→向量化→检索→生成"全链路
  4. 学 Agent:用 @tool + create_tool_calling_agent,对比你 Day1 手写的循环
  5. 学流式:用 stream() + Gradio 做打字机效果
  6. 学结构化输出:用 PydanticOutputParser 让模型输出结构化数据

核心心法:LangChain 的一切都围绕 | 管道符。你不需要记住所有 API,只需要理解:左边组件的输出 = 右边组件的输入。遇到新组件,先看它接收什么、输出什么,就能知道怎么接进链里。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值