一、关卡任务
基础任务
-
选择 InternStudio 算力平台 50% A100 的 cuda 12.2 的开发机,并使用ssh vscode 远程连接到开发机。
-
MindSearch是基于LLM Web搜索引擎的多智能体框架(如 Perplexity.ai Pro和SearchGPT)
比如,对于多步骤的复杂问题,模型能够分析用户需求,先搜索嫦娥 6 号的技术难点、再针对每一个技术难点搜索对应的解决方案,再从任务目标、技术手段、科学成果、国际合作 4 个方面对比阿波罗 11 号登月计划,最后总结我国探月成功的贡献。
MindSearch 依靠其独特的路径建模方式在 agent 层面上进行并发,在 3 分钟内就阅读了 300+ 相关网页,给出了最终的答案。而且,与其他 AI 搜索引擎不同的是,MindSearch 完全对外展示了问题解决过程中的 AI 的思考过程和搜索中间结果,用户可以根据自己的需要查看每一次的搜索过程和模型阅读的网页,增加了对整个内容的可解释性和可信度。
在面对更大规模的信息搜集整理任务时,需面对三重挑战:
(1)搜索引擎难以一次性精确返回复杂请求所需的全量网络信息
(2)待整合的信息散布在多个网页中,信息非常嘈杂
(3)大量的网络内容很容易超过 LLM 可处理的上下文长度
为了解决这些难题,研发团队提出了名为 MindSearch (思·索)的多智能体框架。顾名思义,MindSearch 模拟人的思维过程,先对问题进行充分的“思”考,再进行完整的信息搜“索”,同时将整个系统设计成“思”和“索”两个智能体进行分工协作,交互完成任务。
如上图所示,由规划器专注“思”考,分析问题、拆解任务,并将任务分配给检索器。而检索器负责层次化检“索”网络信息,并回答每个子问题。于此同时,MindSearch 的多智能体设计也将处理大量信息的负载分配给了不同的智能体,使得整个框架能够处理更长的上下文(超过 300 个网页)。
二、实验过程
2.1 使用免费的搜索接口
2.1.1 激活环境
小助手提前帮大家安装好了环境,只需要一步一步按照下面的步骤便可以启动 MindSearch。
conda activate /share/pre_envs/mindsearch
2.1.2 启动后端
打开新终端运行以下命令启动推理后端,使用入门岛中学到的方式使用 vscode 或者 ssh 将端口映射到本地 8002 端口。
conda activate /share/pre_envs/mindsearch
cd /share/demo/MindSearchDuck
python -m mindsearch.app --lang cn --model_format internstudio_server
2.1.3 启动前端
打开新终端运行以下命令启动前端,使用入门岛中学到的方式使用 vscode 或者 ssh 将端口映射到本地 7860 端口。
ssh -CNg -L 7860:127.0.0.1:7860 root@ssh.intern-ai.org.cn -p 35177
conda activate /share/pre_envs/mindsearch
cd /share/demo/MindSearchDuck
python run.py
本地浏览器打开 http://localhost:7860 地址,开始 MindSearch 之旅。
2.2 使用 Bing 的接口
Bing API Key 获取网址(尽量选高一点的定价):Web Search API | Microsoft Bing
学生可以使用学术认证,获得免费的使用资格:
如果没办法注册成功,还可以使用谷歌搜索API或者DuckDuckGo。
DuckDuckGo这个是默认的选项。
2.3 遇到问题
RuntimeError: Operation on the closed queue is forbidden
解决方案:
在mindsearch/app.py
找到
async def generate():
try:
queue = janus.Queue()
# 使用 run_in_executor 将同步生成器包装成异步生成器
def sync_generator_wrapper():
try:
for response in agent.stream_chat(inputs):
queue.sync_q.put(response)
except Exception as e:
logging.exception(
f'Exception in sync_generator_wrapper: {e}')
finally:
# 确保在发生异常时队列中的所有元素都被消费
queue.sync_q.put(None)
async def async_generator_wrapper():
loop = asyncio.get_event_loop()
loop.run_in_executor(None, sync_generator_wrapper)
while True:
response = await queue.async_q.get()
if response is None: # 确保消费完所有元素
break
yield response
if not isinstance(
response,
tuple) and response.state == AgentStatusCode.END:
break
async for response in async_generator_wrapper():
if isinstance(response, tuple):
agent_return, node_name = response
else:
agent_return = response
node_name = None
origin_adj = deepcopy(agent_return.adjacency_list)
adjacency_list = convert_adjacency_to_tree(
agent_return.adjacency_list, 'root')
assert adjacency_list[
'name'] == 'root' and 'children' in adjacency_list
agent_return.adjacency_list = adjacency_list['children']
agent_return = asdict(agent_return)
agent_return['adj'] = origin_adj
response_json = json.dumps(dict(response=agent_return,
current_node=node_name),
ensure_ascii=False)
yield {'data': response_json}
# yield f'data: {response_json}\n\n'
except Exception as exc:
msg = 'An error occurred while generating the response.'
logging.exception(msg)
response_json = json.dumps(
dict(error=dict(msg=msg, details=str(exc))),
ensure_ascii=False)
yield {'data': response_json}
# yield f'data: {response_json}\n\n'
finally:
queue.close()
await queue.wait_closed()
替换为
async def generate():
try:
queue = janus.Queue()
stop_event = asyncio.Event()
# 使用 run_in_executor 将同步生成器包装成异步生成器
# Wrapping a sync generator as an async generator using run_in_executor
def sync_generator_wrapper():
try:
for response in agent.stream_chat(inputs):
@@ -71,21 +72,22 @@ def sync_generator_wrapper():
logging.exception(
f'Exception in sync_generator_wrapper: {e}')
finally:
# 确保在发生异常时队列中的所有元素都被消费
# Notify async_generator_wrapper that the data generation is complete.
queue.sync_q.put(None)
async def async_generator_wrapper():
loop = asyncio.get_event_loop()
loop.run_in_executor(None, sync_generator_wrapper)
while True:
response = await queue.async_q.get()
if response is None: # 确保消费完所有元素
if response is None: # Ensure that all elements are consumed
break
yield response
if not isinstance(
response,
tuple) and response.state == AgentStatusCode.END:
break
stop_event.set() # Inform sync_generator_wrapper to stop
async for response in async_generator_wrapper():
if isinstance(response, tuple):
@@ -115,6 +117,8 @@ async def async_generator_wrapper():
yield {'data': response_json}
# yield f'data: {response_json}\n\n'
finally:
await stop_event.wait(
) # Waiting for async_generator_wrapper to stop
queue.close()
await queue.wait_closed()
如果因为远程机环境不能修改,可以重新去MindSearch仓库下载运行。