【项目实训】智能OJ平台(一):AI Agent工作流设计与Prompt模板搭建
key和url输错几次,还有前面说的重复生成语句的情况,以及run卡住的时候我排查问题试图用终端直接调用api丢些简单句子。还以为是哪里整错了,拉了个test测试,把prompt设成一句很简单的话尝试了一下,还是卡死。第一次调用的时候重复生成了十几句 “就是判题系统返回WA的核心原因”,查了下是大模型调用的正常现象,加上不要重复任何句子和限制每点的字数后就没事了。等,这样只能给到用户有限的帮助,而
一、背景与目标
在传统的OJ中,用户提交代码后只能看到简单的判题结果,例如 Accepted、Wrong Answer、Compile Error 等,这样只能给到用户有限的帮助,而我们的项目希望借助AI,快速地给出有价值的引导与教学,从而帮助用户理解学习,提高效率。
本项目(面向算法学习的智能在线 OJ 平台)在传统判题能力之上,引入 AI 辅导能力,目标是:
在判题结果基础上,向用户提供有学习价值的解释与引导,包括错误原因分析、复杂度说明、思路提示、优化建议等。
这是我有关这次项目的第一篇博客,重点介绍 AI Agent 工作流的设计与实现,即:如何将题目信息、用户代码、判题结果作为输入,通过大模型生成结构化的学习辅导内容。
二、整体架构设计
2.1 调用链路
并没有想象中那么复杂。调用链路如下:
用户提交代码
↓
Docker 沙箱判题
↓
判题结果 + 题目信息 + 用户代码
↓
AI Agent(组装Prompt → 调用大模型)
↓
结构化讲解输出
↓
前端展示
2.2 输入数据
| 数据类型 | 具体字段 | 来源 |
|---|---|---|
| 题目信息 | 标题、难度、知识点标签、题目描述 | 数据库(题目表) |
| 用户代码 | 源码文本、编程语言 | 提交记录 |
| 判题结果 | 结果类型、错误信息、运行日志 | 判题沙箱(Docker) |
2.3 输出格式
AI 返回的内容需要包含三个部分:
-
问题定位:用户代码可能存在的问题是什么?
-
原因分析:为什么会出现这个结果?
-
改进方向:如何修改代码或调整思路?
三、技术选型
| 组件 | 选型 |
|---|---|
| 后端框架 | GoFrame |
| 大模型 API | 通义千问(qwen3.6-plus) |
| 超时阈值 | 60s |
暂时先使用qwen,后面再按需求更改
四、核心代码实现
4.1 API 调用核心函数
func CallQwen(prompt string) (string, error) {
// 请求体
reqBody := ChatRequest{
Model: ModelName,
Messages: []Message{
{Role: "user", Content: prompt},
},
Stream: false,
MaxTokens: 800,
}
jsonData, err := json.Marshal(reqBody)
if err != nil {
return "", fmt.Errorf("JSON编码失败: %v", err)
}
// 创建请求
req, err := http.NewRequest("POST", APIURL, bytes.NewBuffer(jsonData))
if err != nil {
return "", fmt.Errorf("创建请求失败: %v", err)
}
// 设置请求头
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+APIKey)
// 发送请求
client := &http.Client{Timeout: TimeoutSec * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("请求失败: %v", err)
}
defer resp.Body.Close()
// 读取响应
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取响应失败: %v", err)
}
// 检查 HTTP 状态码
if resp.StatusCode != 200 {
return "", fmt.Errorf("HTTP错误 %d: %s", resp.StatusCode, string(body))
}
// 解析响应
var chatResp ChatResponse
if err := json.Unmarshal(body, &chatResp); err != nil {
return "", fmt.Errorf("JSON解析失败: %v", err)
}
// 检查 API 错误
if chatResp.Error.Message != "" {
return "", fmt.Errorf("API错误: %s", chatResp.Error.Message)
}
// 提取结果
if len(chatResp.Choices) == 0 {
return "", fmt.Errorf("没有返回结果")
}
return chatResp.Choices[0].Message.Content, nil
}
4.2 Prompt 模板
func BuildPrompt(problemTitle, problemDesc, userCode, judgeResult, errorMsg string) string {
return fmt.Sprintf(`你是一个算法学习辅导老师。请根据以下信息,为学生提供学习辅导。
题目信息:
1.标题:%s
2.描述:%s
代码:
%s
判题结果:
1.结果类型:%s
2.错误信息:%s
请按以下格式输出:
1.问题定位:代码可能存在的问题是什么?
2.原因分析:为什么会出现这个结果?
3.改进方向:如何修改代码或调整思路?
注意:
1.不要直接给出完整可运行的代码
2.不要重复任何句子 //不加会出现很多重复的句子
3.简明扼要地表述每个要点,最好每个要点控制在150字之内`,
problemTitle, problemDesc, userCode, judgeResult, errorMsg)
}
4.3 完整调用示例
prompt := BuildPrompt(problemTitle, problemDesc, userCode, judgeResult, errorMsg)
result, err := CallQwen(prompt)
if err != nil {
fmt.Printf("调用失败: %v\n", err)
return
}
五、运行结果展示
5.1 测试场景
根据设计的Prompt,整了个场景进行测试
//测试
problemTitle := "两数之和"
problemDesc := "给定一个整数数组 nums 和一个目标值 target,请找出和为目标值的那两个整数,并返回下标。"
userCode := `vector<int> twoSum(vector<int>& nums, int target) {
for (int i = 0; i < nums.size(); i++) {
for (int j = 0; j < nums.size(); j++) {
if (nums[i] + nums[j] == target) {
return {i, j};
}
}
}
return {};
}`
judgeResult := "Wrong Answer"
errorMsg := "预期输出 {0,1},实际输出 {0,0}"
第一次调用的时候重复生成了十几句 “就是判题系统返回WA的核心原因”,查了下是大模型调用的正常现象,加上不要重复任何句子和限制每点的字数后就没事了。
5.2 AI 返回结果
PS D:\learn\go\mygo> go run main.go
正在急头白脸地分析中---
1.问题定位:内层循环的初始条件设置不当,未限制两个下标必须互不相同。代码允许i与j取值重合,导致单个元素被重复参与求和运算。
2.原因分析:当i遍历至首元素时,j同样从0开始匹配。若该元素数值的两倍恰好等于target,判断语句立刻成立,程序提前返回{0,0}。这违背了题目选取两个不同位置元素的核心要求。
3.改进方向:将内层循环的起始边界调整为i+1,确保每次比较的都是数组中靠后的元素。该改动可直接避开索引重叠的陷阱,同时消除约一半的无效配对,使算法逻辑与性能均符合标准解法。
run老卡住,总是run好几次才能成功调用一次,不知道什么情况,这篇先这样了后续再解决一下。
还以为是哪里整错了,拉了个test测试,把prompt设成一句很简单的话尝试了一下,还是卡死。最后没办法了多run了几遍忽然就打印正在急头白脸地分析中了,舒服。
六、出现的问题
其实没有什么,整体工作流程比较少也比较简单。key和url输错几次,还有前面说的重复生成语句的情况,以及run卡住的时候我排查问题试图用终端直接调用api丢些简单句子。问了ai怎么写命令,但是输入后终端没有一点反应,最后放弃了写了个test。
七、收获
把一个api调用跑起来了,设计了一个prompt模板,走出了项目的第一步
八、下一步计划
-
扩展其他场景的 Prompt 模板(CE、TLE 等)
-
接入 RAG 检索模块,实现检索增强讲解
更多推荐




所有评论(0)