LangChain 基础学习

LangChain 安装的依赖工具

uv add langchain

集成deepseek

uv add langchain-deepseek

集成openai

uv add langchain-openai

集成Anthropic

uv add langchain-anthropic

1、任务规划

2、调用工具(传统编程开发对应模块的工具)

3、感知反馈(与2一直相互学习使用)

天气查询的AI智能体

开发Agent

1、加载环境变量

2、定义工具

3、定义Agent

4、调用Agent

1、加载环境变量

from dotenv import load_dotenv

load_dotenv()

2、定义工具

2、定义工具

from langchain.tools import tool

@tool

def getWealther(location:str) -> str:

调用第三方的api获取具体的天气

return f"Current weather in {location} is sunny"

3、创建Agent

from langchain.agents import create_agent

agent = create_agent(

"deepseek-chat"

tools = getWeather

)

4、发起调用

printf(" 正在调用 ")

response = agent.invoke{

message:[

{"role":"users","content":"杭州天气今天如何?"}

]

printf(response)

for messgae in response "message":

print(message.model_dump_json(indent = 2))

print("---------")

chioce - message - tool_calls- function langchain会帮你调用工具

开发步骤

1、引入依赖

uv add langchain

uv add langchian-deepseek

2、配置环境

DEEPSEEK_API_KEY = xxx 需要查找自己要用的apikey键名!!!

3、初始化模型

from langchain.chat_models import init_chat_model

model = init_chat_model(model = "deepseek-chat")

自定义模型提供商

import os

base_url = os.getenv("DASHSCOPE_BASE_URL")

api_key = os.getenv("DASHSCOPE_API_KEY")

model = init_chat_model(

model = "qwen-max", #模型名称

model_provider = "openai" #如果是Langchain不支持的模型需要制定提供商

base_url = base_url,

api_key = api_key

)

response = model.invoke(" ") ----默认role = user

response = model.stream(" ") -----默认流式

访问模型

方式一 invoke---阻塞等待的

response = model.invoke(" ") ----默认role = user

传入数组也可以 定义role content等等

方式二 stream

response = model.stream(" ") -----默认流式

response---generator

遍历循环才能拿到所有数据

for chunk in stream :

print(chunk.content,end = "",flush=Ture)

创建智能体 create_agent

使用初始化好的model

form langchain.agents import create_agent

agent = create_agent(model = model)

agent = create_agent(model = "deepseek-chat")

调用智能体

response = agent.invoke({...:{}})

response = agent.stream(

{"message":{"role":"user","content":"你是谁?"}},

stream_mode = "message"

)

print(type(message))

for token,metadata in message:

if token.content:

print(token.content,end = "",flush=True)

BaseMessage

SystemMessage Role:system

HumanMessage Role:user

AIMesaage Role:assistant

ToolMessage Role:tool

from langchain.agents import create_agent

from langchain.tools import tool

@tool

def get_weather(location:str)->str:

return f"Current weather in {location} is sunny!"

agent = create_agent(model = "deepseek-chat",tools = get_weather)

response = agent.invoke({

"message": [

{role: system ,content : 你是一个AI助手},

{role:user ,content:

多模态:

python 复制代码
from langchain.chat_models import init_chat_model
import os
model = init_chat_model(
        model = "qwen3.5-plus",
        model_provider = "openai",
        base_url = os.getenv("DASHSCOPE_BASE_URL")
        api_key = os.getenv("DASHSCOPE_API_KEY")
)
agent = create_agent(model = moedl)
message = HumanMessage([
    {"type":"text","text":"描述一下图片内容"},
    {"type":"image","url":"图片的地址"}
])
strem = agent.stream(
    {"message":[message]}, #是一个数组
    stream_mode = "message"
)
for chunk,metadata in strem:
    if chunk.content:
        print(chunk.content,end="",flush=True)

本地图片数据

图片转为base64字符串发送给模型

uv add ipywidgets

python 复制代码
from ipywidgets import FileUpload
from Ipython.display import display

uploader = FileUpload(accpet = '*', multiple = False)
display(uploader)
print(uploader.value)

#转base64编码
import base64
uploaded_file = uploader.value[0]
content_mv = uploaded_file["content"]
img_bytes = bytes(content_mv)

img_b64 = base64.b64encode(img_bytes).decode("utf-8")

#整合message
multimodal_question = HumanMessage([
    { "type":"image",
      "base64":img_b64,
      "mime_type":"image/jpeg",
    },
    { "type":"text","text":"给我讲一下图片中的城市"}
])

for chunk,metadata in agent.stream(
    ["message":[mutimodal_question]}, #注意消息传入的时候为数组
    stream_mode = "message"
):
    print(chunk.content,end="",flush=True)

提示词(prompts)

python 复制代码
from langchain.agents import create_agent
from langchain.message import HumanMessage

#创建智能体
agent = create_agent(
    model = "deepseek-chat",
    system_prompt = "你以海盗的身份回答用户的问题"
)

#调用
message = agent.stream(
    {"message":[HumanMessage(content="你是谁?")]},
    stream_mode = "message"
)

for chunk,metadata in message:
    print(chunk.content,end = "",flush = True)

提示词工程

身份角色、指令说明、对话示例、背景信息(RAG中)。

Markdown语法写提示词更好,XML标签也可

python 复制代码
system_prompt = """ 你是一个编程助手,你帮助用户编写Python代码"""

agent = create_agent(
    model = "deepseek-chat",
    system_prompt = system_prompt
)

for token,metadata in agent.stream(
    { "message":[HumanMessage(content = "怎么样定义一个string变量存储学校名称")]},
    stream_mode = "message"
):
    print(token.content,end = "",flush = True)

添加指令的约束:

python 复制代码
system_prompt = """ 
#身份 
-你是一个编程助手,你帮助用户编写Python代码
#指令
-定义变量时,使用snake case命名法,而不是camel case命名法。
-不要反悔markdown格式说明,仅仅返回代码即可
"""

agent = create_agent(
    model = "deepseek-chat",
    system_prompt = system_prompt
)

for token,metadata in agent.stream(
    { "message":[HumanMessage(content = "怎么样定义一个string变量存储学校名称")]},
    stream_mode = "message"
):
    print(token.content,end = "",flush = True)

也可以通过示例来约束回复内容!!!

结构化输出:

python 复制代码
system_prompt = """ 
#身份 
-你是一个编程助手,你帮助用户编写Python代码
#指令
-务必使用JSON格式输出,不要加任何的markdown格式
#示例
-XXXXXXX

"""

agent = create_agent(
    model = "deepseek-chat",
    system_prompt = system_prompt
)
response = agent.invoke(
    { "message":[HumanMessage(content = "怎么样定义一个string变量存储学校名称")]},
)

print(response['messgae'][-1].content)


#基于Model的结构化输出

from pydantic import BaseModel
#定义一个类,用于封装模型要输出的数据:
class CapitalInfo(BaseModel):
    name:str
    location:str
    vibe:str
    economy:str

#创建智能体
agent = create_agent(
    model = "deepseek-chat",
    system_prompt = "你是一名科学家,根据用户的要求创建一个太空之都",
    response_format = CapitalInfo #设置结构化输出
)

response = agent.invoke(
    {"messgae":[HumanMessage(content = "月球的首都是什么")]}
)
print(response)

print(response['structured_response'])
city = response['structured_response']
print(f"{city.name}位于{city.location},是一座{city.vibe}的城市,其主要的产业包括{city.economy}")

工具

模型:Agent的大脑,负责推理、分析、规划任务步骤

工具:Agent的手脚,负责执行任务,与外界交互

因此流程如下: 定义工具--初始化模型--初始化Agent,绑定模型和工具

自定义工具

1.1 基于tool描述工具(不推荐)

from langchain_core.tools import tool

@tool("square_root",description = "Calculate the square root of a number "

def tool1(x:float)->float:

return x**0.5

1.2使用函数名和文档注释描述工具

from langchain_core.tools import tool

@tool

def square_root(x:float)->float:

""" Calculate the square root of a number"""

return x**0.5
#定义一个查询天气的tool

@tool

def get_weather(location: str, units: str = "celsius", include_forecast: bool = False) -> str:

"""

Get current weather and optional forecast.

Args:

location: city name or coordinates

units: unit of degrees

include_forecast: does it include the weather forecast

"""

temp = 22 if units == "celsius" else 72

result = f"Current weather in {location}: {temp} degrees {units0.upper()}"

if include_forecast:

result += "Next 5 days: Sunny"

return result

1.3 定义Pydantic Model 描述参数列表

python 复制代码
# 通过自定义model来约束入参
from pydantic import BaseModel, Field
from typing import Literal

# 例如一个查询天气的tool
class WeatherInput(BaseModel):
    """查询天气的输入参数."""
    location: str = Field(description="City name or coordinates")
    units: Literal["celsius", "fahrenheit"] = Field(
        default="celsius",
        description="Temperature unit preference"
    )
    include_forecast: bool = Field(
        default=False,
        description="Include 5-day forecast"
    )
@tool(args_schema = WeatherInput)
def get_weather(location:str,units:str = "celsius",include_forcast:bool = False)->str:
    """Get current weather and optional forecast"""
    temp =22 if units == "celsius" else 72
    result = f"Current weather in {location}: {temp} degress {units[0].upper()}"
    if include_forecast:
        result += "\nNext 5 days :Sunny"
    return result

测试方案

python 复制代码
from langchain.agents import create_agent
from langchain.message import HumanMessage

agent = create_agent(
    model = "deepseek-chat",
    tools[suqare_root,get_weather]
)

for token,metadata in agent.stream(
    {"message":[HumanMessage("杭州接下来的天气如何?")]},
    stream_mode = "message"
):
    print(token.content,end="",flush=True)

response = agent.invoke(
    {"message":[HumanMessage("467和529的平方根是多少?")]},
)

for message in response['message']:
    print(message.pretty_print())

使用预定义好的工具 Tavily(用于做web搜索的工具)

pip install -qU langchain-tavily

1、注册账号,创建API_KEY

2、配置环境变量:TABVILY_API_KEY

3、安装依赖 uv add langchain-tavily

python 复制代码
from langchain_tavily import TavilySearch

search_tool = TavilySearch(
    max_result = 5,
    topic = "general", #general news finance
    # include_answer = False,
    # include_raw_content = False,
    # include_images = False,
    # include_image_descriptions = False,    
    # search_depth = "basic",
    # time_range = "day",
    # include_domains = None,
    # exclude_domains = None
)

search_tool.invoke("钟山风景区在哪里?")
#result 存储搜索结果 title 为提问 content为搜索到的内容  score 为相关度

agent = create_agent(
    model = "deepseek-chat",
    tools = [search_tool],
    system_prompt = "你是一个智能助手,你使用工具来解决用户的问题"
)

response = agent.invoke(
    {"message":[HumanMessage(content = "钟山风景区在哪个城市?")]},
)

for message,metadata in response['message']:
    message.pretty_print()

优化 减少token使用 提高可信度

解决思路:自定义tavily工具 结构化输出

python 复制代码
tavily = TavilySearch(
    max_results = 5,
    topic = "general"
)

#然后封装自己的tool
@tool
def web_search(query:str):
    return tavily.invoke(query)

from pydantic import BaseModel,Field

#Agent回答内容引用网页信息   结构化输出
class Reference(BaseModel):
    title:str = Field(description = "The title of the web page cited in the answer")
    url:str = Field(description = "The url of the web page cited in the answer")

#Agent的回答内容
class AnswerInfo(BaseModel):
    answer:str = Field(description = "The final answer for user")
    reference : list[Reference] = Filed(descirption = "the web pages cited in the answer")

agent = create_agent(
    model = "deepseek-chat",
    tools = [web_search],
    system_prompt = "你是一个智能助手,你使用工具来解决用户的问题",
    response_format = AnswerInfo
)

response = agent.invoke(
    {"message":[HumanMessage(content = "钟山风景区在那个城市?")]},
)

print(response['structured_response'])

记忆

记忆分类:

短期记忆:当前任务或会话的上下文

长期记忆:跨任务或会话的经验与知识

短期记忆(使用AgentState来实现的,会话历史是AgentState的一部分) 可自定义AgentState

LangChain提供Checkpointer对象来保存AgentState,每一次用户与AI交互都会生成一个快照,记录为一个checkpoint。同一会话的多个checkpoint形成一个组,用同一个thread_id标记。

添加记忆(短期记忆)

存入内存的方法

1、导入并初始化Checkpointer

2、创建Agent,指定Checkpointer

3、调用Agent,指定thread_id

python 复制代码
form langchain.agents import create_agent
form langgraph.checkpoint.mermory import InMemorySaver

agent = create_agent(
    "deepseek-chat",
    checkpointer = InMemorySaver()
)

from langchain.message import HumanMessage

#设定thread_id 作为会话标识
config = {"configurable":{"thread_id":"thread_1"}}

#第一次调用
response = agent.invoke(
    {"message":[HumanMessage(content = "你好,我是h,我喜欢猫咪")]},
    config #调用时添加thread_id
)

print(response)

#第二次调用
response = agent.invoke(
    {"message":[HumanMessage(content = "我最喜欢的动物是什么")]},
    config
)
print(response)

存入数据库的方法

安装依赖

pip install langgraph-checkpoint-postgres

python 复制代码
uv add langgraph-checkpoint-sqlite

持久化存储(SQL)

1、导入依赖

2、初始化checkpointer

3、自动建表

4、创建Agent、吃定checkpointer

python 复制代码
import sqlite3
from langgraph.checkpoint.sqlite import SqliteSaver
from langchain.message import HumanMessage

#连接sqlite 避免创建线程和异步操作出错因此check_same_thread = False
connection = sqlite3.connect("resources/checkpoint.db",check_same_thread = False)
#初始化checkpointer
checkpointer = SqliteSaver(connection)
#自动建表
checkpointer.setup()

#创建Agent
agent = create_agent(
    "deepseek-chat",
    checkpointer = checkpointer,
)

#设定thread_id
config = {"configurable":{"thread_id":"thread_2"}}

#第一次调用
response = agent.invoke(
    {"message":[HumanMessage(content:"你好 我喜欢猫咪!")]},
    config #调用时添加thread_id
)
print(response)

#第二次调用
response = agent.invoke(
    {"message":[HumanMessage(content:"我最喜欢什么动物?")]},
    config
)
print(response)

记忆管理策略(避免 上下文溢出)

策略:修剪(Trim)、删除(Delete)、总结摘要(Summarize)、自定义(Custom)

修剪:会移除最老旧的内容,会出现记忆丢失(不推荐)

删除:直接删除快照信息(不推荐)

总结摘要:先总结历史信息的早期信息,得到摘要(总结摘要的模型--会花token)放在快照里,然后再次调用与摘要进行拼接发送给Agent

SummarizationMiddleware中间件(实现消息摘要)

from langchain.agents.middleware import SummarizationMiddleware

一个agent可以传入多个中间件的

python 复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware
from langchain_core.runnables import RunnableConfig
from langgraph.checkpoint.memort import InMemorySaver

checkpointer = InMemorySaver

middleware = SummarizationMiddleware(
    model = "deepseek-chat",
    trigger = ("message",3), #消息超出3条时触发
    keep = ("message",1)    #总结为一条消息
)

agent = create_agent(
    model = "deepseek-chat",
    middleware = [middleware],
    checkpointer = checkpointer,
)
#制造长会话
config:RunnableConfig = {"configurable" :{"thread_id":"thread_3"}}
agent.invoke({"message":[HumanMessage("你好,我是胡歌")]},config)
agent.invoke({"message":[HumanMessage("我最喜欢的运动是打篮球")]},config)
agent.invoke({"message":[HumanMessage("我最喜欢的动物时猫咪")]},config)
#测试处理
final_response = agent.invoke({"message":HumanMessage(content = "你还记得我吗")},config)

print(final_response)
for message in final_message["message"]:
    message.pretty_print()

小型案例--AI私厨管家(多模态模型+Tavily)

图片识别、智能搜索、智能排序、创意建议、对话交互

难点:前端、提示词prompt。

python 复制代码
#1、定义模型 2、定义工具 3、添加记忆管理 4、定义智能体 5、测试 6、流式调用 7、获取会话历史 

from dotenv import load_dotenv
load_dotenv()

#定义模型
from langchain.chat_model import init_chat_model
import os

model = init_chat_model(
    model = "qwen3.5-plus",
    model_provider = "openai",
    base_url = os.getenv("DASHSCOPE_BASE_URL"),
    api_key = os.getenv("DASHSCOPE_API_KEY")
)

#定义工具
from langchain_tavily impor TavilySearch

#web搜索工具 使用Tavily
web_search = TavilySearch(
    max_result = 5,
    topic = "general",
)

#记忆管理 sqlite
from langgraph.checkpoint.sqlite import SqliteSaver
import sqlite3
#链接sqlite
connection = sqlite3.connect("resources/personal_clief.db",check_same_thread = False)
#初始化checkpinter
checkpointer = SqliteSaver(connection)
#自动建立表格
checkpointer.setup()

#定义智能体
from langchain.agents import create_agent
system_prompt = """
你是一名私人厨师,收到用户提供的食材照片或清单后,请按照以下流程进行操作:
1、识别和评估食材:若用户提供照片,首先辨识所有可见食材。基于食材的外观状态,评估其新鲜程度与可用量,整理出一份"当前可用食材清单"。
2、智能食谱检索:优先调用web_search工具,以"可用食材清单"为核心关键词,查找可行菜谱。
3、多维度评估与排序:从营养价值和制作难度两个维度对检索到的候选食谱进行量化打分,并根据得分排序,制作简单且营养丰富的排名靠前。
4、结构化方案输出:把排序后的食谱整理为一份接口清晰的建议报告,要包含食谱信息、得分、推荐理由、食谱的参考图片,帮助用户快速做出决策。

请严格按照流程,优先调用web_search 工具进行搜索食谱,搜索不到的情况下才能自己发挥。
"""

agent = create_agent(
    model = model,
    tools = [web_search],
    system_prompt = system_prompt,
    checkpointer = checkpointer
)

#测试
from langchain.message import HumanMessage
multimodal_message = HumanMessage([
    {"type":"text","text":"帮我看看能做什么"},
    {"type":"image","url":"https://aisearch.cdn.bcebos.com/pic_create/2026-04-10/10/74d52055e4947f8c.jpg"},
]}

config = {"configurable":{"thread_id":"1"}}

response = agent.invoke({"message":[multimodal_message]},config)
#友好打印
for message in response["message"]:
    message.pretty_print()

response = agent.invoke(
    {"message":[HumanMessage(content = "我喜欢第三道菜,可以说得详细一些吗?")]},
    config
)
#友好打印
for message in response["message"]:
    message.pretty_print()

response['message'][-1].pretty_print()

基于LangSmith调试

langchain的Agent底层基于LangGraph实现的,而LangGraph提供了完整的后端部署功能,自带非常完善的API接口,无需额外处理。

同时Langchain还提供了基于LangSmith的GUI控制台实现Agent的调试、监控、一键部署。

https://smith.langchain.com注册LangSmith===settings 创建API_KEY

在.env文件中添加配置

#LangSmith

LANGSMITH_API_KEY = xxxxx

LANGSMITH_TRACING = true

#项目名,可以自定义

LANGSMITH_PROJECT= lc-course

创建app/agents/创建文件(xxx.py)

personal_chief.py就是放入上面的代码 提问相关的不进行放入 Langsmith 自动会记忆 不需要checkpointer

LangGraph会自动托管Agent的记忆,因此代码中不需要自己添加checkpointer!!!!

LangGraph自带Restful的API接口,我们只要定义好agent即可,其他不用管

本地部署

uv add langgraph-cliinmem

在项目根目录中新加一个langgraph.json

{

"dependencies":".",

"graphs":{

"chief_agent":"./app/agents/personal_chief.py:agent" #agent变量

},

"env" : ".env"

}

使用LangGraph命令本地部署Agent

uv run langgraph dev 环境中不要添加中文即可

自己开发服务端自己进行部署 价格上会更加划算

服务端部署

服务端:工具tavily、智能体Agent、记忆存储checkpointer

入口 /api/vl/chat聊天 /api/vl/msg 会话历史 /api/vl/oss 文件上传授权

前端调用接口进行使用即可,

oss服务 先完成图片的上传然后传入url给发模型就可以了

注册一个OSS服务

安装依赖

uv add fastapi alibabacloud-oss-v2
项目框架

|-app

|----agents

|---------init.py

|---------personal_chief.py

|----api

|-------v1

|----------init.py

|----------chat.py

|----------oss.py 身份验证

|-------init.py

|----common 日志存储

|-------init.py

|-------logger.py

|----models

|-------init.py

|-------schemas.py

|----static 静态页面

|-------init.py

|-------main.py 静态页面内容
.env 配置oss环境

OSS_ACCESS_KEY_ID =

OSS_ACCESS_KEY_SECRET =

OSS_BUCKET = temp

python 复制代码
#./app/api/v1/oss.py
import alibabacloud_oss_v2 as oss
from fastapi import APIRouter
from datetime import timedelta
import os

router = APIRouter

#从环境变量中加载凭证信息,用于身份验证
credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()

#加载SDK的默认配置,并设置凭证提供者
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider

#方式一:只填写Region(推荐)
#必须指定Region ID,SDK会根据Region自动构造HTTPS访问域名
cfg.region = 'cn-beijing'

#使用配置好的信息创建OSS客户端
client = oss.Client(cfg)

#OSS 域名配置
OSS_ENDPOINT = os.getenv("OSS_ENDPOINT","oss-cn-beijing.aliyuncs.com")
OSS_BUCKET = os.getenv("OSS_BUCKET")

#对外暴露的接口
@router.get("/oss/presign")
def chat_endpoint(filename:str):
    #根据文件扩展名判断 Content-Type
    content_type_map = {
        "jpg":"image/jpeg",
        "jpeg":"image/jpeg",
        "png":"image/png",
        "gif":"image/gif",
        "webp":"image/webp",
    } 
    ext = filename.split(".")[-1].lower() if filename else "jpg"
    content_type = content_type_map.get(ext,"application/octet-stream")

    pre_result = client.presign(oss.PutObjectRequest(
        bucket = OSS_BUCKET,
        key = filename,
        content_type = content_type,
    ),expires = timedelta(seconds = 3600))

    #返回上传 URL 和可访问的图片路径
    return {
        "uploadUrl": pre_result.utl.strip('"'),
        "contentType": content_type,
        "accessUrl": f"https://{OSS_BUCKET}.{OSS_ENDPOINT}/{filename}"
    }
python 复制代码
#./app/api/v1/char.py
from fastapi import APIRouter
from fastapi.responses import StreamingResponse
from app.models.schemas import ChatRequest
from app.agents.personal_chief import search_recipes,get_messages,clear_messages

router = APIRouter()

@router.post("/chat/stream")
async def chat_endpoint(request:ChatRequest): #前端对话的请求
   """流式对话"""
    return StreamingResponse(
        search_recipes(request.message, request.image_url, request.thread_id),
        media_type="text/event-stream"
    )

@router.get("/chat/message")
async def get_chat_message(thread_id:str):
     """获取历史消息"""
    messages = get_messages(thread_id)
    return {"messages": messages}


@router.delete("/chat/message")
async def clear_chat_message(thread_id:str):
     """获取历史消息"""
    messages = get_messages(thread_id)
    return {"messages": messages}
python 复制代码
#./app/models/schemas.py
import...

#数据模型
class ChatRequest(BaseModel): 2usages
    message:str
    image_url:Optional[str] = None
    thread_id:str
python 复制代码
#./app/common/logger.py

#日志的处理 时间-级别-模块-消息
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(name)s - %(message)s"

def setup_logging():
    logging.basicConfig(
        level = logging.INFO,
        format = LOG_FORMAT,
        handlers = [
            logging.StreamHandler(sys.stdout), #输出到控制台
            #logging.FileHandler("app.log") #如果需要存到文件可以开启
        ]
    )

#创建一个全局的logger 实例
logger = logging.getLogger("personal_chief")
python 复制代码
# 该内容放在./app/agents/personal_chief.py
from app.common.logger import logger
from langchain.mesage import HumanMessage,AIMessage,AIMessageChunk

#流式对话
async def search_recipes(prompt: str, image: str, thread_id: str):
    """调用agent搜索食谱"""

    logger.info(f"[用户]: {prompt}, image: {image}, thread_id: {thread_id}")
    try:
        # 判断是否有图片,封装不同格式的消息
        if not image or image.strip() == "":
            message = HumanMessage(content=prompt)
        else:
            message = HumanMessage(content=[
                {"type": "image", "url": image},
                {"type": "text", "text": prompt}
            ])

        # 流式调用Agent
        for chunk, metadata in agent.stream(
            input: {"messages": [message]},
            config: {"configurable": {"thread_id": thread_id}},
            stream_mode="messages"
        ):
        if isinstance(chunk, AIMessageChunk) and chunk.content:
            yield chunk.content

    except Exception as e:
        logger.error(f"[错误]: {str(e)}")
        yield "信息检索失败,试看手动输入食物列表?"


# 清空会话
def clear_messages(thread_id: str):
    """清空会话"""
    logger.info(f"清空历史消息, thread_id: {thread_id}")
    checkpointer.delete_thread(thread_id)


# 查询会话历史
def get_messages(thread_id: str) -> list[dict[str, str]]:
    """获取会话历史"""
    logger.info(f"获取历史消息,thread_id: {thread_id}")

    # 根据 thread_id 查询 checkpoint
    checkpoint = checkpointer.get({"configurable": {"thread_id": thread_id}})

    # 如果不存在,返回空列表
    if not checkpoint:
        return []

    # 安全获取 messages
    channel_values = checkpoint.get("channel_values")
    if not channel_values:
        return []

    messages = channel_values.get("messages", [])
    if not messages:
        return []

    # 转换消息格式
    result = []
    for msg in messages:
        if not msg.content:
            continue
        if isinstance(msg, HumanMessage):
            result.append({"role": "user", "content": msg.content})
        elif isinstance(msg, AIMessage):
            result.append({"role": "assistant", "content": msg.content})
    return result

流式调用

python 复制代码
from langchain.messages import AIMessageChunk

for chunk, metadata in agent.stream(
    {"messages": [multимодal_message]},
    config,
    stream_mode="messages"
):
    if isinstance(chunk, AIMessageChunk) and chunk.content:
        print(chunk.content, end="", flush=True)

获取会话历史

python 复制代码
checkpointer.get(config)

for m in checkpointer.get(config)['channel_values']['messages']:
    print(type(m))
    print(m)

#便捷的获取会话历史的函数
from langchain.messages import AIMessage

def get_messages(thread_id: str) -> list[dict[str, str]]:
    """获取会话历史"""

    # 根据 thread_id 查询 checkpoint
    cp = checkpointer.get({"configurable": {"thread_id": thread_id}})

    # 如果不存在,返回空列表
    if not cp:
        return []

    # 安全获取 messages
    channel_values = cp.get("channel_values")
    if not channel_values:
        return []

    messages = channel_values.get("messages", [])
    if not messages:
        return []

    # 转换消息格式
    result = []
    for msg in messages:
        if not msg.content:
            continue

        if isinstance(msg, HumanMessage):
            result.append({"role": "user", "content": msg.content})
        elif isinstance(msg, AIMessage):
            result.append({"role": "assistant", "content": msg.content})

    return result

清空会话历史

checkpointer.delete_thread("6")