昨天晚上躺床上睡不着,思考很多事,我总是这样,在遇到重大变化的时候就容易失眠(最近好一些了)之前更是一晚上睡不着,思考得太多,做的太少
虚假的todo:
- 把混淆矩阵页面优化一下
- 添加子文件夹功能
- 添加局部屏蔽和全局屏蔽标注
- 添加推理结果查看界面(标注结果/推理结果)
真正的todo:
- 看webai-device推理客户端的C++代码
- 看当前的lite客户端推理部分和之前的iosAPP后端推理管线
- 从全局看项目,用了哪些技术栈,为什么要用?
上午主要就是把webai-lite和当前原型图对齐了一下,加了一个三级文件夹,到下午都没加完,gpt限额+电脑卡死,只好重启电脑
上午还把推理客户端的代码看了一些
上午还收到消息,明天有一个专业实验,加上要实习证明,让公司给我开了一个,打算拿了下午下班后就寄出去,专业实验也只能下班之后找找代课了
今天后面看leader看不看原型图,不看的话我就把局部屏蔽和全局屏蔽区加上,再优化一下混淆矩阵界面就完事~
我chovy,我发现原来是公司内部的中转站给我限额了,每天100刀,天才程序员陨落了……今天下午只能用cc+ds了
下午leader拉了个会,主要就是让我讲一下原型图,然后也说了下改进方向
1 | 加入无监督模式 |
里面有些东西我都不知道是啥,他们说到的时候我都是蒙的,比如定位模型、无监督模式这TM都是啥啊!
不管了,做到哪是哪吧,反正干完这个星期就不干了。接下来的几天主要就是把实习期间学到的东西总结总结,然后能投一点东西是一点(特别是mentor做的agent,听说项目已经收尾了),我现在还呆在这里纯粹是因为想要多混几天的工资,还有就是多偷一点东西
今天下午还和bite学管、鹏哥聊了一会,主要就是现在的企业技术相关的都要求会Ai,并且要有自己的一套AI开发范式,并且在面试的时候不仅看你会不会AI,也会更看重基本功一些,鹏哥说今年bite的实习率比去年还多了5%,鹏哥和学管也说根据我的实际情况来定,学校的考试还是比较重要的,也都认为我可以先结束这段实习之后再参加秋招,没必要骑驴找马
下班啦,今天感觉没干啥,算法也没刷,就看了一点推理客户端代码,回去还得刷一道算法题!
今日工作内容
Task: 样本集子文件夹层级管理 — 前后端实现 + 前端树形展示统一
完成后端 sample_sets v2 数据模型(文件夹树 CRUD、迁移到未分配区、训练/推理递归展开)、前端 WorkspaceSampleSidebar 树形结构(展开/收起根节点、文件夹树形连接线、统一样式)、数据页/标注页文件夹维度浏览和移动。更新前端组件 spec 记录树形连接线实现模式。
2) 优化webai-lite原型图
下阶段计划
- 优化webai-lite原型图并给同事测试拿反馈
- 补充webai-lite必要的后端接口
客户端与服务端通信概要
| 信道 | 发起方 | 客户端端口 | 服务端端口 | 协议/格式 | 作用 |
|---|---|---|---|---|---|
| 主任务通道 | 客户端主动连服务端 | OS 随机临时端口 | webai.json 的 port,当前是 1234 |
TCP,自定义二进制协议 | 心跳、接收训练/推理/升级任务、回传结果文件 |
| 消息通道 | 客户端主动连服务端 | OS 随机临时端口 | 固定 8005 |
TCP,直接发送 JSON 文本 | 回传训练/推理过程日志、错误消息 |
| HTTP 查询/控制通道 | 外部主动连客户端 | 固定 8001 |
对端随机临时端口 | HTTP/1.1 over TCP | 查询设备/任务状态、取消任务、缓存检查、升级检查 |
为什么要有三个信道,不能一个TCP传文件,一个HTTP传消息吗?
可以,架构上完全可以改成“一个 TCP 传任务/文件 + 一个 HTTP 传消息/状态”。现在做成三个信道,主要是为了把职责拆开,降低互相阻塞和协议解析复杂度。
当前三个信道分别是:
主 TCP 信道
负责重活:心跳、接收任务文件、回传结果文件。
这个信道传的是自定义二进制协议,里面有大文件流,所以一旦在收/发大文件时插入日志消息,就必须做更复杂的多路复用协议,否则容易把文件流和消息流混在一起。消息 TCP 信道
负责轻量实时消息:训练进度、推理进度、错误信息。
它和主 TCP 分开,是为了避免“大文件发送中,进度消息发不出去”这种队头阻塞,也让TrainingWorker/InferenceWorker可以直接发消息,不用抢AgentServer的主 socket。客户端本地 HTTP 信道
这个不是给训练/推理过程发日志的,主要是给本地管理端/WebUI 查状态、取消任务、检查缓存、检查升级。
它是服务端/管理端主动请求客户端:GET /api/tasks/status、GET /api/tasks/cancel这种。
所以你说的方案可以成立,但要看“HTTP传消息”指哪种消息:
- 如果是 客户端把训练/推理日志上报给服务端,可以改成 HTTP
POST /task/message,甚至 WebSocket。这样可以替代现在的8005消息 TCP。 - 如果是 服务端/管理端查询客户端状态,现在的
8001HTTP 已经在做这件事。 - 如果想用 同一个 HTTP 信道既查状态又上报消息,也可以,但服务端和客户端要约定方向:客户端主动 POST 服务端,还是服务端轮询客户端。
我个人判断:现在这个项目保留三个信道是偏工程实用的选择。主 TCP 传大文件和任务,消息 TCP 传实时日志,本地 HTTP 给外部查询控制。它不优雅,但简单抗阻塞。
更现代一点的改法可以是:
- 保留一个主 TCP 或改成 HTTP 上传/下载,用来传任务包和结果包。
- 把
8005消息 TCP 改成 HTTP POST 或 WebSocket。 - 保留客户端
8001HTTP,只做本地查询/取消/健康检查。
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 通过快照配合 |
关键点:TrainingThread、InferenceThread 这个名字有点误导,它们本身不是继承 QThread,而是 QObject + 内部持有一个 QThread workerThread。比如 TrainingThread 构造、InferenceThread 构造。
它们之间的配合大概是这样:
1 | 主线程 AgentServer |
几个重要协作机制:
Qt signal/slot
AgentServer收完文件后发信号启动任务;worker 完成后发信号回到AgentServer处理结果。TaskInfo共享状态m_taskInfo[uuid]是任务总账,里面有 header、线程指针、结果状态、取消标记。访问时用QMutexLocker保护。HTTP 快照
HTTP 线程不直接长时间读m_taskInfo,而是读m_httpTaskStatusSnapshot,减少阻塞主任务线程。结果发送的等待/唤醒
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 | 1e0924d3-2f2e-4ce6-b636-deb3d9fbb296/code/main.pyc |
所以:训练算法主要在下发的 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); |
m_runner 是:
1 | fsai::SolutionRunner |
它来自第三方推理库,不是本仓库自己实现的算法核心。相关头文件在:
一句话:训练算法在服务端下发的 Python 包里;推理算法在第三方 SolutionRunner/插件/模型流里。这个 C++ 项目主要是客户端调度壳:收任务、解压、调用训练/推理引擎、回传结果。