1
This commit is contained in:
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user