mirror of https://github.com/Mai-with-u/MaiBot.git
feat:优化记忆查询表现
parent
a392608b7e
commit
43754b5c18
|
|
@ -98,8 +98,13 @@ class QAManager:
|
|||
|
||||
return result, ppr_node_weights
|
||||
|
||||
async def get_knowledge(self, question: str) -> Optional[str]:
|
||||
"""获取知识"""
|
||||
async def get_knowledge(self, question: str, limit: int = 5) -> Optional[str]:
|
||||
"""获取知识
|
||||
|
||||
Args:
|
||||
question: 查询问题
|
||||
limit: 返回的相关知识条数
|
||||
"""
|
||||
# 处理查询
|
||||
processed_result = await self.process_query(question)
|
||||
if processed_result is not None:
|
||||
|
|
@ -109,6 +114,8 @@ class QAManager:
|
|||
logger.debug("知识库查询结果为空,可能是知识库中没有相关内容")
|
||||
return None
|
||||
|
||||
limit = max(1, limit) if isinstance(limit, int) else 5
|
||||
|
||||
knowledge = [
|
||||
(
|
||||
self.embed_manager.paragraphs_embedding_store.store[res[0]].str,
|
||||
|
|
@ -116,9 +123,18 @@ class QAManager:
|
|||
)
|
||||
for res in query_res
|
||||
]
|
||||
found_knowledge = "\n".join(
|
||||
[f"第{i + 1}条知识:{k[0]}\n 该条知识对于问题的相关性:{k[1]}" for i, k in enumerate(knowledge)]
|
||||
)
|
||||
|
||||
# max_score = max([k[1] for k in knowledge]) if knowledge else None
|
||||
selected_knowledge = knowledge[:limit]
|
||||
|
||||
formatted_knowledge = [
|
||||
f"第{i + 1}条知识:{k[0]}\n 该条知识对于问题的相关性:{k[1]}"
|
||||
for i, k in enumerate(selected_knowledge)
|
||||
]
|
||||
# if max_score is not None:
|
||||
# formatted_knowledge.insert(0, f"最高相关系数:{max_score}")
|
||||
|
||||
found_knowledge = "\n".join(formatted_knowledge)
|
||||
if len(found_knowledge) > MAX_KNOWLEDGE_LENGTH:
|
||||
found_knowledge = found_knowledge[:MAX_KNOWLEDGE_LENGTH] + "\n"
|
||||
return found_knowledge
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from src.plugin_system.apis import llm_api
|
|||
from src.common.database.database_model import ThinkingBack
|
||||
from json_repair import repair_json
|
||||
from src.memory_system.retrieval_tools import get_tool_registry, init_all_tools
|
||||
from src.memory_system.retrieval_tools.query_lpmm_knowledge import query_lpmm_knowledge
|
||||
from src.llm_models.payload_content.message import MessageBuilder, RoleType, Message
|
||||
|
||||
logger = get_logger("memory_retrieval")
|
||||
|
|
@ -63,20 +64,17 @@ def init_memory_retrieval_prompt():
|
|||
1. 对话中是否提到了过去发生的事情、人物、事件或信息
|
||||
2. 是否有需要回忆的内容(比如"之前说过"、"上次"、"以前"等)
|
||||
3. 是否有需要查找历史信息的问题
|
||||
4. 是否需要查找某人的信息(person: 如果对话中提到人名、昵称、用户ID等,需要查询该人物的详细信息)
|
||||
5. 是否有问题可以搜集信息帮助你聊天
|
||||
6. 对话中是否包含黑话、俚语、缩写等可能需要查询的概念
|
||||
4. 是否有问题可以搜集信息帮助你聊天
|
||||
5. 对话中是否包含黑话、俚语、缩写等可能需要查询的概念
|
||||
|
||||
重要提示:
|
||||
- **每次只能提出一个问题**,选择最需要查询的关键问题
|
||||
- 如果"最近已查询的问题和结果"中已经包含了类似的问题,请避免重复生成相同或相似的问题
|
||||
- 如果"最近已查询的问题和结果"中已经包含了类似的问题并得到了答案,请避免重复生成相同或相似的问题,不需要重复查询
|
||||
- 如果之前已经查询过某个问题但未找到答案,可以尝试用不同的方式提问或更具体的问题
|
||||
- 如果之前已经查询过某个问题并找到了答案,可以直接参考已有结果,不需要重复查询
|
||||
|
||||
如果你认为需要从记忆中检索信息来回答,请:
|
||||
1. 先识别对话中可能需要查询的概念(黑话/俚语/缩写/专有名词等关键词),放入"concepts"字段
|
||||
2. 识别对话中提到的人物名称(人名、昵称等),放入"person"字段
|
||||
3. 然后根据上下文提出**一个**最关键的问题来帮助你回复目标消息,放入"questions"字段
|
||||
1. 识别对话中可能需要查询的概念(黑话/俚语/缩写/专有名词等关键词),放入"concepts"字段
|
||||
2. 根据上下文提出**一个**最关键的问题来帮助你回复目标消息,放入"questions"字段
|
||||
|
||||
问题格式示例:
|
||||
- "xxx在前几天干了什么"
|
||||
|
|
@ -84,17 +82,11 @@ def init_memory_retrieval_prompt():
|
|||
- "xxxx和xxx的关系是什么"
|
||||
- "xxx在某个时间点发生了什么"
|
||||
|
||||
请输出JSON格式,包含三个字段:
|
||||
- "concepts": 需要检索的概念列表(字符串数组),如果不需要检索概念则输出空数组[]
|
||||
- "person": 需要查询的人物名称列表(字符串数组),如果不需要查询人物信息则输出空数组[]
|
||||
- "questions": 问题数组(字符串数组),如果不需要检索记忆则输出空数组[],如果需要检索则只输出包含一个问题的数组
|
||||
|
||||
输出格式示例(需要检索时):
|
||||
```json
|
||||
{{
|
||||
"concepts": ["AAA", "BBB", "CCC"],
|
||||
"person": ["张三", "李四"],
|
||||
"questions": ["张三在前几天干了什么"]
|
||||
"concepts": ["AAA", "BBB", "CCC"], #需要检索的概念列表(字符串数组),如果不需要检索概念则输出空数组[]
|
||||
"questions": ["张三在前几天干了什么"] #问题数组(字符串数组),如果不需要检索记忆则输出空数组[],如果需要检索则只输出包含一个问题的数组
|
||||
}}
|
||||
```
|
||||
|
||||
|
|
@ -102,7 +94,6 @@ def init_memory_retrieval_prompt():
|
|||
```json
|
||||
{{
|
||||
"concepts": [],
|
||||
"person": [],
|
||||
"questions": []
|
||||
}}
|
||||
```
|
||||
|
|
@ -114,10 +105,8 @@ def init_memory_retrieval_prompt():
|
|||
|
||||
# 第二步:ReAct Agent prompt(使用function calling,要求先思考再行动)
|
||||
Prompt(
|
||||
"""
|
||||
你的名字是{bot_name}。现在是{time_now}。
|
||||
"""你的名字是{bot_name}。现在是{time_now}。
|
||||
你正在参与聊天,你需要搜集信息来回答问题,帮助你参与聊天。
|
||||
你需要通过思考(Think)、行动(Action)、观察(Observation)的循环来回答问题。
|
||||
|
||||
**重要限制:**
|
||||
- 最大查询轮数:{max_iterations}轮(当前第{current_iteration}轮,剩余{remaining_iterations}轮)
|
||||
|
|
@ -130,7 +119,6 @@ def init_memory_retrieval_prompt():
|
|||
{collected_info}
|
||||
|
||||
**执行步骤:**
|
||||
|
||||
**第一步:思考(Think)**
|
||||
在思考中分析:
|
||||
- 当前信息是否足够回答问题?
|
||||
|
|
@ -139,6 +127,10 @@ def init_memory_retrieval_prompt():
|
|||
- **如果已有信息不足或无法找到答案**,在思考中给出:not_enough_info(reason="信息不足或无法找到答案的原因")
|
||||
|
||||
**第二步:行动(Action)**
|
||||
- 如果涉及过往事件,可以使用聊天记录查询工具查询过往事件
|
||||
- 如果涉及概念,可以用jargon查询,或根据关键词检索聊天记录
|
||||
- 如果涉及人物,可以使用人物信息查询工具查询人物信息
|
||||
- 如果不确定查询类别,也可以使用lpmm知识库查询
|
||||
- 如果信息不足且需要继续查询,说明最需要查询什么,并输出为纯文本说明,然后调用相应工具查询(可并行调用多个工具)
|
||||
|
||||
**重要规则:**
|
||||
|
|
@ -151,14 +143,8 @@ def init_memory_retrieval_prompt():
|
|||
|
||||
# 额外,如果最后一轮迭代:ReAct Agent prompt(使用function calling,要求先思考再行动)
|
||||
Prompt(
|
||||
"""
|
||||
你的名字是{bot_name}。现在是{time_now}。
|
||||
你正在参与聊天,你需要搜集信息来回答问题,帮助你参与聊天。
|
||||
|
||||
**重要限制:**
|
||||
- 你已经经过几轮查询,尝试了信息搜集,现在你需要总结信息,选择回答问题或判断问题无法回答
|
||||
- 思考要简短,直接切入要点
|
||||
- 必须严格使用检索到的信息回答问题,不要编造信息
|
||||
"""你的名字是{bot_name}。现在是{time_now}。
|
||||
你正在参与聊天,你需要根据搜集到的信息判断问题是否可以回答问题。
|
||||
|
||||
当前问题:{question}
|
||||
已收集的信息:
|
||||
|
|
@ -171,6 +157,9 @@ def init_memory_retrieval_prompt():
|
|||
- **如果信息不足或无法找到答案**,在思考中给出:not_enough_info(reason="信息不足或无法找到答案的原因")
|
||||
|
||||
**重要规则:**
|
||||
- 你已经经过几轮查询,尝试了信息搜集,现在你需要总结信息,选择回答问题或判断问题无法回答
|
||||
- 必须严格使用检索到的信息回答问题,不要编造信息
|
||||
- 答案必须精简,不要过多解释
|
||||
- **只有在检索到明确、具体的答案时,才使用found_answer**
|
||||
- **如果信息不足、无法确定、找不到相关信息,必须使用not_enough_info,不要使用found_answer**
|
||||
- 答案必须给出,格式为 found_answer(answer="...") 或 not_enough_info(reason="...")。
|
||||
|
|
@ -308,46 +297,6 @@ async def _retrieve_concepts_with_jargon(
|
|||
return ""
|
||||
|
||||
|
||||
async def _retrieve_persons_info(
|
||||
persons: List[str],
|
||||
chat_id: str
|
||||
) -> str:
|
||||
"""对人物列表进行信息检索
|
||||
|
||||
Args:
|
||||
persons: 人物名称列表
|
||||
chat_id: 聊天ID
|
||||
|
||||
Returns:
|
||||
str: 检索结果字符串
|
||||
"""
|
||||
if not persons:
|
||||
return ""
|
||||
|
||||
from src.memory_system.retrieval_tools.query_person_info import query_person_info
|
||||
|
||||
results = []
|
||||
for person in persons:
|
||||
person = person.strip()
|
||||
if not person:
|
||||
continue
|
||||
|
||||
try:
|
||||
person_info = await query_person_info(person)
|
||||
if person_info and "未找到" not in person_info:
|
||||
results.append(f"【{person}】\n{person_info}")
|
||||
logger.info(f"查询到人物信息: {person}")
|
||||
else:
|
||||
# 未找到时不插入占位信息
|
||||
logger.info(f"未找到人物信息: {person}")
|
||||
except Exception as e:
|
||||
logger.error(f"查询人物信息失败: {person}, 错误: {e}")
|
||||
|
||||
if results:
|
||||
return "【人物信息检索结果】\n" + "\n\n".join(results) + "\n"
|
||||
return ""
|
||||
|
||||
|
||||
async def _react_agent_solve_question(
|
||||
question: str,
|
||||
chat_id: str,
|
||||
|
|
@ -468,11 +417,10 @@ async def _react_agent_solve_question(
|
|||
content_type = "未知"
|
||||
|
||||
# 构建单条消息的日志信息
|
||||
msg_info = f"\n[消息 {idx}] 角色: {role_name}"
|
||||
msg_info += f"\n 内容类型: {content_type}"
|
||||
msg_info = f"\n[消息 {idx}] 角色: {role_name} 内容类型: {content_type}\n========================================"
|
||||
|
||||
if full_content:
|
||||
msg_info += f"\n 内容:\n{full_content}"
|
||||
msg_info += f"\n{full_content}"
|
||||
|
||||
if msg.tool_calls:
|
||||
msg_info += f"\n 工具调用: {len(msg.tool_calls)}个"
|
||||
|
|
@ -1014,6 +962,22 @@ async def _process_single_question(
|
|||
logger.info(f"开始处理问题: {question}")
|
||||
|
||||
_cleanup_stale_not_found_thinking_back()
|
||||
|
||||
question_initial_info = initial_info or ""
|
||||
|
||||
# 预先进行一次LPMM知识库查询,作为后续ReAct Agent的辅助信息
|
||||
if global_config.lpmm_knowledge.enable:
|
||||
try:
|
||||
lpmm_result = await query_lpmm_knowledge(question, limit=2)
|
||||
if lpmm_result and lpmm_result.startswith("你从LPMM知识库中找到"):
|
||||
if question_initial_info:
|
||||
question_initial_info += "\n"
|
||||
question_initial_info += f"【LPMM知识库预查询】\n{lpmm_result}"
|
||||
logger.info(f"LPMM预查询命中,问题: {question[:50]}...")
|
||||
else:
|
||||
logger.info(f"LPMM预查询未命中或未找到信息,问题: {question[:50]}...")
|
||||
except Exception as e:
|
||||
logger.error(f"LPMM预查询失败,问题: {question[:50]}... 错误: {e}")
|
||||
|
||||
# 先检查thinking_back数据库中是否有现成答案
|
||||
cached_result = _query_thinking_back(chat_id, question)
|
||||
|
|
@ -1051,7 +1015,7 @@ async def _process_single_question(
|
|||
chat_id=chat_id,
|
||||
max_iterations=global_config.memory.max_agent_iterations,
|
||||
timeout=120.0,
|
||||
initial_info=initial_info
|
||||
initial_info=question_initial_info
|
||||
)
|
||||
|
||||
# 存储到数据库(超时时不存储)
|
||||
|
|
@ -1132,10 +1096,9 @@ async def build_memory_retrieval_prompt(
|
|||
logger.error(f"LLM生成问题失败: {response}")
|
||||
return ""
|
||||
|
||||
# 解析概念列表、人物列表和问题列表
|
||||
concepts, persons, questions = _parse_questions_json(response)
|
||||
# 解析概念列表和问题列表
|
||||
concepts, questions = _parse_questions_json(response)
|
||||
logger.info(f"解析到 {len(concepts)} 个概念: {concepts}")
|
||||
logger.info(f"解析到 {len(persons)} 个人物: {persons}")
|
||||
logger.info(f"解析到 {len(questions)} 个问题: {questions}")
|
||||
|
||||
# 对概念进行jargon检索,作为初始信息
|
||||
|
|
@ -1149,22 +1112,13 @@ async def build_memory_retrieval_prompt(
|
|||
else:
|
||||
logger.info("概念检索未找到任何结果")
|
||||
|
||||
# 对人物进行信息检索,添加到初始信息
|
||||
if persons:
|
||||
logger.info(f"开始对 {len(persons)} 个人物进行信息检索")
|
||||
person_info = await _retrieve_persons_info(persons, chat_id)
|
||||
if person_info:
|
||||
initial_info += person_info
|
||||
logger.info(f"人物信息检索完成,结果: {person_info[:200]}...")
|
||||
else:
|
||||
logger.info("人物信息检索未找到任何结果")
|
||||
|
||||
# 获取缓存的记忆(与question时使用相同的时间窗口和数量限制)
|
||||
cached_memories = _get_cached_memories(chat_id, time_window_seconds=300.0)
|
||||
|
||||
if not questions:
|
||||
logger.debug("模型认为不需要检索记忆或解析失败")
|
||||
# 即使没有当次查询,也返回缓存的记忆、概念检索结果和人物信息检索结果
|
||||
# 即使没有当次查询,也返回缓存的记忆和概念检索结果
|
||||
all_results = []
|
||||
if initial_info:
|
||||
all_results.append(initial_info.strip())
|
||||
|
|
@ -1174,7 +1128,7 @@ async def build_memory_retrieval_prompt(
|
|||
if all_results:
|
||||
retrieved_memory = "\n\n".join(all_results)
|
||||
end_time = time.time()
|
||||
logger.info(f"无当次查询,返回缓存记忆、概念检索和人物信息检索结果,耗时: {(end_time - start_time):.3f}秒")
|
||||
logger.info(f"无当次查询,返回缓存记忆和概念检索结果,耗时: {(end_time - start_time):.3f}秒")
|
||||
return f"你回忆起了以下信息:\n{retrieved_memory}\n如果与回复内容相关,可以参考这些回忆的信息。\n"
|
||||
else:
|
||||
return ""
|
||||
|
|
@ -1236,14 +1190,14 @@ async def build_memory_retrieval_prompt(
|
|||
return ""
|
||||
|
||||
|
||||
def _parse_questions_json(response: str) -> Tuple[List[str], List[str], List[str]]:
|
||||
"""解析问题JSON,返回概念列表、人物列表和问题列表
|
||||
def _parse_questions_json(response: str) -> Tuple[List[str], List[str]]:
|
||||
"""解析问题JSON,返回概念列表和问题列表
|
||||
|
||||
Args:
|
||||
response: LLM返回的响应
|
||||
|
||||
Returns:
|
||||
Tuple[List[str], List[str], List[str]]: (概念列表, 人物列表, 问题列表)
|
||||
Tuple[List[str], List[str]]: (概念列表, 问题列表)
|
||||
"""
|
||||
try:
|
||||
# 尝试提取JSON(可能包含在```json代码块中)
|
||||
|
|
@ -1262,30 +1216,26 @@ def _parse_questions_json(response: str) -> Tuple[List[str], List[str], List[str
|
|||
# 解析JSON
|
||||
parsed = json.loads(repaired_json)
|
||||
|
||||
# 只支持新格式:包含concepts、person和questions的对象
|
||||
# 只支持新格式:包含concepts和questions的对象
|
||||
if not isinstance(parsed, dict):
|
||||
logger.warning(f"解析的JSON不是对象格式: {parsed}")
|
||||
return [], [], []
|
||||
return [], []
|
||||
|
||||
concepts_raw = parsed.get("concepts", [])
|
||||
persons_raw = parsed.get("person", [])
|
||||
questions_raw = parsed.get("questions", [])
|
||||
|
||||
# 确保是列表
|
||||
if not isinstance(concepts_raw, list):
|
||||
concepts_raw = []
|
||||
if not isinstance(persons_raw, list):
|
||||
persons_raw = []
|
||||
if not isinstance(questions_raw, list):
|
||||
questions_raw = []
|
||||
|
||||
# 确保所有元素都是字符串
|
||||
concepts = [c for c in concepts_raw if isinstance(c, str) and c.strip()]
|
||||
persons = [p for p in persons_raw if isinstance(p, str) and p.strip()]
|
||||
questions = [q for q in questions_raw if isinstance(q, str) and q.strip()]
|
||||
|
||||
return concepts, persons, questions
|
||||
return concepts, questions
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"解析问题JSON失败: {e}, 响应内容: {response[:200]}...")
|
||||
return [], [], []
|
||||
return [], []
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ def register_tool():
|
|||
"""注册工具"""
|
||||
register_memory_retrieval_tool(
|
||||
name="query_chat_history",
|
||||
description="根据时间或关键词在chat_history表的聊天记录概述库中查询。可以查询某个时间点发生了什么、某个时间范围内的事件,或根据关键词搜索消息概述。支持两种匹配模式:模糊匹配(默认,只要包含任意一个关键词即匹配)和全匹配(必须包含所有关键词才匹配)",
|
||||
description="根据时间或关键词在聊天记录中查询。可以查询某个时间点发生了什么、某个时间范围内的事件,或根据关键词搜索消息概述。支持两种匹配模式:模糊匹配(默认,只要包含任意一个关键词即匹配)和全匹配(必须包含所有关键词才匹配)",
|
||||
parameters=[
|
||||
{
|
||||
"name": "keyword",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from .tool_registry import register_memory_retrieval_tool
|
|||
logger = get_logger("memory_retrieval_tools")
|
||||
|
||||
|
||||
async def query_lpmm_knowledge(query: str) -> str:
|
||||
async def query_lpmm_knowledge(query: str, limit: int = 5) -> str:
|
||||
"""在LPMM知识库中查询相关信息
|
||||
|
||||
Args:
|
||||
|
|
@ -24,6 +24,12 @@ async def query_lpmm_knowledge(query: str) -> str:
|
|||
if not content:
|
||||
return "查询关键词为空"
|
||||
|
||||
try:
|
||||
limit_value = int(limit)
|
||||
except (TypeError, ValueError):
|
||||
limit_value = 5
|
||||
limit_value = max(1, limit_value)
|
||||
|
||||
if not global_config.lpmm_knowledge.enable:
|
||||
logger.debug("LPMM知识库未启用")
|
||||
return "LPMM知识库未启用"
|
||||
|
|
@ -33,7 +39,7 @@ async def query_lpmm_knowledge(query: str) -> str:
|
|||
logger.debug("LPMM知识库未初始化,跳过查询")
|
||||
return "LPMM知识库未初始化"
|
||||
|
||||
knowledge_info = await qa_manager.get_knowledge(content)
|
||||
knowledge_info = await qa_manager.get_knowledge(content, limit=limit_value)
|
||||
logger.debug(f"LPMM知识库查询结果: {knowledge_info}")
|
||||
|
||||
if knowledge_info:
|
||||
|
|
@ -57,7 +63,13 @@ def register_tool():
|
|||
"type": "string",
|
||||
"description": "需要查询的关键词或问题",
|
||||
"required": True,
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"type": "integer",
|
||||
"description": "希望返回的相关知识条数,默认为5",
|
||||
"required": False,
|
||||
},
|
||||
],
|
||||
execute_func=query_lpmm_knowledge,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class SearchKnowledgeFromLPMMTool(BaseTool):
|
|||
description = "从知识库中搜索相关信息,如果你需要知识,就使用这个工具"
|
||||
parameters = [
|
||||
("query", ToolParamType.STRING, "搜索查询关键词", True, None),
|
||||
("limit", ToolParamType.INTEGER, "希望返回的相关知识条数,默认5", False, 5),
|
||||
]
|
||||
available_for_llm = global_config.lpmm_knowledge.enable
|
||||
|
||||
|
|
@ -29,6 +30,12 @@ class SearchKnowledgeFromLPMMTool(BaseTool):
|
|||
"""
|
||||
try:
|
||||
query: str = function_args.get("query") # type: ignore
|
||||
limit = function_args.get("limit", 5)
|
||||
try:
|
||||
limit_value = int(limit)
|
||||
except (TypeError, ValueError):
|
||||
limit_value = 5
|
||||
limit_value = max(1, limit_value)
|
||||
# threshold = function_args.get("threshold", 0.4)
|
||||
|
||||
# 检查LPMM知识库是否启用
|
||||
|
|
@ -38,7 +45,7 @@ class SearchKnowledgeFromLPMMTool(BaseTool):
|
|||
|
||||
# 调用知识库搜索
|
||||
|
||||
knowledge_info = await qa_manager.get_knowledge(query)
|
||||
knowledge_info = await qa_manager.get_knowledge(query, limit=limit_value)
|
||||
|
||||
logger.debug(f"知识库查询结果: {knowledge_info}")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[inner]
|
||||
version = "6.21.5"
|
||||
version = "6.21.6"
|
||||
|
||||
#----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
|
||||
#如果你想要修改配置文件,请递增version的值
|
||||
|
|
@ -104,7 +104,7 @@ talk_value_rules = [
|
|||
include_planner_reasoning = false # 是否将planner推理加入replyer,默认关闭(不加入)
|
||||
|
||||
[memory]
|
||||
max_agent_iterations = 5 # 记忆思考深度(最低为1(不深入思考))
|
||||
max_agent_iterations = 3 # 记忆思考深度(最低为1(不深入思考))
|
||||
|
||||
[jargon]
|
||||
all_global = true # 是否开启全局黑话模式,注意,此功能关闭后,已经记录的全局黑话不会改变,需要手动删除
|
||||
|
|
|
|||
Loading…
Reference in New Issue