用 Qt 做本地 AI 桌面助手:架构设计、任务编排与聊天界面实现

过去两年,AI 应用大多集中在网页和云端服务里。但对于很多桌面场景来说,用户真正需要的并不只是一个“聊天框”,而是一个能够长期运行、可接入本地工具、兼顾隐私与效率的桌面助手。如果你已经在用 Qt 做桌面软件,那么把 AI 助手能力整合进现有产品,几乎是一个迟早会遇到的课题。

本文不打算教你“如何调一个大模型接口”,而是从产品和工程的角度,讲清楚:

  • 本地 AI 桌面助手应如何拆模块
  • 聊天界面该怎么组织
  • 任务面板和会话管理如何设计
  • 模型调用层如何与 UI 解耦
  • 后续如何支持插件和工具调用

一、先想清楚:桌面 AI 助手不是“加个聊天窗口”

很多项目在接入 AI 时,第一反应是:

“做个聊天页,发请求,显示返回内容,不就行了吗?”

这在 Demo 阶段没问题,但一到真实产品就会暴露问题:

  • 会话没法持久化
  • 模型切换困难
  • 回复流式输出卡 UI
  • 工具调用逻辑写得到处都是
  • 用户输入、任务执行、历史记录、附件处理全耦在一起

所以,桌面 AI 助手更合理的定义应该是:

一个由 UI 层、会话层、任务层、模型层、工具层共同组成的本地应用子系统。


二、推荐的整体架构

一个比较稳妥的架构拆分可以是这样:

┌──────────────────────────────┐
│         Qt UI 表现层         │
│ 聊天窗口 / 侧边栏 / 任务面板 │
└──────────────┬───────────────┘
               │
┌──────────────▼───────────────┐
│        会话与状态管理层       │
│ SessionManager / ViewModel   │
└──────────────┬───────────────┘
               │
┌──────────────▼───────────────┐
│         任务编排与调度层      │
│ Prompt组织 / 工具路由 / 队列   │
└──────────────┬───────────────┘
               │
┌──────────────▼───────────────┐
│         模型调用抽象层        │
│ LocalModel / OpenAI / Hybrid │
└──────────────┬───────────────┘
               │
┌──────────────▼───────────────┐
│      工具与存储扩展层         │
│ 文件检索 / 命令执行 / DB / 插件│
└──────────────────────────────┘

这个拆法的好处是:

  • UI 不直接耦合具体模型
  • 模型切换成本低
  • 后续容易接入本地工具或 MCP 类能力
  • 聊天助手能逐渐从“问答框”进化成“任务系统”

三、UI 层应该怎么设计?

3.1 聊天区只负责呈现,不负责业务决策

聊天页面建议拆成:

  • 消息列表区:展示用户消息、AI 回复、系统提示
  • 输入区:支持普通输入、快捷发送、附件拖拽
  • 会话侧边栏:切换历史会话
  • 任务面板:展示后台任务、执行进度、失败重试

其中,消息列表本身只负责“显示状态”,不要让它直接去管模型请求、数据库写入、工具执行这些业务。

3.2 建议引入 ViewModel / Presenter 层

如果你用 Qt Widgets,可以用 Presenter;如果是 QML,更适合用 QObject + Q_PROPERTY 风格的 ViewModel。

例如:

class ChatViewModel : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString currentInput READ currentInput WRITE setCurrentInput NOTIFY currentInputChanged)
    Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
public:
    Q_INVOKABLE void sendMessage();
signals:
    void currentInputChanged();
    void busyChanged();
    void messageAppended();
};

这样 UI 层只绑定 ViewModel,不直接碰底层服务。


四、会话管理是桌面助手能不能“长期使用”的关键

一个真正可用的 AI 助手,不可能每次启动都从空白开始。

你至少要考虑:

  • 会话标题怎么生成
  • 消息历史怎么存储
  • 会话切换如何恢复上下文
  • 是否支持固定系统提示词
  • 是否支持 pin 住重要会话

4.1 建议的数据模型

struct ChatMessage {
    QString role;        // user / assistant / system / tool
    QString content;
    QDateTime createdAt;
    QString taskId;
};

struct ChatSession {
    QString sessionId;
    QString title;
    QVector<ChatMessage> messages;
    QDateTime updatedAt;
};

持久化可以用:

  • SQLite
  • JSON 文件
  • 自定义轻量数据库封装

如果只是桌面应用,SQLite 往往是最稳的选择。


五、模型调用层必须抽象,不要把 API 写死在 UI 里

桌面 AI 助手未来很可能会出现几种模型来源:

  • 本地模型(如 Ollama、llama.cpp、vLLM 网关)
  • 云端模型(OpenAI、DeepSeek、Kimi 等)
  • 混合策略(本地优先,云端兜底)

如果一开始就把请求代码写在 MainWindow::onSendClicked() 里,后面会非常痛苦。

5.1 推荐接口

class IChatProvider {
public:
    virtual ~IChatProvider() = default;
    virtual void send(const QVector<ChatMessage>& context) = 0;
};

然后分别实现:

  • OpenAiProvider
  • LocalModelProvider
  • HybridProvider

这样后续切模型、切服务地址、加重试策略都比较自然。


六、任务编排层:让助手从“聊天”走向“做事”

如果你的目标只是问答,聊天层就够了;但如果你要的是“桌面助手”,那它迟早要处理:

  • 打开本地文件
  • 检索历史资料
  • 执行某个批处理任务
  • 组合多个子步骤

这就需要一个任务编排层。

6.1 编排层主要做什么?

  • 组装 Prompt
  • 决定是否调用工具
  • 跟踪子任务状态
  • 把执行过程同步回 UI

可以理解为:

模型层负责生成,编排层负责决定下一步干什么。

6.2 UI 上要不要显示任务过程?

建议要。

否则用户只会看到:

  • “正在思考……”
  • “完成了。”

中间发生了什么完全不可见,体验很差。

更好的做法是把执行过程映射到任务面板里:

  • 读取文件中
  • 正在解析内容
  • 正在调用模型
  • 正在生成摘要
  • 已完成

这样用户对助手的信任度会高很多。


七、流式输出和 UI 响应怎么兼顾?

AI 回复最常见的体验需求是:边生成边显示

但如果你处理不好,就会出现:

  • 文本疯狂 repaint
  • QML 富文本区滚动抖动
  • 主线程被大量 append 操作拖慢

建议做法

  • 后台线程接收 token
  • 主线程按小批次刷新 UI
  • 不要每个 token 都立即更新界面

例如每 30~50ms 合并一次文本块再刷到消息气泡里,这样视觉上已经足够“实时”,但性能更稳定。


八、插件与工具扩展:一开始就预留接口

就算你现在不做工具调用,也建议先把接口留好。

比如:

class IAssistantTool {
public:
    virtual ~IAssistantTool() = default;
    virtual QString name() const = 0;
    virtual QString description() const = 0;
    virtual QString execute(const QVariantMap& args) = 0;
};

未来你可以逐步接入:

  • 文件搜索工具
  • 本地命令执行工具
  • 项目知识库工具
  • 文档摘要工具

这样架构演进时不会推倒重来。


九、一个落地顺序建议

如果你准备真正做一个 Qt 本地 AI 桌面助手,建议按下面节奏推进:

第一阶段:最小可用版本

  • 聊天窗口
  • 基础消息列表
  • 单模型调用
  • 会话持久化

第二阶段:工程化增强

  • 模型抽象层
  • 流式输出
  • 错误处理与重试
  • 会话搜索 / 删除 / 重命名

第三阶段:助手化升级

  • 任务面板
  • 工具调用
  • 文件附件
  • 插件体系

第四阶段:产品化细节

  • 多窗口协同
  • 快捷键唤起
  • 系统托盘
  • 自动升级 / 配置中心

十、结语

用 Qt 做本地 AI 桌面助手,难点从来都不是“能不能接上模型”,而是:

如何把 AI 能力组织成一个长期可维护、可扩展、可交付的桌面产品模块。

如果你一开始就把聊天、任务、模型、工具、存储拆清楚,后续无论是换模型、加插件还是做产品化,都能顺着现有架构往前走。

反过来,如果一上来就把所有逻辑堆进一个窗口类里,后面几乎一定会重构。

所以这类项目最值钱的,不是“某个模型 API 调通了”,而是:

  • 架构有没有边界
  • UI 和业务有没有解耦
  • 状态和任务有没有被管理好

这决定了你的 AI 助手,最终是一个 Demo,还是一个能长期迭代的产品。


本地 AI 桌面助手真正的竞争力,不只是回答得像不像,而是它能不能稳定、安静、长期地待在用户的桌面上,成为一个真正有用的工具。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐