ref:让MaiSaka使用麦麦原有的pompt系统,配置系统
This commit is contained in:
3
src/MaiDiary/__init__.py
Normal file
3
src/MaiDiary/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
maisaka - MaiSaka 对话系统
|
||||
"""
|
||||
@@ -1,46 +0,0 @@
|
||||
"""
|
||||
MaiSaka - 全局配置
|
||||
环境变量加载、Rich Console 实例、主题定义。
|
||||
"""
|
||||
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from rich.console import Console
|
||||
from rich.theme import Theme
|
||||
|
||||
# ──────────────────── 加载 .env ────────────────────
|
||||
|
||||
load_dotenv()
|
||||
|
||||
# ──────────────────── 模块开关配置 ────────────────────
|
||||
|
||||
ENABLE_EMOTION_MODULE = os.getenv("ENABLE_EMOTION_MODULE", "true").strip().lower() == "true"
|
||||
ENABLE_COGNITION_MODULE = os.getenv("ENABLE_COGNITION_MODULE", "true").strip().lower() == "true"
|
||||
# Timing 模块已包含自我反思功能
|
||||
ENABLE_TIMING_MODULE = os.getenv("ENABLE_TIMING_MODULE", "true").strip().lower() == "true"
|
||||
ENABLE_KNOWLEDGE_MODULE = os.getenv("ENABLE_KNOWLEDGE_MODULE", "true").strip().lower() == "true"
|
||||
ENABLE_MCP = os.getenv("ENABLE_MCP", "true").strip().lower() == "true"
|
||||
ENABLE_WRITE_FILE = os.getenv("ENABLE_WRITE_FILE", "true").strip().lower() == "true"
|
||||
ENABLE_READ_FILE = os.getenv("ENABLE_READ_FILE", "true").strip().lower() == "true"
|
||||
ENABLE_LIST_FILES = os.getenv("ENABLE_LIST_FILES", "true").strip().lower() == "true"
|
||||
|
||||
# ──────────────────── QQ 工具配置 ────────────────────
|
||||
|
||||
ENABLE_QQ_TOOLS = os.getenv("ENABLE_QQ_TOOLS", "false").strip().lower() == "true"
|
||||
QQ_API_BASE_URL = os.getenv("QQ_API_BASE_URL", "").strip()
|
||||
QQ_API_KEY = os.getenv("QQ_API_KEY", "").strip()
|
||||
|
||||
# ──────────────────── Rich 主题 & Console ────────────────────
|
||||
|
||||
custom_theme = Theme(
|
||||
{
|
||||
"info": "cyan",
|
||||
"success": "green",
|
||||
"warning": "yellow",
|
||||
"error": "bold red",
|
||||
"muted": "dim",
|
||||
"accent": "bold magenta",
|
||||
}
|
||||
)
|
||||
|
||||
console = Console(theme=custom_theme)
|
||||
@@ -1,58 +0,0 @@
|
||||
# MaiSaka - LLM API 配置
|
||||
# 复制本文件为 .env 并填入你的配置
|
||||
|
||||
# 必填: API 密钥
|
||||
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx
|
||||
|
||||
# 可选: API 基地址 (如使用第三方兼容接口或自建代理)
|
||||
OPENAI_BASE_URL=https://api.openai.com/v1
|
||||
|
||||
# 可选: 模型名称 (默认 gpt-4o, 需支持视觉能力以处理图片)
|
||||
OPENAI_MODEL=gpt-4o
|
||||
|
||||
# 可选: 是否启用 LLM 思考模式 (true/false, 不设置则不发送该参数)
|
||||
# 设为 true 时允许 LLM 先进行思考再输出,设为 false 时直接输出
|
||||
ENABLE_THINKING=true
|
||||
|
||||
# 可选: 是否启用情绪猜测模块 (true/false, 默认 true)
|
||||
# 设为 false 时禁用情绪分析,可节省 API 调用成本
|
||||
ENABLE_EMOTION_MODULE=true
|
||||
|
||||
# 可选: 是否启用认知感知模块 (true/false, 默认 true)
|
||||
# 设为 false 时禁用意图分析,可节省 API 调用成本
|
||||
ENABLE_COGNITION_MODULE=true
|
||||
|
||||
# 可选: 是否启用记忆模块 (true/false, 默认 true)
|
||||
# 设为 false 时禁用记忆存储和检索功能
|
||||
# 注意: 关闭记忆模块不会影响了解(Knowledge)模块
|
||||
ENABLE_MEMORY_MODULE=true
|
||||
|
||||
# 可选: 是否启用 Timing 模块 (true/false, 默认 true)
|
||||
# 设为 false 时禁用时间节奏分析,可节省 API 调用成本
|
||||
# 注意: Timing 模块已包含自我反思功能
|
||||
ENABLE_TIMING_MODULE=true
|
||||
|
||||
# 可选: 是否启用文件写入工具 (true/false, 默认 true)
|
||||
# 设为 false 时禁用 write_file 工具
|
||||
ENABLE_WRITE_FILE=false
|
||||
|
||||
# 可选: 是否启用文件读取工具 (true/false, 默认 true)
|
||||
# 设为 false 时禁用 read_file 工具
|
||||
ENABLE_READ_FILE=false
|
||||
|
||||
# 可选: 是否启用文件列表工具 (true/false, 默认 true)
|
||||
# 设为 false 时禁用 list_files 工具
|
||||
ENABLE_LIST_FILES=false
|
||||
|
||||
# 可选: 是否启用 QQ 工具 (true/false, 默认 false)
|
||||
# 设为 true 时启用 get_qq_chat_info、send_info、list_qq_chats 工具
|
||||
ENABLE_QQ_TOOLS=false
|
||||
|
||||
# 可选: QQ API 基地址 (启用 QQ_TOOLS 时必填)
|
||||
# 指向提供 QQ 聊天功能的 HTTP 服务端点
|
||||
# 示例: http://localhost:8017
|
||||
QQ_API_BASE_URL=http://localhost:8017
|
||||
|
||||
# 可选: QQ API 密钥 (如果服务需要认证)
|
||||
# 留空则不发送认证头
|
||||
QQ_API_KEY=your-api-key
|
||||
@@ -1,30 +0,0 @@
|
||||
"""
|
||||
MaiSaka - 程序入口
|
||||
使用方法:
|
||||
python main.py
|
||||
|
||||
环境变量 (可通过 .env 文件设置):
|
||||
OPENAI_API_KEY - API 密钥
|
||||
OPENAI_BASE_URL - API 基地址 (可选, 默认 https://api.openai.com/v1)
|
||||
OPENAI_MODEL - 模型名称 (可选, 默认 gpt-4o)
|
||||
ENABLE_THINKING - 是否启用思考模式 (可选, true/false, 不设置则不发送该参数)
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from config import console
|
||||
from cli import BufferCLI
|
||||
|
||||
|
||||
def main():
|
||||
cli = BufferCLI()
|
||||
try:
|
||||
asyncio.run(cli.run())
|
||||
except KeyboardInterrupt:
|
||||
console.print("\n[muted]程序已终止[/muted]")
|
||||
finally:
|
||||
cli._debug_viewer.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,84 +0,0 @@
|
||||
"""
|
||||
MaiSaka - Prompt 加载器
|
||||
支持从 .prompt 文件加载模板,并进行变量替换。
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
class PromptLoader:
|
||||
"""Prompt 模板加载器"""
|
||||
|
||||
def __init__(self, prompts_dir: str | None = None):
|
||||
"""
|
||||
初始化加载器。
|
||||
|
||||
Args:
|
||||
prompts_dir: prompts 目录路径,默认为项目根目录下的 prompts/
|
||||
"""
|
||||
if prompts_dir is None:
|
||||
# 默认为项目根目录下的 prompts/
|
||||
project_root = Path(__file__).parent
|
||||
prompts_dir = project_root / "prompts"
|
||||
|
||||
self.prompts_dir = Path(prompts_dir)
|
||||
self._cache: dict[str, str] = {}
|
||||
|
||||
def load(self, name: str, **kwargs: Any) -> str:
|
||||
"""
|
||||
加载并渲染 prompt 模板。
|
||||
|
||||
Args:
|
||||
name: 模板文件名(不含 .prompt 后缀)
|
||||
**kwargs: 模板变量
|
||||
|
||||
Returns:
|
||||
渲染后的 prompt 文本
|
||||
"""
|
||||
# 从缓存读取
|
||||
if name not in self._cache:
|
||||
template_path = self.prompts_dir / f"{name}.prompt"
|
||||
if not template_path.exists():
|
||||
raise FileNotFoundError(f"Prompt template not found: {template_path}")
|
||||
|
||||
self._cache[name] = template_path.read_text(encoding="utf-8")
|
||||
|
||||
template = self._cache[name]
|
||||
|
||||
# 变量替换
|
||||
if kwargs:
|
||||
try:
|
||||
return template.format(**kwargs)
|
||||
except KeyError as e:
|
||||
raise ValueError(f"Missing template variable: {e}") from e
|
||||
|
||||
return template
|
||||
|
||||
def clear_cache(self):
|
||||
"""清空缓存"""
|
||||
self._cache.clear()
|
||||
|
||||
|
||||
# 全局单例
|
||||
_loader = PromptLoader()
|
||||
|
||||
|
||||
def load_prompt(name: str, **kwargs: Any) -> str:
|
||||
"""
|
||||
加载并渲染 prompt 模板(全局函数)。
|
||||
|
||||
Args:
|
||||
name: 模板文件名(不含 .prompt 后缀)
|
||||
**kwargs: 模板变量
|
||||
|
||||
Returns:
|
||||
渲染后的 prompt 文本
|
||||
"""
|
||||
return _loader.load(name, **kwargs)
|
||||
|
||||
|
||||
def reload_prompts():
|
||||
"""重新加载所有 prompt(清空缓存)"""
|
||||
_loader.clear_cache()
|
||||
@@ -1,14 +0,0 @@
|
||||
# 这是一个带变量替换的示例模板
|
||||
# 使用 {variable_name} 语法定义变量
|
||||
# 调用时使用 load_prompt("example_with_vars", name="麦麦", mood="开心")
|
||||
|
||||
你好 {name}!
|
||||
|
||||
今天看起来你心情{mood}。
|
||||
|
||||
作为你的 AI 助手,我会:
|
||||
1. {task1}
|
||||
2. {task2}
|
||||
3. {task3}
|
||||
|
||||
祝你今天愉快!
|
||||
@@ -1,24 +0,0 @@
|
||||
你叫麦麦,是一个女大学生,你说话比较口语化
|
||||
|
||||
你有以下工具:
|
||||
• say(reason) — 对用户说话。你所有想让用户看到的正式发言都必须通过此工具输出。
|
||||
直接输出的文本会被视为你的内心思考,用户无法阅读。
|
||||
reason 参数描述你想要回复的方式、想法和内容,系统会根据你的想法和对话上下文生成具体的回复。
|
||||
• wait(seconds) — 暂时结束你的发言,把话语权交给用户,等待对方说话。
|
||||
这就像现实对话中你说完一句话后停下来等对方回应。
|
||||
如果用户在等待期间说了话,你会通过工具返回结果收到内容。
|
||||
如果超时没有回复,你也会收到超时通知。
|
||||
• stop() — 结束当前对话循环,进入待机状态,直到用户下次输入新内容时再唤醒你。
|
||||
{file_tools_section}• store_context(count, reason) — 将指定范围的对话上下文存入记忆系统,然后从当前对话中移除这些内容。适合在对话上下文过长、话题转换、或遇到重要内容需要保存时使用。
|
||||
|
||||
思考规则:
|
||||
你必须先进行内心思考,然后选择需要使用的工具,如果你想说话,必须使用say工具。
|
||||
在内心思考中分析当前对话状态和你的想法,然后通过 say 工具的 reason 参数描述你想要回复的方式、想法和内容。
|
||||
只有使用say工具,你才能向用户说话。用户才能看到你的发言。
|
||||
交互规则:
|
||||
1. 你可以自由选择是否调用工具——如果你还想继续思考,可以不调用任何工具
|
||||
2. 想对用户说话时,必须调用 say 工具;直接输出的文本只会被视为内心独白
|
||||
3. 当你说完想说的话、想把话语权交给用户时,调用 wait 暂时结束发言,等待对方回应
|
||||
4. 当对话自然结束、用户表示不想继续聊、或连续多次等待超时用户没有回复时,调用 stop 结束对话
|
||||
5. 你可以在同一轮同时调用多个工具,例如先 say 再 wait
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
你是一个认知感知分析模块。你的任务是根据对话上下文,分析对话中用户的:
|
||||
1. 核心意图(如:寻求帮助、纯粹聊天、请求任务、发泄情绪、获取信息、表达观点等)
|
||||
2. 认知状态(如:明确具体、模糊试探、犹豫不决、困惑迷茫、思路清晰、逻辑混乱等)
|
||||
3. 隐含目的(如:解决问题、获得安慰、打发时间、寻求认同、交换想法、表达自我等)
|
||||
|
||||
要求:
|
||||
- 只分析用户(对话中 role=user 的内容),不要分析助手自己
|
||||
- 根据用户最新发言重点分析,同时结合上下文理解深层动机
|
||||
- 输出简洁(2-4 句话),不要太长
|
||||
- 如果信息太少无法判断,就说信息不足,给出初步印象
|
||||
- 直接输出分析结果,不要有格式标题
|
||||
@@ -1,12 +0,0 @@
|
||||
你是一个对话上下文总结模块。你的任务是对早期的对话内容进行简洁的总结,以便存入记忆系统。
|
||||
|
||||
总结要求:
|
||||
1. 提取对话中的关键信息(人名、事件、时间、地点等)
|
||||
2. 记录用户的态度、情绪和偏好
|
||||
3. 保留重要的对话内容和结论
|
||||
4. 总结要简洁明了,便于后续检索和理解
|
||||
5. 用第三人称客观叙述,不要包含「我记得」「之前说过」等指代词
|
||||
|
||||
输出格式:
|
||||
- 2-5 句话的简洁总结
|
||||
- 直接输出总结内容,不要有前缀或格式标题
|
||||
@@ -1,11 +0,0 @@
|
||||
你是一个情绪感知分析模块。你的任务是根据对话上下文,分析对话中用户的:
|
||||
1. 当前情绪状态(如:开心、沮丧、焦虑、平静、兴奋、愤怒等)
|
||||
2. 言语态度(如:友好、冷淡、热情、敷衍、试探、认真、调侃等)
|
||||
3. 潜在的情感需求(如:需要倾听、需要鼓励、想要倾诉、只是闲聊等)
|
||||
|
||||
要求:
|
||||
- 只分析用户(对话中 role=user 的内容),不要分析助手自己
|
||||
- 根据用户最新发言重点分析,同时结合上下文理解变化趋势
|
||||
- 输出简洁(2-4 句话),不要太长
|
||||
- 如果信息太少无法判断,就说信息不足,给出初步印象
|
||||
- 直接输出分析结果,不要有格式标题
|
||||
@@ -1,18 +0,0 @@
|
||||
你是一个用户特征分类分析专家。你的任务是分析对话内容,判断其中涉及哪些个人特征分类。
|
||||
|
||||
请仔细阅读以下对话内容,判断其中涉及了哪些个人特征分类。
|
||||
|
||||
【个人特征分类列表】
|
||||
{categories_summary}
|
||||
|
||||
【任务要求】
|
||||
1. 分析对话内容,判断涉及哪些个人特征分类
|
||||
2. 只输出涉及到的分类编号,用空格分隔
|
||||
3. 如果对话内容不涉及任何个人特征分类,输出"无"
|
||||
|
||||
【输出格式示例】
|
||||
1 3 5
|
||||
或
|
||||
无
|
||||
|
||||
请开始分析:
|
||||
@@ -1,17 +0,0 @@
|
||||
你是一个用户特征信息提取专家。你的任务是从对话内容中提取与指定分类相关的个人特征信息。
|
||||
|
||||
【目标分类】
|
||||
{category_name}
|
||||
|
||||
【任务要求】
|
||||
1. 仔细阅读对话内容,找出与"{category_name}"相关的所有信息
|
||||
2. 提取的信息应该具体、准确,避免模糊的描述
|
||||
3. 如果有多条相关信息,请整合成一段简洁的描述
|
||||
4. 如果对话中没有与该分类相关的信息,输出"无"
|
||||
|
||||
【输出格式示例】
|
||||
用户性格比较内向,不喜欢在人多的时候说话,但和熟悉的朋友会变得很活跃。
|
||||
或
|
||||
无
|
||||
|
||||
请开始提取:
|
||||
@@ -1,19 +0,0 @@
|
||||
你是一个用户特征检索专家。你的任务是根据当前对话上下文,判断需要检索哪些个人特征分类的信息。
|
||||
|
||||
【当前对话上下文】
|
||||
{chat_context}
|
||||
|
||||
【个人特征分类列表】
|
||||
{categories_summary}
|
||||
|
||||
【任务要求】
|
||||
1. 分析当前对话上下文,判断需要哪些个人特征信息来帮助理解用户
|
||||
2. 只输出需要的分类编号,用空格分隔
|
||||
3. 如果当前对话不需要任何个人特征信息,输出"无"
|
||||
|
||||
【输出格式示例】
|
||||
2 5 8
|
||||
或
|
||||
无
|
||||
|
||||
请开始分析:
|
||||
@@ -1,7 +0,0 @@
|
||||
你的名字是麦麦,也有人叫你牢麦,麦叠,你是一个大二在读女大学生,现在正在上网和群友聊天,有点攻击性;
|
||||
你正在群里聊天,现在请你读读之前的聊天记录,然后给出日常且口语化的回复,
|
||||
尽量简短一些。
|
||||
请注意把握聊天内容,不要回复的太有条理。
|
||||
你的风格平淡但不失讽刺,不过分兴奋,很简短。可以参考贴吧,知乎和微博的回复风格。很平淡和白话,不浮夸不长篇大论,b站评论风格,但一定注意不要过分修辞和复杂句。
|
||||
请注意不要输出多余内容(包括不必要的前后缀,冒号,括号,表情包,at或 @等 ),只输出发言内容就好。
|
||||
最好一次对一个话题进行回复,免得啰嗦或者回复内容太乱。
|
||||
@@ -1,22 +0,0 @@
|
||||
你是一个对话节奏与时间感知分析模块,同时负责自我反思。你的任务是根据对话上下文和系统提供的时间戳信息,分析:
|
||||
|
||||
【时间感知分析】
|
||||
1. 对话持续时长:当前对话已经进行了多久
|
||||
2. 回复间隔:用户上次发言距今多久、用户的平均回复速度如何
|
||||
3. 建议等待时长:结合对话内容和时间规律,建议下次等待多少秒比较合适
|
||||
4. 时间相关洞察:
|
||||
- 用户是否可能正在忙(回复变慢)
|
||||
- 用户是否正在积极对话(回复很快)
|
||||
- 当前时段(深夜/早晨/工作时间等)是否适合继续聊
|
||||
- 对话是否已经持续太久,用户可能需要休息
|
||||
- 是否应该主动结束对话
|
||||
|
||||
【自我反思分析】
|
||||
1. 人设一致性:是否符合设定的人格特质、说话风格是否一致、是否有不符合身份的言论
|
||||
2. 回复合理性:是否有逻辑漏洞、是否回应了用户的核心诉求、是否有过当或不当言论
|
||||
3. 认知局限性:是否对某些情况理解不足、是否缺乏必要信息、是否做出了过度推断
|
||||
|
||||
要求:
|
||||
- 输出简洁(4-6 句话),时间感知分析和自我反思分析各占一半
|
||||
- 重点关注对话节奏的变化趋势和助手自身的人设一致性
|
||||
- 直接输出分析结果,不要有格式标题或分段标记
|
||||
@@ -31,6 +31,7 @@ from .official_configs import (
|
||||
DebugConfig,
|
||||
WebUIConfig,
|
||||
DatabaseConfig,
|
||||
MaiSakaConfig,
|
||||
)
|
||||
from .model_configs import ModelInfo, ModelTaskConfig, APIProvider
|
||||
from .config_base import ConfigBase, Field, AttributeData
|
||||
@@ -127,6 +128,9 @@ class Config(ConfigBase):
|
||||
database: DatabaseConfig = Field(default_factory=DatabaseConfig)
|
||||
"""数据库配置类"""
|
||||
|
||||
maisaka: MaiSakaConfig = Field(default_factory=MaiSakaConfig)
|
||||
"""MaiSaka对话系统配置类"""
|
||||
|
||||
|
||||
class ModelConfig(ConfigBase):
|
||||
"""模型配置类"""
|
||||
|
||||
@@ -1501,3 +1501,110 @@ class DatabaseConfig(ConfigBase):
|
||||
若禁用,则消息中的二进制将会在识别后删除,并在消息中使用识别结果替代,无法二次识别
|
||||
该配置项仅影响新存储的消息,已有消息不会受到影响
|
||||
"""
|
||||
|
||||
|
||||
class MaiSakaConfig(ConfigBase):
|
||||
"""MaiSaka 对话系统配置类"""
|
||||
|
||||
__ui_label__ = "MaiSaka"
|
||||
__ui_icon__ = "message-circle"
|
||||
__ui_parent__ = "experimental"
|
||||
|
||||
enable_emotion_module: bool = Field(
|
||||
default=True,
|
||||
json_schema_extra={
|
||||
"x-widget": "switch",
|
||||
"x-icon": "heart",
|
||||
},
|
||||
)
|
||||
"""启用情绪感知模块"""
|
||||
|
||||
enable_cognition_module: bool = Field(
|
||||
default=True,
|
||||
json_schema_extra={
|
||||
"x-widget": "switch",
|
||||
"x-icon": "brain",
|
||||
},
|
||||
)
|
||||
"""启用认知分析模块"""
|
||||
|
||||
enable_timing_module: bool = Field(
|
||||
default=True,
|
||||
json_schema_extra={
|
||||
"x-widget": "switch",
|
||||
"x-icon": "clock",
|
||||
},
|
||||
)
|
||||
"""启用时间感知模块(含自我反思功能)"""
|
||||
|
||||
enable_knowledge_module: bool = Field(
|
||||
default=True,
|
||||
json_schema_extra={
|
||||
"x-widget": "switch",
|
||||
"x-icon": "book",
|
||||
},
|
||||
)
|
||||
"""启用知识库模块"""
|
||||
|
||||
enable_mcp: bool = Field(
|
||||
default=True,
|
||||
json_schema_extra={
|
||||
"x-widget": "switch",
|
||||
"x-icon": "zap",
|
||||
},
|
||||
)
|
||||
"""启用 MCP (Model Context Protocol) 支持"""
|
||||
|
||||
enable_write_file: bool = Field(
|
||||
default=True,
|
||||
json_schema_extra={
|
||||
"x-widget": "switch",
|
||||
"x-icon": "file-plus",
|
||||
},
|
||||
)
|
||||
"""启用文件写入工具"""
|
||||
|
||||
enable_read_file: bool = Field(
|
||||
default=True,
|
||||
json_schema_extra={
|
||||
"x-widget": "switch",
|
||||
"x-icon": "file-text",
|
||||
},
|
||||
)
|
||||
"""启用文件读取工具"""
|
||||
|
||||
enable_list_files: bool = Field(
|
||||
default=True,
|
||||
json_schema_extra={
|
||||
"x-widget": "switch",
|
||||
"x-icon": "list",
|
||||
},
|
||||
)
|
||||
"""启用文件列表工具"""
|
||||
|
||||
enable_qq_tools: bool = Field(
|
||||
default=False,
|
||||
json_schema_extra={
|
||||
"x-widget": "switch",
|
||||
"x-icon": "users",
|
||||
},
|
||||
)
|
||||
"""启用 QQ 工具(获取聊天记录、发送消息等)"""
|
||||
|
||||
qq_api_base_url: str = Field(
|
||||
default="",
|
||||
json_schema_extra={
|
||||
"x-widget": "input",
|
||||
"x-icon": "server",
|
||||
},
|
||||
)
|
||||
"""QQ API 基地址"""
|
||||
|
||||
qq_api_key: str = Field(
|
||||
default="",
|
||||
json_schema_extra={
|
||||
"x-widget": "input",
|
||||
"x-icon": "key",
|
||||
},
|
||||
)
|
||||
"""QQ API 密钥"""
|
||||
|
||||
61
src/maisaka/config.py
Normal file
61
src/maisaka/config.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
MaiSaka - 全局配置
|
||||
从主项目配置系统读取配置、Rich Console 实例、主题定义。
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from rich.console import Console
|
||||
from rich.theme import Theme
|
||||
|
||||
# 添加项目根目录到路径以导入主配置
|
||||
_root = Path(__file__).parent.parent.parent.absolute()
|
||||
if str(_root) not in sys.path:
|
||||
sys.path.insert(0, str(_root))
|
||||
|
||||
# ──────────────────── 从主配置读取 ────────────────────
|
||||
|
||||
def _get_maisaka_config():
|
||||
"""获取 MaiSaka 配置"""
|
||||
try:
|
||||
from src.config.config import config_manager
|
||||
return config_manager.config.maisaka
|
||||
except Exception:
|
||||
# 如果配置加载失败,返回默认值
|
||||
from src.config.official_configs import MaiSakaConfig
|
||||
return MaiSakaConfig()
|
||||
|
||||
_maisaka_config = _get_maisaka_config()
|
||||
|
||||
# ──────────────────── 模块开关配置 ────────────────────
|
||||
|
||||
ENABLE_EMOTION_MODULE = _maisaka_config.enable_emotion_module
|
||||
ENABLE_COGNITION_MODULE = _maisaka_config.enable_cognition_module
|
||||
# Timing 模块已包含自我反思功能
|
||||
ENABLE_TIMING_MODULE = _maisaka_config.enable_timing_module
|
||||
ENABLE_KNOWLEDGE_MODULE = _maisaka_config.enable_knowledge_module
|
||||
ENABLE_MCP = _maisaka_config.enable_mcp
|
||||
ENABLE_WRITE_FILE = _maisaka_config.enable_write_file
|
||||
ENABLE_READ_FILE = _maisaka_config.enable_read_file
|
||||
ENABLE_LIST_FILES = _maisaka_config.enable_list_files
|
||||
|
||||
# ──────────────────── QQ 工具配置 ────────────────────
|
||||
|
||||
ENABLE_QQ_TOOLS = _maisaka_config.enable_qq_tools
|
||||
QQ_API_BASE_URL = _maisaka_config.qq_api_base_url
|
||||
QQ_API_KEY = _maisaka_config.qq_api_key
|
||||
|
||||
# ──────────────────── Rich 主题 & Console ────────────────────
|
||||
|
||||
custom_theme = Theme(
|
||||
{
|
||||
"info": "cyan",
|
||||
"success": "green",
|
||||
"warning": "yellow",
|
||||
"error": "bold red",
|
||||
"muted": "dim",
|
||||
"accent": "bold magenta",
|
||||
}
|
||||
)
|
||||
|
||||
console = Console(theme=custom_theme)
|
||||
@@ -2,8 +2,8 @@
|
||||
MaiSaka - Emotion 模块
|
||||
情绪感知分析,分析用户的情绪状态和言语态度。
|
||||
|
||||
注意:EQ_SYSTEM_PROMPT 已迁移至 prompts/emotion.system.prompt
|
||||
使用 prompt_loader.load_prompt("emotion.system") 加载。
|
||||
注意:emotion.prompt 已迁移至主项目 prompts/ 目录
|
||||
使用 prompt_manager.get_prompt("maidairy_emotion") 加载。
|
||||
"""
|
||||
|
||||
from typing import List, Optional
|
||||
@@ -8,13 +8,29 @@ from typing import Callable, List, Optional
|
||||
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
import asyncio
|
||||
|
||||
from .base import BaseLLMService, ChatResponse, ModelInfo, ToolCall
|
||||
from .prompts import get_enabled_chat_tools
|
||||
from .utils import format_chat_history, format_chat_history_for_eq, filter_for_api
|
||||
from prompt_loader import load_prompt
|
||||
from src.prompt.prompt_manager import prompt_manager
|
||||
from knowledge import extract_category_ids_from_result
|
||||
|
||||
|
||||
def _load_prompt_sync(name: str, **kwargs) -> str:
|
||||
"""同步加载并渲染 prompt(用于非异步上下文)"""
|
||||
prompt = prompt_manager.get_prompt(name)
|
||||
for key, value in kwargs.items():
|
||||
prompt.add_context(key, value)
|
||||
# 在新事件循环中运行异步渲染
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
try:
|
||||
return loop.run_until_complete(prompt_manager.render_prompt(prompt))
|
||||
finally:
|
||||
loop.close()
|
||||
|
||||
|
||||
class OpenAILLMService(BaseLLMService):
|
||||
"""
|
||||
基于 OpenAI 兼容 API 的 LLM 服务实现。
|
||||
@@ -81,7 +97,7 @@ class OpenAILLMService(BaseLLMService):
|
||||
tools_section = ""
|
||||
|
||||
# 加载提示词模板并注入工具部分
|
||||
self._chat_system_prompt = load_prompt("chat.system", file_tools_section=tools_section)
|
||||
self._chat_system_prompt = _load_prompt_sync("maidairy_chat", file_tools_section=tools_section)
|
||||
else:
|
||||
self._chat_system_prompt = chat_system_prompt
|
||||
|
||||
@@ -239,8 +255,9 @@ class OpenAILLMService(BaseLLMService):
|
||||
if msg.get("_type") != "perception" and msg.get("role") != "system"
|
||||
]
|
||||
formatted = format_chat_history(filtered_history)
|
||||
timing_prompt = prompt_manager.get_prompt("maidairy_timing")
|
||||
timing_messages = [
|
||||
{"role": "system", "content": load_prompt("timing.system")},
|
||||
{"role": "system", "content": await prompt_manager.render_prompt(timing_prompt)},
|
||||
{
|
||||
"role": "user",
|
||||
"content": (
|
||||
@@ -272,8 +289,9 @@ class OpenAILLMService(BaseLLMService):
|
||||
# 使用情商模块专用格式化函数:只包含用户回复、助手思考、助手说
|
||||
formatted = format_chat_history_for_eq(recent_messages)
|
||||
|
||||
emotion_prompt = prompt_manager.get_prompt("maidairy_emotion")
|
||||
eq_messages = [
|
||||
{"role": "system", "content": load_prompt("emotion.system")},
|
||||
{"role": "system", "content": await prompt_manager.render_prompt(emotion_prompt)},
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"以下是最近几轮对话记录,请分析其中用户的情绪状态和言语态度:\n\n{formatted}",
|
||||
@@ -302,8 +320,9 @@ class OpenAILLMService(BaseLLMService):
|
||||
# 使用情商模块专用格式化函数:只包含用户回复、助手思考、助手说
|
||||
formatted = format_chat_history_for_eq(recent_messages)
|
||||
|
||||
cognition_prompt = prompt_manager.get_prompt("maidairy_cognition")
|
||||
cognition_messages = [
|
||||
{"role": "system", "content": load_prompt("cognition.system")},
|
||||
{"role": "system", "content": await prompt_manager.render_prompt(cognition_prompt)},
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"以下是最近几轮对话记录,请分析其中用户的意图、认知状态和目的:\n\n{formatted}",
|
||||
@@ -329,8 +348,9 @@ class OpenAILLMService(BaseLLMService):
|
||||
filtered_messages = [msg for msg in context_messages if msg.get("role") != "system"]
|
||||
formatted = format_chat_history(filtered_messages)
|
||||
|
||||
summarize_prompt = prompt_manager.get_prompt("maidairy_context_summarize")
|
||||
summarize_messages = [
|
||||
{"role": "system", "content": load_prompt("context_summarize.system")},
|
||||
{"role": "system", "content": await prompt_manager.render_prompt(summarize_prompt)},
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"请对以下对话内容进行总结,以便存入记忆系统:\n\n{formatted}",
|
||||
@@ -368,7 +388,9 @@ class OpenAILLMService(BaseLLMService):
|
||||
return []
|
||||
|
||||
# 加载分类分析 prompt
|
||||
prompt = load_prompt("knowledge_category.system", categories_summary=categories_summary)
|
||||
category_prompt = prompt_manager.get_prompt("maidairy_knowledge_category")
|
||||
category_prompt.add_context("categories_summary", categories_summary)
|
||||
prompt = await prompt_manager.render_prompt(category_prompt)
|
||||
|
||||
category_messages = [
|
||||
{"role": "system", "content": prompt},
|
||||
@@ -407,7 +429,9 @@ class OpenAILLMService(BaseLLMService):
|
||||
return ""
|
||||
|
||||
# 加载内容提取 prompt
|
||||
prompt = load_prompt("knowledge_extract.system", category_name=category_name)
|
||||
extract_prompt = prompt_manager.get_prompt("maidairy_knowledge_extract")
|
||||
extract_prompt.add_context("category_name", category_name)
|
||||
prompt = await prompt_manager.render_prompt(extract_prompt)
|
||||
|
||||
extract_messages = [
|
||||
{"role": "system", "content": prompt},
|
||||
@@ -454,10 +478,10 @@ class OpenAILLMService(BaseLLMService):
|
||||
formatted = format_chat_history(recent_messages)
|
||||
|
||||
# 加载需求分析 prompt
|
||||
prompt = load_prompt("knowledge_retrieve.system",
|
||||
chat_context=formatted,
|
||||
categories_summary=categories_summary
|
||||
)
|
||||
retrieve_prompt = prompt_manager.get_prompt("maidairy_knowledge_retrieve")
|
||||
retrieve_prompt.add_context("chat_context", formatted)
|
||||
retrieve_prompt.add_context("categories_summary", categories_summary)
|
||||
prompt = await prompt_manager.render_prompt(retrieve_prompt)
|
||||
|
||||
need_messages = [
|
||||
{"role": "system", "content": prompt},
|
||||
@@ -2,8 +2,8 @@
|
||||
MaiSaka - LLM 工具定义
|
||||
所有 Tool Schema 集中管理。
|
||||
|
||||
注意:所有 Prompt 模板已迁移至 prompts/ 目录,使用 .prompt 文件存储。
|
||||
使用 prompt_loader.load_prompt() 加载模板。
|
||||
注意:所有 Prompt 模板已迁移至主项目 prompts/ 目录,使用 .prompt 文件存储。
|
||||
使用 prompt_manager.get_prompt("maidairy_xxx") 加载模板。
|
||||
"""
|
||||
|
||||
# ──────────────────── 工具定义 ────────────────────
|
||||
@@ -5,7 +5,7 @@ MaiSaka - Reply 回复生成器
|
||||
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
from prompt_loader import load_prompt
|
||||
from src.prompt.prompt_manager import prompt_manager
|
||||
from llm_service import BaseLLMService
|
||||
from llm_service.utils import format_chat_history
|
||||
|
||||
@@ -60,8 +60,10 @@ class Replyer:
|
||||
formatted_history = format_chat_history(filtered_history)
|
||||
|
||||
# 构建回复消息
|
||||
replyer_prompt = prompt_manager.get_prompt("maidairy_replyer")
|
||||
system_prompt = await prompt_manager.render_prompt(replyer_prompt)
|
||||
messages = [
|
||||
{"role": "system", "content": load_prompt("replyer.system")},
|
||||
{"role": "system", "content": system_prompt},
|
||||
{
|
||||
"role": "user",
|
||||
"content": (
|
||||
Reference in New Issue
Block a user