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 = { 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 } }