LangChain 基础

LangChain 的核心是把模型、提示词、工具和调用流程包装成可以组合的 Python 应用代码,让 AI 能力从“调用一次模型”变成“组织一条链路”。

LangChain 是什么

LangChain 是 Python 生态里常用的 LLM 应用开发框架。

LLM,中文一般叫“大语言模型”。

如果直接调用模型 API,代码通常是:

准备 messages
调用模型接口
拿到回答
返回给用户

LangChain 在这个基础上继续包装:

模型
提示词
工具
记忆
检索
工作流
Agent

本节先不要把它想复杂。

本节只记住一句话:

LangChain 是把模型调用和应用逻辑组织起来的工具箱。

为什么要学 LangChain

前面已经学过:

直接调用 LLM API
Prompt 基础
JSON 结构化输出
流式输出
Tool Calling
FastAPI

这些能力都可以手写。

但项目变复杂以后,会遇到几个问题:

提示词越来越多
模型切换不方便
工具越来越多
调用步骤越来越长
RAG 需要加载、切片、检索、生成
Agent 需要循环调用模型和工具

LangChain 的价值是帮你把这些步骤拆成可组合的组件。

比如:

Prompt Template
  -> Chat Model
  -> Tool
  -> Agent

它不是替代大模型。

它是让你更方便地组织大模型应用。

安装依赖

学习阶段先安装最小依赖:

python -m pip install langchain langchain-openai python-dotenv

如果后面要做文本切片,再安装:

python -m pip install langchain-text-splitters

如果后面要用社区文档加载器,再安装:

python -m pip install langchain-community

建议 .env 保持和前面 Python 学习一致:

DASHSCOPE_API_KEY=你的百炼 API Key
DASHSCOPE_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
QWEN_MODEL=qwen-plus

Chat Model

Chat Model,中文可以理解为“聊天模型”。

它对应前面直接调用 API 时的模型客户端。

使用 DashScope 的 OpenAI 兼容接口时,可以用 ChatOpenAI

import os

from dotenv import load_dotenv
from langchain_openai import ChatOpenAI

load_dotenv()

model = ChatOpenAI(
    model=os.getenv("QWEN_MODEL", "qwen-plus"),
    api_key=os.environ["DASHSCOPE_API_KEY"],
    base_url=os.getenv(
        "DASHSCOPE_BASE_URL",
        "https://dashscope.aliyuncs.com/compatible-mode/v1",
    ),
    temperature=0.3,
)

response = model.invoke("用一句话解释 LangChain 是什么。")

print(response.content)

这里的 invoke 可以理解为“执行一次调用”。

返回值不是普通字符串,而是一个消息对象。

常用字段是:

content:模型回答文本
response_metadata:响应元信息
usage_metadata:token 用量信息

Messages

前面调用 OpenAI 兼容接口时,已经见过 messages:

messages = [
    {"role": "system", "content": "你是一个严谨的 AI 学习助手。"},
    {"role": "user", "content": "什么是 LangChain?"},
]

LangChain 也支持这种字典格式:

response = model.invoke([
    {
        "role": "system",
        "content": "你是一个严谨的 AI 学习助手。",
    },
    {
        "role": "user",
        "content": "什么是 LangChain?",
    },
])

print(response.content)

也可以使用消息对象:

from langchain_core.messages import HumanMessage, SystemMessage

response = model.invoke([
    SystemMessage(content="你是一个严谨的 AI 学习助手。"),
    HumanMessage(content="什么是 LangChain?"),
])

print(response.content)

学习阶段先用字典格式就够了。

它和前面的 LLM API 更像。

Prompt Template

Prompt Template,中文可以理解为“提示词模板”。

它的作用是把固定提示词和变量分开。

不要这样到处拼字符串:

role = "Java 后端工程师"
question = "怎么理解 Tool Calling?"

prompt = "你是" + role + ",请回答:" + question

用模板更清楚:

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages([
    (
        "system",
        "你是一个{role},回答要清楚、直接、适合初学者。",
    ),
    (
        "user",
        "{question}",
    ),
])

messages = prompt.invoke({
    "role": "Java 后端工程师",
    "question": "怎么理解 Tool Calling?",
})

response = model.invoke(messages)

print(response.content)

模板的好处是:

变量更明确
提示词更容易复用
后面能组合成 Chain
错误更容易定位

Chain

Chain,中文可以理解为“链”。

它表示把多个步骤串起来。

最小 Chain:

Prompt Template
  -> Chat Model

代码:

chain = prompt | model

response = chain.invoke({
    "role": "Java 后端工程师",
    "question": "怎么理解 RAG?",
})

print(response.content)

这里的 | 可以理解为管道。

左边的输出会交给右边。

流程是:

输入变量
  -> 渲染 prompt
  -> 调用模型
  -> 返回 AIMessage

这就是 LangChain 的基本组合方式。

加一个输出解析

如果只想要字符串,可以加一个输出解析器。

from langchain_core.output_parsers import StrOutputParser

chain = prompt | model | StrOutputParser()

answer = chain.invoke({
    "role": "Java 后端工程师",
    "question": "LangChain 的 Chain 是什么?",
})

print(answer)

流程变成:

Prompt Template
  -> Chat Model
  -> StrOutputParser
  -> string

这样 Controller 或 FastAPI 返回时更方便。

Tools 入门

Tools,中文就是“工具”。

前面已经学过 Tool Calling。

在 LangChain 里,可以用 @tool 把 Python 函数声明成工具:

from langchain.tools import tool


@tool
def query_order(order_id: str) -> str:
    """根据订单号查询订单状态。只用于订单发货、物流和售后进度查询。"""
    mock_orders = {
        "A10001": "订单 A10001 已发货,物流单号 SF123456。",
        "A10002": "订单 A10002 待发货,预计很快出库。",
    }
    return mock_orders.get(order_id, f"没有查询到订单 {order_id}。")

注意这个 docstring 很重要。

模型会根据它判断什么时候调用工具。

工具函数要尽量做到:

函数名清楚
参数类型明确
docstring 写明用途
返回值稳定
不要直接做高风险操作

创建一个 Agent

Agent,中文可以理解为“代理”或“智能体”。

在本节里,先把 Agent 理解为:

模型 + 工具 + 循环决策

最小示例:

from langchain.agents import create_agent

agent = create_agent(
    model=model,
    tools=[query_order],
    system_prompt="""
    你是一个售后客服助手。
    用户询问订单状态时,优先使用 query_order 工具。
    不要编造订单结果。
    """,
)

result = agent.invoke({
    "messages": [
        {
            "role": "user",
            "content": "帮我查一下订单 A10001 发货了吗?",
        }
    ]
})

print(result["messages"][-1].content)

这段代码里,模型可能会做这些事:

理解用户问题
判断需要查订单
生成工具参数 order_id=A10001
调用 query_order
拿到工具结果
生成最终回答

这和前面 Java Tool Calling 是同一个思想。

区别是这里放在 Python LangChain 里。

LangChain 和 LangGraph 的关系

先记一个简单区别:

LangChain:快速组合模型、提示词、工具、Agent
LangGraph:更底层地控制状态、节点、边和工作流

LangChain 适合先把应用跑起来。

LangGraph 适合工作流变复杂以后:

多步骤流程
条件分支
循环
人工确认
持久化状态
可观测执行路径

下一节会单独学习 LangGraph。

一个完整 demo

可以新建 langchain_basic_demo.py

import os

from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain.tools import tool
from langchain_openai import ChatOpenAI

load_dotenv()


@tool
def query_order(order_id: str) -> str:
    """根据订单号查询订单状态。只用于订单发货、物流和售后进度查询。"""
    mock_orders = {
        "A10001": "订单 A10001 已发货,物流单号 SF123456。",
        "A10002": "订单 A10002 待发货,预计很快出库。",
    }
    return mock_orders.get(order_id, f"没有查询到订单 {order_id}。")


model = ChatOpenAI(
    model=os.getenv("QWEN_MODEL", "qwen-plus"),
    api_key=os.environ["DASHSCOPE_API_KEY"],
    base_url=os.getenv(
        "DASHSCOPE_BASE_URL",
        "https://dashscope.aliyuncs.com/compatible-mode/v1",
    ),
    temperature=0.3,
)

agent = create_agent(
    model=model,
    tools=[query_order],
    system_prompt="""
    你是一个售后客服助手。
    用户询问订单状态时,优先使用 query_order 工具。
    不要编造订单结果。
    """,
)

result = agent.invoke({
    "messages": [
        {
            "role": "user",
            "content": "帮我查一下订单 A10001 发货了吗?",
        }
    ]
})

print(result["messages"][-1].content)

运行:

python langchain_basic_demo.py

常见问题

LangChain 会不会替代 OpenAI SDK

不会。

LangChain 底层仍然会调用模型供应商的 SDK 或 HTTP 接口。

它是更高一层的应用组织方式。

是不是所有 AI 项目都要用 LangChain

不是。

简单聊天接口,用 OpenAI SDK 或 Spring AI 就够。

当你需要 RAG、Agent、工具编排、链路追踪时,再引入 LangChain 更自然。

为什么工具描述很重要

因为模型看不到工具内部代码。

它主要根据工具名称、参数和描述来决定是否调用。

描述太模糊,模型就容易选错工具。

Chain 和 Agent 有什么区别

Chain 更像固定流程。

Agent 更像模型自己决定下一步。

简单理解:

Chain:你安排步骤
Agent:模型决定是否调用工具和怎么继续

练习清单

完成几件事情:

安装 langchain 和 langchain-openai
用 ChatOpenAI 调通 qwen-plus
用 messages 调用一次模型
用 ChatPromptTemplate 写一个模板
用 prompt | model 组成一个 Chain
加 StrOutputParser 返回字符串
用 @tool 定义 query_order
用 create_agent 创建一个带工具的 Agent
能解释 LangChain 和 LangGraph 的区别

建议目录:

ai-agent-study
├── python
│   └── langchain-basic
│       ├── langchain_basic_demo.py
│       ├── requirements.txt
│       └── README.md
└── docs
    └── langchain-basic.md

小结

本节的结论:

LangChain 先从 Chat Model、Prompt Template、Chain、Tool、Agent 这五个概念入手,不要一开始陷入复杂生态。

最小链路:

输入变量
  -> Prompt Template
  -> Chat Model
  -> Output Parser
  -> 返回结果

带工具链路:

用户问题
  -> Agent
  -> 模型判断是否需要工具
  -> Python 工具函数执行
  -> 模型生成最终回答
import argparse
import os
from collections.abc import Callable

from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain.tools import tool
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI


DEFAULT_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
DEFAULT_MODEL = "qwen3.5-flash"
DEFAULT_ROLE = "Java 后端工程师"
DEFAULT_QUESTION = "LangChain 的 Chain 是什么?"
DEFAULT_AGENT_QUESTION = "帮我查一下订单 A10001 发货了吗?"


load_dotenv()


@tool
def query_order(order_id: str) -> str:
    """根据订单号查询订单状态。只用于订单发货、物流和售后进度查询。"""
    mock_orders = {
        "A10001": "订单 A10001 已发货,物流单号 SF123456。",
        "A10002": "订单 A10002 待发货,预计很快出库。",
        "A10003": "订单 A10003 已签收,如需售后可以提交工单。",
    }
    return mock_orders.get(order_id, f"没有查询到订单 {order_id}。")


def build_model() -> ChatOpenAI:
    """创建 LangChain Chat Model,对接 DashScope OpenAI 兼容接口。"""
    return ChatOpenAI(
        model=os.getenv("QWEN_MODEL", DEFAULT_MODEL),
        api_key=required_env("DASHSCOPE_API_KEY"),
        base_url=os.getenv("DASHSCOPE_BASE_URL", DEFAULT_BASE_URL),
        temperature=0.3,
        max_completion_tokens=1024,
        extra_body={"enable_thinking": bool_env("QWEN_ENABLE_THINKING", False)},
    )


def run_chat_model(question: str) -> str:
    """Chat Model:直接把字符串交给模型。"""
    model = build_model()
    response = model.invoke(question)
    return response.content


def run_messages(question: str) -> str:
    """Messages:使用 system/user 消息调用模型。"""
    model = build_model()
    response = model.invoke(
        [
            {
                "role": "system",
                "content": "你是一个严谨的 AI 学习助手,用中文回答,回答要清楚、直接。",
            },
            {
                "role": "user",
                "content": question,
            },
        ]
    )
    return response.content


def build_prompt() -> ChatPromptTemplate:
    """Prompt Template:把固定提示词和变量分开。"""
    return ChatPromptTemplate.from_messages(
        [
            (
                "system",
                "你是一个{role},用中文回答,回答要清楚、直接、适合初学者。",
            ),
            (
                "user",
                "{question}",
            ),
        ]
    )


def run_prompt_template(question: str, role: str) -> str:
    """Prompt Template:先渲染模板,再调用模型。"""
    model = build_model()
    prompt = build_prompt()
    messages = prompt.invoke(
        {
            "role": role,
            "question": question,
        }
    )
    response = model.invoke(messages)
    return response.content


def run_chain(question: str, role: str) -> str:
    """Chain:用 prompt | model | parser 组成固定流程。"""
    model = build_model()
    prompt = build_prompt()
    chain = prompt | model | StrOutputParser()
    return chain.invoke(
        {
            "role": role,
            "question": question,
        }
    )


def run_agent(question: str) -> str:
    """Agent:让模型判断是否需要调用 query_order 工具。"""
    model = build_model()
    agent = create_agent(
        model=model,
        tools=[query_order],
        system_prompt=(
            "你是一个售后客服助手。"
            "用户询问订单状态时,优先使用 query_order 工具。"
            "不要编造订单结果。"
        ),
    )
    result = agent.invoke(
        {
            "messages": [
                {
                    "role": "user",
                    "content": question,
                }
            ]
        }
    )
    return result["messages"][-1].content


def required_env(name: str) -> str:
    value = os.getenv(name)
    if value:
        return value
    raise RuntimeError(f"请先在 .env 文件中配置 {name}")


def bool_env(name: str, default: bool) -> bool:
    value = os.getenv(name)
    if value is None:
        return default
    return value.strip().lower() in {"1", "true", "yes", "on"}


def print_section(title: str, value: str) -> None:
    print(f"\n=== {title} ===")
    print(value)


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Day11 LangChain 基础示例")
    parser.add_argument(
        "--mode",
        choices=["chat", "messages", "prompt", "chain", "agent", "all"],
        default="chain",
        help="选择要运行的 LangChain 示例",
    )
    parser.add_argument(
        "--question",
        default=DEFAULT_QUESTION,
        help="传给模型的问题",
    )
    parser.add_argument(
        "--role",
        default=DEFAULT_ROLE,
        help="Prompt Template 中的角色变量",
    )
    return parser.parse_args()


def main() -> None:
    args = parse_args()
    runners: dict[str, Callable[[], str]] = {
        "chat": lambda: run_chat_model(args.question),
        "messages": lambda: run_messages(args.question),
        "prompt": lambda: run_prompt_template(args.question, args.role),
        "chain": lambda: run_chain(args.question, args.role),
        "agent": lambda: run_agent(
            args.question if args.question != DEFAULT_QUESTION else DEFAULT_AGENT_QUESTION
        ),
    }

    if args.mode == "all":
        print_section("Chat Model", runners["chat"]())
        print_section("Messages", runners["messages"]())
        print_section("Prompt Template", runners["prompt"]())
        print_section("Chain", runners["chain"]())
        print_section("Agent + Tool", run_agent(DEFAULT_AGENT_QUESTION))
        return

    print(runners[args.mode]())


if __name__ == "__main__":
    main()

image.png

参考资料


这个家伙很懒,啥也没有留下😋