feat: 新增全局学习模式
This commit is contained in:
parent
8261415b40
commit
9abe247503
|
|
@ -80,12 +80,21 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
|
<div class="learning-mode-toggle">
|
||||||
|
<span class="learning-mode-label">学习模式</span>
|
||||||
|
<FormSwitch
|
||||||
|
:model-value="settings.learningModeEnabled"
|
||||||
|
@update:model-value="settingsStore.setLearningModeEnabled($event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
import {
|
import {
|
||||||
Menu,
|
Menu,
|
||||||
Trash2,
|
Trash2,
|
||||||
|
|
@ -97,6 +106,7 @@ import {
|
||||||
import SidebarExpandIcon from "@/components/icons/custom/SidebarExpandIcon.vue";
|
import SidebarExpandIcon from "@/components/icons/custom/SidebarExpandIcon.vue";
|
||||||
import SidebarCollapseIcon from "@/components/icons/custom/SidebarCollapseIcon.vue";
|
import SidebarCollapseIcon from "@/components/icons/custom/SidebarCollapseIcon.vue";
|
||||||
import { useSettingsStore } from "@/stores/settings.ts";
|
import { useSettingsStore } from "@/stores/settings.ts";
|
||||||
|
import FormSwitch from "@/components/ui/FormSwitch.vue";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|
@ -129,6 +139,7 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const showMoreMenu = ref(false);
|
const showMoreMenu = ref(false);
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
const { settings } = storeToRefs(settingsStore);
|
||||||
|
|
||||||
function handleClear() {
|
function handleClear() {
|
||||||
if (confirm("确定要清空当前对话吗?此操作不可恢复。")) {
|
if (confirm("确定要清空当前对话吗?此操作不可恢复。")) {
|
||||||
|
|
@ -244,6 +255,24 @@ if (typeof window !== "undefined") {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.learning-mode-toggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learning-mode-label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #6b7280;
|
||||||
|
user-select: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
.dark & {
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.header-btn {
|
.header-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,10 @@ async function handleSend(
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取系统提示词(优先使用传入的,否则使用会话设置)
|
// 获取系统提示词(优先使用传入的,否则使用会话设置)
|
||||||
const systemPrompt = options?.systemPrompt || currentConversation.value?.settings?.systemPrompt;
|
const systemPrompt =
|
||||||
|
options?.systemPrompt ||
|
||||||
|
currentConversation.value?.settings?.systemPrompt ||
|
||||||
|
settings.value.defaultSystemPrompt;
|
||||||
|
|
||||||
// 检查是否需要添加系统消息
|
// 检查是否需要添加系统消息
|
||||||
const existingMessages = currentConversation.value?.messages || [];
|
const existingMessages = currentConversation.value?.messages || [];
|
||||||
|
|
@ -283,7 +286,7 @@ async function handleSend(
|
||||||
deepSearch: options?.deepSearch,
|
deepSearch: options?.deepSearch,
|
||||||
webSearch: options?.webSearch,
|
webSearch: options?.webSearch,
|
||||||
deepThinking: options?.deepThinking,
|
deepThinking: options?.deepThinking,
|
||||||
systemPrompt: options?.systemPrompt,
|
systemPrompt,
|
||||||
},
|
},
|
||||||
abortController.value.signal,
|
abortController.value.signal,
|
||||||
);
|
);
|
||||||
|
|
@ -404,6 +407,9 @@ async function handleRetry(messageId: string) {
|
||||||
currentStreamingMessageId.value = messageId;
|
currentStreamingMessageId.value = messageId;
|
||||||
chatStore.startStreaming();
|
chatStore.startStreaming();
|
||||||
abortController.value = new AbortController();
|
abortController.value = new AbortController();
|
||||||
|
const systemPrompt =
|
||||||
|
currentConversation.value?.settings?.systemPrompt ||
|
||||||
|
settings.value.defaultSystemPrompt;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const stream = chatApi.streamChat(
|
const stream = chatApi.streamChat(
|
||||||
|
|
@ -413,6 +419,7 @@ async function handleRetry(messageId: string) {
|
||||||
model: settings.value.defaultModel,
|
model: settings.value.defaultModel,
|
||||||
stream: true,
|
stream: true,
|
||||||
history: priorMessages,
|
history: priorMessages,
|
||||||
|
systemPrompt,
|
||||||
},
|
},
|
||||||
abortController.value.signal,
|
abortController.value.signal,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -203,6 +203,8 @@
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
<textarea
|
||||||
class="prompt-textarea"
|
class="prompt-textarea"
|
||||||
|
:class="{ disabled: settings.learningModeEnabled }"
|
||||||
|
:disabled="settings.learningModeEnabled"
|
||||||
:value="settings.defaultSystemPrompt"
|
:value="settings.defaultSystemPrompt"
|
||||||
rows="4"
|
rows="4"
|
||||||
placeholder="输入系统提示词..."
|
placeholder="输入系统提示词..."
|
||||||
|
|
@ -214,6 +216,9 @@
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
<p v-if="settings.learningModeEnabled" class="setting-desc">
|
||||||
|
当前提示词已由学习模式接管,关闭学习模式后可恢复编辑。
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -849,6 +854,16 @@ function handleClearData() {
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: #9ca3af;
|
color: #9ca3af;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.7;
|
||||||
|
cursor: not-allowed;
|
||||||
|
background: #f3f4f6;
|
||||||
|
|
||||||
|
.dark & {
|
||||||
|
background: #252533;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-actions {
|
.data-actions {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
* 所有端点都是固定的,后端需要实现这些端点
|
* 所有端点都是固定的,后端需要实现这些端点
|
||||||
*/
|
*/
|
||||||
import { getAuthHeaders } from './request';
|
import { getAuthHeaders } from './request';
|
||||||
|
import { useSettingsStore } from "@/stores/settings";
|
||||||
|
|
||||||
// API 端点定义(固定)
|
// API 端点定义(固定)
|
||||||
const API_ENDPOINTS = {
|
const API_ENDPOINTS = {
|
||||||
|
|
@ -24,6 +25,9 @@ const API_ENDPOINTS = {
|
||||||
STOP: "/api/chat-ui/stop",
|
STOP: "/api/chat-ui/stop",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_SYSTEM_PROMPT =
|
||||||
|
"你是一个智能助手,可以分析用户发送的文字,文件或图片内容,并进行回答。";
|
||||||
|
|
||||||
// 请求类型定义
|
// 请求类型定义
|
||||||
export interface ChatMessage {
|
export interface ChatMessage {
|
||||||
role: "user" | "assistant" | "system";
|
role: "user" | "assistant" | "system";
|
||||||
|
|
@ -96,6 +100,24 @@ class ChatApi {
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private resolveSystemPrompt(explicit?: string): string {
|
||||||
|
if (explicit?.trim()) {
|
||||||
|
return explicit.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
const fallbackPrompt = settingsStore.settings.defaultSystemPrompt;
|
||||||
|
if (fallbackPrompt?.trim()) {
|
||||||
|
return fallbackPrompt.trim();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("读取全局默认系统提示词失败,使用内置兜底提示词", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DEFAULT_SYSTEM_PROMPT;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流式对话
|
* 流式对话
|
||||||
*/
|
*/
|
||||||
|
|
@ -135,9 +157,7 @@ class ChatApi {
|
||||||
// 否则添加系统消息
|
// 否则添加系统消息
|
||||||
const systemMessage = {
|
const systemMessage = {
|
||||||
role: "system",
|
role: "system",
|
||||||
content:
|
content: this.resolveSystemPrompt(request.systemPrompt),
|
||||||
request.systemPrompt ||
|
|
||||||
"你是一个智能助手,可以分析用户发送的文字,文件或图片内容,并进行回答。",
|
|
||||||
};
|
};
|
||||||
allMessages = [systemMessage, ...request.history, { role: "user", content: userContent }];
|
allMessages = [systemMessage, ...request.history, { role: "user", content: userContent }];
|
||||||
}
|
}
|
||||||
|
|
@ -145,9 +165,7 @@ class ChatApi {
|
||||||
// 没有历史消息,添加系统消息
|
// 没有历史消息,添加系统消息
|
||||||
const systemMessage = {
|
const systemMessage = {
|
||||||
role: "system",
|
role: "system",
|
||||||
content:
|
content: this.resolveSystemPrompt(request.systemPrompt),
|
||||||
request.systemPrompt ||
|
|
||||||
"你是一个智能助手,可以分析用户发送的文字,文件或图片内容,并进行回答。",
|
|
||||||
};
|
};
|
||||||
allMessages = [systemMessage, { role: "user", content: userContent }];
|
allMessages = [systemMessage, { role: "user", content: userContent }];
|
||||||
}
|
}
|
||||||
|
|
@ -257,6 +275,7 @@ class ChatApi {
|
||||||
const requestBody = {
|
const requestBody = {
|
||||||
...request,
|
...request,
|
||||||
message: userContent,
|
message: userContent,
|
||||||
|
systemPrompt: this.resolveSystemPrompt(request.systemPrompt),
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch(`${this.baseUrl}${API_ENDPOINTS.CHAT}`, {
|
const response = await fetch(`${this.baseUrl}${API_ENDPOINTS.CHAT}`, {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import type { AppSettings, AIModel } from "@/types/chat";
|
import type { AppSettings, AIModel } from "@/types/chat";
|
||||||
|
import promptData from "@/assets/prompt.json";
|
||||||
|
|
||||||
// 分享结果类型
|
// 分享结果类型
|
||||||
export interface ShareResult {
|
export interface ShareResult {
|
||||||
|
|
@ -13,6 +14,10 @@ export interface ShareResult {
|
||||||
export const useSettingsStore = defineStore("settings", () => {
|
export const useSettingsStore = defineStore("settings", () => {
|
||||||
const MIN_SIDEBAR_WIDTH = 310;
|
const MIN_SIDEBAR_WIDTH = 310;
|
||||||
const MAX_SIDEBAR_WIDTH = 400;
|
const MAX_SIDEBAR_WIDTH = 400;
|
||||||
|
const LEARNING_MODE_PROMPT_TITLE = "让可学 AI 成为我的全科学习导师?";
|
||||||
|
const LEARNING_MODE_SYSTEM_PROMPT =
|
||||||
|
promptData["分析与实践"]?.[LEARNING_MODE_PROMPT_TITLE] ||
|
||||||
|
"你是一位“学习模式”引导员,通过严格的苏格拉底式提问法,引导用户自己思考并逐步得出答案。";
|
||||||
|
|
||||||
// 默认设置
|
// 默认设置
|
||||||
const defaultSettings: AppSettings = {
|
const defaultSettings: AppSettings = {
|
||||||
|
|
@ -31,6 +36,8 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||||
defaultTemperature: 0.7,
|
defaultTemperature: 0.7,
|
||||||
defaultMaxTokens: 4096,
|
defaultMaxTokens: 4096,
|
||||||
defaultSystemPrompt: "你是一个有帮助的 AI 助手。",
|
defaultSystemPrompt: "你是一个有帮助的 AI 助手。",
|
||||||
|
learningModeEnabled: false,
|
||||||
|
learningModePrevDefaultSystemPrompt: "",
|
||||||
|
|
||||||
// 功能设置
|
// 功能设置
|
||||||
enableSound: true,
|
enableSound: true,
|
||||||
|
|
@ -223,9 +230,42 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||||
shareResult.value = null;
|
shareResult.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeLearningModeState() {
|
||||||
|
if (!settings.value.learningModeEnabled) return;
|
||||||
|
|
||||||
|
const currentPrompt = settings.value.defaultSystemPrompt || "";
|
||||||
|
const isUsingLearningPrompt = currentPrompt === LEARNING_MODE_SYSTEM_PROMPT;
|
||||||
|
|
||||||
|
if (!isUsingLearningPrompt && !settings.value.learningModePrevDefaultSystemPrompt) {
|
||||||
|
settings.value.learningModePrevDefaultSystemPrompt = currentPrompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.value.defaultSystemPrompt = LEARNING_MODE_SYSTEM_PROMPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLearningModeEnabled(enabled: boolean) {
|
||||||
|
if (enabled) {
|
||||||
|
if (!settings.value.learningModePrevDefaultSystemPrompt) {
|
||||||
|
settings.value.learningModePrevDefaultSystemPrompt =
|
||||||
|
settings.value.defaultSystemPrompt || defaultSettings.defaultSystemPrompt;
|
||||||
|
}
|
||||||
|
settings.value.learningModeEnabled = true;
|
||||||
|
settings.value.defaultSystemPrompt = LEARNING_MODE_SYSTEM_PROMPT;
|
||||||
|
} else {
|
||||||
|
settings.value.learningModeEnabled = false;
|
||||||
|
if (settings.value.learningModePrevDefaultSystemPrompt) {
|
||||||
|
settings.value.defaultSystemPrompt =
|
||||||
|
settings.value.learningModePrevDefaultSystemPrompt;
|
||||||
|
}
|
||||||
|
settings.value.learningModePrevDefaultSystemPrompt = "";
|
||||||
|
}
|
||||||
|
saveToStorage();
|
||||||
|
}
|
||||||
|
|
||||||
// 更新设置
|
// 更新设置
|
||||||
function updateSettings(updates: Partial<AppSettings>) {
|
function updateSettings(updates: Partial<AppSettings>) {
|
||||||
Object.assign(settings.value, updates);
|
Object.assign(settings.value, updates);
|
||||||
|
normalizeLearningModeState();
|
||||||
|
|
||||||
if (updates.theme) {
|
if (updates.theme) {
|
||||||
applyTheme(updates.theme);
|
applyTheme(updates.theme);
|
||||||
|
|
@ -256,6 +296,7 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||||
try {
|
try {
|
||||||
const imported = JSON.parse(json);
|
const imported = JSON.parse(json);
|
||||||
settings.value = { ...defaultSettings, ...imported };
|
settings.value = { ...defaultSettings, ...imported };
|
||||||
|
normalizeLearningModeState();
|
||||||
applyTheme(settings.value.theme);
|
applyTheme(settings.value.theme);
|
||||||
applyFontSize(settings.value.fontSize);
|
applyFontSize(settings.value.fontSize);
|
||||||
saveToStorage();
|
saveToStorage();
|
||||||
|
|
@ -304,6 +345,7 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||||
if (stored) {
|
if (stored) {
|
||||||
settings.value = { ...defaultSettings, ...JSON.parse(stored) };
|
settings.value = { ...defaultSettings, ...JSON.parse(stored) };
|
||||||
}
|
}
|
||||||
|
normalizeLearningModeState();
|
||||||
|
|
||||||
const collapsedStored = localStorage.getItem("chat-sidebar-collapsed");
|
const collapsedStored = localStorage.getItem("chat-sidebar-collapsed");
|
||||||
if (collapsedStored) {
|
if (collapsedStored) {
|
||||||
|
|
@ -381,5 +423,6 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||||
loadFromStorage,
|
loadFromStorage,
|
||||||
getSelectedModelId,
|
getSelectedModelId,
|
||||||
setSelectedModelId,
|
setSelectedModelId,
|
||||||
|
setLearningModeEnabled,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,8 @@ export interface AppSettings {
|
||||||
defaultTemperature: number;
|
defaultTemperature: number;
|
||||||
defaultMaxTokens: number;
|
defaultMaxTokens: number;
|
||||||
defaultSystemPrompt: string;
|
defaultSystemPrompt: string;
|
||||||
|
learningModeEnabled: boolean;
|
||||||
|
learningModePrevDefaultSystemPrompt: string;
|
||||||
|
|
||||||
// 功能设置
|
// 功能设置
|
||||||
enableSound: boolean;
|
enableSound: boolean;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue