您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds the ability to modify system prompts in the Qwen Chat interface to customize AI behavior.
- // ==UserScript==
- // @name System Prompt Editor for Qwen Chat
- // @name:ru Редактор системного промпта для Qwen Chat
- // @namespace https://chat.qwen.ai/
- // @version 2025-06-19
- // @description Adds the ability to modify system prompts in the Qwen Chat interface to customize AI behavior.
- // @description:ru Добавляет возможность изменения системных промптов в интерфейсе Qwen Chat для настройки поведения ИИ.
- // @author Mikhail Zuenko
- // @match https://chat.qwen.ai/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=qwen.ai
- // @grant unsafeWindow
- // ==/UserScript==
- function generateUUID() {
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
- const r = Math.random() * 16 | 0;
- const v = c === 'x' ? r : (r & 0x3) | 0x8;
- return v.toString(16);
- });
- }
- function getSystemPromptMessage(data) {
- const rootMessages = Object.values(data.chat.history.messages).filter(msg => msg.parentId === null);
- const promptMessage = rootMessages.find(msg => msg.role === 'system');
- return promptMessage || null;
- }
- function setSystemPrompt(data, systemPrompt) {
- const promptMessage = getSystemPromptMessage(data);
- if (promptMessage) {
- promptMessage.content = systemPrompt;
- data.chat.messages.find(msg => msg.id === promptMessage.id).content = systemPrompt;
- }
- else {
- const rootMessages = Object.values(data.chat.history.messages).filter(msg => msg.parentId === null);
- const promptMessage = {
- id: generateUUID(),
- parentId: null,
- childrenIds: rootMessages.map(msg => msg.id),
- role: 'system',
- content: systemPrompt
- };
- for (const message of rootMessages) {
- message.parentId = promptMessage.id;
- }
- data.chat.history.messages[promptMessage.id] = promptMessage;
- let firstIndex = null;
- for (let msg = 0; msg < data.chat.messages.length; ++msg) {
- if (data.chat.messages[msg].parentId === null) {
- data.chat.messages[msg].parentId = promptMessage.id;
- if (firstIndex === null) firstIndex = msg;
- }
- }
- data.chat.messages.splice(firstIndex, 0, promptMessage);
- }
- }
- function deleteSystemPrompt(data) {
- const promptMessage = getSystemPromptMessage(data);
- if (!promptMessage) return;
- const children = promptMessage.childrenIds;
- for (const childId of children) {
- data.chat.history.messages[childId].parentId = null;
- }
- for (const message of data.chat.messages) {
- if (children.includes(message.id)) message.parentId = null;
- }
- data.chat.messages.splice(data.chat.messages.findIndex(msg => msg.id === promptMessage.id), 1);
- delete data.chat.history.messages[promptMessage.id];
- }
- let origFetch = unsafeWindow.fetch;
- unsafeWindow.fetch = async (input, init) => {
- if (init && init._noChange) return origFetch(input, init);
- if (init && input === '/api/chat/completions') {
- const body = JSON.parse(init.body);
- const promptMessage = getSystemPromptMessage(await request('/api/v1/chats/' + body.chat_id));
- if (promptMessage) {
- body.messages.unshift({ role: 'system', content: promptMessage.content });
- init.body = JSON.stringify(body);
- }
- }
- else if (typeof input === 'string') {
- if (/^\/api\/v1\/chats\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/?$/.test(input)) {
- if (init && init.method === 'POST') {
- const promptMessage = getSystemPromptMessage(await request(input));
- if (promptMessage) {
- const body = JSON.parse(init.body);
- setSystemPrompt(body, promptMessage.content);
- init.body = JSON.stringify(body);
- }
- }
- const res = await origFetch(input, init);
- const data = await res.json();
- deleteSystemPrompt(data);
- return new Response(JSON.stringify(data), {
- status: res.status,
- statusText: res.statusText,
- headers: res.headers
- });
- }
- }
- return origFetch(input, init);
- };
- function getIdFromUrl() {
- const path = location.pathname.match(/^\/c\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\/?$/);
- return path ? path[1] : null;
- }
- function request(input, init) {
- return unsafeWindow.fetch(input, { _noChange: true, ...init }).then(res => res.json());
- }
- function $E(tag, props, children) {
- const elem = document.createElement(tag);
- for (const prop in props) {
- if (prop.startsWith('on')) elem.addEventListener(prop.slice(2).toLowerCase(), props[prop]);
- else if (prop === 'classes') elem.classList.add(props[prop]);
- else {
- const snakeProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
- if (props[prop] === true) elem.setAttribute(snakeProp, '');
- else elem.setAttribute(snakeProp, props[prop]);
- }
- }
- elem.append(...children);
- return elem;
- }
- function $T(text) {
- return document.createTextNode(text || '');
- }
- const textarea = $E('textarea', {
- class: 'block w-full h-[200px] p-2 bg-white dark:bg-[#2A2A2A] text-[#2C2C36] dark:text-[#FAFAFC] rounded-lg resize-none',
- placeholder: 'How should I answer you?'
- }, [])
- const button = $E(
- 'button',
- {
- class: 'ml-1',
- async onClick() {
- const id = getIdFromUrl();
- if (id) {
- const data = await request('/api/v1/chats/' + id);
- const promptMessage = getSystemPromptMessage(data);
- textarea.value = promptMessage ? promptMessage.content : '';
- document.body.append(editor);
- document.addEventListener('keydown', escCloseEditor);
- }
- }
- },
- [$E('i', { class: 'iconfont leading-none icon-line-message-circle-02 w-[32px] h-[32px] chat-input-feature-btn !mr-[0]' }, [])]
- );
- const editor = $E('div', {
- class: 'modal fixed inset-0 z-[9999] flex h-full w-full items-center justify-center overflow-hidden bg-black/60',
- onMousedown: closeEditor
- }, [
- $E('div', {
- class: 'm-auto max-w-full w-[480px] mx-2 shadow-3xl scrollbar-hidden max-h-[90vh] overflow-y-auto bg-gray-50 dark:bg-gray-900 rounded-2xl',
- onMousedown: event => event.stopPropagation()
- }, [
- $E('div', { class: 'flex justify-between px-5 pb-1 pt-4 dark:text-gray-300' }, [
- $E('div', { class: 'self-center text-lg font-medium' }, [$T('System Prompt')]),
- $E('button', {
- class: 'self-center',
- onClick: closeEditor
- }, [
- $E('i', { class: 'iconfont leading-none icon-line-x-02 font-bold' }, [])
- ])
- ]),
- $E('div', { class: 'px-4 pt-1' }, [textarea]),
- $E('div', { class: 'flex justify-end p-4 pt-3 text-sm font-medium' }, [
- $E('button', {
- class: 'dark:purple-500 dark:hover:purple-400 rounded-full bg-purple-500 px-3.5 py-1.5 text-sm font-medium text-white transition hover:bg-purple-400',
- async onClick() {
- closeEditor();
- const id = getIdFromUrl();
- if (id) {
- const data = await request('/api/v1/chats/' + id);
- if (textarea.value === '') deleteSystemPrompt(data);
- else setSystemPrompt(data, textarea.value);
- request('/api/v1/chats/' + id, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(data)
- });
- }
- }
- }, [$T('Save')])
- ])
- ])
- ]);
- function closeEditor() {
- editor.remove();
- document.removeEventListener('keydown', escCloseEditor);
- }
- function escCloseEditor(event) {
- if (event.code === 'Escape') closeEditor();
- }
- let lastId = getIdFromUrl();
- addEventListener('popstate', () => {
- const newId = getIdFromUrl();
- if (lastId === newId) return;
- closeEditor();
- lastId = newId;
- });
- new MutationObserver(() => {
- const elem = document.querySelector('div:has(>.operationBtn)>:first-child');
- if (document.getElementById('messages-container') && elem && button.parentNode !== elem) {
- elem.append(button);
- }
- }).observe(document.body, { childList: true, subtree: true });