This commit is contained in:
张成
2025-12-27 20:14:40 +08:00
parent 43382668a3
commit 43f7884e52
14 changed files with 1818 additions and 21 deletions

View File

@@ -4,6 +4,7 @@
*/
const axios = require('axios');
const AiCallRecorder = require('./ai_call_recorder.js');
class AIService {
constructor(config = {}) {
@@ -30,6 +31,9 @@ class AIService {
* @returns {Promise<String>} AI响应内容
*/
async chat(messages, options = {}) {
const startTime = Date.now();
const requestContent = JSON.stringify(messages);
try {
const response = await this.client.post('/v1/chat/completions', {
model: this.model,
@@ -39,19 +43,90 @@ class AIService {
...options
});
return response.data.choices[0].message.content;
const responseTime = Date.now() - startTime;
const responseContent = response.data.choices[0].message.content;
const usage = response.data.usage || {};
// 记录AI调用异步不阻塞主流程
this.recordAiCall({
user_id: options.user_id,
sn_code: options.sn_code,
service_type: options.service_type || 'chat',
model_name: this.model,
prompt_tokens: usage.prompt_tokens || 0,
completion_tokens: usage.completion_tokens || 0,
total_tokens: usage.total_tokens || 0,
request_content: requestContent,
response_content: responseContent,
cost_amount: this.calculateCost(usage.total_tokens || 0),
status: 'success',
response_time: responseTime,
api_provider: 'deepseek',
business_type: options.business_type,
reference_id: options.reference_id
}).catch(err => {
console.warn('记录AI调用失败不影响主流程:', err.message);
});
return responseContent;
} catch (error) {
const responseTime = Date.now() - startTime;
// 记录失败的调用
this.recordAiCall({
user_id: options.user_id,
sn_code: options.sn_code,
service_type: options.service_type || 'chat',
model_name: this.model,
request_content: requestContent,
status: 'failed',
error_message: error.message,
response_time: responseTime,
api_provider: 'deepseek',
business_type: options.business_type,
reference_id: options.reference_id
}).catch(err => {
console.warn('记录失败调用失败:', err.message);
});
console.warn('AI服务调用失败:', error.message);
throw new Error(`AI服务调用失败: ${error.message}`);
}
}
/**
* 记录AI调用
* @param {Object} params - 调用参数
* @returns {Promise}
*/
async recordAiCall(params) {
try {
await AiCallRecorder.record(params);
} catch (error) {
// 记录失败不应影响主流程
console.warn('AI调用记录失败:', error.message);
}
}
/**
* 计算调用费用
* @param {Number} totalTokens - 总Token数
* @returns {Number} 费用(元)
*/
calculateCost(totalTokens) {
// DeepSeek 价格(元/1000 tokens
// 可以根据实际API定价调整
const pricePerThousand = 0.001; // 示例价格
return (totalTokens / 1000) * pricePerThousand;
}
/**
* 分析简历竞争力
* @param {Object} resumeData - 简历数据
* @param {Object} context - 上下文信息user_id, sn_code等
* @returns {Promise<Object>} 分析结果
*/
async analyzeResume(resumeData) {
async analyzeResume(resumeData, context = {}) {
const prompt = `请分析以下简历的竞争力,并提供详细评估:
简历信息:
@@ -91,7 +166,14 @@ class AIService {
];
try {
const response = await this.chat(messages, { temperature: 0.3 });
const response = await this.chat(messages, {
temperature: 0.3,
user_id: context.user_id,
sn_code: context.sn_code,
service_type: 'completion',
business_type: 'resume_analysis',
reference_id: resumeData.id || resumeData.resumeId
});
// 提取JSON部分
const jsonMatch = response.match(/\{[\s\S]*\}/);
if (jsonMatch) {
@@ -120,9 +202,10 @@ class AIService {
* 岗位匹配度评估
* @param {Object} jobData - 岗位数据
* @param {Object} resumeData - 简历数据
* @param {Object} context - 上下文信息user_id, sn_code等
* @returns {Promise<Object>} 匹配结果
*/
async matchJobWithResume(jobData, resumeData) {
async matchJobWithResume(jobData, resumeData, context = {}) {
const prompt = `请评估以下岗位与简历的匹配度:
【岗位信息】
@@ -169,7 +252,14 @@ class AIService {
];
try {
const response = await this.chat(messages, { temperature: 0.3 });
const response = await this.chat(messages, {
temperature: 0.3,
user_id: context.user_id,
sn_code: context.sn_code,
service_type: 'completion',
business_type: 'job_matching',
reference_id: jobData.id || jobData.jobId
});
const jsonMatch = response.match(/\{[\s\S]*\}/);
if (jsonMatch) {
return JSON.parse(jsonMatch[0]);
@@ -197,9 +287,10 @@ class AIService {
* 批量评估岗位(用于智能筛选)
* @param {Array} jobs - 岗位列表
* @param {Object} resumeData - 简历数据
* @param {Object} context - 上下文信息user_id, sn_code等
* @returns {Promise<Array>} 评估结果列表
*/
async batchMatchJobs(jobs, resumeData) {
async batchMatchJobs(jobs, resumeData, context = {}) {
const results = [];
// 限制并发数量避免API限流
@@ -207,7 +298,7 @@ class AIService {
for (let i = 0; i < jobs.length; i += concurrency) {
const batch = jobs.slice(i, i + concurrency);
const batchPromises = batch.map(job =>
this.matchJobWithResume(job, resumeData).catch(err => {
this.matchJobWithResume(job, resumeData, context).catch(err => {
console.warn(`岗位${job.jobId}匹配失败:`, err.message);
return {
jobId: job.jobId,
@@ -231,11 +322,11 @@ class AIService {
/**
* 生成聊天内容
* @param {Object} context - 聊天上下文
* @param {Object} context - 聊天上下文包含jobInfo, resumeInfo, chatType, user_id, sn_code等
* @returns {Promise<String>} 生成的聊天内容
*/
async generateChatContent(context) {
const { jobInfo, resumeInfo, chatType = 'greeting', previousMessages = [] } = context;
const { jobInfo, resumeInfo, chatType = 'greeting', previousMessages = [], user_id, sn_code } = context;
let prompt = '';
@@ -279,7 +370,15 @@ class AIService {
];
try {
const response = await this.chat(messages, { temperature: 0.8, max_tokens: 200 });
const response = await this.chat(messages, {
temperature: 0.8,
max_tokens: 200,
user_id,
sn_code,
service_type: 'chat',
business_type: 'chat_generation',
reference_id: jobInfo?.jobId || jobInfo?.id
});
return response.trim();
} catch (error) {
console.warn('生成聊天内容失败:', error);
@@ -300,9 +399,10 @@ class AIService {
/**
* 判断是否为面试邀约
* @param {String} message - HR消息内容
* @param {Object} context - 上下文信息user_id, sn_code等
* @returns {Promise<Object>} 判断结果
*/
async detectInterviewInvitation(message) {
async detectInterviewInvitation(message, context = {}) {
const prompt = `判断以下HR消息是否为面试邀约并提取关键信息
消息内容: "${message}"
@@ -323,7 +423,14 @@ class AIService {
];
try {
const response = await this.chat(messages, { temperature: 0.1 });
const response = await this.chat(messages, {
temperature: 0.1,
user_id: context.user_id,
sn_code: context.sn_code,
service_type: 'completion',
business_type: 'interview_detection',
reference_id: context.conversation_id || context.job_id
});
const jsonMatch = response.match(/\{[\s\S]*\}/);
if (jsonMatch) {
return JSON.parse(jsonMatch[0]);
@@ -349,9 +456,10 @@ class AIService {
/**
* 分析HR反馈情感
* @param {String} message - HR消息内容
* @param {Object} context - 上下文信息user_id, sn_code等
* @returns {Promise<Object>} 情感分析结果
*/
async analyzeSentiment(message) {
async analyzeSentiment(message, context = {}) {
const prompt = `分析以下HR消息的情感倾向
消息: "${message}"
@@ -370,7 +478,14 @@ class AIService {
];
try {
const response = await this.chat(messages, { temperature: 0.1 });
const response = await this.chat(messages, {
temperature: 0.1,
user_id: context.user_id,
sn_code: context.sn_code,
service_type: 'completion',
business_type: 'sentiment_analysis',
reference_id: context.conversation_id || context.job_id
});
const jsonMatch = response.match(/\{[\s\S]*\}/);
if (jsonMatch) {
return JSON.parse(jsonMatch[0]);