Langchain学习笔记(1)——如何调用Huggingface的模型并实现实时返回生成结果

Langchain支持很方便的OpenAI模型的调用,可以做到快速开发大模型应用。但是要使用Huggingface上的开源模型就没有那么方便了,本文就详细阐述如何用Langchain开发基于Huggingface上的模型,并实时返回生成结果。

实时返回生成结果是LLM很关键的一环,大模型的响应速度很大程度上会影响用户的使用体验,较长的等待时间会导致用户流失。同时,Langchain可以很方便的调用openAI的接口,但是对于我们这种穷屌丝来说用不起GPT的接口,只能用huggingface上的开源模型。所以本文将详细介绍如何使用Langchain调用Huggingface的模型并做到实时返回生成结果。

本文选用Qwen2.5-0.5B-Instruct作为部署的模型,同时我是下载到了本地,所以代码中的路径是本地路径,如果不想下载到本地的话直接用Huggingface上的路径即可。

1. Quick start

如果使用OpenAI的模型,要实现实施返回结果(即流式调用),只需要以下几行代码就可以快速实现:

from langchain_openai import ChatOpenAImodel = ChatOpenAI(model='gpt-4')
chunks = []
for chunk in model.stream('天空是什么颜色?'):chunks.append(chunk)print(chunk.content, end='|', flush=True)

但是鉴于我们是穷批,用不起GPT的API,所以我们只能借助transformers库自己实现上述的功能。

首先加载模型及其 tokenizer:

from transformers import Qwen2Tokenizer, Qwen2ForCausalLMtokenizer = Qwen2Tokenizer.from_pretrained(r'D:\huggingface\Qwen2.5-0.5B-Instruct')
model = Qwen2ForCausalLM.from_pretrained(r'D:\huggingface\Qwen2.5-0.5B-Instruct')

接着我们采用以下代码即可实现流式迭代返回生成结果:

import threading
from transformers import TextIteratorStreamer
from langchain_core.messages import AIMessageChunkdef hf_stream(prompt: str):inputs = tokenizer(prompt, return_tensors='pt')# 创建一个 “流式文本迭代器”, 每当模型生成一个新 token,就会立刻把它变成字符串,通过 streamer 吐出来(yield)streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)generation_kwargs = dict(**inputs,streamer=streamer,max_new_tokens=100,do_sample=True,temperature=0.95,)# 启动模型的文本生成过程,但用新线程异步执行,让主线程能实时处理输出# 因为 model.generate() 是 阻塞函数(会等生成完才返回),我们要“边生成边取结果”,所以不能直接运行它,而是让它在后台线程里跑thread = threading.Thread(target=model.generate, kwargs=generation_kwargs)thread.start()# 流式迭代返回的 token 段for new_text in streamer:yield AIMessageChunk(content=new_text)

上述代码中需要注意的有几个地方:streamer 是一个关键点,这里是将 tokenizer 放入到流式处理中,这是因为 model.generate 是个同步操作,无法执行异步调用,所以采用多线程的方式将 model.generate 放入到多线程中,这样就可以多线程执行模型的生成。同时,由于 generate 的结果是 token id 而非文字,这里 tokenzier 会将 token id decode 成为文字,并用 yield 流式输出,所以 streamer 是将 tokenizer 放入到的流中。

接着就可以直接查看模型的输出结果:

for chunk in hf_stream('请为我介绍梦幻西游的天宫门派'):print(chunk.content, end='|')

由于用了end='|',所以在每个输出字符后都会看到"|",以下是运行结果:
生成1

2. 采用链式调用的方式执行代码

Langchain 一个核心功能就是链式调用,即我们可以用诸如:

chain = prompt | llm | parser

的方式,同时执行提示模板、大模型生成、结构化输出的功能。当然,由于穷批用不起API,所以我们这里依旧自己打造 Langchain 调用 huggingface 的链式调用。

首先我们定义如下的类:

from langchain_core.runnables import Runnable
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParserclass HuggingFaceStreamWrapper(Runnable):def __init__(self, model, tokenizer, max_new_tokens=128):self.model = modelself.tokenizer = tokenizerself.max_new_tokens = max_new_tokensdef invoke(self, input_text, config=None) -> AIMessage:# Runnable 类要求强制实现抽象方法, 否则会报错 TypeError: Can't instantiate abstract class with abstract method invokehuman_texts = [msg.content for msg in input_text.messages if isinstance(msg, HumanMessage)]prompt = "\n".join(human_texts)inputs = self.tokenizer(prompt, return_tensors='pt').to(self.model.device)outputs = self.model.generate(**inputs,max_new_tokens=self.max_new_tokens,do_sample=True,top_p=0.95,temperature=0.8,)decoded = self.tokenizer.decode(outputs[0], skip_special_tokens=True)return AIMessage(content=decoded)async def astream(self, input_text, config=None):human_texts = [msg.content for msg in input_text.messages if isinstance(msg, HumanMessage)]prompt = "\n".join(human_texts)# tokenizer encodeinputs = self.tokenizer(prompt, return_tensors='pt').to(self.model.device)# streamerstreamer = TextIteratorStreamer(self.tokenizer, skip_prompt=True, skip_special_tokens=True)# 生成参数generation_kwargs = dict(**inputs,streamer=streamer,max_new_tokens=self.max_new_tokens,do_sample=True,top_p=0.95,temperature=0.8,)# 用线程调用生成(阻塞生成转异步)thread = threading.Thread(target=self.model.generate, kwargs=generation_kwargs)thread.start()for token in streamer:# 手动触发事件循环调度,允许其他任务执行。# 它的作用不是延时,而是调度让步await asyncio.sleep(0)  # 允许事件循环切换# yield 是 Python 中的一个“进阶但极其实用”的关键字,它的作用是让函数变成一个生成器(generator),# 实现“边计算边返回”的效果,非常适合处理大数据、流式生成、异步 LLM 响应等场景。# yield 会暂停函数执行,返回一个值,但不会结束函数;yield AIMessageChunk(content=token)

这个是继承了 langchain Runable 类,用于我们自定义开发 langchain 的链式调用。在这里有几个要注意的点:Runable 中有个方法,名为 invoke,这个函数是必须要实现的函数,就算是

def invoke(self):pass

都没问题,但是如果没有该函数,那么会报错:

TypeError: Can't instantiate abstract class with abstract method invoke

说明 invoke 这个函数是必须实现的。

而调用 invoke 函数也能够返回结果,不过 invoke 是同步执行的,模型会一次性返回所有的结果,而非一个字一个字的蹦出来,比如执行下面代码:

hf_model = HuggingFaceStreamWrapper(model, tokenizer)# 构建链
prompt = ChatPromptTemplate.from_template('请给我介绍梦幻西游中{topic}门派')
parser = StrOutputParser()
chain = prompt | hf_model | parser# 如果调用 invoke 方法, 必须实现 invoke 函数, 否则可以 pass
outputs = chain.invoke({'topic': '九黎城'})print(outputs)

会获得:
invoke
就是这样一次性输出出来(前面我删了三百多帧),这种用户体验感就会很差。

在上述代码中,我们可以看到,采用 ChatPromptTemplate.from_template('请给我介绍梦幻西游中{topic}门派') 可以生成一个提示语,之后我们在调用的时候,传入 topic 参数,就能够直接将 九黎城 传入进去,这样在做实际开发的时候,就能够给定一个提示模板,由用户自行填充内容。

那么说完了 invoke 调用,链式调用流式输出其实就很简单了,只需要用如下代码即可实现:

hf_model = HuggingFaceStreamWrapper(model, tokenizer)# 构建链
prompt = ChatPromptTemplate.from_template('请给我介绍梦幻西游中{topic}门派')
parser = StrOutputParser()
chain = prompt | hf_model | parserasync def async_stream():# 这里同样, 如果要调用 astream 方法, 必须实现 astream 函数, 否则可以 passasync for chunk in chain.astream({'topic': '九黎城'}):print(chunk, end='|', flush=True)asyncio.run(async_stream())

以下是结果:
生成2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/909714.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/909714.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java安全-常规漏洞问题(SQL注入,XXE,SSRF,RCE)

靶场搭建 靶场下载 : https://github.com/whgojp/JavaSecLab这个靶场是使用Springboot搭建的所以不要下载 jar 文件运行,要使用IDEA运行他的文件夹 先打开pom 然后进行maven一下 改一下端口 配置完成之后修改一下 运行的模式 使用phpstudy搞一个sql数…

基于视频的 AI 内存库,极速语义检索

简介 在大模型应用里,将文本数据分块嵌入存储在向量数据库已经是标准做法。然而,传统向量数据库虽然功能强大,但其高昂的RAM和存储需求,以及复杂的部署运维,常常让开发者望而却步。今天,介绍一个名为 Memv…

接口适配器模式实现令牌桶算法和漏桶算法

以下是令牌桶算法、漏桶算法和雪花算法的清晰对比解析。它们属于完全不同的技术领域,前两者用于流量控制,后者用于分布式ID生成: 1. 令牌桶算法(Token Bucket) 领域:流量整形 / 速率限制核心目标&#xff…

618背后的电商逻辑重构:从价格血战到价值共生

“今年终于没做数学题。” 618进行到一半,行云已经买了很多,大件的有iPad、iWatch,小件的有运动鞋、面膜、纸巾。往年她要凑凑减减,经常要找个店铺凑单,下完单再马上退掉,今年她没废太多脑细胞&#xff0c…

解决 PyTorch 与 Python 3.12 的兼容性问题:`operator torchvision::nms does not exist` 深度解析

解决 PyTorch 与 Python 3.12 的兼容性问题 问题现象错误根源分析终极解决方案🚀 推荐方案:创建 Python 3.11 虚拟环境⚡ 备选方案:使用 PyTorch 夜间构建版(Python 3.12)验证修复技术深度解析最佳实践建议问题现象 当在 Python 3.12 环境中运行以下代码时: from tran…

Git 实战场景

四、标签管理 4.1、标签的理解 在使用 Git 进行版本管理时,**标签(Tag)**扮演着非常重要的角色。它其实就是对某次提交(commit)的一个简洁标识,相当于给这次提交起了一个可读、易记的“别名”。比如&…

在同态加密系统中,参与角色以及各角色的功能作用流程图,私钥和公钥分发流程,可能遇到的攻击

一、角色划分与职责 角色身份核心任务密钥权限客户端数据所有者 (如医院、用户)1. 加密原始数据 2. 上传密文至服务器 3. 接收并解密结果(可选)持有公钥服务器计算服务提供方 (如云平台)1. 接收客户端密文…

langchain从入门到精通(六)——LCEL 表达式与 Runnable 可运行协议

1. 多组件 invoke 嵌套的缺点 prompt ChatPromptTemplate.from_template("{query}") llm ChatOpenAI(model"gpt-3.5-turbo-16k") parser StrOutputParser() # 获取输出内容 content parser.invoke( llm.invoke( prompt.invoke( {"query": r…

ArcGIS中批量获取输入面图层A中各要素的四至点的实现方法

一、背景及意义 在日常工作中,我们经常会需要获取面图层的四至点,我们能否在ArcGIS中直接获取面图层的四至点呢?答案是肯定的,请继续往下看。 二、大体思路 使用字段计算器计算输入面图层A中各面要素的XY的最大值和最小值&…

大IPD之——华为的战略本质与实践(二)

华为战略执行的能力如此强,有两个核心原因:一是管理体系起了非常重大的作用;二是企业文化导致华为的执行力特别强。华为在战略方面,为什么每次都能转型成功?背后是有很多实质性的内容支撑的。而华为如何做战略&#xf…

『大模型笔记』第3篇:多长的 Prompt 会阻塞其他请求?优化策略解析

『大模型笔记』多长的 Prompt 会阻塞其他请求?优化策略解析 文章目录 一、更简单的问题:长 Prompt 阻塞请求队列1. 请求并行预填方案(Request-Parallel Prefills)二、根本的问题(Fundamental Flaw):Token 生成被并行预填拖慢1. 解耦预填(Disaggregated Prefill):以延迟优…

21 - GAM模块

论文《Global Attention Mechanism: Retain Information to Enhance Channel-Spatial Interactions》 1、作用 这篇论文提出了全局注意力机制(Global Attention Mechanism, GAM),旨在通过保留通道和空间方面的信息来增强跨维度交互&#xf…

Java01--使用IDEA编写运行第一个Java程序HelloWorld

一.先新建一个文件夹存放项目(后续可以推送到Gitee) 二.创建项目 1.打开IDEA,点击首页的新建项目 2.新建空项目并命名,存放路径为步骤一创建的文件夹: 3.在新项目中新建一个src文件夹(用于集中管理文件) 4.在src文件夹…

目标检测相关【清晰易懂】

目标检测相关 (b)是语义分割,(c)是实例分割 目标检测 每个目标一个框标签 实例分割 语义分割 识别每一个目标个体 目标检测基础上进一步提升模型能力有两个方向:实例分割、旋转目标检测。 实例分割 …

强化学习 A2C算法

3.actor-critic方法 3.1 Reinforce 算法,也称为蒙特卡洛策略梯度。蒙特卡洛方差 第一节介绍了DQN 在上一节基于策略的方法中,我们的目标是直接优化策略,而无需使用价值函数。更准确地说,Reinforce 是 基于策略的方法 的一个子类…

关于MCU、MPU、SoC、DSP四大类型芯片

目录 MCU、MPU、SoC、DSP四大类型芯片分析 一、MCU 1、概念 2、特点 3、常见芯片 4、应用场景 二、MPU 1、概念 2、特点 3、常见芯片 4、应用场景 三、SoC 1、概念 2、特点 3、常见芯片 4、应用场景 四、DSP 1、概念 2、特点 3、常见芯片 4、应用场景 MCU、…

【数据结构】图论最短路圣器:Floyd算法如何用双矩阵征服负权图?

最短路径 穿越负权迷雾:Floyd算法如何解锁全图最短路径?​​一、Floyd算法1.1 算法思想1.2 算法逻辑1.3 算法评价1.4 算法限制 二、三种算法对比🌟结语 穿越负权迷雾:Floyd算法如何解锁全图最短路径?​​ 大家好&…

宝塔面板集成阿里云 OSS 备份失败的解决方案

宝塔面板集成阿里云OSS备份失败的解决方案 一、问题背景 在使用宝塔面板配置阿里云OSS云存储备份功能时,用户遇到如下错误: Traceback (most recent call last):File "class/CloudStoraUpload.py", line 144, in __init__from alioss_main import OSSClient as ocFile "…

如何安全高效地维护CMS智能插件?

作为网站开发者或运维人员,你是否经历过这样的场景:满怀期待地点击了插件“更新”按钮,刷新页面后却看到一片刺眼的500错误?或发现网站加载速度从2秒骤降到10秒?智能插件为CMS系统(如WordPress、Drupal、亿…

FastAPI如何用角色权限让Web应用安全又灵活?

title: FastAPI如何用角色权限让Web应用安全又灵活? date: 2025/06/13 05:46:55 updated: 2025/06/13 05:46:55 author: cmdragon excerpt: 基于角色的路由访问控制是Web应用中常见的安全控制模式,通过为用户分配特定角色来管理权限。FastAPI利用依赖注入系统实现权限控制…