






















由于之前白嫖的DeepSeek的API过期了,导致博客的AI摘要功能也报废了,于是咬咬牙斥巨资花了10块买了DeepSeek的500w的tokens。
但后面看着躺在自己账号的500w的tokens,觉得这些钱不花心里难受啊,于是便四处寻找能将这钱花出去的办法,某天在Langchain的Data Loader中发现一个奇怪的东西,没错,就是这玩意:BiliBiliLoader
简单来说它可以抓取B站视频的转录文本,然后将其转化为Document对象,而有了Document对象我们就可以做很多事情了,比如可以将Document对象丢给大模型,让大模型帮我们总结B站的视频内容。既然轮子别人都已经造好,只需要组装一下自己的跑车自行车就可以让其跑起来了,于是记录一下这个折腾的过程,当然项目已开源。
实现的本质上是借助一种基于RAG(检索增强生成)思想优化大语言模型(LLM)输出的方法,让LLM能够实现检索到自搭建知识库的内容从而回答问题,增加LLM输出相关性以及减少大语言模型的幻觉(AI Hallucinations)问题,对于RAG感兴趣可以参考这篇文章:【RAG系统综述】|一文读懂RAG(检索增强生成)
前面提到通过RAG可以让LLM检索到自搭建知识库的内容,所以可以让LLM检索B站视频的内容,从而实现让LLM总结B站视频的效果,流程图大致如下:
要使用BiliBili Loader首先要获取三个参数:sessdata、bili_jct、buvid3
以Edge为例,首先需要登录B站账号,打开F12开发者工具,应用程序 -> 存储 -> Cookie,选择B站的Cookie,右侧展开的信息找依次找到对应的三个参数值(注意不要泄露):
获取之后写段测试代码看是否能正常获取视频内容,测试代码如下:
python
from langchain_community.document_loaders import BiliBiliLoader
SESSDATA =""
BUVID3 = ""
BILI_JCT = ""
bv_id = "BV1iGpueKE26"
loader = BiliBiliLoader(
[
f"https://www.bilibili.com/video/{bv_id}",
],
sessdata=SESSDATA,
bili_jct=BILI_JCT,
buvid3=BUVID3,
)
docs = loader.load()
print(docs[0].__dict__)小手一抖,内容到手~
由于B站的转录文档大多数没有固定的单词边界,所以使用RecursiveCharacterTextSplitter来对B站的转录文档进行切分,需要注意的是,如果使用RecursiveCharacterTextSplitter的方式来进行切分,则需要使用filter_complex_metadata对metadata进行二次过滤,否则后面的Retrieve会报错,样例代码如下:
python
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores.utils import filter_complex_metadata
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=20
)
# 分割 bilbili documents
#docs为上面获取的bilbili documents
documents = text_splitter.split_documents(docs)
#二次过滤
docs_sec = filter_complex_metadata(documents=documents)切分文档之后,如果有其他情况可以使用预训练的向量模型对文档进行向量化,使用的是bert-base-chinese模型,(目前暂时只需要对单个的B站的视频转录文档进行总结,我并没有进行这一步,这里仅列出可能会出现的其他情况)
下载地址:google-bert/bert-base-chinese · Hugging Face
国内的可使用这个链接:google-bert/bert-base-chinese · HF Mirror (hf-mirror.com)
然后使用本地加载的方式运行bert-base-chinese,样例代码如下:
python
from langchain_huggingface import HuggingFaceEmbeddings
embed_model = HuggingFaceEmbeddings(
#模型路径
model_name = "model_path",
#运行embeding模型的device,可选参数:['cuda','cpu',"mps", "npu"]
model_kwargs = {'device': 'cuda'},
encode_kwargs = {'normalize_embeddings': False}
)
from langchain.vectorstores import Chroma
#文档转化为向量值,并存储到chroma中
docsearch = Chroma.from_documents(docs_final, embed_model)Ps:也可以使用Api的形式调用向量模型,这里不过多展开
在进行最后总结之前,需要创建Prompt对LLM的输出做一定的提示,以及创建检索链,让LLM检索B站视频的内容进行最终的个人总结
Prompt示例如下:
python
default_prompt ="""请根据视频内容提炼并总结以下关键信息:
1. 视频的核心观点或论述(概述视频所传达的主要思想或立场),
2. 关键人物或事件(突出视频中涉及的重要角色或事件),
3. 视频的主线内容(梳理视频的整体逻辑和发展脉络,重点描述重要的情节或讨论的主题),
4.在总结的最后,提供你个人对视频内容的看法,包括你的评价、支持或质疑的原因,并简要解释为何持有此观点。
输出格式如下:
核心观点:
关键人物/事件:
主线内容:
个人看法:
"""最终的函数代码示例如下:
python
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
api_key = ""
api_base = ""
llm = ChatOpenAI(api_key=api_key,base_url = api_base,temperature=0, model_name="gpt-4")
#RAG文档抽取模板
RAG_TEMPLATE = """
You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.
<context>
{context}
</context>
Answer the following question:
{question}
"""
default_prompt ="""请根据视频内容提炼并总结以下关键信息:
1. 视频的核心观点或论述(概述视频所传达的主要思想或立场),
2. 关键人物或事件(突出视频中涉及的重要角色或事件),
3. 视频的主线内容(梳理视频的整体逻辑和发展脉络,重点描述重要的情节或讨论的主题),
4.在总结的最后,提供你个人对视频内容的看法,包括你的评价、支持或质疑的原因,并简要解释为何持有此观点。
输出格式如下:
核心观点:
关键人物/事件:
主线内容:
个人看法:
"""
def format_docs(docs):
# 格式化文档列表,将每个文档的页面内容连接成一个字符串
return "\n\n".join(doc.page_content for doc in docs)
# 加载提示模板
rag_prompt = ChatPromptTemplate.from_template(RAG_TEMPLATE)
# 创建检索链
rag_chain = (RunnablePassthrough.assign(context=lambda input: format_docs(input["context"]))
| rag_prompt
| llm
| StrOutputParser()
)
# 设置提问提示词
question_prompt = default_prompt
# 非流式处理
ans = rag_chain.invoke({"context": docs_final, "question": question_prompt})
print(ans)最终的运行效果大概是这样的:
启动项目
cmd
#启动FastAPI服务
uvicorn main:app --host 0.0.0.0 --port 3010
#启动Streamlit服务
streamlit run streamlit_ui/summary_ui.pyPs:两种运行方法都是独立的,看情况二选一即可
请求路径:/bilismmary
请求标头:application/json
请求方法:POST
请求参数:
| 参数 | 参数类型 | 说明 |
|---|---|---|
| bv_id | str | B站视频的bv号 |
| prompt | str | 喂给LLM的总结Prompt,不写则采用默认参数 |
| stream | bool | 是否开启流式输出,默认为False |
响应参数(非流式):
| 参数 | 参数类型 | 说明 |
|---|---|---|
| avid | int | 视频的avid |
| bvid | str | 视频的bvid |
| cid | int | 视频的分Pid |
| title | str | 视频的标题 |
| url | str | 视频的链接 |
| answer | str | 视频的总结内容 |
非流式请求示例:
json
{
"bv_id":"BV1Wd4YeZEqG"
}非流式响应示例:
json
{
"avid": 113115312168779,
"bvid": "BV1Wd4YeZEqG",
"cid": 25640244589,
"title": "国足1:2不敌沙特,天胡开局也能崩盘?",
"url": "https://www.bilibili.com/video/BV1Wd4YeZEqG",
"answer": "核心观点:\n视频主要讨论了中国足球队在与沙特队的比赛中,尽管开局有利,但最终以1:2不敌沙特,引发了关于教练伊万和中国足球未来的讨论。\n\n关键人物/事件:\n1. 伊万(教练)\n2. 中国队与沙特队的比赛\n3. 马图伊迪哈(法国世界杯冠军成员,观看比赛)\n\n主线内容:\n视频首先介绍了比赛前的氛围和球场的特点,随后详细描述了比赛的过程,包括中国队开局的有利情况和沙特队的红牌事件。接着,视频记录了比赛中的关键瞬间和球迷的反应,最终中国队被绝杀,赛后球场响起了要求教练下课的声音。\n\n个人看法:\n视频内容反映了足球比赛的不确定性和球迷的情感波动。尽管中国队开局有利,但最终失利,这凸显了足球比赛的不可预测性。个人认为,视频成功捕捉了比赛的紧张氛围和球迷的热情,但也暴露了中国足球在技术和战术上的不足。支持这一观点的原因是,视频中的比赛结果和球迷反应都指向了中国足球需要改进的方面。"
}流式请求示例:
json
{
"bv_id":"BV1kE4HesEUP",
"stream":true
}流式响应示例(部分):
打开项目streamlit地址:http://yourip:8502
界面如下:
左侧参数填写完整之后,右侧输入bv号,点击开始总结,即可看到效果
最终效果如下:
目前项目还比较糙,等后续有时间了继续写Bug优化
以上所展示的只是Langchain提供功能中的冰山一角,真正的潜力远不止于此。目前的这个项目只是抛砖引玉,算是个小小的开端。后续看看能否利用利用Langchain开发更多好玩的东西,还有就是这次的折腾仅仅只用掉了DeepSeek的20w的tokens,可恶啊
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。