MindSearch踩坑心得

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

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

%%{init:{'theme':'forest', 'themeVariables':{'fontSize':'20px'}, "securityLevel": "loose"}}%% sequenceDiagram participant Client participant FastAPI participant EventSourceResponse participant init_agent participant MindSearchAgent participant SearcherAgent participant WebSearchGraph participant LLM participant ActionExecutor Client->>FastAPI: POST /solve (GenerationParams) activate FastAPI FastAPI->>init_agent: init_agent(lang, model_format, search_engine) activate init_agent init_agent->>LLM: Create/Get LLM instance init_agent->>ActionExecutor: Create BingBrowser executor init_agent->>MindSearchProtocol: Create protocol with prompts init_agent->>MindSearchAgent: Create agent(llm, protocol, searcher_cfg) init_agent-->>FastAPI: Return agent instance deactivate init_agent FastAPI->>MindSearchAgent: stream_chat(inputs) activate MindSearchAgent MindSearchAgent->>MindSearchProtocol: format(inner_step) MindSearchProtocol-->>MindSearchAgent: formatted prompt MindSearchAgent->>LLM: stream_chat(prompt) loop For each response LLM-->>MindSearchAgent: Stream response MindSearchAgent->>MindSearchProtocol: parse(response) MindSearchProtocol-->>MindSearchAgent: language, action alt Has code execution MindSearchAgent->>WebSearchGraph: execute_code(command) activate WebSearchGraph WebSearchGraph->>SearcherAgent: Create searcher agents activate SearcherAgent SearcherAgent->>MindSearchProtocol: format(question) MindSearchProtocol-->>SearcherAgent: formatted prompt SearcherAgent->>LLM: stream_chat(question) LLM-->>SearcherAgent: Stream response alt Has tool action SearcherAgent->>ActionExecutor: Execute tool action activate ActionExecutor note over ActionExecutor: Handles plugin execution note over ActionExecutor: - BingBrowser actions note over ActionExecutor: - Tool invocations note over ActionExecutor: - Action status tracking ActionExecutor-->>SearcherAgent: Action result deactivate ActionExecutor end SearcherAgent->>WebSearchGraph: Put result in queue deactivate SearcherAgent loop For each queued result WebSearchGraph->>WebSearchGraph: Process queue WebSearchGraph-->>MindSearchAgent: Yield (node_name, node, adj) end deactivate WebSearchGraph MindSearchAgent->>MindSearchAgent: _generate_reference() alt Has plugin action MindSearchAgent->>ActionExecutor: Execute plugin action activate ActionExecutor note over ActionExecutor: Handles plugin execution note over ActionExecutor: - Plugin invocations note over ActionExecutor: - Action status tracking ActionExecutor-->>MindSearchAgent: Action result deactivate ActionExecutor end end MindSearchAgent->>MindSearchAgent: convert_adjacency_to_tree() MindSearchAgent-->>FastAPI: Yield AgentReturn end deactivate MindSearchAgent FastAPI->>EventSourceResponse: Create SSE response EventSourceResponse-->>Client: Stream JSON responses deactivate FastAPI note over MindSearchAgent: AgentReturn includes: note over MindSearchAgent: - nodes note over MindSearchAgent: - adjacency_list (tree structure) note over MindSearchAgent: - inner_steps note over MindSearchAgent: - references note over MindSearchAgent: - adj (original adjacency list)

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 @ 2024-11-01 22:48  索美不达米亚  阅读(286)  评论(4)    收藏  举报