让大模型使用 byzerllm 从 0 实现RAG应用

科技   2024-08-09 16:10   上海  

这里我们使用了 auto-coder.chat  来实现编码。我们假设你对 auto-coder.chat 有个基本了解。

首先添加两篇文章,这样大模型就可以学习文档来学会如何使用  byzerllm:

/add_files /Users/allwefantasy/projects/byzer-llm/docs/zh/003_byzerllm_大模型编程快速指南.md/add_files /Users/allwefantasy/projects/byzer-llm/docs/zh/004_byzerllm_storage_一个好用的AI存储引擎.md

启动 byzerllm storage (向量加全文检索融合引擎):

byzerllm storage start --enable_emb


现在可以开始编程(我们是使用了 sonnet3.5):

/coding 根据大模型和存储的使用文档,我们要在 rag_exmaples 目录下实现一个 rag.py 文件,该文件指定一个目录,对目录里的md 文档按1000字最大长度做切割(按换行做分隔符,不断追加,直到超过1000时停止追加,得到一个chunk),然后对chunk 进行向量化,存储到存储引擎里.之后再根据query 召回并使用大模型回答.请注意使用 prompt 函数。


生成的代码(我大概人工调整了两到三行代码):

import osimport byzerllmfrom byzerllm.apps.byzer_storage.simple_api import (    ByzerStorage,    DataType,    FieldOption,    SortOption,)
def chunk_text(text, max_length=1000): chunks = [] current_chunk = [] current_length = 0
for line in text.split("\n"): if current_length + len(line) > max_length and current_chunk: chunks.append("\n".join(current_chunk)) current_chunk = [] current_length = 0 current_chunk.append(line) current_length += len(line)
if current_chunk: chunks.append("\n".join(current_chunk))
return chunks
@byzerllm.prompt()def process_query(context: str, query: str) -> str: """ Based on the following context, please answer the query:
Context: {{ context }}
Query: {{ query }}
Please provide a concise and accurate answer based on the given context. """
class RAG: def __init__( self, llm_model="deepseek_chat", emb_model="emb", storage_name="byzerai_store" ): self.storage = ByzerStorage( storage_name, "rag_database", "rag_table", emb_model=emb_model ) self.llm = byzerllm.ByzerLLM() self.llm.setup_default_model_name(llm_model)
# Create schema if not exists _ = ( self.storage.schema_builder() .add_field("_id", DataType.STRING) .add_field("content", DataType.STRING, [FieldOption.ANALYZE]) .add_field("raw_content", DataType.STRING, [FieldOption.NO_INDEX]) .add_array_field("vector", DataType.FLOAT) .execute() )
def index_directory(self, directory): for filename in os.listdir(directory): if filename.endswith(".md"): with open(os.path.join(directory, filename), "r") as file: content = file.read() chunks = chunk_text(content)
for i, chunk in enumerate(chunks): item = { "_id": f"{filename}_{i}", "content": chunk, "raw_content": chunk, "vector": chunk, } self.storage.write_builder().add_items( [item], vector_fields=["vector"], search_fields=["content"] ).execute()
self.storage.commit()
def query(self, query_text): query = self.storage.query_builder() query.set_vector_query(query_text, fields=["vector"]) results = query.execute()
if results: context = results[0]["raw_content"] response = process_query.with_llm(self.llm).run( context=context, query=query_text ) return response else: return "Sorry, I couldn't find relevant information to answer your query."
if __name__ == "__main__": rag = RAG()
# Index documents in a directory rag.index_directory("/Users/allwefantasy/projects/auto-coder/docs/zh")
# Query example query = "AutoCoder 如何配置搜索引擎?" answer = rag.query(query) print(f"Query: {query}") print(f"Answer: {answer}")

我们来对代码进行一些梳理:

  1. 我们通过一个简单的 chunk_text 方法实现了对文本的分割,保证生成的chunk 最多不超过 1000字符。

  2. 我们提供了一个prompt 函数 process_query,给定指定的检索结果以及问题,然后进行回复。

  3. 我们封装了一个 RAG 类,里面实现了建表,切割,把数据写入表,以及查询并调用 process_query 的逻辑。

当然了,chunk_text 和 process_query 其实都可以放到 RAG 类里,不过因为大模型生成的,我也就懒得调整了。

整个 RAG 实现实际上不到100行代码就完成了,用户可以任意自定义逻辑,无需学习特殊的组件或者模块,重要的是可以继续使用 auto-coder.chat 来迭代。

下面是我们使用 /chat 指令对生成代码的一个解读(还是很漂亮的):

运行后的结果:

Query: AutoCoder 如何配置搜索引擎?Answer: 要配置 AutoCoder 使用搜索引擎,您需要完成以下步骤:
1. **申请搜索API Token**:推荐使用 Bing 搜索引擎,访问 [Bing Web Search API](https://www.microsoft.com/en-us/bing/apis/bing-web-search-api) 获取 token。
2. **在 AutoCoder 参数中配置搜索引擎**:在配置文件中添加以下参数: ```yml search_engine: bing search_engine_token: ENV {{BING_SEARCH_TOKEN}} ```
这样,AutoCoder 就可以使用 Bing 搜索引擎来辅助代码编写和文档查找。


可以看到, byzerllm 包含了大模型编程接口以及存储引擎,简单灵活,给定两个文档大模型即可基本完整无误的开发一整个 RAG 应用流程。这个是当前其他的框架引擎难以做到的。实际上我们还有相当复杂的案例,效果也是非常惊人。


祝威廉
架构/大数据/机器学习的心得和体会,也会因为爱情和生活写首诗
 最新文章