OJ系统集成及借助大模型实现功能扩展(一)
首先非常感谢小米官方的“百万亿 Token 创造者激励计划”给的token-plan,我申请的理由写了要写数据可视化课设,发了一个月价值100刀乐的Max plan,量大管饱。

前几周完成了 remote_judge 远端判题子系统的完整开发——Docker 沙箱执行、容器池复用、熔断降级、Seccomp 安全加固,以及 Compose 四服务一键部署。判题系统本身已经是一个功能完备的独立服务,但和 OJ 后端的整合一直停留在"各自能跑"的阶段。本周的工作集中在一个核心问题上:把 OJ 后端和 remote_judge 真正跑通一整条链路,并在此基础上借助 MIMO 大模型对系统进行功能扩展。
当前 OJ 后端本质上还是一个框架——用户系统、题目管理、提交入口等基础 CRUD 都有实现占位,但实际功能、判题状态、采集信息和远端判题系统并没有对齐。remote_judge 作为完整独立的判题后端服务,在功能上与 OJ 后端存在耦合:两者都用 RabbitMQ 做异步判题调度,各自维护独立的题库,导致项目合并时出现了不少问题。这篇博客重点记录解决上述耦合、完成系统联调的过程,以及随后用 MIMO 搭建 agent-service 做 AI 功能扩展的整体架构和流程设计。
一、耦合问题分析
合并前两个系统的状态:
核心矛盾:
| 问题 | OJ 后端 | remote_judge | 冲突点 |
|---|---|---|---|
| 消息队列 | 使用 RabbitMQ 异步调度 | 同样使用 RabbitMQ | 两套队列,提交流转混乱 |
| 题库 | 自有 problems 表 |
自有 problems 表 |
题目数据双写,判题用例不一致 |
| 判题状态 | 简单的 Accepted / WA / CE | 4 中间态 + 7 终态 + 丰富字段 | 状态模型不对齐 |
| 判题结果 | 扁平字段 | 完整 JudgeResult(traceId / runtimeMs / caseResults[] 等) | 信息丢失 |
| 提交 ID | uint64 自增 | int64 自增 | 类型不一致 |
如果强行合并,要么两个 Worker 争抢 RabbitMQ 消息,要么题库数据不同步导致判题用例对不上。
二、两种集成思路
针对上述耦合问题,提出了两种解决思路。
思路一:薄代理 + 共享数据库
OJ 后端删掉所有调度代码变成纯粹的代理层,接收提交后直接转发给 remote_judge 的 cmd/server。调度、判题、结果持久化全权交给 remote_judge。同时两系统共享同一套 MySQL,判题用例通过题号直接查询,远端判题结果直接写入。
优点: 判题用例不再经 gRPC 跨服务传输(大输入场景价值明显);OJ 后端专注业务,remote_judge 专注判题,职责清晰;共享数据库,数据天然一致。
缺点: 改动量大,OJ 后端需要删除调度模块、重构提交流程;数据库耦合降低部署灵活性。
思路二:直连 Judger 模块
remote_judge 内部本身就是解耦的——cmd/server 负责 HTTP API 和调度,cmd/judger 负责纯判题,两者通过 gRPC 通信。恰好 cmd/judger 的 gRPC 接口和 OJ 后端对判题的需求一致。因此 OJ 后端可以绕过 remote_judge 自己的 Server / Queue / Worker,通过 gRPC 直连 cmd/judger,把它当作一个"判题函数"来调用。
优点: 几乎零代码改动,改个配置指向 remote_judge 的 gRPC 地址即可;只需启动 cmd/judger,不需要 remote_judge 完整四服务;思路一能力完整保留在 cmd/server 中,可随时切换。
缺点: gRPC 消息体需包含完整判题用例;OJ 后端仍须维护自己的调度逻辑(Worker + RabbitMQ)。
三、选择与实施
优先选择了思路二。
权衡因素:
- 开发效率:思路二几乎零代码改动,思路一需要重构 OJ 后端的提交和调度模块
- 风险控制:先从改动最小的方案入手,快速验证链路可行性,后续再考虑架构升级
- 可逆性:思路一的完整能力保留在
cmd/server中,随时可以切换过去
3.1 配置修改
编辑 config.yaml,把判题 gRPC 地址从内置 Judger 指向 remote_judge:
judger:
grpc_addr: "127.0.0.1:9090" # remote_judge cmd/judger 的 gRPC 地址
remote: true # 启用远程判题模式
OJ 后端已有的 internal/judger/ 模块实现了 gRPC 客户端,使用自定义 JSON Codec(无需 protoc 编译)。Worker 从 RabbitMQ 消费判题任务后,构建 JudgeRequest 通过 gRPC 发给 remote_judge 的 Judger,获取 JudgeResponse 后写入 MySQL。
3.2 判题状态对齐
remote_judge 的 JudgeResult 远丰富于 OJ 后端原有的扁平状态。扩展了 submissions 表和相关模型以完整接收:
| 新增字段 | 来源 | 含义 |
|---|---|---|
trace_id |
JudgeResponse.traceId | 全链路追踪 ID |
runtime_ms |
JudgeResponse.runtimeMs | 运行耗时 (ms) |
memory_kb |
JudgeResponse.memoryKb | 内存峰值 (KB) |
compile_output |
JudgeResponse.compileOutput | 编译输出 |
error_message |
JudgeResponse.errorMessage | 错误信息 |
signal |
JudgeResponse.signal | 终止信号 |
case_results |
JudgeResponse.caseResults | JSON 字段,逐测试点详情 |
同时新增 case_results 子表存储每个测试点的判定结果,方便前端查询单个测试点的输入 / 期望输出 / 实际输出。
3.3 gRPC ID 类型适配
remote_judge 的 proto 使用 int64 类型 ID,OJ 后端使用 uint64。由于用的是 JSON Codec 而非二进制 protobuf,Go 的 encoding/json 对两者的 JSON 表示一致(都是数字),实际测试中未发现兼容性问题。
3.4 联调验证
启动顺序:
# 1. 基础设施
cd AIOJ-main/backend
docker compose -f docker/docker-compose.yml up -d mysql rabbitmq
# 2. remote_judge 判题服务
cd remote_judge
REMOTE_JUDGE_GRPC_ADDR=127.0.0.1:9090 go run ./cmd/judger
# 3. OJ 后端
cd AIOJ-main/backend
go run ./cmd/server -config config.yaml
# 4. 前端
cd AIOJ-main/frontend
npm run dev
联调过程并非一帆风顺。最初 OJ 后端的 gRPC 客户端使用旧版 JudgeRequest(缺少 run_mode 字段),导致判题结果一直显示 System Error。排查发现是 remote_judge 新加字段在 OJ 侧未设默认值,Judger 解析时认为请求不合法。补上默认值后解决。
调试中的判题沙箱运行截图:


代码沙箱对各种情况(AC、WA、CE、RE、TLE)都能给出正确的判别,逐测试点判定结果显示在 case_results 中,包含每个点的运行时间、内存用量和判定状态。
四、借助 MIMO 进行功能扩展
联调跑通后,系统已具备完整判题能力。但一个现代 OJ 平台仅有判题是不够的——用户代码失败了需要有人告诉他错在哪,通过了题目需要途径沉淀解题思路。这正是大模型的用武之地。
接下来一周多时间,以 mimo-v2.5-pro 为驱动,通过 vibe-coding 方式搭建了 agent-service 的完整架构,并为 OJ 系统扩展了 5 个核心 AI 能力。
4.1 架构定位
agent-service 定位为"AI 中间层"——不直接暴露给前端,所有请求经 OJ 后端转发。OJ 后端负责数据整理和上下文组装,agent-service 负责 AI 推理。
核心设计原则:
- 用户不可感知:agent-service 不暴露端口给前端,所有 AI 请求须经 OJ 后端 JWT 鉴权和限流
- 职责分离:OJ 后端负责数据整理(查数据库、组装用户画像 + 提交历史 + 题目信息)、上下文组装、结果持久化;agent-service 只做 AI 推理
- 统一标签字典:OJ 后端维护 90+ 算法标签作为唯一字典,agent-service 启动时拉取缓存,AI Prompt 中注入标签列表,确保 AI 输出的算法标签与题库一致
- RAG 增强:agent-service 启动时索引 OI-Wiki 文档,请求时检索相关知识块注入 System Prompt,让 AI 回答有据可依
4.2 双 Provider 降级
配置项 AI_PROVIDER 控制优先级,生产环境 openai(MIMO 为主),本地开发 ollama。Chat 和 Embedding 各自独立降级,互不阻塞。Embedding 使用独立模型 nomic-embed-text:latest,不占用对话模型资源。
4.3 RAG 检索增强
RAG 是 agent-service 核心差异化能力——让 AI 引用 OI-Wiki 知识而不是凭空编造。
设计核心理念是"向量优先 + 关键词降级"——宁可降低检索精度也不报错,保证 AI 功能始终可用。修复后 RAG 系统成功索引 600 个文档块。(RAG 的详细实现和迭代过程见(二)。)
4.4 五个 AI 功能
基于上述架构,设计并实现了 5 个核心 AI 功能:
| 功能 | 触发场景 | 核心设计 |
|---|---|---|
| 代码诊断 | 判题失败后点击"获取诊断" | OJ 后端组装题目 + 代码 + 判题结果 + 最近 3 次提交历史;agent-service 注入 RAG 知识 + 标签字典后调用 LLM,返回结构化诊断(summary / issues[] / suggestions[]) |
| 解题辅助 | 做题时获取帮助 | 三级帮助:hint(提示不给代码)、explain(思路不给代码)、full(完整代码 + 判题验证状态机,最多 3 轮自修正) |
| AI 对话 | 侧边栏自由对话 | 自动注入题目上下文 + 编辑器代码;持久化到 conversations 表;多轮记忆;RAG 自动检索注入 |
| 题解生成 | 通过题目后点击"AI 生成题解" | 提交历史按规则筛选(最近 3 天内最多 5 条、含最近一次 AC);前端自动填充到题解编辑器 |
| 知识图谱 | 点击"整理我的知识图谱" | OJ 后端统计用户做题数据;agent-service 生成 nodes[] + edges[] + suggestions[];前端 ECharts 层级布局渲染 |
其中 Solve full 级别的判题验证状态机是设计最复杂的功能——AI 生成的代码不一定正确,需要实际跑一遍来验证:
调用链路:agent-service → OJ Backend /api/problems/:id/run → remote_judge gRPC → Docker Sandbox。同时判题等待从硬编码 time.Sleep(2s) 优化为轮询(500ms × 10 次,终态提前返回)。
(五个 AI 功能的 Prompt 设计、数据流时序图、Handler 实现细节见(二)。)
五、MIMO 优化成果
借助 mimo-v2.5-pro 的 vibe-coding 能力,一周多时间完成了大量系统优化。MIMO 优化后的系统:





Agent-Service 侧
| 改进项 | 说明 |
|---|---|
| RAG 系统完整实现 | 种子数据 + 爬虫 + Embedding 修复,600 个文档块已索引 |
| AI JSON 结构化输出 | 所有端点 Prompt 要求 JSON 返回,解析失败降级 rawMarkdown |
| 双 Provider 回退修复 | ollama / openai 模式各自正确降级 |
| AI 沙箱调用集成 | Solve full 自动调 remote_judge 判题验证 |
| 请求体大小限制 | Gin 中间件 1MB,防 OOM |
| 双重 Body 读取修复 | CodeDiagnosis / Solve 改为 io.ReadAll 一次读取 |
| Judge 验证轮询 | 硬编码 sleep → 500ms × 10 次轮询 |
| Embedding 模型独立 | OpenAI Provider 用 text-embedding-3-small |
| .env 解析增强 | 处理引号、注释、空值 |
| 优雅关闭 | SIGINT / SIGTERM 触发 Shutdown |
OJ Backend 侧
| 改进项 | 说明 |
|---|---|
| Worker 判题重试 | 指数退避(500ms / 1s / 2s,最多 3 次) |
| AI 端点速率限制 | 复用 PerUserRateLimit,10/min |
| JWT 角色实时校验 | 新增 RequireAdminDB 查库验证 |
| Rating 历史曲线 | rating_history 表 + ECharts 折线图 |
| 题目列表分页修复 | 过滤条件下 total 计数修正 |
| 提交 ID 多实例安全 | 数据库序列表原子递增 |
| N+1 查询消除 | 推荐查询改单次 WHERE IN |
| Mastery 计算优化 | 全表扫描 → GROUP BY + COUNT |
| AI 失败 code 修正 | 失败返回 code:-1 |
| Rating 默认值统一 | 三处统一为 1200 |
Frontend 侧
| 改进项 | 说明 |
|---|---|
| 知识图谱层级化 | 按难度分层 + 跨类别关联 + 颜色/大小反映掌握度 |
| CodeEditor 功能补充 | 全屏 + ESC 退出 + wordWrap 切换 |
| 代码块复制按钮 | MarkdownRenderer 自动为 pre 添加 |
| 多页面错误处理 | StudyPlan / Stats / AdminAuditLogs 加 catch |
| Mock 清理 | VITE_USE_MOCK 环境变量控制 |
| 死代码清理 | MySolutions / SolutionDetail / mock.js 移除 |
| 标签动态获取 | ProblemList 改为 API 获取 |
最终统计
- P0 项:6/6 完成
- P1 项:7/7 完成
- P2 项:7/7 完成
- P3 项:4/4 完成
- 数据库缺陷:10/10 完成
- 总计:34/34 核心改进项完成(100%)
六、总结
本次工作完成了两件核心事情:
第一,OJ 系统集成。 分析了 OJ 后端与 remote_judge 之间的耦合问题(两套队列、各自题库、状态不对齐、ID 类型不一致),提出了薄代理 + 共享数据库(思路一)和直连 Judger 模块(思路二)两种解决思路。优先选择了改动量最小、风险最低的思路二——gRPC 直连 cmd/judger,同时保留思路一作为后续架构升级的备用方案。联调过程中解决了 gRPC 接口适配(run_mode 字段缺失)、判题状态对齐(7 个新字段)、ID 类型兼容等问题,最终跑通了"前端提交 → OJ Backend → RabbitMQ → Worker → gRPC → remote_judge → Docker Sandbox → 结果回写 → 前端展示"的完整链路。
第二,借助大模型进行功能扩展。 以 mimo-v2.5-pro 为驱动,搭建了 agent-service 的完整架构——双 Provider 降级策略、RAG 检索增强(600 个 OI-Wiki 文档块)、统一标签字典(90+ 标签)、5 个核心 AI 功能(代码诊断、解题辅助含判题验证状态机、AI 对话含多轮记忆和 RAG 注入、题解生成、知识图谱生成)。同时在代码审查中修复了 34 项缺陷,覆盖后端、前端、agent-service、数据库四个维度。
(二)将深入展开 agent-service 的架构设计思路、关键模块实现细节,以及用 MIMO 做 vibe-coding 的迭代经验。
博客中涉及的完整代码见 fused 融合仓库:
AIOJ-main/、remote_judge/、agent-service/。

浙公网安备 33010602011771号