f1a02f0439
- #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 新增公司备用文件上传模块
411 lines
13 KiB
TypeScript
411 lines
13 KiB
TypeScript
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
|
||
}
|
||
}
|
||
|