Langchain学习笔记(一):认识Langchain-调用LLM的正确姿势

11 阅读6分钟

Langchain是一款开源框架,用于构建Agent,集成了众多大模型供应商和工具。

  • langchain主要负责与LLM交互,Tool,Rag,Memory,Agent等功能。
  • LangGraph负责实现Agent编排,专用于构建、管理和部署长时间运行(long-running)且具备状态管理(stateful的智能体。
  • LangSmith则负责提升Agent的可观测性,提供了用于开发、调试和部署 LLM 应用程序的工具。 它能够帮助您在一个统一的平台上追踪请求、评估输出、测试提示词(Prompts)以及管理部署。

Langchain核心功能

LCEL语法

早期的Langchain,将提示词,模型,工具调用,Rag,Memory等模块组织成链式结构,让开发者能够清晰的组合这些组件,构建出复杂的Agent。但是缺点也很明显:

  1. 流程不透明、调试困难:链条中间没有明确的状态表达,出错时难以定位是哪一步出了问题。
  2. 缺乏并发与分支控制能力:只能“从左到右”执行,面对多轮对话、并行任务,力不从心。
  3. Agent 系统复杂难控:LangChain Agent 的“反复推理 + 工具调用”机制,虽然看似智能,但在实际工程中不稳定、不易测试。

为了解决这些问题,Langchain团队研发出了LCEL。LCEL (LangChain Expression Language) 是 LangChain 提供的一种声明式语言,用于轻松构建和组合复杂的链(Chains)。使得代码更加简洁、清晰,并且原生支持流式/非流式输出。

在LCEL中,所有组件都实现了 langchain_core.runnables.base.Runnable接口,都实现了

  • invoke() - 单个输入
  • batch() - 批量输入
  • stream() - 流式输出
  • ainvoke() - 异步单个输入
组件名称作用
RunnableSequence顺序执行
RunnableParallel并行执行多个任务
RunnablePassthrough透传数据,不做处理
RunnableLambda将自定义 Python 函数转为 Runnable
RunnableConfig配置项
RunnableBranch条件分支

但是,现在的Agent是越来越复杂,需要更加精细化的状态管控,任务调度,失败处理,事件监听,整个Agent的生命周期到每一个事件的生命周期,都需要更加细粒度的管理,还需要一套更为清晰完整的,适合工作流程编排的框架。那就是LangGraph的诞生。

  • LCEL:描述单个“图节点”的调用逻辑(比如一个子任务、一个 API 调用)。
  • LangGraph:编排多个节点,负责整个工作流的调度、状态跳转与生命周期控制。

Model

安装 openai依赖 pip install langchain-openai

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
from langchain_core.tools import Tool
import datetime

# 加载环境变量
load_dotenv()
api_key = os.getenv('OPENAI_API_KEY')
base_url = os.getenv('OPENAI_API_BASE')

# ========== 1. 初始化 LLM(大语言模型) ==========
# LangChain 特点:统一的 LLM 接口,支持多种模型提供商
llm = ChatOpenAI(
    base_url=base_url,
    api_key=api_key,
    model="deepseek-ai/DeepSeek-V3.2",
    temperature=0.7  # LangChain 特点:统一的参数配置
)

# 也可以使用 init_chat_model 由于我使用的是硅基流动的,所以需要提供model_provider为openai
# 如果你直接使用的是deepseek,model_provider可以不用填,但需要安装langchain-deepseek包
model = init_chat_model(
    model="deepseek-ai/DeepSeek-V3.2",
    model_provider="openai",
    temperature=0.7
)


# ========== 2. LLMChain:Prompt → LLM → 输出链的基本流程封装 ==========
def demo_llm_chain():
    """
    演示 LLMChain:支持变量注入与模板复用的核心组件
    LangChain 特点:模板化提示词管理,支持变量替换
    """
    print("=" * 50)
    print("🔗 LLMChain 演示:Prompt → LLM → 输出链")
    print("=" * 50)

    # 创建提示词模板 - LangChain 特点:模板复用
    prompt_template = PromptTemplate(
        input_variables=["topic", "style"],
        template="""
        请以{style}的风格,写一段关于{topic}的介绍。
        要求:简洁明了,不超过100字。
        """
    )

    # LCEL (LangChain Expression Language),还是0.x的版本用的比较多
    # 1.x开始采用Message
    # 下面两种方式都行
    chain = prompt_template | llm
    chain = prompt_template | model
    # 执行链 - 变量注入 

    result = chain.invoke({"topic": "人工智能", "style": "科普"})
    print(f"📝 LLMChain 输出:\n{result.content}\n")

    # 流式输出
    for chunk in chain.stream({"topic": "人工智能", "style": "科普"}):
        print(chunk.text, end="|", flush=True)

    # batch
    messages = [
        {"topic": "人工智能", "style": "科普"},
        {"topic": "大模型", "style": "科普"},
        {"topic": "langchain", "style": "科普"}
    ]
    responses = chain.batch(messages)
    for response in responses:
        print(response)


    return result.content

核心的方法

  • invoke
result = chain.invoke({"topic": "人工智能", "style": "科普"})
print(f"📝 LLMChain 输出:\n{result.content}\n")
  • stream
# 流式输出
for chunk in chain.stream({"topic": "人工智能", "style": "科普"}):
    print(chunk.text, end="|", flush=True)
  • batch
# batch
messages = [
    {"topic": "人工智能", "style": "科普"},
    {"topic": "大模型", "style": "科普"},
    {"topic": "langchain", "style": "科普"}
]
responses = chain.batch(messages)
for response in responses:
    print(response)

核心参数

  • model: 大模型名称
  • model_provider: 模型供应商,我用的是硅基流动,langchain没有提供硅基流动的包,但硅基流动兼容了OpenAi协议,模型供应商选择OpenAi,也能连接硅基流动。
  • api_key:
  • temperature:
  • max_token:
  • max_retries:
  • timeout:

比较常用的Model 功能

思考模式打开

reasoning = {
    "effort": "medium"
}

reasoning_model = ChatOpenAI(
    model="gpt-5.2",
    temperature=0.7,
    reasoning=reasoning
)

for chunk in reasoning_model.stream("大模型的原理"):
    reasoning_steps = [r for r in chunk.content_blocks if r["type"] == "reasoning"]
    print(reasoning_steps if reasoning_steps else chunk.text)

结构化输出

Message

Message按照OpenAi的规范需要区分角色:

  • developer: developer message的主要职责是
    • 系统的核心规则
    • 系统的业务逻辑
    • 工具的定义
  • user:用户的输入和配置,用于
  • Assistant:大模型的输出

developer以前是system,现在变成了developer,但是langchain或者其他智能体框架,都还沿用着system作为系统角色,OpenAi两者都兼容。

Langchain还新增了一个角色,Tool,工具消息用于将单个工具执行的结果传递回模型。工具可以直接生成 ToolMessage 对象。

messages = [
    SystemMessage(content="你是一名资深的Python后端工程师面试官,负责考察候选人的技术能力。"),
    HumanMessage(content="请问什么是装饰器?"),
    AIMessage(content="装饰器是Python中用于修改函数或类行为的语法糖..."),
    HumanMessage(content="能举个实际应用的例子吗?")
]

# 
# messages = [
#     {"role": "system", "content": "你是一名资深的Python后端工程师面试官,负责考察候选人的技术能力。"}, # 对应SystemMessage
#     {"role": "user", "content": "请问什么是装饰器?"}, # 对应HumanMessage
#     {"role": "assistant", "content": "装饰器是Python中用于修改函数或类行为的语法糖..."}, # 对应AIMessage
#     {"role": "user", "content": "能举个实际应用的例子吗?"}
# ]


response = llm.invoke(messages)

print(f"📝 LLMChain 输出:\n{response.content}\n")

ToolMessage的使用方式

def get_weather(location: str) -> str:
    """Get the weather at a location."""
    # 先获取城市ID
    location_url = f"https://n53h2qt5jy.re.qweatherapi.com/geo/v2/city/lookup?location={location}"
    location_response = (requests.get(url=location_url,headers={"Content-Type": "application/json","X-QW-Api-Key":  q_weather_api_key}).json())

    if location_response['code'] != '200':
        return f"未找到城市:{location}"

    location_id = location_response['location'][0]['id']

    # 查询天气
    weather_url = f"https://n53h2qt5jy.re.qweatherapi.com/v7/weather/now?location={location_id}"
    weather_response = requests.get(url=weather_url, headers={"Content-Type": "application/json", "X-QW-Api-Key":  q_weather_api_key}).json()

    if weather_response['code'] == '200':
        now = weather_response['now']
        return f"{location}当前天气:{now['text']},温度{now['temp']}°C,体感温度{now['feelsLike']}°C"

    return "天气查询失败"

model_with_tools = llm.bind_tools([get_weather])
response = model_with_tools.invoke("What's the weather in 上海?")

for tool_call in response.tool_calls:
    print(f"Tool: {tool_call['name']}")
    print(f"Args: {tool_call['args']}")
    print(f"ID: {tool_call['id']}")

# After a model makes a tool call
# (Here, we demonstrate manually creating the messages for brevity)
ai_message = AIMessage(
    content=[],
    tool_calls=[{
        "name": "get_weather",
        "args": {"location": "上海"},
        "id": "call_123"
    }]
)

# Execute tool and create result message
tool_message = ToolMessage(
    content=get_weather("上海"),
    tool_call_id="call_123"  # Must match the call ID
)

# Continue conversation
messages = [
    HumanMessage("上海天气如何?"),
    ai_message,  # Model's tool call
    tool_message,  # Tool execution result
]
response = llm.invoke(messages)  # Model processes the result
print("==================")
print(f"📝 LLMChain 输出:\n{response.content}\n")

print(f"llm 元数据:{response.usage_metadata}")