使用 LangChain 和 Ollama 构建 YouTube 转载代理:分步指南
AI代理现在无处不在。新的实现、新的想法和无尽的可能性。但跟上这一切可能会让人感到不知所措,有时整个概念似乎比实际更复杂。在本指南中,我们将以简单、结构化的方式来解析这一切。到最后,您不仅会理解AI代理是什么,还会亲自构建一个。
什么是AI代理?
随着大语言模型(LLMs)的发展,新的应用进入了我们的生活。AI代理就是其中之一。LLMs本质上处于静态状态。它们无法根据所训练的数据做出响应,也无法执行当今的发展和一些特殊任务。AI代理是能够与其自身环境互动的系统。它们赋予LLMs访问工具的能力,从而使其能够满足超出LLM范围的所需请求。
AI代理由两个部分组成:
- AI模型(LLMs): 执行推理、规划和选择最佳行动。
- 工具: 这些是AI代理使用的特殊功能。它们的发展决定了模型能够执行的能力。
AI代理可以为您搜索互联网并带来最合适的结果,分析文档并通过API检索数据。您可以根据需要扩展这些使用领域。
AI代理是如何工作的?
你可以把AI代理看作智能助手。当你提出一个问题时,它首先分析你的问题,决定需要做什么,然后使用适当的工具进行处理并给出答案。
代理工作流程图(图片由作者提供)
有一个流程图展示了上述代理的工作方式。工作逻辑如下:
- 用户输入一个输入。这可以是一个问题或需要执行的动作。
- 如有必要,处理输入。(这取决于你将采取的动作。我在这里添加它是因为它在我的例子中)
- 如果代理需要使用工具,它会向该工具提供适当的输入并运行它。
- 在观察部分,接收来自工具的响应,并评估其是否为足够的响应。
- 如果响应不够充分,循环可以继续。
- 当最后的响应足够充分时,停止生成令牌并返回响应。
代理最重要的特征是,当动作完成时,它们可以停止生成新的令牌。
什么是工具?
工具是使代理能够采取行动的结构。实际上,如果我们简单地思考,工具就是函数。这些函数被提供给 LLM,LLM 预计通过选择正确的函数来完成所需的任务。
在准备工具时,使用 正确的结构 是非常重要的。工具应该清楚地反映你的目的。 不应有任何解释的余地。
工具(函数)应该有一个 文档字符串。如果你熟悉 Python,当你在使用不同库的函数时,将鼠标悬停在 IDE 中的一个函数上,会弹出一个窗口,写出该函数的目的、它接受的参数和返回的结果。这就是文档字符串,也应该出现在工具中。
工具可以有不同的形状并服务于不同的目的。以下是一些最常用工具的示例:
- Web 搜索
- 图像生成
- 使用外部 API
LangChain 中 AI 代理开发简介
到目前为止,我们已经讨论了代理是什么、它们是如何工作的以及工具的作用。当我们想将这些概念转化为实际应用时,LangChain 就派上用场了。
LangChain 是用于开发基于 LLM 的应用程序的框架之一。它支持代理结构并促进工具的使用。
我们可以用 LangChain 做什么?
使用 LangChain;
- 我们可以开发与 LLM 对话的代理。
- 我们可以开发与外部 API 交互的工具。
- 我们可以指定代理将使用哪个工具以及何时使用。
- 我们可以为复杂过程创建多重推理过程。
通过 LangChain,我们可以用非常少的代码将这些组件结合在一起,开发一个可工作的 AI 代理。特别是在进行实际应用时,我们将清楚地看到 LangChain 提供的灵活性和强大功能。
什么是 Ollama,为什么我们使用它?
Ollama 是一个可以本地运行并且便于管理 LLMs 的框架。 使用 Ollama,我们可以在自己的计算机上运行大型语言模型,而不是使用像 OpenAI API 这样的基于云的解决方案。这提供了隐私优势,并消除了外部 API 调用的费用。
如果我们来看一下优势:
- 通过本地运行模型来确保数据隐私
- Ollama 被优化为更快、更高效地运行 LLMs。
- 与 LangChain 直接兼容。
- 作为开源软件,我们可以使用和自定义不同的模型。
在这个项目中,我们使用Ollama + Llama3模型构建了一个强大的 AI Agent,以总结视频的文字记录并提取重要信息。
示例项目:YouTube 视频摘要 AI 代理
在之前的部分,我们讨论了什么是 AI 驱动的代理,它们是如何工作的,以及如何使用 LangChain 开发它们。现在是时候将我们所学的付诸实践了。在这一部分,我们将通过一个实际项目逐步开发一个 AI 代理。
我们将开发的项目是 YouTube 视频摘要 AI 代理。 该代理的目的是通过获取 YouTube 视频的转录文本进行基于文本的分析。用户可以通过向代理提问,快速获取摘要、主要观点和重要引用,而无需观看冗长的视频。因此,他们将能够节省时间,并高效地从内容中受益。
如果你准备好了,我们就开始吧。
所需安装
在本项目中,我们将使用 LangChain、Ollama 和 YouTubeTranscriptAPI 等库。如果这些库尚未安装在您的系统上,您可以通过运行以下命令进行安装:
pip install langchain langchain_ollama youtube-transcript-api
此外,由于我们将使用 Ollama 模型,您需要在计算机上安装 Ollama。如果您尚未安装 Ollama,可以按照以下步骤进行:
- 下载并安装 Ollama: https://ollama.com/
- 要将模型下载到您的系统,您可以在终端中运行以下命令:
I chose the llama3 model because it works more easily in my local, but you can also try different models.
从YouTube视频提取文本
我们的第一步是转录YouTube视频中的演讲。为此,我们将使用YouTubeTranscriptAPI库。该API获取YouTube的自动字幕,并以纯文本格式呈现给我们。
以下函数接受一个YouTube视频URL并提取视频的文本:
def parse\_url(url: str) -\> str:
"""
从URL中提取视频ID。
参数:
url(str): youtube视频url
返回:
YouTube视频的ID
"""
if "=" in url:
return url.split("=")\[-1\]
return url
def get\_text\_from\_video(url: str) -\> str:
"""
从YouTube视频中获取文本。
参数:
url(str): youtube视频url
返回:
YouTube视频的转录文本
"""
video\_id = parse\_url(url)
try:
transcript = YouTubeTranscriptApi.get\_transcript(video\_id)
transcript\_text = " ".join(\[entry\["text"\] for entry in transcript\])
transcript\_text = transcript\_text.replace("\\n", " ").replace("'", "")
return transcript\_text
except Exception as e:
return f"无法获取转录文本: {str(e)}"
- parse_url(url: str) -> str: 它接受YouTube URL并从中提取视频ID。
- get_text_from_video(url: str) -> str: 它使用parse_url函数获取视频ID。它使用YouTubeTranscriptAPI获取视频的转录,并将其返回为编辑后的字符串。
在这一步结束时,我们将拥有视频的转录文本。然而,由于长文本可能难以处理,我们将在下一步将文本拆分成较小的部分(块)。
将文本拆分为较小的部分 (Chunking)
在从YouTube视频获取转录文本后,我们需要将其拆分为小块(chunks),而不是直接处理。这是因为大型语言模型通常有一定的令牌限制。如果我们直接将文本输入模型,可能会超出模型的容量或失去上下文。
因此,通过将长文本拆分为更有意义的部分进行处理,不仅可以帮助我们获得更准确的答案,还可以使模型更高效地工作。在这里,我们将使用LangChain的RecursiveCharacterTextSplitter工具将文本拆分成不同的部分。
def create\_chunks(transcript\_text: str) -\> list:
"""
Split transcript text into processable chunks.
Args:
transcript\_text (str): Youtube video's transcripted text
Returns:
processable chunks
"""
text\_splitter = RecursiveCharacterTextSplitter(chunk\_size=1000, chunk\_overlap=100)
chunks = text\_splitter.split\_text(transcript\_text)
return chunks
RecursiveCharacterTextSplitter将文本拆分为1000个字符的部分。它保留100个字符的重叠,以便在部分之间保持语义完整性。给定的文本随后被拆分成片段并作为列表返回。
使用 AI 模型进行总结
现在我们已经从 YouTube 视频中获取了转录文本,并将其分解为更小、更有意义的部分,我们可以开始使用 LLM 对这些文本进行总结。在这一步中,我们将使用 LangChain 和 Ollama 为每个部分生成摘要,然后将所有摘要组合在一起以获得一个整体摘要。
def get\_summary(chunks: list) -\> str:
"""
Summarize text chunks and create a single summary.
Args:
chunks (list): processable chunks of transcriptted text
Returns:
A single summary for youtube video
"""
llm = OllamaLLM(model="llama3")
template = """Text: {text}
Goal: Summarize given text.
Answer: """
prompt = ChatPromptTemplate.from\_template(template)
chain = prompt | llm
summaries = \[chain.invoke({"text": chunk}) for chunk in chunks\]
combined\_summary = " ".join(summaries)
final\_summary\_prompt = ChatPromptTemplate.from\_template(
"Multiple summaries: {summaries}\\nGoal: Create a coherent single summary.\\nAnswer: "
)
final\_summary\_chain = final\_summary\_prompt | llm
final\_summary = final\_summary\_chain.invoke({"summaries": combined\_summary})
return final\_summary
上面的函数是我们将作为工具使用的函数之一。首先,让我们检查一下函数的结构。
- 函数接受的参数及其类型已给出。
- 函数返回的结果类型指定为**“-> str”。**
- 在函数开头的注释行中,我们首先写出函数的功能。
- 在 Args 部分,我们写出函数接受的参数、它们的类型和解释。
- 在 Return 部分,我们解释函数返回的结果。
我们将在每个函数中执行这些操作,因此可以将其视为通用结构。
当这些过程完成后,我们开始使用 Ollama 的“llama3”模型的大型语言模型。该模型具有语言理解、推理和总结的能力。
我们创建一个提示模板来总结每个文本片段。使用 ChatPromptTemplate 工具,我们将此提示调整为我们将使用的模型的结构。在 “chain = prompt | llm” 部分,我们将提示与模型连接。通过这种方式,每个部分将自动发送到模型进行总结。在逐个总结这些部分后,摘要将被组合。这个组合的摘要再次发送到模型以获得最终摘要,并且函数返回该摘要。
提取主要主题
在这个阶段,我们将专注于通过识别YouTube视频的转录文本中的主要主题来提取内容的基本构建块。通过这样做,我们将创建一个摘要,帮助我们快速理解视频的主要思想。
def extract\_topics(chunks:list) -\> list:
"""
Extract main topics from text chunks.
Args:
chunks (list): processable chunks of transcriptted text
Returns:
Main topic list
"""
llm = OllamaLLM(model="llama3")
template = """Text: {text}
Goal: Extract main topics from the given text.
Answer: List the key topics separated by commas."""
prompt = ChatPromptTemplate.from\_template(template)
chain = prompt | llm
topics\_list = \[chain.invoke({"text": chunk}) for chunk in chunks\]
all\_topics = set()
for topics in topics\_list:
topic\_items = \[t.strip() for t in topics.split(",")\]
all\_topics.update(topic\_items)
all\_topics = {topic for topic in all\_topics if topic}
return list(all\_topics)
首先,我们再次定义我们的模型并创建我们的示例模板。然后,与第一个函数一样,我们将文本块发送给模型,这次我们希望它分离主要主题。所选的主要主题作为列表存储,并消除重复数据。
提取重要引用
在这一步中,我们编写了一个函数,用于识别视频转录文本中最重要和引人注目的引用。我们的目标是直接从视频中识别出最有效的引用,而不是对视频内容进行总结。
def extract\_quotes(chunks:list) -\> list:
"""
Extract important quotes from text chunks.
Args:
chunks (list): processable chunks of transcriptted text
Returns:
important quotes list
"""
llm = OllamaLLM(model="llama3")
template = """Text: {text}
Goal: Extract the most important quote from this text.
Answer: Provide the quote as plain text."""
prompt = ChatPromptTemplate.from\_template(template)
chain = prompt | llm
quotes = \[chain.invoke({"text": chunk}) for chunk in chunks\]
unique\_quotes = \[\]
seen\_quotes = set()
for quote in quotes:
normalized = quote.strip().lower()
if normalized and normalized not in seen\_quotes:
unique\_quotes.append(quote.strip())
seen\_quotes.add(normalized)
return unique\_quotes
这个函数的工作结构实际上与之前的函数相同。我们定义模型并创建提示模板。然后我们使引用唯一。
创建代理结构
我们已经创建了所有工具。现在是将它们组合在一起的时候。为此,我们将使用以下函数。
def process_user_query(query, chunks, url):
"""根据用户查询选择合适的工具并生成响应。"""
llm = OllamaLLM(model="llama3")
def get_summary_wrapper(input_str=""):
return get_summary(chunks)
def extract_topics_wrapper(input_str=""):
return extract_topics(chunks)
def extract_quotes_wrapper(input_str=""):
return extract_quotes(chunks)
tools = [
Tool(
name="get_summary",
func=get_summary_wrapper,
description="提供逐字稿的详细摘要。"
),
Tool(
name="extract_topics",
func=extract_topics_wrapper,
description="从逐字稿中提取主要主题。"
),
Tool(
name="extract_quotes",
func=extract_quotes_wrapper,
description="从逐字稿中提取重要引用。"
)
]
agent = initialize_agent(
tools=tools,
llm=llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
response = agent.invoke(input=query, handle_parsing_errors=True)
return response
此函数的目的是根据用户的输入选择合适的工具,以实现所需的任务并达到结果。
在这个函数中,我们首先加载模型。然后我们创建以下包装函数。LangChain 工具机制不支持调用可以直接传递参数的函数。
def get_summary_wrapper(input_str=""):
return get_summary(chunks)
def extract_topics_wrapper(input_str=""):
return extract_topics(chunks)
def extract_quotes_wrapper(input_str=""):
return extract_quotes(chunks)
因此,如果你必须像这样使用函数 “get_summary(chunks)”,在将其传递给 Tool 对象时会出现错误。正因如此,我们将想要使用的函数放在一个包装函数中,并像这样发送给 Tool。
Tool(
name="extract_quotes",
func=get_summary(chunks),
description="从逐字稿中提取重要引用。"
)
我们已经学习了如何使用这个函数。现在是时候将 Tool 属性分配给这些函数了。为此,我们在 langchain.tools 中使用 Tool 对象。它的参数是:
- name: 工具的名称
- func: 工具调用的函数
- description: 工具的用途
我们对所有函数执行此操作,并将它们收集在一个列表中。
我们创建了工具,现在是时候创建代理了。为此,我们使用 initialize_agent。参数:
- tools: 使用的工具列表
- llm: 要使用的模型
- agent — AgentType.ZERO_SHOT_REACT_DESCRIPTION: Zero shot 意味着代理将直接回答,而无需预定义的示例。React description 意味着代理将分析用户查询并选择最佳工具
- verbose=True: 将显示更详细的操作输出(可选)
最后,用户查询被处理。
response = agent.invoke(input=query, handle_parsing_errors=True)
当上述代码运行时,用户提出的问题将首先发送给 AI 代理。代理将分析问题并选择最合适的工具。
- handle_parsing_errors=True: 确保函数在可能的错误情况下正常工作。
如果你完成了所有这些步骤,恭喜你,你已经创建了自己的 AI 代理。你可以使用以下代码运行它。
if __name__ == "__main__":
llm = OllamaLLM(model="llama3")
url = "youtube_url"
transcript_text = get_text_from_video(url)
chunks = create_chunks(transcript_text)
user_query = "你能给我这个视频的摘要吗?"
result = process_user_query(user_query, chunks, url)
print(f"响应: {result}")
你可以在 github repo 找到所有代码。
在这个项目中,我们开发了一个使用 LangChain 和 Ollama 分析 YouTube 视频的 AI 代理。首先,我们完成了从视频获取逐字稿的步骤,然后对文本进行分块、总结、提取主要主题和识别重要引用。最后,我们创建了一个动态系统,可以使用 AI 代理结构为用户的问题提供最合适的答案。
你可以进一步开发这个项目。可以通过视频获取更多信息,或者使用 YouTube API 添加接口。我想展示如何使用它以及如何创建自己的工具。
别忘了关注我、给我点赞并留下评论。如果你想查看我的其他文章: