MindSearch踩坑心得

MindSearch允许llm生成类似jupyter notebook的代码片段自主的规划搜索路径,形成搜索图可以自由的控制最大迭代步数,这种灵活的特性使得的MindSearch搜索效果相比写死的代码要效果好很多。

MindSearch代码不多,但是调用很复杂,不行请看这个时序图,请格外关注WebSearchGraphMindSearchAgent两个类,MindSearchAgent是一切代理的总主管,WebSearchGraph是通过llm生成自主规划的搜索路径的小主管,由llm生成的代码就是通过WebSearchGraph规划搜索路径的。

MindSearchProtocolActionExecutorLLMWebSearchGraphSearcherAgentMindSearchAgentinit_agentEventSourceResponseFastAPIClientMindSearchProtocolActionExecutorLLMWebSearchGraphSearcherAgentMindSearchAgentinit_agentEventSourceResponseFastAPIClientHandles plugin execution- BingBrowser actions- Tool invocations- Action status trackingalt[Has tool action]loop[For each queued result]Handles plugin execution- Plugin invocations- Action status trackingalt[Has plugin action]alt[Has code execution]loop[For each response]AgentReturn includes:- nodes- adjacency_list (tree structure)- inner_steps- references- adj (original adjacency list)POST /solve (GenerationParams)init_agent(lang, model_format, search_engine)Create/Get LLM instanceCreate BingBrowser executorCreate protocol with promptsCreate agent(llm, protocol, searcher_cfg)Return agent instancestream_chat(inputs)format(inner_step)formatted promptstream_chat(prompt)Stream responseparse(response)language, actionexecute_code(command)Create searcher agentsformat(question)formatted promptstream_chat(question)Stream responseExecute tool actionAction resultPut result in queueProcess queueYield (node_name, node, adj)_generate_reference()Execute plugin actionAction resultconvert_adjacency_to_tree()Yield AgentReturnCreate SSE responseStream JSON responses

MindSearch的llm调用库lagent是他们团队手搓原创的,连siliconflow调用qwen模型都不支持,为了调通只能魔改lagent,这里就不赘述。文档里支持的四个搜索API:GoogleSearchDuckDuckGoSearchBraveSearchBingSearch,推荐用GoogleSearchDuckDuckGoSearchDuckDuckGoSearch免费需要设置proxy,GoogleSearch是通过serper.dev绑卡赠送一次性额度,BraveSearch绑卡有免费月额度但是并发1,BingSearch要开通azure绑定Visa卡。
如果单纯调用LLM的API,不需要本地推理,可以把requirements.txt里面的lmdeploytransformers注释掉不用安装。
用原版的提示词效果最好的是GPT4o和internlm2.5,为了让qwen和deepseek也能跑通,需要修改提示词文件mindsearch_prompt.py,修改GRAPH_PROMPT_CN给llm增加一个示例,下面是我改的:

## 注意事项

1. 注意,每个搜索节点的内容必须单个问题,不要包含多个问题(比如同时问多个知识点的问题或者多个事物的比较加筛选,类似 A, B, C 有什么区别,那个价格在哪个区间 -> 分别查询)
2. 不要杜撰搜索结果,要等待代码返回结果
3. 同样的问题不要重复提问,可以在已有问题的基础上继续提问
4. 一次输出中,不要包含多个代码块,每次只能有一个代码块
5. 每个代码块应该放置在一个代码块标记中,同时生成完代码后添加一个<|action_end|>标志,如下所示:
    <|action_start|><|interpreter|>```python
    # 你的代码块
    ```<|action_end|>
6. 一个代码块中,一个节点必须和一条边相连,并最后调用node方法获取结果。
7. 整个图构建最后一次回复应该是添加node_name为'response'的 response 节点,必须添加 response 节点,不要添加其他节点。 添加 response 节点的时候,要单独添加,不要和其他节点一起添加,不能同时添加 response 节点和其他节点
示例(注意这是多次的回答而不是单次的):
<|action_start|><|interpreter|>```python
graph = WebSearchGraph()
# 添加根节点
graph.add_root_node(node_content="人工智能的最新进展有哪些?\", node_name="root")
# 添加搜索节点
graph.add_node(node_name="key_areas", node_content="人工智能研究的关键领域有哪些?")
# 添加边,表示搜索节点之间的关系
graph.add_edge(start_node="key_areas", end_node="trends")
# 读取节点的查询结果并打印出来
graph.node("key_areas")
```<|action_end|>
<|action_start|><|interpreter|>```python
# 添加根节点
graph.add_node(node_name="trends", node_content="机器学习的最新趋势是什么?")
graph.add_edge(start_node="key_areas", end_node="trends")
graph.node("trends")
```<|action_end|>
<|action_start|><|interpreter|>```python
graph.add_response_node(node_name="response")
graph.add_edge(start_node="trends", end_node="response")
```<|action_end|>

从这个例子可以看出,graph.node()方法必须调用,否则llm就获取不到搜索代理返回的信息,导致结果瞎编乱造。顺带一提,internlm2.5竟然把from ilagent.agents.python_web import WebSearchGraph炼在模型里,光靠提示词大模型是不可能写出这行代码的,怪不得配合MindSearch效果好。
我还碰到一个奇怪的问题,有的时候只会执行一次搜索就结束了,但是代码明显没跑完,因为他没有走到最后一个response节点,我怀疑是mindsearch_agent.py代码导致的,这里修改确保graph已经创建成功在执行后续操作。

@@ -373,6 +402,20 @@ class MindSearchAgent(BaseAgent):
                                            args=(command, ))
         producer_thread.start()
 
+        # 等待确保 graph 对象被创建
+        max_retries = 10
+        retry_count = 0
+        while retry_count < max_retries:
+            graph = self.local_dict.get('graph')
+            if graph is not None:
+                break
+            time.sleep(0.1)  # 短暂等待
+            retry_count += 1
+            logger.info(f"Waiting for graph object, attempt {retry_count}")
+        
+        if self.local_dict.get('graph') is None:
+            raise RuntimeError("Failed to initialize graph object")
+
         responses = defaultdict(list)
         ordered_nodes = []
         active_node = None
posted @   索美不达米亚  阅读(207)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示