抓包LangChain-了解LangChain的原理

当我们通过LangChain框架的init_chat_modelChatDeepSeek调用大模型的时候,由于LangChain把细节都封装起来了,我们看不到LangChain是怎么跟大模型交互的。LangChain发送给大模型的原始报文到底是什么样子的?出问题时,我们还是想要看一下到底是大模型返回的原始内容就不对呢?还是LangChain框架出问题了?

抓包LangChain

本次我们还是使用之前《抓包Codex-查看Codex的提示词是怎么写的》用到的抓包工具mitmproxy进行抓包。不知道怎么使用mitmproxy工具的请参考文章《抓包Codex-查看Codex的提示词是怎么写的》

执行mitmproxymitmweb抓包命令:mitmweb --mode reverse:https://api.deepseek.com -p 8889

上述命令执行成功之后,在浏览器中访问:http://127.0.0.1:8081/?token=你的token

安装LangChain依赖,调用DeepSeek大模型必须安装langchain-deepseek这个依赖。

csharp 复制代码
uv add langchain
uv add langchain-deepseek
uv add python-dotenv

LangChain调用大模型代码如下:

注意 :将init_chat_modelapi_basebase_url设置为:http://127.0.0.1:8889

如果你的langchain-deepseek版本小于1.1.0版本,init_chat_model的参数名字必须是api_base

如果你的langchain-deepseek版本大于等于1.1.0版本,init_chat_model的参数是api_basebase_url都可以。

ini 复制代码
from langchain.chat_models import init_chat_model  
from langchain.messages import AIMessage  
from dotenv import load_dotenv  
  
# 加载.env文件中的环境变量  
load_dotenv()  
  
# init_chat_model 会自动从环境变量中读取DEEPSEEK_API_KEY这个环境变量  
# 使用deepseek大模型,必须安装langchain-deepseek这个依赖包  
model = init_chat_model(
	model="deepseek-v4-flash",
	model_provider="deepseek",
	api_base="http://127.0.0.1:8889"
)  
  
response = model.invoke("你好")  
print(response) # response是AIMessage  
print("response的类型为:", isinstance(response, AIMessage))

右键运行上面的代码,然后就能在浏览器中看到LangChain发送给DeepSeek的原始报文以及DeepSeek的原始响应内容了。

LangChain结构化输出的原理

结构化输出就是让大模型按照固定格式输出,比如要求大模型的回答必须是一个JSON字符串或者必须是一个XML字符串。这样方便我们将将大模型的输出转换为代码中的对象。

结构化输出的代码如下:

python 复制代码
from langchain.chat_models import init_chat_model  
from langchain.messages import AIMessage  
from dotenv import load_dotenv  
from pydantic import BaseModel, Field  
  
# 加载.env文件中的环境变量  
load_dotenv()  
  
class Movie(BaseModel):  
    """电影的详细信息"""  
    title: str = Field(description="电影的名字")  
    year: int = Field(description="电影的上映日期")  
    director: str = Field(description="电影的导演名字")  
    rating: float = Field(description="电影的豆瓣评分")  
  
# init_chat_model 会自动从环境变量中读取DEEPSEEK_API_KEY这个环境变量  
# 使用deepseek大模型,必须安装langchain-deepseek这个依赖包  
model = init_chat_model(
	model="deepseek-v4-flash",
	model_provider="deepseek",
	api_base="http://127.0.0.1:8889"
)  
  
model_with_structured_output = model.with_structured_output(Movie)  
  
response = model_with_structured_output.invoke("请提供《我不是药神》这部电影的详细信息")  
  
print(response) # response是AIMessage  
print("response的类型为:", isinstance(response, AIMessage))

LangChain的结构化输出,实际上是通过大模型的toolstool_choice工具调用实现的。我们看LangChain发送给DeepSeek大模型的请求报文就知道了。

DeepSeek的说明

这个问题已经有人反馈了,BUG DeepSeek V4 rejects tool_choice="required" and specific function tool_choice --- breaks structured output in all agent frameworks #1376

LangChain社区解决方案

解决办法:将DeepSeek的思考模式thinking设置为disabled

Python 复制代码
from langchain.chat_models import init_chat_model   
from dotenv import load_dotenv   
from pydantic import BaseModel, Field  
  
# 加载.env文件中的环境变量  
load_dotenv()  
  
class Movie(BaseModel):  
    """电影的详细信息"""  
    title: str = Field(description="电影的名字")  
    year: int = Field(description="电影的上映日期")  
    director: str = Field(description="电影的导演名字")  
    rating: float = Field(description="电影的豆瓣评分")  
  
# init_chat_model 会自动从环境变量中读取DEEPSEEK_API_KEY这个环境变量  
# 使用deepseek大模型,必须安装langchain-deepseek这个依赖包  
model = init_chat_model(
	model="deepseek-v4-flash", 
	model_provider="deepseek", 
	extra_body={"thinking": {"type": "disabled"}},                                     api_base="http://127.0.0.1:8889"
)  
  
model_with_structured_output = model.with_structured_output(Movie)  
  
response = model_with_structured_output.invoke("请提供《我不是药神》这部电影的详细信息")  
  
print(response) # response是Movie  
print("response的类型为:", isinstance(response, Movie))

结构化输出时获取大模型返回的原始数据include_raw

python 复制代码
from langchain.chat_models import init_chat_model  
from langchain.messages import AIMessage  
from dotenv import load_dotenv  
from pydantic import BaseModel, Field  
  
# 加载.env文件中的环境变量  
load_dotenv()  
  
class Movie(BaseModel):  
    """电影的详细信息"""  
    title: str = Field(description="电影的名字")  
    year: int = Field(description="电影的上映日期")  
    director: str = Field(description="电影的导演名字")  
    rating: float = Field(description="电影的豆瓣评分")  
  
# init_chat_model 会自动从环境变量中读取DEEPSEEK_API_KEY这个环境变量  
# 使用deepseek大模型,必须安装langchain-deepseek这个依赖包  
model = init_chat_model(
	model="deepseek-v4-flash", 
	model_provider="deepseek", 
	extra_body={"thinking": {"type": "disabled"}}, 
	api_base="http://127.0.0.1:8889"
)  
  
model_with_structured_output = model.with_structured_output(Movie, include_raw=True)  
  
response = model_with_structured_output.invoke("请提供《我不是药神》这部电影的详细信息")  
  
print(response) # response是dict  
print(type(response))  
print("response的类型为:", isinstance(response, AIMessage))

或者继续使用DeepSeek的思考模式,但是通过LangChaindisabled_params参数,禁用DeepSeektool_choice参数。注意,我们禁用的是tool_choice参数,不是禁用tools参数。DeepSeekFunction Calling功能还在的,DeepSeek还是会调用工具的。

Python 复制代码
from langchain.chat_models import init_chat_model  
from langchain.messages import AIMessage  
from dotenv import load_dotenv  
from pydantic import BaseModel, Field  
  
# 加载.env文件中的环境变量  
load_dotenv()  
  
class Movie(BaseModel):  
    """电影的详细信息"""  
    title: str = Field(description="电影的名字")  
    year: int = Field(description="电影的上映日期")  
    director: str = Field(description="电影的导演名字")  
    rating: float = Field(description="电影的豆瓣评分")  
  
# init_chat_model 会自动从环境变量中读取DEEPSEEK_API_KEY这个环境变量  
# 使用deepseek大模型,必须安装langchain-deepseek这个依赖包  
model = init_chat_model(
		model="deepseek-v4-flash", 
		model_provider="deepseek", 
		disabled_params={"tool_choice": None},  
        api_base="http://127.0.0.1:8889"
    )  
  
model_with_structured_output = model.with_structured_output(Movie, include_raw=True)  
  
response = model_with_structured_output.invoke("请提供《我不是药神》这部电影的详细信息")  
  
print(response) # response是dict  
print("大模型是否正常返回结构化信息,解析是否出错:",response['parsing_error'])  
if response['parsed']:  
    print("大模型返回正常:",response['parsed']) # Movie
print(type(response))  
print("response的类型为:", isinstance(response, AIMessage))

LangChain的Tool calling工具调用原理

可以这么说,大模型的Function Calling功能是Agent的基石。如果大模型没有Function Calling功能,所有的Agent都得当场瘫痪。

调用工具的代码如下:

python 复制代码
from langchain.chat_models import init_chat_model  
from langchain.messages import AIMessage  
from dotenv import load_dotenv  
from langchain.tools import tool  
  
# 加载.env文件中的环境变量  
load_dotenv()  
# 使用@tool装饰器,定义一个工具
@tool  
def get_weather(cityname: str) -> str:  
    """根据城市名称获取城市的天气信息"""  
    return f"{cityname},晴转多云,局部有雨。"  
  
# init_chat_model 会自动从环境变量中读取DEEPSEEK_API_KEY这个环境变量  
# 使用deepseek大模型,必须安装langchain-deepseek这个依赖包  
model = init_chat_model(
	model="deepseek-v4-flash", 
	model_provider="deepseek", 
	disabled_params={"tool_choice": None},  
    api_base="http://127.0.0.1:8889"
)  

# 给大模型绑定工具
model_with_tools = model.bind_tools([get_weather])  
  
messages = [{"role": "user", "content": "请帮我查询深圳的天气信息"}]  
ai_msg = model_with_tools.invoke(messages)  
messages.append(ai_msg)  

# 大模型请求调用工具
for tool_call in ai_msg.tool_calls: 
	# 代码帮大模型调用工具,然后将工具的执行结果告诉大模型
    tool_result = get_weather.invoke(tool_call)  
    messages.append(tool_result)

# 大模型根据工具的执行结果,回答用户信息
final_response = model_with_tools.invoke(messages)  
print(final_response.text)  
print("response的类型为:", isinstance(final_response, AIMessage))

注意 :即使我们禁用了DeepSeektool_choice参数,DeepSeek一样可以正常调用我们自定义的函数。

相关推荐
南雨北斗1 小时前
TS方式构建Vue3 toast组件(一)
前端
IT小白杨1 小时前
2026游戏工作室环境隔离指南:从设备指纹到移动IP的部署实践
前端·经验分享·网络协议·tcp/ip·游戏·安全架构·指纹浏览器
泡海椒1 小时前
JQuick-Curl 快速上手:Maven 依赖与第一个 HelloWorld 请求
后端
用户298698530141 小时前
使用 JavaScript 在 React 中实现 Word 转 PDF
前端·javascript·react.js
QQ_21696290961 小时前
【源码编号:project86570】SpringBoot电影院在线选座售票系统:电影排片、在线选座、订单购票、后台管理完整实战
java·spring boot·后端
卷无止境1 小时前
FastAPI 实现 SSO,从协议原理到生产级落地
后端·python·fastapi
悟空瞎说1 小时前
UICollectionViewLayout 全套源码 + 逐行中文注释 + 使用场景说明
javascript
hunterandroid1 小时前
[鸿蒙从零到一] @Observed 与 @ObjectLink 深层响应陷阱与最佳实践
前端
卷无止境1 小时前
FastAPI 与 RustFS 集成指南
后端·python·fastapi