2026-05-10 13:12:44 +08:00

131 lines
4.3 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';
const openai = new OpenAI({
apiKey: process.env.LLM_API_KEY || 'sk-placeholder',
baseURL: process.env.LLM_BASE_URL || 'https://api.openai.com/v1',
});
// Per-user conversation history (in-memory; use Redis in production)
const MAX_CACHE_USERS = 1000;
const conversationCache = new Map<string, { messages: Array<{ role: 'user' | 'assistant'; content: string }>; lastAccess: number }>();
// Evict least-recently-used entries when cache exceeds limit
function getHistory(userId: string): Array<{ role: 'user' | 'assistant'; content: string }> {
const entry = conversationCache.get(userId);
if (entry) {
entry.lastAccess = Date.now();
return entry.messages;
}
return [];
}
function setHistory(userId: string, messages: Array<{ role: 'user' | 'assistant'; content: string }>): void {
if (conversationCache.size >= MAX_CACHE_USERS) {
// Evict oldest entry
let oldestKey = '';
let oldestTime = Infinity;
for (const [key, val] of conversationCache.entries()) {
if (val.lastAccess < oldestTime) {
oldestTime = val.lastAccess;
oldestKey = key;
}
}
if (oldestKey) conversationCache.delete(oldestKey);
}
conversationCache.set(userId, { messages, lastAccess: Date.now() });
}
export async function callAI(params: {
systemPrompt: string;
userMessage: string;
userId: string;
}): Promise<string> {
const { systemPrompt, userMessage, userId } = params;
// Get recent conversation history (last 10 turns = 20 messages)
const history = getHistory(userId);
const messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }> = [
{ role: 'system', content: systemPrompt },
...history.slice(-20),
{ role: 'user', content: userMessage },
];
try {
const completion = await openai.chat.completions.create({
model: process.env.LLM_MODEL || 'gpt-4',
messages,
temperature: 0.7,
max_tokens: 800,
});
const aiResponse = completion.choices[0]?.message?.content || '...';
// Update conversation cache
const updatedHistory = [
...history,
{ role: 'user' as const, content: userMessage },
{ role: 'assistant' as const, content: aiResponse },
].slice(-20);
setHistory(userId, updatedHistory);
return aiResponse;
} catch (err: any) {
console.error('[AI] Call failed:', err.message);
throw new Error('AI服务暂时不可用');
}
}
export function getWelcomeMessage(user: {
nickname: string;
role: string;
aiCompanion: { name: string };
}): string {
const { nickname, role, aiCompanion } = user;
const name = aiCompanion.name;
if (role === 'author') {
return `早上好,${nickname}!我是${name}你的AI创作伙伴。\n\n今天想做什么写作、看数据、还是找合作机会`;
} else if (role === 'editor') {
return `早上好,${nickname}!我是${name}你的AI审稿助手。\n\n今天有新投稿等你审核要看看吗`;
} else {
return `早上好,${nickname}!我是${name}你的AI数据助手。\n\n今天的数据已更新要看看趋势分析吗`;
}
}
export function buildSystemPrompt(user: {
nickname: string;
role: string;
}): string {
const prompts: Record<string, string> = {
author: `你是用户的AI创作伙伴名字叫「笔灵」。
用户信息:昵称=${user.nickname},角色=作者。
你的职责:
1. 帮助用户创作(提供灵感、扩写、优化文笔)
2. 管理写作项目(打开文档、查看进度)
3. 提醒和建议(写作时间、字数目标、市场趋势)
4. 记住用户的写作风格和习惯
语气:温暖、鼓励、专业。像一个懂你的创作搭档。`,
editor: `你是用户的AI审稿助手名字叫「慧眼」。
用户信息:昵称=${user.nickname},角色=编辑。
你的职责:
1. 帮助筛选和评估投稿
2. 分析AI使用报告
3. 提供审稿建议
4. 管理审核工作流
语气:专业、精准、高效。`,
operator: `你是用户的AI数据助手名字叫「星图」。
用户信息:昵称=${user.nickname},角色=运营。
你的职责:
1. 分析平台数据趋势
2. 提供运营策略建议
3. 管理跨平台合作
4. 生成数据报告
语气:敏锐、有洞察力、数据驱动。`,
};
return prompts[user.role] || prompts.author;
}