代理正在彻底改变我们利用语言模型进行决策和执行任务的方式。代理是使用语言模型做出决策和执行任务的系统。与传统方法相比,它们旨在处理复杂的场景并提供更大的灵活性。代理可以被认为是推理引擎,利用语言模型来处理信息、检索相关数据、摄取(块/嵌入)并生成响应。
未来,随着语言模型的进步,代理将在处理文本、自动化任务和改善人机交互方面发挥至关重要的作用。
在此示例中,我们将特别关注在动态检索增强生成(RAG)中利用代理。使用 ActionWeaver 和 MongoDB Atlas,您将能够通过对话交互实时修改 RAG 策略。无论是选择更多块、增加块大小还是调整其他参数,您都可以微调 RAG 方法以实现所需的响应质量和准确性。您甚至可以使用自然语言向矢量数据库添加/删除源!
# LLM Config
self.rag_config = {
"num_sources": 2,
"source_chunk_size": 1000,
"min_rel_score": 0.00,
"unique": True,
"summarize_chunks": True, # adds latency at ingest, everything comes at a cost
}
分块文本很棒,但是如何存储它呢?
总结可以节省空间并加快速度,但可能会丢失细节。
存储原始数据是准确的,但体积庞大、速度较慢且“嘈杂”。
总结的优点:
总结的缺点:
什么适合你?这取决于您的需求!考虑:
演示1
创建新的Python环境
python3 -m venv env
激活新的Python环境
source env/bin/activate
安装要求
pip3 install -r requirements.txt
在params.py中设置参数:
# MongoDB
MONGODB_URI = " "
DATABASE_NAME = " genai "
COLLECTION_NAME = " rag "
# If using OpenAI
OPENAI_API_KEY = " "
# If using Azure OpenAI
OPENAI_TYPE = " azure "
OPENAI_API_VERSION = " 2023-10-01-preview "
OPENAI_AZURE_ENDPOINT = " https://.openai.azure.com/ "
OPENAI_AZURE_DEPLOYMENT = " "
使用以下定义创建搜索索引
{
"mappings" : {
"dynamic" : true ,
"fields" : {
"embedding" : {
"dimensions" : 384 ,
"similarity" : " cosine " ,
"type" : " knnVector "
}
}
}
}
设置环境
export OPENAI_API_KEY=
运行 RAG 应用程序
env/bin/streamlit run rag/app.py
应用程序生成的日志信息将附加到app.log中。
该机器人支持以下操作:回答问题、搜索网络、读取 URL、删除源、列出所有源以及重置消息。它还支持称为 iRAG 的操作,使您可以动态控制代理的 RAG 策略。
例如:“将 RAG 配置设置为 3 个源,块大小为 1250”=> 新的 RAG 配置:{'num_sources': 3, 'source_chunk_size': 1250, 'min_rel_score': 0, 'unique': True}。
def __call__(self, text):
text = self.preprocess_query(text)
self.messages += [{"role": "user", "content":text}]
response = self.llm.create(messages=self.messages, actions = [
self.read_url,self.answer_question,self.remove_source,self.reset_messages,
self.iRAG, self.get_sources_list,self.search_web
], stream=True)
return response
如果机器人无法根据 Atlas Vector 存储中存储的数据和您的 RAG 策略(源数量、块大小、min_rel_score 等)提供问题的答案,它将启动 Web 搜索以查找相关信息。然后,您可以指示机器人读取这些结果并从中学习。
RAG 很酷,但制定正确的“RAG 策略”却很棘手。块大小和独特来源的数量将直接影响法学硕士生成的响应。
在制定有效的 RAG 策略时,网络源的摄取过程、分块、嵌入、块大小以及所使用的源数量起着至关重要的作用。分块对输入文本进行分解以更好地理解,嵌入捕获含义,来源数量影响响应多样性。在块大小和源数量之间找到适当的平衡对于准确且相关的响应至关重要。需要进行实验和微调来确定最佳设置。
在我们深入“检索”之前,我们先来谈谈“摄取过程”
为什么需要一个单独的过程来将您的内容“摄取”到矢量数据库中?利用代理的魔力,我们可以轻松地将新内容添加到矢量数据库中。
有许多类型的数据库可以存储这些嵌入,每种都有其特殊用途。但对于涉及 GenAI 应用程序的任务,我推荐 MongoDB。
将 MongoDB 视为一块既可吃又可吃的蛋糕。它为您提供了进行查询的语言(Mongo 查询语言)的强大功能。它还包括 MongoDB 的所有强大功能。最重要的是,它允许您存储这些构建块(向量嵌入)并对它们进行数学运算,所有这些都在一个地方。这使得 MongoDB Atlas 成为满足您所有矢量嵌入需求的一站式商店!
@action("read_url", stop=True)
def read_url(self, urls: List[str]):
"""
Invoke this ONLY when the user asks you to 'read', 'add' or 'learn' some URL(s).
This function reads the content from specified sources, and ingests it into the Knowledgebase.
URLs may be provided as a single string or as a list of strings.
IMPORTANT! Use conversation history to make sure you are reading/learning/adding the right URLs.
Parameters
----------
urls : List[str]
List of URLs to scrape.
Returns
-------
str
A message indicating successful reading of content from the provided URLs.
"""
with self.st.spinner(f"```Analyzing the content in {urls}```"):
loader = PlaywrightURLLoader(urls=urls, remove_selectors=["header", "footer"])
documents = loader.load_and_split(self.text_splitter)
self.index.add_documents(
documents
)
return f"```Contents in URLs {urls} have been successfully ingested (vector embeddings + content).```"
{
"mappings": {
"dynamic": true,
"fields": {
"embedding": {
"dimensions": 384, #dimensions depends on the model
"similarity": "cosine",
"type": "knnVector"
}
}
}
}
def recall(self, text, n_docs=2, min_rel_score=0.25, chunk_max_length=800,unique=True):
#$vectorSearch
print("recall=>"+str(text))
response = self.collection.aggregate([
{
"$vectorSearch": {
"index": "default",
"queryVector": self.gpt4all_embd.embed_query(text), #GPT4AllEmbeddings()
"path": "embedding",
#"filter": {},
"limit": 15, #Number (of type int only) of documents to return in the results. Value can't exceed the value of numCandidates.
"numCandidates": 50 #Number of nearest neighbors to use during the search. You can't specify a number less than the number of documents to return (limit).
}
},
{
"$addFields":
{
"score": {
"$meta": "vectorSearchScore"
}
}
},
{
"$match": {
"score": {
"$gte": min_rel_score
}
}
},{"$project":{"score":1,"_id":0, "source":1, "text":1}}])
tmp_docs = []
str_response = []
for d in response:
if len(tmp_docs) == n_docs:
break
if unique and d["source"] in tmp_docs:
continue
tmp_docs.append(d["source"])
str_response.append({"URL":d["source"],"content":d["text"][:chunk_max_length],"score":d["score"]})
kb_output = f"Knowledgebase Results[{len(tmp_docs)}]:n```{str(str_response)}```n## n```SOURCES: "+str(tmp_docs)+"```nn"
self.st.write(kb_output)
return str(kb_output)
使用 ActionWeaver(函数调用 API 的轻量级包装器),我们可以构建一个用户代理,使用 MongoDB Atlas 高效检索和摄取相关信息。
代理是一个中间人,将客户端请求发送到其他服务器或资源,然后返回响应。
该代理以交互式和可定制的方式向用户呈现数据,从而增强整体用户体验。
UserProxyAgent
有几个可以定制的RAG参数,例如chunk_size
(例如1000)、 num_sources
(例如2)、 unique
(例如True)和min_rel_score
(例如0.00)。
class UserProxyAgent:
def __init__(self, logger, st):
self.rag_config = {
"num_sources": 2,
"source_chunk_size": 1000,
"min_rel_score": 0.00,
"unique": True,
}
以下是影响我们选择 ActionWeaver 决定的一些主要优势:
代理基本上只是一个计算机程序或系统,旨在感知其环境、做出决策并实现特定目标。
将代理视为一个软件实体,它显示出一定程度的自主权,并代表其用户或所有者在其环境中执行操作,但以相对独立的方式。它通过审议其选项来主动采取行动以实现其目标。代理的核心思想是使用语言模型来选择要采取的一系列操作。与链相反,链中的一系列操作被硬编码在代码中,代理使用语言模型作为推理引擎来确定要采取哪些操作以及按什么顺序。
操作是代理可以调用的函数。围绕操作有两个重要的设计考虑因素:
Giving the agent access to the right actions
Describing the actions in a way that is most helpful to the agent
如果不考虑这两点,你将无法构建一个有效的代理。如果您不授予代理访问一组正确操作的权限,它将永远无法实现您赋予它的目标。如果你没有很好地描述这些动作,代理将不知道如何正确使用它们。
然后调用 LLM,导致对用户的响应或要采取的操作。如果确定需要响应,则将其传递给用户,并且该周期结束。如果确定需要采取行动,则采取该行动,并进行观察(行动结果)。该操作和相应的观察被添加回提示(我们称之为“代理草稿本”),并且循环重置,即。再次调用 LLM(使用更新的代理暂存器)。
在 ActionWeaver 中,我们可以通过向操作添加stop=True|False
来影响循环。如果stop=True
,LLM 将立即返回函数的输出。这也将限制 LLM 进行多个函数调用。在此演示中,我们将仅使用stop=True
ActionWeaver 还支持使用orch_expr(SelectOne[actions])
和orch_expr(RequireNext[actions])
更复杂的循环控制,但我将把它留给第二部分。
ActionWeaver代理框架是一个以函数调用为核心的AI应用框架。它旨在实现传统计算系统与语言模型强大的推理功能的无缝合并。 ActionWeaver 是围绕 LLM 函数调用的概念构建的,而像 Langchain 和 Haystack 这样的流行框架是围绕管道的概念构建的。
了解更多信息:https://thinhdanggroup.github.io/function-calling-openai/
开发人员可以使用简单的装饰器将任何 Python 函数附加为工具。在下面的示例中,我们引入了 get_sources_list 操作,该操作将由 OpenAI API 调用。
ActionWeaver 利用装饰方法的签名和文档字符串作为描述,将它们传递给 OpenAI 的函数 API。
ActionWeaver 提供了一个轻量级包装器,负责将文档字符串/装饰器信息转换为 OpenAI API 的正确格式。
@action(name="get_sources_list", stop=True)
def get_sources_list(self):
"""
Invoke this to respond to list all the available sources in your knowledge base.
Parameters
----------
None
"""
sources = self.collection.distinct("source")
if sources:
result = f"Available Sources [{len(sources)}]:n"
result += "n".join(sources[:5000])
return result
else:
return "N/A"
stop=True 添加到操作时意味着 LLM 将立即返回函数的输出,但这也限制了 LLM 进行多个函数调用。例如,如果询问纽约和旧金山的天气,该模型将为每个城市依次调用两个单独的函数。但是,使用stop=True
时,一旦第一个函数返回纽约市或旧金山的天气信息(具体取决于它首先查询哪个城市),此过程就会中断。
要更深入地了解该机器人的工作原理,请参阅 bot.py 文件。此外,您可以探索 ActionWeaver 存储库以获取更多详细信息。
生成推理轨迹允许模型诱导、跟踪和更新行动计划,甚至处理异常。此示例使用 ReAct 与思想链 (CoT) 相结合。
思想链
推理+行动
[EXAMPLES]
- User Input: What is MongoDB?
- Thought: I have to think step by step. I should not answer directly, let me check my available actions before responding.
- Observation: I have an action available "answer_question".
- Action: "answer_question"('What is MongoDB?')
- User Input: Reset chat history
- Thought: I have to think step by step. I should not answer directly, let me check my available actions before responding.
- Observation: I have an action available "reset_messages".
- Action: "reset_messages"()
- User Input: remove source https://www.google.com, https://www.example.com
- Thought: I have to think step by step. I should not answer directly, let me check my available actions before responding.
- Observation: I have an action available "remove_source".
- Action: "remove_source"(['https://www.google.com', 'https://www.example.com'])
- User Input: read https://www.google.com, https://www.example.com
- Thought: I have to think step by step. I should not answer directly, let me check my available actions before responding.
- Observation: I have an action available "read_url".
- Action: "read_url"(['https://www.google.com','https://www.example.com'])
[END EXAMPLES]
思维链 (CoT) 和 ReAct 提示技术在这些示例中都发挥了作用。方法如下:
思想链(CoT)提示:
反应提示:
总之,CoT 和 ReAct 在这些例子中都发挥着至关重要的作用。 CoT 使模型能够逐步推理并选择适当的操作,而 ReAct 通过允许模型与其环境交互并相应地更新其计划来扩展此功能。这种推理和行动的结合使大型语言模型更加灵活和通用,使它们能够处理更广泛的任务和情况。
让我们首先向我们的代理询问一个问题。在这种情况下, “什么是芒果?” 。首先发生的事情是,它将尝试使用向量嵌入相似性来“回忆”任何相关信息。然后,它将用它“召回”的内容制定响应,或者执行网络搜索。由于我们的知识库目前是空的,因此我们需要添加一些来源,然后才能制定响应。
由于机器人无法使用矢量数据库中的内容提供答案,因此它启动了 Google 搜索以查找相关信息。我们现在可以告诉它应该“学习”哪些来源。在本例中,我们将告诉它从搜索结果中了解前两个来源。
接下来我们来修改RAG策略!让我们让它只使用一个源,并让它使用 500 个字符的小块。
请注意,虽然它能够检索具有相当高相关性分数的块,但它无法生成响应,因为块大小太小并且块内容的相关性不足以制定响应。由于它无法生成小块的响应,因此它代表用户执行网络搜索。
让我们看看如果将块大小从 500 个字符增加到 3000 个字符会发生什么。
现在,有了更大的块大小,它就能够使用矢量数据库中的知识准确地制定响应!
让我们通过询问代理的知识库中可用的内容:您的知识库中有哪些来源?
如果您想删除特定资源,您可以执行以下操作:
USER: remove source 'https://www.oracle.com' from the knowledge base
要删除集合中的所有源 - 我们可以执行以下操作:
USER: what sources do you have in your knowledge base?
AGENT: {response}
USER: remove all those sources please
该演示让我们了解了人工智能代理的内部工作原理,展示了它以交互方式学习和响应用户查询的能力。我们亲眼目睹了它如何将内部知识库与实时网络搜索无缝结合起来,以提供全面而准确的信息。这项技术的潜力是巨大的,远远超出了简单的问答范畴。如果没有函数调用 API的魔力,这一切都是不可能的。
这是受到 https://github.com/TengHu/Interactive-RAG 的启发
我们欢迎开源社区的贡献。
阿帕奇许可证 2.0