开发博客:AI面试官个性化出题MCP功能最终完善
本周作为项目开发的最后冲刺阶段,我们致力于进一步增强AI面试官在个性化题目生成方面的能力。核心工作是新增和优化了一系列MCP(Multi-turn Conversation Protocol)工具,旨在为AI面试官提供更丰富、更精准的用户画像和知识背景,从而生成更具针对性的面试题目。
目前整个MCP工具链的结构如下:
本周主要完成的MCP功能模块包括:query_user_submission_stats
,query_user_articles
,以及search_articles_by_keyword
。以下将对这些功能进行详细说明。
1. query_user_submission_stats
:用户答题数据统计
- 功能描述: 此工具通过分析用户在在线判题系统(OJ)中的答题记录,评估用户的代码熟练度和能力水平。统计信息包括用户尝试过的题目总数、成功解答的题目总数,以及成功解答题目按难度(简单、中等、困难)的分布情况。
- 实现逻辑:
- 工具接收用户ID(
userId
)作为输入。首先对userId
进行校验,确保其为有效的整型数字字符串。 - 从
oj_code_submission
数据库表中查询指定用户的所有提交记录,提取problem_id
和status
。 - 使用
Set
数据结构统计用户尝试过的所有题目ID(attemptedProblemIds
)和状态为“accepted”的题目ID(acceptedProblemIds
),以实现自动去重。 - 若用户有成功解答的题目(
acceptedProblemIds
不为空),则进一步从oj_problem
表中查询这些已通过题目的难度(difficulty
)。 - 根据查询到的难度信息,统计“简单”、“中等”、“困难”各自的数量。
- 最终返回一个包含总尝试题目数(
totalAttempted
)、总通过题目数(totalAccepted
)以及按难度分类的通过题目数(acceptedByDifficulty
)的JSON对象。 - 包含完整的错误处理机制,如无效
userId
或数据库查询失败等情况。
- 工具接收用户ID(
- 价值: AI面试官可以利用这些数据了解候选人的实际编程能力和薄弱环节,从而调整面试题目的难度和类型,实现更精准的考察。
- 代码实现:
server.tool("query_user_submission_stats","统计用户的答题记录信息,包括已完成题目数和完成题目的按难度分类统计",{userId: z.string().describe("用户ID,为整形数字的字符串格式"),},async ({ userId }) => {try {const mysqlPool = getMySQL();const userIdInt = parseInt(userId);if (isNaN(userIdInt)) {return {content: [{ type: "text", text: `无效的userId格式: ${userId}` }],isError: true};}// 获取用户的提交记录const [submissions] = await mysqlPool.query(`SELECT problem_id, status FROM oj_code_submission WHERE user_id = ?`, [userIdInt]);// 统计已尝试和已通过的题目const attemptedProblemIds = new Set();const acceptedProblemIds = new Set();const ACCEPTED_STATUS = "accepted";for (const submission of submissions) {const problemId = submission.problem_id;// 所有提交过的题目ID(自动去重)attemptedProblemIds.add(problemId);// 仅添加ACCEPTED状态的题目ID(自动去重)if (ACCEPTED_STATUS.toLowerCase() === submission.status.toLowerCase()) {acceptedProblemIds.add(problemId);}}// 查询已通过题目的难度分布const acceptedProblemsList = Array.from(acceptedProblemIds);let difficultyStats = {easy: 0,medium: 0,hard: 0};if (acceptedProblemsList.length > 0) {const [problemDifficulties] = await mysqlPool.query(`SELECT id, difficulty FROM oj_problem WHERE id IN (?)`, [acceptedProblemsList]);// 按难度统计for (const problem of problemDifficulties) {const difficulty = problem.difficulty ? problem.difficulty.toLowerCase() : 'unknown';if (difficulty === '简单') difficultyStats.easy++;else if (difficulty === '中等') difficultyStats.medium++;else if (difficulty === '困难') difficultyStats.hard++;}}// 构建统计结果const stats = {totalAttempted: attemptedProblemIds.size,totalAccepted: acceptedProblemIds.size,acceptedByDifficulty: difficultyStats};return {content: [{ type: "text", text: JSON.stringify(stats, null, 2) }]};} catch (error) {console.error(`查询用户答题统计时发生错误: ${error.message}`);return {content: [{ type: "text", text: `查询用户答题统计时发生内部错误: ${error.message}` }],isError: true};}} );
2. query_user_articles
:用户论坛发帖分析
- 功能描述: 该工具用于检索并分析用户近期在社区论坛中发布的文章。目的是从用户的发帖内容中提取其关注的技术方向、感兴趣的公司、讨论过的面试题目等信息,作为个性化出题的参考。
- 实现逻辑:
- 工具接收用户ID(
userId
)作为输入,并进行有效性校验。 - 通过联合查询
forest_article
(文章主表)和forest_article_content
(文章内容表),获取指定article_author_id
用户的最多20篇最新发布的文章,包括文章标题、标签、浏览/评论/点赞数、创建时间、预览内容及完整内容。 - 若未找到用户文章,则返回相应提示。
- 如果查询到文章,且配置了DashScope的API密钥(
DASHSCOPE_API_KEY
)和应用ID(DASHSCOPE_SUM_APP_ID
),则将获取的文章数据(JSON格式)作为输入,调用DashScope大模型服务进行内容分析和总结。Prompt中指定为“模式一以分析下面内容”。 - AI服务返回对用户发帖内容的提炼信息,该信息将作为出题参考返回。
- 若AI调用失败、未配置API密钥或应用ID,则直接返回原始查询到的文章数据(JSON格式)。
- 包含数据库查询和AI服务调用的错误处理。
- 工具接收用户ID(
- 价值: 通过用户自身的言论,AI面试官能更深入地了解候选人的求职意向、技术栈偏好以及对特定问题的看法,为生成高度个性化的面试场景和问题提供素材。
- 代码实现:
server.tool("query_user_articles","根据用户ID获取该用户发布文章,用于作为用户个性化问题出题参考",{userId: z.string().describe("用户ID,为整形数字的字符串格式"),},async ({ userId }) => {try {const mysqlPool = getMySQL();const userIdInt = parseInt(userId);if (isNaN(userIdInt)) {return {content: [{ type: "text", text: `无效的userId格式: ${userId}` }],isError: true};}// 联合查询获取用户的文章及其内容const [articles] = await mysqlPool.query(`SELECT a.id, a.article_title, a.article_tags, a.article_view_count,a.article_comment_count,a.article_thumbs_up_count,a.created_time,a.article_preview_content,c.article_contentFROM forest_article aLEFT JOIN forest_article_content c ON a.id = c.id_articleWHERE a.article_author_id = ?ORDER BY a.created_time DESCLIMIT 20`, [userIdInt]);if (!articles || articles.length === 0) {return {content: [{ type: "text", text: `未找到用户ID为${userId}的文章` }]};}// 调用AI处理查询结果const apiKey = process.env.DASHSCOPE_API_KEY;const appId = process.env.DASHSCOPE_SUM_APP_ID;if (!apiKey || !appId) {console.error('DashScope API Key or App ID not configured.');return {content: [{ type: "text", text: JSON.stringify(articles, null, 2) }]};}const url = `https://dashscope.aliyuncs.com/api/v1/apps/${appId}/completion`;const data = {input: {prompt: `以模式一以分析下面内容:\n${JSON.stringify(articles)}`},parameters: {},debug: {}};try {const response = await axios.post(url, data, {headers: {'Authorization': `Bearer ${apiKey}`,'Content-Type': 'application/json'}});if (response.status === 200 && response.data.output && response.data.output.text) {return {content: [{ type: "text", text: "获取到以下信息作为你的出题参考"+response.data.output.text }]};} } catch (aiError) {console.error(`Error calling DashScope: ${aiError.message}`);// 如果AI调用失败,返回原始查询结果return {content: [{ type: "text", text: JSON.stringify(articles, null, 2) }]};}} catch (error) {console.error(`查询用户文章时发生错误: ${error.message}`);return {content: [{ type: "text", text: `查询用户文章时发生内部错误: ${error.message}` }],isError: true};}} );
3. search_articles_by_keyword
:社区内容关键词检索
- 功能描述: 此工具允许根据特定关键词(如目标公司名称“阿里”、“腾讯”等)在社区论坛中搜索相关的文章,如面经分享、技术探讨、解题思路等,为AI面试官提供针对性的出题参考。
- 实现逻辑:
- 工具接收搜索关键词(
keyword
)作为输入,并校验关键词非空。 - 构建SQL的
LIKE
查询模式(%keyword%
),在forest_article
表的article_title
字段和forest_article_content
表的article_content
字段中进行模糊匹配。 - 查询并返回按创建时间倒序排列的最多20篇相关文章,包含与
query_user_articles
类似的字段信息。 - 若未找到匹配文章,则返回相应提示。
- 与
query_user_articles
类似,如果查询到文章且配置了DashScope服务,则将文章数据发送给大模型进行分析总结。Prompt中指定为“模式二以分析下面内容”。 - AI服务返回对搜索结果的提炼信息,作为出题参考。
- 若AI调用失败、未配置或关键词无效,则返回原始文章数据或错误信息。
- 包含数据库查询和AI服务调用的错误处理。
- 工具接收搜索关键词(
- 价值: 当AI面试官需要针对特定公司或技术领域出题时,此工具能快速从社区中聚合相关的高价值信息,确保面试问题的前沿性和针对性,例如了解某公司常考的知识点或最新的技术趋势。
- 代码实现:
server.tool("search_articles_by_keyword","根据企业相关关键词(如阿里,腾讯)获取社区中提及相关出题知识点,用于作为出题参考",{keyword: z.string().describe("搜索关键词"),},async ({ keyword }) => {try {if (!keyword || keyword.trim() === "") {return {content: [{ type: "text", text: "搜索关键词不能为空" }],isError: true};}const mysqlPool = getMySQL();const searchKeyword = `%${keyword}%`; // 构建LIKE匹配模式// 联合查询匹配标题或内容的文章const [articles] = await mysqlPool.query(`SELECT a.id, a.article_title, a.article_tags, a.article_view_count,a.article_comment_count,a.article_thumbs_up_count,a.created_time,a.article_preview_content,c.article_contentFROM forest_article aLEFT JOIN forest_article_content c ON a.id = c.id_articleWHERE a.article_title LIKE ? OR c.article_content LIKE ?ORDER BY a.created_time DESCLIMIT 20`, [searchKeyword, searchKeyword]);if (!articles || articles.length === 0) {return {content: [{ type: "text", text: `未找到包含关键词"${keyword}"的文章` }]};}// 调用AI处理查询结果const apiKey = process.env.DASHSCOPE_API_KEY;const appId = process.env.DASHSCOPE_SUM_APP_ID;if (!apiKey || !appId) {console.error('DashScope API Key or App ID not configured.');return {content: [{ type: "text", text: JSON.stringify(articles, null, 2) }]};}const url = `https://dashscope.aliyuncs.com/api/v1/apps/${appId}/completion`;const data = {input: {prompt: `以模式二以分析下面内容:\n${JSON.stringify(articles)}`},parameters: {},debug: {}};try {const response = await axios.post(url, data, {headers: {'Authorization': `Bearer ${apiKey}`,'Content-Type': 'application/json'}});if (response.status === 200 && response.data.output && response.data.output.text) {return {content: [{ type: "text", text:"获取到以下信息作为你的出题参考"+ response.data.output.text }]};} } catch (aiError) {console.error(`Error calling DashScope: ${aiError.message}`);// 如果AI调用失败,返回原始查询结果return {content: [{ type: "text", text: JSON.stringify(articles, null, 2) }]};}} catch (error) {console.error(`关键词搜索文章时发生错误: ${error.message}`);return {content: [{ type: "text", text: `关键词搜索文章时发生内部错误: ${error.message}` }],isError: true};}} );
代码集成说明
上述三个核心功能均已通过server.tool
方法在服务端进行了注册。每个工具都明确了其名称、功能描述、输入参数模式(使用zod
进行定义和校验)以及异步执行函数。这种模块化的工具设计,使得AI面试官能够根据对话上下文和面试需求,灵活地调用这些外部能力。代码实现细节已在各功能模块中展示。
本周工作总结与意义
通过本周新增的这三个MCP工具,AI面试官获取个性化信息的能力得到了显著加强:
- 更精准的能力评估: 基于用户的实际刷题数据,准确判断其编程水平。
- 更深入的意向洞察: 通过分析用户发帖,了解其技术偏好和求职目标。
- 更相关的知识获取: 结合社区中关于目标公司的热门讨论,使面试题目更贴近实际。
这些工具的组合使AI面试官能够构建一个更全面的用户画像,从而设计出既能考察核心能力,又能体现候选人特点和意向的面试流程。