有用的 ChatGPT 库:产品化和强化

1,362 阅读13分钟

您可能会惊讶地发现,基于 ChatGPT开发大型语言模型应用程序不需要对模型本身进行大量编码。相反,开发过程的很大一部分侧重于应用程序架构和管理支持 LLM 引擎的操作环境。

这些支持工具旨在简化开发人员的工作,帮助数据组织、内存优化和任务监控等。在本文中,我们将研究一些可帮助您更仔细地完成此任务的框架,并讨论它们在生产环境中的作用。

LangChain框架

我们将从LangChain开始,这个框架因其在构建 LLM 支持的应用程序方面的功效而受到关注。

LangChain 与许多 LLM 工具一样,默认在 OpenAI 的 GPT 模型之上运行,您需要 OpenAI 的 API 密钥才能运行它。但首先,不要忘记通过运行以下命令来安装 Python 库:pip install langchain。

LangChain 如此有效的原因在于其模块化方法。它将功能分解为不同的“链”,开发人员可以轻松修改或交换这些“链”,以构建满足其特定要求的定制模板。

LangChain的模块化方法

Langchain 链包括以下模块:

  • 模型:该模块在您的 LLM 申请中发挥着核心作用,支持来自OpenAI、Hugging Face、Anthropic、Cohere等提供商的不同模型类型的集成。
  • 提示模板:这些用于创建提示的预定义方法以不同的方式构建,以引发不同的响应。
  • **索引:**新发布的模块可以将法学硕士与您现有的数据集成,并帮助管理以前处理的文档。
  • 链:允许创建包含多个模型或提示的调用序列。
  • 代理:分析用户请求、做出决策并选择合适的工具来完成任务,循环运行直到找到解决方案。
  • 内存:此功能维护链或代理调用之间的状态。

动态提示

让我们通过一个简单的脚本将LangChain带入生活。在这里,我们将 OpenAI 和 LangChain 配对来完成简单的文本完成任务:

from langchain.chat_models import ChatOpenAI
from langchain import PromptTemplate, LLMChain

template = """Question: {input}
Analyzing the information provided step by step.
Answer: """

prompt = PromptTemplate(template=template, input_variables=["input"])

llm = ChatOpenAI(model_name="gpt-3.5-turbo")
llm_chain = LLMChain(prompt=prompt, llm=llm)

question = """Who discovered penicillin?"""

llm_chain.run(question)

响应将类似于:

Step 1: Identification of the Key Question
Answer: The main question is: "Who discovered penicillin?"

Step 2: Contextual Understanding
Answer: Penicillin is an antibiotic used in medicine.

Step 3: Historical Context
Answer: Tn 1928, scientist Alexander Fleming noticed a mold killing bacteria in a petri dish at St. Mary's Hospital in London.

Step 5: Identification of the Scientist
The discoverer of penicillin is Sir Alexander Fleming.

Answer: Sir Alexander Fleming discovered penicillin in 1928.

这PromptTemplate是模型输入的主构建器,由可修改的文本字符串组成template。它旨在通过使用input_variables. 在我们的演示中,提示创造性地将“让我们一步一步思考”片段的概念融入到查询中,设置了一个系统的思考阶段。

我们的法学硕士模型使用该函数变得栩栩如生ChatOpenAI()。然后,该LLMChain()函数在提示和模型之间建立连接,创建共生对或“链”。

最后一步涉及调用该run()函数,从而向系统提出我们的问题。激活后run(),LLMChain()系统会利用提供的输入键值(以及内存键值(如果存在))编排提示模板,将结构化字符串传递给 LLM,并以 LLM 响应结束。

动态提示虽然简单,但在改进复杂应用程序和优化提示管理方面具有巨大潜力。

代理和工具

在 LangChain 工作流程中发挥关键作用的其他关键元素是代理和工具。它们使法学硕士能够执行行动并集成各种功能,从而促进复杂问题的解决。

这种情况下的工具是一种功能抽象,旨在使 LLM 交互更加简单。它通过一个简单的界面工作,该界面接受一个文本输入并返回一个文本输出。LangChain包含许多预定义的工具,例如谷歌搜索、计算器、世界天气预报API等。此外,用户可以选择开发自定义工具并将其添加到其代理中,从而提高代理的灵活性和有效性。

另一方面,代理负责协调各个步骤并访问多个工具,选择最合适的工具来回答用户的查询。这个过程让代理有更多的时间来形成策略,这有助于它有效地管理更复杂的任务。

代理的操作可以概括如下:

  1. 代理接收用户的输入。
  2. 它决定使用哪个工具以及要输入的文本。
  3. 使用输入文本激活所选工具,生成输出文本。
  4. 然后,代理将工具的输出合并到其上下文中。
  5. 重复步骤 2-4,直到代理确定其拥有直接回复用户所需的所有信息。

所包含的图像直观地展示了代理如何使用 LangChain 中的工具。

LangChain中代理与工具的交互

记忆

记忆和上下文理解对于问答应用程序至关重要,尤其是在聊天机器人方面。LangChain 可以使用状态为您的应用程序添加内存。

例如,使用ConversationChain,您只需几行代码即可将语言模型转变为交互式聊天机器人。

from langchain import OpenAI, ConversationChain
chatbot_llm = OpenAI(model_name='gpt-3.5-turbo')
chatbot = ConversationChain(llm=chatbot_llm , verbose=True)
chatbot.predict(input='Hi!')

在上面的代码片段中,该方法predict(input='Hi!')提示聊天机器人响应问候语Hi!。模型回复如下:

> Initiating new ConversationChain sequence...
Prompt after processing:
Below is a friendly dialogue between a human and an AI. 
The AI is characterized by its eloquence and propensity for 
providing detailed information, drawing from its contextual 
understanding. Should the AI find a question beyond its 
knowledge, it will honestly admit its limitation.
Ongoing conversation:
Human: Hi!
AI:
> Finished chain.
' Hi! How can I assist you today?'

verbose=True为了更好的评估性能,设置参数ConversationChain可以让我们深入了解LangChain使用的底层提示结构。当predict(input='Hi!')被调用时,LLM 会收到一个全面的提示,由标记> Initiating new ConversationChain sequence...和界定> Finished chain。

随着对话的继续,该函数会跟踪整个对话,并根据需要更新提示。例如,如果您接下来询问机器人的名称,提示将相应更新:

> Initiating new ConversationChain sequence...
Prompt following refinement:
The following [...] does not know.
Ongoing conversation:
Human: Hi"
AI: Hi! How can I assist you today?
Human: What's your name?
AI:
> Finished chain.
'\n\nI'm ChatGPT, an AI created by OpenAI.'

因此,我们可以看到,通过战略操作设计和内存管理技术,该ConversationChain课程有效地将任何文本填充的LLM变成了交互式聊天工具。

但是我们如何专门针对我们的数据定制应用程序呢?

LlamaIndex 框架

现在让我们探索如何llama-index使用 Pinecone 这样的矢量数据库将(以前的 GPT 索引)集成到生产环境中。

LlamaIndex是一个动态库,旨在优化法学硕士中的检索增强 (RAG) 管道。当您需要利用各种来源的全面数据来丰富您的法学硕士课程以最大程度地减少模型幻觉时,它特别有用。

LlamaIndex 的主要特点:

  • 数据加载器可以简化多种格式的数据提取,无论您处理的是 API、PDF、数据库还是 CSV。
  • 节点有助于更复杂地构建数据,在不同点(例如,源、上一个、下一个、父级、子级)之间建立连接。当您需要使用“上一个”和“下一个”等清晰标签逻辑地组织 PDF 中的多个文本片段时,它们特别有用,从而简化数据探索。
  • 附加功能: LlamaIndex 还提供对其他过程的支持,例如数据检索后的重新排名。

开始使用 LlamaIndex

首先,您应该使用命令设置所需的库pip install -q llama-index pinecone-client。

环境准备就绪后,请考虑使用SQUAD等示例数据集,其中包含 ID、上下文和标题等列。在 LlamaIndex 中,这些文档对象是数据上下文的核心。

小队数据集示例。

在我们的例子中,我们只是title在extra_info. 但是,我们鼓励您根据项目的要求扩展其他参数。

from llama_index import Document

docs = []

for i, row in data.iterrows():
  docs.append(Document(
    text=row['context'],
    doc_id=row['id'],
    extra_info={'title': row['title']}
  ))
docs[1]

因此,上面的脚本有效地将 DataFrame 转换为 Document 对象列表,使它们准备好在llama-index.

Document(
  doc_id='5733bf84d058e614000b61be',
  embedding=None,
  extra_info={'title': 'University_of_Notre_Dame'},
  excluded_embed_metadata_keys=[],
  excluded_llm_metadata_keys=[],
  relationships={},
  doc_hash='4731d2eb1d86f2798922d48727e4a8e77a27afeecbcdc8c3cbb31d77f65ba5ec',
  text: "As at most other universities, Notre Dame's students run…",
  start_char_idx=None,
  end_char_idx=None,
  text_template='{metadata_str}\n\n{content}',
  metadata_template='{key}: {value}',
  metadata_seperator='\n')

使用 LlamaIndex 和 ChatGPT 创建嵌入

下一步涉及创建嵌入,这对于搜索数据集至关重要。为此,您应该准备好设置 OpenAI API 密钥并初始化SimpleNodeParser.

import os
os.environ['OPENAI_API_KEY'] = 'OPENAI_API_KEY' # Retrieve this from platform.openai.com

Document该解析器会将我们的对象列表转换为节点llama_index,这是用于索引和查询的基本单位。

from llama_index.node_parser import SimpleNodeParser

parser = SimpleNodeParser.from_defaults()

nodes = parser.get_nodes_from_documents(docs)
print(nodes[1]) # Let's check out the first node!

将节点视为文档对象,但具有有关其与数据库中其他文档的关系的附加信息。

LlamaIndex 中文档与节点的关系

例如,如果 PDF 中有多个文本块,则节点将包含有关这些块的顺序和关系的信息。它知道“Chunk One”出现在“Chunk Two”之前,依此类推,从而提供标准文档所没有的关系上下文。

节点如下所示:

TextNode(
  doc_id='ed04a675-a461-49ac-91c5-6eaf32bf72b5',
  embedding=None,
  extra_info={'title': 'University_of_Notre_Dame'},
  excluded_embed_metadata_keys=[],
  excluded_llm_metadata_keys=[],
  relationships={<NodeRelationship.SOURCE: '1'>: RelatedNodeInfo(
    node_id='5733bf84d058e614000b61be',
    node_type=None,
    extra_info={'title': 'University_of_Notre_Dame'},
    doc_hash='4731d2eb1d86f2798922d48727e4a8e77a27afeecbcdc8c3cbb31d77f65ba5ec')},
  doc_hash='4731d2eb1d86f2798922d48727e4a8e77a27afeecbcdc8c3cbb31d77f65ba5ec',
  text: "As at most other universities, Notre Dame's students run…",
  start_char_idx=None,
  end_char_idx=None,
  text_template='{metadata_str}\n\n{content}',
  metadata_template='{key}: {value}',
  metadata_seperator='\n')

虽然节点包含与文档类似的信息,但它们的不同之处在于它们构成了矢量数据库的基础。它们保存数据库中更复杂的操作和高效搜索所需的关系信息。

Pinecone 中的索引

到目前为止取得了很大进展!现在,我们准备探索Pinecone,这是一种非常适合机器学习应用程序的托管矢量数据库服务。我们将把llama_index数据存储在 Pinecone 中,使我们能够有效地管理 LLM 的嵌入,以进行基于语义的搜索。

使用 API 密钥和环境值启动 Pinecone,这两者都可以在控制台中免费获得。

import pinecone
import os

# Retrieve your API key and environment from the console at app.pinecone.io
os.environ['PINECONE_API_KEY'] = 'PINECONE_API_KEY' # Replace with your real API key!
os.environ['PINECONE_ENVIRONMENT'] = 'PINECONE_ENVIRONMENT' # and with environment

# Initialize Pinecone
pinecone.init(
  api_key=os.environ['PINECONE_API_KEY'],
  environment=os.environ['PINECONE_ENVIRONMENT']
)

# Create the index if it doesn't exist
index_name = 'index'
if index_name not in pinecone.list_indexes():
  pinecone.create_index(
    index_name,
    dimension=1536, # Match this with the text embedding model's dimension
    metric='cosine' # Cosine is usually efficient for text embeddings
  )

# Connect to the index
pinecone_index = pinecone.Index(index_name)

如果这是您第一次运行此程序,请不要担心,您的索引尚不存在。创建索引时,将维度与模型的维度相匹配非常重要text-embedding-ada-002,即1536。您可以自由选择不同的metric,但在我们的示例中,我们使用它cosine,因为它通常对于文本嵌入中的相似性计算最有效。

设置索引后,我们将连接到它。这就是PineconeVectorStore发挥作用的地方。它将充当我们存储和检索文档嵌入的接口。

from llama_index.vector_stores import PineconeVectorStore
vector_store = PineconeVectorStore(pinecone_index=pinecone_index)

接下来,我们将调用ServiceContext通过嵌入管道将文档提供到存储在StorageContext. 结果将被包装到一个 GPTVectorStoreIndex 实例中,该实例处理索引和查询过程。

from llama_index import GPTVectorStoreIndex, StorageContext, ServiceContext
from llama_index.embeddings.openai import OpenAIEmbedding

# Preparing our storage venue (aka vector db)
storage_context = StorageContext.from_defaults(
  vector_store=vector_store
)
# setup the index/query process
embedding = OpenAIEmbedding(model='text-embedding-ada-002', embed_batch_size=100)
service_context = ServiceContext.from_defaults(embed_model=embedding)

# Voilà! Our index, born from documents and nurtured by contexts!
index = GPTVectorStoreIndex.from_documents(
  docs, storage_context=storage_context,
  service_context=service_context
)

这里的一个关键参数是嵌入批量大小。默认情况下,它批量处理数据,这意味着它将文本块发送到 OpenAI,接收嵌入,然后将它们存储在 Pinecone 中。我们将批量大小设置为 100,允许我们将更大的批量发送到 OpenAI,然后发送到 Pinecone。这种方法减少了所需请求的数量,由于减少了网络延迟,因此有效地加快了流程。

使用 LlamaIndex 查询

现在我们已经构建了我们的index,我们可以开始享受一些真正的乐趣:查询。将查询引擎视为解锁该索引中存储的信息的钥匙。它并不复杂——本质上是将我们的索引重新格式化为查询友好的形式。

看看它是多么简单:

query_engine = index.as_query_engine()
response = query_engine.query("What are the various student-run media outlets at the University of Notre Dame?")
print(response)

就像那样:"The various student-run media outlets at the University of Notre Dame include three newspapers, a radio station, a television station, several magazines and journals, and a yearbook."

容易,对吧?这就是 LlamaIndex 的美妙之处。

结论

LangChain 和 LlamaIndex 等编排框架显着简化了 ChatGPT 和其他 LLM 的使用。他们能够处理各种任务,从数据提取到跨多个法学硕士交互的内存维护。

LangChain 因其丰富的工具和模块而成为法学硕士领域的重要库。它在集成不同模型、管理提示、排序链、处理代理和采用内存管理方面的多功能性为开发人员打开了新的大门。

与此同时,LlamaIndex 将自己视为一个强大的竞争对手,以其独特的处理文档结构的方法开辟了一个利基市场。这种独特的方法强调了它的潜力,并巩固了它作为语言模型编排动态领域中具有影响力的实体的地位。