From 363c0a77b73f2302b65bed64048cc4b4f3abc22b Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 21 Apr 2026 18:26:54 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9Avisual=20style=E4=BB=8E=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E4=B8=AD=E7=A7=BB=E9=99=A4=EF=BC=8C=E6=94=B9=E4=B8=BA?= =?UTF-8?q?prompt=E6=A8=A1=E6=9D=BF=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prompts/en-US/image_description.prompt | 1 + prompts/ja-JP/image_description.prompt | 1 + prompts/zh-CN/image_description.prompt | 1 + pytests/image_sys_test/image_manager_test.py | 14 ++++-- src/chat/image_system/image_manager.py | 5 ++- src/config/config.py | 2 +- src/config/legacy_migration.py | 15 +++---- src/config/official_configs.py | 46 ++++++++++---------- 8 files changed, 46 insertions(+), 39 deletions(-) create mode 100644 prompts/en-US/image_description.prompt create mode 100644 prompts/ja-JP/image_description.prompt create mode 100644 prompts/zh-CN/image_description.prompt diff --git a/prompts/en-US/image_description.prompt b/prompts/en-US/image_description.prompt new file mode 100644 index 00000000..bc5a2188 --- /dev/null +++ b/prompts/en-US/image_description.prompt @@ -0,0 +1 @@ +Describe the content of this image in English. If there is text, summarize the text. Pay attention to its topic and immediate impression. Output one plain-text paragraph within 30 words. Do not use bullet points. diff --git a/prompts/ja-JP/image_description.prompt b/prompts/ja-JP/image_description.prompt new file mode 100644 index 00000000..ee4a81b8 --- /dev/null +++ b/prompts/ja-JP/image_description.prompt @@ -0,0 +1 @@ +この画像の内容を日本語で説明してください。文字がある場合は、その内容を要約してください。主題と直感的な印象に注意し、30語以内の平文1段落で出力してください。箇条書きにしないでください。 diff --git a/prompts/zh-CN/image_description.prompt b/prompts/zh-CN/image_description.prompt new file mode 100644 index 00000000..783daeb1 --- /dev/null +++ b/prompts/zh-CN/image_description.prompt @@ -0,0 +1 @@ +请用中文描述这张图片的内容。如果有文字,请把文字描述概括出来,请留意其主题、直观感受,输出为一段平文本,最多30字,请注意不要分点,就输出一段文本 diff --git a/pytests/image_sys_test/image_manager_test.py b/pytests/image_sys_test/image_manager_test.py index 9c4143f3..51fd0c5d 100644 --- a/pytests/image_sys_test/image_manager_test.py +++ b/pytests/image_sys_test/image_manager_test.py @@ -183,10 +183,16 @@ def patch_external_dependencies(monkeypatch): sql_mod = types.SimpleNamespace(select=lambda *a, **k: DummySelect()) monkeypatch.setitem(sys.modules, "sqlmodel", sql_mod) - # Patch config values used at import-time - cfg = types.SimpleNamespace(visual=types.SimpleNamespace(visual_style="test-style")) - config_mod = types.SimpleNamespace(global_config=cfg) - monkeypatch.setitem(sys.modules, "src.config.config", config_mod) + # Patch prompt manager used to build image description prompt. + class _PromptManager: + def get_prompt(self, _name): + return types.SimpleNamespace() + + async def render_prompt(self, _prompt): + return "test-style" + + prompt_manager_mod = types.SimpleNamespace(prompt_manager=_PromptManager()) + monkeypatch.setitem(sys.modules, "src.prompt.prompt_manager", prompt_manager_mod) llm_options_mod = types.SimpleNamespace(LLMImageOptions=lambda **kwargs: types.SimpleNamespace(**kwargs)) monkeypatch.setitem(sys.modules, "src.common.data_models.llm_service_data_models", llm_options_mod) diff --git a/src/chat/image_system/image_manager.py b/src/chat/image_system/image_manager.py index d30cb8ae..b2e424ca 100644 --- a/src/chat/image_system/image_manager.py +++ b/src/chat/image_system/image_manager.py @@ -13,7 +13,7 @@ from src.common.logger import get_logger from src.common.database.database import get_db_session from src.common.database.database_model import Images, ImageType from src.common.data_models.image_data_model import MaiImage -from src.config.config import global_config +from src.prompt.prompt_manager import prompt_manager from src.services.llm_service import LLMServiceClient install(extra_lines=3) @@ -374,7 +374,8 @@ class ImageManager: logger.info(f"Cleaned mistaken registration state on {fixed_counter} image records") async def _generate_image_description(self, image_bytes: bytes, image_format: str) -> str: - prompt = global_config.visual.visual_style + prompt_template = prompt_manager.get_prompt("image_description") + prompt = await prompt_manager.render_prompt(prompt_template) image_base64 = base64.b64encode(image_bytes).decode("utf-8") generation_result = await vlm.generate_response_for_image( diff --git a/src/config/config.py b/src/config/config.py index 881084d5..9bd5876a 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -55,7 +55,7 @@ BOT_CONFIG_PATH: Path = (CONFIG_DIR / "bot_config.toml").resolve().absolute() MODEL_CONFIG_PATH: Path = (CONFIG_DIR / "model_config.toml").resolve().absolute() LEGACY_ENV_PATH: Path = (PROJECT_ROOT / ".env").resolve().absolute() MMC_VERSION: str = "1.0.0" -CONFIG_VERSION: str = "8.9.6" +CONFIG_VERSION: str = "8.9.8" MODEL_CONFIG_VERSION: str = "1.14.1" logger = get_logger("config") diff --git a/src/config/legacy_migration.py b/src/config/legacy_migration.py index eae7366b..fad11ac7 100644 --- a/src/config/legacy_migration.py +++ b/src/config/legacy_migration.py @@ -382,16 +382,15 @@ def try_migrate_legacy_bot_config_dict(data: dict[str, Any]) -> MigrationResult: personality = _as_dict(data.get("personality")) visual = _as_dict(data.get("visual")) - if visual is None and personality is not None and "visual_style" in personality: - visual = {} - data["visual"] = visual - - if visual is not None and personality is not None and "visual_style" in personality: - if "visual_style" not in visual: - visual["visual_style"] = personality["visual_style"] + if personality is not None and "visual_style" in personality: personality.pop("visual_style", None) migrated_any = True - reasons.append("personality.visual_style_moved_to_visual.visual_style") + reasons.append("personality.visual_style_removed") + + if visual is not None and "visual_style" in visual: + visual.pop("visual_style", None) + migrated_any = True + reasons.append("visual.visual_style_removed") memory = _as_dict(data.get("memory")) if memory is not None and _migrate_target_item_list(memory, "global_memory_blacklist"): diff --git a/src/config/official_configs.py b/src/config/official_configs.py index f40c55d8..5facdb42 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -75,7 +75,7 @@ class PersonalityConfig(ConfigBase): __ui_icon__ = "user-circle" personality: str = Field( - default="是一个大二在读女大学生,现在正在上网和群友聊天,有时有点攻击性,有时比较温柔", + default="是一个大二女大学生,现在正在上网和群友聊天。", json_schema_extra={ "x-widget": "textarea", "x-icon": "user-circle", @@ -84,7 +84,7 @@ class PersonalityConfig(ConfigBase): """人格,建议100字以内,描述人格特质和身份特征""" reply_style: str = Field( - default="请不要刻意突出自身学科背景。可以参考贴吧,知乎和微博的回复风格。", + default="你的风格平淡简短。可以参考贴吧,知乎和微博的回复风格。不浮夸不长篇大论,不要过分修辞和复杂句。尽量回复的简短一些,平淡一些", json_schema_extra={ "x-widget": "textarea", "x-icon": "message-square", @@ -93,7 +93,13 @@ class PersonalityConfig(ConfigBase): """默认表达风格,描述麦麦说话的表达风格,表达习惯,如要修改,可以酌情新增内容,建议1-2行""" multiple_reply_style: list[str] = Field( - default_factory=lambda: [], + default_factory=lambda: [ + "你的风格平淡但不失讽刺,很简短,很白话。可以参考贴吧,微博的回复风格。", + "用1-2个字进行回复", + "用1-2个符号进行回复", + "言辭凝練古雅,穿插《論語》經句卻不晦澀,以文言短句為基,輔以淺白語意,持長者溫和風範,全用繁體字表達,具先秦儒者談吐韻致。", + "带点翻译腔,但不要太长", + ], json_schema_extra={ "x-widget": "custom", "x-icon": "list", @@ -102,7 +108,7 @@ class PersonalityConfig(ConfigBase): """可选的多种表达风格列表,当配置不为空时可按概率随机替换 reply_style""" multiple_probability: float = Field( - default=0.3, + default=0.2, ge=0, le=1, json_schema_extra={ @@ -137,15 +143,6 @@ class VisualConfig(ConfigBase): ) """回复器模式,auto根据模型信息自动选择,text为纯文本模式,multimodal为多模态模式""" - visual_style: str = Field( - default="请用中文描述这张图片的内容。如果有文字,请把文字描述概括出来,请留意其主题,直观感受,输出为一段平文本,最多30字,请注意不要分点,就输出一段文本", - json_schema_extra={ - "x-widget": "textarea", - "x-icon": "image", - }, - ) - """_wrap_识图提示词,不建议修改""" - class TalkRulesItem(ConfigBase): platform: str = "" @@ -204,7 +201,7 @@ class ChatConfig(ConfigBase): """是否启用回复时附带引用回复""" max_context_size: int = Field( - default=30, + default=40, json_schema_extra={ "x-widget": "input", "x-icon": "layers", @@ -223,11 +220,12 @@ class ChatConfig(ConfigBase): """Planner 连续被新消息打断的最大次数,0 表示不启用打断""" group_chat_prompt: str = Field( - default=""" -你正在qq群里聊天,下面是群里正在聊的内容,其中包含聊天记录和聊天中的图片。 -回复尽量简短一些。最好一次对一个话题进行回复,免得啰嗦或者回复内容太乱。请注意把握聊天内容。 -不要回复的太频繁!控制回复的频率,不要每个人的消息都回复,只回复你感兴趣的或者主动提及你的。 -""", + default=( + "你正在qq群里聊天,下面是群里正在聊的内容,其中包含聊天记录和聊天中的图片和表情包。\n" + "回复尽量简短一些。最好一次对一个话题进行回复,但必须考虑不同群友发言之间的交互,免得啰嗦或者回复内容太乱。请注意把握聊天内容。\n" + "不要总是提及自己的身份背景,根据聊天内容自由发挥,但是要日常不浮夸,不要太关注具体的聊天内容,不要刻意找话题,。\n" + "不要回复的太频繁!不用刻意回复表情包,只要关注表情包表达的含义。控制回复的频率,不要每个人的消息都回复,只回复你感兴趣的或者主动提及你的。\n" + ), json_schema_extra={ "x-widget": "textarea", "x-icon": "users", @@ -236,11 +234,11 @@ class ChatConfig(ConfigBase): """_wrap_群聊通用注意事项""" private_chat_prompts: str = Field( - default=""" -你正在聊天,下面是正在聊的内容,其中包含聊天记录和聊天中的图片。 -回复尽量简短一些。请注意把握聊天内容。 -请考虑对方的发言频率,想法,思考自己何时回复以及回复内容。 -""", + default=( + "你正在聊天,下面是正在聊的内容,其中包含聊天记录和聊天中的图片。\n" + "回复尽量简短一些。请注意把握聊天内容。\n" + "请考虑对方的发言频率,想法,思考自己何时回复以及回复内容。\n" + ), json_schema_extra={ "x-widget": "textarea", "x-icon": "user",