基于DeepSeek与豆包API构建AI互评系统:自动化出题答题实战
大家好我是专注于AI应用开发与集成实战的技术博主。在日常工作中我们常常需要对比不同大语言模型LLM的能力或者构建一些有趣的自动化评测流程。今天我们就来探讨一个非常实用的场景如何让DeepSeek模型自动生成题目并调用豆包字节跳动的AI模型的API来回答从而实现一个自动化的“出题-答题”评测系统。这个项目不仅有趣更能帮助我们深入理解如何通过API集成不同的AI服务构建自动化工作流。无论你是想对比模型性能、构建智能评测工具还是单纯想探索AI的更多玩法本文都将提供一套从环境搭建、代码实现到问题排查的完整闭环方案。1. 背景与核心概念为什么需要AI互评在AI技术快速迭代的今天各类大模型层出不穷如DeepSeek、豆包、通义千问、文心一言等。开发者、研究者和企业经常面临一个问题如何客观、高效地评估不同模型在特定任务如代码生成、逻辑推理、创意写作上的表现手动设计测试用例并逐一评测不仅耗时耗力而且难以保证全面性和一致性。因此自动化评测系统变得至关重要。其核心思想是出题方由一个AI模型如DeepSeek根据预设的领域或难度动态生成一系列高质量的测试题目。答题方由另一个或多个AI模型如豆包接收题目并生成答案。评估方通过规则如关键词匹配、模型打分如用GPT-4做裁判或人工抽查等方式对答案的质量进行评估。本项目的核心价值在于技术集成学习如何调用不同厂商的AI APIDeepSeek API 和 豆包API。流程自动化构建一个完整的、可复用的自动化脚本。模型对比为后续深入的模型能力分析提供原始数据。思维启发展示了AI不仅可以作为工具还可以成为流程中的“协作者”和“评估者”。2. 环境准备与版本说明在开始编码前我们需要准备好开发环境和必要的账户权限。2.1 基础开发环境操作系统Windows 10/11, macOS, 或 Linux (如 Ubuntu 20.04)。本文示例命令以Linux/macOS的bash为主Windows用户可使用PowerShell或WSL。Python版本 3.8 或更高。这是调用各类AI SDK最常用的语言。包管理工具pip(Python自带)。2.2 核心依赖库我们将使用requests库进行HTTP API调用使用openai库兼容DeepSeek API来简化调用流程。首先创建一个新的项目目录并安装依赖。# 创建项目目录 mkdir ai-quiz-master cd ai-quiz-master # 创建虚拟环境推荐 python -m venv venv # 激活虚拟环境 # Linux/macOS: source venv/bin/activate # Windows: # venv\Scripts\activate # 安装依赖包 pip install requests openai2.3 API密钥申请与配置本项目需要两个关键的API密钥DeepSeek API Key用于让DeepSeek出题。访问 DeepSeek 开放平台官网注册并获取API Key。目前支持的模型名称为deepseek-v4-pro或deepseek根据官方文档调整。豆包 API Key用于让豆包答题。访问字节跳动火山引擎云平台在“模型服务”中找到豆包模型创建应用并获取API Key。安全提示API Key是访问服务的凭证务必妥善保管切勿上传至公开仓库。在项目根目录创建一个名为.env的文件来存储密钥确保该文件在.gitignore中# .env 文件内容 DEEPSEEK_API_KEYyour_deepseek_api_key_here DEEPSEEK_BASE_URLhttps://api.deepseek.com # 以官方文档为准 DEEPSEEK_MODELdeepseek-v4-pro # 或 deepseek DOUBAO_API_KEYyour_doubao_api_key_here DOUBAO_BASE_URLhttps://ark.cn-beijing.volces.com/api/v3 # 豆包API地址以火山引擎控制台为准 DOUBAO_MODELep-20250205142120-abcdefg # 你的豆包模型Endpoint ID同时安装python-dotenv库来方便地读取环境变量pip install python-dotenv2.4 项目结构预览完成后的项目结构将如下所示ai-quiz-master/ ├── .env # 存储API密钥保密 ├── .gitignore # 忽略.env和虚拟环境 ├── requirements.txt # 项目依赖声明 ├── quiz_master.py # 主程序出题、答题、评测流程控制 ├── deepseek_client.py # DeepSeek API 客户端封装 ├── doubao_client.py # 豆包 API 客户端封装 ├── evaluator.py # 简单的答案评估逻辑可选 └── logs/ # 存放运行日志3. 核心模块拆解与代码实现接下来我们将分模块构建整个系统。3.1 配置加载模块首先创建一个工具模块来安全地加载环境变量。虽然简单但这是保证代码可移植性和安全性的基础。# config.py import os from dotenv import load_dotenv load_dotenv() # 加载 .env 文件中的变量到环境变量 class Config: 配置管理类 DEEPSEEK_API_KEY os.getenv(DEEPSEEK_API_KEY) DEEPSEEK_BASE_URL os.getenv(DEEPSEEK_BASE_URL, https://api.deepseek.com) DEEPSEEK_MODEL os.getenv(DEEPSEEK_MODEL, deepseek-v4-pro) DOUBAO_API_KEY os.getenv(DOUBAO_API_KEY) DOUBAO_BASE_URL os.getenv(DOUBAO_BASE_URL) DOUBAO_MODEL os.getenv(DOUBAO_MODEL) classmethod def validate(cls): 验证必要配置是否存在 missing [] if not cls.DEEPSEEK_API_KEY: missing.append(DEEPSEEK_API_KEY) if not cls.DOUBAO_API_KEY: missing.append(DOUBAO_API_KEY) if not cls.DOUBAO_BASE_URL: missing.append(DOUBAO_BASE_URL) if missing: raise ValueError(f缺少必要的环境变量配置: {, .join(missing)}。请检查 .env 文件。)3.2 DeepSeek 客户端出题官这个模块负责调用DeepSeek API根据我们的指令生成题目。我们使用openai库的兼容模式。# deepseek_client.py import openai from config import Config import logging logging.basicConfig(levellogging.INFO) logger logging.getLogger(__name__) class DeepSeekClient: DeepSeek API 客户端负责生成题目 def __init__(self): Config.validate() # 配置OpenAI客户端指向DeepSeek的API端点 self.client openai.OpenAI( api_keyConfig.DEEPSEEK_API_KEY, base_urlConfig.DEEPSEEK_BASE_URL ) self.model Config.DEEPSEEK_MODEL def generate_quiz(self, topicPython编程, difficulty中等, num_questions3): 请求DeepSeek生成指定主题和难度的题目。 Args: topic (str): 题目主题如“Python编程”、“数据结构”、“中国历史”。 difficulty (str): 题目难度如“简单”、“中等”、“困难”。 num_questions (int): 生成题目的数量。 Returns: list: 包含题目字典的列表每个字典有 id, topic, difficulty, content。 system_prompt f你是一位严谨的出题专家。请生成 {num_questions} 道关于 **{topic}** 的题目难度为 **{difficulty}**。 要求 1. 题目类型可以是单选题、多选题、判断题或简答题。 2. 题目表述清晰无歧义。 3. 如果是选择题请提供3-4个选项并标明正确答案。 4. 以清晰的JSON格式返回包含以下字段id从1开始, topic, difficulty, content题目内容, options选择题选项列表可选, correct_answer正确答案可选。 示例格式 [ {{id: 1, topic: Python编程, difficulty: 中等, content: Python中以下哪个关键字用于定义函数, options: [A. func, B. def, C. function, D. define], correct_answer: B}} ] 现在请开始生成题目 try: logger.info(f正在请求DeepSeek生成题目主题{topic}难度{difficulty}) response self.client.chat.completions.create( modelself.model, messages[ {role: system, content: 你是一位专业的出题官。}, {role: user, content: system_prompt} ], temperature0.7, # 控制创造性0.7能平衡一致性和多样性 response_format{type: json_object} # 要求返回JSON格式 ) result response.choices[0].message.content # 解析返回的JSON字符串 import json quizzes json.loads(result) # 确保返回的是列表 if isinstance(quizzes, dict) and quizzes in quizzes: quizzes quizzes[quizzes] elif isinstance(quizzes, dict): quizzes [quizzes] # 如果返回的是单个对象包装成列表 logger.info(f成功生成 {len(quizzes)} 道题目。) return quizzes except openai.APIError as e: logger.error(fDeepSeek API调用失败: {e}) raise except json.JSONDecodeError as e: logger.error(f解析DeepSeek返回的JSON失败: {e}\n原始返回{result}) raise3.3 豆包客户端答题者这个模块负责调用豆包火山引擎的API来回答DeepSeek生成的题目。豆包API通常遵循类似OpenAI的格式但参数可能略有不同需参考其官方文档。# doubao_client.py import requests import json import logging from config import Config logger logging.getLogger(__name__) class DoubaoClient: 豆包火山引擎API 客户端负责回答问题 def __init__(self): Config.validate() self.api_key Config.DOUBAO_API_KEY self.base_url Config.DOUBAO_BASE_URL self.model Config.DOUBAO_MODEL self.headers { Authorization: fBearer {self.api_key}, Content-Type: application/json } def answer_question(self, question_text, max_tokens500): 向豆包模型提问并获取答案。 Args: question_text (str): 问题文本。 max_tokens (int): 回答的最大token数。 Returns: str: 豆包模型生成的答案文本。 # 豆包API的请求体可能与OpenAI不完全相同此处为示例请以火山引擎官方文档为准 payload { model: self.model, messages: [ {role: user, content: question_text} ], max_tokens: max_tokens, temperature: 0.3 # 温度调低让答案更确定、更简洁 } try: logger.info(f向豆包提问: {question_text[:50]}...) # 日志只记录前50字符 response requests.post( f{self.base_url}/chat/completions, # API路径可能不同 headersself.headers, datajson.dumps(payload), timeout30 # 设置超时 ) response.raise_for_status() # 如果状态码不是200抛出HTTPError result response.json() # 解析响应提取答案文本。实际路径需根据豆包API响应结构调整。 # 常见结构result[choices][0][message][content] answer result.get(choices, [{}])[0].get(message, {}).get(content, ) if not answer: logger.warning(f豆包返回的答案内容为空。完整响应{result}) answer [未获得有效答案] logger.info(豆包已回答。) return answer.strip() except requests.exceptions.RequestException as e: logger.error(f调用豆包API时发生网络或请求错误: {e}) raise except (KeyError, IndexError, json.JSONDecodeError) as e: logger.error(f解析豆包API响应失败: {e}\n响应内容{response.text if response in locals() else N/A}) raise3.4 主流程控制与评测逻辑现在我们将出题官和答题者串联起来并加入简单的评测逻辑。# quiz_master.py import json import time import logging from deepseek_client import DeepSeekClient from doubao_client import DoubaoClient logging.basicConfig(levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s) logger logging.getLogger(__name__) class QuizMaster: 主控制器协调出题、答题、评测全流程 def __init__(self): self.deepseek DeepSeekClient() self.doubao DoubaoClient() self.results [] def run_quiz_session(self, topic计算机科学, difficulty中等, num_questions2): 执行一次完整的出题-答题会话。 logger.info(f 开始新的评测会话 ) logger.info(f主题{topic} | 难度{difficulty} | 题数{num_questions}) # 1. 出题阶段 try: quizzes self.deepseek.generate_quiz(topic, difficulty, num_questions) except Exception as e: logger.error(f出题阶段失败: {e}) return # 2. 答题与评测阶段 for quiz in quizzes: quiz_id quiz.get(id, len(self.results) 1) content quiz.get(content, ) correct_answer quiz.get(correct_answer, 未知) options quiz.get(options, []) logger.info(f\n--- 处理第 {quiz_id} 题 ---) logger.info(f题目{content}) if options: logger.info(f选项{options}) # 构建给豆包的提问 question_for_doubao f请回答以下问题\n{content}\n if options: question_for_doubao f选项{ .join(options)}\n question_for_doubao 请直接给出你的答案无需解释。 # 调用豆包答题 try: time.sleep(1) # 简单限流避免请求过快 doubao_answer self.doubao.answer_question(question_for_doubao) except Exception as e: logger.error(f豆包答题失败: {e}) doubao_answer [答题失败] # 3. 简单评测规则匹配示例 evaluation self._evaluate_answer(correct_answer, doubao_answer, options) # 保存结果 result_record { quiz_id: quiz_id, topic: topic, difficulty: difficulty, question: content, correct_answer: correct_answer, doubao_answer: doubao_answer, evaluation: evaluation, is_correct: evaluation.get(match, False) } self.results.append(result_record) logger.info(f豆包答案{doubao_answer}) logger.info(f评测结果{evaluation[verdict]}) # 4. 会话总结 self._summarize_session() def _evaluate_answer(self, correct_answer, doubao_answer, optionsNone): 简单的答案评估器。 这是一个基础示例实际评估可能更复杂如使用另一个LLM进行评分。 # 预处理去除空格转为小写进行粗略匹配 correct_processed str(correct_answer).strip().lower() doubao_processed doubao_answer.strip().lower() evaluation { verdict: 未匹配, match: False, method: exact_match } # 情况1完全匹配 if correct_processed and correct_processed in doubao_processed: evaluation[verdict] 答案正确包含正确答案 evaluation[match] True return evaluation # 情况2选择题选项匹配 (例如正确答案是“B”豆包回答了“选项B”) if options and correct_processed in [a, b, c, d]: # 在豆包答案中查找 A, B, C, D import re pattern r[A-D] matches re.findall(pattern, doubao_answer.upper()) if matches and matches[0].lower() correct_processed: evaluation[verdict] f答案正确匹配选项 {matches[0]} evaluation[match] True return evaluation # 情况3无法自动判断 evaluation[verdict] 需人工复核 return evaluation def _summarize_session(self): 打印本次会话的统计摘要 if not self.results: logger.info(本次会话未产生任何结果。) return total len(self.results) correct sum(1 for r in self.results if r[is_correct]) accuracy (correct / total * 100) if total 0 else 0 logger.info(f\n 会话总结 ) logger.info(f总题数{total}) logger.info(f正确题数{correct}) logger.info(f自动判题准确率{accuracy:.2f}%) logger.info(f详细结果已保存至 quiz_results.json。) def save_results(self, filenamequiz_results.json): 将评测结果保存为JSON文件 with open(filename, w, encodingutf-8) as f: json.dump(self.results, f, ensure_asciiFalse, indent2) logger.info(f结果已保存到 {filename}) if __name__ __main__: # 示例运行一个评测会话 master QuizMaster() master.run_quiz_session(topicPython编程, difficulty中等, num_questions3) master.save_results()4. 运行与验证完成所有代码编写后让我们来运行这个系统。4.1 运行程序在项目根目录下确保虚拟环境已激活并执行主程序python quiz_master.py4.2 预期输出你将在控制台看到类似以下的日志输出展示了完整的出题、答题、评测流程2023-10-27 10:00:00 - root - INFO - 开始新的评测会话 2023-10-27 10:00:00 - root - INFO - 主题Python编程 | 难度中等 | 题数3 2023-10-27 10:00:00 - deepseek_client - INFO - 正在请求DeepSeek生成题目主题Python编程难度中等 2023-10-27 10:00:02 - deepseek_client - INFO - 成功生成 3 道题目。 --- 处理第 1 题 --- 2023-10-27 10:00:02 - root - INFO - 题目Python中用于抛出异常的关键字是什么 2023-10-27 10:00:02 - root - INFO - 选项[A. throw, B. raise, C. except, D. error] 2023-10-27 10:00:02 - doubao_client - INFO - 向豆包提问: 请回答以下问题 Python中用于抛出异常的关键字是什么... 2023-10-27 10:00:03 - doubao_client - INFO - 豆包已回答。 2023-10-27 10:00:03 - root - INFO - 豆包答案raise 2023-10-27 10:00:03 - root - INFO - 评测结果答案正确包含正确答案 ... (第2、3题处理过程) ... 会话总结 2023-10-27 10:00:07 - root - INFO - 总题数3 2023-10-27 10:00:07 - root - INFO - 正确题数2 2023-10-27 10:00:07 - root - INFO - 自动判题准确率66.67% 2023-10-27 10:00:07 - root - INFO - 详细结果已保存至 quiz_results.json。 2023-10-27 10:00:07 - root - INFO - 结果已保存到 quiz_results.json4.3 结果文件程序运行后会生成一个quiz_results.json文件里面记录了所有题目的详细信息、豆包的答案以及自动评测的结果方便后续分析和复查。[ { quiz_id: 1, topic: Python编程, difficulty: 中等, question: Python中用于抛出异常的关键字是什么, correct_answer: B, doubao_answer: raise, evaluation: { verdict: 答案正确包含正确答案, match: true, method: exact_match }, is_correct: true }, ... ]5. 常见问题与排查思路在实际运行中你可能会遇到一些问题。以下是常见问题的排查指南。问题现象可能原因排查步骤与解决方案导入错误ModuleNotFoundError1. 虚拟环境未激活。2. 依赖包未安装。1. 运行source venv/bin/activate(Linux/macOS) 或venv\Scripts\activate(Windows) 激活环境。2. 运行pip install -r requirements.txt或重新安装requests openai python-dotenv。DeepSeek API 调用失败错误 401/4031. API Key 错误或过期。2. 请求的模型名称不对。3. 账户余额不足或未开通服务。1. 检查.env文件中的DEEPSEEK_API_KEY是否正确确保没有多余空格。2. 确认DEEPSEEK_MODEL是当前平台支持的模型如deepseek-v4-pro。3. 登录DeepSeek平台检查API使用情况和账户状态。豆包 API 调用失败错误 4001. API Key、Base URL 或 Model Endpoint 错误。2. 请求体格式不符合豆包API要求。3. 模型Endpoint未正确创建或未发布。1. 仔细检查.env中豆包相关的三个变量确保全部从火山引擎控制台正确复制。2.重点对照火山引擎豆包API的最新官方文档检查doubao_client.py中的请求URL (/chat/completions) 和请求体格式。不同版本API可能有差异。3. 在火山引擎控制台确认模型服务已成功部署并处于“运行中”状态。豆包返回答案为空或格式异常1. API响应结构解析路径错误。2. 模型因内容策略未返回答案。1. 在doubao_client.py的answer_question方法中添加print(result)或logger.debug打印完整的API响应根据实际结构调整解析代码如result[data][choices][0][message][content]。2. 尝试调整提问的措辞避免敏感或模糊的指令。程序卡住或无响应1. 网络超时。2. API服务器响应慢。1. 在requests.post调用中增加timeout参数如timeout30。2. 在调用API前后添加日志定位卡在哪一步。考虑增加重试机制。自动评测结果不准确1. 规则匹配 (_evaluate_answer) 过于简单。2. 正确答案格式与豆包答案格式差异大。1. 这是预期之内。本项目的自动评测仅为示例。对于复杂问题如简答、编程应考虑使用更高级的评估方法例如a)使用第三个LLM作为裁判将题目、标准答案和豆包答案一起发给GPT-4等模型让其评分。b)嵌入向量相似度将答案文本转换为向量计算余弦相似度。c)关键词扩展匹配使用同义词库进行匹配。6. 最佳实践与工程建议将这个小项目投入生产环境或进行扩展时需要考虑以下几点6.1 配置与安全密钥管理绝对不要将.env文件提交到Git。确保.gitignore中包含.env和venv/。在生产环境中应使用环境变量、密钥管理服务如AWS Secrets Manager、HashiCorp Vault或云平台提供的安全配置。配置验证像Config.validate()那样在程序启动时强制检查关键配置避免运行时因配置缺失而失败。6.2 代码健壮性异常处理API调用必须包裹在try-except块中并记录详细的错误日志便于排查。区分网络错误、认证错误、API限制错误等。重试机制对于网络抖动或API限流导致的瞬时失败可以实现一个带指数退避的重试逻辑。import time from tenacity import retry, stop_after_attempt, wait_exponential retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min4, max10)) def call_api_safely(): # API调用代码 pass限流控制尊重API提供商的速率限制。在循环调用API时如批量答题使用time.sleep()或更高级的令牌桶算法来控制请求频率。6.3 评估体系优化评估器抽象将_evaluate_answer方法抽象成一个独立的Evaluator类或模块便于未来扩展多种评估策略规则匹配、LLM评分、向量相似度。人工复核接口对于自动评测不确定的答案可以设计一个简单的Web界面或标记系统方便人工进行最终裁定并将结果反馈回系统用于优化自动评估逻辑。6.4 可扩展性设计多模型支持当前的QuizMaster类紧密耦合了DeepSeek和豆包。可以设计一个QuizGenerator抽象基类和AnswerBot抽象基类让系统能够轻松接入新的出题模型如GPT-4、Claude和新的答题模型如通义千问、文心一言。结果持久化不要只保存在JSON文件。可以考虑集成数据库如SQLite、PostgreSQL来存储历史评测记录便于进行趋势分析和模型对比。异步处理如果题目数量很多同步调用API会非常慢。可以使用asyncio和aiohttp库进行异步并发调用大幅提升吞吐量。6.5 监控与日志结构化日志使用structlog或logging的Formatter输出JSON格式的日志方便接入ELKElasticsearch, Logstash, Kibana或云日志服务进行监控和告警。关键指标记录每次API调用的耗时、成功率、token消耗量等。这些数据对于成本优化和性能分析至关重要。通过这个“让DeepSeek出题考豆包”的项目我们不仅实现了一个有趣的AI互评Demo更实践了多AI服务集成、API调用、异常处理、流程编排等后端开发的核心技能。你可以在此基础上继续探索更复杂的评估方法、更丰富的题目类型甚至构建一个完整的模型能力基准测试平台。