LangChain 主包可以理解为 Agent 的组装层:选择模型、注册工具、创建 Agent,并控制输出格式、运行过程和对话状态。学习当前 1.x 版本,重点掌握下面这条主线:
用户消息 → Agent → 模型决定是否调用工具 → 执行工具 ↑ ↓ └──── 读取工具结果 ────┘ ↓ 返回运行状态一、初始化聊天模型
init_chat_model提供统一的模型创建入口:
from langchain.chat_models import init_chat_model model = init_chat_model( "openai:gpt-4.1-mini", temperature=0, ) reply = model.invoke("你好") print(reply.content)模型名中的openai:指定提供方。运行时仍需安装对应的模型接入包并配置凭证。model.invoke()只调用模型;它本身不会自动形成工具执行循环。
create_agent也可以直接接收模型名称字符串。如果已经创建了模型对象,则可以把对象传给 Agent。
二、定义工具并创建 Agent
工具是 Agent 能够执行的动作。函数名、参数类型和文档字符串共同描述工具的用途及输入:
from langchain.tools import tool @tool def calculate_total(price: float, quantity: int) -> float: """根据商品单价和数量计算总价。""" return price * quantity使用create_agent把模型与工具组合起来:
from langchain.agents import create_agent agent = create_agent( model=model, tools=[calculate_total], system_prompt="遇到商品总价问题时,先使用计算工具。", ) result = agent.invoke({ "messages": [ {"role": "user", "content": "单价 12.5 元,买 2 件,共多少钱?"} ] })tools=[calculate_total]是注册工具,不是立即执行工具。运行时,模型可以提出工具请求;Agent 执行对应函数,把结果送回模型,再由模型决定下一步。模型也可能不调用工具,因此需要检查实际运行记录,而不能仅凭tools参数断定工具已经执行。
三、理解 Agent 的返回值
agent.invoke()返回的是包含运行状态的字典。最常查看的是messages:
for message in result["messages"]: print(message.type, message.content)一次发生工具调用的运行,通常能看到用户提问、助手提出工具请求、工具返回结果、助手最终回答。调试时要检查整段消息:助手提出请求和工具完成执行是两个不同阶段。
system_prompt会参与模型调用,但不必期待它作为一条普通历史消息出现在返回的messages中。
四、结构化输出
当下游程序需要明确字段时,不要依赖从自然语言回答里截取数字。通过response_format声明结果结构:
from pydantic import BaseModel, Field from langchain.agents.structured_output import ToolStrategy class TotalResult(BaseModel): amount: float = Field(description="商品总价") agent = create_agent( model=model, tools=[calculate_total], response_format=ToolStrategy(TotalResult), )调用后读取专门的结构化结果:
result = agent.invoke({ "messages": [ {"role": "user", "content": "单价 12.5 元,买 2 件,共多少钱?"} ] }) print(result["structured_response"].amount)常见方式有三种:
| 写法 | 含义 |
|---|---|
response_format=TotalResult | 让 LangChain 根据模型能力选择策略 |
ToolStrategy(TotalResult) | 通过工具调用机制获取结构化结果 |
ProviderStrategy(TotalResult) | 使用模型提供方支持的原生结构化输出 |
结构化输出解决的是结果字段和类型问题,不保证模型的事实判断或计算过程一定正确。配置了结构化输出后,应优先读取result["structured_response"],不要假设最后一条消息文本一定包含最终数据。
五、流式查看运行过程
除了等待invoke()返回完整结果,还可以边运行边读取更新:
for update in agent.stream( {"messages": [{"role": "user", "content": "计算 12.5 × 2"}]}, stream_mode="updates", ): print(update)两种常见模式要分清:
stream_mode="updates":查看模型、工具等执行步骤产生的更新,适合调试 Agent 流程。stream_mode="messages":读取生成的消息块及元数据,适合逐步展示模型输出。
“流式”不等于严格逐字输出;每块的大小取决于实际组件。
六、用中间件控制 Agent
中间件用于给 Agent 加入通用行为,例如记录日志、重试、限制调用次数、总结过长的对话或在执行高风险工具前要求人工审批。
一个最小示例是在调用模型前观察状态:
from langchain.agents.middleware import before_model @before_model def log_model_call(state, runtime): print("当前消息数:", len(state["messages"])) agent = create_agent( model=model, tools=[calculate_total], middleware=[log_model_call], )理解三个钩子的时机即可建立基础:
before_model:模型调用前。after_model:模型返回后。wrap_model_call:包住一次模型调用,可在调用前后加入处理。
主包还提供ModelRetryMiddleware、ToolRetryMiddleware、ModelCallLimitMiddleware、SummarizationMiddleware和HumanInTheLoopMiddleware等现成能力。模型调用失败与工具执行失败是不同问题,应选对应的处理方式。
七、多轮对话与记忆
默认情况下,两次独立的agent.invoke()不会自动共享上一轮对话。要让同一会话接续先前状态,需要给create_agent配置checkpointer,并在调用时使用相同的thread_id:
# saver 表示已配置好的状态保存器。 agent = create_agent( model=model, tools=[calculate_total], checkpointer=saver, ) config = {"configurable": {"thread_id": "conversation-001"}} agent.invoke( {"messages": [{"role": "user", "content": "我叫小明"}]}, config=config, ) agent.invoke( {"messages": [{"role": "user", "content": "我叫什么?"}]}, config=config, )相同thread_id对应同一会话;不同 ID 用来隔离会话。只有thread_id而没有状态保存器,不会自动产生记忆。checkpointer是主包提供的接入点,具体保存器的实现不在本文范围内。
八、完整示例
下面把模型、工具、结构化输出和模型重试放到一起。示例使用 OpenAI 模型,运行前需要安装langchain、对应的模型接入包,并配置 API Key。
from pydantic import BaseModel, Field from langchain.chat_models import init_chat_model from langchain.tools import tool from langchain.agents import create_agent from langchain.agents.structured_output import ToolStrategy from langchain.agents.middleware import ModelRetryMiddleware class TotalResult(BaseModel): amount: float = Field(description="商品总价") @tool def calculate_total(price: float, quantity: int) -> float: """根据商品单价和数量计算总价。""" return price * quantity model = init_chat_model( "openai:gpt-4.1-mini", temperature=0, ) agent = create_agent( model=model, tools=[calculate_total], system_prompt="回答商品总价问题时,先调用 calculate_total 工具。", response_format=ToolStrategy(TotalResult), middleware=[ModelRetryMiddleware(max_retries=2)], ) result = agent.invoke({ "messages": [ { "role": "user", "content": "一件商品 12.5 元,买 2 件,共多少钱?", } ] }) for message in result["messages"]: print(message.type, message.content) print("结构化总价:", result["structured_response"].amount)其中ModelRetryMiddleware处理模型调用失败后的重试;它不能代替工具错误处理。查看result["messages"]可以确认这次运行是否实际调用了calculate_total。
九、补充:Embedding 入口
主包还提供init_embeddings,用于统一创建向量模型。在 RAG 场景中,它可以把查询或文档转换成向量:
from langchain.embeddings import init_embeddings embeddings = init_embeddings("openai:text-embedding-3-small") query_vector = embeddings.embed_query("退款规则")这部分与 Agent 主循环不同。准备 Agent 开发面试时,知道这个入口及其用途即可;具体检索系统可单独学习。
十、常见面试问题
问:注册了工具,为什么运行记录里没有工具结果?
答:注册工具只是向 Agent 提供能力;模型这次可能没有提出工具调用。
问:模型提出工具请求,是否说明函数已经运行?
答:没有。请求和执行是两个阶段;要检查后续是否出现工具结果。
问:为什么结构化输出不从最后一条消息里读取?
答:主包将解析后的结果放在structured_response。结构化输出流程也不保证最后一条消息是适合直接解析的自然语言回答。
问:传了相同的thread_id,为什么 Agent 仍不记得上次对话?
答:还需要配置checkpointer。会话 ID 负责标识会话,不负责保存状态。
问:当前 1.x 版本应以哪些 API 为主线?
答:init_chat_model、create_agent、工具注册、response_format、middleware、stream和会话状态。旧教程中的LLMChain、AgentExecutor、initialize_agent不宜作为当前主包的入门路线。