这次我们来看一个很有意思的开源小项目:Wikipedia: AI or Not Quiz。它的玩法一句话就能说清楚——给你看一段文字,你判断这段文字究竟是 AI 写的,还是真人写的。
听起来简单,但真上手会发现,这其实是一个非常实用的 AI 文本鉴别试验场。它把“人能否识别 AI 生成内容”这件事,变成了一个可重复、可评分、可拆解的量化测试。
这个项目适合三类读者:第一类是刚接触 AI 应用开发的工程师,想找一个结构简单、前端交互 + 后端接口都能练到的小项目;第二类是做内容审核、编辑、运营的同学,想系统性地测一测自己对 AI 文本的判断力,顺便理解 AI 写作的常见特征;第三类是研究 AI 检测方向的人,可以通过这种问答游戏形式收集人机判别数据,了解人类识别 AI 文本的真实准确率。
本文会带你完整拆解这个 Quiz 的核心功能、数据组织方式、前后端交互逻辑,并给出一套可复现的本地部署和接口测试流程。文章里涉及的技术点包括:文本样本管理、Quiz 题组设计、前端答题交互、后端判定接口、评分与结果统计、以及 AI 文本检测的常见误区和合规边界。
先放一个核心结论:这一类“AI or Not”测验最有价值的不是准确率有多高,而是它能让你批量建立对 AI 写作特征的敏感度。下面进入正题。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目类型 | 人机文本判别问答应用(Quiz) |
| 核心功能 | 展示文本样本,让用户判断是否由 AI 生成,并给出评分与解释 |
| 输入素材 | 需要准备人工文本和 AI 生成文本两组样本 |
| 主要交互 | 前端题目展示、选项点击、结果反馈、排行榜/统计 |
| 后端能力 | 题组分发出题、答案校验、用户得分统计、样本池管理 |
| 启动方式 | Web 应用,可本地命令启动 |
| 是否支持 API | 可以拆成独立判定/出题接口,方便二次开发 |
| 是否支持批量任务 | 样本导入和批量评测可以做成脚本;实时答题是单次交互 |
| 推荐环境 | 普通开发机即可,基本无 GPU 依赖 |
| 数据依赖 | 需要自备或自行生成足够数量的文本样本 |
从这张表能看出来,这个项目本质上是一个“文本鉴别任务”的 Web 化封装。它不依赖大模型推理,不要求显卡,也不需要本地跑权重。核心工作量在数据组织、交互设计和判定逻辑上。
2. 适用场景与使用边界
先搞清楚它能干什么。
适用场景一:人机判别能力训练。把 AI 生成的段落和真人写作的段落混在一起,让用户不断判断、纠错、回看,逐渐形成对 AI 文本的直觉。内容编辑、新媒体运营、审核人员可以用它做内部训练。
适用场景二:AI 文本特征研究。通过 Quiz 记录用户的判断结果,可以统计出:哪种类型的 AI 文本最容易骗过人?哪些特征容易被误判?不同模型生成的文本,人类识别率差异有多大?这些数据对内容安全工作很有参考价值。
适用场景三:教学演示。在 AI 通识课或技术分享中,现场跑一轮 Quiz,比讲十页 PPT 更能说明“AI 文本很难一眼识别”。
不适合什么场景?
- 不适合作为正式的 AI 内容检测工具。人眼判断永远是概率性的,不是检测标准。
- 不适合用来给文本“定罪”。一段文字被判断为 AI 生成,不意味着它真的由 AI 生成,更不能作为处罚或封禁的唯一依据。
- 不适合直接商用发布。如果要上线给别人用,需要补充用户协议、隐私说明、免责声明。
使用边界和合规提醒:
AI 文本检测本身存在两个根本性限制:一是模型能力在快速迭代,旧的判别经验会迅速失效;二是任何检测手段都无法做到 100% 准确,误判成本很高。因此,凡涉及内容审核、版权认定、学术诚信判断的场景,一定要结合其他证据,不能只靠人眼判断或单一检测工具。
如果后续要用 AI 生成样本文本,请使用合法渠道获取模型服务,并确认样本内容不侵犯他人版权、不包含个人隐私信息。涉及批量抓取文本时,注意遵守数据来源网站的条款和 robots 协议。
3. 整体架构与数据设计
3.1 架构分层
“Wikipedia: AI or Not Quiz”这类 Quiz 应用,架构可以拆成四层:
前端展示层:题目卡片、选项按钮、计分面板 接口服务层:出题接口、提交答案接口、结果统计接口 数据管理层:人工文本样本库、AI 文本样本库、答题记录 样本准备层:文本采集、去重、长度归一化、标签标注对于本地部署或二次开发,不需要过度设计。前端用原生 HTML/JS 或者 Vue/React 都可以,后端用一个轻量框架提供 JSON 接口就够。关键是数据层要设计好样本结构。
3.2 样本数据结构
一段测试文本最好包含以下字段:
{ "id": "sample-001", "content": "这是需要用户判断的正文内容……", "source_type": "ai", "model_name": "gpt-4o-mini", "topic": "机器学习基础概念", "difficulty": 1, "used_count": 0, "correct_rate": 0.0 }字段说明:
source_type:ai或human,这是答案标签;model_name:如果是 AI 样本,记录生成时使用的模型,方便后续分析;topic:文本主题,方便按主题出题;difficulty:难度等级,可以先由人工预设,后期通过答题正确率动态调整;used_count和correct_rate:记录样本被使用次数和被答对比例,用来淘汰过难或过易的题目。
这里要注意一个数据偏差问题:AI 样本是由某个模型在特定提示词下生成的,人眼识别率只代表“那个模型 + 那个提示词”下的表现,不能泛化成“AI 文本都这样”。所以样本数据里必须保留模型名和生成参数,否则统计结果无法解释。
3.3 样本准备流程
准备样本的思路如下:
- 从公开语料中挑选人工文本,优先选择有明确作者出处的段落;
- 确定主题范围,比如科普、科技、生活、历史;
- 用不同模型、不同提示词批量生成 AI 样本;
- 对人工样本和 AI 样本做长度归一化,避免“AI 文本长度差异”成为泄露答案的线索;
- 做一轮人工抽查,去掉有明显格式特征、明显错误或包含敏感内容的样本;
- 打乱顺序,导出成 JSON 文件作为题库。
如果样本数量不大,直接把题库文件放在后端目录里即可。样本量达到数千条后,再考虑引入数据库。
4. 环境准备与前置条件
这个项目是典型的轻量 Web 应用,环境准备非常简单:
- Python 3.9+,用于后端服务和样本处理脚本;
- Node.js 或纯静态前端(如果只做演示,原生 HTML 也可以);
- 一个本地终端,一个浏览器;
- 无 GPU 依赖,普通办公电脑即可;
- 磁盘占用通常在几百 MB 以内,主要是依赖库和样本文件。
如果后端使用 FastAPI,需要安装以下依赖:
pip install fastapi uvicorn pydantic如果使用 Flask:
pip install flask flask-cors如果前端使用 Vue/React,按对应脚手架创建项目即可。这里不再展开。
5. 后端接口设计与实现
5.1 出题接口
出题接口的核心逻辑是:从样本池中随机抽取一条未被当前用户答过的样本返回,同时隐藏答案标签。
参考实现如下:
import random from fastapi import FastAPI, HTTPException from pydantic import BaseModel app = FastAPI() # 模拟样本池,实际项目可以从 JSON 文件或数据库读取 SAMPLE_POOL = [ { "id": "s001", "content": "机器学习是人工智能的一个重要分支,它通过数据驱动的方式让计算机自动改进算法表现。", "source_type": "human", "difficulty": 1, }, { "id": "s002", "content": "在自然语言处理领域,Transformer 架构通过自注意力机制有效建模文本中的长距离依赖关系。", "source_type": "ai", "difficulty": 2, } ] class AnswerIn(BaseModel): sample_id: str user_answer: str # 取值 "ai" 或 "human" @app.get("/api/quiz/next") def get_next_question(user_id: str = "guest"): # 简化逻辑:随机抽一条,实际项目应排除已答过的样本 sample = random.choice(SAMPLE_POOL) return { "sample_id": sample["id"], "content": sample["content"], "difficulty": sample["difficulty"] } @app.post("/api/quiz/answer") def submit_answer(payload: AnswerIn): sample = next((s for s in SAMPLE_POOL if s["id"] == payload.sample_id), None) if not sample: raise HTTPException(status_code=404, detail="sample not found") correct = (sample["source_type"] == payload.user_answer) return { "sample_id": sample["id"], "correct": correct, "actual_type": sample["source_type"] }注意几个细节:
- 出题接口不要返回
source_type,否则用户直接看接口响应就能作弊; user_id用来记录答题进度,避免同一用户反复抽到同一题;- 真实项目里,答题记录要持久化,不能只存在内存里。
5.2 结果统计接口
结果统计接口返回用户的答题总数、正确数、正确率和错题列表:
@app.get("/api/quiz/stats/{user_id}") def get_stats(user_id: str): # 实际项目从答题记录表聚合数据 return { "user_id": user_id, "total": 20, "correct": 14, "accuracy": 0.7, "wrong_samples": ["s003", "s007"] }5.3 启动服务
后端写好之后,启动命令:
uvicorn main:app --host 127.0.0.1 --port 8000 --reload启动后可以访问http://127.0.0.1:8000/docs查看 Swagger 接口文档。
6. 前端答题页面设计
如果不想引入复杂框架,一个单页 HTML 就能完成基本交互。核心逻辑是:
- 页面加载时请求
/api/quiz/next; - 渲染文本内容;
- 用户点击“AI 生成”或“人类写作”;
- 提交答案到
/api/quiz/answer; - 展示判定结果并加载下一题。
一个简化版前端示例:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>AI or Not Quiz</title> </head> <body> <div id="quiz-box"> <p id="content">加载中...</p> <button onclick="submit('ai')">AI 生成</button> <button onclick="submit('human')">人类写作</button> <p id="result"></p> </div> <script> let currentSampleId = ""; async function loadNext() { const res = await fetch("/api/quiz/next"); const data = await res.json(); currentSampleId = data.sample_id; document.getElementById("content").innerText = data.content; document.getElementById("result").innerText = ""; } async function submit(answer) { const res = await fetch("/api/quiz/answer", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sample_id: currentSampleId, user_answer: answer }) }); const data = await res.json(); const feedback = data.correct ? "回答正确" : "回答错误,正确答案是 " + data.actual_type; document.getElementById("result").innerText = feedback; setTimeout(loadNext, 1500); } loadNext(); </script> </body> </html>如果你有 CORS 跨域需求,后端需要配置允许跨域:
from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"] )注意:allow_origins=["*"]只建议在本地测试环境使用。部署到公网时,应配置为具体的域名列表。
7. 批量样本生成与评测脚本
7.1 批量生成 AI 文本样本
如果你要通过大模型 API 批量生成测试样本,可以写成脚本,把生成结果直接落盘为 JSON。
参考脚本框架:
pip install openaiimport json from openai import OpenAI client = OpenAI( api_key="your-api-key", base_url="your-base-url" # 如果使用代理服务,替换为对应地址 ) topics = [ "人工智能发展史", "机器学习基础概念", "深度学习在图像识别中的应用", "大语言模型的工作原理" ] samples = [] for topic in topics: prompt = ( f"请写一篇 200 字左右的科普短文,主题是「{topic}」。" "要求表达自然、结构清晰、没有明显机器痕迹。" ) response = client.chat.completions.create( model="your-model-name", messages=[{"role": "user", "content": prompt}], temperature=0.8 ) text = response.choices[0].message.content.strip() samples.append({ "id": f"ai-{len(samples)+1:03d}", "content": text, "source_type": "ai", "model_name": "your-model-name", "topic": topic, "difficulty": 1, "used_count": 0, "correct_rate": 0.0 }) with open("ai_samples.json", "w", encoding="utf-8") as f: json.dump(samples, f, ensure_ascii=False, indent=2) print(f"已生成 {len(samples)} 条 AI 样本")注意:不同模型的写作风格差异很大,temperature参数也会影响文本多样性。建议每个主题生成多条样本,并且让不同模型、不同参数混在一起,避免题库里全是同一风格。
7.2 样本质量检查
生成完 AI 样本后,需要做一轮质量检查。重点看:
- 是否存在事实错误或明显幻觉内容;
- 是否存在“作为AI语言模型”之类的 AI 自曝话术;
- 段落长度是否与人工样本匹配;
- 是否有明显的模板化开头,比如“在当今社会”“随着科技进步”。
这类特征如果大量存在,会让 Quiz 变成“找模板”而不是“辨真假”,降低测试价值。
7.3 批量答题评测
如果不想手动一题一题点,可以写脚本直接调用出题和判定接口做批量评测。这种自动化方式适合收集大规模样本准确率:
import requests BASE_URL = "http://127.0.0.1:8000" user_id = "batch-user-001" total = 0 correct = 0 for _ in range(50): resp = requests.get(f"{BASE_URL}/api/quiz/next", params={"user_id": user_id}) if resp.status_code != 200: break question = resp.json() # 这里可以替换成任意分类逻辑,比如规则关键词、另一个模型、人工决策等 user_answer = "ai" # 示例:全部猜 AI,可用于对照实验 answer_resp = requests.post( f"{BASE_URL}/api/quiz/answer", json={"sample_id": question["sample_id"], "user_answer": user_answer} ) result = answer_resp.json() total += 1 if result["correct"]: correct += 1 print(f"total={total}, correct={correct}, accuracy={correct / total:.2f}")这个脚本的价值在于,你可以设计多组对照实验。例如:
- 全部猜 AI 的基准正确率;
- 按文本长度字段判别的规则准确率;
- 某个提示词生成的样本人眼识别率;
- 真人短文本被误判为 AI 的概率。
这些统计数据比单一题目的对错更有分析价值。
8. 资源占用与性能观察
这类 Quiz 应用性能瓶颈非常低。如果你在本地启动,可以观察以下几点:
CPU 和内存:后端服务只做随机出题和答案对比,没有模型推理,CPU 占用极低,内存占用取决于题库大小。几千条样本加载到内存里几十分钟都没问题;十万条以上建议换用 SQLite 或 PostgreSQL。
响应时间:本地启动后,/api/quiz/next接口响应应该在几十毫秒量级。如果响应变慢,优先检查是否每次请求都重新加载了题库文件。正确做法是启动时一次性加载,后续请求只做查询。
并发能力:FastAPI + Uvicorn 单进程可以轻松处理单机小规模并发。但如果用户量上来了,要注意:
- 答题记录写入是否需要加锁;
- 出题接口是否会因为用户并发抽到同一样本;
- 是否需要限制单个用户的连续请求频率。
网络带宽:如果题库里的文本很长,或者需要一次性返回大量样本列表,注意接口响应体大小。一个 20 题的试卷接口返回几十 KB 是正常的,但不要一次性把整个题库返回给前端。
前端资源:原生 HTML 页面几乎没有资源占用。如果使用 Vue/React 进行打包构建,首屏资源通常也在几百 KB 以内,不需要额外优化。
8.1 如何观察接口耗时
可以在启动 Uvicorn 时开启访问日志,观察每个请求的耗时特征:
uvicorn main:app --host 0.0.0.0 --port 8000 --access-log如果要精确测量某个接口的性能,可以写个简单压测脚本:
import time import requests BASE_URL = "http://127.0.0.1:8000" times = [] for _ in range(100): start = time.time() requests.get(f"{BASE_URL}/api/quiz/next") times.append(time.time() - start) avg_time = sum(times) / len(times) max_time = max(times) print(f"avg={avg_time*1000:.1f}ms, max={max_time*1000:.1f}ms")注意:压测数据只代表你的本地环境,不同配置、不同题库大小结果会差很多。更稳妥的判断是,只要接口平均响应在 200ms 以内,对 Quiz 场景就没有感知差异。
9. 功能测试与效果验证
9.1 出题接口测试
测试目的:确认接口能返回不包含答案标签的随机题目。
操作步骤:
- 启动后端服务;
- 访问
http://127.0.0.1:8000/api/quiz/next; - 检查返回 JSON 中是否包含
source_type字段。
预期结果:
{ "sample_id": "s002", "content": "在自然语言处理领域,Transformer 架构通过自注意力机制有效建模文本中的长距离依赖关系。", "difficulty": 2 }判断标准:响应里不能出现source_type,否则属于接口设计缺陷。
9.2 提交答案测试
测试目的:确认答案校验正确,并返回实际答案。
操作步骤:
curl -X POST http://127.0.0.1:8000/api/quiz/answer \ -H "Content-Type: application/json" \ -d '{"sample_id":"s001","user_answer":"ai"}'预期结果:
{ "sample_id": "s001", "correct": false, "actual_type": "human" }判断标准:correct字段与样本真实标签一致。
9.3 前端交互测试
测试目的:确认页面能正常加载题目、提交答案、展示结果。
判断标准:
- 首次打开页面能显示题目文本;
- 点击按钮后能显示“回答正确/回答错误”;
- 1.5 秒后能自动加载下一题;
- 浏览器开发者工具中没有报错。
常见失败原因:
- 后端没启动,导致接口 404;
- CORS 未配置,导致前端无法跨域请求;
- 题库为空,导致出题接口返回 404 或 500。
9.4 批量评测脚本测试
测试目的:确认批量脚本能连续出题和判定,不会因为单次失败中断。
操作步骤:先启动后端,再运行批量脚本。
判断标准:脚本能跑完 50 次请求,并按格式输出正确率;中途任何一次请求失败,脚本应有日志记录而不是静默跳过。
9.5 数据偏差验证
这是在样本准备完成后必须做的一步。
测试目的:确认题目中是否存在“一眼假”的特征,导致用户不需要阅读内容就能答对。
验证方法:把 AI 样本和人工样本混合后,找三个之前没接触过题库的人各做 20 题,统计正确率。
- 如果正确率接近 100%,说明样本特征太明显,题目失去了区分度;
- 如果正确率接近 50%,说明样本已经达到“人眼无法稳定识别”的状态;
- 如果某个模型生成的样本被识别率特别高,需要检查是否存在固定格式,比如“总之”“总的来说”之类的模板化结尾。
10. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
启动时报ModuleNotFoundError | 依赖未安装或版本不匹配 | 执行pip list检查依赖 | 按requirements.txt重新安装 |
| 出题接口返回空内容 | 题库文件未加载或格式错误 | 检查后端日志和题库 JSON 结构 | 确认content字段非空,题库路径正确 |
| 前端无法请求接口 | CORS 未配置或端口不一致 | 打开浏览器开发者工具查看网络请求 | 后端配置 CORS,确保前端访问端口正确 |
| 提交答案后一直卡住 | 后端进程崩溃或无响应 | 查看 Uvicorn 终端日志 | 重启服务,检查接口是否正常返回 |
| 同一用户反复抽到同一题 | 出题逻辑未记录已答题 | 检查是否传入了user_id | 给每个用户维护当前会话的已答题目列表 |
| 批量脚本中途报错 | 接口偶发超时或返回 500 | 给请求增加重试和日志 | 每次请求设置timeout,失败后重试 3 次 |
| 答题结果显示错误答案 | 样本标签source_type标错 | 抽查题库 JSON 文件 | 人工复核标签,必要时重建样本池 |
| 接口响应很慢 | 每次请求重复加载题库 | 查看代码中是否有文件读取逻辑 | 将题库加载到内存,启动时一次性读入 |
| 端口被占用 | 8000 端口已有其他服务 | 使用lsof -i:8000或netstat -ano检查 | 更换端口启动,例如--port 8001 |
11. 最佳实践与使用建议
第一,样本管理要留元数据。每条样本至少保留来源类型、生成模型、主题、难度和命中次数。没有元数据的题库,做出来的统计结论很容易失效。
第二,每次只做单变量对比。测试人眼识别率时,一次只改变一个变量。比如固定模型只换主题,或者固定主题只换模型。这样才能解释正确率变化的原因。
第三,记录答题数据。把用户的每次答题结果落库,哪怕只是存成 JSON 行日志。积累一个月后回头分析,你能看到 AI 生成文本风格变化对自己判断力的影响。
第四,控制题库难度。正确率长期高于 90% 的题目可以降权或下架;正确率长期低于 50% 的题目要检查是否存在误导性特征。动态难度调节是 Quiz 类应用做好体验的关键。
第五,接口要加访问控制。如果部署到公网,必须给接口加鉴权和限流。否则别人可以循环调用出题接口把所有样本抓走,或者用脚本刷排行榜。
第六,不要回避误判风险。不管答题正确率多高,这个项目本质上是一个教育/训练工具,不是检测工具。在项目文档和界面上,建议明确写出“判断结果仅供参考,不能作为内容来源鉴定依据”。
第七,AI 文本检测要关注幻觉内容。你在准备 AI 样本时,可能会发现某些模型生成的内容存在事实错误。这些内容在 Quiz 里可以用来测试用户的“阅读警惕性”,但如果在内容生产场景里被误用,会造成传播错误信息的风险。合法合规的 AI 内容生产必须经过事实核查。
12. 本地一键启动参考
如果你想把前后端串起来,一个简单的方式是:后端提供接口,前端用静态文件托管。目录结构建议如下:
quiz-project/ ├── backend/ │ ├── main.py │ ├── samples/ │ │ ├── human_samples.json │ │ └── ai_samples.json │ └── requirements.txt └── frontend/ └── index.html启动顺序:
先启动后端:
cd backend pip install -r requirements.txt uvicorn main:app --host 127.0.0.1 --port 8000再启动前端静态服务(在另一个终端):
cd frontend python -m http.server 8080浏览器访问http://127.0.0.1:8080即可开始答题。
注意:如果你的前端代码和后端接口不在同一个端口,需要确保后端正确配置了 CORS,或者让前端通过后端静态托管页面。
13. 后续可扩展方向
这个项目继续往下做,有几个比较自然的方向:
方向一:接入自动判定模型。把“用户判断”和“模型判断”放到同一套题目上对比,统计人机识别率差异。这就变成了一个半演化的研判工具。
方向二:增加错题复盘。答错后展示正确答案,同时标注原文中可能存在的线索,比如逻辑连贯性、句式多样性、用词分布等。这对提升用户的文本鉴别能力很有帮助。
方向三:动态题库。接入文本样本池的自动扩充管线,定期生成新样本人,淘汰旧样本,保持题目的时效性。
方向四:多人对战模式。多用户实时答题,通过正确率和响应速度排名。这个方向的工程复杂度会明显上升,需要考虑 WebSocket 通信和在线状态管理。
最值得先验证的功能是样本质量检查。如果你能把 AI 样本准备到“人眼无法稳定识别”的程度,这个 Quiz 就成功了一半。相比之下,前端的交互轮播、计分动画、排行榜都不是核心难点。
最容易踩的坑是样本标签错标。人工样本里混入 AI 生成内容,或者 AI 样本里夹杂大量模板化句式,都会直接毁掉整个题库的统计学意义。准备样本时,宁可少一些,也要确保每条样本的标签可靠。
如果这个项目你准备继续深挖,可以重点研究两个方向:一是不同大模型生成文本的指纹特征,二是人类对 AI 文本误判的心理机制。前者是工程问题,后者是认知科学问题,两者结合,就是内容安全管理里非常实用的一层防御能力。