langchain 入门指南 - 实现一个多模态 chatbot

news2024/9/17 7:31:56

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。

在前面的文章中,我们学会了如何通过 langchain 实现本地文档库的 QA,又或者通过 langchain 来实现对话式的问答系统。
在这篇文章中,我们将会学习如何通过 langchain 来实现一个多模态的 chatbot。

本文会构建一个有如下功能的 chatbot:

  • 可以生成图片
  • 可以回答用户的问题
  • 可以检索本地文档库中的信息
  • 可以从互联网进行搜索信息

什么是多模态

在前面的大部分例子中,我们跟 LLM 对话的时候都是使用了文本作为输入和输出。
但是除了文本,我们也可以让 LLM 来为我们生成图片。

多模态是指同时使用两种或两种以上的信息模式或表现形式。在人工智能和机器学习的背景下,
多模态通常指的是能够处理和融合不同类型数据的系统,这些数据可能包括文本、图像、音频、视频或其他传感器数据。

准备操作

  • 配置 OPENAI_API_KEYOPENAI_BASE_URL 环墋变量。
  • 配置 SERPER_API_KEY 环境变量,可以从 https://serper.dev 获取。

如和实现对本地文档的 QA

langchain 中,RetrievalQA 是一个结合了检索(Retrieval)和问答(QA)的组件。
它允许你构建一个系统,该系统能够根据用户的提问,从提供的文档或知识库中检索相关信息,并回答用户的问题。

RetrievalQA 的工作流程如下:

  • 检索(Retrieval):当用户提出一个问题时,RetrievalQA 会使用一个检索机制(本文会使用向量数据库做语义检索)
  • 阅读理解:一旦检索到相关的信息,RetrievalQA 会使用一个阅读理解模型来理解这些信息,并回答用户的问题。
  • 问答:最后,RetrievalQA 会使用一个问答模型(ChatModel)来生成最终的回答。

RetrievalQA 的优势在于它能够处理大量复杂的信息,并提供精确的答案。它特别适合那些需要从大量文档中检索信息的场景,例如法律文件、医学文献、技术手册等。

直接跟 LLM 对话的时候,一般都会有一个上下文大小限制的问题,太大的文档无法全部放入到上下文中。
但是可以先分片存入向量数据库中,在跟 LLM 对话之前,再从向量数据库中检索出相关的文档。最终发给 LLM 的数据只有相关的文档,这样就能够更好地回答用户的问题。

将 pdf 存入向量数据库

我们可以使用自己的 pdf 文档。

在这个例子中,我们将会使用 langchain 来将一个 pdf 文档存入向量数据库中:

from langchain_community.document_loaders import PyPDFLoader

# 加载 pdf 文档
loader = PyPDFLoader("Spotmax_intro_cn_2020.pdf")
docs = loader.load()

# 文档分片
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_spliter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=10)

splits = text_spliter.split_documents(docs)
persist_directory = 'data/'

from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
embedding = OpenAIEmbeddings()
# 创建向量数据库
vectordb = Chroma.from_documents(
    documents=splits,
    embedding=embedding,
    collection_name="spotmax",
    persist_directory=persist_directory,
)
# 持久化向量数据库
vectordb.persist()

说明:

  • PyPDFLoader 是一个用于加载 pdf 文档的类。
  • RecursiveCharacterTextSplitter 是一个用于将文档分片的类。
  • Chroma 是一个向量数据库类,用于存储和检索向量化的文档。
  • vectordbChroma 的一个实例,用于存储和检索文档。
  • vectordb.persist() 用于将向量数据库持久化到磁盘。

通过上面的代码,我们将会把 Spotmax_intro_cn_2020.pdf 文档存入到向量数据库中。

使用 RetrievalQA 进行问答

在上一步将 pdf 文档存入向量数据库之后,我们就可以通过 Chroma 的实例来对其做语义检索了。

def qa(question):
    from langchain_community.vectorstores import Chroma
    from langchain_openai import OpenAIEmbeddings
    embedding = OpenAIEmbeddings()
    vectordb = Chroma(persist_directory='data/', embedding_function=embedding, collection_name='spotmax')

    from langchain.chains.retrieval_qa.base import RetrievalQA
    from langchain_openai import ChatOpenAI

    llm = ChatOpenAI(
        model_name="gpt-3.5-turbo",
        temperature=0,
        max_tokens=200,
    )
    retriever = vectordb.as_retriever(
        search_type="mmr",
        search_kwargs={"k": 3}
    )
    qa0 = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever,
                                     return_source_documents=False, verbose=True)
    result = qa0({"query": question})
    return result['result']

print(qa("Spotmax 是什么?"))

说明:

  • vectordb 是从现有的 Chroma 向量数据库中加载的。
  • llm 是最终回答用户问题的大模型。
  • retriever 是用于检索文档的检索器,用户的问题会先通过检索器检索到相关的文档。
  • RetrievalQA.from_chain_type 创建一个 RetrievalQA 实例,用于回答用户的问题。
  • qa0({"query": question}) 用户的问题会先通过 retriever 检索到相关的文档,然后再交给 LLM,通过 llm 来回答用户的问题。

让 LLM 生成图片

这个比较简单,使用 OpenAIdall-e-2 模型即可:

def create_image(prompt):
    from openai import OpenAI
    client = OpenAI()
    response = client.images.generate(
        model='dall-e-2',
        prompt=prompt,
        size='256x256',
        quality='standard',
        n=1
    )
    u = response.data[0].url
    markdown_url = f"![image]({u})"
    return markdown_url

这个例子中,我们会根据用户的 prompt 生成一张 256x256 像素的图片,并且返回一个 markdown 链接形式的图片地址。

从互联网搜索信息

我们可以使用 GoogleSerperAPIWrapper 来从互联网搜索信息:

def query_web(question):
    """查询谷歌搜索结果"""
    from langchain_community.utilities import GoogleSerperAPIWrapper
    search = GoogleSerperAPIWrapper()
    return search.run(question)

如何让 chatbot 理解不同的操作?

我们可以使用 Agent 来让 chatbot 理解不同的操作:

  1. 将上面提供的几种操作封装成不同的 Tool
  2. 创建一个 AgentExecutor,根据用户的输入,选择合适的 Tool 来执行。
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
    model_name="gpt-4",
    temperature=0.7,
    max_tokens=1000,
)
from langchain.agents import Tool

tools = [
    Tool(
        name="Get current info",
        func=query_web,
        description="""only invoke it when you need to answer question about realtime info.
            And the input should be a search query."""
    ),
    Tool(
        name="query spotmax info",
        func=qa,
        description="""only invoke it when you need to get the info about spotmax/maxgroup/maxarch/maxchaos.
            And the input should be the question."""
    ),
    Tool(
        name="create an image",
        func=create_image,
        description="""invoke it when you need to create an image.
            And the input should be the description of the image."""
    )
]
from langchain.memory import ConversationBufferWindowMemory
from langchain.agents import ZeroShotAgent, AgentExecutor
from langchain.chains.llm import LLMChain

prefix = """Have a conversation with a human, answering the following questions as best you can. You have access to the following tools:"""
suffix = """Begin!"

{chat_history}
Question: {input}
{agent_scratchpad}"""

prompt = ZeroShotAgent.create_prompt(
    tools,
    prefix=prefix,
    suffix=suffix,
    input_variables=["input", "chat_history", "agent_scratchpad"],
)
memory = ConversationBufferWindowMemory(k=10, memory_key="chat_history")

llm_chain = LLMChain(llm=llm, prompt=prompt)
agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools)
agent_chain = AgentExecutor.from_agent_and_tools(
    agent=agent, tools=tools, verbose=True, memory=memory, handle_parsing_errors=True

说明:

  • 将前文提到的几种能力,封装为 AgentExecutor 可以使用的 Tool
  • 使用 llm 以及 tools 作为参数创建一个 AgentExecutor

AgentExecutor

在 LangChain 中,AgentExecutor 是一个组件,它负责执行一个代理(Agent)的推理循环。Agent 是一个更高级的组件,它可以根据输入动态选择和执行工具(Tools)。

Agent 通常用于构建更复杂的应用,其中 AI 模型需要根据上下文做出决策,选择合适的行动方案,并执行这些方案以达到某个目标。例如,一个 Agent 可能需要决定何时查询数据库,何时生成文本,或者何时调用外部 API。

AgentExecutor 的作用是作为一个执行环境,它接收用户的输入,然后根据 Agent 的策略或算法来指导 Agent 如何使用可用的工具来处理这个输入。代理会生成一个或多个动作(Actions),每个动作都对应一个工具的调用。

AgentExecutor 会执行这些动作,并可能根据动作的结果更新 Agent 的状态,然后返回最终的输出给用户。

如何跟 AgentExecutor 交互

直接使用 AgentExecutorinvoke 方法即可:

agent_chain.invoke(question)

调用 invoke 之后,AgentExecutor 会根据用户的输入,选择合适的 Tool 来执行,然根据 Tool 的输出进行下一步操作(调用其他 Tool 或者生成最终答案等)。

界面展示

我们最后可以使用 gradio 来构建一个简单的 web 界面:

import gradio as gr

with gr.Blocks() as demo:
    chatbot = gr.Chatbot(height=500) # 对话框
    msg = gr.Textbox(label="Prompt") # 输入框
    btn = gr.Button("Submit") # 按钮
    clear = gr.ClearButton(components=[msg, chatbot], value="Clear console") # 清除按钮

    btn.click(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])
    msg.submit(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])

gr.close_all()
demo.launch()

这个例子中,我们添加了一个 chatbot 组件,以及为用户提供了一个输入框和一个提交按钮。

inputsoutputs 参数用于指定输入和输出的组件。inputs 会作为参数传递给 respond 函数,respond 的返回值会被传递给 outputs 组件。

最终效果如下:

在这里插入图片描述

AgentExecutor 的处理过程如下(Thought -> Action -> Observation -> Thought -> Final Answer):

> Entering new AgentExecutor chain...
Thought: The question is asking for the current weather in Guangzhou and a male outfit recommendation. I can use the 'Get current info' tool to find the weather, and the 'create an image' tool to generate the outfit image.
Action: Get current info
Action Input: Guangzhou weather today
Observation: 94°F
Thought:The weather in Guangzhou is quite hot today. Now I need to think of an outfit that would be suitable for such warm weather.
Action: create an image
Action Input: A light summer outfit for men suitable for 94°F weather
Observation: ![image](https://oaidalleapiprodscus.blob.core.windows.net/private/org-GFz12lkhEotcvDvFYzePwrtK/user-1Ci7Ci1YNFjtlIO7AIY9aNux/img-zRsrd0cFFfxYAwW1oKZV9643.png?st=2024-07-24T05%3A29%3A33Z&se=2024-07-24T07%3A29%3A33Z&sp=r&sv=2023-11-03&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2024-07-23T23%3A15%3A19Z&ske=2024-07-24T23%3A15%3A19Z&sks=b&skv=2023-11-03&sig=g9L0m2GHy%2BHtC48NPVDBjZWVGfrXGQzRam6XayUZvJ0%3D)
Thought:I now have the final answer.
Final Answer: 广州今天的天气很热,达到了94°F。我为你创建了一张适合这种天气的男士夏季轻便穿搭图。请参考图片中的服装搭配。![image](https://oaidalleapiprodscus.blob.core.windows.net/private/org-GFz12lkhEotcvDvFYzePwrtK/user-1Ci7Ci1YNFjtlIO7AIY9aNux/img-zRsrd0cFFfxYAwW1oKZV9643.png?st=2024-07-24T05%3A29%3A33Z&se=2024-07-24T07%3A29%3A33Z&sp=r&sv=2023-11-03&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2024-07-23T23%3A15%3A19Z&ske=2024-07-24T23%3A15%3A19Z&sks=b&skv=2023-11-03&sig=g9L0m2GHy%2BHtC48NPVDBjZWVGfrXGQzRam6XayUZvJ0%3D)

> Finished chain.

我们可以看到在我提这个问题的时候,它做了如下操作:

  • 思考,然后发现需要获取今天广州的天气,这是 LLM 不懂的,所以使用了 Get current info 工具。
  • 获取到了天气信息之后,思考,然后发现需要生成一张图片,而我们有一个 create an image 工具,因此使用了这个工具来生成图片
  • 最终返回了今天广州的天气状况以及一张图片。

当然,我们也可以问它关于本地知识库的问题,比如 “什么是 spotmax?”(根据你自己的 pdf 提问,这里只是一个示例)

完整代码

最终完整的代码如下:

  • qa 函数用于回答用户关于本地知识库的问题
  • create_image 函数用于生成图片
  • query_web 函数用于从互联网搜索信息
  • respond 函数用于处理 chatbot 的对话响应
  • agent_chain 是一个 AgentExecutor 实例,用于执行 Agent 的推理循环
import gradio as gr

def qa(question):
    from langchain_community.vectorstores import Chroma
    from langchain_openai import OpenAIEmbeddings
    embedding = OpenAIEmbeddings()
    vectordb = Chroma(persist_directory='data1/', embedding_function=embedding, collection_name='spotmax')

    from langchain.chains.retrieval_qa.base import RetrievalQA
    from langchain_openai import ChatOpenAI

    llm = ChatOpenAI(
        model_name="gpt-3.5-turbo",
        temperature=0,
        max_tokens=200,
    )
    retriever = vectordb.as_retriever(
        search_type="mmr",
        search_kwargs={"k": 3}
    )
    qa0 = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever,
                                     return_source_documents=False, verbose=True)
    result = qa0({"query": question})
    return result['result']

def create_image(prompt):
    from openai import OpenAI
    client = OpenAI()
    response = client.images.generate(
        model='dall-e-2',
        prompt=prompt,
        size='256x256',
        quality='standard',
        n=1
    )
    u = response.data[0].url
    markdown_url = f"![image]({u})"
    return markdown_url

def query_web(question):
    """查询谷歌搜索结果"""
    from langchain_community.utilities import GoogleSerperAPIWrapper
    search = GoogleSerperAPIWrapper()
    return search.run(question)

def respond(message, chat_history):
    """对话函数"""
    bot_message = get_response(message)
    chat_history.append((message, bot_message))
    return "", chat_history

from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
    model_name="gpt-4",
    temperature=0.7,
    max_tokens=1000,
)
from langchain.agents import Tool

tools = [
    Tool(
        name="Get current info",
        func=query_web,
        description="""only invoke it when you need to answer question about realtime info.
            And the input should be a search query."""
    ),
    Tool(
        name="query spotmax info",
        func=qa,
        description="""only invoke it when you need to get the info about spotmax/maxgroup/maxarch/maxchaos.
            And the input should be the question."""
    ),
    Tool(
        name="create an image",
        func=create_image,
        description="""invoke it when you need to create an image.
            And the input should be the description of the image."""
    )
]
from langchain.memory import ConversationBufferWindowMemory
from langchain.agents import ZeroShotAgent, AgentExecutor
from langchain.chains.llm import LLMChain

prefix = """Have a conversation with a human, answering the following questions as best you can. You have access to the following tools:"""
suffix = """Begin!"

{chat_history}
Question: {input}
{agent_scratchpad}"""

prompt = ZeroShotAgent.create_prompt(
    tools,
    prefix=prefix,
    suffix=suffix,
    input_variables=["input", "chat_history", "agent_scratchpad"],
)
memory = ConversationBufferWindowMemory(k=10, memory_key="chat_history")

llm_chain = LLMChain(llm=llm, prompt=prompt)
agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools, verbose=True, handle_parsing_errors=True)
agent_chain = AgentExecutor.from_agent_and_tools(
    agent=agent, tools=tools, verbose=True, memory=memory, handle_parsing_errors=True
)

def get_response(message):
    res = agent_chain.invoke(message)
    return res['output']

with gr.Blocks() as demo:
    chatbot = gr.Chatbot(height=500) # 对话框
    msg = gr.Textbox(label="Prompt") # 输入框
    btn = gr.Button("Submit") # 按钮
    clear = gr.ClearButton(components=[msg, chatbot], value="Clear console") # 清除按钮

    btn.click(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])
    msg.submit(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])

gr.close_all()
demo.launch()

总结

虽然 OpenAI 提供了 function calling 的特性,但是直接使用起来还是比较麻烦,通过 AgentExecutor 结合 tools 的方式,可以更好地组织和管理 chatbot 的能力。

在这篇文章中,我们学习了如何通过 langchain 来实现一个多模态的 chatbot,它可以生成图片、回答用户的问题、检索本地文档库中的信息、从互联网搜索信息等。

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

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

相关文章

2024下半年,前端的技术风口来了

“ 你近期有体验过哪些大模型产品呢? 你有使用大模型API做过一些实际开发吗? 在你日常开发中,可以与大模型相关应用结合来完成工作吗? ” **最近,一直在和同事聊,关于前端可以用大模型干点啥&#xff…

Vue2和Vue3实战代码中的小差异(实时更新)

目录 前言1. 未使用自闭合标签2. 事件名连字符3. 换行符4. 弃用.sync5. 弃用slot 前言 以下文章实时更新&#xff0c;主打记录差异 1. 未使用自闭合标签 104:7 error Require self-closing on Vue.js custom components (<el-table-column>) vue/html-self-closing✖…

【华为OD机考】2024D卷最全真题【完全原创题解 | 详细考点分类 | 不断更新题目】

可上 欧弟OJ系统 练习华子OD、大厂真题 绿色聊天软件戳 od1441了解算法冲刺训练&#xff08;备注【CSDN】否则不通过&#xff09; 文章目录 相关推荐阅读栈常规栈单调栈 队列&#xff08;题目极少&#xff0c;几乎不考&#xff09;哈希哈希集合哈希表 前缀和双指针同向双指针 贪…

我与C语言二周目邂逅vlog——6.文件操作

1. 为什么使⽤⽂件&#xff1f; 如果没有⽂件&#xff0c;我们写的程序的数据是存储在电脑的内存中&#xff0c;如果程序退出&#xff0c;内存回收&#xff0c;数据就丢失 了&#xff0c;等再次运⾏程序&#xff0c;是看不到上次程序的数据的&#xff0c;如果要将数据进⾏持久…

从区块链到股票市场的全方位布局,广辉团队创新引领共创财富未来!

广辉团队作为一家涉足互联网投资领域的团队&#xff0c;在短短几年内迅速崛起&#xff0c;成为行业中的佼佼者。这支团队汇聚了来自各行各业的商业精英&#xff0c;并在互联网金融领域创造了巨大的财富。业务范畴涵盖了资产管理、资本市场、消费金融、保险市场、零售银行及财富…

SSM项目实战

项目实战一 这里实战的是我Javaweb项目实战&#xff08;后端篇&#xff09;的改写 Javaweb项目实战用到的技术是servletvue3 这里用到的是springspringmvcmybatisvue3 项目结构 步骤一:导入需要依赖 <!--mybatis核心--><dependency><groupId>org.mybatis<…

Intel12代处理器在虚拟机中安装Windows98SE

最近想把以前写的那个Windows98开始菜单完善一下&#xff0c;装个Windows98来参考参考。 项目地址&#xff1a;GitHub - zhaotianff/WindowsX: windows toolsets 路过的小伙伴可以帮忙点个star。 这里把安装过程分享一下。 本文以VMware17虚拟机为例&#xff0c;介绍如何在1…

阿里玄铁处理器涉及的相关技术居然有PHP

其实跟PHP没啥关系&#xff0c;也可以说有点关系 指令集说明&#xff1a; RISC-V 指令集是由美国加州大学伯克利分校&#xff08;University of California, Berkeley&#xff09;的研究人员开发的。该项目主要由Krste Asanović教授领导&#xff0c;并且得到了计算机体系结构…

Java面试八股之JDK 动态代理和 CGLIB 动态代理的区别

JDK 动态代理和 CGLIB 动态代理的区别 JDK 动态代理和 CGLIB 动态代理都是在 Java 中实现动态代理的两种常见方式。它们各自有不同的特点和适用场景。下面详细介绍一下这两种动态代理的区别&#xff1a; 1. 代理机制 JDK 动态代理: 实现原理: JDK 动态代理基于 Java 的反射…

微信小程序开发中如何通过正确的步骤和调试方法来解决问题

个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119@qq.com] 📱个人微信:15279484656 🌐个人导航网站:www.forff.top 💡座右铭:总有人要赢。为什么不能是我呢? 专栏导…

Docker容器基础篇

一.Docker容器简要介绍 Docker 是一个开源项目&#xff0c;旨在提供轻量级的应用容器化解决方案。它允许开发者打包应用及其所有依赖项到一个标准化的单元中&#xff0c;称为容器。这些容器可以在开发人员的工作环境中构建&#xff0c;然后轻松地在不同的计算机、服务器或云平…

论文阅读:面向自动驾驶场景的多目标点云检测算法

论文地址:面向自动驾驶场景的多目标点云检测算法 概要 点云在自动驾驶系统中的三维目标检测是关键技术之一。目前主流的基于体素的无锚框检测算法通常采用复杂的二阶段修正模块,虽然在算法性能上有所提升,但往往伴随着较大的延迟。单阶段无锚框点云检测算法简化了检测流程,…

Linux安装vmware tools(vmware tools软件包来源ESXI8.0.3)

一、默认正常安装(也可以下载文章顶部资源上传linux服务器解压安装&#xff0c;免去挂载光驱的步骤) ##挂载cdrom到/mnt目录 [rootlocalhost /]# mount /dev/cdrom /mnt mount: /dev/sr0 is write-protected, mounting read-only [rootlocalhost /]# ##切换至/mnt目录 [rootlo…

CTF竞赛介绍以及刷题网址(非常详细)零基础入门到精通,收藏这一篇就够了

前言 CTF&#xff08;Capture The Flag&#xff09;中文一般译作夺旗赛&#xff0c;在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会&#xff0c;以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。发展至今…

数据结构【有头双向链表】

目录 实现双向链表 双向链表数据 创建双向链表 初始化双向链表创建&#xff08;哨兵位&#xff09; 尾插 打印双向链表 头插 布尔类型 尾删 头删 查询 指定位置后插入 指定位置删除数据 销毁 顺序表和链表的分析 代码 list.h list.c test.c 注意&#xff1a…

用Python打造精彩动画与视频,2.2 使用Jupyter Notebook进行编程

2.2 使用Jupyter Notebook进行编程 Jupyter Notebook是一款广泛应用于数据科学、教学和研究的开源工具。它提供了一个交互式的编程环境&#xff0c;支持代码、文本、公式和可视化内容的集成显示&#xff0c;非常适合Python编程尤其是数据分析与可视化任务。 1. 什么是Jupyter…

2026考研数学武忠祥课程视频百度网盘资源+PDF讲义(永久更新)

虽然每年大家推荐的最多的是张宇和汤家凤&#xff0c;但是我强烈推荐武忠祥老师&#xff01; 2026考研数学武忠祥课程领取&#xff1a;2026武忠祥课程&#xff08;考研数学全程&#xff09;基础强化 武忠祥老师真宝藏老师&#xff0c;他讲课不像张宇老师那样段子频出&#xf…

模拟实现c++中的string

c内置string库的相关函数&#xff1a;string - C Reference 目录 一string类构造&#xff0c;拷贝构造和析构&#xff1a; 二string内正向迭代器实现&#xff1a; 三赋值运算符重载实现&#xff1a; 四reserve&#xff0c;empty&#xff0c;clear实现&#xff1a; 五push_b…

PHP与SEO,应用curl库获取百度下拉关键词案例!

编程语言从来都是工具&#xff0c;编程逻辑思维才是最重要的&#xff0c;在限定的规则内&#xff0c;实现自己的想法&#xff0c;正如人生一样&#xff01; 不管是python还是php只要掌握了基础语法规则&#xff0c;明确了实现过程&#xff0c;都能达到想要实现的结果&#xff0…

FFmpeg实战 - 解复用解码

文章目录 前置知识音视频基础概念解复用、解码的流程分析FFMPEG有8个常用库 常见音视频格式的介绍aac格式介绍h264格式介绍flv格式介绍mp4格式介绍 FFmpeg解码解封装实战数据包和数据帧&#xff08;AVPacket/AVFrame&#xff09;AVPacket/AVFrame的引用计数问题API介绍注意事项…