随着大型语言模型(LLMs)的发展与广泛应用,我们正身处一个技术革新的时代。尤其是以检索为基础的系统,在这场技术飞跃中走在了最前沿。这些模型不仅仅是自动化的工具,它们更是提高生产力的得力助手,能够与各种工具和文档进行交互,展开有根据的对话。
为了深入挖掘和探索这一技术的潜力,我们诚挚地邀请您参加NVIDIA 第十届Sky Hackathon活动。本次活动将聚焦于大型语言模型的实际部署以及为满足用户和深度学习模型需求所需的高效实施策略。
本次Sky Hackathon的核心议题是“基于RAG技术创新构建智能对话机器人 ”。我们鼓励参赛者运用RAG(Retrieval-Augmented Generation)技术,并结合NVIDIA的SDK工具,自主设计和搭建对话机器人。通过这一过程,您将亲身体验从数据检索到生成对话的完整流程,并深入了解NVIDIA SDK在生成式AI领域的实际应用。
引自官方比赛说明
为了更好的开发本次活动的项目,我们先在本地电脑上搭建一个开发环境,进行本地项目开发。
如果有Ubuntu系统是最好的,如果没有的话,可以使用WSL2来代替
官方环境使用的应该是Python3.8,所以我们也使用这个版本
现在Ubuntu环境中安装好miniconda
mkdir -p ~/miniconda3
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda3/miniconda.sh
bash ~/miniconda3/miniconda.sh -b -u -p ~/miniconda3
rm -rf ~/miniconda3/miniconda.sh
~/miniconda3/bin/conda init bash
~/miniconda3/bin/conda init zsh
conda create -n nv python=3.8 -y
为了后续我们方便调试,我们使用jupyterlab来做为开发环境
conda activate nv
pip install jupyter
pip install ipykernel -i https://pypi.tuna.tsinghua.edu.cn/simple
python -m ipykernel install --name=nv_py38
进入到我们的工作目录并启动notebook
mkdir -p work/nv
cd work/nv/
jupyter-lib
我们点击上面的链接会在电脑上打开一个网站
打开一个终端,安装我们本次活动需要的依赖库
gradio
faiss-cpu==1.7.4
llama-index==0.9.22
langchain==0.0.352
langchain-nvidia-ai-endpoints==0.0.11
Baseline中使用的是ai-nemotron-4-340b-instruct
,因此我们点击这个banner,然后创建个API KEY
点击生成Key
将生成的key保存下来,但是注意他是会过期的
我们需要将上面的模型加载下来,并测试一下效果
llm = ChatNVIDIA(model="ai-nemotron-4-340b-instruct")
result = llm.invoke("what is nemo")
print(result.content)
运行后,可以看到输出效果。
embedder = NVIDIAEmbeddings(model="ai-embed-qa-4")
embedder.embed_query("test")
我们可以使用这次比赛准备的测数据,当然也可以使用自己的数据
也可以使用自己的数据
例如我这里添加了一个关于软考高级架构师考试的介绍的内容。
from tqdm import tqdm
from pathlib import Path
# 在这里我们读入文本数据并将它们准备到 vectorstore 中
ps = os.listdir("./zh_data/")
data = []
sources = []
docs_name = []
for p in ps:
if p.endswith('.txt'):
path2file="./zh_data/"+p
docs_name.append(path2file)
with open(path2file,encoding="utf-8") as f:
lines=f.readlines()
for line in lines:
if len(line)>=1:
data.append(line)
sources.append(path2file)
documents=[d for d in data if d != '\n']
len(data), len(documents), data[0]
from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(chunk_size=400, separator=" ")
docs = []
metadatas = []
生成embed
数据
for i, d in enumerate(documents):
splits = text_splitter.split_text(d)
#print(len(splits))
docs.extend(splits)
metadatas.extend([{"source": sources[i]}] * len(splits))
### 将创建好的embed存储到本地
store = FAISS.from_texts(docs, embedder , metadatas=metadatas)
store.save_local('./embed')
这一步需要花费一点时间,如果数据没有发生变化的情况下,可以不用重新执行。
读取本地生成好的的数据
vecstores = [FAISS.load_local(folder_path="./embed/", embeddings=embedder)]
设置default_FAISS() 函数,初始化空向量存储,通过convstore储存对话向量
设置aggregate_vstores()函数将有用的文档信息存储在 docstore变量中
embed_dims = len(embedder.embed_query("test"))
def default_FAISS():
'''Useful utility for making an empty FAISS vectorstore'''
return FAISS(
embedding_function=embedder,
index=IndexFlatL2(embed_dims),
docstore=InMemoryDocstore(),
index_to_docstore_id={},
normalize_L2=False
)
def aggregate_vstores(vectorstores):
## 初始化一个空的 FAISS 索引并将其他索引合并到其中
agg_vstore = default_FAISS()
for vstore in vectorstores:
agg_vstore.merge_from(vstore)
return agg_vstore
if 'docstore' not in globals():
docstore = aggregate_vstores(vecstores)
print(f"Constructed aggregate docstore with {len(docstore.docstore._dict)} chunks")
llm = ChatNVIDIA(model="ai-nemotron-4-340b-instruct") | StrOutputParser()
convstore = default_FAISS()
doc_names_string = "\n"
for doc_name in docs_name:
doc_names_string += doc_name+"\n"
def save_memory_and_get_output(d, vstore):
"""Accepts 'input'/'output' dictionary and saves to convstore"""
vstore.add_texts([
f"User previously responded with {d.get('input')}",
f"Agent previously responded with {d.get('output')}"
])
return d.get('output')
initial_msg = (
"Hello! I am a document chat agent here to help the user!"
f" I have access to the following documents: {doc_names_string}\n\nHow can I help you?"
)
chat_prompt = ChatPromptTemplate.from_messages([("system",
"You are a document chatbot. Help the user as they ask questions about documents."
" User messaged just asked: {input}\n\n"
" From this, we have retrieved the following potentially-useful info: "
" Conversation History Retrieval:\n{history}\n\n"
" Document Retrieval:\n{context}\n\n"
" (Answer only from retrieval. Only cite sources that are used. Make your response conversational.Reply must more than 100 words)"
), ('user', '{input}')])
## Utility Runnables/Methods
def RPrint(preface=""):
"""Simple passthrough "prints, then returns" chain"""
def print_and_return(x, preface):
print(f"{preface}{x}")
return x
return RunnableLambda(partial(print_and_return, preface=preface))
def docs2str(docs, title="Document"):
"""Useful utility for making chunks into context string. Optional, but useful"""
out_str = ""
for doc in docs:
doc_name = getattr(doc, 'metadata', {}).get('Title', title)
if doc_name:
out_str += f"[Quote from {doc_name}] "
out_str += getattr(doc, 'page_content', str(doc)) + "\n"
return out_str
## 将较长的文档重新排序到输出文本的中心, RunnableLambda在链中运行无参自定义函数 ,长上下文重排序(LongContextReorder)
long_reorder = RunnableLambda(LongContextReorder().transform_documents)
retrieval_chain = (
{'input' : (lambda x: x)}
| RunnableAssign({'history' : itemgetter('input') | convstore.as_retriever() | long_reorder | docs2str})
| RunnableAssign({'context' : itemgetter('input') | docstore.as_retriever() | long_reorder | docs2str})
| RPrint()
)
stream_chain = chat_prompt | llm
def chat_gen(message, history=[], return_buffer=True):
buffer = ""
##首先根据输入的消息进行检索
retrieval = retrieval_chain.invoke(message)
line_buffer = ""
## 然后流式传输stream_chain的结果
for token in stream_chain.stream(retrieval):
buffer += token
## 优化信息打印的格式
if not return_buffer:
line_buffer += token
if "\n" in line_buffer:
line_buffer = ""
if ((len(line_buffer)>84 and token and token[0] == " ") or len(line_buffer)>100):
line_buffer = ""
yield "\n"
token = " " + token.lstrip()
yield buffer if return_buffer else token
##最后将聊天内容保存到对话内存缓冲区中
save_memory_and_get_output({'input': message, 'output': buffer}, convstore)
chatbot = gr.Chatbot(value = [[None, initial_msg]])
demo = gr.ChatInterface(chat_gen, chatbot=chatbot).queue()
try:
demo.launch(debug=True, share=False, show_api=False, server_port=5000, server_name="0.0.0.0")
demo.close()
except Exception as e:
demo.close()
print(e)
raise e
运行后,我们在notebook中会看到一个界面,如果你觉得在这里操作不方便的话,也可以在浏览器中打开
至此,我们的基础环境已经准备好了,剩下就是好好的发挥自己的创建。来完成本次比赛了。
为什么要在本地拆创建环境呢?
主要要是本地次比赛为了不让大家太卷,服务器每天10点就关闭了,第二天9点才开启,我们上班的小伙伴表示时间根本不够用啊,因此在本地创建一个开发环境是非常必要的。
好了,环境搭建部分即使这些了,更多的内容,等我比赛完成后再和大家继续分享了。我是Tango,一个热爱分享各种技术的程序猿,我们下期见。
我正在参与2024腾讯技术创作特训营最新征文,快来和我瓜分大奖!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。