feat(API): 添加提示词优化相关接口
- 新增 /api/prompt/optimize 接口用于调用 AI 优化提示词 - 支持简洁版和详细版两种优化模式 - 支持 Claude 原生格式和 OpenAI 兼容格式 - 新增 /api/prompt/history 接口管理优化历史记录 - 支持查询和删除历史记录
This commit is contained in:
parent
fae1dfb7c9
commit
4b4732a583
97
src/app/api/prompt/history/route.ts
Normal file
97
src/app/api/prompt/history/route.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { db } from '@/drizzle/db';
|
||||||
|
import { promptOptimizations } from '@/drizzle/schema';
|
||||||
|
import { eq, desc } from 'drizzle-orm';
|
||||||
|
import { getCurrentUser } from '@/lib/auth';
|
||||||
|
|
||||||
|
// GET /api/prompt/history - 获取用户的优化历史
|
||||||
|
export async function GET() {
|
||||||
|
try {
|
||||||
|
// 获取当前用户
|
||||||
|
const user = await getCurrentUser();
|
||||||
|
if (!user) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '请先登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询该用户的优化历史(最近 50 条)
|
||||||
|
const history = await db
|
||||||
|
.select()
|
||||||
|
.from(promptOptimizations)
|
||||||
|
.where(eq(promptOptimizations.userId, user.userId))
|
||||||
|
.orderBy(desc(promptOptimizations.createdAt))
|
||||||
|
.limit(50);
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: history,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取优化历史失败:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '获取历史记录失败' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE /api/prompt/history - 删除指定的优化历史
|
||||||
|
export async function DELETE(request: Request) {
|
||||||
|
try {
|
||||||
|
// 获取当前用户
|
||||||
|
const user = await getCurrentUser();
|
||||||
|
if (!user) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '请先登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { searchParams } = new URL(request.url);
|
||||||
|
const id = searchParams.get('id');
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '缺少记录 ID' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证记录属于当前用户后删除
|
||||||
|
const record = await db
|
||||||
|
.select()
|
||||||
|
.from(promptOptimizations)
|
||||||
|
.where(eq(promptOptimizations.id, parseInt(id)))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (record.length === 0) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '记录不存在' },
|
||||||
|
{ status: 404 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record[0].userId !== user.userId) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '无权删除此记录' },
|
||||||
|
{ status: 403 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await db
|
||||||
|
.delete(promptOptimizations)
|
||||||
|
.where(eq(promptOptimizations.id, parseInt(id)));
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除优化历史失败:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '删除失败' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
166
src/app/api/prompt/optimize/route.ts
Normal file
166
src/app/api/prompt/optimize/route.ts
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
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 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user