当前位置: 首页 > article >正文

AI搜索之问题分解 智谱清言、Felo、360 AI搜索、mindsearch

问题分解是当前大部分AI搜索应用具备的高级搜索功能,通过对用户问题分解、解决、总结,能提供更完备、更准确的搜索结果。

我们测试一个问题案例——“哆啦A梦的作者还有什么别的作品?”,该问题是一个典型的两跳问题,需要做两次推理(第一次是“哆啦A梦的作者是谁?”,第二次是“该作者还有什么别的作品”)

智谱清言

![[Pasted image 20241218182750.png]]

Felo

![[Pasted image 20241218182651.png]]

360 AI搜索

![[Pasted image 20241218222123.png]]

头一次见在AI搜索里插广告的。。。,360厉害了

mindsearch

![[Pasted image 20241218170226.png]]

原理

![[Pasted image 20241218183037.png]]

步骤如下:

  1. Web planner:将当前问题分解成子问题,利用LLM的代码生成功能,将原问题、子问题建立图谱
  2. 一旦子问题建立成功,执行web searcher。调用搜索引擎,获得title、url、summary
  3. 按照title、url、summary信息,利用LLM来选择网页,执行爬虫程序,拿到网页内的所有内容
  4. 生成单个子问题的回复
  5. 不断迭代
  6. 基于所有中间结果,生成最终回答

代码

目前开源的具备问题分解的AI搜索工作非常少,mindsearch是一个,难能可贵。

服务启动

python -m mindsearch.app --lang en --model_format internlm_silicon --search_engine DuckDuckGoSearch --port 18882
建议在硅基流动上新建一个api_key,可免费调用internlm/internlm2_5-7b-chat

API调用

python backend_example.py

agent代码
  • 核心代码:MindSearch/mindsearch/agent/init.py
    mode = "async" if use_async else "sync"
    llm = LLM.get(model_format, {}).get(mode)
    if llm is None:
        llm_cfg = deepcopy(getattr(llm_factory, model_format))
        if llm_cfg is None:
            raise NotImplementedError
        if use_async:
            cls_name = (
                llm_cfg["type"].split(".")[-1] if isinstance(
                    llm_cfg["type"], str) else llm_cfg["type"].__name__)
            llm_cfg["type"] = f"lagent.llms.Async{cls_name}"
        llm = create_object(llm_cfg)
        LLM.setdefault(model_format, {}).setdefault(mode, llm)

    date = datetime.now().strftime("The current date is %Y-%m-%d.")
    plugins = [(dict(
        type=AsyncWebBrowser if use_async else WebBrowser,
        searcher_type=search_engine,
        topk=6,
        secret_id=os.getenv("TENCENT_SEARCH_SECRET_ID"),
        secret_key=os.getenv("TENCENT_SEARCH_SECRET_KEY"),
    ) if search_engine == "TencentSearch" else dict(
        type=AsyncWebBrowser if use_async else WebBrowser,
        searcher_type=search_engine,
        topk=6,
        api_key=os.getenv("WEB_SEARCH_API_KEY"),
    ))]
    agent = (AsyncMindSearchAgent if use_async else MindSearchAgent)(
        llm=llm,
        template=date,
        output_format=InterpreterParser(
            template=GRAPH_PROMPT_CN if lang == "cn" else GRAPH_PROMPT_EN),
        searcher_cfg=dict(
            llm=llm,
            plugins=plugins,
            template=date,
            output_format=PluginParser(
                template=searcher_system_prompt_cn
                if lang == "cn" else searcher_system_prompt_en,
                tool_info=get_plugin_prompt(plugins),
            ),
            user_input_template=(searcher_input_template_cn if lang == "cn"
                                 else searcher_input_template_en),
            user_context_template=(searcher_context_template_cn if lang == "cn"
                                   else searcher_context_template_en),
        ),
        summary_prompt=FINAL_RESPONSE_CN
        if lang == "cn" else FINAL_RESPONSE_EN,
        max_turn=10,
    )
  • Web Planner
    对应的prompt为
        output_format=InterpreterParser(
            template=GRAPH_PROMPT_CN if lang == "cn" else GRAPH_PROMPT_EN),

output_format的作用原理是联合聊天历史、template等信息构成prompt,送到LLM中

class StreamingAgent(StreamingAgentMixin, Agent):
    """Base streaming agent class"""

    def forward(self, *message: AgentMessage, session_id=0, **kwargs):
        formatted_messages = self.aggregator.aggregate(
            self.memory.get(session_id),
            self.name,
            self.output_format,
            self.template,
        )
        for model_state, response, _ in self.llm.stream_chat(
            formatted_messages, session_id=session_id, **kwargs
        ):
            yield AgentMessage(
                sender=self.name,
                content=response,
                formatted=self.output_format.parse_response(response),
                stream_state=model_state,
            ) if self.output_format else (model_state, response)

GRAPH_PROMPT_CN的具体内容为

GRAPH_PROMPT_CN = """## 人物简介
你是一个可以利用 Jupyter 环境 Python 编程的程序员。你可以利用提供的 API 来构建 Web 搜索图,最终生成代码并执行。

## API 介绍

下面是包含属性详细说明的 `WebSearchGraph` 类的 API 文档:

### 类:`WebSearchGraph`

此类用于管理网络搜索图的节点和边,并通过网络代理进行搜索。

#### 初始化方法

初始化 `WebSearchGraph` 实例。

**属性:**

- `nodes` (Dict[str, Dict[str, str]]): 存储图中所有节点的字典。每个节点由其名称索引,并包含内容、类型以及其他相关信息。
- `adjacency_list` (Dict[str, List[str]]): 存储图中所有节点之间连接关系的邻接表。每个节点由其名称索引,并包含一个相邻节点名称的列表。


#### 方法:`add_root_node`

添加原始问题作为根节点。
**参数:**

- `node_content` (str): 用户提出的问题。
- `node_name` (str, 可选): 节点名称,默认为 'root'。


#### 方法:`add_node`

添加搜索子问题节点并返回搜索结果。
**参数:

- `node_name` (str): 节点名称。
- `node_content` (str): 子问题内容。

**返回:**

- `str`: 返回搜索结果。


#### 方法:`add_response_node`

当前获取的信息已经满足问题需求,添加回复节点。

**参数:**

- `node_name` (str, 可选): 节点名称,默认为 'response'。


#### 方法:`add_edge`

添加边。

**参数:**

- `start_node` (str): 起始节点名称。
- `end_node` (str): 结束节点名称。


#### 方法:`reset`

重置节点和边。


#### 方法:`node`

获取节点信息。

```python
def node(self, node_name: str) -> str
```

**参数:**

- `node_name` (str): 节点名称。

**返回:**

- `str`: 返回包含节点信息的字典,包含节点的内容、类型、思考过程(如果有)和前驱节点列表。

## 任务介绍
通过将一个问题拆分成能够通过搜索回答的子问题(没有关联的问题可以同步并列搜索),每个搜索的问题应该是一个单一问题,即单个具体人、事、物、具体时间点、地点或知识点的问题,不是一个复合问题(比如某个时间段), 一步步构建搜索图,最终回答问题。

## 注意事项

1. 注意,每个搜索节点的内容必须单个问题,不要包含多个问题(比如同时问多个知识点的问题或者多个事物的比较加筛选,类似 A, B, C 有什么区别,那个价格在哪个区间 -> 分别查询)
2. 不要杜撰搜索结果,要等待代码返回结果
3. 同样的问题不要重复提问,可以在已有问题的基础上继续提问
4. 添加 response 节点的时候,要单独添加,不要和其他节点一起添加,不能同时添加 response 节点和其他节点
5. 一次输出中,不要包含多个代码块,每次只能有一个代码块
6. 每个代码块应该放置在一个代码块标记中,同时生成完代码后添加一个<|action_end|>标志,如下所示:
    <|action_start|><|interpreter|>```python
    # 你的代码块
    ```<|action_end|>
7. 最后一次回复应该是添加node_name为'response'的 response 节点,必须添加 response 节点,不要添加其他节点
"""

期望LLM生成图结构的问题解决代码,实现原问题、子问题之间的依赖,具体的图结构实现见MindSearch/mindsearch/agent/graph.py)

  • Web Searcher
    相关的配置为
        searcher_cfg=dict(
            llm=llm,
            plugins=plugins,
            template=date,
            output_format=PluginParser(
                template=searcher_system_prompt_cn
                if lang == "cn" else searcher_system_prompt_en,
                tool_info=get_plugin_prompt(plugins),
            ),
            user_input_template=(searcher_input_template_cn if lang == "cn"
                                 else searcher_input_template_en),
            user_context_template=(searcher_context_template_cn if lang == "cn"
                                   else searcher_context_template_en),
        ),

Web Searcher的prompt为searcher_system_prompt_cn,具体指

searcher_system_prompt_cn = """## 人物简介
你是一个可以调用网络搜索工具的智能助手。请根据"当前问题",调用搜索工具收集信息并回复问题。你能够调用如下工具:
{tool_info}
## 回复格式

调用工具时,请按照以下格式:
```
你的思考过程...<|action_start|><|plugin|>{{"name": "tool_name", "parameters": {{"param1": "value1"}}}}<|action_end|>
```

## 要求

- 回答中每个关键点需标注引用的搜索结果来源,以确保信息的可信度。给出索引的形式为`[[int]]`,如果有多个索引,则用多个[[]]表示,如`[[id_1]][[id_2]]`。
- 基于"当前问题"的搜索结果,撰写详细完备的回复,优先回答"当前问题"。

"""

将该prompt送到LLM中,促使LLM具备工具调用功能,调用plugin工具。plugin工具的定义为:

    plugins = [(dict(
        type=AsyncWebBrowser if use_async else WebBrowser,
        searcher_type=search_engine,
        topk=6,
        secret_id=os.getenv("TENCENT_SEARCH_SECRET_ID"),
        secret_key=os.getenv("TENCENT_SEARCH_SECRET_KEY"),
    ) if search_engine == "TencentSearch" else dict(
        type=AsyncWebBrowser if use_async else WebBrowser,
        searcher_type=search_engine,
        topk=6,
        api_key=os.getenv("WEB_SEARCH_API_KEY"),
    ))]

这个工具是lagent/lagent/actions/web_browser.py/lagent,能够实现网页搜索。

mindsearch和lagent

Mindsearch代码和lagent耦合程度太深了,比如web_browser直接用lagent里实现的,但lagent是作为一个库供mindsearch使用的,如果想新增搜索源,需要看lagent的底层代码,一层一层改。。。

总结

子问题分解是AI搜索的一个较核心功能,在大多数AI搜索应用中都有体现。Mindsearch借助lagent智能体框架,完成了子问题分解的初步实现,具备较强的借鉴意义。但mindsearch和lagent的代码耦合程度太高,较难自定义,也是在实现层面需要注意的问题。
最令人震惊的是,360 AI搜索居然有广告,可谓是AI搜索商业化排头兵!!!


http://www.kler.cn/a/448470.html

相关文章:

  • JAVA:组合模式(Composite Pattern)的技术指南
  • 基于深度学习多图像融合的屏幕缺陷检测方案
  • 本机如何连接虚拟机MYSQL
  • workman服务端开发模式-应用开发-gateway长链接端工作原理
  • 【C语言】动态内存管理:详解malloc和free函数
  • StarRocks:存算一体模式部署
  • ESP8266 WiFi模块入门:搭建网络与测试实践
  • 普通人不搞副业还有什么出路?难道都能选择躺平?
  • 记录仪方案_记录仪安卓主板定制_音视频记录仪PCBA定制开发
  • Ubuntu Netlink 套接字使用介绍
  • Linux之进程相关命令
  • 版本更新导致前端网站资源加载失败:Failed to fetch dynamically imported module
  • 设计模式の享元模板代理模式
  • Redis 基本命令操作指南
  • 解决Ubuntu下蓝牙耳机连接成功但无声音输出问题
  • 开源轮子 - EasyExcel01(核心api)
  • 高超声速技术对于无人机的推进!
  • FFmpeg第二话:FFmpeg 主要结构体剖析
  • 洛谷 P1886:滑动窗口 ← 单调队列(STL queue)
  • 【计算机网络课程设计】校园网规划与设计
  • 【原生js案例】让你的移动页面实现自定义的上拉加载和下拉刷新
  • 贪心算法在背包问题上的运用(Python)
  • mysql免安装版配置教程
  • 数据结构 (数组和矩阵,初级动态规划)
  • Ubuntu 环境安装 之 RabbitMQ 快速入手
  • 【学习笔记】数据结构(八)