智能物业管家:扣子智能体接入QT实现AI对话
文档信息
| 项目 | 内容 |
|---|---|
| 例子项目 | 小区管家物业综合服务平台 |
| 技术栈 | Qt 5/6、C++、扣子(Coze)智能体API、SSE协议 |
前言
随着人工智能技术的快速发展,智能对话系统在各个领域的应用越来越广泛。在物业管理领域,传统的物业服务模式往往需要用户通过电话、现场咨询等方式获取服务,效率较低且体验不佳。
为了解决这一问题,本文实现了基于扣子智能体的AI交互功能,将先进的AI对话能力集成到小区管家物业综合服务平台中。通过该功能,业主可以与AI智能管家进行自然语言对话,AI不仅能回答问题,还能通过意图识别自动跳转到缴费、报修、公告等页面,极大提升了服务效率与用户体验。
本文主要内容包括:
-
项目涉及的相关技术知识与扣子智能体平台配置方法
-
Qt 框架下 AI 对话功能的整体架构设计与数据流向
-
HTTP 网络请求与 SSE 流式响应数据的解析处理
-
CozeAI 通信类的完整代码实现与详细注释说明
-
AI 回复文本中隐藏命令的识别机制与页面跳转实现
-
智能体提示词的设计、意图识别规则与优化方案
一、实现效果
本项目使用Qt搭建物业平台界面并对接扣子智能体,实现AI智能对话。用户在输入框输入“小区公告”,AI经过意图识别后,自动跳转到公告页面;输入“物业费”,AI引导用户前往缴费页面。


二、相关知识介绍
2.1 扣子智能体平台
扣子(Coze)是字节推出的零代码智能体搭建平台,支持可视化配置知识库、角色设定、工具插件,一键发布在线 API 接口。开发者无需训练大模型,在平台完成智能体定制后,通过官方 HTTP/SSE 接口即可在自有程序调用 AI 能力,适配桌面软件、嵌入式设备、后端服务等多场景。
平台提供两类接口:
| 接口类型 | 特点 | 适用场景 |
|---|---|---|
| 普通POST同步接口 | 一次性返回完整回答 | 短文本、容忍延迟 |
| SSE流式接口 | 分片实时推送回答,打字机效果 | AI对话、实时推送 |
核心概念:
| 概念 | 说明 |
|---|---|
| 智能体(Bot) | 具有特定人设和功能的AI对话机器人 |
| 知识库 | 智能体的知识来源,可上传文档作为背景知识 |
| 插件 | 扩展智能体能力的工具,如天气查询、新闻获取等 |
| 工作流 | 编排多个节点实现复杂业务逻辑 |
| API/SDK | 将智能体以API形式提供给外部系统调用 |
2.2 SSE(Server-Sent Events)
SSE 是服务端单向推送协议,基于 HTTP 长连接实现服务端主动向客户端持续下发数据,相比 WebSocket:仅服务端→客户端单向通信、复用 HTTP 协议无需额外握手,天然适配大模型流式输出场景。 特点:
- 连接建立后持续保持,服务端分段返回
data:xxx\n\n格式数据; - Qt 通过
QNetworkAccessManager监听 readyRead 分片数据,实时解析拼接 AI 内容; - 网络异常自动重连,适配大模型长文本输出。
SSE与传统HTTP的对比:
| 特性 | 传统HTTP | SSE |
|---|---|---|
| 请求次数 | 一请求一响应 | 一次请求,多次响应 |
| 用户体验 | 等待完整响应后显示 | 逐步显示,如打字效果 |
| 适用场景 | 文件传输、普通API | 实时推送、AI对话 |
| 数据格式 | 任意格式 | 以data:开头的文本流 |
SSE数据格式示例:
text
data: {"type":"answer","content":{"answer":"您"}}
data: {"type":"answer","content":{"answer":"好"}}
data: [DONE]
2.3 Qt 网络编程
Qt 使用QtNetwork模块完成 HTTP/SSE 网络交互,核心类:
主要类说明:
| 类名 | 作用 |
|---|---|
| QNetworkAccessManager |
管理网络请求,发送请求和接收响应。 全局网络管理器,统一发起 GET/POST 请求; |
| QNetworkRequest | 封装请求头、请求地址、Authorization 鉴权 |
| QNetworkReply |
封装HTTP响应,提供数据读取接口。 接收接口返回数据流, |
2.4 信号与槽机制
Qt 核心通信机制,解耦网络数据接收与 UI 界面更新:网络类收到 SSE 分片数据触发自定义信号,绑定 UI 控件的槽函数,实时追加文字到文本框,避免跨线程 UI 崩溃。 例:CozeAI类发出recvAiMsg(QString)信号 → 绑定MainWindow槽函数,在对话框实时渲染 AI 打字效果。
// 信号声明(发送方)
signals:
void replyReceived(const QString &content);
// 槽函数连接(接收方)
connect(cozeAI, &CozeAI::replyReceived, this, [=](const QString &answer){
// 处理接收到的AI回复
});
三,常见的问题
在开始实现之前,先解答几个关键问题,帮助理解整个技术方案。
问题1:什么是API?为什么需要API接口?每个大模型的API是否不一样?
API(应用程序编程接口) 是软件系统之间预先约定好的通信规则与数据格式,规定了程序之间 “如何发送请求、传什么参数、返回什么内容”。可以把 API 理解为软件与软件之间的通用接口和翻译官,就像电源插座一样 —— 只要符合标准,就能直接 “对接取电”,不需要关心内部如何实现。
为什么需要 API 接口?
-
让不同系统能够互相调用能力 本项目中,Qt 程序本身不具备 AI 对话能力,无法自己运行大模型,必须通过 API 调用云端扣子智能体的服务。
-
屏蔽底层复杂性,降低开发难度 大模型需要强大算力、专业框架、海量数据与复杂训练,普通开发者无法直接实现。 API 把这些复杂能力封装成一个简单的网络接口,我们只需要发送文字,就能拿到 AI 回复。
-
实现功能解耦,便于维护与升级 前端 Qt 界面、后端 AI 服务、业务逻辑可以独立开发、独立更新。 模型升级、服务更换时,只要 API 不变,客户端代码无需修改。
-
统一权限与安全管控 通过 API 可以做身份验证、调用限流、日志记录,保证接口不被滥用。
每个大模型的 API 是否不一样?
不一样,且不能直接通用。
不同厂商的大模型(如扣子 Coze、百度文心、阿里通义、科大讯飞等),在以下方面都存在差异:
- 接口请求地址(URL)不同
- 身份验证方式(Token/Key 格式)不同
- 请求参数的 JSON 结构不同
- 字段名称、层级嵌套不同
- 返回结果的数据格式不同
- 流式输出(SSE)的协议格式不同
但它们底层都基于 HTTP/HTTPS 协议,整体思路一致:发送 JSON → 接收 JSON / SSE 流。
在本项目中,我们直接对接扣子智能体专属 API,因此只需按照 Coze 平台规定的格式发送请求即可,无需适配其他模型接口
问题2:平台提供哪几类接口?
扣子平台主要提供两类接口:
| 类型 | 特点 | 适用场景 |
|---|---|---|
| 普通POST同步接口 | 一次性返回完整回答 | 短文本、容忍延迟 |
| SSE流式接口 | 分片实时推送回答,打字机效果 | AI对话、实时推送 |
本项目采用SSE流式接口,以实现AI回复逐步显示的效果。
问题3:什么是API/SDK?
| 术语 | 含义 | 类比 |
|---|---|---|
| API(接口) | 调用服务的标准,包括地址、参数格式、返回格式等 | 餐厅的菜单(你只能按菜单点菜) |
| SDK(软件开发工具包) | 封装了API调用的现成库,通常包含语言封装、错误处理等 | 预制菜包(打开即用) |
本项目没有使用Coze的官方SDK,而是直接调用HTTP API,因为需要跨平台且灵活控制细节(比如SSE解析)。
问题4:什么是个人访问令牌?为什么需要?什么作用?
个人访问令牌(Personal Access Token)是一串加密的字符串,相当于长期有效的“临时密码”。
为什么需要: API是公开的URL,如果不验证身份,任何人都可以调用你的智能体,消耗你的资源或获取敏感信息。
作用:
-
鉴权:请求时放在
Authorization: Bearer <token>头中,服务器验证令牌合法性 -
权限控制:不同令牌可绑定不同的智能体、不同的数据权限
-
统计与限流:平台通过令牌识别调用方,进行计费、限流
问题5:SSE是什么?为什么QT能解析SSE?
SSE(Server-Sent Events,服务端推送事件)是一种基于HTTP的服务器推送技术,允许服务器主动向客户端发送多次数据。
与WebSocket区别:
-
SSE是单向(服务端→客户端),协议更轻,自动重连
-
WebSocket是双向,适合聊天室、游戏等场景
SSE的本质就是分段返回的HTTP响应。Qt的QNetworkReply::readAll()会一次性读取当前收到的所有数据。因为网络是分包的,你可能收到: 第1次readyRead:data: {"type":"answer","content":{"answer":"您"}} 第2次readyRead:data: {"type":"answer","content":{"answer":"好"}} finished信号在所有数据都收到后才触发,所以我们可以在finished里处理完整数据。
问题6:为什么这个功能用到了HTTP?
-
HTTP是互联网上应用最广泛的协议,任何联网设备都能发送HTTP请求
-
SSE本身就是基于HTTP的扩展,不需要额外协议
-
Qt内置了
QNetworkAccessManager,对HTTP和HTTPS支持完善,实现简单
问题7:涉及到了哪些Qt知识?为什么Qt能解析CMD命令?
| 知识模块 | 在本项目中的作用 |
|---|---|
Qt Network (QT += network) |
提供QNetworkAccessManager、QNetworkRequest、QNetworkReply,完成HTTP请求与响应 |
Qt Core (QObject, 信号槽) |
CozeAI继承QObject,利用信号槽异步传递网络响应给UI,避免界面卡顿 |
Qt JSON (QJsonDocument, QJsonObject) |
构造请求体,解析服务器返回的JSON片段 |
Qt 正则表达式 (QRegularExpression) |
用于从文本中提取隐藏命令(如【CMD:OPEN_FEE】) |
Qt 字符串处理 (QString, QByteArray) |
处理SSE数据行、拼接回复、编码转换 |
- 为什么Qt能解析CMD命令?
- 因为AI返回的answer是一个普通的QString字符串。QString提供了各种查找、匹配的方法。QRegularExpression是Qt对正则表达式的封装,它可以在字符串中搜索匹配模式,返回匹配位置和捕获的内容。 整个过程就像在文章中查找特定格式的词语: text 输入:你好,请帮我缴费【CMD:OPEN_FEE】 ↓ 正则匹配 找到:【CMD:OPEN_FEE】 ↓ 提取 命令:OPEN_FEE ↓ 执行 跳转到缴费页面
问题8:JSON是什么?
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,类似Python字典或C++的map,使用{key: value}表示。
为什么AI API都用JSON:
1.结构灵活,支持嵌套(如本项目的content.query.prompt):
-
AI对话往往需要传递结构化的信息,而JSON可以轻松表达这种层级关系。以本项目为例,发送给AI的请求体需要包含"用户的问题"、"会话ID"、"项目ID"等多个信息,其中"用户的问题"本身又有"类型"和"内容"两个属性。
//JSON可以用嵌套的方式清晰表达这种结构: json { "content": { "query": { "prompt": [ { "type": "text", "content": { "text": "物业费多少钱" } } ] } }, "type": "query", "session_id": "rbkdP69JpgfngtdcBejyt", "project_id": 7646999877276155939 } -
可以看到,
content里面包着query,query里面包着prompt数组,数组里又包着具体的消息内容和类型。这种多层嵌套的结构如果用其他格式(如纯文本、CSV)来表示,会非常复杂且难以解析。而JSON天生支持对象套对象、数组套对象的写法,与AI API复杂的参数结构天然匹配。
2,人类可读,便于调试(curl返回后一眼能看出问题)
data: {"type":"answer","content":{"answer":"您"}}
data: {"type":"answer","content":{"answer":"好"}}
- 你可以立刻看出:
type是answer,content里嵌套的answer字段的值是"您"和"好"。如果返回的是二进制格式或某种自定义格式,你必须先写解析代码才能看懂内容,大大增加调试难度。
3,几乎所有编程语言都有成熟解析库(Qt提供了QJsonDocument)
-
AI服务需要被各种语言的客户端调用——可能是C++写的桌面软件,可能是Python写的后端服务,也可能是JavaScript写的前端页面。JSON作为一种语言无关的文本格式,几乎所有编程语言都提供了成熟的解析库
-
本项目使用Qt,Qt提供了完整的JSON处理类:
QJsonDocument(解析/生成JSON文档)、QJsonObject(操作对象)、QJsonArray(操作数组)、QJsonValue(操作值)。开发者只需要几行代码就能完成解析,无需自己写字符串处理逻辑。cpp // Qt中解析JSON只需几行 QJsonDocument doc = QJsonDocument::fromJson(jsonStr.toUtf8()); QJsonObject obj = doc.object(); QString answer = obj["content"]["answer"].toString();
问题9:什么是代理服务器?为什么我这个用到的是代理服务器?
代理服务器是客户端与目标服务器之间的中间服务。客户端不直接访问最终服务器,而是先把请求发给代理,由代理转发到目标服务,并将结果返回给客户端。在AI调用中,它可以做:
- 隐藏真实的后端 API 地址,提高安全性;
- 统一请求入口,方便做权限校验、日志记录、限流管控;
- 对请求 / 响应格式进行转换,简化前端调用复杂度;
- 实现负载均衡、缓存加速,提升接口稳定性
本项目为什么要使用代理服务器?
本项目中调用的接口地址: https://nxjxfxyw45.coze.site/stream_run 并不是扣子(Coze)官方原生 API 地址,而是一个专门封装过的代理服务器地址。
使用这个代理的主要原因:
-
简化请求结构,降低 Qt 开发难度 扣子官方 API 的请求体结构复杂,包含多层嵌套 JSON、BotId、UserId、ConversationId 等大量参数。 代理服务器对官方接口做了一层封装,让我们只需要传入简单的文本内容即可调用,不用处理复杂的官方字段。
-
自动路由到对应智能体,无需配置 Bot ID 原生 Coze 接口需要明确指定 Bot ID 才能调用对应智能体。 该代理通过 Token 就能自动识别并绑定对应的智能体,Qt 端无需额外配置 Bot 相关参数。
-
统一接口格式,适配 SSE 流式输出 代理对返回的 SSE 数据流做了格式规整,让 Qt 端更容易解析 “data:” 分片数据,实现稳定的打字机效果。
-
提高安全性与可维护性 真实的 Coze 官方地址、密钥校验逻辑、权限策略都在代理后端处理,Qt 客户端只面对一个简单稳定的接口,便于后期更换模型、调整服务而不改动前端代码。
简单总结: 代理服务器把复杂、繁琐、参数多的官方 AI 接口,封装成了简单、统一、易接入的简化接口,让我们在 Qt 中可以更轻松、更稳定地实现 AI 对话功能。
问题10:如何发送和接收信息,并显示在界面上?
发送流程:
-
用户在
QLineEdit输入文本,点击发送按钮 -
MainWindow::on_aiSendBtn_clicked获取文本,调用cozeAI->sendMessage(txt) -
sendMessage构造QNetworkRequest,添加Authorization头,用QJsonDocument生成请求体,通过netManager->post发送
接收与显示流程:
-
服务器分片返回
data: {...}\n\n格式数据,QNetworkReply触发finished信号 -
在
onReplyFinished中读取所有数据,按行解析data:前缀,提取content.answer字段并拼接到fullAnswer -
解析完毕后
emit replyReceived(fullAnswer) -
MainWindow中连接的Lambda函数被调用,将fullAnswer追加到QTextEdit中显示
问题11:curl是什么?
curl是一个命令行下的HTTP客户端,可发送任意HTTP请求并打印响应。
在本项目中的作用: 在编写Qt代码前,先用curl测试API是否通畅,确认参数格式、Token是否正确。curl命令就是一份“可执行的接口说明书”。
以项目中的命令为例:
bash
curl --location --request POST "https://nxjxfxyw45.coze.site/stream_run" \
--header "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
--header "Content-Type: application/json" \
--data '{
"content": {
"query": {
"prompt": [
{
"type": "text",
"content": {
"text": "你好"
}
}
]
}
},
"type": "query",
"session_id": "rbkdP69JpgfngtdcBejyt",
"project_id": 7646999877276155939
}'
| 参数 | 含义 | 对应Qt代码 |
|---|---|---|
curl |
命令本身 | - |
--location |
跟随重定向(如果服务器返回301/302,自动跳转) | QNetworkRequest默认支持 |
--request POST |
指定HTTP方法为POST | netManager->post() |
"https://..." |
请求的URL地址 | QUrl(API_URL) |
--header "Authorization: Bearer xxx" |
添加请求头,用于身份验证 | req.setRawHeader("Authorization", ...) |
--header "Content-Type: application/json" |
告诉服务器请求体是JSON格式 | req.setHeader(ContentTypeHeader, "application/json") |
--data '{...}' |
请求体内容(JSON格式) | netManager->post(req, jsonData) |
//curl返回结果解读
bash
data: {"type":"answer","content":{"answer":"您"}}
data: {"type":"answer","content":{"answer":"好"}}
data: {"type":"answer","content":{"answer":","}}
data: {"type":"answer","content":{"answer":"欢迎使用"}}
data: [DONE]
解读:
-
每行以
data:开头 -
后面跟着JSON字符串
-
[DONE]表示所有内容发送完毕
从curl到Qt代码的转换对照表:
| curl部分 | Qt代码 |
|---|---|
--request POST |
netManager->post(req, data) |
--header "Authorization: Bearer xxx" |
req.setRawHeader("Authorization", "Bearer xxx") |
--header "Content-Type: application/json" |
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json") |
--data '{...}' |
QJsonDocument(root).toJson() |
| SSE响应解析 | onReplyFinished中按行解析 data: 前缀 |
问题12:网络在这个过程中怎么设置的?
| 层面 | 设置内容 | 代码位置 |
|---|---|---|
| 项目配置 | .pro文件中添加QT += network |
项目根目录 |
| 请求参数 | URL、请求头(Authorization)、请求体(JSON) | CozeAI::sendMessage |
| 代理设置(可选) | 如果本机需要代理才能访问外网,可设置QNetworkProxy |
本项目未使用,默认直连 |
| SSL/TLS | https地址默认启用 |
本项目使用https,未特殊配置 |
本项目中网络配置的核心是:
-
使用代理服务器地址
-
通过
Bearer token鉴权 -
请求体为自定义JSON格式
-
响应解析为SSE流式
四、系统架构设计
4.1 整体架构图
整体分为三层:
- 扣子智能体服务端:云端部署大模型 + 提示词逻辑,接收 API 入参,SSE 流式返回 AI 结果;
- Qt 中间逻辑层(CozeAI 封装类):封装网络请求、SSE 协议解析、数据拼接、异常捕获;
- Qt UI 表现层 (MainWindow):输入框录入用户提问、按钮触发请求、文本框实时展示流式 AI 回复。

数据流:用户输入问题→UI 层调用 CozeAI 接口→发起 SSE 请求到 Coze 云端→云端分片推送数据→CozeAI 解析 SSE 报文→信号推送文本→UI 实时刷新内容。
4.2 数据流向

1.用户在Qt界面输入提问内容,点击发送按钮;
2.MainWindow捕获输入内容,调用CozeAI::sendMessage()将用户问题传入AI通信类;
3.CozeAI组装带Bearer Token鉴权的请求头与JSON请求体,通过HTTP POST把请求发送至代理服务器;
4.代理服务器接收请求并转发至扣子官方API,扣子云端校验访问凭证、运行预设智能体逻辑,分段生成回答内容,以SSE数据流格式逐层原路下发;
5.CozeAI依靠QNetworkReply接收全部SSE原始数据,按行过滤data:报文前缀、剔除无效数据,循环拼接所有AI回复片段;
6.识别到SSE末尾[DONE]结束标记后停止解析,将拼接完整的AI文本通过replyReceived信号发送至MainWindow;
7.主窗口接收信号数据,把AI回复内容一次性渲染到聊天输入框,本次SSE连接生命周期结束。
五、扣子平台配置步骤
5.1 需要获取的内容
| 参数 | 说明 |
|---|---|
| API地址 | 代理服务器地址 |
| Token | 个人访问令牌 |
| session_id | 会话ID,保持对话上下文 |
| project_id | 项目ID |
说明:本文针对的是”项目开发“中创建的智能体 。
5.2 获取方式
步骤一:创建智能体并进行部署
📷 :
步骤二:获取API接口,curl测试命令
📷 :
//在API接入页面,平台会自动生成curl测试命令,可用于验证接口是否正常。 //curl命令就是一份“可执行的接口说明书”。 curl --location --request POST "https://nxjxfxyw45.coze.site/stream_run" \ --header "Authorization: Bearer <YOUR_TOKEN>" \ --header "Content-Type: application/json" \ --data '{ "content": { "query": { "prompt": [ { "type": "text", "content": { "text": "物业费多少钱" } } ] } }, "type": "query", "session_id": "rbkdP69JpgfngtdcBejyt", "project_id": 7646999877276155939 }'
步骤三:获取个人访问令牌(Token)
📷 :
六、核心代码详解
前置配置:.pro 文件需要添加模块:
QT += network📌Qt将网络功能单独放在
network模块中,不添加就无法使用QNetworkAccessManager、QNetworkRequest等网络相关的类
6.1 核心参数说明
void CozeAI::sendMessage(const QString &question)
{
// ① API地址(请求的目标地址,所有角色共用,固定不变)
//代理服务器的固定地址,所有请求都发到这里
const QString API_URL = "https://nxjxfxyw45.coze.site/stream_run";
// ② Authorization Token(身份验证),Bearer是标准的Token认证方式,后面跟着Token字符串
req.setRawHeader("Authorization", ("Bearer " + currentApiToken).toUtf8());
// ③ session_id(会话标识),相同的ID能让AI记住之前的对话,相当于聊天记录ID
root["session_id"] = "rbkdP69JpgfngtdcBejyt";
// ④ project_id(项目标识),告诉服务器是哪个项目在调用,固定值
root["project_id"] = 7646999877276155939;
}
6.2 CozeAI.h - 头文件
AI 功能的接口说明书:它不实现功能,只负责告诉编译器这个类有什么函数、有什么变量、有什么信号。
#ifndef COZEAI_H
#define COZEAI_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
/**
* @brief CozeAI 类
* 负责与扣子智能体API进行网络通信,发送问题、接收AI回复
*/
class CozeAI : public QObject
{
Q_OBJECT
public:
// 构造函数:初始化AI网络模块
explicit CozeAI(QObject *parent = nullptr);
// 设置API访问令牌,配置身份密钥,让服务器知道是谁在调用AI
void setApiToken(const QString &apiToken);
// 发送用户问题,把用户输入的文字发送给扣子智能体
void sendMessage(const QString &question);
signals:
// AI回复完成信号,当AI返回完整回答后,发射信号通知UI界面显示内容
void replyReceived(const QString &content);
private slots:
// 【核心槽函数】网络请求结束自动调用,接收服务器返回的数据,解析SSE流式响应
void onReplyFinished(QNetworkReply *reply);
private:
QNetworkAccessManager *netManager; // 网络管理器:Qt官方网络工具,负责发送/接收请求
QString currentApiToken; // API令牌:用户身份验证密钥,接口鉴权使用
// ========== 固定API配置(与扣子平台一一对应)==========
const QString API_URL = "https://nxjxfxyw45.coze.site/stream_run"; // 平台接口地址
const QString SESSION_ID = "rbkdP69JpgfngtdcBejyt"; // 对话会话ID
const int PROJECT_ID = 7646999877276155939; // 项目编号
};
#endif
6.3 CozeAI.cpp - 实现文件
AI 功能的真正实现层:这是整个 AI 功能最重要的文件,所有网络请求、解析、拼接、发送信号都在这里
#include "CozeAI.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>
// 构造函数:初始化AI网络模块
CozeAI::CozeAI(QObject *parent) : QObject(parent)
{
// 创建Qt网络管理器,负责所有HTTP请求
netManager = new QNetworkAccessManager(this);
// 绑定信号槽:请求完成后自动调用 onReplyFinished
connect(netManager, &QNetworkAccessManager::finished,
this, &CozeAI::onReplyFinished);
}
// 设置API访问令牌(身份密钥)
// 作用:保存从扣子平台获取的Token,用于接口鉴权
void CozeAI::setApiToken(const QString &apiToken)
{
currentApiToken = apiToken;
}
// 发送用户问题给AI
void CozeAI::sendMessage(const QString &question)
{
// 安全判断:如果没设置Token,直接返回错误
if(currentApiToken.isEmpty()) {
emit replyReceived("[错误]AI密钥未配置");
return;
}
// 第1步:构建网络请求对象
QUrl url(API_URL);
QNetworkRequest req(url);
// 第2步:设置请求头(身份验证+数据格式)
req.setRawHeader("Authorization", ("Bearer " + currentApiToken).toUtf8());
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
// ======================
// 第3步:构建符合扣子API要求的JSON格式请求体
// ======================
QJsonObject root;
// 封装用户输入的文本
QJsonObject contentTextObj;
contentTextObj["text"] = question;
// 封装消息类型和内容
QJsonObject promptItem;
promptItem["type"] = "text";
promptItem["content"] = contentTextObj;
// 放入消息数组
QJsonArray promptArray;
promptArray.append(promptItem);
// 组装query结构
QJsonObject queryObj;
queryObj["prompt"] = promptArray;
// 组装content结构
QJsonObject contentObj;
contentObj["query"] = queryObj;
// 组装最终JSON根对象
root["content"] = contentObj;
root["type"] = "query";
root["session_id"] = SESSION_ID;
root["project_id"] = PROJECT_ID;
// 将JSON转为字节流,准备发送
QByteArray jsonData = QJsonDocument(root).toJson(QJsonDocument::Compact);
// 第4步:发送POST请求到扣子服务器
netManager->post(req, jsonData);
}
// 网络请求完成,解析服务器返回的SSE数据
void CozeAI::onReplyFinished(QNetworkReply *reply)
{
// 判断网络是否出错
if (reply->error() != QNetworkReply::NoError) {
emit replyReceived("[网络错误]" + reply->errorString());
reply->deleteLater();
return;
}
// 读取服务器返回的全部原始数据
QByteArray raw = reply->readAll();
QString fullAnswer = ""; // 存储最终拼接好的AI回答
// SSE数据按行分割,逐行解析
QStringList lines = QString(raw).split("\n");
for (QString line : lines) {
line = line.trimmed(); // 去掉空格换行
// 只处理以 data: 开头的有效行
if (!line.startsWith("data: ")) continue;
// 去掉前缀 "data: ",取出后面的JSON内容
QString jsonStr = line.mid(6);
// 如果是结束标记,停止解析
if (jsonStr == "[DONE]") break;
// 转为JSON对象
QJsonDocument doc = QJsonDocument::fromJson(jsonStr.toUtf8());
if (!doc.isObject()) continue;
QJsonObject obj = doc.object();
// 判断是否是AI回复内容
if (obj["type"].toString() == "answer") {
QJsonValue contentVal = obj["content"];
if (contentVal.isObject()) {
QJsonObject contentObj = contentVal.toObject();
// 提取answer字段,拼接到完整回答中
if (contentObj.contains("answer")) {
fullAnswer += contentObj["answer"].toString();
}
}
}
}
// 如果没有获取到有效内容,给出提示
if (fullAnswer.isEmpty()) {
fullAnswer = "[AI] 未获取到有效回复";
}
// 发送信号,把AI回复传给UI界面显示
emit replyReceived(fullAnswer);
// 释放网络请求资源
reply->deleteLater();
}
6.4 MainWindow中的集成
UI 界面与 AI 功能的连接层:它不做网络通信,只负责界面显示、用户操作、调用 AI、展示结果。
#include "CozeAI.h"
// AI 智能体密钥配置,不同角色(业主/物业/管理员)使用不同的 Token
// 业主角色专用 AI 访问令牌(从扣子平台获取)
const QString OWNER_AI_TOKEN = "eyJhbGciOiJSUzI1NiIs...";
// 主窗口构造函数中初始化 AI,创建 AI 通信对象
cozeAI = new CozeAI(this);
// 根据当前登录用户的角色,分配对应的 AI 密钥
if (currentUser.role == "owner") {
// 如果是业主,设置业主专用 AI Token
cozeAI->setApiToken(OWNER_AI_TOKEN);
}
// 绑定信号与槽:接收 AI 返回的消息,AI 回答完成后,自动显示到界面聊天框
connect(cozeAI, &CozeAI::replyReceived, this, [=](const QString &rawAnswer){
// 将 AI 返回的原始内容追加到聊天显示框
ui->aiTextEdit->append("[AI] " + rawAnswer);
});
// 发送按钮点击事件,获取用户输入,发送给 AI,并显示在聊天界面
void MainWindow::on_aiSendBtn_clicked()
{
// 获取输入框内容,并去除首尾空格
QString txt = ui->aiInputEdit->text().trimmed();
// 如果输入为空,不执行发送
if(txt.isEmpty()) return;
// 将用户输入的内容显示到聊天框
ui->aiTextEdit->append("[我] " + txt);
// 清空输入框,方便下次输入
ui->aiInputEdit->clear();
// 把问题发送给 AI 服务
cozeAI->sendMessage(txt);
}
6.5 正则解析隐藏命令
cpp
void MainWindow::parseAndExecuteCMD(const QString &answer)
{
// 正则表达式详解:
// 【CMD:([^】]+)】
// | | |
// | | └─ 结束符】
// | └──────── 捕获组:匹配除】以外的任意字符
// └──────────── 固定开头【CMD:
QRegularExpression regex("【CMD:([^】]+)】");
QRegularExpressionMatchIterator it = regex.globalMatch(answer);
while (it.hasNext()) {
QRegularExpressionMatch match = it.next();
QString cmd = match.captured(1); // 捕获组的内容
if (cmd == "OPEN_FEE") {
ui->stackedWidget->setCurrentIndex(3); // 跳转缴费页
}
else if (cmd == "OPEN_REPAIR") {
ui->stackedWidget->setCurrentIndex(2); // 跳转报修页
}
else if (cmd == "OPEN_NOTICE") {
ui->stackedWidget->setCurrentIndex(1); // 跳转公告页
}
}
}
七、智能体实现页面跳转的提示词设计
7.1 基础提示词
# 人设
你是一个专业的物业智能管家,名叫"小管家"。服务对象是小区的业主用户。
性格特点:热情、耐心、专业、友好。回答简洁明了,不要过于冗长。
# 系统跳转命令
当需要引导用户跳转页面时,在回复末尾加上命令标记:
- 【CMD:OPEN_NOTICE】- 打开公告页面
- 【CMD:OPEN_REPAIR】- 打开报修页面
- 【CMD:OPEN_FEE】- 打开缴费页面
------>为什么要在提示词里写规则?
------:AI大模型(如GPT、豆包)本质上是根据提示词来“理解”自己要做什么。如果你不告诉它要输出命令,它就只会输出普通文本,你的程序就识别不到【CMD:XXX】。
# 功能范围
1. 物业服务:物业费查询、报修处理、公告查看
2. 生活服务:天气查询、美食推荐、出行规划
3. 健康咨询:提供健康建议,引导就医
4. 法律参考:提供基础法律常识,引导专业咨询
7.2 回答模板示例
| 用户问 | AI回复 |
|---|---|
| 物业费多少钱 | 您好,物业费根据您的房型面积计算,具体金额请在缴费页面查看详情。【CMD:OPEN_FEE】 |
| 家里水管漏水 | 请您尽快提交维修申请,我们会安排师傅上门处理。【CMD:OPEN_REPAIR】 |
| 今天天气怎么样 | 调用天气插件获取实时天气信息并返回 |
总结
该系统已成功应用于小区管家物业综合服务平台,为业主提供了便捷、智能的物业服务体验,具有良好的实用价值和扩展性。
扩展方向:
一、智能体能力增强
- 切换和扩展智能体,使业主、物业、管理员等不同角色获得定制化服务体验
- 针对缴费、报修、投诉等场景设置专用智能体,实现垂直领域回答更专业精准,并根据任务复杂度在不同大模型间动态切换以优化性能与成本。
- 同时添加插件能力,使AI管家可调用天气插件提供出行建议、日历插件实现缴费提醒、地图插件查询周边设施、知识库插件自动回答物业政策,将AI从对话机器人升级为具备行动能力的智能助理。
- 优化提示词,通过增加命令类型(OPEN_FEE、OPEN_REPAIR等)实现更多页面跳转控制,丰富人设设定使回复更自然亲切,添加上下文记忆规则提供个性化服务。
二、对话历史记录功能
- 添加对话历史记录功能,将用户与AI的问答持久化存储到数据库,设计会话表、消息表和反馈表。
- 基于历史记录,系统可实现多轮对话上下文记忆——再次提问时自动加载历史对话,使AI理解指代关系、回答更连贯自然。
- 实现用户行为分析,提供历史查询回溯界面供用户查看交流记录,支持管理员导出数据进行分析,了解业主高频诉求,为服务优化提供数据支撑。
三、语音识别集成
- 集成语音识别功能,实现从纯文字到“语音输入+语音输出”的全方位语音对话体验。
- 进一步增强包括语音唤醒(关键词免点击唤醒)、连续对话模式、方言支持及情绪分析(识别愤怒语气时优先转人工),真正实现“说话就能办事”的无障碍使用体验。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐









所有评论(0)