昨天晚上躺床上睡不着,思考很多事,我总是这样,在遇到重大变化的时候就容易失眠(最近好一些了)之前更是一晚上睡不着,思考得太多,做的太少

虚假的todo:

  • 把混淆矩阵页面优化一下
  • 添加子文件夹功能
  • 添加局部屏蔽和全局屏蔽标注
  • 添加推理结果查看界面(标注结果/推理结果)

真正的todo:

  • 看webai-device推理客户端的C++代码
  • 看当前的lite客户端推理部分和之前的iosAPP后端推理管线
  • 从全局看项目,用了哪些技术栈,为什么要用?

上午主要就是把webai-lite和当前原型图对齐了一下,加了一个三级文件夹,到下午都没加完,gpt限额+电脑卡死,只好重启电脑

上午还把推理客户端的代码看了一些

上午还收到消息,明天有一个专业实验,加上要实习证明,让公司给我开了一个,打算拿了下午下班后就寄出去,专业实验也只能下班之后找找代课了

今天后面看leader看不看原型图,不看的话我就把局部屏蔽和全局屏蔽区加上,再优化一下混淆矩阵界面就完事~

我chovy,我发现原来是公司内部的中转站给我限额了,每天100刀,天才程序员陨落了……今天下午只能用cc+ds了

下午leader拉了个会,主要就是让我讲一下原型图,然后也说了下改进方向

1
2
3
4
5
6
7
8
9
10
11
12
13
加入无监督模式

新建项目 + 仿真选项
导入模型流 推理
导入大模型 只支持测试(推理)

定位模型

混淆矩阵 去零

推理结果页面 + 反馈

新建项目 模型类型不能修改

里面有些东西我都不知道是啥,他们说到的时候我都是蒙的,比如定位模型、无监督模式这TM都是啥啊!

不管了,做到哪是哪吧,反正干完这个星期就不干了。接下来的几天主要就是把实习期间学到的东西总结总结,然后能投一点东西是一点(特别是mentor做的agent,听说项目已经收尾了),我现在还呆在这里纯粹是因为想要多混几天的工资,还有就是多偷一点东西

今天下午还和bite学管、鹏哥聊了一会,主要就是现在的企业技术相关的都要求会Ai,并且要有自己的一套AI开发范式,并且在面试的时候不仅看你会不会AI,也会更看重基本功一些,鹏哥说今年bite的实习率比去年还多了5%,鹏哥和学管也说根据我的实际情况来定,学校的考试还是比较重要的,也都认为我可以先结束这段实习之后再参加秋招,没必要骑驴找马

下班啦,今天感觉没干啥,算法也没刷,就看了一点推理客户端代码,回去还得刷一道算法题!


今日工作内容
Task: 样本集子文件夹层级管理 — 前后端实现 + 前端树形展示统一
完成后端 sample_sets v2 数据模型(文件夹树 CRUD、迁移到未分配区、训练/推理递归展开)、前端 WorkspaceSampleSidebar 树形结构(展开/收起根节点、文件夹树形连接线、统一样式)、数据页/标注页文件夹维度浏览和移动。更新前端组件 spec 记录树形连接线实现模式。
2) 优化webai-lite原型图

下阶段计划

  1. 优化webai-lite原型图并给同事测试拿反馈
  2. 补充webai-lite必要的后端接口

客户端与服务端通信概要

信道 发起方 客户端端口 服务端端口 协议/格式 作用
主任务通道 客户端主动连服务端 OS 随机临时端口 webai.jsonport,当前是 1234 TCP,自定义二进制协议 心跳、接收训练/推理/升级任务、回传结果文件
消息通道 客户端主动连服务端 OS 随机临时端口 固定 8005 TCP,直接发送 JSON 文本 回传训练/推理过程日志、错误消息
HTTP 查询/控制通道 外部主动连客户端 固定 8001 对端随机临时端口 HTTP/1.1 over TCP 查询设备/任务状态、取消任务、缓存检查、升级检查

为什么要有三个信道,不能一个TCP传文件,一个HTTP传消息吗?

可以,架构上完全可以改成“一个 TCP 传任务/文件 + 一个 HTTP 传消息/状态”。现在做成三个信道,主要是为了把职责拆开,降低互相阻塞和协议解析复杂度。

当前三个信道分别是:

  1. 主 TCP 信道
    负责重活:心跳、接收任务文件、回传结果文件。
    这个信道传的是自定义二进制协议,里面有大文件流,所以一旦在收/发大文件时插入日志消息,就必须做更复杂的多路复用协议,否则容易把文件流和消息流混在一起。

  2. 消息 TCP 信道
    负责轻量实时消息:训练进度、推理进度、错误信息。
    它和主 TCP 分开,是为了避免“大文件发送中,进度消息发不出去”这种队头阻塞,也让 TrainingWorker / InferenceWorker 可以直接发消息,不用抢 AgentServer 的主 socket。

  3. 客户端本地 HTTP 信道
    这个不是给训练/推理过程发日志的,主要是给本地管理端/WebUI 查状态、取消任务、检查缓存、检查升级。
    它是服务端/管理端主动请求客户端:GET /api/tasks/statusGET /api/tasks/cancel 这种。

所以你说的方案可以成立,但要看“HTTP传消息”指哪种消息:

  • 如果是 客户端把训练/推理日志上报给服务端,可以改成 HTTP POST /task/message,甚至 WebSocket。这样可以替代现在的 8005 消息 TCP。
  • 如果是 服务端/管理端查询客户端状态,现在的 8001 HTTP 已经在做这件事。
  • 如果想用 同一个 HTTP 信道既查状态又上报消息,也可以,但服务端和客户端要约定方向:客户端主动 POST 服务端,还是服务端轮询客户端。

我个人判断:现在这个项目保留三个信道是偏工程实用的选择。主 TCP 传大文件和任务,消息 TCP 传实时日志,本地 HTTP 给外部查询控制。它不优雅,但简单抗阻塞。

更现代一点的改法可以是:

  • 保留一个主 TCP 或改成 HTTP 上传/下载,用来传任务包和结果包。
  • 8005 消息 TCP 改成 HTTP POST 或 WebSocket。
  • 保留客户端 8001 HTTP,只做本地查询/取消/健康检查。

agentserver里面分别有几个线程,分别是干什么的?他们之前是如何配合的

严格说,AgentServer 自己不是一个线程类,它是一个 QObject,通常运行在 Qt 主线程/界面线程里。它内部和它调度的线程可以分成几层:

线程/对象 是否 AgentServer 直接创建 作用
主线程中的 AgentServer 是,随 MainWindow 创建 维护主 TCP、解析协议、写任务文件、更新任务状态、启动训练/推理
CleanThread 后台删除过期训练/推理目录和旧日志
SendResultManager 单线程池 间接使用 串行发送训练/推理结果,避免多个结果同时抢主 TCP
TrainingThread::workerThread 任务到达时创建 执行 TrainingWorker::train(),启动 Python 训练流程
InferenceThread::workerThread 任务到达时创建 执行 InferenceWorker::inference(),准备并启动推理流水线
InferenceWorker 内部 3 个线程 推理任务内创建 ReaderThread 读图,InferThread 跑推理,WriterThread 写结果
HTTPServerThread 不是 AgentServer 创建,是 MainWindow 创建 本地 HTTP 查询状态/取消任务/检查缓存,和 AgentServer 通过快照配合

关键点:TrainingThreadInferenceThread 这个名字有点误导,它们本身不是继承 QThread,而是 QObject + 内部持有一个 QThread workerThread。比如 TrainingThread 构造InferenceThread 构造

它们之间的配合大概是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
主线程 AgentServer
接收 TCP 数据
parseCommand/parseHeader/parseFilename/parseData
文件落盘完成
├─ emit TrainReceiveFinished -> startTraining
│ └─ TrainingThread.workerThread -> TrainingWorker::train
│ └─ QProcess 启动 Python 训练

└─ emit InferReceiveFinished -> startInference
└─ InferenceThread.workerThread -> InferenceWorker::inference
├─ ReaderThread 读图片
├─ InferThread 跑 SolutionRunner 推理
└─ WriterThread 写推理结果

任务结束
worker 发 trainingFinished/inferenceFinished
AgentServer::onTrainingFinished/onInferenceFinished
更新数据库/状态
SendResultManager 单线程池发送结果

几个重要协作机制:

  1. Qt signal/slot
    AgentServer 收完文件后发信号启动任务;worker 完成后发信号回到 AgentServer 处理结果。

  2. TaskInfo 共享状态
    m_taskInfo[uuid] 是任务总账,里面有 header、线程指针、结果状态、取消标记。访问时用 QMutexLocker 保护。

  3. HTTP 快照
    HTTP 线程不直接长时间读 m_taskInfo,而是读 m_httpTaskStatusSnapshot,减少阻塞主任务线程。

  4. 结果发送的等待/唤醒
    SendResultManager 在线程池里发起结果发送,但真正写主 TCP socket 由 AgentServer::sendResFile() 配合完成,发送完用 QWaitCondition 唤醒发送任务。

一句话总结:AgentServer 是调度中心,主线程负责收任务和状态管理;训练/推理/清理/发送结果这些耗时工作被拆到 worker 线程或线程池里,通过信号、锁和任务状态表配合。
这样职责会更清楚:文件通道、上报通道、查询控制通道。当前代码只是把“上报通道”也做成了裸 TCP。

训练/推理代码在哪里?

训练和推理分两层看:

1. C++ 调度代码
训练入口在 src/trainingthread.cpp

  • TrainingWorker::train():真正启动训练流程
  • TrainPrepareThread::run():解压训练代码、样本、配置、预训练模型
  • m_process->start(ClientConfig::GetPythonPath(), ...):用 Python 启动训练脚本

但 C++ 本身不写训练算法,它主要负责准备目录、启动 Python、解析日志、打包结果。

真正训练脚本是服务端下发的 code.zip 解压出来的:

1
webai_data/training/<uuid>/code/main.pyc

当前仓库里有一个已接收任务样例:

1
2
3
4
5
1e0924d3-2f2e-4ce6-b636-deb3d9fbb296/code/main.pyc
1e0924d3-2f2e-4ce6-b636-deb3d9fbb296/code/train.pyd
1e0924d3-2f2e-4ce6-b636-deb3d9fbb296/code/main_inner.pyd
1e0924d3-2f2e-4ce6-b636-deb3d9fbb296/code/ultralytics/
1e0924d3-2f2e-4ce6-b636-deb3d9fbb296/code/fs_yolos/

所以:训练算法主要在下发的 Python/pyc/pyd 包里,不在 src/ 里。

2. 推理代码
推理入口在 src/inferencethread.cpp

  • InferenceWorker::inference():推理任务总入口
  • InferPrepareThread::run():解压 flow、模型、样本、大模型/SAM 资源
  • ReaderThread::run():读图片
  • InferThread::run():调用推理引擎
  • WriterThread::run():保存推理结果

真正执行推理的关键调用是这里:

1
m_runner->RunFlow(*image, param);

src/inferencethread.cpp

m_runner 是:

1
fsai::SolutionRunner

它来自第三方推理库,不是本仓库自己实现的算法核心。相关头文件在:

一句话:训练算法在服务端下发的 Python 包里;推理算法在第三方 SolutionRunner/插件/模型流里。这个 C++ 项目主要是客户端调度壳:收任务、解压、调用训练/推理引擎、回传结果。