131 lines
4.3 KiB
TypeScript
Raw Normal View History

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;
}