Deepresearch的MCP实践

项目链接:https://github.com/stay-leave/enhance_llm/tree/main/deepresearch

deepresearch是目前最流行的大模型应用范式,将agent应用于调研报告上,实现了用户只需要输入自己的问题,大模型自动搜索信息完成报告的过程。

区别于rag的单次检索过程和定制化的流程,deepresearch建立在deepsearch的的基础上,由LLM自主控制整个流程。
deepsearch的核心在于搜索、阅读、推理的循环。接收到查询时,进行搜索和阅读,然后根据当前搜索结果,决定是否终止或是扩展查询继续搜索。
参考:
https://jina.ai/news/a-practical-guide-to-implementing-deepsearch-deepresearch

本项目,就是基于MCP工具的deepresearch实现。首先定义了满足MCP协议的工具,主要是本地知识库检索工具、网络检索工具、混合检索工具。然后建立MCP客户端,与工具函数进行链接,从而将不同类型的工具,以统一的方式集成到项目。

满足MCP协议的工具

无需手动定义工具的类型、参数等,直接用mcp库封装,只需要编写实际可执行的函数即可。

# 本地知识库检索
@mcp.tool()
def retrieve(query: str) -> str:
    """本地知识库检索"""
    iteration_limit = 3  # 最大迭代次数
    iteration = 0
    aggregated_contexts = []  # 聚合的检索结果
    all_search_queries = []   # 所有查询记录
    
    # 初始查询改写
    current_query = rewrite_query(query)
    all_search_queries.append(current_query)

    while iteration < iteration_limit:
        print(f"\n=== 第 {iteration + 1} 次迭代 ===")
        print(f"用户原始查询:{query} | 当前检索查询:{current_query}")
        
        # 执行本地知识库混合检索
        try:
            chunk_df, image_df, _ = chunk_hybid_search(db_object, model, current_query)
            iteration_contexts = chunk_df["restored_content"].tolist()
        except Exception as e:
            print(f"检索失败:{e}")
            break
        
        # 处理检索结果
        if iteration_contexts:
            aggregated_contexts.extend(iteration_contexts)
        else:
            print("本次迭代未找到有用信息。")
        
        # 生成新查询
        new_query = get_new_search_query(
            user_query=query,
            previous_search_queries=all_search_queries,
            all_contexts=aggregated_contexts
        )
        
        # 终止条件判断
        if not new_query:
            print("无需进一步研究。")
            break
        elif new_query in all_search_queries:
            print(f"查询 {new_query} 已执行过,停止迭代。")
            break
        
        # 更新查询和记录
        current_query = new_query
        all_search_queries.append(current_query)
        iteration += 1

    return '\n\n'.join(aggregated_contexts)

MCP客户端

链接MCP工具,并执行查询流程的循环。

# 客户端
class MCPClient:
    def __init__(self):
        self.session: Optional[ClientSession] = None
        self.exit_stack = AsyncExitStack()
        self.client = ZhipuAI(
            api_key=api_key
        )
    
    # 启动本地工具服务器(如 search_mcp.py),并通过标准输入/输出与其通信
    async def connect_to_server(self, server_script_path: str):
        server_params = StdioServerParameters(
            command="python",
            args=[server_script_path],
            env=None
        )

        stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
        self.stdio, self.write = stdio_transport
        self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))

        await self.session.initialize()

        # 列出可用工具
        response = await self.session.list_tools()
        tools = response.tools
        print(f"\nConnected to server with tools: {[tool.name for tool in tools]}")
    
    # 进入查询处理流程
    async def process_query(self, query: str) -> str:
        """使用 LLM 和 MCP 服务器提供的工具处理查询"""
        # 列出所有的工具
        response = await self.session.list_tools()
        
        available_tools = [{
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "parameters": tool.inputSchema
            }
        } for tool in response.tools]
        print(f'available_tools:\n{available_tools}')
        
        # 初始规划,选择工具,返回工具名称和参数
        messages = [
            {
                "role": "system",
                "content": prompts["SYSTEM_PROMPT"] + str(available_tools)
            },
            {
                "role": "user",
                "content": query
            }
        ]
        response = self.client.chat.completions.create(
                model=model_name,
                messages=messages
            )
        
        message = response.choices[0].message
        print(f'llm_output(tool call):{message.content}') # 这一步直接给我返回大模型的结果了,无语
        
        # 确定好工具,进行循环
        results = [] # 工具返回的结果聚合
        while True:
            
            flag, json_text = get_clear_json(message.content)# 根据是否能解析出json,执行不同的方法
            
            if flag == 0: # 没有生成json格式,直接用大模型生成回复
                response = self.client.chat.completions.create(
                    model=model_name,
                    messages=[{"role": "user", "content": query}]
                )
                return response.choices[0].message.content
            
            # 成功生成json,解析出工具名和参数
            json_text = json.loads(json_text)
            tool_name = json_text['name']
            tool_args = json_text['params']
            # 执行工具函数,获得返回值
            result = await self.session.call_tool(tool_name, tool_args)
            print(f'tool name: \n{tool_name}\ntool call result: \n{result}')
            results.append(result.content[0].text)
            
            # 把返回工具回复加入历史消息列表
            messages.append({
                "role": "assistant",
                "content": message.content
            })
            messages.append({
                "role": "user",
                "content": f'工具调用结果如下:{result}'
            })
            
            # 在工具调用完成后,由大模型决定是否结束检索,还是继续检索
            messages.append({
                "role": "user",
                "content": prompts["NEXT_STEP_PROMPT"].format(query)
            })
            
            response = self.client.chat.completions.create(
                model=model_name,
                messages=messages
            )
            
            message = response.choices[0].message
            print(f'llm_output:\n{message.content}')
            # 检查是否该结束
            if 'finish' in message.content:
                break
            
            # 继续检索,就把大模型的回复加入历史消息,继续循环
            messages.append({
                "role": "assistant",
                "content": message.content
            })
        
        # 循环终止后,进入报告撰写阶段
        messages.append({
                "role": "user",
                "content": prompts["FINISH_GENETATE"].format('\n\n'.join(results), query)
                })
        
        response = self.client.chat.completions.create(
                model=model_name,
                messages=messages
            )
        # 返回报告内容
        message = response.choices[0].message.content
        return message
    

    async def chat_loop(self):
        """运行交互式聊天循环"""
        print("\nMCP Client Started!")
        print("Type your queries or 'quit' to exit.")

        while True:
            try:
                query = input("\nQuery: ").strip()
                if query.lower() == 'quit':
                    break
                response = await self.process_query(query)
                print(response)
            except Exception as e:
                print(f"\nError: {str(e)}")

注意事项

1.LLM一定要满足:上下文窗口比较长,人类对齐能力强。否则无法容纳长文本的检索结果,或者无法成功调用工具。

2.无论是网络检索,还是本地检索,都只是信息检索的一种具体路径。

### MCP 实践案例与教程 #### 文件系统中的MCP协议应用 在文件系统的开发过程中,采用特定的MCP服务器和客户端实现能够显著提高数据处理效率以及增强安全性能。这些实践表明,在设计分布式存储解决方案时引入MCP技术可以带来诸多优势[^1]。 #### Web 自动化测试框架集成MCP 对于Web自动化领域而言,利用支持MCP标准的产品有助于加速页面加载时间的同时保持高精度的操作执行。这不仅提升了用户体验也加强了应用程序的安全防护措施。 #### Rotary Encoder Over MCP23017 教程 针对硬件爱好者或者嵌入式开发者来说,《rotary-encoder-over-mcp23017》提供了一个详细的指南来帮助理解如何将旋转编码器连接到基于I²C总线扩展GPIO端口数量而闻名的芯片——MCP23017上工作。此项目非常适合那些想要探索更多关于输入设备接口编程可能性的人士[^2]。 #### Arduino MCP2515 CAN 接口库使用说明 面向汽车电子工程师或是对车载网络感兴趣的个人,《Arduino MCP2515 CAN 接口库使用教程》则深入浅出地介绍了怎样借助于Microchip公司的专用集成电路(MCP2515),配合开源硬件平台Arduino搭建起稳定可靠的Controller Area Network (CAN)通讯链路。该文档还分享了一些实用技巧以指导读者更好地完成从理论学习到动手实操整个过程[^3]。 ```cpp // 示例代码片段展示如何初始化MCP2515 CAN模块 #include <SPI.h> #include <mcp_can.h> #define CAN0_INT 2 // Set interrupt pin, using D2 here. MCP_CAN CAN0(10); // Set CS to pin 10 void setup() { if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) == CAN_OK){ Serial.println("CAN BUS Shield init ok!"); }else{ Serial.println("CAN BUS Shield init fail"); while(1); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灵海之森

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值