03 大模型核心开发框架

01 大模型基础能力构建 之后,继续阅读该篇。


9 LangChain概述与架构

如果是java选手,学习过langchain4j,上手这部分内容应该很快。

先详细看看课程目标

  • 建立对 LangChain 的整体认识,知道它是什么、解决什么问题、在 AI 应用开发里处于什么位置。
  • 理解 LangChain 在 1.x 时代的产品边界、包结构、版本演进,以及它与 LangGraph、LangSmith、Deep Agents 的关系。
  • 建立对 Model I/O、Chains、Memory、Retrieval、Tools/Agents、Callbacks 这六类教学视角的整体认知

看完第9章建立认知,第10章实践进行熟悉。

1 Langchain简介

LangChain 是一个面向 LLM 应用开发的开源框架。它本身不是大模型,也不是数据库,更不是知识库平台;它更像一层“应用编排层”,负责把模型、提示词、外部知识、工具、记忆、输出解析、调试追踪等能力组织成一个完整、可维护、可扩展的 AI 应用。

Langchain是 用代码把大模型和外部世界连接起来的应用开发框架

Langchain在于把原本零散的东西组织起来,

  • 不同厂商的大模型调用方式
  • Prompt 与多角色消息
  • 结构化输出与解析
  • 检索增强生成(RAG)
  • 工具调用、Agent、多步任务
  • 记忆、会话状态、持久化
  • 日志、追踪、调试、评估

直接调API可以,适合简单任务,但是做应用,框架就会越来越有价值。

如下图所示的对比,可以利用框架进一步抽象,隔离,进行统一编排,统一接入在这里插入图片描述
Coze/Dify等平台是低代码平台,受限于平台。

Langchain是代码框架或库,在 Python 或 JS 中以代码方式编排模型、工具、RAG、Agent

实际项目里可以先低代码验证,再代码化落地。

现在版本更稳定,1.x版本,学习入口更清晰,重点放在 create_agent、统一模型初始化、消息、工具、记忆、检索和可观测性上。

LangChain、LangGraph、LangSmith 的分工更明确:你更容易理解“什么时候用高层框架,什么时候用底层图编排,什么时候做调试与评估”

LangChain 的优势很明显,但它也不是银弹。学习时要同时看到它的“能做什么”和“不能替你做什么”。

优势:

  • 统一模型接口,降低模型切换和多模型共存成本。
  • 提供 Prompt、Message、Parser、Retriever、Tool、Agent 等常用抽象
  • 能把“单次调用”提升为“可维护流程”。
  • 与 LangGraph、LangSmith、MCP 等生态衔接紧密,利于走向复杂应用。
  • 资料多、案例多、社区活跃,适合建立体系化认知。

边界:

  • 它不会替你设计业务逻辑,只是帮你更好地实现。
  • 它不会替你训练模型,模型能力强弱仍取决于底层模型。
  • 它不会替你存业务数据,向量库、数据库、对象存储仍要你自己选型。
  • 它不会天然让效果变好,Prompt、知识质量、工具设计、评估流程依然是关键。

不足与槽点:

  • 版本变化快:老教程里的类名、导入路径、写法,很可能在新版本里已经迁移。
  • 抽象多,学习成本不低:初学时容易觉得“明明只是调个模型,为什么多了这么多概念”。
  • 历史包袱真实存在:很多文章还在讲 LLMChain、ConversationChain、旧版 AgentExecutor,但新项目未必应照搬。
  • 文档存在新旧并存现象:官方文档已经比早期稳定很多,但生态广,仍会碰到版本语境不一致的问题。

所以结论很明确:LangChain 适合做 AI 应用,但要用“工程框架”的心态去学,不要把它当成一个简单工具函数库。

2 LangChain定位

2.1 大模型开发体系分类

按照大模型相关工作,从底到顶的一个分类

  • 基础模型层
  • 模型定制层
  • 应用开发层

LangChain 主要服务的,就是第三层:应用开发层。负责如何把模型能力变成一个能工作的应用系统。显然LangChain,LangGraph是服务编排层。

数据流:请求 自上而下(用户 → 服务/链 → 模型 → 存储),结果 自下而上返回用户;LangChain 处在 服务/编排层,串联 UI、模型与存储,而不是替代其中任一层。

2.3 使用场景

真实项目里,LangChain往往会承担下面几件事:

  • 统一接模型
  • 统一处理输入输出:用PromptTemplate、消息对象、输出解析器来规范请求与结果
  • 组织固定流程
  • 接入工具与外部系统
  • 管理状态与记忆
  • 做调试与评估

学习框架,掌握这套思维,再迁移到其他框架会快很多。

这里的简历看一下,在这里插入图片描述

3 LangChain包与版本管理

3.1 版本演进:从链到Agent + 图 + 生态

  • 第一阶段:0.0.x/0.1早期版本,特点是链优先
  • 第二阶段:0.2,0.3阶段,开始重视生态拆分和工程边界
  • 第三阶段:1.x阶段,重点变成精简主包,强化Agent,与LangGraph深度融合
    2025年10月20日发布LangChainv1.0.0

3.2 当前官方产品线理解

官方讲一整套出产品线。

  • LangChain:高层应用框架,快速构建,屏蔽大量底层细节
  • LangGraph:低层图编排与运行时
  • LangSmith:可观测性、评估、调试、部署平台,用来追踪、调试、评估和上线应用
  • Deep Agents:开箱即用的复杂Agent方法,建立在Langchain/ LangGraph之上

3.3 0.x与1.x的核心差异

如下图所示:
在这里插入图片描述
1.x更像精简主包 + 统一入口 + Agent建立在LangGraph之上

下面有一个0.x和1.x版本对比:

对比项0.x写法1.x写法
Agent 创建常见要手动组装Agent + Executor以create_agent为主入口
模型初始化常见直接记忆具体类或旧导入路径更强调统一入口,如init_chat_model
主包内容langchain中内容很多,历史包袱重langchain主包被精简,只聚焦核心能力
旧能力去向LLMChain, 老Retriever等混在主包中迁到langchain-classic
底层运行时以前很多人只感受到链现在agent明前建立在langgraph之上
python版本老资料中常见3.8, 3.9官方1.x要求python3.10

3.4 LangChain常见包

按照官方主线重新梳理

  • langchain-core:核心抽象层,包含消息、Runnable、工具基础、Prompt 基础、模型接口等,是整个生态的底座
  • langchain:高级应用框架主包,1.x中聚焦核心高层能力,如Agent,统一模型初始化,消息与工具等
  • langchain-openai/ langchain-anthropic/langchain-allama:厂商/provider集成包,每个模型 厂商或平台通常有自己的独立集成包,便于独立版本管理。
  • langchain-community:社区集成包,放置大量社区维护的集成,例如某些工具,loader, 向量库等。
  • langchain-classic:旧版兼容包,
  • langgraph:图编排与运行时,用于构建更复杂、可控、可持久化的Agentic workflow
  • langsmith:调试、评估、Tracing SDK,对接LangSmith平台,常用语可观测性与实验评估
  • langchain-text-splitters:文本切分组件,RAG常用,负责把 长文本拆分为合适片段。
  • langchain-mcp-adpters:MCP适配包,用于在langchain/langgrpah应用中接入MCP工具

4 LangChain核心模块

4.1 全景图

下面这张图展示了完整知识体系,
在这里插入图片描述

从这个视角看,LangChain 的核心模块其实是在回答六个问题:

  • 怎么接模型:Model / Model I/O
  • 怎么组织固定流程:Chains / LCEL
  • 怎么记住上下文:Memory
  • 怎么接外部知识:Retrieval / RAG
  • 怎么让模型会做事:Tools / Agents
  • 怎么观测与调试:Callbacks / LangSmith

4.2 Model I/O 模型输入输出

该部分是有关模型调用本身的一圈能力,解决的是输入怎么组织、模型怎么调、输出怎么拿的稳。
在这里插入图片描述
主要包含:

  • Format:输入格式化,把原始输入组织成prompt或多角色消息
  • Predict:模型调用,用统一接口调用不同厂商模型
  • Parse:输出解析,把自然语言输出转成更稳定的结构化结果

4.3 Chains链:固定流程编排

Chain核心思想很简单:把多个步骤按固定顺序串起来。

链式思维仍然是LangChain的基础能力。

固定流程更适合Chain/LCEL。动态决策流程更适合Agent/Graph。

4.4 Memory记忆:记忆与上下文状态

Memory指的是应用如何“记住”过去发生过什么。注意,这里的记忆不是让模型变得更聪明,而是让系统在多轮交互中保留必要上下文。例如历史对话、用户偏好、会话状态、阶段性结果等。

在官方当前语境里,记忆通常会去分为Short-term Memory,和Long term memory。

4.5 Retrieval检索:检索与RAG

Retrieval模块解决的是模型不知道的知识从哪里来的问题。

是RAG的核心组成部分,负责从外部知识源中检索和当前问题相关的信息,再把检索结果交给模型辅助生成答案。

4.6 Tools/Agents:让模型不只是会说,还会做

4.6.1 Tool是什么

Tool本质上是一个可以被模型调用的外部能力

4.6.2 Agent是什么

则是在Tool之上再多一层,不是简单执行固定步骤,而是让模型根据当前任务自主决定下一步该做什么。

在1.x官方语境下,LangChain的Agent已经明确建立在LangGraph运行时之上,即使只是调用 create_agent,底层也不再只是一个松散的“工具循环”,而是带有状态、节点、流转、持久化能力的图式运行逻辑。

4.7 Callbacks回调:日志、调试与可观测性

一个可用的AI应用,不只是能跑,还要能看见他怎么跑。

现在官方更强调的是LangSmith + Tracing + Evaluation这一整套可观测性能力。

AI应用最难排查的问题,通常不是程序报错,而是为什么这次没检索到,为什么调了错误工具,为什么模型理解偏了,为什么这轮成本突然变高,为什么线上效果和本地不一致。

如果没有可观测性,很多问题只能猜。

4.8 六大模块总览

教学视图下的六大模块
在这里插入图片描述
官方 1.x 文档中,你会看到这些能力被拆到 Agents、Models、Messages、Tools、Memory、Retrieval、Streaming、Structured Output、Middleware、Runtime、LangSmith 等更细的栏目里

进一步展开模块:
在这里插入图片描述
根据图的理解,理解LangChain做的不是某一件事,而是把模型调用、知识接入、流程编排、状态管理、工具执行、调试追踪这些原本分散的环节串成一个工程系统。

4.9 怎么记这套框架

章节思考题

为什么学习LangChain时要同时知道1.x,provider, Langgraph,

A : 1.x是当前主线,provider包负责具体模型接入,LangGraph承担复杂流程和Agent的底层编排

10 LangChain快速上手与HelloWorld

langchain中文文档链接
中文文档都是翻译更新的,可能慢于官方文档的更新速度

这里需要在新电脑按照之前配置环境配置好python3.10的虚拟环境。

这里还是解释一下pip和uv,

  • pip:它是 Python 的官方包管理工具(全称 Pip Installs Packages)。开发者使用它来查找、下载、安装、升级和卸载 Python 第三方库和依赖包。例如,使用 pip install requests 就可以安装网络请求库13。
  • uv:它是一个用 Rust 编写的、极其快速的 Python 包和项目管理器。你可以把它理解为 pip 的“超级替代品”。它的核心优势是速度极快(比传统 pip 快 10 到 100 倍),并且兼容 pip 的常用命令(如 uv pip install),同时还集成了虚拟环境管理等高级功能

1 LangChain环境与约定

1.1 支持的大模型与课程选用

langchain官方提供了完整的provider列表,将provider理解为封装好各个大模型的api提供者,只需要安装对应包 -》设置模型名称就可以切换各个大模型。
在这里插入图片描述
本课程选择阿里云百炼、千问,关键是理解怎么用langchain接模型。

1.2 python版本

langchain1.x推荐python3.10

1.3 运行案例前置注意事项

  • 尽量在项目根目录运行案例
  • 先配置.env,

2 常见大模型服务平台介绍

2.1 调用三件套

  • API KEY
  • 模型名
  • Base URL

2.2 常见平台

3 安装依赖

将仓库拉到本地,安装依赖,之前已经弄好了。

3.3 验证安装

执行GetEnvInfo.py,检测环境,如图所示:
在这里插入图片描述
并下载了AI Agents Debugger插件,用于调试langgraph agent。

运行项目时要确保用的同一个python环境。

4 案例:基于百炼的HelloWorld

例如在百炼上找到:

  • 模型名:qwen3.6-35b-a3b
  • baseUrl:可以在对应模型下面找到OpenAI兼容,https://llm-55uiolr4hol9nlrx.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
  • api-key:在.env中声明了

4.2 HelloWorld的最小调用链路

最小链路流程:

准备三件套 → 初始化模型 → invoke("问题") → 读取 response.content

其中关键词:

  • invoke:同步调用模型,返回一个消息对象
  • .content:取出消息对象里的正文文本

4.3 示例代码

通过阅读代码,掌握这样的习惯,不将api-key硬编码到代码里,而是通过os.getenv()获取

查看langchain1.x写法

  • 使用init_chat_model统一入口调用大模型,通过model_provider指定厂商
  • 接国内平台(阿里百炼、通义等)时需显式写 model_provider=“openai”,否则会报错无法推断 provider。
  • 调用三件套:API Key、模型名、Base URL;invoke(问题) 返回消息对象,.content 取正文。

其中invoke源码定义:

@override
def invoke(
    self,
    input: LanguageModelInput,
    config: RunnableConfig | None = None,
    *,
    stop: list[str] | None = None,
    **kwargs: Any,
) -> AIMessage:

invoke接收用户的输入和运行时配置,调用底层的大语言模型生成响应,并最终将其转换为标准的AIMessage对象返回。

  • @Override是python3.12+引入的装饰器,明确表示该方法是对父类中invoke方法的重写
  • input用户的输入,可以使字符串,消息列表等
  • config:LangChain的运行时配置对象,用于控制执行行为
  • stop:可选参数

核心执行与结果转换,嵌套的cast逻辑,从内向外执行以下操作:

# 1. 输入转换与生成请求
self.generate_prompt(
    [self._convert_input(input)],  # 将原始输入转换为模型能理解的格式
    stop=stop,                     # 传入停止词
    callbacks=config.get("callbacks"), # 提取配置中的回调处理器
    tags=config.get("tags"),           # 提取标签
    metadata=config.get("metadata"),   # 提取元数据
    run_name=config.get("run_name"),   # 提取运行名称
    run_id=config.pop("run_id", None), # 提取并移除运行ID
    **kwargs,                          # 其他额外参数
)
# generate_prompt 返回的是一个包含 generations 列表的 LLMResult 对象

# 2. 提取生成结果并转换类型
# .generations 获取了第一个批次中的第一个生成结果(ChatGeneration 对象)
cast("ChatGeneration", ...).message 
# 从 ChatGeneration 对象中提取出底层的 message 属性

# 3. 最终类型转换
cast("AIMessage", ...)
# 确保最终返回的对象被明确标记为 AIMessage(AI 消息)类型

这段代码本质上是一个“适配器”。它将 LangChain 标准的 invoke 调用,转化为底层 generate_prompt 的调用,并且通过 config 实现了任务追踪、超时控制、日志记录等运行时控制712。最后,它把大模型返回的复杂嵌套结果(LLMResult -> ChatGeneration -> Message),剥离并强转为标准的 AIMessage,以便在 LangChain 的对话链(Chain)或 Agent 中继续流转

在openAI中的格式调用就是:

# Please install OpenAI SDK first: `pip3 install openai`
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ.get('DEEPSEEK_API_KEY'),
    base_url="https://api.deepseek.com")

response = client.chat.completions.create(
    model="deepseek-v4-pro",
    messages=[
        {"role": "system", "content": "You are a helpful assistant"},
        {"role": "user", "content": "Hello"},
    ],
    stream=False,
    reasoning_effort="high",
    extra_body={"thinking": {"type": "enabled"}}
)

print(response.choices[0].message.content)

可以发现langchain的invoke就是封装了的,可以理解为response.choices[0].message就是langchaininvoke得到的AIMessage,然后可以通过.content拿到正文,如下图所示:
在这里插入图片描述
通过init_chat_model统一入口,不需要再为每个模型厂商记一套不同的初始化方式,而是先记住同一套调用骨架,通过参数切换不同模型和provider

4.4 0.3与1.0写法差异

0.3 / 经典写法的思路是:

  • 直接从具体集成包导入类,例如 ChatOpenAI
  • 类名本身就带有“我是按哪种协议接入”的语义
  • 代码非常直观,但不同厂商、不同类名会让项目越写越散

1.x 的思路是:

  • 用 init_chat_model 作为统一入口
  • 通过 model、model_provider、api_key、base_url 等参数描述“我要接谁”
  • 同一套代码骨架更容易迁移、统一与维护

5 多模型共存

5.1 多模型共存场景

存在不同模型的需求

5.2 调用三件套

5.3 多模型共存示例代码

记住就是多个模型使用变量名进行区分。

6 实战:企业级封装与流式输出

6.1 从HelloWorld到项目写法

真实项目里还要那么些就有问题了,因此要了解企业级封装和流式输出。

6.2 invoke()与stream()的区别

invoke()就是一次性返回完整结果。适合简单问答,后台处理,不需要实时展示中间输出的场景。

stream()就是变生成边返回。适合命令行实时输出,聊天界面打字机效果,长文本生成,用户等待体验更敏感的场景。

示例:

for chunk in model.stream("请介绍一下LangGraph"):
	print(chunk.content, end="")

6.3 示例代码

下面是自己按照langchain1.0版本风格写的封装初始化代码

"""
【案例】标准/工程化写法,用LangChain调用大模型invoke或者stream

将初始化模型封装成函数便于复用
    用.env存密钥
    logging打日志
    try/except区分错误

运行前记得在项目根目录配置.env中的api_key
这里直接看LangChainV1.0风格的写法
"""
# 导入环境
import os
import logging
from unittest import loader

from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_core.exceptions import LangChainException

load_dotenv(encoding="utf-8")

# 日志配置
_log_level = os.getenv("LOG_LEVEL", "INFO").upper()
logging.basicConfig(
    level=getattr(logging, _log_level, logging.INFO),
    format="%(asctime)s - %(levelname)s - %(message)s",
)

logger = logging.getLogger(__name__)

# LLM客户端初始化,封装为函数,便于多处复用
def init_llm_client():
    """
    肯定是返回初始化好的模型实例
    """
    api_key = os.getenv("deepseek-api")
    if not api_key:
        raise ValueError("环境变量DEEPSEEK_API_KEY未配置,请检查.env文件")

    return init_chat_model(
        model="deepseek-v4-pro",
        model_provider="openai",
        api_key=api_key,
        base_url="https://api.deepseek.com",
    )


# 主逻辑:invoke或者stream两种调用方式
def main():
    """
    主函数:封装核心逻辑,符合python工程化规范
    """
    try:
        model = init_llm_client()
        logger.info("LLM客户端初始化成功")

        # invoke方式
        question = "你是谁"
        response = model.invoke(question)
        logger.info(f"问题:{question}")
        logger.info(f"回答:{response.content}")

        # stream流式,边生成边输出
        print("============下面是流式输出=============")
        print("*" * 50)

        for chunk in model.stream(question):
            print(chunk.content, end="")

        print()
    except ValueError as e:
        logger.error(f"配置错误:{str(e)}")
    except LangChainException as e:
        logger.error(f"模型调用失败: {str(e)}")
    except Exception as e:
        logger.error(f"未知错误:{str(e)}")


# 注意不要开代理,否则无法正常访问API端点
if __name__ == "__main__":
    main()

上述示例:

  • 把模型初始化封装为函数,避免到处重复写配置
  • 显式检查环境变量
  • 使用日志,这个很重要,
  • 区分异常类型,便于排查问题
  • 同时演示invoke和stream()

思考题

真实项目里还要处理配置校验、日志、异常、超时、重试、流式输出和敏感信息隐藏

接下来学习11,13,14,掌握完整的输入-》模型-》输出

第11章:Model I/O与模型接入

掌握Modell I/O模块中的输入提示Promot, 调用模型Model, 输出解析Parser三件套

掌握LangChain里最常见的模型分类,标准化参数,返回值结构

1 Model I/O 简介

1.1 定义

该模块关心的是模型的输入、调用和输出。

  • Format(输入格式化):把原始业务输入整理成模型更容易理解的形式,例如 Prompt 模板、多角色消息、变量填充等。
  • Predict(模型调用):通过 LangChain 的统一接口调用不同模型提供商,例如 OpenAI、DeepSeek、阿里百炼、Ollama 等。
  • Parse(输出解析):把模型返回的自然语言结果转成更稳定、程序更好处理的形式,例如字符串、JSON、结构化对象等。
    在这里插入图片描述

1.2 为什么需要Model I/O

真实项目里,还是需要Model I/O进行解决

2 LangChain模型分类、参数与返回

讲解模型调用Predict,弄清楚:

  • LangChain中到底有哪些模型类型
  • 调模型时常见参数是什么
  • 调完模型后,返回的到底是什么

2.1 先分清:模型本身,模型提供商,LangChain模型对象

这个很好理解
在这里插入图片描述

2.2 LangChain常见模型分类

这里再次回顾LangChain框架是什么,是为了开发LLM应用程序设计的开源框架。
所以LangChain不提供模型权重本身,提供的是如何接这些模型的统一抽象。

LangChain的核心价值与能力体现在:

  • 模块化的组件结构,这在第4章讲了一个概览,有一个印象
  • 解决原生API调用的痛点
  • 强大的生态与扩展能力

覆盖了AI应用从开发、测试到部署的完整生命周期。

LangChain中实际开发过程中最常用的是聊天对话模型,用于多轮对话、系统角色、用户消息等场景。

最常碰到的三类:

  • LLM
  • ChatModel
  • Embeddings

后面的Prompt, Message, Tools, Agent, Memory本质大多都建立在ChatModel的消息交互之上。

2.3 LLM和Chat Model区别

在LangChain中,LLM和ChatModel,接口思维不一样,

  • LLM更像给一段文本,让模型补全或生成下一段
  • CahtModel更像给一段对话上下文,让模型按角色回复。

LLM风格就是提供prompt = "这是什么"

而ChatModel风格如下:

messages = [
    {"role": "system", "content": "你是一个技术助教"},
    {"role": "user", "content": "请用一句话解释什么是 LangChain"},
]

2.4 为什么Embedding放在这里

是向量模型

2.5 常用模型参数

LangChain对聊天模型定义了一批标准化参数,名称在不用写发下基本一致

掌握参数:

  • model
  • model_provider
  • api_key
  • base_url
  • temperature
  • max_tokens最大生成长度
  • timeout超时时间,请求最长等多久
  • max_retries 失败重试次数
  • stop停止词,生成到某些位置时强制停止

在项目里最先调temperature 和max_tokens

2.6 Token, max_tokens与计费的关系

用量统计通常都以token数为单位

也有token可视化工具

2.7 参数的选择

根据业务场景选:

  • 客服问答 / 规则回答 / 翻译:temperature 往往设低一些,例如 0~0.3
  • 知识问答 / 通用助手:可设在 0.2~0.7
  • 文案、创意写作、头脑风暴:可适当更高,例如 0.8~1.2
    max_tokens 的使用也一样:

如果只是简短问答,可以限制小一点,避免无意义长回答
如果是总结、分析、长文输出,可以适当放大
但有一点要始终记住:

参数只是调味料,底层模型能力、Prompt 设计、上下文质量,通常比单纯调 temperature 更重要。

2.8 基本案例

temperature取值范围在0-2

"""

理解模型参数与返回对象结构
temperature影响输出随机性
max_tokens控制返回长度涉及到成本
使用api_key为deepseek_api


"""
import os
from langchain.chat_models import init_chat_model

from dotenv import load_dotenv

load_dotenv(encoding="utf-8")

# 实例化设置常用参数

model = init_chat_model(
    model="deepseek-v4-flash",
    model_provider="openai",
    api_key=os.getenv("deepseek-api"),
    base_url="https://api.deepseek.com",
    temperature=0.7,  # 0~1,越高越随机;此处略高便于看到多次输出差异
    # max_tokens=256,  # 可选:限制单次回复长度
)

print(model.invoke("写一句关于春天的词,14字以内"))
print(type(model))
print(type(model.invoke("写一个关于春天的词,14字以内").content))
print(type(model.invoke("写一句关于春天的词,14字以内")))



通过上述案例理解模型和返回的类型

2.9 调用后的返回信息

将init_chat_modle理解为模型客户端对象,invoke()返回的是<class 'langchain_core.messages.ai.AIMessage'>

2.10 AIMessage上常见字段

掌握字段:

  • content
  • response_metadata,厂商返回的原始元数据,用于看模型名、finish_reason, token_usage等
  • usage_metadata,这是langchain统一整理后的用量信息,看input/output/total tokens
  • tool_calls工具调用信息
  • additional_kwargs厂商扩展字段

所以可以看到一个典型的AiMessage:

AIMessage(
  content='《鹧鸪天·春》\n一篙绿涨江南岸,…',   # 用户可见回复
  response_metadata={
    'token_usage': {
      'prompt_tokens': 14,
      'completion_tokens': 109,
      'total_tokens': 123,
      ...
    },
    'model_name': 'deepseek-v4-flash',
    'finish_reason': 'stop',
    ...
  },
  usage_metadata={
    'input_tokens': 14,
    'output_tokens': 109,
    'total_tokens': 123,
    ...
  },
  tool_calls=[],
  ...
)

如图加深理解:
在这里插入图片描述
所以看content, response_metadata, usage_metadata

2.11 Messages的作用

ChatModel的输入输出,本质上都是消息Message

例如SystemMessage, HumanMessage, AIMessage, ToolMessage

Prompt只是输入的一种组织方式,Message才是更底层的统一表达

3 接入大模型

怎么把不同模型接进来

3.1 三种常见接入思路

从SDK, provider,到使用init_chat_model

3.2 接入OpenAI及兼容接口

3.2.2 openai.OpenAI与ChatOpenAI的区别

oepnai.OpenAI是官方的pythonSDK,langchain_openai.ChatOpenAI是LangChain生态

显然使用ChatOpenAI方便后续使用

3.2.6 真实项目建议

3.3 接入DeepSeek

3.3.1 常见接法
  • 通过OpenAI兼容接口接入
  • 通过langchain-deepseek原生provider接入

原生的更贴近deepseek自身集成,某些特性表达更自然

3.3.3 用init_chat_model接入

两种方法

  • 按照OpenAI兼容接口接
  • 使用deepseek provider包

3.4 接入通义千问

也是同样的方式,兼容接口或原生路线

3.7 模型对象拿到后还要关注什么

模型初始化之后,一个chat Model通常还会继续承担下面这些能力:

  • 普通调用
  • 流式输出
  • 结构化输出with_structured_output(schema)
  • 绑定工具model.bind_tools(tools)
  • 放入链路

这里的模型也是核心组件

3.8 调用配置与运行时信息

除了初始化时传入的模型参数,LangChain每次调用还需要传入config,这是给LangChain运行时看的信息。

常见配置项:

  • tags,给本次调用打标签,用于区分rag, agent, demo等不同链路
  • metadata,记录额外上下文
  • callback,挂接回调处理器
  • configurable,运行时切换配置

示例:

response = model.invoke(
    "用一句话解释 LangChain 的作用",
    config={
        "tags": ["model-io", "demo"],
        "metadata": {"course": "ai-agents-from-zero"},
    },
)

如果后面接入LangSmith,这些tags和metadata会很有用,因为可以在追踪记录里快速筛选某一类调用

还有一种更进阶的用法是运行时切换模型。例如先创建一个可配置的模型对象,再在调用时指定具体模型。

config更偏运行时管理和观测。

第12章 Ollama本地部署与调用

这一章先跳过

第13章 提示词语消息模板

掌握LangChain中与输入组织最相关的三块内容:消息类型Message,模型调用方式invoke, stream,提示词模板PromptTemplate, ChatPromptTemplate。

理解本章全部案例。从模型需要吃进什么,理解提示词模板。

1 Prompt简介

对应了Model I/O中的输入格式化format和模型调用predict

将之前了解的提示词工程,应用到本章就是代码实现版。

将之前提到的角色、任务、上下文、输入、输出和约束,理解在LangChain中以什么形态存在的。

1.1 定义

提示词,从自然语言到带有角色的,再到代码可复用、可维护、可协作,从而把输入写成模板,把会变化的部分改为占位符,这就是从随手提问走向工程化输入管理。

因此Prompt需要把模型输入组织清楚

随着项目复杂度提高,Prompt会从一个字符串逐步演化为多角色消息 + 模板 + 占位符 + 外部配置文件。

1.2 Prompt作用

将输入组织清楚

下面这些真实开发场景,都离不开本章内容:

  • 智能客服:需要系统提示词规定语气、身份和拒答策略。
  • 企业知识库问答:需要把“检索出来的上下文 + 用户问题”组合成一条清晰提示。
  • 多轮聊天:需要把历史对话插回当前输入,而不是每轮都从头问。
  • 结构化输出:需要提前在 Prompt 里写清楚输出格式要求,方便后面交给解析器处理(通过提示词就一定能保证按照结构化方式输出吗)。
  • 团队协作与 A/B 测试:需要把 Prompt 模板从代码里抽出来,放到 JSON / YAML 中做版本管理。(例如java项目中使用nacos进行统一配置——AI版天机学堂)

模型能力决定上限,Prompt 设计决定你能不能稳定接近这个上限。

1.3 本章在项目中的位置

分为4类:

  • invoke,模型调用方式,invoke, stream,batch及异步版本
  • prompt_templates,文本模板,
  • chat_prompt_tempalte,对话模板
  • load_external,外部文件加载

2 调用大模型的入参类型

调用模型可以有多种输入形态

2.1 入参形态总览

  • str
  • PromptTemplate.format()后的字符串
  • 消息对象列表
  • (role, content)元组列表
  • 字典列表

回顾python的基础知识,元组有序不可变,字典大括号。

示例:

"""

多种输入类型
invoke可以接受消息对象列表
(role, content)元组
{"role": "...", "content": "..."}字典

这样写都是表达这次输入由哪些角色、哪些内容组成,LangChain会在内部转成统一消息表示
可以使用Message类写法
"""

import asyncio
import os

from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_core.messages import HumanMessage, SystemMessage

load_dotenv()

model = init_chat_model(
    model="deepseek-v4-flash",
    model_provider="openai",
    api_key=os.getenv("deepseek-api"),
    base_url="https://api.deepseek.com",
)

def demo_message_objects():
    """推荐,显式Message对象,角色与字段最清晰"""
    message = [
        SystemMessage(content="你是一个专业的数学助手,回答要简短。"), # 列表
        HumanMessage(content="你好,你是谁?"),
    ]

    resp = model.invoke(message)

    print(type(resp), resp.content[:80] if resp.content else "")

def demo_tuple_list():
    """元组列表:与 ChatPromptTemplate.from_messages 的写法一致。"""
    messages = [
        ("system", "你是一个专业的数学助手,回答要简短。"),
        ("human", "你好,你是谁?"),
    ]
    resp = model.invoke(messages)
    print(type(resp), resp.content[:80] if resp.content else "")


def demo_dict_list():
    """字典列表:与 OpenAI Chat Completions 等 API 的请求体形状接近。"""
    messages = [
        {"role": "system", "content": "你是一个专业的数学助手,回答要简短。"},
        {"role": "user", "content": "你好,你是谁?"},
    ]
    resp = model.invoke(messages)
    print(type(resp), resp.content[:80] if resp.content else "")

async def demo_ainvoke_tuple():
    """异步调用同样支持元组简写。"""
    resp = await model.ainvoke([(
        "user", "用一句话说明什么是素数。"
    )])

if __name__ == "__main__":
    print("--- Message 对象列表 ---")
    demo_message_objects()
    print("--- 元组列表 ---")
    demo_tuple_list()
    print("--- 字典列表 ---")
    demo_dict_list()
    print("--- ainvoke + 元组 ---")
    asyncio.run(demo_ainvoke_tuple())


回顾python中的异步编程语法async与await

  • async def: 这是python定义异步函数协程的语法,告诉python解释器,这个函数内部包含需要等待的I/O操作,在等待期间可以释放控制权去执行其他任务。
  • await,用于挂起当前协程的执行,等待model.ainvoke()这个异步操作完成并获取返回结果。LangChain中,ainvoke是同步方法invoke的异步版本,专门用于异步环境

2.3 写法二:模板 + 占位符

示例:

from langchain_core.prompts import PromptTemplate

template = PromptTemplate.from_template(
    "用不超过 50 字介绍:{topic} 是什么?"
)
prompt_str = template.format(topic="LangChain")
resp = model.invoke(prompt_str)
print(resp.content)

2.4 写法三:多角色消息列表

构建消息类型列表,可以简化为列表,例如HumanMessage就是"user",SystemMessage可以为"system",Langchain会转换的。

from langchain_core.messages import SystemMessage, HumanMessage, AIMessage

messages = [
    SystemMessage(content="你是只回答技术问题的助手,回答要简短。"),
    HumanMessage(content="什么是 LangChain?"),
    # 多轮示例:
    # AIMessage(content="LangChain 是用于编排 LLM 应用的框架……"),
    # HumanMessage(content="它和直接调 API 有什么区别?"),
]
resp = model.invoke(messages)
print(resp.content)

在实际项目里,这种写法特别常见,

  • 系统提示词放在SystemMessage
  • 用户问题放在HumanMessage
  • 历史回复可放在AIMessage
  • 工具执行结果后续可用ToolMessage

后续做多轮对话,Agent, RAG,这种消息列表思维会反复用到。

还有就是ChatPromptValue,链式编排里更常见。

直接构造对象然后转换,ChatPromptValue ->调用to_message()

2.4 写法四:元组列表与字典列表

2.6 Java生态中的多角色

多角色消息并不是某个框架的语法技巧,而是现代聊天模型交互的一种通用抽象。

3 入参的消息类型

知道每种消息类型代表什么

3.1 四类核心消息

  • SystemMessage
  • HumanMessage
  • AIMessage,模型回复消息,保存上一轮回复,支持多轮上下文
  • ToolMessage,工具执行结果,

3.3 ToolMessage什么时候会出现

ToolMessage不会在普通问答里频繁出现,更常见于函数调用,工具调用,Agent编排场景

简单示例:

from langchain_core.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage

messages = [
    SystemMessage(content="你是一位乐于助人的智能小助手"),
    HumanMessage(content="你好,请你介绍一下你自己"),
    AIMessage(content="我是一名人工智能助手,请问您有什么想问的吗?"),
    ToolMessage(
        content='{"population": 21540000, "area": "16410平方公里"}',
        tool_call_id="call_abc123",
    ),
]
print(messages)

4 调用大模型的调用方式

LangChain 聊天模型常见的调用方式有四类:普通调用、流式调用、批量调用,以及它们各自的异步版本

例如:

  • invoke/ainvoke:一次发一条
  • stream/astream:一边生成一边返回
  • batch/abatch:一次发很多条

4.1 普通调用invoke/ ainvoke

  • invoke:同步调用,最常用,适合单轮问答与脚本演示。
  • ainvoke:异步调用,适合异步 Web 服务、并发任务和高吞吐场景。

实际项目中选择:

  • 命令行脚本,教学示例,简单后台任务,优先invoke
  • FastAPI, 异步服务,并发请求,优先ainvoke
  • 本地开源模型也适用

这里了解ainvoke,

  • ainvokeinvoke 的异步版本,等待模型返回时不会阻塞事件循环。
  • 它适合异步 Web 服务、并发任务和需要同时处理多条请求的场景。
  • 返回结果类型通常仍是 AIMessage;只是调用方式从“直接返回”变成了“await 后返回”。

示例:

resp = await model.ainvoke("解释一下你是谁")

if __name__ == "__main__":
	asyncio.run(main())

4.2 流式调用

即:

  • stream,同步流式输出
  • astream,异步流式输出

流式的最大价值不是“更快算完”,而是更快把正在生成的内容展示给用户。在聊天机器人、报告生成、代码生成等场景里,用户体验会明显更好。

示例:

messages = [
    SystemMessage(content="你叫小问,是一个乐于助人的AI助手"),
    HumanMessage(content="你是谁")
]
for chunk in model.stream(messages):
    print(chunk.content, end="", flush=True)
print()

异步流式,返回的是异步生成器,因此必须用asynch for遍历,循环的每一块通常仍是AIMessageChunk

示例:

# ---------- 3. 异步流式调用(在 async 函数中)----------
async def async_stream_call():
    # astream(messages) 返回的是「异步生成器」,不是 await 一个整体结果
    response = model.astream(messages)
    print(f"响应类型:{type(response)}")  # <class 'async_generator'>

    # 必须用 async for 遍历异步生成器,不能用普通 for
    async for chunk in response:
        print(chunk.content, end="", flush=True)
    print("\n")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

所谓远行Misnearch

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值