claude-code-cchui/src/app/api/prompt/optimize/route.ts
gaoziman 4b4732a583 feat(API): 添加提示词优化相关接口
- 新增 /api/prompt/optimize 接口用于调用 AI 优化提示词
- 支持简洁版和详细版两种优化模式
- 支持 Claude 原生格式和 OpenAI 兼容格式
- 新增 /api/prompt/history 接口管理优化历史记录
- 支持查询和删除历史记录
2025-12-22 00:06:26 +08:00

167 lines
5.1 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 { NextResponse } from 'next/server';
import { db } from '@/drizzle/db';
import { promptOptimizations, userSettings } from '@/drizzle/schema';
import { eq } from 'drizzle-orm';
import { getCurrentUser } from '@/lib/auth';
import { decryptApiKey } from '@/lib/crypto';
// 简洁版系统提示词
const CONCISE_SYSTEM_PROMPT = `你是一个提示词优化专家。用户会给你一个原始的想法或问题,请帮助优化成简洁明了的提示词。
要求:
1. 保持核心意图不变
2. 去除冗余和模糊的表达
3. 使用清晰、准确的语言
4. 输出应该简短精炼不超过150字
5. 直接输出优化后的提示词,不要解释或添加其他内容`;
// 详细版系统提示词
const DETAILED_SYSTEM_PROMPT = `你是一个提示词优化专家。用户会给你一个原始的想法或问题,请帮助优化成结构化的详细提示词。
优化后的提示词应包含以下要素(根据实际情况选择):
1. **任务目标**:明确说明要完成什么
2. **背景上下文**:提供必要的背景信息
3. **具体要求**:列出详细的要求和约束
4. **输出格式**:说明期望的输出格式
5. **示例**(如有必要):提供参考示例
要求:
- 使用清晰的结构和层次
- 语言准确、专业
- 保持原始意图的完整性
- 直接输出优化后的提示词,不要解释或添加额外的说明文字`;
// 规范化 URL
function normalizeBaseUrl(url: string): string {
return url.replace(/\/+$/, '');
}
// POST /api/prompt/optimize - 优化提示词
export async function POST(request: Request) {
try {
// 获取当前用户
const user = await getCurrentUser();
if (!user) {
return NextResponse.json(
{ error: '请先登录' },
{ status: 401 }
);
}
const body = await request.json();
const { originalPrompt, mode } = body;
if (!originalPrompt || !mode) {
return NextResponse.json(
{ error: '缺少必要参数' },
{ status: 400 }
);
}
if (!['concise', 'detailed'].includes(mode)) {
return NextResponse.json(
{ error: '无效的优化模式' },
{ status: 400 }
);
}
// 获取用户设置
const settings = await db.query.userSettings.findFirst({
where: eq(userSettings.userId, user.userId),
});
if (!settings?.cchApiKey) {
return NextResponse.json(
{ error: '请先在设置中配置 API Key' },
{ status: 400 }
);
}
// 解密 API Key
const apiKey = decryptApiKey(settings.cchApiKey);
const cchUrl = settings.cchUrl || process.env.CCH_DEFAULT_URL || 'https://claude.leocoder.cn/';
const apiFormat = (settings.apiFormat as 'claude' | 'openai') || 'claude';
// 选择系统提示词
const systemPrompt = mode === 'concise' ? CONCISE_SYSTEM_PROMPT : DETAILED_SYSTEM_PROMPT;
// 调用 AI 进行优化
let optimizedPrompt: string;
if (apiFormat === 'openai') {
// OpenAI 兼容格式
const response = await fetch(`${normalizeBaseUrl(cchUrl)}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({
model: 'claude-sonnet-4-5-20250929',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: `请优化以下提示词:\n\n${originalPrompt}` },
],
temperature: 0.7,
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API 调用失败: ${errorText}`);
}
const data = await response.json();
optimizedPrompt = data.choices?.[0]?.message?.content || '';
} else {
// Claude 原生格式
const response = await fetch(`${normalizeBaseUrl(cchUrl)}/v1/messages`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'anthropic-version': '2023-06-01',
},
body: JSON.stringify({
model: 'claude-sonnet-4-5-20250929',
max_tokens: 2048,
system: systemPrompt,
messages: [
{ role: 'user', content: `请优化以下提示词:\n\n${originalPrompt}` },
],
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API 调用失败: ${errorText}`);
}
const data = await response.json();
optimizedPrompt = data.content?.[0]?.text || '';
}
// 清理 <think> 标签(如果有)
optimizedPrompt = optimizedPrompt.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
// 保存到数据库
await db.insert(promptOptimizations).values({
userId: user.userId,
originalPrompt,
optimizedPrompt,
mode,
});
return NextResponse.json({
success: true,
optimizedPrompt,
});
} catch (error) {
console.error('提示词优化失败:', error);
return NextResponse.json(
{ error: error instanceof Error ? error.message : '优化失败,请稍后重试' },
{ status: 500 }
);
}
}