From 8824f0630f78e0090542d38b3a506e1adc0201f7 Mon Sep 17 00:00:00 2001 From: chenxiangtong Date: Wed, 15 Apr 2026 18:25:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0llm=20call=20tool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/main.py b/main.py index 99f39bb..8e38c23 100644 --- a/main.py +++ b/main.py @@ -314,6 +314,164 @@ class BangumiPlugin(Star): result = await self.subscription_service.unsubscribe(session_key, query) yield event.plain_result(result) + # --- LLM Tool 区 --- + + @filter.llm_tool(name="bangumi_search") + async def llm_search( + self, event: AstrMessageEvent, query: str, top_k: int = 1 + ) -> AsyncGenerator[object, None]: + """在 Bangumi 数据库中全类别搜索番剧、漫画、游戏等条目,展示条目简介、评分等信息。当用户询问某个作品的详情、评价、评分时调用。 + + Args: + query(string): 搜索关键词,如作品名称 + top_k(number): 返回结果数量,默认 1,最多 5 + """ + if not self.search_service: + yield event.plain_result("❌ 搜索服务未就绪") + return + async for result in self.search_service.handle_subject_search( + event, query, int(top_k), subject_type=None + ): + yield result + + @filter.llm_tool(name="bangumi_search_anime") + async def llm_search_anime( + self, event: AstrMessageEvent, query: str, top_k: int = 1 + ) -> AsyncGenerator[object, None]: + """在 Bangumi 数据库中搜索 TV 番剧/动漫条目,展示动漫详情与评分。当用户明确询问某部 TV 动漫/番剧时调用。 + + Args: + query(string): 搜索关键词,如番剧名称 + top_k(number): 返回结果数量,默认 1,最多 5 + """ + if not self.search_service: + yield event.plain_result("❌ 搜索服务未就绪") + return + async for result in self.search_service.handle_subject_search( + event, query, int(top_k), subject_type=[2], subject_tags=["TV"] + ): + yield result + + @filter.llm_tool(name="bangumi_search_movie") + async def llm_search_movie( + self, event: AstrMessageEvent, query: str, top_k: int = 1 + ) -> AsyncGenerator[object, None]: + """在 Bangumi 数据库中搜索剧场版动画条目。当用户询问某部剧场版/电影动画的信息时调用。 + + Args: + query(string): 搜索关键词,如剧场版名称 + top_k(number): 返回结果数量,默认 1,最多 5 + """ + if not self.search_service: + yield event.plain_result("❌ 搜索服务未就绪") + return + async for result in self.search_service.handle_subject_search( + event, query, int(top_k), subject_type=[2], subject_tags=["剧场版"] + ): + yield result + + @filter.llm_tool(name="bangumi_search_manga") + async def llm_search_manga( + self, event: AstrMessageEvent, query: str, top_k: int = 1 + ) -> AsyncGenerator[object, None]: + """在 Bangumi 数据库中搜索漫画条目,展示漫画详情与评分。当用户询问某部漫画的信息时调用。 + + Args: + query(string): 搜索关键词,如漫画名称 + top_k(number): 返回结果数量,默认 1,最多 5 + """ + if not self.search_service: + yield event.plain_result("❌ 搜索服务未就绪") + return + async for result in self.search_service.handle_subject_search( + event, query, int(top_k), subject_type=[1], subject_tags=["漫画"] + ): + yield result + + @filter.llm_tool(name="bangumi_today_calendar") + async def llm_today_calendar( + self, event: AstrMessageEvent + ) -> AsyncGenerator[object, None]: + """获取今日番剧放送时刻表,展示今天有哪些动漫新番更新。当用户询问"今天有什么番"、"今天更新了什么"、"今日放送"时调用。""" + if not self.search_service: + yield event.plain_result("❌ 搜索服务未就绪") + return + async for result in self.search_service.handle_calendar(event): + yield result + + @filter.llm_tool(name="bangumi_subscribe") + async def llm_subscribe( + self, + event: AstrMessageEvent, + query: str = "", + subject_id: str = "", + ) -> AsyncGenerator[object, None]: + """订阅番剧更新通知。当用户表达想要追番、订阅、收到某番剧更新提醒时调用。 + 若提供 subject_id 则直接订阅;否则按 query 关键词搜索,唯一匹配时自动订阅,多个候选时返回列表供用户确认(用户选定后再次调用并传入 subject_id)。 + + Args: + query(string): 要订阅的番剧名称关键词,与 subject_id 二选一 + subject_id(string): 番剧的 Bangumi ID,优先级高于 query + """ + if not self.subscription_service: + yield event.plain_result("❌ 订阅服务未就绪") + return + + session_key = self._resolve_session_key(event) + + if subject_id: + result = await self.subscription_service.subscribe_by_subject_id( + session_id=session_key, + subject_id=subject_id, + ) + yield event.plain_result(result) + return + + if not query: + yield event.plain_result("❌ 请提供番剧名称关键词或 Bangumi ID") + return + + error_msg, candidates = await self.subscription_service.get_subscribe_candidates( + keyword=query, + limit=self.config_manager.get_max_fuzzy_results(), + ) + if error_msg: + yield event.plain_result(error_msg) + return + if not candidates: + yield event.plain_result("🔍 未找到相关番剧") + return + + if len(candidates) == 1: + result = await self.subscription_service.subscribe_by_subject_id( + session_id=session_key, + subject_id=candidates[0]["subject_id"], + ) + yield event.plain_result(result) + return + + lines = ["⚠️ 匹配到多个候选,请告知序号或提供 Bangumi ID 以确认订阅:"] + for index, candidate in enumerate(candidates, start=1): + lines.append(f"{index}. {candidate['name']} (ID: {candidate['subject_id']})") + yield event.plain_result("\n".join(lines)) + + @filter.llm_tool(name="bangumi_unsubscribe") + async def llm_unsubscribe( + self, event: AstrMessageEvent, query: str + ) -> AsyncGenerator[object, None]: + """取消订阅番剧更新通知(弃坑)。当用户表达想要退订、弃坑、取消追某部番剧时调用。 + + Args: + query(string): 要取消订阅的番剧名称关键词或 Bangumi ID + """ + if not self.subscription_service: + yield event.plain_result("❌ 订阅服务未就绪") + return + + session_key = self._resolve_session_key(event) + result = await self.subscription_service.unsubscribe(session_key, query) + yield event.plain_result(result) + async def terminate(self) -> None: logger.info("正在清理 Bangumi 插件资源...") if self.scheduler_manager.scheduler.running: