feat: 将系统提示词加入会话上下文中,系统提示词不会显示在前端。

This commit is contained in:
肖应宇 2026-03-11 13:39:41 +08:00
parent 38faeeb46d
commit 3471e8552f
6 changed files with 115 additions and 29 deletions

View File

@ -223,15 +223,25 @@ async function handleSend(
await chatStore.createConversation(); await chatStore.createConversation();
} }
// // 使使
const systemPrompt = options?.systemPrompt || currentConversation.value?.settings?.systemPrompt;
//
const existingMessages = currentConversation.value?.messages || []; const existingMessages = currentConversation.value?.messages || [];
const hasSystemMessage = existingMessages.some((m: any) => m.role === MessageRole.SYSTEM);
//
if (systemPrompt && !hasSystemMessage) {
await chatStore.addMessage(MessageRole.SYSTEM, {
type: MessageType.TEXT,
text: systemPrompt,
});
}
//
const updatedMessages = currentConversation.value?.messages || [];
const MAX_HISTORY_ROUNDS = 20; // 20 40 const MAX_HISTORY_ROUNDS = 20; // 20 40
const historyMessages = existingMessages const historyMessages = updatedMessages.filter((m: any) => m.content?.text) //
.filter(
(m: any) =>
m.role === MessageRole.USER || m.role === MessageRole.ASSISTANT,
)
.filter((m: any) => m.content?.text) //
.slice(-(MAX_HISTORY_ROUNDS * 2)) .slice(-(MAX_HISTORY_ROUNDS * 2))
.map((m: any) => ({ role: m.role, content: m.content.text })); .map((m: any) => ({ role: m.role, content: m.content.text }));

View File

@ -3,7 +3,7 @@
<div ref="containerRef" class="message-list" @scroll="handleScroll"> <div ref="containerRef" class="message-list" @scroll="handleScroll">
<!-- 欢迎界面 --> <!-- 欢迎界面 -->
<WelcomeScreen <WelcomeScreen
v-if="messages.length === 0" v-if="visibleMessages.length === 0"
@select="$emit('select-suggestion', $event)" @select="$emit('select-suggestion', $event)"
/> />
@ -12,12 +12,12 @@
<div class="messages-wrapper"> <div class="messages-wrapper">
<TransitionGroup name="message"> <TransitionGroup name="message">
<MessageBubble <MessageBubble
v-for="(message, index) in messages" v-for="(message, index) in visibleMessages"
:key="message.id" :key="message.id"
:message="message" :message="message"
:show-timestamp="showTimestamp" :show-timestamp="showTimestamp"
:compact="compact" :compact="compact"
:is-New="index === messages.length - 1" :is-New="index === visibleMessages.length - 1"
@retry="$emit('retry', message.id)" @retry="$emit('retry', message.id)"
@regenerate="$emit('regenerate', message.id)" @regenerate="$emit('regenerate', message.id)"
@copy="handleCopy(message)" @copy="handleCopy(message)"
@ -62,12 +62,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch, nextTick, onMounted } from "vue"; import { ref, watch, nextTick, onMounted, computed } from "vue";
import { useChatStore } from "@/stores/chat"; import { useChatStore } from "@/stores/chat";
import MessageBubble from "@/components/message/MessageBubble.vue"; import MessageBubble from "@/components/message/MessageBubble.vue";
import WelcomeScreen from "./WelcomeScreen.vue"; import WelcomeScreen from "./WelcomeScreen.vue";
import { Bot, ChevronDown } from "@/components/icons"; import { Bot, ChevronDown } from "@/components/icons";
import type { Message, Attachment, VideoInfo, Suggestion } from "@/types/chat"; import type { Message, Attachment, VideoInfo, Suggestion } from "@/types/chat";
import { MessageRole } from "@/types/chat";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@ -83,6 +84,13 @@ const props = withDefaults(
}, },
); );
//
const visibleMessages = computed(() => {
return props.messages.filter(
(message) => message.role !== MessageRole.SYSTEM
);
});
const emit = defineEmits<{ const emit = defineEmits<{
retry: [messageId: string]; retry: [messageId: string];
regenerate: [messageId: string]; regenerate: [messageId: string];
@ -175,7 +183,7 @@ function handleDownloadFile(file: Attachment) {
// //
watch( watch(
() => props.messages.length, () => visibleMessages.value.length,
(newLen, oldLen) => { (newLen, oldLen) => {
if (newLen > oldLen) { if (newLen > oldLen) {
if (isAutoScrolling.value) { if (isAutoScrolling.value) {
@ -191,7 +199,7 @@ watch(
// //
watch( watch(
() => props.messages[props.messages.length - 1]?.content.text, () => visibleMessages.value[visibleMessages.value.length - 1]?.content.text,
() => { () => {
if (isAutoScrolling.value) { if (isAutoScrolling.value) {
nextTick(() => { nextTick(() => {

View File

@ -163,7 +163,7 @@ async function textCopy(data: any) {
/* 可折叠内容 */ /* 可折叠内容 */
.thinking-content { .thinking-content {
max-height: 2000px; max-height: 2000px;
overflow: hidden; overflow: auto;
transition: transition:
max-height 0.35s ease, max-height 0.35s ease,
opacity 0.25s ease; opacity 0.25s ease;

View File

@ -202,6 +202,7 @@ import FormSelect from "@/components/ui/FormSelect.vue";
import { MessageSquare, X, Check, Trash2 } from "@/components/icons"; import { MessageSquare, X, Check, Trash2 } from "@/components/icons";
import { chatApi } from "@/services/api.ts"; import { chatApi } from "@/services/api.ts";
import type { ConversationSettings } from "@/types/chat"; import type { ConversationSettings } from "@/types/chat";
import { MessageRole, MessageType } from "@/types/chat";
const chatStore = useChatStore(); const chatStore = useChatStore();
const settingsStore = useSettingsStore(); const settingsStore = useSettingsStore();
@ -359,6 +360,25 @@ function handleSave() {
// //
chatStore.updateConversationSettings(conversation.value.id, convSettings); chatStore.updateConversationSettings(conversation.value.id, convSettings);
//
if (localSettings.value.systemPrompt) {
const messages = conversation.value.messages || [];
const systemMsgIndex = messages.findIndex((m: any) => m.role === MessageRole.SYSTEM);
if (systemMsgIndex >= 0) {
//
chatStore.updateMessage(messages[systemMsgIndex].id, {
content: {
type: MessageType.TEXT,
text: localSettings.value.systemPrompt,
},
});
} else {
//
chatStore.addSystemMessage(conversation.value.id, localSettings.value.systemPrompt);
}
}
close(); close();
// //

View File

@ -121,21 +121,36 @@ class ChatApi {
} }
// 将前端简化的请求翻译为 OpenAI 兼容的规范请求体 // 将前端简化的请求翻译为 OpenAI 兼容的规范请求体
// 构建 messages 数组system + 历史消息 + 当前用户消息 // 检查历史消息中是否已有系统消息
const systemMessage = { const historyHasSystem = request.history?.some((m) => m.role === "system");
role: "system",
content: // 构建 messages 数组
request.systemPrompt || let allMessages: Array<{ role: string; content: any }>;
"你是一个智能助手,可以分析用户发送的文字,文件或图片内容,并进行回答。",
}; if (request.history && request.history.length > 0) {
const currentUserMessage = { // 如果历史中有系统消息,直接使用历史消息
role: "user", if (historyHasSystem) {
content: userContent, allMessages = [...request.history, { role: "user", content: userContent }];
}; } else {
const allMessages = // 否则添加系统消息
request.history && request.history.length > 0 const systemMessage = {
? [systemMessage, ...request.history, currentUserMessage] role: "system",
: [systemMessage, currentUserMessage]; content:
request.systemPrompt ||
"你是一个智能助手,可以分析用户发送的文字,文件或图片内容,并进行回答。",
};
allMessages = [systemMessage, ...request.history, { role: "user", content: userContent }];
}
} else {
// 没有历史消息,添加系统消息
const systemMessage = {
role: "system",
content:
request.systemPrompt ||
"你是一个智能助手,可以分析用户发送的文字,文件或图片内容,并进行回答。",
};
allMessages = [systemMessage, { role: "user", content: userContent }];
}
const openAiRequest = { const openAiRequest = {
model: request.model || "glm-4-flash", model: request.model || "glm-4-flash",

View File

@ -290,6 +290,38 @@ export const useChatStore = defineStore("chat", () => {
return message; return message;
} }
// 添加系统消息(放在消息列表开头)
async function addSystemMessage(
conversationId: string,
systemPrompt: string
): Promise<Message> {
const conversation = conversations.value.find((c) => c.id === conversationId);
if (!conversation) {
throw new Error("Conversation not found");
}
const message: Message = {
id: generateId(),
role: MessageRole.SYSTEM,
content: { type: "text" as const, text: systemPrompt },
timestamp: Date.now(),
isStreaming: false,
} as Message;
// 将系统消息插入到消息列表开头
conversation.messages.unshift(message);
conversation.updatedAt = Date.now();
// 异步保存
try {
await conversationApi.addMessage(conversationId, message);
} catch (error) {
console.error("Failed to save system message:", error);
}
return message;
}
// 更新消息 // 更新消息
async function updateMessage(messageId: string, updates: Partial<Message>) { async function updateMessage(messageId: string, updates: Partial<Message>) {
const conversation = currentConversation.value; const conversation = currentConversation.value;
@ -479,6 +511,7 @@ export const useChatStore = defineStore("chat", () => {
renameConversation, renameConversation,
updateConversationSettings, updateConversationSettings,
addMessage, addMessage,
addSystemMessage,
updateMessage, updateMessage,
updateMessageContent, updateMessageContent,
saveConversation, saveConversation,