带有 Web 搜索的 RAG
检索增强生成 (RAG) 是一种将现有或专有数据与大型语言模型 (LLM) 结合使用的常用方法。许多文章描述了如何执行 RAG。通常,它们涉及将数据编码为向量,并将向量存储在数据库中。查询数据库,并将数据置于上下文中,并在其中进行标记化(转换为向量),同时将提示符发送到 LLM。简而言之,RAG 就是将数据放入提示符中,供 LLM 处理。
从实际意义上讲,互联网就是世界的数据库,我们可以使用搜索引擎进行查询,因为搜索引擎拥有返回相关结果的方法。听起来很熟悉?我们可以利用搜索功能来驱动 RAG 应用程序。
在这个例子中,我们将使用 DuckDuckGo 进行搜索,使用 Langchain 检索网页并处理数据,并选择具有开源 LLM 的 Ollama 或像 OpenAI 这样的 LLM 服务。
对于没有耐心的人,代码
首先,将包导入到您的环境中。
pip install -r requirements.txt
让我们深入研究代码!查询 DuckDuckGo、检索网页以及格式化以插入提示都由这三个函数完成。ddg_search
函数查询 DuckDuckGo。get_page
函数使用 Langchain 的文档加载器从搜索中检索页面,仅提取p
带有 的 HTML 标签之间的文本,并返回 Langchain文档BeautifulSoupTransformer
列表。
该ddg_search
函数从文档中提取文本并截断它们,以确保它们适合LLM的上下文窗口。最近的LLM具有更大的上下文窗口,您可以通过更改值来更改截断量和截断位置。例如,您可能希望捕获文本的结尾,其中包括结论和摘要。每个文档中处理后的文本将以列表形式返回。
def ddg_search(query):
results = DDGS().text(query, max_results=5)
print(results)
urls = []
for result in results:
url = result['href']
urls.append(url)
docs = get_page(urls)
content = []
for doc in docs:
page_text = re.sub("\n\n+", "\n", doc.page_content)
text = truncate(page_text)
content.append(text)
return content
def get_page(urls):
loader = AsyncChromiumLoader(urls)
html = loader.load()
bs_transformer = BeautifulSoupTransformer()
docs_transformed = bs_transformer.transform_documents(html, tags_to_extract=["p"], remove_unwanted_tags=["a"])
return docs_transformed
def truncate(text):
words = text.split()
truncated = " ".join(words[:400])
return truncated
以下部分将创建一个提示。目前,没有标准的提示格式,每个 LLM 都实现了自己的提示格式。以下部分演示了如何为 llama2 或 llama3 LLM 以及 OpenAI LLM 创建提示。请注意其prompt
构造有何不同。
def create_prompt_ollama(llm_query, search_results):
content_start = (
"Answer the question using only the context below.\n\n"+
"Context:\n"
)
content_end = (
f"\n\nQuestion: {llm_query}\nAnswer:"
)
content = (
content_start + "\n\n---\n\n".join(search_results) +
content_end
)
prompt = [{'role': 'user', 'content': content }]
return prompt
def create_prompt_openai(llm_request, search_results):
prompt_start = (
"Answer the question using only the context below.\n\n"+
"Context:\n"
)
prompt_end = (
f"\n\nQuestion: {llm_request}\nAnswer:"
)
prompt = (
prompt_start + "\n\n---\n\n".join(search_results) +
prompt_end
)
return prompt
为每个 LLM 创建完成(或响应)也不同。
# use ollama with llama3 foundation model
def create_completion_ollama(prompt):
completion = ollama.chat(model='llama3', messages=prompt)
return completion['message']['content']
# use openai's foundation models
def create_completion_openai(prompt):
res = client.completions.create(
model="gpt-3.5-turbo-instruct",
prompt=prompt,
temperature=0,
max_tokens=1000,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
stop=None
)
return res.choices[0].text
该应用程序使用 Streamlit 搜索 DuckDuckGo,将结果发送给 LLM,并显示完成情况。
# ui
with st.form("prompt_form"):
result =""
prompt = ""
search_query = st.text_area("DuckDuckGo search:", None)
llm_query = st.text_area("LLM prompt:", None)
submitted = st.form_submit_button("Send")
if submitted:
search_results = ddg_search(search_query)
# prompt = create_prompt_ollama(llm_query,search_results)
# result = create_completion_ollama(prompt)
prompt = create_prompt_openai(llm_query,search_results)
result = create_completion_openai(prompt)
e = st.expander("LLM prompt created:")
e.write(prompt)
st.write(result)
这是一个使用 OpenAI 的 GPT3.5 对泰勒·斯威夫特的新专辑《Tortured Poets Department》进行评价的示例查询。哎哟!有点苛刻。
思考
这可以改进吗?当然!大多数搜索引擎都支持搜索特定站点或排除站点的操作符。例如,搜索 Kubernetes 的容器网络接口 (CNI) 时,可以将其限制在 kubernetes.io,而不是所有其他涉及 CNI 的站点。BeautifulSoupTransformer 支持按标签提取或排除文本,并且truncate
可以扩展该功能以从某些部分(例如结论和摘要所在的末尾)提取文本。您还可以将 LLM 从通用聊天模型更改为指导模型,并将其用作编码助手。
拥有法学硕士学位的学生可以使用网页搜索功能,从而获得更精准的搜索结果和摘要。请务必查看Github上的代码。
文章来源:https://dev.to/spara_50/rag-with-web-search-2c3e