模型调用与后台任务
插件处理一件事有三种做法,按需要选择:
# 普通查询:直接用 Python 处理和发送。
await ctx.reply("今天的订阅内容")
# 单次模型生成:插件给出完整的提示,拿到文字后自己发送。
text = await ctx.generate("待处理的资料", role="mind", system="本次整理要求")
await ctx.reply(text)
# 长工作或文件:交给后台任务,以触发消息的发送者作为请求人。
await ctx.delegate("整理刚才的活动", "CSV 文件", context="实际活动记录")单次生成
ctx.generate(prompt, role="mind", system=None) 做一次不带工具的模型生成,返回文字,不会唤醒聊天或自动发送。role 是 mind 或 learner,没有单独配置 learner 时用 mind 的绑定。调用计入实例的模型用量和每日上限,用量记录里标明来自哪个插件。需要 generate 权限;self.ctx.generate(scene, ...) 要先传入群。
委派后台任务
ctx.delegate(goal, deliverable, context="", materials=()) 以触发这次调用的真实发送者为请求人,把工作交给后台任务,materials 是本群共享资料的文件名。返回值表示任务已接受,结果由宿主送回群里,不要轮询等待。
- 需要实例启用后台任务,请求人有任务权限,并且插件声明
delegate权限。 - 只能在有真实触发消息的调用中使用:命令、规则和
needs_source=True的工具。通知、定点任务和没有来源的工具不能委派。
定点任务
在 start() 中登记,处理器收到的 ctx 没有触发消息:
async def start(self) -> None:
for scene in self.ctx.scenes:
self.ctx.cron("日报", "0 8 * * *", self.publish, scene=scene, timezone=self.ctx.timezone(scene))
async def publish(self, ctx: Invocation) -> None:
await ctx.reply("早上好,这是今天的日报")- 五段式 cron 和明确的 IANA 时区,同一插件同一群中任务名唯一。需要
schedule权限。 - 登记只在本次运行中有效。重启后从当前时间算下一次,停机期间错过的次数不补。
- 单次执行失败时记录错误,下一次照常执行。
后台循环和自建任务
@background(every="5m") 标记的方法在上一轮执行完后等一个间隔再执行,最短 10 秒,适合轮询外部服务:
@background(every="5m")
async def poll(self, ctx: PluginContext) -> None:
for scene in ctx.scenes:
...插件自己的长期协程用 self.ctx.start_task(name, coroutine) 登记,插件停止时宿主取消它们。不要在主循环里做阻塞的网络请求。
self.ctx.scenes 是启用了插件且处于开启状态的群,self.ctx.enabled_scenes 是启用了插件的全部群。群切到插件模式(关闭聊天)后只是不再唤醒聊天模型,插件的命令、通知、定时任务和发送照常工作。群在面板中关闭后,宿主不再向插件派发该群的消息、命令和通知,定时任务跳过该群,在该群发送或推送事件抛出 PermissionError;重新开启后恢复。
生命周期
start()在平台连接就绪后调用一次,限时 60 秒;耗时的准备(等待网络、预热缓存)放进start_task。stop()在宿主停止或插件重新加载时调用,限时 30 秒,此前后台循环和自建任务已经取消。- 超时会被取消并记为插件错误,宿主的其他部分照常运行。
插件技能
插件可以带 skills/<技能名>/SKILL.md 和相关资源。启用了这个插件的群里,后台任务可以按角色的技能许可使用这些技能,技能目录以只读方式挂载。