Qt Agent 智能体让大模型调用Qt的函数,实现Ai 大语言模型与数据的交互

在很多 Qt 桌面软件中,数据并不在大模型手里,而是在本地进程、业务对象、数据库、设备采集模块或 Qt Model 中。用户真正想要的也不是“模型回答一段泛泛而谈的文字”,而是让 AI 能看懂当前软件里的数据,然后执行查询、分析、汇总、导出等动作。

这就是 Agent 能力比普通聊天机器人更有价值的地方:大模型负责理解自然语言和规划动作,Qt 程序负责提供真实数据和可控函数。二者通过 tool calling 连接起来,AI 才能真正和桌面应用里的数据发生交互。

本文基于 UIGearsAgentKit 中的“数据分析”页面,说明如何用 Qt-UI AgentKit 把 Qt/C++ 函数注册为工具,让大模型完成“查询异常设备、生成告警摘要、导出报告”这样的业务流程。

 

image

 

一、普通聊天和 Agent 的差别

普通聊天流程通常是:

  1. 用户输入问题。
  2. Qt 程序把文本发给大模型。
  3. 大模型返回文本。
  4. UI 显示结果。

这个流程可以回答知识类问题,但它不知道 Qt 程序里的实时数据。例如用户问:

查找异常设备并导出报告。

如果只是普通聊天,大模型并不知道有哪些设备、哪些设备异常、报告应该保存在哪里。它只能猜。

Agent 流程不同:

  1. Qt 程序把可调用工具列表交给模型。
  2. 模型根据用户意图选择工具,例如 find_abnormal_devices
  3. AgentExecutor 执行 Qt/C++ 函数,拿到真实数据。
  4. 工具结果回填给模型。
  5. 模型基于真实结果生成最终回答。
  6. 如果工具涉及文件写入等高风险动作,先触发用户审批。

这样 AI 不是“凭空回答”,而是通过 Qt 函数读取业务状态。

二、Demo 中的设备数据

在 AgentKitDemoWindow 中,Demo 内置了一组设备状态数据:

_devices = {
    { "Pump-001", "主冷却泵", "正常", 53.2, 0.18, "" },
    { "Pump-002", "备用冷却泵", "预警", 74.5, 0.42, "温度持续升高" },
    { "Valve-014", "蒸汽阀门", "严重", 88.1, 0.66, "压力波动" },
    { "Fan-008", "排风机", "正常", 42.0, 0.12, "" },
    { "Motor-021", "输送电机", "预警", 69.3, 0.39, "振动超过阈值" }
};

这些数据在 Qt 程序内存中,不在模型上下文里。模型如果要知道异常设备,就必须调用 Qt 提供的函数。

Demo 页面同时展示:

  • 左侧设备状态表。
  • 右侧 Agent 聊天窗口。
  • 底部工具调用 Trace 表。

用户输入“查找异常设备并导出报告”后,可以看到模型调用了哪些工具、工具是否成功、是否触发审批、耗时和输出结果。

三、把 Qt 函数注册成 Agent 工具

AgentKit 中工具通过 FunctionTool 注册到 ToolRegistry。一个工具通常包含:

  • 工具名称。
  • 工具说明。
  • 参数 schema。
  • 权限类型。
  • 实际执行的 C++ 回调。

异常设备查询工具如下:

_toolRegistry->registerTool(new UIGQtLib::QtAgent::FunctionTool(
    "find_abnormal_devices",
    "查找预警或严重状态的演示设备。",
    objectSchema({}),
    UIGQtLib::QtAgent::ToolPermission::ReadOnly,
    [this](const QJsonObject& args) { return findAbnormalDevices(args); },
    _toolRegistry));

 

这里最重要的是最后的 Lambda:

[this](const QJsonObject& args) { return findAbnormalDevices(args); }

 

它把大模型提出的 tool call 映射到了 Qt/C++ 成员函数。模型不直接访问 _devices,也不能随便执行代码,它只能调用你明确注册出来的工具。

四、异常设备查询函数

findAbnormalDevices() 是一个普通 Qt/C++ 函数。它遍历设备列表,把状态不是“正常”的设备返回给 Agent。

UIGQtLib::QtAgent::ToolResult AgentKitDemoWindow::findAbnormalDevices(const QJsonObject& args) const
{
    Q_UNUSED(args);

    QJsonArray array;
    QStringList ids;

    for (const Device& device : _devices)
    {
        if (device.status != "正常")
        {
            array.append(QJsonObject{
                { "id", device.id },
                { "status", device.status },
                { "alarm", device.alarm }
            });
            ids << device.id;
        }
    }

    return {
        true,
        QJsonObject{ { "devices", array } },
        QStringLiteral("异常设备:%1").arg(ids.join(", "))
    };
}

 

ToolResult 同时返回结构化数据和可读文本:

  • success:工具是否执行成功。
  • data:结构化 JSON,方便模型继续推理。
  • content:给模型和 Trace 展示的简要说明。
  • error:失败时的错误信息。

这种设计的好处是:Qt 程序仍然掌握数据来源和执行边界,大模型只负责选择工具和组织结果。

五、为单个设备查询定义参数

如果工具需要参数,可以通过 schema 告诉模型参数结构。例如按设备 ID 查询:

_toolRegistry->registerTool(new UIGQtLib::QtAgent::FunctionTool(
    "query_device_status",
    "按 deviceId 查询单个演示设备状态。",
    objectSchema({
        {
            "deviceId",
            QJsonObject{
                { "type", "string" },
                { "description", "设备 id,例如 Pump-001" }
            }
        }
    }, { "deviceId" }),
    UIGQtLib::QtAgent::ToolPermission::ReadOnly,
    [this](const QJsonObject& args) { return queryDeviceStatus(args); },
    _toolRegistry));

 

模型看到这个 schema 后,就知道需要生成类似这样的参数:

{
  "deviceId": "Pump-002"
}

Qt 函数再从 QJsonObject 中读取参数:

const QString id = args.value("deviceId").toString();

这让自然语言和强类型业务函数之间有了一个稳定的桥。

六、AgentExecutor 如何串起模型和工具

设备分析不是手写关键词判断,而是通过通用 AgentExecutor 执行:

_deviceExecutor = new UIGQtLib::QtAgent::AgentExecutor(this);
_deviceExecutor->setToolRegistry(_toolRegistry);
_deviceExecutor->setSystemPrompt(QStringLiteral(
    "你是设备状态分析助手。用户要求查找异常设备、生成告警摘要或导出报告时,应优先使用可用工具完成任务。需要导出报告时调用 export_report。"
));

 

用户发送消息时:

void AgentKitDemoWindow::sendDeviceMessage(const QString& text)
{
    _traceView->clearTraces();
    _deviceChat->appendUserMessage(text);
    _deviceChat->beginAssistantMessage();
    _deviceChat->setRunning(true);
    _deviceExecutor->run(text);
}

AgentExecutor 的通用流程是:

  1. 构造 ChatRequest,加入 system prompt 和 user message。
  2. 把 ToolRegistry::definitions() 传给模型。
  3. 模型返回 OpenAI-compatible tool_calls
  4. Executor 解析工具名和参数。
  5. 调用对应的 FunctionTool
  6. 把工具结果以 role=tool 消息追加回 ChatRequest
  7. 再次调用模型,直到模型输出最终回答。

这个执行器是通用的,不绑定设备 Demo。你可以把工具换成订单查询、数据库检索、CAD 对象分析、PLC 状态读取、日志分析,流程仍然成立。

七、Trace 让工具调用可观察

Agent 做业务系统时,最怕“模型偷偷做了什么”。AgentKit Demo 的底部 Trace 表会显示每次工具调用:

  • 工具名称。
  • 执行状态。
  • 权限类型。
  • 耗时。
  • 输入参数。
  • 输出或错误。
  • 时间。

连接方式很直接:

connect(_deviceExecutor,
        &UIGQtLib::QtAgent::AgentExecutor::traceAdded,
        _traceView,
        &UIGQtLib::QtAgent::ToolCallTraceView::addTrace);

有了 Trace,Agent 的执行过程就不是一个黑盒。开发者可以看到模型是否选择了正确工具、参数是否合理、工具返回了什么数据。

八、文件导出需要审批

Demo 中 export_report 是文件系统权限工具:

_toolRegistry->registerTool(new UIGQtLib::QtAgent::FunctionTool(
    "export_report",
    "将演示报告导出到系统临时目录。",
    objectSchema({
        {
            "fileName",
            QJsonObject{
                { "type", "string" },
                { "description", "输出文件名" }
            }
        }
    }),
    UIGQtLib::QtAgent::ToolPermission::FileSystem,
    [this](const QJsonObject& args) { return exportReport(args); },
    _toolRegistry));

 

FileSystem 权限默认需要用户确认。Demo 中审批弹窗连接如下:

connect(_deviceExecutor,
        &UIGQtLib::QtAgent::AgentExecutor::approvalRequired,
        this,
        [this](const UIGQtLib::QtAgent::ApprovalRequest& request) {
            const bool allowed = UIGQtLib::QtAgent::ApprovalDialog::ask(request, this);
            _deviceExecutor->resolveApproval(request.id, allowed);
        });

  

这样模型可以提出“导出报告”的意图,但真正写文件前必须经过用户允许。对于桌面软件来说,这是 Agent 落地的关键边界:AI 可以建议和发起动作,但高风险动作要可审计、可拒绝。

九、一次完整交互是什么样

用户输入:

查找异常设备并导出报告。

理想流程如下:

  1. 模型识别用户要查异常设备。
  2. 调用 find_abnormal_devices
  3. Qt 返回 Pump-002Valve-014Motor-021 等异常设备。
  4. 模型继续调用 generate_alarm_summary 生成摘要。
  5. 模型判断用户要求导出报告,调用 export_report
  6. AgentKit 弹出审批对话框。
  7. 用户允许后,Qt 写入临时目录下的报告文件。
  8. 模型汇总工具结果并回复用户。

这时大模型的回答不是凭空生成的,而是来自 Qt 程序真实函数返回的数据。

十、接入自己的业务数据

换成真实业务系统时,可以按这个思路扩展:

_toolRegistry->registerTool(new UIGQtLib::QtAgent::FunctionTool(
    "query_order_status",
    "根据订单号查询订单状态。",
    objectSchema({
        {
            "orderNo",
            QJsonObject{
                { "type", "string" },
                { "description", "订单号" }
            }
        }
    }, { "orderNo" }),
    UIGQtLib::QtAgent::ToolPermission::ReadOnly,
    [this](const QJsonObject& args) {
        return queryOrderStatus(args.value("orderNo").toString());
    },
    _toolRegistry));

 

工具可以来自:

  • Qt 内存对象。
  • QAbstractItemModel
  • 数据库查询。
  • 本地日志文件。
  • 设备通信接口。
  • 项目配置或工程文件。
  • 内部 HTTP 服务。

建议先从只读工具开始,例如查询状态、检索数据、生成摘要。涉及写文件、执行命令、修改数据库、调用外部网络时,要使用更高权限并引入审批。

十一、设计 Agent 工具的几个原则

1. 工具名称要稳定

工具名会被模型引用,尽量使用清晰的英文 snake_case,例如:

find_abnormal_devices
query_device_status
generate_alarm_summary
export_report

2. 工具说明要描述业务意图

工具说明不是给开发者看的注释,而是给模型看的能力说明。它应该告诉模型“什么时候该调用这个工具”。

3. 参数 schema 要尽量明确

参数越清楚,模型越不容易传错。例如 deviceId 要说明示例值是 Pump-001

4. 返回值同时给结构化数据和文本摘要

结构化 JSON 方便模型继续推理,文本摘要方便 Trace 和最终回答使用。

5. 高风险工具必须审批

文件系统、系统命令、写数据库、外部网络请求都不应该静默执行。

Qt Agent 的核心不是让大模型替代 Qt 程序,而是让大模型调用 Qt 程序已经具备的能力。

在这个 Demo 中,模型不会直接访问设备列表,也不会直接写文件。它只能通过开发者注册的工具完成动作:

  • find_abnormal_devices 负责查询异常设备。
  • generate_alarm_summary 负责生成告警摘要。
  • export_report 负责导出报告,并且需要审批。
  • ToolCallTraceView 负责展示执行过程。

这种架构让 AI 与数据交互变得可控、可观察、可扩展。对于 Qt 桌面产品来说,它可以把传统按钮和表格之外的交互升级为自然语言驱动的业务操作,同时保留工程软件所需要的边界感和可靠性。

 

产品官网  www.qt-ui.com

 

Qt-UI C++Python界面开发工具 UI开发工具 qt解决方案 qt开源项目

 

posted on 2026-06-21 23:13  Qt-UI  阅读(3)  评论(0)    收藏  举报

导航