Python调用DeepSeek API查询ClickHouse的流程步骤

 更新时间:2026年03月20日 09:38:35   作者:言之。  
本文介绍如何利用DeepSeek API和ClickHouse构建自然语言查询数据库,文章详细说明了环境配置步骤,包括Python安装、必备库安装、DeepSeek API Key获取和ClickHouse连接配置,需要的朋友可以参考下

调用 DeepSeek API,并通过 MCP (Model Context Protocol) 协议接入 mcp-clickhouse,从而让 DeepSeek 能够查询 ClickHouse 数据库来回答用户问题。

  • stdio 模式

前置准备

在运行代码之前,你需要确保环境中有以下依赖:

安装 Python 库

pip install mcp openai

安装 uv (因为你的配置中使用了 uv 来运行 mcp-clickhouse):

pip install uv

获取 DeepSeek API Key:你需要一个有效的 DeepSeek API 密钥。

Python 代码 (main.py)

请将代码中的 <YOUR_DEEPSEEK_API_KEY> 替换为你的实际 Key,并确保 <clickhouse-host> 等配置已填入正确的值。

import asyncio
import os
import json
import sys
from typing import List, Dict, Any, Optional

# 导入 MCP 相关库
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
import mcp.types as types

# 导入 OpenAI SDK (DeepSeek 兼容 OpenAI 格式)
from openai import AsyncOpenAI

# ================= 配置部分 =================

# DeepSeek API 配置
DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "<YOUR_DEEPSEEK_API_KEY>")
DEEPSEEK_BASE_URL = "https://api.deepseek.com"
MODEL_NAME = "deepseek-chat"  # 或者 "deepseek-reasoner" (R1)

# MCP ClickHouse Server 配置 (来自你的 JSON)
MCP_CLICKHOUSE_CONFIG = {
    "command": "uv",
    "args": [
        "run",
        "--with",
        "mcp-clickhouse",
        "--python",
        "3.10",
        "mcp-clickhouse"
    ],
    "env": {
        "CLICKHOUSE_HOST": "<clickhouse-host>",
        "CLICKHOUSE_PORT": "<clickhouse-port>",
        "CLICKHOUSE_USER": "<clickhouse-user>",
        "CLICKHOUSE_PASSWORD": "<clickhouse-password>",
        "CLICKHOUSE_ROLE": "<clickhouse-role>",
        "CLICKHOUSE_SECURE": "true",
        "CLICKHOUSE_VERIFY": "true",
        "CLICKHOUSE_CONNECT_TIMEOUT": "30",
        "CLICKHOUSE_SEND_RECEIVE_TIMEOUT": "30"
    }
}

# ================= 工具函数 =================

def convert_mcp_tool_to_openai(mcp_tool: types.Tool) -> Dict[str, Any]:
    """
    将 MCP 工具定义转换为 OpenAI/DeepSeek 支持的 Function Calling 格式。
    """
    return {
        "type": "function",
        "function": {
            "name": mcp_tool.name,
            "description": mcp_tool.description,
            "parameters": mcp_tool.inputSchema
        }
    }

# ================= 主逻辑 =================

async def run_chat_loop():
    # 1. 初始化 DeepSeek 客户端
    client = AsyncOpenAI(api_key=DEEPSEEK_API_KEY, base_url=DEEPSEEK_BASE_URL)
    
    # 2. 准备 MCP Server 参数
    server_params = StdioServerParameters(
        command=MCP_CLICKHOUSE_CONFIG["command"],
        args=MCP_CLICKHOUSE_CONFIG["args"],
        env={**os.environ, **MCP_CLICKHOUSE_CONFIG["env"]} # 合并当前环境变量
    )

    print(f"正在启动 MCP Server: {MCP_CLICKHOUSE_CONFIG['command']}...")
    
    # 3. 连接 MCP Server 并开始对话循环
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # 初始化连接
            await session.initialize()
            
            # 获取 MCP Server 提供的工具列表
            mcp_tools_list = await session.list_tools()
            openai_tools = [convert_mcp_tool_to_openai(tool) for tool in mcp_tools_list.tools]
            
            print(f"\n已连接 MCP Server,加载了 {len(openai_tools)} 个工具: {[t['function']['name'] for t in openai_tools]}")
            print("-" * 50)
            print("你可以开始询问关于 ClickHouse 的问题了 (输入 'quit' 退出)。")

            messages = [
                {"role": "system", "content": "你是一个智能助手,可以利用工具查询 ClickHouse 数据库来回答用户问题。"}
            ]

            while True:
                user_input = input("\n用户: ")
                if user_input.lower() in ["quit", "exit"]:
                    break

                messages.append({"role": "user", "content": user_input})

                # 第一轮调用:发送用户问题 + 工具定义给 DeepSeek
                try:
                    response = await client.chat.completions.create(
                        model=MODEL_NAME,
                        messages=messages,
                        tools=openai_tools,
                    )
                except Exception as e:
                    print(f"API 调用错误: {e}")
                    continue

                assistant_msg = response.choices[0].message
                messages.append(assistant_msg)

                # 检查 DeepSeek 是否想调用工具
                if assistant_msg.tool_calls:
                    print(f"\n[思考] 模型决定调用工具...")
                    
                    for tool_call in assistant_msg.tool_calls:
                        tool_name = tool_call.function.name
                        tool_args = json.loads(tool_call.function.arguments)
                        
                        print(f"  -> 调用工具: {tool_name}, 参数: {tool_args}")

                        # 执行 MCP 工具
                        try:
                            mcp_result = await session.call_tool(tool_name, arguments=tool_args)
                            
                            # 获取文本结果 (ClickHouse MCP 通常返回文本或 JSON 文本)
                            tool_output = ""
                            if mcp_result.content:
                                for content in mcp_result.content:
                                    if content.type == "text":
                                        tool_output += content.text
                            
                            print(f"  <- 工具返回结果 (前100字符): {tool_output[:100]}...")

                            # 将工具结果添加回对话历史
                            messages.append({
                                "role": "tool",
                                "tool_call_id": tool_call.id,
                                "content": tool_output
                            })

                        except Exception as e:
                            error_msg = f"工具执行出错: {str(e)}"
                            print(f"  ! {error_msg}")
                            messages.append({
                                "role": "tool",
                                "tool_call_id": tool_call.id,
                                "content": error_msg
                            })

                    # 第二轮调用:将工具结果发送回 DeepSeek 获取最终回答
                    final_response = await client.chat.completions.create(
                        model=MODEL_NAME,
                        messages=messages,
                        # 这里依然传入 tools,以防模型需要继续多步调用
                        tools=openai_tools, 
                    )
                    
                    final_content = final_response.choices[0].message.content
                    print(f"\nDeepSeek: {final_content}")
                    messages.append(final_response.choices[0].message)
                
                else:
                    # 如果不需要调用工具,直接输出结果
                    print(f"\nDeepSeek: {assistant_msg.content}")

if __name__ == "__main__":
    # Windows 下通常需要这个策略
    if sys.platform.startswith('win'):
        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
        
    asyncio.run(run_chat_loop())

代码逻辑解释

MCP 连接 (stdio_client):

  • 代码使用 uv run ... 命令启动 mcp-clickhouse 的子进程。
  • 通过标准输入/输出 (stdio) 与该进程建立通信通道。
  • CLICKHOUSE_HOST 等敏感信息通过环境变量 (env) 传递给子进程,保证安全性。

工具发现与转换 (convert_mcp_tool_to_openai):

  • session.list_tools() 从 mcp-clickhouse 获取可用的工具(例如执行 SQL 查询的工具)。
  • 由于 MCP 的工具定义格式与 OpenAI/DeepSeek 的 Function Calling 格式略有不同(主要在于外层结构),代码中定义了一个转换函数将其适配。

对话循环 (Chat Loop):

  • User Input: 接收你的问题。
  • First API Call: 将问题和转换后的工具列表发送给 DeepSeek。
  • Tool Execution: 如果 DeepSeek 返回 tool_calls(例如它生成了一个 SQL 语句来查询数据),Python 代码会拦截这个请求,通过 session.call_tool 在本地的 mcp-clickhouse 服务中执行该 SQL。
  • Second API Call: 将 ClickHouse 返回的查询结果(数据)作为 tool 类型的消息发回给 DeepSeek。
  • Final Answer: DeepSeek 根据查询结果生成最终的自然语言回答。

运行示例

假设你问:“查询 users 表里有多少行数据?”

  1. DeepSeek 分析意图,返回 tool_calls,调用 query_sql 工具,参数为 SELECT count() FROM users
  2. Python 脚本通过 MCP 执行该 SQL。
  3. ClickHouse 返回 105
  4. Python 脚本将 105 发给 DeepSeek。
  5. DeepSeek 回答:“users 表里共有 105 行数据。”

到此这篇关于Python调用DeepSeek API查询ClickHouse的流程步骤的文章就介绍到这了,更多相关Python DeepSeek API查询ClickHouse内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python自动化unittest yaml使用过程解析

    python自动化unittest yaml使用过程解析

    这篇文章主要介绍了python自动化unittest yaml使用过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • 用python制作游戏外挂

    用python制作游戏外挂

    玩过电脑游戏的同学对于外挂肯定不陌生,但是你在用外挂的时候有没有想过如何做一个外挂呢?那我们就来看一下如何用python来制作一个外挂
    2018-01-01
  • 无需压缩软件,用python帮你操作压缩包

    无需压缩软件,用python帮你操作压缩包

    这篇文章主要介绍了如何用python帮你操作压缩包,帮助大家更好的理解和学习python,感兴趣的朋友可以了解下
    2020-08-08
  • Python提取JSON格式数据实战案例

    Python提取JSON格式数据实战案例

    这篇文章主要给大家介绍了关于Python提取JSON格式数据的相关资料, Python提供了内置的json模块,用于处理JSON数据,文中给出了详细的代码示例,需要的朋友可以参考下
    2023-07-07
  • Pandas创建DataFrame提示:type object 'object' has no attribute 'dtype'解决方案

    Pandas创建DataFrame提示:type object 'object' has n

    Pandas数据帧(DataFrame)是二维数据结构,它包含一组有序的列,每列可以是不同的数据类型,这篇文章主要给大家介绍了关于Pandas创建DataFrame提示:type object ‘object‘ has no attribute ‘dtype‘的解决方案,需要的朋友可以参考下
    2023-02-02
  • Python的Flask框架标配模板引擎Jinja2的使用教程

    Python的Flask框架标配模板引擎Jinja2的使用教程

    Jinja2是Python世界的一款高人气template engine,是许多开源Web框架的选择,包括Flask这样的明星级项目,这里我们就来共同学习Python的Flask框架标配模板引擎Jinja2的使用教程
    2016-07-07
  • Scrapy框架基本命令与settings.py设置

    Scrapy框架基本命令与settings.py设置

    这篇文章主要介绍了Scrapy框架基本命令与settings.py设置,结合实例形式分析了创建爬虫项目、创建爬虫文件、存储、打开网页及settings.py设置等相关操作技巧,需要的朋友可以参考下
    2020-02-02
  • django连接mysql数据库及建表操作实例详解

    django连接mysql数据库及建表操作实例详解

    这篇文章主要介绍了django连接mysql数据库及建表操作,结合实例形式详细分析了Django框架连接mysql数据库、创建与查询数据表相关操作实现技巧,需要的朋友可以参考下
    2019-12-12
  • Windows上安装tensorflow  详细教程(图文详解)

    Windows上安装tensorflow 详细教程(图文详解)

    这篇文章主要介绍了Windows上安装TENSORFLOW 详细教程,本文通过图文并茂的形式给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-02-02
  • Python3 关于pycharm自动导入包快捷设置的方法

    Python3 关于pycharm自动导入包快捷设置的方法

    今天小编就为大家分享一篇Python3 关于pycharm自动导入包快捷设置的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-01-01

最新评论