Files
TurboHR/backend/src/services/ai.service.ts
T
selfrelease f1a02f0439 feat: TurboHR 14项优化与功能增强
- #1 Dashboard风险提醒增加立刻办理按钮
- #2 Calendar月份选择器改为input month
- #3 Termination增加7种解聘原因法律依据和操作步骤
- #5 合同审查支持PDF TXT格式
- #6 AI合同审查prompt优化为具体修改建议
- #7 知识库添加更新机制说明
- #9 SpecialStatus员工选择改用all-lite接口
- #10 Termination增加详细法律条款引用
- #11 Money发薪批次增加社保公积金合计列
- #12 EmployeeAttachment扩展文件类型
- #13 花名册增加女职工干部工人选项加退休提醒
- #14 新增公司备用文件上传模块
2026-08-01 13:47:49 +08:00

411 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 = `请审查以下劳动合同文本的合法性和合规性,逐条检查并标注风险等级(红/黄/绿),对每个风险点必须给出:
1. 问题说明:具体哪一条款存在什么问题
2. 法律依据:引用《劳动合同法》具体条款
3. 具体修改建议:给出可以直接替换的修改后条款文本
最后给出合规评分(0-100分)。
合同文本:
${contractText}
请严格按以下格式输出:
【风险项】
🔴/🟡/🟢 [问题标题] - [问题说明+法律依据] - [具体修改建议:应将xxx修改为yyy]
【合规评分】XX/100
【总体建议】
一段话总结,指出最需要优先修改的3个问题`
const response = await client.chat.completions.create({
model: 'qwen-max',
messages: [
{ role: 'system', content: '你是劳动法合同审查专家,精通劳动合同法。对每个风险点必须给出法律依据和可直接替换的具体修改建议文本。' },
{ role: 'user', content: prompt },
],
temperature: 0.3,
max_tokens: 4000,
})
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 || ''
}
export async function* predictRisksStream(orgContext: string) {
const prompt = `基于以下企业用工数据,预测未来30天可能出现的合规风险,并给出优先级建议。
请使用 Markdown 格式输出,包含以下部分:
## 未来30天预计风险
用表格列出每个风险项:
| 优先级 | 员工/范围 | 风险描述 | 法律依据 | 建议措施 | 截止日期 |
|--------|-----------|----------|----------|----------|----------|
## 优先级建议
按紧急程度排序,说明先处理什么、再处理什么。
## 合规建议总结
简要总结企业当前用工合规状况和改进方向。
企业数据:
${orgContext}`
const stream = await client.chat.completions.create({
model: 'qwen-plus',
messages: [
{ role: 'system', content: '你是劳动用工风险预测专家,能基于企业数据分析未来风险趋势。请使用 Markdown 格式输出,善用表格、加粗、列表等格式。' },
{ role: 'user', content: prompt },
],
temperature: 0.5,
max_tokens: 8000,
stream: true,
})
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content
if (delta) yield delta
}
}
/** 12 类争议场景中文映射 */
const SCENARIO_LABELS: Record<string, string> = {
discipline: '违纪解除',
incompetence: '不胜任解除',
probation: '试用期解除',
layoff: '经济性裁员',
expiry: '合同到期不续签',
negotiated: '协商解除',
transfer: '调岗调薪争议',
overtime: '加班费争议',
injury: '工伤待遇争议',
noncompete: '竞业限制争议',
confidentiality: '保密协议争议',
social_insurance: '社保公积金争议',
}
/**
* 结构化判赔预测流式输出
* @param scenarioType 争议场景类型
* @param keyFacts HR 填写的关键事实
* @param orgContext 企业上下文(员工信息等)
*/
export async function* predictStructuredStream(
scenarioType: string,
keyFacts: {
employeeName?: string
violationFact?: string
region?: string
monthlySalary?: string
democracyStatus?: string
disciplinaryRecord?: string
extraInfo?: string
},
orgContext: string,
) {
const scenarioLabel = SCENARIO_LABELS[scenarioType] || scenarioType
const factsText = [
keyFacts.employeeName ? `- 涉及员工:${keyFacts.employeeName}` : '',
keyFacts.violationFact ? `- 违纪/争议事实:${keyFacts.violationFact}` : '',
keyFacts.region ? `- 所在地区:${keyFacts.region}` : '',
keyFacts.monthlySalary ? `- 员工月薪:${keyFacts.monthlySalary}` : '',
keyFacts.democracyStatus ? `- 制度民主公示状态:${keyFacts.democracyStatus}` : '',
keyFacts.disciplinaryRecord ? `- 违纪记录留痕情况:${keyFacts.disciplinaryRecord}` : '',
keyFacts.extraInfo ? `- 补充信息:${keyFacts.extraInfo}` : '',
].filter(Boolean).join('\n')
const prompt = `你是一位资深劳动法律师,请基于以下争议场景和企业数据,进行专业的判赔风险分析。
## 争议场景
${scenarioLabel}
## 关键事实(由 HR 提供)
${factsText}
## 企业数据
${orgContext}
请使用 Markdown 格式输出,包含以下部分:
## 风险分析
分析企业在当前场景下的主要法律风险点,引用具体法律条文。
## 预估赔偿范围
根据员工月薪、工龄、地区裁判口径,给出经济补偿/赔偿金的预估范围(注意:此为参考值,实际以仲裁裁决为准):
| 项目 | 金额范围(元) | 计算依据 |
|------|--------------|----------|
## 建议方案
给出 2-3 个可行方案,按推荐度排序:
### 方案一(推荐)
- **操作步骤**...
- **预估成本**...
- **风险等级**:低/中/高
- **法律依据**...
### 方案二
- **操作步骤**...
- **预估成本**...
- **风险等级**:低/中/高
- **法律依据**...
## 关键注意事项
列出操作中必须注意的法律细节和证据要求。
> ⚠️ 以上分析基于当前提供的信息,仅供参考。实际仲裁结果受具体事实、证据、地区裁判口径等因素影响,建议在操作前咨询专业律师。`
const stream = await client.chat.completions.create({
model: 'qwen-plus',
messages: [
{
role: 'system',
content: '你是劳动用工法律风险评估专家,精通中国劳动法、劳动合同法及相关司法解释。请基于企业提供的事实和数据,给出专业、客观的风险分析和方案建议。请使用 Markdown 格式输出,善用表格、加粗、列表等格式。所有金额预估必须注明"仅供参考"。',
},
{ role: 'user', content: prompt },
],
temperature: 0.5,
max_tokens: 8000,
stream: true,
})
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content
if (delta) yield delta
}
}
/**
* AI 人力分析报告:基于企业数据自动生成结构化报告
*/
export async function* generateHRReportStream(orgData: string) {
const prompt = `请基于以下企业人力数据,生成一份结构化的 HR 人力分析报告。请使用 Markdown 格式输出,包含以下部分:
## 一、人力概况
- 员工总数、部门分布、性别比例、年龄段分布、学历分布、司龄分布
## 二、风险提示
- 当前存在的用工风险(合同到期、试用期、特殊状态员工等)
- 风险等级和紧急程度
## 三、成本分析
- 人力成本概况(工资、社保、公积金等)
- 人均成本、部门成本差异
- 成本趋势分析
## 四、合规建议
- 合同管理建议
- 社保公积金合规建议
- 规章制度完善建议
## 五、改进方向
- 人才结构优化建议
- 成本控制建议
- 管理流程改进建议
报告要求:
- 数据驱动的分析,引用具体数字
- 每个部分给出 2-3 条具体可操作的建议
- 语言简洁专业,避免空话套话
企业数据:
${orgData}`
const stream = await client.chat.completions.create({
model: 'qwen-plus',
messages: [
{
role: 'system',
content: '你是一个专业的人力资源分析师,精通中国劳动法规和人力资源管理。请基于企业实际数据生成专业、客观、可操作的人力分析报告。使用 Markdown 格式输出。',
},
{ role: 'user', content: prompt },
],
temperature: 0.5,
max_tokens: 8000,
stream: true,
})
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content
if (delta) yield delta
}
}