大模型之Langchain篇——基础操作
写在前面
Langchain学习相对简单,但是及其重要,之前学了一点忘了一点,现在根据楼兰老师再学一遍【LangChain教程】2025吃透LangChain框架快速上手与深度实战,全程干货无废话,三天学完,让你少走百分之99弯路!_哔哩哔哩_bilibili
学习的过程中我只挑了我觉得有意思的,因为之前跟着官网学了一遍,建议跟着楼兰老师都看下。
另外,学习的过程中借用了大模型进行学习。
基础操作
init_chat_model初始化模型
由于openai不对大陆开放,所以需要中转代理,我这里选择closeAI平台,代码如下,apikey自行添加。
from langchain.chat_models import init_chat_model
model = init_chat_model("gpt-4o-mini",model_provider="openai",base_url='https://api.openai-proxy.org/v1')
from langchain_core.messages import HumanMessage,SystemMessage
messages = [SystemMessage("Translate the following from English into Chinese"),HumanMessage("Hello, how are you?")
]
# 返回⼀个AIMessage对象。
model.invoke(messages)
.invoke可以理解为触发请求。
使用除了openai的其他大模型
方法一
Langchain的最大优势就是统一了不同模型的接口,所以一般来说市面上的大模型都支持langchain。用qwen举例,bing搜索百炼大模型平台langchain,然后就会有支持文档。在LangChain中使用百炼_大模型服务平台百炼(Model Studio)-阿里云帮助中心
方法二
可以看到在链接中还是用到了ChatOpenAI类,那有没有单独的模型类呢?有的兄弟有的,我们在langchain_community 0.2.19 — 🦜🔗 LangChain 0.2.17链接中的chat_models模块可以看到有单独实现的ChatTongyi类,我们就可以不用ChatOpenAI类,而直接使用ChatTongyi实现。但实际效果没什么区别。
from langchain_community.chat_models import ChatTongyi
from langchain_core.messages.human import HumanMessage
llm = ChatTongyi(model="qwen-max",pi_key="sk-xxxxxxxxxxxxxxxxxxx",
)
llm.invoke([HumanMessage("你是谁?你能帮我解决什么问题?")])
方法三
还有一种方法是在Providers | 🦜️🔗 LangChain中查看有哪些langchain团队支持的调用方式。比如调用deepseek,需要注意的是我们要提前下载对应的依赖包pip install -U "langchain[deepseek]"
from langchain_deepseek import ChatDeepSeek
提示词模板
提示词模板是用的from langchain_core.prompts import ChatPromptTemplate类实现,我们可以用系统提示词进行隐形设置提示词和避免用户重复输入一些提示词
prompt_template = ChatPromptTemplate.from_messages([("system","Translat the following from English into {language}"),("user","{text}")
])
prompt = prompt_template.invoke({"language":"Chinese","text":"Hello, how are you?"})
prompt
ChatOpenAI参数
我们一般使用ChatOpenAI时常用的参数不多,可以学会常用的,有需求在查资料
llm = ChatOpenAI(model="deepseek-v3",base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",openai_api_key="sk-xxxxxxxxxxxxxxxxxxx",
)
参数目录
参照下面表格,越靠前越常用。
参数名称 | 类型 | 默认值 | 含义 |
model_name | str | "gpt-3.5-turbo" | 要使用的模型名称,例如 "gpt-4", "gpt-3.5-turbo" 等。这是最常用的参数之一。 |
temperature | Optional[float] | None | 采样温度,用于控制生成文本的随机性。值越高,输出越随机。常用参数,用于调整模型创造性。 |
max_tokens | Optional[int] | None | 要生成的最大 token 数量。非常常用,用于控制生成文本的长度。 |
openai_api_key | Optional[SecretStr] | 从 OPENAI_API_KEY 环境变量获取 | OpenAI API 密钥。连接API的必需参数。 |
openai_api_base | Optional[str] | None | API 请求的基础 URL 路径,如果未使用代理或服务模拟器,则留空。如果使用代理或自定义服务,则非常常用。 |
streaming | bool | False | 是否以流式方式返回结果。对于实时应用或长文本生成,此参数非常常用。 |
stop | Optional[Union[List[str], str]] | None | 默认的停止序列。当模型生成这些序列中的任何一个时,将停止生成。常用参数,用于控制生成结束。 |
top_p | Optional[float] | None | 在每一步考虑的 token 的总概率质量。与 temperature 类似,用于控制随机性,但方式不同。常用参数。 |
n | Optional[int] | None | 为每个提示生成的聊天完成次数。当需要生成多个备选答案时使用。 |
presence_penalty | Optional[float] | None | 惩罚重复的 token。用于减少模型生成重复内容。 |
frequency_penalty | Optional[float] | None | 根据频率惩罚重复的 token。与 presence_penalty 类似,但更侧重于频率。 |
seed | Optional[int] | None | 生成的种子,用于保证可复现性(如果模型支持)。对于需要调试或复现结果的场景有用。 |
request_timeout | Union[float, Tuple[float, float], Any, None] | None | 请求 OpenAI 完成 API 的超时时间。对于网络环境不稳定或需要严格控制响应时间的场景有用。 |
max_retries | Optional[int] | None | 生成时最大重试次数。用于处理临时网络问题或API错误。 |
model_kwargs | Dict[str, Any] | {} | 包含任何未明确指定但对 create 调用有效的模型参数的字典。当需要传递OpenAI API中未直接暴露的参数时使用。 |
openai_organization | Optional[str] | 从 OPENAI_ORG_ID 环境变量推断 | OpenAI 组织ID。对于有多个组织的OpenAI账户有用。 |
openai_proxy | Optional[str] | 从 OPENAI_PROXY 环境变量获取 | OpenAI 的显式代理地址。当需要通过特定代理访问OpenAI API时使用。 |
logprobs | Optional[bool] | None | 是否返回 logprobs(对数概率)。用于高级分析或调试模型输出。 |
top_logprobs | Optional[int] | None | 在每个 token 位置返回的最有可能的 token 数量,每个都带有相关的对数概率。使用此参数时,logprobs 必须设置为 True。 |
logit_bias | Optional[Dict[int, int]] | None | 修改指定 token 在完成中出现的可能性。用于引导模型生成或避免特定词汇。 |
reasoning_effort | Optional[str] | None | 约束推理模型的推理工作量。仅适用于推理模型,如 OpenAI o1 和 o3-mini。目前支持的值为 low、medium 和 high。 |
tiktoken_model_name | Optional[str] | None | 使用此类别时传递给 tiktoken 的模型名称。主要用于 token 计数,当实际模型名称不被 tiktoken 直接支持时指定。 |
default_headers | Union[Mapping[str, str], None] | None | 请求中包含的默认 HTTP 头。用于自定义HTTP请求头。 |
default_query | Union[Mapping[str, object], None] | None | 请求中包含的默认查询参数。用于自定义HTTP请求查询参数。 |
http_client | Union[Any, None] | None | 可选的 httpx.Client 实例。仅用于同步调用。用于高级HTTP客户端配置。 |
http_async_client | Union[Any, None] | None | 可选的 httpx.AsyncClient 实例。仅用于异步调用。用于高级HTTP客户端配置。 |
extra_body | Optional[Mapping[str, Any]] | None | 向 OpenAI 兼容 API(如 vLLM)发出请求时,请求参数中要包含的可选额外 JSON 属性。用于与非标准OpenAI兼容API交互。 |
include_response_headers | bool | False | 是否在输出消息的 response_metadata 中包含响应头。用于调试或获取更多响应信息。 |
disabled_params | Optional[Dict[str, Any]] | None | OpenAI 客户端或 chat.completions 端点的参数,对于给定模型应禁用。用于兼容性或强制禁用某些参数。 |
use_responses_api | Optional[bool] | None | 是否使用 Responses API 而不是 Chat API。如果未指定,则将根据调用参数推断。 |
流式输出
可以用流stream的方式进行输出,如下代码
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="qwen-plus",base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",openai_api_key="sk-xxxxxxxxxxxxxxxxxxx",
)
stream = llm.stream([HumanMessage("你是谁?你能帮我解决什么问题?")])
for chunk in stream:print(chunk.text(), end="")
Chain
串行Chain
构建链主要使用 | 符号来构建,因为
1、满足构建的要求是每个链的部件必须是RunnableSerializable的子类,比如prompt_template、ChatOpenAI、StrOutputParser的顶级父类就是RunnableSerializable;
2、使用Chain的目的是方便实现,少代码,少调用invoke
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
# 提示词模板
prompt_template = ChatPromptTemplate.from_messages([("system","Translat the following from English into {language}"),("user","{text}")
])
# 构建阿⾥云百炼⼤模型客户端
llm = ChatOpenAI(model="qwen-plus",base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",openai_api_key="sk-xxxxxxxxxxxxxxxx",
)
# 结果解析器 StrOutputParser会AIMessage转换成为str,实际上就是获取AIMessage的content属性。
parser = StrOutputParser()
# 构建链
chain = prompt_template | llm | parser
# 直接调⽤链
print(chain.invoke({"text":"It rained heavily in Chongqing tonight. I really like it", "language":"Chinese"}))# 继续构建更复杂的链
analysis_prompt = ChatPromptTemplate.from_template("我应该怎么回答这句话? {talk} 。给我⼀个五个字以内的示例")
chain2 = {"talk":chain} | analysis_prompt | llm | parser
print(chain2.invoke({"text":"It rained heavily in Chongqing tonight. I really like it", "language":"Chinese"}))
并行Chain
通过RunnableMap和RunnableLambda实现,RunnableMap作为执行,RunnableLambda用作将结果整合
############### 并行链
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableMap, RunnableLambda, RunnableWithMessageHistory
# 提示词模板
prompt_template_zh = ChatPromptTemplate.from_messages([("system", "Translate the following from English into Chinese"),("user", "{text}")
])
prompt_template_fr = ChatPromptTemplate.from_messages([("system", "Translate the following from English into French"),("user", "{text}")
])
# 构建链
chain_zh = prompt_template_zh | llm | parser
chain_fr = prompt_template_fr | llm | parser
# 并⾏执⾏两个链
parallel_chains = RunnableMap({"zh_translation": chain_zh,"fr_translation": chain_fr
})
# 合并结果
final_chain = parallel_chains | RunnableLambda(lambda x: f"Chinese:{x['zh_translation']}\nFrench: {x['fr_translation']}")
# 调⽤链
print(final_chain.invoke({"text": "nice to meet you"}))
打印出图形化的链图
用.get_graph().print_ascii()来打印输出,需要
final_chain.get_graph().print_ascii()
保存聊天记录
保存聊天记录作为一个基本功能也被langchain封装了,可以用内存、数据库、reids等进行保存,可以参照Message histories | 🦜️🔗 LangChain,这里和楼兰老师一样选择reids,resp.app安装参照windows+Reids可视化工具RESP.app的安装以及使用-CSDN博客,然后就可以运行代码
from langchain_redis import RedisChatMessageHistory
# 这也是BaseChatMessageHistory的⼦类。本地需启动Redis服务
history = RedisChatMessageHistory(session_id="test",redis_url="redis://localhost:6379/0")
# 第⼀轮聊天
history.add_user_message("你是谁?")
aimessage = llm.invoke(history.messages)
print(aimessage.content)
history.add_message(aimessage)
# 第⼆轮聊天
history.add_user_message("请重复⼀次")
aimessage2 = llm.invoke(history.messages)
print(aimessage2.content)
history.add_message(aimessage2)
由于RedisChatMessageHistory等保存历史记录方法不是runnable的子类,没有invoke方法,所以无法直接整合到chain中,需要引入RunnableWithMessageHistory,将chain和history整合为一个runnable,然后调用invoke。
from langchain_core.runnables.history import RunnableWithMessageHistory # RunnableWithMessageHistory是Runnable子类,具备invoke()功能,能够加入到chain中
from langchain_core.messages import SystemMessage
# 提示词模板
prompt_template = ChatPromptTemplate.from_messages([("user", "{text}")
])
history = RedisChatMessageHistory(session_id="test",redis_url="redis://localhost:6379/0")
# 结果解析器 StrOutputParser会AIMessage转换成为str,实际上就是获取AIMessage的content属性。
parser = StrOutputParser()
# 构建链
chain = prompt_template | llm | parser
runnable = RunnableWithMessageHistory(chain, # 整合get_session_history=lambda: history, # 匿名函数,返回history) # 返回的key
# 第⼀次聊天,清除历史聊天记录
history.clear()
# 每次聊天时,会⾃动带上Redis中的聊天记录。
runnable.invoke({"text":"你是谁"})
runnable.invoke({"text":"请重复⼀次"})
调用工具
常用Agent方式调用
### 用Agent来调用,常用!!!
import datetime
from langchain.tools import tool
from langchain.agents import initialize_agent, AgentType
# 定义⼯具 注意要添加注释
@tool(description="获取某个城市的天⽓")
def get_city_weather(city:str):"""获取某个城市的天⽓Args:city: 具体城市"""return "城市"+city+",今天天⽓不错"# 初始化代理
agent = initialize_agent(tools=[get_city_weather], # 使⽤装饰器定义的⼯具llm=llm,agent=AgentType.OPENAI_FUNCTIONS,verbose=True
)
其中AgentType的枚举值包括
AgentType
枚举值
AgentType
有多个枚举值,下面介绍常见的几种:
1. OPENAI_FUNCTIONS
-
描述:利用 OpenAI 函数调用 API 来决定是否调用工具以及调用哪些工具。适用于支持函数调用的 OpenAI 模型,如
gpt-3.5-turbo-0613
、gpt-4-0613
等。 -
使用场景:需要大模型根据输入智能选择工具时使用。
2. ZERO_SHOT_REACT_DESCRIPTION
-
描述:基于 ReAct 框架,使用工具的描述信息来决定何时调用工具。该智能体通过“推理(Reasoning)”和“行动(Action)”的循环,逐步解决问题。
-
使用场景:当有多个工具可供选择,且需要智能体根据工具描述进行决策时使用。
3. CONVERSATIONAL_REACT_DESCRIPTION
-
描述:同样基于 ReAct 框架,适用于对话场景。它能维护对话历史,根据历史信息和当前输入决定是否调用工具。
-
使用场景:构建多轮对话的智能体,需要考虑对话上下文时使用。
4. STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION
-
描述:与
ZERO_SHOT_REACT_DESCRIPTION
类似,但支持结构化的工具输入和输出,适用于工具参数较为复杂的场景。 -
使用场景:工具参数包含多个字段,需要结构化处理时使用。
5. CHAT_CONVERSATIONAL_REACT_DESCRIPTION
-
描述:基于对话的 ReAct 智能体,支持聊天格式的输入输出,能更好地处理对话场景。
-
使用场景:构建聊天机器人,需要自然流畅的对话交互时使用。
定义工具
方法一
用@tool定义描述,函数内需要按照格式写参数信息,简述功能
from langchain.tools import tool
# 定义⼯具 注意要添加注释
# @tool(description="写一本某种风格的小说")
@tool(description="写一本某种风格的小说")
def write_novels(Style:str):"""写一本某种风格的小说Args:Style: 小说的风格"""return "我正在写" + Style + "风格的小说"
方法二 用StructuredTool方法
用StructuredTool方法来定义函数工具的描述和命名等。
## 用StructuredTool方法来定义函数工具的描述和命名等
from langchain_core.tools import StructuredTool
def bad_weather_tool(city:str):"""获取某个城市的天⽓Args:city: 具体城市"""return "城市"+city+",今天天⽓不太好"
# 定义⼯具。这个⽅法中有更多参数可以定制
weatherTool = StructuredTool.from_function(func=bad_weather_tool,description="获取某个城市的天⽓",name="bad_weather_tool")
all_tools = {"bad_weather_tool": weatherTool}
llm_with_tools = llm.bind_tools([weatherTool])
# 把所有消息存到⼀起
query = "北京今天的天⽓怎么样?"
messages = [query]
# 第⼀次访问⼤模型返回的结果
ai_msg = llm_with_tools.invoke(messages)
messages.append(ai_msg)
print(ai_msg.tool_calls)
# 调⽤本地⼯具
if ai_msg.tool_calls:for tool_call in ai_msg.tool_calls:selected_tool = all_tools[tool_call["name"].lower()]tool_msg = selected_tool.invoke(tool_call)messages.append(tool_msg)
# 第⼆次返回的结果
llm_with_tools.invoke(messages).content
关于LLM如何找到对应的工具函数,我试了下多写几个工具,让大模型自己找需要的工具,发现LLM不只是参考描述信息,还会参照注释内的内容和参数信息(浅层理解,可能有问题),所以需要写好注释。
调用工具API
langchain支持的API工具见Tools | 🦜️🔗 LangChain,但由于国内使用麻烦,所以找了下国内的搜索工具,见博客langchain 接入国内搜索api——百度AI搜索_百度搜索api-CSDN博客