我想到了一个想法——构建一个 AI API 应用程序,存储从 medrxiv.org 下载的最新发表的研究论文,然后可以随时从她的 Mac 上查询。
以下是通过 API 获得的 AI 研究助手的结果。AI 的结果以用户友好的格式呈现,其中“research_result”显示从数据库检索到的答案,“source”指示其来自的特定 PDF。这对于编写脚注和参考书目特别有用。
事不宜迟,我将带您了解该项目的开发过程,该项目自 2024 年 8 月 19 日起全面投入使用。代码可在 GitHub 存储库中找到。
开发过程相当简单,遵循常规的 RAG(检索增强生成)LLMOp 方法。首先,我在线抓取数据,通过嵌入进行处理,然后开发应用程序。部署后,该应用程序经过多次测试、微调和版本控制,才达到您今天看到的最终版本。
1. 使用 Ollama 创建我们自己的 LLama3 模型。
为了快速实现 MVP,我没有从头开始构建 LLaMA3 模型或对其进行微调。相反,我使用一些简单的工程在 Ollama 环境中创建了一个自定义 LLaMA3 模型。以下是我的 Modelfile 的摘录:
FROM /usr/share/ollama/.ollama/models/blobs/sha256-6a0746a1ec1aef3e7ec53868f220ff6e389f6f8ef87a01d77c96807de94ca2aa
# My param configurations
# Number of Context Tokens controls the maximum number of tokens (words or subwords) the model can use to understand and generate a response.
PARAMETER num_ctx 4096
# temperature parameter controls the randomness or creativity of the model's responses.
PARAMETER temperature 0.75
# top_k limits the number of potential next tokens (words or subwords) the model considers when generating text.
# top_k 60 means the top 60 most likely options
PARAMETER top_k 60
# top_p controls the model to consider only the most probable next tokens that collectively have a probability of top_p.
PARAMETER top_p 0.90
TEMPLATE """{{ if .System }}<|start_header_id|>system<|end_header_id|>
{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>
{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>
{{ .Response }}<|eot_id|>"""
PARAMETER stop "<|start_header_id|>"
PARAMETER stop "<|end_header_id|>"
PARAMETER stop "<|eot_id|>"
PARAMETER stop "<|reserved_special_token"
SYSTEM """
As a personal assistant for medical researchers, your task is to analyze the provided research papers and extract pertinent information based on the given keywords. Summarize key findings, methodologies, and notable insights clearly and concisely to facilitate researchers' understanding of the current state of knowledge.
Your goal is to provide researchers with 'aha' moments by explaining complex topics in a straightforward manner.
Instructions:
Focus on: Summarizing key insights and methodologies that are directly relevant to the provided keywords.
Ensure: Your responses are concise and easy to understand, emphasizing clarity and readability.
Avoid improvisation: Only use the information provided in the research papers.
If the provided context does not contain the necessary information to answer a query, respond with: 'Sorry, the provided context is not clear enough for me to answer the query from the research papers.'
"""
这些参数控制模型行为的各个方面:
- num_ctx:控制模型可以使用的最大标记数。
- 温度:控制响应的随机性或创造性。
- top_k:限制模型考虑的潜在下一个标记的数量。
- top_p:限制模型仅考虑最可能的下一个标记。
模板很重要,否则模型不知道在哪里停止。
SYSTEM 是即时工程,它指示 AI 该做什么,不该做什么。更多信息请阅读Ollama 的官方文档。
不过在应用这个 Modelfile 之前,我们需要先安装 Ollama 和 Llama3。如果你还没有安装 Llama3 和 Ollama,请自行 google,网上有无数的实现教程。
您可以在终端中测试您的 Llama3。只需输入命令 ollama run llama3。如果成功,您将看到如下所示的内容。
ollama run llama3
现在 cd 到你的工作文件夹或 Modelfile 所在的文件夹。然后运行以下命令:
ollama create ollama3_research -f Modelfile
然后运行此命令来尝试这个新创建的预训练模型。
ollama run ollama3_research
恭喜!您有了自己的研究模型,该模型经过了轻微的设计,可用于研究!
2. 设置 Qdrant Docker 实例
在索引和存储数据之前,我们需要构建数据库。幸运的是,这一步可以通过在终端中使用以下命令轻松完成。
docker run -p 6333:6333 qdrant/qdrant
此命令使用“qdrant/qdrant”映像运行 Docker 容器,并将容器的端口 6333 映射到主机的端口 6333。此设置允许我们通过“http://localhost:6333”访问 Qdrant 实例。
然而,我们希望即使在 Docker 容器停止后,Qdrant 中存储的数据仍然能够保留下来。
docker run -p 6333:6333 -v /mnt/x/data/217_local_rag_api/qdrant_storage:/qdrant/storage:z qdrant/qdrant
此命令与上一个命令功能相同,但还包括卷挂载 (-v /mnt/x/data/217_local_rag_api/qdrant_storage:/qdrant/storage:z)。卷挂载可确保即使在 Docker 容器停止后,Qdrant 中存储的数据仍会保留。主机上的 `/mnt/x/data/217_local_rag_api/qdrant_storage` 目录映射到容器内的 `/qdrant/storage` 目录。这样,Qdrant 存储的任何数据都将保存到主机的 `/mnt/x/data/217_local_rag_api/qdrant_storage` 目录中,从而使其持久化。
:z 选项告诉 Docker 重新标记卷中的文件,以便容器可以访问它们,确保 SELinux 不会阻止容器访问已挂载的卷。
应用上述命令后,我们的数据库已准备就绪。我们可以通过http://localhost:6333/dashboard 访问我们的 Qdrant UI。
3. 从 medrxiv 下载我们的数据
现在我们可以从 medrxiv 下载医学论文了。下面是我编写的用于处理下载的 Python 类。有关详细的 API 说明,请参阅medrixiv 官方网页。
class Data:
def __init__(self, config):
self.config = config
def create_data_folder(self, download_path):
if not os.path.exists(download_path):
os.makedirs(download_path)
print("Output folder created")
else:
print("Output folder already exists.")
def download_papers(self, search_query, download_path, server="arxiv", start_date=None, end_date=None):
self.create_data_folder(download_path)
if server == "arxiv":
client = arxiv.Client()
search = arxiv.Search(
query=search_query,
sort_by=arxiv.SortCriterion.SubmittedDate,
)
results = list(client.results(search))
for paper in tqdm(results):
if os.path.exists(download_path):
paper_title = (paper.title).replace(" ", "_")
paper.download_pdf(dirpath=download_path, filename=f"{paper_title}.pdf")
print(f"{paper.title} Downloaded.")
elif server == "medrxiv":
if not start_date or not end_date:
print("Error: 'start_date' and 'end_date' are required for medRxiv.")
return
# Construct the API URL
api_url = f"https://api.medrxiv.org/details/{server}/{start_date}/{end_date}/0/json"
response = requests.get(api_url)
if response.status_code != 200:
print(f"Failed to retrieve data from MedRxiv API. Status code: {response.status_code}")
return
data = response.json()
if 'collection' not in data or len(data['collection']) == 0:
print("No papers found with the given search query.")
return
papers = data['collection']
for paper in tqdm(papers):
title = paper['title'].strip().replace(" ", "_").replace("/", "_") # Replace spaces and slashes with underscores
pdf_url = f"https://www.medrxiv.org/content/{paper['doi']}.full.pdf"
print(f"Attempting to download {title} from {pdf_url}")
try:
pdf_response = requests.get(pdf_url)
if pdf_response.status_code == 200:
pdf_path = os.path.join(download_path, f"{title}.pdf")
with open(pdf_path, 'wb') as pdf_file:
pdf_file.write(pdf_response.content)
print(f"{title} Downloaded to {pdf_path}.")
else:
print(f"Failed to download {title}. Status code: {pdf_response.status_code}")
except Exception as e:
print(f"An error occurred while downloading {title}: {e}")
else:
print(f"Server '{server}' is not supported.")
def ingest(self, embedder, llm):
print("Reading pdf from the specified directory...")
documents = SimpleDirectoryReader(self.config["data_path"]).load_data()
print("Initializing Qdrant client...")
client = qdrant_client.QdrantClient(url=self.config["qdrant_url"])
vector_store = QdrantVectorStore(
client=client,
collection_name=self.config["collection_name"]
)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
# Use the Settings object correctly
service_context = ServiceContext.from_defaults(
llm=llm, embed_model=embedder, chunk_size=self.config["chunk_size"]
)
print("Start embedding the documents and storing the resulting vectors in the Qdrant vector store...")
index = VectorStoreIndex.from_documents(
documents, storage_context=storage_context, service_context=service_context
)
print(
f"Data indexed successfully to Qdrant. Collection: {self.config['collection_name']}"
)
return index
接下来,我们将实际下载并将 pdf 导入到我们的 Qdrant 数据库。
至于嵌入模型,huggingface 中有很多。对于这个特定的项目,我们将使用`all-MiniLM-L6-v2`,它的下载量为 58.3M,点赞数为 2.1K。
在运行下载、嵌入和提取功能之前,请记住手动设置参数。
# Manually set the parameters
query = "heart failure exercise tolerance" # The topic my wife is interested in
ingest = True # Set to True if you want to ingest data
server = "medrxiv" # Set server to "medrxiv" or "arxiv"
start_date = "2003-07-31" # Required for medRxiv, it is useful becacuse you don't want outdated papers
end_date = "2024-08-01" # Required for medRxiv, it is useful becacuse you don't want outdated papers
欲了解更多信息,请访问 llamaindex 官方网页。
4. 构建查询引擎
这是另一个简单的过程,让我们设置我们的查询引擎:
class RAG:
def __init__(self, config_file, llm):
self.config = config_file
self.qdrant_client = qdrant_client.QdrantClient(
url=self.config['qdrant_url']
)
self.llm = llm # ollama llm
def load_embedder(self):
embed_model = LangchainEmbedding(
HuggingFaceEmbeddings(model_name=self.config['embedding_model'])
)
return embed_model
def qdrant_index(self):
client = qdrant_client.QdrantClient(url=self.config["qdrant_url"])
qdrant_vector_store = QdrantVectorStore(
client=client, collection_name=self.config['collection_name']
)
service_context = ServiceContext.from_defaults(
llm=self.llm, embed_model=self.load_embedder(), chunk_size=self.config["chunk_size"]
)
index = VectorStoreIndex.from_vector_store(
vector_store=qdrant_vector_store, service_context=service_context
)
return index
5.启动 API 应用程序
最后,是时候启动 API 应用程序了。首先,导航到您的工作目录并运行:
uvicorn app:app --host 0.0.0.0 --port 8000 --reload
然后你可以转到 localhost FastAPI — Swagger UI,并测试这个 FastAPI 应用。我对 AI 的查询是“告诉我更多关于心力衰竭和运动耐受性的信息。有没有与运动和猝死相关的论文?告诉我它的细节。”
http://localhost:8000/docs#/default/search_api_search_post
结论
在这个项目中,展示了如何为医疗专业人士量身打造一个人工智能研究助理,解决方案包括结合使用 Llama3 通过 Ollama 进行自然语言处理、使用 Qdrant 进行基于向量的存储和检索,以及使用 FastAPI 将应用程序部署为可访问的 API。
这款 AI 研究助手大大减少了阅读研究论文所需的时间和精力,使像我妻子(一位高级护士)这样的专业人士能够将更多精力放在写作上,而不是数据收集上。整个开发过程(从设置 Llama3 模型到使用 FastAPI 部署应用程序)凸显了现代 AI 工具在改变我们学习和以建设性但不作弊的方式进行研究方面的力量。
该项目是如何利用数据科学和人工智能解决实际问题的实际示例,让我们一窥个性化和自动化研究助理的未来。提供的代码和设置说明确保解决方案具有可复制性和适应性,允许其他人修改和扩展应用程序以满足他们的特定需求。
RA/SD 衍生者AI训练营。发布者:chris,转载请注明出处:https://www.shxcj.com/archives/5440