使用 ClickHouse MCP 和 CopilotKit 构建智能 AI 应用

图片

本文字数:7143;估计阅读时间:18 分钟

作者:Lionel Palacin

本文在公众号【ClickHouseInc】首发

图片

假设你正在找新房,想了解某个社区近几年的房价走势。与其反复点开图表、调整筛选条件,你能不能直接问一句:

“展示过去十年曼彻斯特的房价变化。”

然后系统立刻生成图表,附带解释,甚至还能继续提出后续问题。

图片

这就是智能 AI 应用(Agentic 应用)的魅力所在。借助大语言模型(Large Language Model,LLM)的强大能力,它不仅能理解复杂的请求,还能自动调用 API,基于单个用户指令完成整套流程,带来真正智能、交互式的使用体验。

你可以点击文末原文观看我们客户小组的视频,了解 MCP 在实时分析类应用中的强大潜力。

在本文中,我们将教你如何构建这样一个智能应用。我们会基于 ClickHouse MCP Server 和 CopilotKit,开发一个可自定义的英国房产市场分析仪表板。前端使用 React 和 Next.js 构建,但你也可以将相同方法应用到任何现代应用框架中。

智能应用的核心组件

我们先来看看构建这样一个应用所需的关键组成部分。

大语言模型(LLM)

任何智能应用的核心都是大语言模型。它负责理解用户的输入、识别上下文、生成响应内容,并决定下一步该做什么。

为了确保用户体验流畅、响应及时,选用一款性能强大、上下文窗口足够大的模型非常关键。智能应用通常涉及复杂的任务、与外部工具的交互,以及处理来自第三方系统的数据。随着上下文信息越来越多,模型必须具备高效的处理和快速响应能力,才能让最终用户获得自然、流畅的交互体验。

在本示例中,我们使用的是 Anthropic 提供的 Claude Sonnet 3.7 模型。截至本文撰写时,该模型在 TAU-bench 基准测试中,在航空和零售行业场景下表现出色。这个基准测试主要评估各类 LLM 在与用户交互以及遵循专业规则方面的能力。

ClickHouse MCP Server:连接数据与智能的桥梁

我们的智能应用将通过构建用户自定义的仪表板,帮助大家分析英国房地产市场数据。虽然这些数据本身是公开的,模型在训练过程中也可能接触过,但这些信息存储在模型的参数中,并不是逐条记录。换句话说,即使模型曾见过这类数据,当我们询问英国某地的房价时,它依然可能“凭空想象”出一些数字。因此,在很多场景下,模型必须访问实时或专有的数据源,才能输出准确、有价值的结果。

这正是模型上下文协议(Model Context Protocol,MCP)服务器诞生的背景。MCP 是一个开放标准,帮助开发者在自有数据源与 AI 工具之间建立安全、双向的连接。

ClickHouse MCP Server 让开发者可以在智能应用中无缝集成 ClickHouse,使模型能够直接发起查询,获取实时数据支持。

CopilotKit:简化智能应用开发的 UI 框架

在整个架构中,CopilotKit 是第三个核心组件。它是一个专为 Agentic 应用设计的 UI 框架,极大地简化了开发流程。

我之所以选择 CopilotKit,是因为它帮我屏蔽了很多底层的复杂细节。它内置聊天界面支持、可轻松接入不同的大语言模型,还能自动处理模型触发的各种工具调用和 UI 动作。

接下来我们就来看一下,这些组件是如何协同工作的。

整体架构:从用户请求到可视化图表

我们以一个用户输入为例,展示整个流程如何运转。这个流程展示了各个组件如何协作,将自然语言请求转换为一张图表结果。

图片

  1. 用户输入请求:“展示过去十年曼彻斯特的房价变化。”

  2. 该请求与当前应用状态和可用操作项一起,被发送至 CopilotKit 的运行时。

  3. CopilotKit 将 MCP 资源列表加入提示中,并将其转发给大语言模型(LLM)。LLM 分析提示内容与当前资源,判断需要查询数据,并生成一条针对 ClickHouse 的 SQL 查询语句。

  4. CopilotKit 运行时通过 MCP 客户端发起查询请求。

  5. MCP 客户端调用 ClickHouse MCP Server,根据生成的 SQL 查询获取曼彻斯特近十年的房价数据。

  6. 数据与上下文信息一起返回给 LLM,模型识别出预定义的 generateChart 动作,并按照图表要求对数据进行格式化,生成最终响应。最终,图表被呈现在用户界面中

这个流程充分展现了智能应用背后各组件的紧密配合:模型理解用户意图,通过 ClickHouse 获取实时数据,再结合 CopilotKit 调用 UI 动作,将结果以图表形式反馈给用户。

如何搭建智能应用(Agentic Application)

在上一部分中我们已经了解了整个应用的高层架构,接下来我们将一步一步讲解如何从头开始构建它。

本节将重点展示实现过程中的关键步骤。如需查看完整可运行的示例,请访问我们的 Github 仓库获取参考代码。【https://github.com/ClickHouse/examples/tree/main/ai/mcp/copilotkit】

初始化应用

首先,我们需要创建一个全新的 React 应用,并使用 CopilotKit 框架进行初始化。使用 npx 命令可以快速生成项目。在提示如何与大语言模型交互时,请选择 MCP 作为连接方式。

npx create-next-app@latest
cd agentic-app
npx copilotkit@latest init

接入你自己的大语言模型

CopilotKit 默认使用的是 OpenAI 模型。但你也可以根据需要切换为其他支持的模型,具体支持列表可参考官方文档。【https://docs.copilotkit.ai/guides/bring-your-own-llm】

模型连接通常在服务端完成。CopilotKit 默认会暴露一个用于与大语言模型通信的 API 路由,前端通过这个接口与模型进行交互。

你可以编辑文件 ./app/api/copilotkit/routes.ts 来更换所使用的模型。

import { AnthropicAdapter } from "@copilotkit/runtime";

// const serviceAdapter = new OpenAIAdapter()
const serviceAdapter = new AnthropicAdapter({model: "claude-3-7-sonnet-latest"});

同时,别忘了通过环境变量的方式提供 API 密钥(如:ANTHROPIC_API_KEY)。

部署 ClickHouse MCP Server

在这个示例中,我们选择在本地部署 ClickHouse MCP Server。当然,你也可以将其部署到远程服务器,并让多个 MCP 客户端连接到同一个服务。

未来,ClickHouse Cloud 将支持远程 MCP Server 作为默认接口。这意味着,无需本地部署,任何 MCP 客户端都能直接连接到你的云端 ClickHouse 实例。

如果你希望抢先体验这项功能,可以前往 clickhouse.ai 注册 AI 功能的候补名单。【https://clickhouse.com/ai】

# Clone the ClickHouse MCP Server repository
git clone https://github.com/ClickHouse/mcp-clickhouse
# Install dependencies
python3 -m venv .venv && source .venv/bin/activate && uv sync && uv add fastmcp
# Configure connection to ClickHouse database 
export CLICKHOUSE_HOST="sql-clickhouse.clickhouse.com"
export CLICKHOUSE_USER="demo"
export CLICKHOUSE_SECURE="true"
export CLICKHOUSE_PORT="8443"
# Run the MCP server and expose SSE transport protocol
fastmcp run mcp_clickhouse/mcp_server.py:mcp --transport sse

本次演示中我们使用的是 ClickHouse SQL Playground,它包含了英国房产市场的数据集。

最后,我们通过 fastmcp 命令启动 MCP Server,并使用 SSE(Server-Sent Events)作为默认传输方式,监听端口为 8000。

配置 MCP 客户端

CopilotKit 内置支持 MCP 客户端,我们只需配置服务器连接地址即可完成对接。

打开并编辑 ./app/copilotkit/page.tsx 文件,添加 ClickHouse MCP Server 的连接信息即可。

useEffect(() => {
    setMcpServers([
      { endpoint: "http://localhost:8000/sse" },
    ]);
 }, []);

定义智能体的操作逻辑

Agentic 应用最核心的特性之一,就是它能够根据与用户的对话自动执行操作。

在本项目中,我们希望应用能够根据用户的描述动态生成图表。这就是我们需要告诉大语言模型(LLM)该如何执行的操作。

我们使用 CopilotKit 提供的 useCopilotAction Hook 来实现这一功能。通过这个 Hook,开发者可以定义一组可由模型调用的自定义操作。在我们的示例中,该操作就是向状态中添加一条新的图表配置。

此外,我们还会用到另一个 Hook:useCopilotReadable,它允许我们将某个状态变量(例如图表配置数组)暴露给模型,以便它在推理过程中能使用这些信息。

要完成设置,你需要编辑文件 ./app/copilotkit/page.tsx,在其中定义好动作逻辑,并将图表的状态变量共享给模型。

// Chart configuration array 
const [charts, setCharts] = useState<Chart[]>([]);

// Share the charts state variable with LLM
useCopilotReadable({
  description: "These are all the charts props",
  value: charts,
});

// Create a new action generateChart that will be used by the LLM to create the correct chart configuration and add it to the state variable. 
useCopilotAction({
    name: "generateChart",
    description: "Generate a chart based on the provided data. Make sure to provide the data in the correct format and specify what field should be used a x-axis.",
    parameters: [
        {
            name: "data",
            type: "object[]",
            description: "Data to be used for the chart. The data should be an array of objects, where each object represents a data point.",
        },
        {
            name: "chartType",
            type: "string",
            description: "Type of chart to be generated. Lets use bar, line, area, or pie.",
        },
        {
            name: "title",
            type: "string",
            description: "Title of the chart. Cant be more than 30 characters.",
        },
        { name: "xAxis", type: "string", description: "x-axis label" }
    ],

    handler: async ({ data, chartType, title, xAxis }) => {
        const newChart = {
            data,
            chartType,
            title,
            xAxis
        };
        setCharts((charts) => [...charts, newChart] );
    },
    render: "Adding chart...",
});

接下来,我们添加一个 DynamicGrid 组件,用来遍历图表配置数组,并为每一项渲染一个图表。

function DynamicGrid({ charts }: { charts: Chart[] }) {
return (
      charts.map((chart, index) => (
      <div className="flex flex-col gap-4" key={index}>
              <p className="text-white whitespace-nowrap overflow-hidden text-overflow-ellipsis text-xl leading-[150%] font-bold font-inter">{chart.title}</p>
              <GenericChart {...chart} />
          </div>
      ))
  )
}

图表本身由 GenericChart 组件实现,它基于 ECharts 的 React 版本。当然,你也可以自由更换成自己喜欢的图表库。GenericChart 的源码可以在这里查看。【https://github.com/ClickHouse/examples/blob/copilotkit/ai/mcp/copilotkit/components/GenericChart.tsx】

最终效果展示

我们已经完成了整个实现的核心逻辑。接下来的工作主要是美化界面,为应用添加一些样式细节,让整体更具视觉吸引力。完整代码已托管在 Github 上,欢迎参考。【https://github.com/ClickHouse/examples/tree/main/ai/mcp/copilotkit】

在智能应用中使用 ClickHouse 的优势

实时分析数据库的强大能力

对于这类需要动态响应和实时推理的智能应用,选择一款实时分析型数据库至关重要。ClickHouse 正是这方面的理想选择。

图片

实时分析数据库具备一系列适配 Agentic 应用场景的特性。首先,它支持近实时数据处理,可以让系统在信息刚产生时就立刻用于推理与响应。这对于需要及时辅助用户做决策的 AI 智能体至关重要。

其次,这类数据库天生适合执行复杂的数据分析任务,例如多维聚合、趋势挖掘和异常检测等。它们不像传统事务型数据库那样只关注数据的读写,而是专注于挖掘和提取洞察。

最后,它们还能支持高频率、高并发的交互式查询,无论是在对话过程中生成图表,还是执行多轮数据探索分析,都能保持稳定的性能,带来更加丝滑、即时的用户体验。

细粒度权限与配额控制

构建智能应用的一大挑战,是如何控制大语言模型(LLM)在代表用户操作时的权限范围。尤其是在模型可以通过 MCP Server 直接访问生产数据库的场景下,权限管理就显得尤为关键。

好在 ClickHouse 提供了非常灵活的权限机制与配额(quota)设置,让我们可以精准控制 MCP Server 向模型开放的数据与操作权限。

在本示例中,我们通过 SQL Playground 托管了英国房地产市场的数据集,并将 MCP Server 配置为以 demo 用户的身份进行身份验证。你可以在对应链接中查看该用户的具体配置方式。【https://github.com/ClickHouse/sql.clickhouse.com/blob/main/setup.sql】

demo 用户被限制为只读权限,且只能访问特定数据库。这种限制确保了模型只能查询我们允许的数据。在此基础上,我们还为该用户设置了配额限制,并分配了一个受限权限的用户配置文件,防止模型发起过多或资源开销过大的查询操作。通过这样的设置,我们可以在操作范围和资源消耗两方面实现精细化(fine-grained)控制。

总结

在这篇博客中,我们详细介绍了如何使用 ClickHouse MCP Server 与 CopilotKit 构建一款智能应用。

借助大语言模型的能力,用户可以自由构建基于英国市场数据的分析仪表板,实现个性化的数据洞察体验。

而像 ClickHouse 这样的高速、可扩展、具备安全机制的分析型数据库,是实现这种应用高效、稳定运行的关键所在。这种新型架构为 AI 工具的构建打开了全新思路,让我们能够为用户提供更深入的分析能力和更出色的使用体验。

征稿启示

面向社区长期正文,文章内容包括但不限于关于 ClickHouse 的技术研究、项目实践和创新做法等。建议行文风格干货输出&图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:Tracy.Wang@clickhouse.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值