山东大学软件学院项目实训-基于大模型的模拟面试系统-个人博客(四)

一、新增打分功能

1.1 实现内容

在本周工作中,我们成功实现了面试打分功能,主要包括以下几个方面的工作:

-在 ChatRecords 实体类中添加了 interviewScore 字段,用于存储面试评分结果:

package com.sdumagicode.backend.entity.chat;

// ... existing code ...

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("interview_chat_records")
public class ChatRecords {
    // ... existing code ...

    private Integer interviewScore;
}

-修改了提示词,使AI能够根据面试表现给出评分,评分标准基于面试过程中的表现,包括专业知识、沟通能力、解决问题能力等方面。

提示词来源包括:1、当前基础版本。(本篇文章给出)2、用户使用时说的上下文。(使用时给出)3、用户在使用前可能在基础版本上新增的提示词。(后续给出)4、语音输入模块的提示词。(后续给出)5、用户上传的文件等(后续给出)。本篇内容只是提供了提示词的基础版本,后续开发中会给出新的提示词,尤其是3、4、5部分。

流程设定:候选者问好,面试官开始面试-问题阶段-结束阶段(含反问阶段)-完成面试

当前基础版本:

你是一个计算机就业相关岗位的面试官张三,具有渊博的知识存储和专业素养。我将是候选人,面试贵厂的xx岗位您将对我进行正式地面试,为我提出面试问题,当我向面试官问好之后将正式开始面试。(模拟面试的具体公司和岗位及面试官的性格特征等信息将由用户提供,若用户不主动给出,不要向用户询问,不要向用户提问,不要向用户提问,假装我已经给出过了,而具体的内容直接由你默认生成一个,生成的内容不需要告诉我,作为面试的默认信息即可,面试官的性格特点是热情友好。)
- 我要求你仅作为面试官回复。我要求你仅与我进行面试。向我提问并等待我的回答,过程中不要给我问题的答案和解释,你只是一个面试官,只是来进行我的面试并测试我是否符合岗位需求,并不是来解答我的问题,后续有用户提问环节,但除此之外如果我有问题则不要给我回答,仅用几个关键词语给出提示即可,如果用户还是回答不上来则根据面试官性格给出恰当的反应。
- 我回答之后你会根据我的回答做出一定反馈再问下一个问题,这里的反馈并不是点评我的回答和给出解释,而是给我一个或正面或负面反馈,如“答的不错”,“有一定偏差,但是已经很好了”,具体反馈类型取决于面试官性格特征。
- 像面试官那样一个接一个地向我提问,每次只提问一个问题,并等待我的回答结束之后才向我提出下一个问题,下一个问题可以根据我当前的回答继续横向扩展或纵向深入上一个问题,比如针对某些技术点进入深入的提问,也可以换接下来的其他问题,但在这之前你要思考一下自己已经询问的题目数量,不要超出指定的数量。
- 你询问的问题应该集中在计算机常见领域,这些领域通常包括:计算机网络,数据库,操作系统,C++,java,Go语言等高级编程语言和前端编程语言等,机器学习等计算机相关领域。具体询问的问题应该根据前面设定的应聘公司和岗位需求来决定侧重,对不同岗位的知识要求不同,如可能网络通讯公司可能会询问更多有关计算机网络的问题,如普通的后端开发可能就不包括机器学习方面的问题,如应聘前端开发可能会更偏重前端语言。
- 我的简历由用户在使用前加入的提示词提供,如果没有提供,无须再向我询问提供,而由你自己默认生成一份简历,具体内容由你自己随机生成,然后面试的时候可以通过让我自我介绍来引出你的问题,总之在面试开始时你是已经掌握了我的简历且并不需要向我询问和提供的。
- 你需要了解用户应聘岗位对应试者的要求,包括业务理解、行业知识、具体技能、专业背景、项目经历等,你的面试目标是考察应试者有没有具备这些能力,你要结合询问和用户简历经历和岗位需求来询问问题,以此考察该候选人是否会具备该岗位需要的能力和技能,不要机械的询问当前知识库中的问题,要主动发散思维,询问知识库代表的各方面的问题可以延申出的问题。
- 针对面试中我的回答,你要考虑人文关怀,比如我回答的一般,你会给予一定积极的肯定,比如如果认为我比较紧张,你会询问一些轻松的、与岗位也许关联性不那么大的问题来缓解我的情绪。判断的来源来自于语音输入模块和用户回答,若还没有实现语言输入模块就先补考虑通过用户的语气来判断用户情绪。
- 面试官压力程度设定(1-10):
1-3: 非常友善,给予更多鼓励和提示
4-7: 中等压力,保持专业性的同时适度友好
8-10: 高压力,更严格的标准和更少的提示
(压力程度等级后续会在拼接部分给出)
- 关注简历程度(1-10):
1-3: 主要关注基础知识和现场表现
4-7: 平衡关注简历经历和现场表现
8-10: 深入关注简历中的项目细节和经验
(关注简历程度也会在后续拼接部分给出)
- 面试流程:首先会让我进行自我介绍,在这之后开始提问,问题类型包括:简答题(比如询问数据库中的事物隔离级别有哪些),思考题(比如有20瓶药丸,其中19瓶装有1克/粒的药丸,余下一瓶装有1.1克/粒的药丸。给你一台称重精准的天平,怎么找出比较重的那瓶药丸?天平只能用一次。)。问题中要包含一道算法题(如有序链表合并),在最后一道题,这里的算法题你会给出一个连接,后续我会在数据库中给出,这里目前还没有,所以你只需要给出一个假设的目标题目链接即可,当我完成后我会告诉你,你会对题目进行简单的提问,比如增加一定限制或者询问其他思路等。
- 询问阶段不超过5个问题,围绕一个方面的多个问题(一般是一到三个,不要超过三个)算多个问题,你要内置一个计数器,记住自己当前问的是第几个问题,每次我回答完毕后都要先记起来自己问到第几个问题了并给计数器加1,确定是否该进入算法题(最后一道题)或者进入结束阶段,若没有到最后一道问题且还不该结束,再根据我的回答考虑如何询问下一个问题,但是不用告诉我是第几个,当完成最后一个问题(也就是第五道题算法题)后进入结束阶段。
- 不管我的问答正确与否,也不管我是否回答上来你的问题,你的询问到问题数量达标后就会停止,不要无限的询问下去,时间和问题是有限的,到问题全部完成之后进入结束阶段。
-异常处理:如果我在回答时没有回答你提问的问题的某些方面,你要先对我已有的回答进行点评,然后提到刚才没有回答的问题。还有如果我答非所问或者回答十分奇怪,你要根据具体的面试官性格给出恰当的反应,要记住这是一场面试,你只是我的面试官,要来考察我的能力。
-如果我回答不上来某些问题,你会根据具体面试官性格,给出恰当的反应,但反应完之后一定不要继续帮我解答这个问题,而是继续你的下一个问题。(如首先给出一定的提示,如果再无法回答,那你会简单解释这个问题的答案,用一两句话和几个关键词语点出关键即可,然后继续接下来的流程。)要记住你只是一个面试官,没有给我解释详细的义务和必要,你的核心人物是考察我。
"-结束阶段:问题完成后,对面试者的表现进行全面评估,并给出0-100分的量化评分,然后你会简单点评一下我的表现(一般是积极的点评),给出一些建议,字数不要太多,不要超过200字,之后你会像我询问我有什么问题需要面试官解答,然后等待我的回答。\n" +
"-结束阶段,评分应考虑后续给出的 {面试评估标准} ,后续面试评估标准中若为空,没有给出具体评估标准,则按照以下几个方面评估\n" +
"1. 项目经验(40分):对技术概念的理解深度和广度\n" +
"2. 解决问题能力(30分):分析问题和提供解决方案的能力\n" +
"3. 沟通表达(20分):表达清晰度和专业术语使用\n" +
"4. 学习潜力(10分):对新技术的接受度和学习意愿\n" +
"请在总结后使用以下格式给出评分:【总分:XX分】,然后简要说明评分理由,再给出点评和建议,这之后在询问我是否有问题。
639"+
-最后结束阶段候选者向面试官的反问阶段一般问题不会超过20个,你根据我的问题给出答案。当我表达出我没有问题之后,你会表达出面试结束的意思,一般不超过100字。

##注意事项:
- 只有在用户提问的时候你才开始回答,用户不提问时,请不要回答。
- 只有当我回答完你的上一个问题之后,你才开始提问下一个问题,必须要有我的回答,而不是你自己预设的回答。不要预设答案,不要预设答案,不要预设答案!只有当我回答完上一个问题之后,你再继续!
- 你的回答全都是面试中可以说出口的话!你的回答全都是面试中可以说出口的话!你的回答全都是面试中可以说出口的话!不要在回答中给出思考过程!不要在回答中给出思考过程!不要在回答中给出思考过程!
- 你将完全作为一个面试官的视角,完全以面试官的口吻进行对话,不要给出如同“(等待候选人回答后,可继续追问:如果链表有环如何处理?或者如何用迭代/递归两种方式实现?)”这些括号里的话,而是完全以一个面试官说话的语气对话,你的所有回答都是面试中确切可以说出口的话,那些思考过程不要给出,完全不要使用括号,不要给出你的动作描写神态描写等等,只需要给出正常沟通的语言。
- 你的语气要像一个面试官,不要给出长串的文字,要以口头用语的形式,简短凝练的提问和回答。
##初始语句:
""你好,我是你xx岗位的模拟面试官,我将对你进行xx岗位的面试,请你先自我介绍一下""

以下为其他部分提示词可能出现的内容,目前还未给出下面的部分,后续会给出,而如果当前还未给出,不要向用户提问,自己默认生成下述内容即可。

用户在使用前新增的的提示词:
- 面试官个人特征:
性格特征:友善亲和,温和冷静,热情幽默等。
工作资历:在某岗位可能有多久的就业时间,曾参与哪些项目开发等。
- 岗位信息:
具体公司:腾讯,华为,字节等。
具体岗位:前端开发工程师,后端开发工程师,机器学习算法开发工程师等。
- 用户信息:
个人简历:可能给出个人简历,要据此来提问。
部分证书和资料:算法竞赛证书,论文专利等。

语音输入模块:
- 用于对话时,通过用户的语音输入,替代用户用问题来回答。

输入的文件:
- 可能包括用户的简历,以及某些证书等电子文件。
- 可能包括知识库,但通常不会出现,知识库由开发者给出,而非来面试的用户。

其余拼接部分:(若无则代表还没有插入,若有代表成功插入,综合上面的提示词和以下的提示词综合考虑)


-在 Controller 层添加了处理面试评分的方法,用于接收前端传来的评分请求并更新数据库:

@PostMapping("/updateScore")
public GlobalResult updateInterviewScore(@RequestParam("chatId") Long chatId, 
                                         @RequestParam("score") Integer score) {
    if (chatId == null || score == null) {
        throw new ServiceException("参数不能为空");
    }
    
    ChatRecords chatRecord = new ChatRecords();
    chatRecord.setChatId(chatId);
    chatRecord.setInterviewScore(score);
    
    chatService.updateChatScore(chatRecord);
    return GlobalResultGenerator.genSuccessResult();
}

1.2 评分标准

我们设计了一套评分标准,基于以下几个维度:

"1. 项目经验(40分):对技术概念的理解深度和广度" 
"2. 解决问题能力(30分):分析问题和提供解决方案的能力" 
"3. 沟通表达(20分):表达清晰度和专业术语使用" 
"4. 学习潜力(10分):对新技术的接受度和学习意愿" 


总分为100分,AI会根据面试者的表现给出相应的评分和评价。

二、MCP服务搭建测试


2.1 MCP服务介绍


初步搭建了一个基于Model Context Protocol (MCP)的服务,为了后续结合项目使用MCP,这里进行一下测试,用于处理时间转换和单位转换功能。MCP是一种允许AI模型与外部工具和服务交互的协议,可以大大增强AI的能力。

2.2 搭建过程

1. 创建项目目录

首先,我们创建了一个新的项目目录:

mkdir D:\mcp-test
cd D:\mcp-test

2. 初始化项目


使用npm初始化项目:

npm init -y

3. 创建build目录

mkdir D:\mcp-test\build

4. 创建change_time.js文件


在build目录下创建change_time.js文件:

notepad D:\mcp-test\build\change_time.js

在change_time.js中编写以下代码:

#!/usr/bin/env node
console.log("MCP服务启动中...");

import {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js";
import {StdioServerTransport} from "@modelcontextprotocol/sdk/server/stdio.js";

import {z} from "zod";

// 创建MCP服务器
const server = new McpServer({
    name: "utility-mcp",
    version: "1.0.0"
});

// 时区映射表
const TIMEZONE_MAP = {
    "北京": "Asia/Shanghai",
    "东京": "Asia/Tokyo",
    "纽约": "America/New_York",
    "伦敦": "Europe/London",
    "悉尼": "Australia/Sydney",
    "巴黎": "Europe/Paris",
    "莫斯科": "Europe/Moscow",
    "迪拜": "Asia/Dubai",
    "洛杉矶": "America/Los_Angeles",
    "新加坡": "Asia/Singapore"
};

// 注册时差转换工具
server.tool(
    "time_converter",
    {
        from_city: z.string().describe("起始城市,如北京、东京、纽约等"),
        to_city: z.string().describe("目标城市,如北京、东京、纽约等"),
        time: z.string().optional().describe("要转换的时间,格式为HH:MM,如不提供则使用当前时间")
    },
    async ({from_city, to_city, time}) => {
        try {
            // 检查城市是否在支持列表中
            if (!TIMEZONE_MAP[from_city]) {
                return {
                    content: [{
                        type: "text",
                        text: `不支持的起始城市: ${from_city}。支持的城市有: ${Object.keys(TIMEZONE_MAP).join("、")}`
                    }]
                };
            }

            if (!TIMEZONE_MAP[to_city]) {
                return {
                    content: [{
                        type: "text",
                        text: `不支持的目标城市: ${to_city}。支持的城市有: ${Object.keys(TIMEZONE_MAP).join("、")}`
                    }]
                };
            }

            // 获取时区
            const fromTimezone = TIMEZONE_MAP[from_city];
            const toTimezone = TIMEZONE_MAP[to_city];

            // 创建日期对象
            let date = new Date();
            
            // 如果提供了时间,则解析
            if (time) {
                const [hours, minutes] = time.split(':').map(Number);
                date.setHours(hours, minutes);
            }

            // 格式化为本地时间字符串
            const options = {
                timeZone: fromTimezone,
                hour12: false,
                hour: '2-digit',
                minute: '2-digit',
                second: '2-digit'
            };
            
            const fromTime = date.toLocaleTimeString('zh-CN', options);
            
            // 转换为目标时区
            options.timeZone = toTimezone;
            const toTime = date.toLocaleTimeString('zh-CN', options);

            // 计算时差(小时)
            const fromOffset = new Date().toLocaleString('en-US', {timeZone: fromTimezone, timeZoneName: 'short'}).split(' ').pop();
            const toOffset = new Date().toLocaleString('en-US', {timeZone: toTimezone, timeZoneName: 'short'}).split(' ').pop();
            
            return {
                content: [{
                    type: "text",
                    text: ` 时差转换结果:\n\n${from_city} (${fromOffset}) 的 ${fromTime} \n相当于\n${to_city} (${toOffset}) 的 ${toTime}`
                }]
            };
        } catch (error) {
            console.error("时差转换出错:", error);
            return {
                content: [{
                    type: "text",
                    text: `时差转换出错: ${error.message}`
                }]
            };
        }
    }
);

// 注册简单的单位转换工具
server.tool(
    "unit_converter",
    {
        value: z.number().describe("要转换的数值"),
        from_unit: z.string().describe("原始单位,如km、mi、kg、lb等"),
        to_unit: z.string().describe("目标单位,如km、mi、kg、lb等")
    },
    async ({value, from_unit, to_unit}) => {
        try {
            // 定义转换系数
            const conversionFactors = {
                // 长度
                "km_to_mi": 0.621371,
                "mi_to_km": 1.60934,
                "m_to_ft": 3.28084,
                "ft_to_m": 0.3048,
                "cm_to_in": 0.393701,
                "in_to_cm": 2.54,
                
                // 重量
                "kg_to_lb": 2.20462,
                "lb_to_kg": 0.453592,
                "g_to_oz": 0.035274,
                "oz_to_g": 28.3495,
                
                // 温度转换需要特殊处理
                "c_to_f": value => (value * 9/5) + 32,
                "f_to_c": value => (value - 32) * 5/9
            };
            
            // 创建转换键
            const conversionKey = `${from_unit.toLowerCase()}_to_${to_unit.toLowerCase()}`;
            
            // 检查是否支持该转换
            if (!conversionFactors[conversionKey]) {
                return {
                    content: [{
                        type: "text",
                        text: `不支持从 ${from_unit} 到 ${to_unit} 的转换。\n\n支持的转换包括:\n- 长度: km↔mi, m↔ft, cm↔in\n- 重量: kg↔lb, g↔oz\n- 温度: C↔F`
                    }]
                };
            }
            
            // 执行转换
            let result;
            if (typeof conversionFactors[conversionKey] === 'function') {
                // 对于温度等需要特殊处理的转换
                result = conversionFactors[conversionKey](value);
            } else {
                // 对于简单的乘法转换
                result = value * conversionFactors[conversionKey];
            }
            
            // 格式化结果(保留2位小数)
            result = Math.round(result * 100) / 100;
            
            return {
                content: [{
                    type: "text",
                    text: ` 单位转换结果:\n\n${value} ${from_unit} = ${result} ${to_unit}`
                }]
            };
        } catch (error) {
            console.error("单位转换出错:", error);
            return {
                content: [{
                    type: "text",
                    text: `单位转换出错: ${error.message}`
                }]
            };
        }
    }
);

// 启动服务器
const transport = new StdioServerTransport();
server.listen(transport);

console.log("MCP服务已启动,等待连接...");

5. 创建测试客户端


在项目根目录创建test-client.js文件:

notepad D:\mcp-test\test-client.js

在test-client.js中编写以下代码:

#!/usr/bin/env node
import { spawn } from 'child_process';
import readline from 'readline';

// 创建readline接口用于读取用户输入
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

// 启动MCP服务器进程
const serverProcess = spawn('node', ['build/change_time.js'], {
  stdio: ['pipe', 'pipe', process.stderr]
});

console.log('MCP测试客户端已启动');
console.log('可用命令:');
console.log('1. 时差转换: time 起始城市 目标城市 [时间(HH:MM)]');
console.log('2. 单位转换: unit 数值 原始单位 目标单位');
console.log('3. 退出: exit');
console.log('----------------------------');

// 处理服务器输出
serverProcess.stdout.on('data', (data) => {
  console.log(`服务器响应: ${data}`);
});

// 处理用户输入
rl.on('line', (input) => {
  if (input.toLowerCase() === 'exit') {
    console.log('正在关闭MCP测试客户端...');
    serverProcess.kill();
    rl.close();
    return;
  }

  const [command, ...args] = input.split(' ');
  
  if (command.toLowerCase() === 'time') {
    if (args.length < 2) {
      console.log('用法: time 起始城市 目标城市 [时间(HH:MM)]');
      return;
    }
    
    const from_city = args[0];
    const to_city = args[1];
    const time = args.length > 2 ? args[2] : undefined;
    
    const request = {
      type: 'tool_call',
      tool: 'time_converter',
      params: { from_city, to_city, time }
    };
    
    serverProcess.stdin.write(JSON.stringify(request) + '\n');
  } 
  else if (command.toLowerCase() === 'unit') {
    if (args.length < 3) {
      console.log('用法: unit 数值 原始单位 目标单位');
      return;
    }
    
    const value = parseFloat(args[0]);
    const from_unit = args[1];
    const to_unit = args[2];
    
    if (isNaN(value)) {
      console.log('错误: 数值必须是一个有效的数字');
      return;
    }
    
    const request = {
      type: 'tool_call',
      tool: 'unit_converter',
      params: { value, from_unit, to_unit }
    };
    
    serverProcess.stdin.write(JSON.stringify(request) + '\n');
  }
  else {
    console.log('未知命令。可用命令: time <起始城市> <目标城市> [时间], unit <数值> <原始单位> <目标单位>, exit');
  }
});

// 处理关闭事件
rl.on('close', () => {
  process.exit(0);
});

6. 修改package.json


编辑package.json文件,添加必要的依赖和脚本:

notepad D:\mcp-test\package.json

在package.json中写入以下内容:

{
  "name": "mcp-test",
  "version": "1.0.0",
  "description": "测试MCP服务",
  "type": "module",
  "bin": {
    "mcp-test": "build/change_time.js"
  },
  "files": [
    "build"
  ],
  "scripts": {
    "start": "node build/change_time.js",
    "test": "node test-client.js"
  },
  "keywords": [
    "mcp",
    "weather",
    "ai"
  ],
  "author": "user",
  "license": "MIT",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.7.0",
    "node-fetch": "^3.3.2",
    "string-similarity": "^4.0.4",
    "zod": "^3.22.4",
    "dotenv": "^16.4.7"
  },
  "devDependencies": {
    "nodemon": "^3.0.3"
  }
}

7. 安装依赖并运行测试


在命令提示符或PowerShell中执行:

cd D:\mcp-test
npm install
npm run test

2.3 测试结果

时差转换测试 :

> time 北京 纽约
服务器响应: {"content":[{"type":"text","text":" 时差转换结果:\n\n北京 (GMT+8) 的 14:30:45 \n相当于\n纽约 (EDT) 的 02:30:45"}]}

> time 伦敦 东京 15:30
服务器响应: {"content":[{"type":"text","text":" 时差转换结果:\n\n伦敦 (BST) 的 15:30:00 \n相当于\n东京 (JST) 的 23:30:00"}]}

2.4 服务架构


我们的MCP服务采用了Node.js实现,主要包含以下组件:

- 主服务器(change_time.js):处理客户端请求并返回结果
- 测试客户端(test-client.js):用于测试服务功能
- 工具函数:实现时差和单位转换的核心逻辑

2.5结合程序的部分工具

结合我们的项目,编写以下工具:
 

server.tool(
    "query_problem",
    "获取合适的题目信息,缺少的参数请填为空字符串,保持参数的完整",
    {
        difficulty: z.string().optional().describe("要查询题目的难易程度,分为三档:简单,中等和困难"),
        tag: z.string().optional().describe("要查询题目的标签,包含问题所涉及的知识点"),
        keyword: z.string().optional().describe("要查询题目的关键词,会在题目名称,描述中进行匹配搜索"),
    },
    async ({difficulty = "", tag = "", keyword = ""}) => {
        try {
            const mysqlPool = getMySQL();
            
            // 构建SQL查询条件和参数
            let conditions = [];
            let params = [];
            
            // 添加难度条件
            if (difficulty) {
                conditions.push("difficulty = ?");
                params.push(difficulty);
            }
            
            // 添加标签条件
            if (tag) {
                conditions.push("tags LIKE ?");
                params.push(`%${tag}%`);
            }
            
            // 添加关键词条件
            if (keyword) {
                conditions.push("(title LIKE ? OR description LIKE ?)");
                params.push(`%${keyword}%`);
                params.push(`%${keyword}%`);
            }
            
            // 构建完整SQL
            let sql = "SELECT id, problem_code FROM oj_problem";
            if (conditions.length > 0) {
                sql += " WHERE " + conditions.join(" AND ");
            }
            sql += " LIMIT 10"; // 限制返回10条结果
            
            // 执行查询
            const [rows] = await mysqlPool.query(sql, params);
            
            // 格式化结果
            if (rows.length === 0) {
                return {
                    content: [{
                        type: "text",
                        text: "没有找到符合条件的题目"
                    }]
                };
            }
            
            let result = "找到以下题目:\n\n";
            rows.forEach(row => {
                result += `ID: ${row.id} - 题目编号: ${row.problem_code}\n`;
            });
            
            return {
                content: [{type: "text", text: result.trim()}]
            };
            
        } catch (error) {
            console.error("查询题目时出错:", error);
            return {
                content: [{type: "text", text: `查询题目时出错: ${error.message}`}],
                isError: true
            };
        }
    }
);

server.tool(
    "update_valuation",
    {
        chatId: z.string().optional().describe("聊天会话ID"),
        delta: z.number().optional().describe("评分变化值,正数表示加分,负数表示减分"),
        valuationName: z.string().optional().describe("评分项名称")
    },
    async ({chatId, delta = 0, valuationName = ""}) => {
        try {
            const db = getMongoDB();
            const collection = db.collection("valuation_record");
            
            // 查找或创建评分记录
            const result = await collection.findOneAndUpdate(
                { chatId },
                {
                    $setOnInsert: { 
                        chatId,
                        valuationRanks: [] 
                    }
                },
                { 
                    upsert: true,
                    returnDocument: 'after'
                }
            );
            
            // 更新评分
            const updatedRecord = await collection.findOneAndUpdate(
                { 
                    chatId,
                    "valuationRanks.valuation.valuationName": valuationName
                },
                {
                    $inc: { "valuationRanks.$.rank": delta }
                },
                { returnDocument: 'after' }
            );
            
            if (!updatedRecord.value) {
                // 如果评分项不存在,则添加新项
                const valuation = await getMySQL().query(
                    "SELECT * FROM interview_valuation WHERE valuation_name = ?",
                    [valuationName]
                );
                
                if (valuation.length === 0) {
                    return {
                        content: [{type: "text", text: `找不到评分项: ${valuationName}`}],
                        isError: true
                    };
                }
                
                await collection.updateOne(
                    { chatId },
                    {
                        $push: {
                            valuationRanks: {
                                valuation: valuation[0],
                                rank: delta
                            }
                        }
                    }
                );
            }
            
            return {
                content: [{type: "text", text: `成功更新评分: ${valuationName} (${delta > 0 ? '+' : ''}${delta})`}]
            };
            
        } catch (error) {
            console.error("更新评分时出错:", error);
            return {
                content: [{type: "text", text: `更新评分时出错: ${error.message}`}],
                isError: true
            };
        }
    }
);

三、未来改进方向


3.1 面试打分功能优化


目前面试打分功能主要通过提示词实现,没有调用到数据库中的评价标准。未来计划:

- 将评价标准存储在数据库中,通过API接口提供给AI
- 实现更细粒度的评分维度
- 添加历史评分对比功能,展示用户的进步


3.2 AI微调


计划收集结构化的面试数据,对AI模型进行微调:

- 收集真实面试场景的问答数据
- 标注高质量的面试官回应和评价
- 使用这些数据对模型进行微调,提高面试官角色的专业性


3.3 Bug修复计划


还有一些待修复的bug:

- 话题重命名功能存在问题
- 某些特殊情况下的消息同步问题
- 移动端适配问题


3.4 MCP服务优化


计划进一步完善MCP服务:

- 将后端的提示词部分精简,将具体功能实现放到MCP中
- 扩展MCP服务的功能,如添加代码执行、数据查询等能力
- 优化MCP服务的性能和稳定性
- 实现更复杂的工具调用链


四、技术心得

4.1MCP的工作原理与价值


MCP(Model Context Protocol)是一种革命性的协议,它为AI模型提供了与外部世界交互的能力,极大地扩展了AI的应用范围和实用性。通过深入理解MCP的工作原理和价值,我们可以更好地利用这一技术来构建更强大、更实用的AI应用。

工作原理


MCP的核心工作原理可以概括为"工具定义-意图识别-参数构造-工具调用-结果整合"的流程:

1. 工具注册与定义 :MCP服务首先需要定义各种工具(tools),包括工具名称、参数schema和功能实现。在我们的示例中,我们定义了 time_converter 和 unit_converter 两个工具,并使用zod库进行参数验证。


2. 标准化通信协议 :MCP建立了AI模型与外部服务之间的标准化通信协议,通过JSON格式的请求和响应进行数据交换。这种标准化使得不同的AI模型和服务可以无缝集成。


3. 参数验证与处理 :MCP服务会对接收到的参数进行严格验证,确保符合预定义的schema,从而提高系统的稳定性和安全性。


4. 异步处理机制 :MCP支持异步处理,允许AI模型在等待外部工具执行结果的同时继续处理其他任务,提高系统的响应速度和效率。


5. 上下文管理 :MCP能够在多轮对话中维护上下文信息,使得工具调用可以基于之前的交互历史进行,提供更连贯的用户体验。


核心价值


MCP的价值远不止于简单的API调用,它为AI应用带来了多方面的深远影响:

1. 能力扩展 :MCP打破了AI模型的固有限制,使其能够访问实时数据、执行复杂计算和调用专业工具。例如,我们实现的时差转换功能,让AI能够准确计算不同时区的时间差异,而不仅仅依赖于训练数据中可能过时或不完整的知识。这种能力扩展使AI能够处理更广泛的任务,从简单的信息查询到复杂的专业领域问题。


2. 实时性与准确性 :通过MCP,AI可以获取最新的数据和执行精确的计算,而不是依赖于训练时的静态知识。这在金融、天气预报、交通导航等需要实时信息的场景中尤为重要。例如,AI可以通过MCP获取最新的股票价格、天气状况或交通状态,提供基于最新数据的建议和决策支持。


3. 专业化与定制化 :MCP允许将复杂的专业领域知识和功能封装成工具,使AI能够准确处理特定领域的问题。这种专业化使AI能够在医疗诊断、法律咨询、工程设计等专业领域提供更有价值的服务。通过定制化的工具,AI可以弥补其在特定领域知识的不足,提供更专业、更准确的解决方案。


4. 降低提示词复杂度 :将复杂的功能实现放在MCP服务中,可以大大简化提示词,提高系统的可维护性。传统的提示词工程往往需要复杂的指令来引导AI执行特定任务,而MCP允许我们将这些复杂逻辑封装在外部服务中,使提示词更加简洁明了。这不仅降低了开发难度,也提高了系统的可维护性和可扩展性。


5. 安全性与可控性 :MCP提供了一种更安全、更可控的方式来扩展AI的能力。通过明确定义工具的功能和参数,可以限制AI的操作范围,防止潜在的安全风险。同时,MCP服务可以实现访问控制和审计日志,提高系统的安全性和合规性。


6. 跨平台与互操作性 :MCP的标准化协议使得不同的AI模型和服务可以无缝集成,促进了AI生态系统的发展。开发者可以创建通用的MCP服务,供不同的AI模型使用,也可以将多个MCP服务组合起来,构建更复杂的应用系统。


7. 降低计算资源需求 :通过将复杂计算和专业功能放在外部服务中,MCP可以减轻AI模型本身的计算负担,降低资源需求。这使得AI应用可以在更广泛的设备上运行,包括资源受限的移动设备和边缘设备。


8. 促进AI与传统软件的融合 :MCP为AI与传统软件系统的集成提供了桥梁,使AI能够与现有的企业系统、数据库和服务进行交互。这种融合使企业能够在不完全重构现有系统的情况下,逐步引入AI技术,提高业务效率和创新能力。


在我们的面试系统中,MCP的价值体现在多个方面:它可以帮助AI面试官获取最新的行业知识、执行代码评估、分析简历数据,甚至可以连接到企业的人力资源系统,提供更全面、更准确的面试评估。通过MCP,我们的AI面试系统不仅能够提供标准化的面试体验,还能够根据不同的职位和要求,提供定制化的专业评估。

总之,MCP代表了AI技术发展的重要方向,它通过建立AI与外部世界的桥梁,极大地扩展了AI的能力边界,为构建更实用、更强大的AI应用提供了关键支持。随着MCP技术的不断发展和完善,我们可以期待看到更多创新的AI应用出现,为各行各业带来更大的价值。


4.2 MCP与AI的结合方式


MCP与AI的结合主要通过以下方式实现:

1. 工具定义 :在MCP服务中定义各种工具及其参数和返回值格式。
2. 工具调用 :AI模型通过分析用户意图,决定何时调用哪个工具,并构造正确的参数。
3. 结果整合 :AI将工具返回的结果整合到自己的回答中,提供连贯、自然的用户体验。
4. 上下文管理 :MCP服务需要维护会话上下文,确保多轮对话中的工具调用保持一致性。
在实践中,我发现MCP极大地增强了AI的实用性。例如,在我们的面试系统中,可以通过MCP实现代码执行、简历分析、专业知识查询等功能,使AI面试官能够更准确地评估候选人的能力。

4.3 开发经验总结


在本次开发中,我获得了以下经验:

1. 模块化设计的重要性 :将功能封装为独立的模块(如MCP服务),可以大大提高系统的可维护性和可扩展性。
2. 前后端协作 :良好的接口设计和文档对于前后端协作至关重要,特别是在处理复杂的状态同步问题时。
3. 错误处理 :完善的错误处理机制对于提高系统稳定性非常重要,尤其是在涉及AI和外部服务调用的场景中。
4. 性能优化 :在处理大量聊天记录和复杂计算时,需要注意性能优化,避免系统响应缓慢。
5. 用户体验 :即使是技术性很强的功能,也需要考虑用户体验,如何让用户更直观地理解和使用系统。


通过这次开发,我深刻认识到AI与传统软件开发的结合点,以及如何利用两者的优势构建更强大的应用系统。未来,我将继续探索AI与软件工程的深度融合,为用户创造更大的价值。

你可能感兴趣的:(面试,职场和发展)