进阶任务六 MindSearch CPU-only 版部署
任务:
MindSearch 部署到Github Codespace 和 Hugging Face Space
笔者部署在 HuggingFace 的 MindSearch 模型链接为:ccdgyro/MindSearch
1 开发机创建和环境准备
首先,按照文档创建基于 GitHub CodeSpace 的 blank template 开发机,我们会得到一个基于 Web 的 VS Code,其基本操作和客户端 VS Code 基本一致。
然后,我们准备 MindSearch 相关源码,并创建一个虚拟环境来准备 MindSearch 的环境依赖:
mkdir -p /workspaces/mindsearch
cd /workspaces/mindsearch
git clone https://github.com/InternLM/MindSearch.git
cd MindSearch && git checkout b832275 && cd ..
# 创建环境
conda create -n mindsearch python=3.10 -y
# 激活环境
conda init
source ~/.bashrc
conda activate mindsearch
# 安装依赖
pip install -r /workspaces/mindsearch/MindSearch/requirements.txt
真没想到这个 CodeSpace 这么流畅!
2 硅基流动 API Key 获取
按照文档指引,准备硅基流动 API Key:
3 启动 MindSearch
3.1 启动后端
硅基流动 API 的相关配置已经集成于 MindSearch 中,故可以直接执行下面的代码来启动 MindSearch 的后端,即一个 FastAPI 服务:
export SILICON_API_KEY=第二步中复制的密钥
conda init
source ~/.bashrc
conda activate mindsearch
cd /workspaces/mindsearch/MindSearch
python -m mindsearch.app --lang cn --model_format internlm_silicon --search_engine DuckDuckGoSearch
注意:
conda 启动 mindsearch 环境如果报错,需要先进行conda init
和source ~/.bashrc
3.2 启动前端
启动后断后,打开新终端运行如下命令来启动 MindSearch 的前端,即一个 Gradio Web 应用:
conda activate mindsearch
cd /workspaces/mindsearch/MindSearch
python frontend/mindsearch_gradio.py
然后就可以和部署好的 MindSearch 进行交互,我们可以看到 MindSearch 的 Planner规划器 和 Searcher搜索器很好的解决了我们的问题,能给出丰富且高质量的回答,这里我们简单问了两个问题:
- 请尽可能详细的介绍一下 MindSearch
- 请尽可能详细的介绍一下 Arxiv
以第二个问题为例,我们发现 MindSearch 首先使用 Planner 进行了规划,将问题分解成了一系列子问题来搜索,最终整合回答,得到了一个质量相当高的解决方案,我们也可以在 Searcher 部分探索 MindSearch 给出这类答案的路径和方案:
from ilagent.agents.python_web import WebSearchGraph
graph = WebSearchGraph()
graph.add_root_node(node_content="请尽可能详细的介绍一下 Arxiv", node_name="root")
# 添加关于 Arxiv 的历史背景的节点
graph.add_node(
node_name="arxiv历史",
node_content="Arxiv 的历史背景是什么?")
graph.add_edge(start_node="root", end_node="arxiv历史")
# 添加关于 Arxiv 的主要功能的节点
graph.add_node(
node_name="arxiv功能",
node_content="Arxiv 的主要功能有哪些?")
graph.add_edge(start_node="root", end_node="arxiv功能")
# 添加关于如何使用 Arxiv 的节点
graph.add_node(
node_name="如何使用arXive",
node_content="如何有效地使用 ArXive?")
graph.add_edge(start_node="root", end_node="如何使用arXive")
# 添加关于 ArXive 对科学界影响的节点
graph.add_node(
node_name='影响',
node_content='ArXiV对科学界有什么影响?')
graph.add_edge(start_node='root', end_node='影响')
# 请求所有节点的信息以进行下一步处理
[print(graph.node(name)) for name in ["arxiv历史", "arxiv功能", "如何使用arXive", '影响']]
ArXiv(发音为“archive”)是一个由康奈尔大学运营的预印本服务器,旨在促进科学、技术、数学和物理领域的知识共享。自1991年启动以来,ArXiv已成为全球最大的开放获取科学文献库之一,涵盖了广泛的学科领域[[arxiv历史]]。
### 历史背景
ArXiv最初由保罗·格伦德曼(Paul Ginsparg)和布莱恩·纳尔逊(Brian Nelson)于1989年提出,并于1991年正式上线。其初衷是为了提供一个平台,让科学家能够快速分享他们的研究成果,而无需等待同行评审或出版过程[[arxiv历史]]。
### 主要功能
1. **开放获取**:
- ArXiv提供了一个免费的在线平台,使得研究人员可以立即发布他们的预印本论文[[arxiv功能]]。
2. **跨学科覆盖**:
- 该平台涵盖了物理学、数学、计算机科学、生物学等多个学科领域[[arxiv功能]]。
3. **社区互动**:
- 用户可以通过评论系统与其他研究人员交流和讨论论文内容[[arxiv功能]]。
4. **自动分类系统**:
- ArXiv使用自动分类系统将提交的论文分配到相应的学科类别中[[arxiv功能]]。
### 使用方式
1. **提交论文**:
- 研究人员可以通过注册账号并上传他们的研究论文到对应的学科类别中[[如何使用arXive]][[如何上传] [[如何使用arXive]]]。
2. **搜索与浏览**:
- 用户可以通过关键词搜索来查找特定主题的论文或浏览最新的研究进展[[如何使用arXive]][[如何搜索] [[如何使用arXive]]]。
3. **引用与跟踪更新**:
- ArXiV支持引用管理工具如BibTeX等来帮助用户管理和引用已发表的文献;同时用户可以设置邮件通知以跟踪他们感兴趣的研究项目的最新进展[[如何使用 ar X iV]][[如何引用] [[如何使用 ar X iV]]
### 对科学界的影响
1. **加速科研进程**:
- ArXiV允许研究人员迅速分享他们的发现,从而加速了整个科研社区的知识传播和创新过程[[影响]][[加速科研进程]][[促进知识传播]]
2. **提高透明度与可访问性**:
- ArXiV确保所有科学研究都是公开可访问的,这不仅提高了研究的透明度还扩大了研究的受众范围{{影响}}.
3. **推动跨学科合作与发展**:
由于其跨学科覆盖的特性, Ar X i V促进了不同领域之间的交流与合作,推动了交叉科学的进步和发展{{影响}}.
综上所述, Ar X i V作为一个开放获取的平台,在促进科学研究的知识共享方面发挥了重要作用,极大地推动了全球学术界的创新与发展
4 HuggingFace Space 部署
首先按照要求创建 HuggingFace Space,这里我们选择最基本的免费 CPU 即可:
然后,按照文档要求为 HuggingFace Space 添加硅基流动的 API key:
然后,回到之间启动的 Github CodeSpace,准备一下用于 HuggingFace 部署的文件,主要是 Gradio 界面相关的代码:
# 创建新目录
mkdir -p /workspaces/mindsearch/mindsearch_deploy
# 准备复制文件
cd /workspaces/mindsearch
cp -r /workspaces/mindsearch/MindSearch/mindsearch /workspaces/mindsearch/mindsearch_deploy
cp /workspaces/mindsearch/MindSearch/requirements.txt /workspaces/mindsearch/mindsearch_deploy
# 创建 app.py 作为程序入口
touch /workspaces/mindsearch/mindsearch_deploy/app.py
其中,app.py
内容如下,相比参考文档,对 Gradio 界面进行了一定程度的美化:
import json
import os
import gradio as gr
import requests
from lagent.schema import AgentStatusCode
os.system("python -m mindsearch.app --lang cn --model_format internlm_silicon &")
PLANNER_HISTORY = []
SEARCHER_HISTORY = []
def rst_mem(history_planner: list, history_searcher: list):
'''
Reset the chatbot memory.
'''
history_planner = []
history_searcher = []
if PLANNER_HISTORY:
PLANNER_HISTORY.clear()
return history_planner, history_searcher
def format_response(gr_history, agent_return):
if agent_return['state'] in [
AgentStatusCode.STREAM_ING, AgentStatusCode.ANSWER_ING
]:
gr_history[-1][1] = agent_return['response']
elif agent_return['state'] == AgentStatusCode.PLUGIN_START:
thought = gr_history[-1][1].split('```')[0]
if agent_return['response'].startswith('```'):
gr_history[-1][1] = thought + '\n' + agent_return['response']
elif agent_return['state'] == AgentStatusCode.PLUGIN_END:
thought = gr_history[-1][1].split('```')[0]
if isinstance(agent_return['response'], dict):
gr_history[-1][
1] = thought + '\n' + f'```json\n{json.dumps(agent_return["response"], ensure_ascii=False, indent=4)}\n```' # noqa: E501
elif agent_return['state'] == AgentStatusCode.PLUGIN_RETURN:
assert agent_return['inner_steps'][-1]['role'] == 'environment'
item = agent_return['inner_steps'][-1]
gr_history.append([
None,
f"```json\n{json.dumps(item['content'], ensure_ascii=False, indent=4)}\n```"
])
gr_history.append([None, ''])
return
def predict(history_planner, history_searcher):
def streaming(raw_response):
for chunk in raw_response.iter_lines(chunk_size=8192,
decode_unicode=False,
delimiter=b'\n'):
if chunk:
decoded = chunk.decode('utf-8')
if decoded == '\r':
continue
if decoded[:6] == 'data: ':
decoded = decoded[6:]
elif decoded.startswith(': ping - '):
continue
response = json.loads(decoded)
yield (response['response'], response['current_node'])
global PLANNER_HISTORY
PLANNER_HISTORY.append(dict(role='user', content=history_planner[-1][0]))
new_search_turn = True
url = 'http://localhost:8002/solve'
headers = {'Content-Type': 'application/json'}
data = {'inputs': PLANNER_HISTORY}
raw_response = requests.post(url,
headers=headers,
data=json.dumps(data),
timeout=20,
stream=True)
for resp in streaming(raw_response):
agent_return, node_name = resp
if node_name:
if node_name in ['root', 'response']:
continue
agent_return = agent_return['nodes'][node_name]['detail']
if new_search_turn:
history_searcher.append([agent_return['content'], ''])
new_search_turn = False
format_response(history_searcher, agent_return)
if agent_return['state'] == AgentStatusCode.END:
new_search_turn = True
yield history_planner, history_searcher
else:
new_search_turn = True
format_response(history_planner, agent_return)
if agent_return['state'] == AgentStatusCode.END:
PLANNER_HISTORY = agent_return['inner_steps']
yield history_planner, history_searcher
return history_planner, history_searcher
with gr.Blocks(css="""
.gr-button {
background-color: #4A90E2;
color: white;
border-radius: 10px;
padding: 10px 20px;
font-size: 16px;
border: none;
}
.gr-button:hover {
background-color: #357ABD;
}
.gr-textbox {
border: 2px solid #4A90E2;
border-radius: 5px;
padding: 10px;
font-size: 14px;
}
.gr-row {
margin-bottom: 10px;
}
.gr-chatbot {
border: 2px solid #4A90E2;
border-radius: 10px;
padding: 10px;
}
""") as demo:
gr.HTML("""<h1 align="center" style="color:#4A90E2;">MindSearch Gradio Demo</h1>""")
gr.HTML("""<p style="text-align: center; font-family: Arial, sans-serif; color: #333;">MindSearch is an open-source AI Search Engine Framework with Perplexity.ai Pro performance. You can deploy your own Perplexity.ai-style search engine using either closed-source LLMs (GPT, Claude) or open-source LLMs (InternLM2.5-7b-chat).</p>""")
gr.HTML("""
<div style="text-align: center; font-size: 16px; margin-bottom: 20px;">
<a href="https://github.com/InternLM/MindSearch" style="margin-right: 15px; text-decoration: none; color: #4A90E2;">🔗 GitHub</a>
<a href="https://arxiv.org/abs/2407.20183" style="margin-right: 15px; text-decoration: none; color: #4A90E2;">📄 Arxiv</a>
<a href="https://huggingface.co/papers/2407.20183" style="margin-right: 15px; text-decoration: none; color: #4A90E2;">📚 Hugging Face Papers</a>
<a href="https://huggingface.co/spaces/internlm/MindSearch" style="text-decoration: none; color: #4A90E2;">🤗 Hugging Face Demo</a>
</div>
""")
with gr.Row():
with gr.Column(scale=10):
with gr.Row():
with gr.Column():
planner = gr.Chatbot(label='Planner',
height=700,
show_label=True,
show_copy_button=True,
bubble_full_width=True,
render_markdown=True,
elem_id="planner")
with gr.Column():
searcher = gr.Chatbot(label='Searcher',
height=700,
show_label=True,
show_copy_button=True,
bubble_full_width=True,
render_markdown=True,
elem_id="searcher")
with gr.Row():
user_input = gr.Textbox(show_label=False,
placeholder='帮我搜索一下 InternLM 开源体系',
lines=5,
container=False,
elem_id="user_input")
with gr.Row():
with gr.Column(scale=2):
submitBtn = gr.Button('Submit', elem_id="submitBtn")
with gr.Column(scale=1, min_width=20):
emptyBtn = gr.Button('Clear History', elem_id="emptyBtn")
def user(query, history):
return '', history + [[query, '']]
submitBtn.click(user, [user_input, planner], [user_input, planner],
queue=False).then(predict, [planner, searcher],
[planner, searcher])
emptyBtn.click(rst_mem, [planner, searcher], [planner, searcher],
queue=False)
demo.queue()
demo.launch(server_name='0.0.0.0',
server_port=7860,
inbrowser=True,
share=True)
最后,将 /root/mindsearch/mindsearch_deploy
目录下的文件(使用 git)提交到 HuggingFace Space 即可完成部署。
而在提交前,还需要一定的配置,首先从 HuggingFace 创建一个具有写权限的 token,这可以在 HuggingFace 的账户 settings 界面找到入口:
然后,前往 Github Codespac,从 HuggingFace 把空的代码仓库 clone 到 Github CodeSpace:
# 拉取 HuggingFace Space 到 Github CodeSpace
cd /workspaces/codespaces-blank
git clone https://huggingface.co/spaces/ccdgyro/MindSearchCamp3
cd MindSearchCamp3
# 首先,查看当前项目中已经配置的远程仓库,以确认 space 是否存在
git remote -v
# 添加 space 远程仓库,如果确认 space 远程仓库不存在,需要先添加它
git remote add space https://ccdgyro:<HuggingFace token>@huggingface.co/spaces/ccdgyro/MindSearchCamp3
# 然后,把token挂到仓库上,让自己有写权限
git remote set-url space https://ccdgyro:<HuggingFace token>@huggingface.co/spaces/ccdgyro/MindSearchCamp3
git remote set-url space https://ccdgyro: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@huggingface.co/spaces/ccdgyro/MindSearchCamp3
现在,Github CodeSpace 就是本地仓库,Huggingface Space是远程仓库,接下来使用方法就和常规的 git 一样了:
cd /workspaces/codespaces-blank/MindSearchCamp3
# 把刚才准备的文件都copy进来
cp -r /workspaces/mindsearch/mindsearch_deploy/* .
最终文件结构为:
注意:MindSearch文件夹其实是 MindSearch 项目中的一个子文件夹,如果把这个MindSearch 的整个目录 copy 进来会有很多问题(git submodule无法提交代码,space中项目启动失败等)。
最后,我们将代码用 git,从 Github CodeSpace 提交到 HuggingFace Space 就可以完成项目的启动,然后便可以前往 HuggingFace 的 space 地址体验 MindSearch 了。
git add .
git commit -m "update"
git push space # 要 push 到 space 上
MindSearch 很好的回答了一个具有较新时效性的问题:请问你了解开发了《黑神话:悟空》这款游戏的“游戏科学”公司吗? 其 Planner 对这个问题进行了很合理的分解,并运用 Searcher,基于网络资料实现了高效可靠的问答。
from ilagent.agents.python_web import WebSearchGraph
graph = WebSearchGraph()
graph.add_root_node(node_content="请问你了解开发了《黑神话:悟空》这款游戏的“游戏科学”公司吗?", node_name="root")
# 添加节点查询《黑神话:悟空》的开发商
graph.add_node(
node_name="游戏科学",
node_content="《黑神话:悟空》的开发商是谁?")
graph.add_edge(start_node="root", end_node="游戏科学")
# 查询关于“游戏科学”公司的信息
graph.add_node(
node_name="game_science_info",
node_content=f"关于‘游戏科学’公司的信息是什么?")
graph.add_edge(start_node="游戏科学", end_node="game_science_info")
# 获取节点信息以确认是否需要进一步的信息收集或处理
graph.node("game_science"), graph.node("game_science_info")
而在回答中,可以看到 MindSearch 也可以验证存在潜在错误的问题,他甚至先查询了这款游戏的开发商是否是“游戏科学”:
为了回答这个问题,我们需要先确认《黑神话:悟空》的开发商,然后了解这家公司。
为此,我们也尝试用一个误导性的问题测试我们的 MindSearch:请问你了解开发了《黑神话:悟空》这款游戏的“米哈游”公司吗? 发现 MindSearch 能很好的进行事实核查,纠正我们提问中的错误,精彩!!
最后,笔者部署在 HuggingFace 的 MindSearch 模型链接为:ccdgyro/MindSearch。