尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于维基百科与大模型生成的文本分辨测验项目全解析

基于维基百科与大模型生成的文本分辨测验项目全解析 先别急着去训练一个“AI 检测模型”。最近的 AI 应用开发和信息素养项目里经常能遇到同一个问题面对一段百科体文字普通人到底能不能判断它来自维基百科的人类编辑还是大模型生成的伪百科内容。如果不借助检测工具只看文本本身人类判断的正确率其实一直不高。基于这个场景我整理了一个可以直接运行的交互式测验项目Wikipedia: AI or Not Quiz。这篇文章会完整拆解这个项目包括题目数据怎么采集、AI 伪百科文本怎么生成、Streamlit 页面怎么写、常见报错怎么排查以及上线前需要注意的工程和实践问题。无论你只是想做一个有意思的 AI 小应用还是想借这个项目理解大模型提示词、接入维基百科 API、设计前端交互本文都能提供一个可复用的闭环方案。1. 背景与核心概念1.1 项目定位一个面向普通用户的 AI 素养测验Wikipedia: AI or Not Quiz 本质上是一个“人机文本分辨游戏”。系统会把两类文本混在一起展示给用户人类样本从真实维基百科条目中抽取的段落。AI 样本让大模型按照维基百科风格生成的百科体段落。用户每次看到一段文字需要选择“这是人类写的”还是“这是 AI 生成的”。每轮结束之后系统会公布正确答案并显示当前局得分。这个项目的意义不只是“好玩”它承担了三个很实际的职责让用户亲身体验 AI 文本和你脑海中的“人类文本”之间的差异。帮助开发者理解如何把“真实数据采集”和“模型生成内容”结合起来做测试集。用极低的开发成本验证一个 AI 产品的核心交互逻辑给出内容等待判断即时反馈。1.2 为什么选择维基百科作为人类样本来源选择维基百科有四个理由理由说明文本质量稳定维基百科是多人协作编辑的产物语言风格相对统一适合作为“人类百科体”的基准。获取方式公开MediaWiki API 提供免费的文本抽取接口不需要申请复杂的授权。许可相对友好维基百科文本基于 CC BY-SA 协议做非商业教学演示时可以标注来源使用。主题分布广随便抽一个随机条目都可能覆盖到科技、地理、历史、文化等不同领域。而 AI 生成样本则使用大模型按照“维基百科风格”写作。这样设计人类样本和 AI 样本的文本长度、语气、结构都比较接近玩家无法靠“短的就是人写的长的就是 AI 写的”这种粗糙规则钻空子。1.3 项目整体流程整个项目可以拆成四个模块样本采集通过维基百科 API 获取随机页面文本。文本生成通过大模型 API 生成对应主题的伪百科文本。题目装配把两类文本放入题目列表打乱顺序。交互界面用 Streamlit 展示题目、接收用户选择、统计得分。如果不想配置大模型 API项目也提供了一套静态示例文本能保证核心交互在本地跑通。这个设计很实用适合团队协作时多人快速验收交互效果。2. 环境准备与版本说明2.1 基础环境本文示例代码基于以下环境编写操作系统Windows / macOS / Linux 均可。Python建议 3.10 或更高版本。包管理pip 或 conda。浏览器Chrome / Edge / Safari 均可。IDE推荐 VS Code 或 PyCharm不强制。版本需要根据你的项目实际情况调整。本文示例以常见环境为例重点演示配置思路。如果你使用的 Python 版本或依赖包版本不同不一定会报错但建议优先使用较新的稳定版本。2.2 安装依赖在项目目录下新建requirements.txtstreamlit1.28.0 requests2.31.0 python-dotenv1.0.0 openai1.0.0然后执行pip install -r requirements.txt注意openai包主要用于调用大模型 API。如果你暂时不想配置 API Key项目仍然可以运行只是 AI 题目会使用内置静态示例。2.3 项目结构推荐按下面的目录组织代码wikipedia-ai-or-not/ ├── .env.example ├── requirements.txt ├── app.py ├── sample_loader.py ├── llm_generator.py ├── static_samples.py └── README.md其中app.pyStreamlit 主程序。sample_loader.py维基百科真实文本采集。llm_generator.py大模型生成伪百科文本。static_samples.py无 API Key 时的静态演示文本。.env.example环境变量模板。3. 核心模块设计与代码拆解3.1 真实文本采集连接维基百科 API维基百科使用的接口是 MediaWiki API它支持listrandom随机抽取条目也支持propextracts抽取纯文本摘要。我们只需要两次请求第一次请求获取一批随机条目标题。第二次请求用这些标题批量获取正文纯文本。以下是sample_loader.py的核心实现# 文件路径sample_loader.py import requests WIKI_API_URL https://{lang}.wikipedia.org/w/api.php USER_AGENT AIorNotQuiz/0.1 (https://example.com; contactexample.com) def fetch_random_paragraphs(langen, count5, min_chars300, max_chars900): paragraphs [] seen_titles set() while len(paragraphs) count: random_params { action: query, format: json, list: random, rnnamespace: 0, rnlimit: 20, } resp requests.get( WIKI_API_URL.format(langlang), paramsrandom_params, headers{User-Agent: USER_AGENT}, timeout10, ) resp.raise_for_status() random_items resp.json().get(query, {}).get(random, []) titles [] for item in random_items: title item.get(title, ).strip() if title and title not in seen_titles: titles.append(title) seen_titles.add(title) if not titles: break extract_params { action: query, format: json, prop: extracts, explaintext: True, titles: |.join(titles), redirects: 1, } ext_resp requests.get( WIKI_API_URL.format(langlang), paramsextract_params, headers{User-Agent: USER_AGENT}, timeout10, ) ext_resp.raise_for_status() pages ext_resp.json().get(query, {}).get(pages, {}) for page in pages.values(): if extract not in page: continue for para in page[extract].split(\n): para para.strip() if len(para) min_chars: paragraphs.append( { title: page.get(title, Unknown), text: para[:max_chars], label: human, } ) break if len(paragraphs) 0: break return paragraphs[:count]几个关键参数说明参数作用actionquery执行查询操作。formatjson让接口返回 JSON 格式便于解析。listrandom获取随机页面列表。rnnamespace0只在主条目命名空间里抽取避免拿到“讨论页”“用户页”等。propextracts获取页面文本摘要。explaintext让返回内容去掉 HTML 标签只保留纯文本。redirects1自动处理重定向页面。这里的User-Agent很重要。维基百科官方要求程序访问时必须携带可识别的 User-Agent否则可能被限流。如果你要长期运行请把contactexample.com换成自己的联系方式。3.2 生成 AI 伪百科文本提示词设计这一部分是整个项目的“技术灵魂”。如果让大模型直接写一篇“看起来像百科”的文本它容易写得非常空洞满屏都是“广泛应用于”“具有重要意义”这类模板句子。为了尽量接近维基百科风格我们需要在提示词里同时约束话题、语气、结构、长度和事实清晰度。以下是llm_generator.py# 文件路径llm_generator.py import os from dotenv import load_dotenv from openai import OpenAI load_dotenv() client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) SYSTEM_PROMPT ( 你是一名维基百科编辑。你写的文本需要满足以下要求\n 1. 语言客观、语气中性、避免情绪化。\n 2. 尽量包含可复核的信息不要编造不存在的引用。\n 3. 如果信息无法确认请使用‘根据现有资料显示’‘目前尚未有明确结论’等稳妥表达。\n 4. 段落结构清晰句子不要过度排比。 ) def generate_wikipedia_style_paragraph(topic: str, max_tokens: int 400) - str: 根据主题生成一段“维基百科风格”的百科体文本。 注意本函数仅用于教育演示生成文本仍可能存在幻觉。 user_prompt ( f请围绕‘{topic}’撰写一段 200 字到 300 字的百科条目介绍。\n 要求不要太像宣传文案不要堆砌形容词不要编造来源。 ) response client.chat.completions.create( modelgpt-4o-mini, # 如果当前账号没有该模型请改成可用模型名称 messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_prompt}, ], max_tokensmax_tokens, temperature0.7, ) return response.choices[0].message.content.strip()提示词为什么这样设计系统提示词用于限定生成角色的整体行为方式。它比每次在用户提示里重复一大堆规则更稳定。温度参数 temperature0.7表示生成文本的随机程度。太低容易重复模板太高容易跑题。0.7 是一个相对平衡的值。max_tokens控制最大输出长度。维基百科的正文段落通常不会特别长限制在 400 token 左右更贴近真实题目的观感。关于“AI 幻觉”需要多说一句。即使我们在提示词里要求模型“不要编造事实”大模型依然可能生成看似合理但实际错误的内容。所以这个项目的目标不是训练一个“权威百科生成器”而是模拟玩家日常会遇到的 AI 内容形态。如果你把这个项目发布到公共环境建议在说明页里标注部分 AI 生成文本可能包含不准确信息仅供教学体验。3.3 静态示例没有 API Key 也能跑通并不是每个接手项目的人都有可用的 OpenAI API Key。为了保证项目在任何环境下都能启动我增加了一个静态示例文件。# 文件路径static_samples.py STATIC_HUMAN_SAMPLES [ { title: Template variables, text: ( In software engineering, a template variable is a placeholder that is filled with a concrete value at runtime. It is commonly used in template engines to separate presentation logic from business data. The developer writes the placeholder in the template, and the engine replaces it with the corresponding value before rendering the final content. ), label: human, }, ] STATIC_AI_SAMPLES [ { title: Template variables, text: ( Template variables represent a fundamental concept in modern software development. They provide a flexible and efficient way to manage dynamic content, enabling developers to build complex applications with greater maintainability. By decoupling data from presentation, template variables support robust code reuse and significantly improve development efficiency. ), label: ai, }, ]注意这两个文本是用于界面联调的示意文本并不是真实维基百科条目也不是权威 AI 生成结果。它们的主要作用是在没有网络、没有 API Key 时让项目能立即展示“题目展示 → 用户选择 → 反馈结果”的完整交互。3.4 题目装配与交互逻辑核心文件app.py负责把上述模块串起来。# 文件路径app.py import os import random import streamlit as st from dotenv import load_dotenv from llm_generator import generate_wikipedia_style_paragraph from sample_loader import fetch_random_paragraphs from static_samples import STATIC_AI_SAMPLES, STATIC_HUMAN_SAMPLES load_dotenv() ROUNDS 10 def init_state(): if questions not in st.session_state: st.session_state.questions [] if round_index not in st.session_state: st.session_state.round_index 0 if correct not in st.session_state: st.session_state.correct 0 if started not in st.session_state: st.session_state.started False if finished not in st.session_state: st.session_state.finished False def build_questions(totalROUNDS): real_samples [] try: real_samples fetch_random_paragraphs( langen, counttotal // 2, min_chars300, max_chars900 ) except Exception as exc: st.warning(f无法连接维基百科 API{exc}将使用本地示例。) ai_samples [] if os.getenv(OPENAI_API_KEY): for item in real_samples: try: text generate_wikipedia_style_paragraph(item[title], max_tokens400) ai_samples.append( {title: item[title], text: text, label: ai} ) except Exception as exc: st.warning(fAI 生成失败{exc}该题将使用静态 AI 示例。) if len(ai_samples) total // 2: need total // 2 - len(ai_samples) ai_samples.extend( { title: 演示样本, text: sample[text], label: ai, } for sample in STATIC_AI_SAMPLES[:need] ) if len(real_samples) total // 2: need total // 2 - len(real_samples) real_samples.extend( { title: 演示样本, text: sample[text], label: human, } for sample in STATIC_HUMAN_SAMPLES[:need] ) questions [] for item in real_samples: questions.append( {title: item[title], text: item[text], label: item[label]} ) for item in ai_samples: questions.append( {title: item[title], text: item[text], label: item[label]} ) random.shuffle(questions) return questions[:total] def show_question(question, round_index, total): st.progress(round_index / total) st.subheader(f第 {round_index 1} 题 / 共 {total} 题) st.write(question[text]) col1, col2 st.columns(2) answer None with col1: if st.button(人类写的来自维基百科, keyfhuman_{round_index}): answer human with col2: if st.button(AI 生成的, keyfai_{round_index}): answer ai if answer: if answer question[label]: st.session_state.correct 1 st.success(回答正确) else: st.error(回答错误。) source_text ( 维基百科人类样本 if question[label] human else AI 生成样本 ) st.info(f本题真实来源{source_text}) st.session_state.round_index 1 st.rerun() def show_result(): total len(st.session_state.questions) st.header(本局成绩) st.write(f答对{st.session_state.correct} / {total}) st.write(f正确率{st.session_state.correct / total * 100:.1f}%) st.write( 注意这只是一局测验不代表 AI 检测能力定论 也请不要用本工具做严肃的文本真伪鉴定。 ) if st.button(再来一局): for key in [questions, round_index, correct, started, finished]: st.session_state.pop(key, None) st.rerun() def main(): init_state() st.title(Wikipedia: AI or Not Quiz) st.write( 判断下面文字来自真实的维基百科人类编辑还是 AI 生成 选择你认为正确的答案。 ) if not st.session_state.started: st.info( 点击开始后系统会从维基百科拉取真实文本 并调用大模型生成相似风格的 AI 文本。 ) if st.button(开始测验): with st.spinner(正在准备题目……): st.session_state.questions build_questions() st.session_state.started True st.rerun() else: current_total len(st.session_state.questions) st.session_state.finished ( st.session_state.round_index current_total ) if st.session_state.finished: show_result() else: question st.session_state.questions[st.session_state.round_index] show_question(question, st.session_state.round_index, current_total) if __name__ __main__: main()这段代码的关键点在于st.session_state。Streamlit 的特点是只要用户点击按钮、修改输入框、切换组件整个脚本就会从上到下重新执行。如果只把题目和得分存在普通 Python 变量里页面一刷新就会全部丢失。因此必须把状态放进st.session_state这样每次重跑脚本时题目进度、答对数、当前轮次都能保留下来。按钮的选择逻辑也值得注意。我使用了两列布局st.columns(2)左右各放一个按钮。用户点击后系统先判断答案是否正确再显示反馈最后把round_index加一并调用st.rerun()刷新页面进入下一题。如果你用的是比较旧的 Streamlit 版本st.rerun()可能不存在需要改成st.experimental_rerun()具体以当前安装版本为准。4. 运行与验证4.1 创建环境变量模板如果你需要使用大模型 API先创建.env.exampleOPENAI_API_KEYsk-your-api-key然后复制为.envcp .env.example .env在.env中填入真实 Key。注意千万不要把.env提交到 Git 仓库应该在.gitignore中忽略它.env __pycache__/ .venv/4.2 启动项目在项目根目录执行streamlit run app.py启动成功后终端会显示本地访问地址通常是http://localhost:8501浏览器打开后会看到项目的标题和“开始测验”按钮。点击按钮后如果配置了大模型 API系统会先等待维基百科请求和大模型生成的返回结果这个过程可能需要几十秒因此我使用了st.spinner来避免用户误以为页面卡死。4.3 预期效果正常进入第一题后页面会显示一个进度条。当前题号。一段百科体文字。两个判断按钮“人类写的来自维基百科”和“AI 生成的”。点击任意按钮后页面会立即给出对错反馈并显示本题真实来源。点击“再来一局”可以重新生成一批题目默认一轮 10 题。4.4 没有大模型 API 时的效果如果没有配置OPENAI_API_KEY系统会跳过模型生成直接使用静态 AI 示例。此时维基百科请求仍然会尝试访问外网。如果你所在的开发环境没有外网fetch_random_paragraphs会抛异常但会被try...except捕获此时真实样本也会退回到静态人类示例。这种降级方案在项目演示和集成测试中很有用。它保证了一个原则核心交互逻辑不应该被外部 API 的不可用性阻塞。5. 常见问题与排查思路在开发这个项目的过程中有几个问题出现频率很高。下面按“现象 → 原因 → 解决思路”整理成表格。问题现象常见原因解决思路点击“开始测验”后一直转圈维基百科接口响应慢或timeout设置太短把timeout10调整为更大值或先使用静态示例联调维基百科返回空文本随机抽到了重定向页、列表页、消歧页在二次请求中设置redirects1并筛选min_chars长度生成 AI 文本内容空洞提示词没有约束语气和事实要求增加系统提示词明确要求避免形容词堆砌页面每次操作后状态丢失Streamlit 普通变量不会跨重跑保存使用st.session_state保存题目、轮次、得分点击按钮后没有反应按钮 key 冲突或st.rerun()版本问题检查按钮 key 是否唯一旧版本改用st.experimental_rerun()API 调用报 401环境变量未加载或 Key 无效确认.env存在检查load_dotenv()是否调用题目数量不足 10 题维基百科返回的有效段落不够在build_questions中补充静态样本保证题目数对齐5.1 题目数量不足怎么办fetch_random_paragraphs中的while循环有可能因为随机到的页面质量太低而提前退出。这属于正常现象不必紧张。我的处理方式是在build_questions中判断真实样本数量如果不够就用静态人类样本补齐。如果你希望提高真实样本的命中率可以调整筛选条件降低min_chars。增加rnlimit。多次轮询直到取满。但如果min_chars设置太低抽出来的文本可能只是目录、注释或一句话反而降低题目质量。综合来看300 到 500 字符之间比较合适。5.2 大模型生成结果不可控怎么办大模型生成文本本身带有随机性同一主题在不同轮次可能生成完全不同的内容。为了保证题库质量你可以固定随机种子random.seed(42)至少让题目顺序可复现。把生成结果缓存成 JSON 文件避免每次启动都重复调用 API。增加人工审核环节先生成一批再手动剔除质量差的文本。在工程实践中大模型输出“不强校验不展示”是一个重要经验。如果是正式项目建议把生成的文本经过长度过滤、关键词过滤、重复度过滤之后再进入题库。6. 最佳实践与工程建议6.1 使用缓存降低 API 成本在真实项目中10 题可能需要调用 5 次维基百科接口和 5 次大模型接口。如果每个测试人员进入页面都重新生成一次API 费用会快速上升。解决思路是把已经生成的题目缓存到本地文件。例如可以在build_questions中加入一层 JSON 文件缓存import json import os CACHE_FILE question_cache.json def load_cache(): if os.path.exists(CACHE_FILE): with open(CACHE_FILE, r, encodingutf-8) as f: return json.load(f) return [] def save_cache(questions): with open(CACHE_FILE, w, encodingutf-8) as f: json.dump(questions, f, ensure_asciiFalse, indent2)这样同一批题目生成一次后后续测试人员看到的都是相同题目既方便对比答案又节省 API 调用。6.2 注意维基百科 API 的使用规范使用维基百科 API 时请遵守几个基础规范设置可识别的User-Agent包含项目名和联系方式。控制请求频率不要高频连续抓取。对公开文本标注来源。如果在生产环境使用优先考虑使用维基百科的数据库转储或官方数据集而不是在生产环境实时请求 API。这些规范不属于“可选优化”而是公共接口的使用底线。一旦违反轻则被限流重则被封禁 IP。6.3 安全与隐私边界这个项目涉及两类外部数据维基百科公开文本和用户点击行为。关于安全和隐私要注意OPENAI_API_KEY只放在环境变量中不要硬编码进代码。不要把.env上传到公开仓库。不要记录用户的具体判断内容和个人信息。如果部署到公网建议加最小权限的访问控制比如密码保护。还有一点容易被忽略生成文本可能包含不准确或带有偏见的内容。发布时最好在页面底部标注“AI 生成内容可能存在错误或幻觉仅供参考”这样既是对用户负责也是对自己的保护。6.4 让题目更有区分度默认状态下这个测验的题目区分度可能不高。建议在生产版本中增加一层“难度过滤”把 AI 生成文本长度和真实样本控制在接近范围。不要选择太冷门的维基百科条目因为 AI 对冷门主题生成的内容很容易露出马脚。可以生成多个 AI 版本人工挑选最接近真实百科风格的一条。这类“人工把关”在 AI 应用开发里仍然非常重要。不要迷信“模型自己就能做得很好”尤其是面向最终用户的 C 端产品文本质量直接决定产品口碑。6.5 后续扩展方向这个项目可以延伸出很多玩法扩展方向说明排行榜把用户的答题得分、耗时存入数据库展示实时排名多语言支持把lang参数从en改成zh即可使用中文维基百科题目详情页用户答完后展示原文链接或生成提示词模型对比让同一主题由不同大模型生成用户判断哪个更像 AI分析报告记录用户答错题目的特征给出针对性讲解尤其是“模型对比”方向在 AI 工程实践里非常有价值。当多个模型面对同一主题时它们的表达习惯差异会比单一模型更明显玩家也能更直观地理解“模型风格”这个概念。7. 总结与学习路线到这里Wikipedia: AI or Not Quiz 的完整实现已经梳理完了。通过这个项目你可以掌握几条很实用的技能链使用 MediaWiki API 获取维基百科真实文本。通过提示词让大模型生成指定风格的文本。用 Streamlit 构建交互式 AI 应用。在外部 API 不稳定时设计降级方案。结合缓存、安全、权限控制做工程化改造。如果你接下来想继续深入建议按这条路线走先尝试把题目缓存做成 SQLite 存储再增加中文维基百科支持然后加入用户答题记录和排行榜最后把项目部署到 Streamlit Cloud 或自己的服务器上让其他人也能访问。做这个项目时最值得关注的不是“最终得分准确率”而是用户面对文本时的观察过程。真正到了 AI 内容遍地的阶段学会观察、怀疑和验证才是比任何单项技术都更重要的能力。如果你在应用开发中遇到其他 AI 交互设计问题也可以在这个项目框架上继续做实验。
返回列表