如何使用 OpenAI 将公司文档转换为可搜索的数据库

如何使用 OpenAI 将公司文档转换为可搜索的数据库

img

图片由 Unsplash 提供。

在过去的六个月里,我一直在 A 轮创业公司 Voxel51 工作,该公司是开源计算机视觉工具包 FiftyOne的创建者。作为一名机器学习工程师和开发人员布道者,我的工作是倾听我们的开源社区并为他们提供他们所需要的东西 — 新功能、集成、教程、研讨会等等。

几周前,我们为 FiftyOne 添加了对矢量搜索引擎和文本相似性查询的原生支持,以便用户可以通过简单的自然语言查询在其(通常是海量的——包含数百万或数千万个样本的)数据集中找到最相关的图像。

这让我们陷入了一个奇怪的境地:现在人们可以使用开源 FiftyOne 轻松地用自然语言查询搜索数据集,但使用我们的文档仍然需要传统的关键字搜索。

我们有大量文档,这有其优点和缺点。作为一名用户,我有时会发现,鉴于文档数量之多,要找到我想要的确切内容需要比我想象的更多的时间。

我不想让它飞走……所以我利用业余时间建造了这个:
在这里插入图片描述

从命令行语义搜索贵公司的文档。图片由作者提供。

因此,下面是我把我们的文档转换成语义可搜索的矢量数据库的方法:

您可以在voxel51/fiftyone-docs-search repo中找到此帖子的所有代码,并且可以使用 轻松地在编辑模式下在本地安装该包pip install -e .

更好的是,如果您想使用此方法为自己的网站实现语义搜索,您可以继续!以下是您需要的成分:

  • 安装openai *Python 包并创建一个帐户:*您将使用此帐户将您的文档和查询发送到推理端点,该端点将为每段文本返回一个嵌入向量。
  • 安装qdrant-client Python 包并通过 Docker 启动 Qdrant 服务器:您将使用Qdrant为文档创建本地托管的向量索引,并针对该索引运行查询。Qdrant 服务将在 Docker 容器内运行。

将文档转换为统一格式

我公司的文档全部以 HTML 文档形式托管在https://docs.voxel51.com上。一个自然的起点是使用 Python 的请求库下载这些文档,然后使用Beautiful Soup解析文档。

然而,作为一名开发人员(也是我们许多文档的作者),我认为我可以做得更好。我已经在本地计算机上有一个 GitHub 存储库的工作克隆,其中包含用于生成 HTML 文档的所有原始文件。我们的一些文档是用Sphinx ReStructured Text (RST)编写的,而其他文档(如教程)则是从 Jupyter 笔记本转换为 HTML。

我(错误地)认为我越接近 RST 和 Jupyter 文件的原始文本,事情就会越简单。

恢复时间

=在 RST 文档中,各部分由仅包含、-或字符串的行来划分_。例如,以下是来自 FiftyOne 用户指南的文档,其中包含所有三个划分符:

img

来自开源 FiftyOne Docs 的 RST 文档。图片由作者提供。

然后,我可以删除所有 RST 关键字,例如toctree、、code-blockbutton_link(还有更多),以及伴随:关键字、新块的开始或块描述符的、、和。::``..

链接也很容易维护:

no_links_section = re.sub( r"<[^>]+>_?" , "" , section)

当我想从 RST 文件中提取节锚点时,事情开始变得棘手。我们的许多节都有明确指定的锚点,而其他节则留待转换为 HTML 时推断。

以下是一个例子:

.. _brain-embeddings-visualization:

可视化嵌入
______________________ 

FiftyOne Brain 提供了强大的
:meth:`compute_visualization() <fiftyone.brain.compute_visualization>` 方法,您可以使用该方法生成数据集中样本和/或单个对象
的低维表示。这些表示可以在应用程序的:ref:`嵌入面板 <app-embeddings-panel>` 中原生可视化,您可以在其中以交互方式选择兴趣点并在 :ref:`样本面板 <app-samples-panel>` 中查看相应的样本/感兴趣的标签,反之亦然。.. image:: /images/brain/brain-mnist.png    :alt: mnist    :align: center嵌入可视化有两个主要组成部分:用于生成嵌入的方法,以及用于计算嵌入低维表示的降维方法。嵌入方法----------------- :meth:`compute_visualization() <fiftyone.brain.compute_visualization>`的 `embeddings` 和 `model` 参数支持多种为您的数据生成嵌入的方式:

在我们的用户指南文档中的 brain.rst 文件中(其中一部分已在上面重现),可视化嵌入部分有一个#brain-embeddings-visualization由 指定的锚点.. _brain-embeddings-visualization:。但是,紧随其后的嵌入方法子部分被赋予了一个自动生成的锚点。

另一个很快浮现出来的困难是如何处理 RST 中的表格。列表表相当简单。例如,这是我们的 View Stages 备忘单中的一个列表表:

.. 列表表:: 

   * - :meth:`match() <fiftyone.core.collections.SampleCollection.match>` 
   * - :meth:`match_frames() <fiftyone.core.collections.SampleCollection.match_frames>` 
   * - :meth:`match_labels() <fiftyone.core.collections.SampleCollection.match_labels>` 
   * - :meth:`match_tags() <fiftyone.core.collections.SampleCollection.match_tags>`

另一方面,网格表很快就会变得混乱。它们为文档编写者提供了极大的灵活性,但这种灵活性也使解析它们变得很麻烦。从我们的过滤备忘单中获取此表:

+-----------------------------------------------------+-------------------------------------------------------------------------------------+ 
| 操作 | 命令 | 
+===========================================+============================================================================================================+ 
| 文件路径以“/Users”开头 | .. code-block:: | 
| | | 
| | ds.match(F("filepath").starts_with("/Users")) | 
+-----------------------------------------+---------------------------------------------------------------------------+ 
| 文件路径以“10.jpg”或“10.png”结尾 | .. code-block:: | 
| | 
| | ds.match(F("filepath").ends_with(("10.jpg", "10.png")) | 
+--------------------------------------+----------------------------------------------------------------------------------+ 
| 标签包含字符串“be” | .. code-block:: | 
| | | 
| | ds.filter_labels( | 
| | "predictions", | 
| | F("label").contains_str("be"), | 
| | ) | 
+-----------------------------------------+-----------------------------------------------------------------------+ 
| Filepath 包含“088”并且是 JPEG | .. code-block:: | 
| | | 
| | ds.match(F("filepath").re_match("088*.jpg")) | 
+-----------------------------------------+---------------------------------------------------------------------------+

在表格中,行可以占用任意数量的行,列的宽度可以变化。网格表单元格内的代码块也很难解析,因为它们占用多行空间,因此其内容与其他列的内容交错。这意味着这些表格中的代码块需要在解析过程中有效地重建。

这并非世界末日,但也不理想。

Jupyter

Jupyter 笔记本解析起来相对简单。我能够将 Jupyter 笔记本的内容读入字符串列表,每个单元格一个字符串:

导入json 
ifile = "my_notebook.ipynb"
使用 open (ifile, "r" )作为f: 
    content = f.read() 
content = json.loads(contents)[ "cells" ] 
content = [( " " .join(c[ "source" ]), c[ 'cell_type' ] for c in content]

此外,各个部分都由以 开头的 Markdown 单元格划分#

然而,考虑到 RST 带来的挑战,我决定转向 HTML 并平等对待我们所有的文档。

HTML

我使用 构建了本地安装的 HTML 文档bash generate_docs.bash,并开始使用 Beautiful Soup 解析它们。然而,我很快意识到,当 RST 代码块和带有内联代码的表格被转换为 HTML 时,虽然它们可以正确呈现,但 HTML 本身却非常笨重。以我们的过滤备忘单为例。

在浏览器中呈现时,我们的过滤备忘单的日期和时间部分前面的代码块如下所示:

img

开源 FiftyOne Docs 中的备忘单截图。图片由作者提供。

然而,原始 HTML 如下所示:

img

RST 备忘单已转换为 HTML。图片由作者提供。

这并非无法解析,但也远非理想。

Markdown

幸运的是,我能够通过使用markdownify将所有 HTML 文件转换为 Markdown 来克服这些问题。Markdown 具有一些关键优势,使其最适合这项工作。

  1. 比 HTML 更简洁:代码格式从繁琐的元素字符串简化span为以单```引号标记的内联代码片段,以三引号标记的代码块`````。这也使其易于拆分为文本和代码。
  2. **仍包含锚点:**与原始 RST 不同,此 Markdown 包含节标题锚点,因为隐式锚点已生成。这样,我不仅可以链接到包含结果的页面,还可以链接到该页面的特定节或子节。
  3. 标准化:Markdown 为初始的 RST 和 Jupyter 文档提供了几乎统一的格式,使我们能够在向量搜索应用程序中对其内容进行一致的处理。

关于 LangChain 的说明

你们中的一些人可能知道用于使用 LLM 构建应用程序的开源库LangChain,并且可能想知道为什么我不直接使用 LangChain 的文档加载器文本分割器。答案是:我需要更多的控制权!

处理文件

将文档转换为 Markdown 后,我开始清理内容并将其分成更小的部分。

打扫

清洁主要包括去除不必要的元素,包括:

  • 页眉和页脚
  • 表格行和列的支架 —|例如|select()| select_by()|
  • 额外的换行符
  • 链接
  • 图片
  • Unicode 字符
  • 加粗 — 即**text**text

我还删除了从文档中具有特殊含义的字符中转义的转义符:_*。前者用于许多方法名称,而后者通常用于乘法、正则表达式模式和许多其他地方:

文档 = 文档.替换(“\_”,“_”)。替换(“\*”,“*”)

将文档拆分为语义块

清理完文档内容后,我开始将文档分成小块。

首先,我将每个文档分成几部分。乍一看,似乎可以通过查找以字符开头的任何行来完成此操作#。在我的应用程序中,我没有区分 h1、h2、h3 等(###、 ),因此检查第一个字符就足够了。但是,当我们意识到这也用于允许在 Python 代码中注释时,###这种逻辑会给我们带来麻烦。#

为了绕过这个问题,我将文档分成文本块和代码块:

text_and_code = page_md.split( '```' ) 
text = text_and_code[:: 2 ]
代码 = text_and_code[ 1 :: 2 ]

然后,我用 来标识新节的开始,以#在文本块中开始一行。我从此行中提取了节标题和锚点:

def  extract_title_and_anchor ( header ): 
    header = " " .join(header.split( " " )[ 1 :]) 
    title = header.split( " " )[ 0 ] 
    anchor = header.split( "(" )[ 1 ].split( " " )[ 0 ]
    返回title, anchor

并将每个文本块或代码分配到适当的部分。

最初,我还尝试将文本块拆分为段落,假设由于一个部分可能包含有关许多不同主题的信息,因此整个部分的嵌入可能与仅涉及其中一个主题的文本提示的嵌入不同。然而,这种方法导致大多数搜索查询的顶级匹配不成比例地成为单行段落,结果作为搜索结果并没有太多信息。

查看随附的GitHub repo了解这些方法的实现,您可以在自己的文档上尝试它们!

使用 OpenAI 嵌入文本和代码块

在将文档转换、处理并拆分为字符串后,我为每个块生成了一个嵌入向量。由于大型语言模型本质上灵活且功能强大,因此我决定将文本块和代码块视为文本片段,并使用同一模型对其进行嵌入。

我使用了 OpenAI 的text-embedding-ada-002 模型,因为它易于使用,在 OpenAI 的所有嵌入模型中性能最高(在BEIR 基准上),而且价格最便宜。事实上,它非常便宜(0.0004 美元/1K 个代币),以至于生成 FiftyOne 文档的所有嵌入仅需花费几美分!正如 OpenAI 自己所说,“我们建议在几乎所有用例中都使用 text-embedding-ada-002。它更好、更便宜、更易于使用。”

利用这个嵌入模型,您可以生成一个 1536 维的向量来表示任何输入提示,最多 8,191 个标记(约 30,000 个字符)。

首先,您需要创建一个 OpenAI 帐户,在https://platform.openai.com/account/api-keys生成一个 API 密钥,然后使用以下命令将此 API 密钥导出为环境变量:

导出OPENAI_API_KEY= “<MY_API_KEY>”

您还需要安装openai Python 库

pip 安装 openai

我围绕 OpenAI 的 API 编写了一个包装器,它接受文本提示并返回嵌入向量:

MODEL = “text-embedding-ada-002” 

def  embed_text(text):
    response = openai.Embedding.create(
        输入=text,
        model=MODEL 
    )
    embeddings = response[ 'data' ][ 0 ][ 'embedding' ]
    返回嵌入

为了为我们所有的文档生成嵌入,我们只需将此函数应用于所有文档的每个子部分(文本和代码块)。

创建 Qdrant 向量索引

有了嵌入后,我创建了一个用于搜索的向量索引。我选择使用 Qdrant 的原因与我们选择为 FiftyOne 添加原生 Qdrant 支持的原因相同:它是开源的、免费的,而且易于使用。

要开始使用 Qdrant,您可以拉取预先构建的 Docker 映像并运行容器:

docker pull qdrant/qdrant 
docker run -d -p 6333:6333 qdrant/qdrant

此外,您还需要安装 Qdrant Python 客户端:

pip 安装 qdrant 客户端

我创建了 Qdrant 系列:

导入qdrant_client作为qc
导入qdrant_client.http.models作为qmodels

客户端 = qc.QdrantClient(url= “localhost” ) 
METRIC = qmodels.Distance.DOT 
DIMENSION = 1536
 COLLECTION_NAME = “fiftyone_docs” 

def  create_index():
    client.recreate_collection(
    collection_name=COLLECTION_NAME,
    vectors_config = qmodels.VectorParams(
            size=DIMENSION,
            distance=METRIC,
        )
    )

然后我为每个小节(文本或代码块)创建了一个向量:

导入uuid 
def  create_subsection_vector ( 
    subsection_content ,
    section_anchor ,
    page_url ,
    doc_type
     ): 

    vector = embed_text ( subsection_content ) 
    id = str ( uuid.uuid1 ( . int ) [: 32 ] 
    payload = { 
        “text” : subsection_content,
        “url” : page_url,
        “section_anchor” : section_anchor,
        “doc_type” : doc_type,
        “block_type” : block_type 
    }
    返回 id,vector,payload

对于每个向量,您都可以提供额外的上下文作为有效载荷的一部分。在本例中,我包含了可以找到结果的 URL(和锚点)、文档类型,以便用户可以指定他们是否要搜索所有文档,还是只搜索某些类型的文档,以及生成嵌入向量的字符串的内容。我还添加了块类型(文本或代码),因此如果用户正在寻找代码片段,他们可以根据该目的定制搜索。

然后我将这些向量添加到索引中,一次一页:

def add_doc_to_index (subsections、page_url、doc_type、block_type): 
    ids = [] 
    vectors = [] 
    payloads = [] 
    
    for section_anchor, section_content in subsections.items ( ): 
        for subsection in section_content: 
            id, vector, payload = create_subsection_vector ( 
                subsection, 
                section_anchor, 
                page_url, 
                doc_type, 
                block_type 
            ) 
            ids.append (id)             vectors.append ( vector)             payloads.append (payload) ##将    向量添加到集合    client.upsert (         collection_name=COLLECTION_NAME, points =         qmodels.Batch (             ids = ids, vectors             =vectors,             payloads=payloads )         ,     )


    

查询索引

创建索引后,可以通过使用相同的嵌入模型嵌入查询文本,然后在索引中搜索类似的嵌入向量来完成对索引文档的搜索。使用 Qdrant 向量索引,可以使用 Qdrant 客户端的search()命令执行基本查询。

为了使我公司的文档可搜索,我希望允许用户按文档部分以及编码块的类型进行筛选。在向量搜索中,筛选结果同时仍确保返回预定数量的结果(由参数指定top_k)称为预筛选

为了实现这一点,我编写了一个程序过滤器:

def  _generate_query_filter ( query, doc_types, block_types ): 
    """为查询生成一个过滤器。
    参数:
        query:包含查询的字符串。
        doc_types:要搜索的文档类型列表。
        block_types:要搜索的块类型列表。
    返回:
        查询的过滤器。
    """
     doc_types = _parse_doc_types(doc_types) 
    block_types = _parse_block_types(block_types) 

    _ filter = models.Filter( 
        must=[ 
            models.Filter( 
                should= [ 
                    models.FieldCondition( 
                        key= "doc_type" , 
                        match =models.MatchValue(value=dt), 
                    ) 
                for dt in doc_types 
                ], 
        
            ), 
            models.Filter( 
                should= [ 
                    models.FieldCondition( 
                        key= "block_type" , 
                        match =models.MatchValue(value=bt), 
                    ) 
                for bt in block_types 
                ]   
            ) 
        ] 
    )

    返回_过滤器

内部_parse_doc_types()_parse_block_types()函数处理参数为字符串或列表值或为 None 的情况。

然后我编写了一个函数query_index(),它接受用户的文本查询,进行预过滤,搜索索引,并从有效负载中提取相关信息。该函数返回一个形式为 的元组列表(url, contents, score),其中分数表示结果与查询文本的匹配程度。

def  query_index(查询,top_k = 10,doc_types = None,block_types = None):vector = embed_text(查询)_filter = _generate_query_filter (查询
    ,
    doc_types ,block_types)    results = CLIENT.search(        collection_name = COLLECTION_NAME,        query_vector = vector,        query_filter = _filter,        limit = top_k,        with_payload = True,        search_params = _search_params,    )    results = [         (f“ {res.payload [ 'url' ]} # {res.payload [ 'section_anchor' ]} ” ,             res.payload [ “text” ],            res.score,        )for res in results     ]返回结果
    











            



        


    

编写搜索包装器

最后一步是提供一个干净的界面供用户根据这些“矢量化”文档进行语义搜索。

我编写了一个函数print_results(),它接受查询、来自的结果query_index()和一个score参数(是否打印相似度分数),并以易于解释的方式打印结果。我使用丰富的Python 包来格式化终端中的超链接,以便在支持超链接的终端中工作时,单击超链接将在默认浏览器中打开页面。如果需要,我还使用webbrowser自动打开顶部结果的链接。

img

以丰富的超链接显示搜索结果。图片由作者提供。

对于基于 Python 的搜索,我创建了一个类FiftyOneDocsSearch来封装文档搜索行为,以便一旦对象FiftyOneDocsSearch被实例化(可能使用搜索参数的默认设置):

从fiftyone.docs_search导入FiftyOneDocsSearch 
fosearch = FiftyOneDocsSearch(open_url= False , top_k= 3 , score= True )

您可以通过调用此对象在 Python 中进行搜索。例如,要查询“如何加载数据集”的文档,您只需运行:

fosearch(“如何加载数据集”)

在这里插入图片描述

使用 Python 流程对贵公司的文档进行语义搜索。图片由作者提供。

我还使用了argparse来通过命令行提供此文档搜索功能。安装软件包后,可以使用以下命令通过 CLI 搜索文档:

fiftyone-docs-search 查询“<my-query>” <args

只是为了好玩,因为fiftyone-docs-search query有点麻烦,我给我的.zsrch文件添加了一个别名:

别名fosearch= 'fiftyone-docs-search 查询'

使用此别名,可以从命令行搜索文档:

fosearch “<my-query>”参数

结论

在此之前,我已经把自己塑造成了我公司开源 Python 库 FiftyOne 的重度用户。我编写了许多文档,并且每天都在使用(并将继续使用)该库。但是将我们的文档转变为可搜索数据库的过程迫使我更深入地了解我们的文档。当你为他人构建某些东西时,这总是一件很棒的事情,而且它最终也会帮助你!

以下是我了解到的情况:

  • Sphinx RST 很麻烦:虽然可以生成漂亮的文档,但解析起来有点麻烦
  • 不要对预处理太过痴迷: OpenAI 的 text-embeddings-ada-002 模型非常擅长理解文本字符串背后的含义,即使它的格式略有不同。提取词干并费力地删除停用词和杂项字符的日子已经一去不复返了。
  • 最好使用语义上有意义的小片段:将文档拆分成尽可能小的有意义的片段,并保留上下文。对于较长的文本,搜索查询与索引中部分文本之间的重叠部分很可能会因片段中相关性较低的文本而变得模糊。如果将文档拆分得太小,则存在索引中的许多条目包含很少语义信息的风险。
  • 向量搜索功能强大:只需进行少量提升,无需任何微调,我就能显著提高我们文档的可搜索性。从初步估计来看,这种改进的文档搜索返回相关结果的可能性比旧的关键字搜索方法高出两倍多。此外,这种向量搜索方法的语义性质意味着用户现在可以使用任意措辞、任意复杂的查询进行搜索,并保证获得指定数量的结果。

如果您发现自己(或他人)不断挖掘或筛选大量文档以寻找特定信息,我鼓励您根据自己的使用情况调整此流程。您可以修改此流程以适用于您的个人文档或公司档案。如果您这样做,我保证您将从这次体验中以全新的眼光看待您的文档!

您可以使用以下几种方法将其扩展为您自己的文档!

  • 混合搜索:将矢量搜索与传统关键字搜索相结合
  • 走向全球:使用Qdrant Cloud在云端存储和查询集合
  • 整合网络数据:使用请求直接从网络下载 HTML
  • 自动更新:每当底层文档发生变化时,使用Github Actions触发嵌入的重新计算
  • 嵌入:将其包装在 Javascript 元素中,并将其作为传统搜索栏的替代品

保证获得指定数量的结果。

如果您发现自己(或他人)不断挖掘或筛选大量文档以寻找特定信息,我鼓励您根据自己的使用情况调整此流程。您可以修改此流程以适用于您的个人文档或公司档案。如果您这样做,我保证您将从这次体验中以全新的眼光看待您的文档!

您可以使用以下几种方法将其扩展为您自己的文档!

  • 混合搜索:将矢量搜索与传统关键字搜索相结合
  • 走向全球:使用Qdrant Cloud在云端存储和查询集合
  • 整合网络数据:使用请求直接从网络下载 HTML
  • 自动更新:每当底层文档发生变化时,使用Github Actions触发嵌入的重新计算
  • 嵌入:将其包装在 Javascript 元素中,并将其作为传统搜索栏的替代品

用于构建包的所有代码都是开源的,可以在voxel51/fiftyone-docs-search repo 中找到。

博客原文:专业人工智能社区

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 很抱歉,我不是OpenAI官方。我是OpenAI训练的语言模型,不拥有OpenAI官方API文档。 你可以在OpenAI官网上查找该信息:https://beta.openai.com/docs/ 。 如果您有任何关于使用OpenAI API的具体问题,请详细说明您的需求,我将尽力帮助您。 ### 回答2: OpenAI 官方 API 文档OpenAI 公司为开发者提供的详尽说明,用于使用他们的计算机程序接口(API)。该 API 文档提供了关于如何使用 OpenAI 提供的机器学习模型进行各种任务的说明,例如自然语言处理、文本生成等。 在 API 文档中,开发者能够找到有关如何使用 OpenAI API 进行身份验证和设置环境的说明。此外,API 文档还提供了有关如何构建和发送请求以及解析响应的详细指导。开发者可以了解如何使用不同的请求参数来调用特定的模型功能,以及如何处理返回的结果。 API 文档还提供了关于各种模型的说明,包括 GPT-3(生成对抗网络)和 DALL-E(生成的图像模型)。开发者可以查阅这些模型的详细信息,例如它们的用途、输入和输出的格式等。在文档中也提供了一些示例代码和案例研究,帮助开发者更好地理解如何使用这些模型。 总的来说,OpenAI 官方 API 文档是一个详细的技术指南,为开发者提供了使用 OpenAI 提供的机器学习模型进行各种任务的指导和说明。通过阅读并遵循这些文档,开发者可以轻松集成和使用 OpenAI API,以实现各种应用场景,提升他们的开发效率和应用的质量。 ### 回答3: OpenAI官方API文档是指OpenAI为开发者提供的有关其人工智能API的详细指南和说明。这个官方文档包含了OpenAI API的基本概述、功能、操作指南以及使用示例等内容。通过阅读官方API文档,开发者可以了解并学习如何使用OpenAI的人工智能API。 官方API文档中包含了API的认证与使用方法,开发者可以在其中获取API密钥并了解如何进行身份验证,确保安全地使用API。文档中还详细介绍了API的不同功能,如文本生成、对话模型、翻译、摘要等等,并提供了使用示例和代码片段,帮助开发者更好地理解和应用这些功能。 此外,官方API文档还解释了API的限制和配额,以及如何处理数据的格式和输入输出的处理。开发者可以通过文档中的示例来了解如何构建请求并处理返回的结果。还有一些最佳实践和建议,可以帮助开发者优化他们的代码和使用体验。 总而言之,OpenAI官方API文档是开发者学习和使用OpenAI人工智能API的重要资源。它提供了详细的指南和示例,帮助开发者了解API的功能、使用方法和限制等重要信息,使开发者能够充分利用OpenAI的人工智能技术进行应用开发。文档的完整性和准确性使开发者能够更轻松地开始并成功使用OpenAI API。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值