feat: AIHR 智能人力资源管理系统初始提交
- 员工花名册管理(加密存储、导入导出) - 薪酬管理(发薪批次、薪酬模版、加班费计算、工资条) - 社保公积金(多城市配置、版本管理、基数调整) - 解聘管理(6步流程、证据链、工作交接) - AI 助手(合同审查、风险预测、RAG 知识库) - Dashboard 仪表盘 - 设置与通知
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
import OpenAI from 'openai'
|
||||
import { searchKnowledge } from './rag.service'
|
||||
|
||||
const apiKey = process.env.DASHSCOPE_API_KEY || ''
|
||||
const baseURL = 'https://dashscope.aliyuncs.com/compatible-mode/v1'
|
||||
|
||||
const client = new OpenAI({ apiKey, baseURL, timeout: 30 * 1000, maxRetries: 1 })
|
||||
|
||||
const SYSTEM_PROMPT = `你是一个专业的劳动用工合规顾问,精通中国劳动法、劳动合同法、社会保险法等相关法律法规。
|
||||
|
||||
你的职责:
|
||||
1. 回答用户关于劳动用工的合规问题
|
||||
2. 基于企业实际数据给出针对性建议
|
||||
3. 引用具体法律条文作为依据
|
||||
4. 用通俗易懂的语言解释法律问题
|
||||
|
||||
回答要求:
|
||||
- 先给出直接结论,再展开解释
|
||||
- 引用法律条文时标注具体法律名称和条款号
|
||||
- 涉及金额时给出计算过程
|
||||
- 如有关联的企业数据,在回答中提及
|
||||
- 回答简洁有力,避免冗长`
|
||||
|
||||
export async function chat(messages: { role: 'user' | 'assistant'; content: string }[], orgContext?: string) {
|
||||
const lastUserMsg = messages.filter(m => m.role === 'user').pop()
|
||||
let ragContext = ''
|
||||
if (lastUserMsg) {
|
||||
try {
|
||||
const knowledge = await searchKnowledge(lastUserMsg.content, 3)
|
||||
if (knowledge.length > 0) {
|
||||
ragContext = `\n\n相关法律条文(RAG检索结果):\n${knowledge.join('\n\n')}`
|
||||
}
|
||||
} catch { /* RAG not available, continue without */ }
|
||||
}
|
||||
|
||||
const systemMessage = orgContext
|
||||
? `${SYSTEM_PROMPT}\n\n当前企业数据概览:\n${orgContext}${ragContext}`
|
||||
: `${SYSTEM_PROMPT}${ragContext}`
|
||||
|
||||
const response = await client.chat.completions.create({
|
||||
model: 'qwen-plus',
|
||||
messages: [
|
||||
{ role: 'system', content: systemMessage },
|
||||
...messages,
|
||||
],
|
||||
temperature: 0.7,
|
||||
max_tokens: 2000,
|
||||
})
|
||||
|
||||
return response.choices[0]?.message?.content || ''
|
||||
}
|
||||
|
||||
export async function* chatStream(messages: { role: 'user' | 'assistant'; content: string }[], orgContext?: string) {
|
||||
const lastUserMsg = messages.filter(m => m.role === 'user').pop()
|
||||
let ragContext = ''
|
||||
if (lastUserMsg) {
|
||||
try {
|
||||
const knowledge = await searchKnowledge(lastUserMsg.content, 3)
|
||||
if (knowledge.length > 0) {
|
||||
ragContext = `\n\n相关法律条文(RAG检索结果):\n${knowledge.join('\n\n')}`
|
||||
}
|
||||
} catch { /* RAG not available, continue without */ }
|
||||
}
|
||||
|
||||
const systemMessage = orgContext
|
||||
? `${SYSTEM_PROMPT}\n\n当前企业数据概览:\n${orgContext}${ragContext}`
|
||||
: `${SYSTEM_PROMPT}${ragContext}`
|
||||
|
||||
const stream = await client.chat.completions.create({
|
||||
model: 'qwen-plus',
|
||||
messages: [
|
||||
{ role: 'system', content: systemMessage },
|
||||
...messages,
|
||||
],
|
||||
temperature: 0.7,
|
||||
max_tokens: 2000,
|
||||
stream: true,
|
||||
})
|
||||
|
||||
for await (const chunk of stream) {
|
||||
const delta = chunk.choices[0]?.delta?.content
|
||||
if (delta) yield delta
|
||||
}
|
||||
}
|
||||
|
||||
export async function reviewContract(contractText: string): Promise<{ text: string; structured: { riskItems: { level: string; title: string; description: string; suggestion: string }[]; score: number; summary: string } }> {
|
||||
const prompt = `请审查以下劳动合同文本的合法性,逐条检查并标注风险等级(红/黄/绿),给出修改建议,最后给出合规评分(0-100分)。
|
||||
|
||||
合同文本:
|
||||
${contractText}
|
||||
|
||||
请按以下格式输出:
|
||||
【风险项】
|
||||
🔴/🟡/🟢 [问题标题] - [说明] - [修改建议]
|
||||
|
||||
【合规评分】XX/100
|
||||
|
||||
【总体建议】
|
||||
一段话总结`
|
||||
|
||||
const response = await client.chat.completions.create({
|
||||
model: 'qwen-max',
|
||||
messages: [
|
||||
{ role: 'system', content: '你是劳动法合同审查专家,精通劳动合同法。' },
|
||||
{ role: 'user', content: prompt },
|
||||
],
|
||||
temperature: 0.3,
|
||||
max_tokens: 3000,
|
||||
})
|
||||
|
||||
const text = response.choices[0]?.message?.content || ''
|
||||
|
||||
// 解析结构化数据
|
||||
const riskItems: { level: string; title: string; description: string; suggestion: string }[] = []
|
||||
const riskRegex = /(🔴|🟡|🟢)\s*\[([^\]]+)\]\s*-\s*\[([^\]]+)\]\s*-\s*\[([^\]]+)\]/g
|
||||
let match
|
||||
while ((match = riskRegex.exec(text)) !== null) {
|
||||
riskItems.push({
|
||||
level: match[1] === '🔴' ? 'RED' : match[1] === '🟡' ? 'YELLOW' : 'GREEN',
|
||||
title: match[2],
|
||||
description: match[3],
|
||||
suggestion: match[4],
|
||||
})
|
||||
}
|
||||
|
||||
const scoreMatch = text.match(/【合规评分】\s*(\d+)\s*\/\s*100/)
|
||||
const score = scoreMatch ? parseInt(scoreMatch[1]) : 0
|
||||
|
||||
const summaryMatch = text.match(/【总体建议】\s*([\s\S]*?)(?:$|$)/)
|
||||
const summary = summaryMatch ? summaryMatch[1].trim() : ''
|
||||
|
||||
return { text, structured: { riskItems, score, summary } }
|
||||
}
|
||||
|
||||
export async function matchCase(scenario: string) {
|
||||
const prompt = `作为一个劳动法案例匹配专家,请分析以下劳动争议情形,匹配相似的仲裁/诉讼案例,评估败诉风险。
|
||||
|
||||
争议情形:
|
||||
${scenario}
|
||||
|
||||
请按以下格式输出:
|
||||
【相似案例】
|
||||
案例1:[案例标题]
|
||||
- 情形:[简要描述]
|
||||
- 结果:[判决结果]
|
||||
- 赔偿金额:[金额]
|
||||
- 相似度:XX%
|
||||
|
||||
案例2:...
|
||||
|
||||
【败诉风险评估】
|
||||
风险等级:高/中/低(XX%)
|
||||
原因:[分析]
|
||||
|
||||
【建议】
|
||||
[降低风险的具体建议]`
|
||||
|
||||
const response = await client.chat.completions.create({
|
||||
model: 'qwen-max',
|
||||
messages: [
|
||||
{ role: 'system', content: '你是劳动法案例分析专家,熟悉劳动仲裁和诉讼案例。' },
|
||||
{ role: 'user', content: prompt },
|
||||
],
|
||||
temperature: 0.3,
|
||||
max_tokens: 3000,
|
||||
})
|
||||
|
||||
return response.choices[0]?.message?.content || ''
|
||||
}
|
||||
|
||||
export async function predictRisks(orgContext: string) {
|
||||
const prompt = `基于以下企业用工数据,预测未来30天可能出现的合规风险,并给出优先级建议。
|
||||
|
||||
企业数据:
|
||||
${orgContext}
|
||||
|
||||
请按以下格式输出:
|
||||
【未来30天预计风险】
|
||||
- [员工姓名/风险描述] → [建议措施]
|
||||
|
||||
【优先级建议】
|
||||
[先处理什么,再处理什么]`
|
||||
|
||||
const response = await client.chat.completions.create({
|
||||
model: 'qwen-plus',
|
||||
messages: [
|
||||
{ role: 'system', content: '你是劳动用工风险预测专家,能基于企业数据分析未来风险趋势。' },
|
||||
{ role: 'user', content: prompt },
|
||||
],
|
||||
temperature: 0.5,
|
||||
max_tokens: 1500,
|
||||
})
|
||||
|
||||
return response.choices[0]?.message?.content || ''
|
||||
}
|
||||
Reference in New Issue
Block a user