From fbb59823acb0aeb6c3a9bebc7bef56690d7db22b Mon Sep 17 00:00:00 2001 From: "Mr.Xia" <1424473282@qq.com> Date: Sun, 30 Aug 2026 11:08:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(kb=5Fteach):=20=E6=96=B0=E5=A2=9E=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=91=98=E7=9F=A5=E8=AF=86=E5=BA=93=E6=95=99=E5=AD=A6?= =?UTF-8?q?=E5=91=BD=E4=BB=A4(=E6=95=99=E5=AD=A6/=E6=95=99=E5=AD=A6?= =?UTF-8?q?=E5=88=97=E8=A1=A8/=E4=BF=AE=E6=AD=A3/=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=95=99=E5=AD=A6,=E4=BB=85=E8=B6=85=E7=BA=A7=E7=94=A8?= =?UTF-8?q?=E6=88=B7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- danding_bot/plugins/kb_teach.py | 146 ++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 danding_bot/plugins/kb_teach.py diff --git a/danding_bot/plugins/kb_teach.py b/danding_bot/plugins/kb_teach.py new file mode 100644 index 0000000..b186d78 --- /dev/null +++ b/danding_bot/plugins/kb_teach.py @@ -0,0 +1,146 @@ +"""管理员知识库教学命令:直接增删查 wtfllm 全局知识库。 + +用法(仅超级用户,群内 @机器人 或私聊): + 教学 标题 | 内容 + 教学列表 [关键字] + 修正 标题关键字 | 新内容 + 删除教学 标题关键字 +""" +from nonebot import on_command, logger +from nonebot.adapters.onebot.v11 import Bot, MessageEvent +from nonebot.exception import IgnoredException +from nonebot.matcher import Matcher +from nonebot.message import run_preprocessor +from nonebot.permission import SUPERUSER +from nonebot.rule import to_me + +from nonebot_plugin_wtfllm.memory.items.knowledge_base import KnowledgeEntry +from nonebot_plugin_wtfllm.utils import get_agent_id_from_bot +from nonebot_plugin_wtfllm.v_db.repositories import KnowledgeBaseRepository + +WTFLLM_ENABLED_GROUPS = {"621016172"} + + +@run_preprocessor +async def gate_kb_teach_groups(matcher, event): + if matcher.module != "kb_teach": + return + if isinstance(event, MessageEvent) and getattr(event, "group_id", None): + if str(event.group_id) not in WTFLLM_ENABLED_GROUPS: + raise IgnoredException("kb_teach 仅在指定群启用") + + +def _split_args(text: str) -> tuple[str, str] | None: + if "|" not in text: + return None + left, _, right = text.partition("|") + left, right = left.strip(), right.strip() + return (left, right) if left and right else None + + +async def _repo(bot) -> tuple[KnowledgeBaseRepository, str]: + return KnowledgeBaseRepository(), get_agent_id_from_bot(bot) + + +teach_add = on_command("教学", rule=to_me(), permission=SUPERUSER, priority=2, block=True) + + +@teach_add.handle() +async def handle_add(matcher: Matcher, event: MessageEvent, bot: Bot): + args = _split_args(event.get_plaintext().replace("教学", "", 1)) + if not args: + await matcher.finish("格式:教学 标题 | 内容") + title, content = args + repo, agent_id = await _repo(bot) + entry = KnowledgeEntry.create( + content=content, + title=title, + agent_id=agent_id, + category="规则", + tags=["manual"], + token_count=len(content), + ) + await repo.save_knowledge(entry) + logger.info(f"[kb_teach] 新增知识: {title} ({agent_id})") + await matcher.finish(f"已记录知识:【{title}】") + + +teach_list = on_command("教学列表", rule=to_me(), permission=SUPERUSER, priority=2, block=True) + + +@teach_list.handle() +async def handle_list(matcher: Matcher, event: MessageEvent, bot: Bot): + keyword = event.get_plaintext().replace("教学列表", "", 1).strip() + repo, agent_id = await _repo(bot) + entries = await repo.search_by_category(agent_id=agent_id, category="规则", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="功能帮助", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="简单使用", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="脚本进阶", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="其它", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="general", limit=100) + if keyword: + entries = [e for e in entries if keyword in e.title or keyword in e.content] + if not entries: + await matcher.finish("没有匹配的知识条目") + lines = [f"共 {len(entries)} 条(显示前 20 条):"] + for e in entries[:20]: + lines.append(f"- 【{e.title}】{e.content[:60]}...") + await matcher.finish("\n".join(lines)) + + +teach_update = on_command("修正", rule=to_me(), permission=SUPERUSER, priority=2, block=True) + + +@teach_update.handle() +async def handle_update(matcher: Matcher, event: MessageEvent, bot: Bot): + args = _split_args(event.get_plaintext().replace("修正", "", 1)) + if not args: + await matcher.finish("格式:修正 标题关键字 | 新内容") + keyword, new_content = args + repo, agent_id = await _repo(bot) + entries = await repo.search_by_category(agent_id=agent_id, category="规则", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="功能帮助", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="简单使用", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="脚本进阶", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="其它", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="general", limit=100) + hits = [e for e in entries if keyword in e.title] + if not hits: + await matcher.finish(f"没有找到标题包含「{keyword}」的条目") + for e in hits: + await repo.delete_knowledge(e.storage_id) + new_entry = KnowledgeEntry.create( + content=new_content, + title=hits[0].title, + agent_id=agent_id, + category=hits[0].category, + tags=list(hits[0].tags) + ["manual"], + token_count=len(new_content), + ) + await repo.save_knowledge(new_entry) + logger.info(f"[kb_teach] 修正 {len(hits)} 条知识: {keyword}") + await matcher.finish(f"已修正 {len(hits)} 条包含「{keyword}」的条目,新内容:【{new_entry.title}】") + + +teach_delete = on_command("删除教学", rule=to_me(), permission=SUPERUSER, priority=2, block=True) + + +@teach_delete.handle() +async def handle_delete(matcher: Matcher, event: MessageEvent, bot: Bot): + keyword = event.get_plaintext().replace("删除教学", "", 1).strip() + if not keyword: + await matcher.finish("格式:删除教学 标题关键字") + repo, agent_id = await _repo(bot) + entries = await repo.search_by_category(agent_id=agent_id, category="规则", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="功能帮助", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="简单使用", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="脚本进阶", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="其它", limit=100) + entries += await repo.search_by_category(agent_id=agent_id, category="general", limit=100) + hits = [e for e in entries if keyword in e.title] + if not hits: + await matcher.finish(f"没有找到标题包含「{keyword}」的条目") + for e in hits: + await repo.delete_knowledge(e.storage_id) + logger.info(f"[kb_teach] 删除 {len(hits)} 条知识: {keyword}") + await matcher.finish(f"已删除 {len(hits)} 条包含「{keyword}」的条目")