forked from hanfangyuan4396/dify-on-wechat
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request zhayujie#524 from lanvent/dev
plugin: 添加`Role`插件,让机器人角色扮演。
- Loading branch information
Showing
5 changed files
with
315 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
用于让Bot扮演指定角色的聊天插件,触发方法如下: | ||
- `$角色/$role help/帮助` - 打印目前支持的角色列表。 | ||
- `$角色/$role <角色名>` - 让AI扮演该角色,角色名支持模糊匹配。 | ||
- `$停止扮演` - 停止角色扮演。 | ||
|
||
添加自定义角色请在`roles/roles.json`中添加。 | ||
(大部分prompt来自https://github.com/rockbenben/ChatGPT-Shortcut/blob/main/src/data/users.tsx) | ||
|
||
以下为例子, | ||
- `title`是角色名。 | ||
- `description`是使用`$role`触发的英语prompt。 | ||
- `descn`是使用`$角色`触发的中文prompt。 | ||
- `wrapper`用于包装你的消息,可以起到强调的作用。 | ||
- `remark`简短的描述该角色,在打印帮助时显示。 | ||
|
||
```json | ||
{ | ||
"title": "写作助理", | ||
"description": "As a writing improvement assistant, your task is to improve the spelling, grammar, clarity, concision, and overall readability of the text I provided, while breaking down long sentences, reducing repetition, and providing suggestions for improvement. Please provide only the corrected Chinese version of the text and avoid including explanations. Please treat every message I send later as text content.", | ||
"descn": "作为一名中文写作改进助理,你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性,同时分解长句,减少重复,并提供改进建议。请只提供文本的更正版本,避免包括解释。请把我之后的每一条消息都当作文本内容。", | ||
"wrapper": "内容是:\n\"%s\"", | ||
"remark": "最常使用的角色,用于优化文本的语法、清晰度和简洁度,提高可读性。" | ||
}, | ||
``` |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
# encoding:utf-8 | ||
|
||
import json | ||
import os | ||
from bridge.bridge import Bridge | ||
from bridge.context import ContextType | ||
from bridge.reply import Reply, ReplyType | ||
import plugins | ||
from plugins import * | ||
from common.log import logger | ||
|
||
|
||
class RolePlay(): | ||
def __init__(self, bot, sessionid, desc, wrapper=None): | ||
self.bot = bot | ||
self.sessionid = sessionid | ||
bot.sessions.clear_session(sessionid) | ||
bot.sessions.build_session(sessionid, desc) | ||
self.wrapper = wrapper or "%s" # 用于包装用户输入 | ||
|
||
def reset(self): | ||
self.bot.sessions.clear_session(self.sessionid) | ||
|
||
def action(self, user_action): | ||
prompt = self.wrapper % user_action | ||
return prompt | ||
|
||
@plugins.register(name="Role", desc="为你的Bot设置预设角色", version="1.0", author="lanvent", desire_priority= 0) | ||
class Role(Plugin): | ||
def __init__(self): | ||
super().__init__() | ||
curdir = os.path.dirname(__file__) | ||
config_path = os.path.join(curdir, "roles.json") | ||
try: | ||
with open(config_path, "r", encoding="utf-8") as f: | ||
config = json.load(f) | ||
self.roles = {role["title"].lower(): role for role in config["roles"]} | ||
if len(self.roles) == 0: | ||
raise Exception("no role found") | ||
self.handlers[Event.ON_HANDLE_CONTEXT] = self.on_handle_context | ||
self.roleplays = {} | ||
logger.info("[Role] inited") | ||
except FileNotFoundError: | ||
logger.error(f"[Role] init failed, {config_path} not found") | ||
except Exception as e: | ||
logger.error("[Role] init failed, exception: %s" % e) | ||
|
||
def get_role(self, name, find_closest=True): | ||
name = name.lower() | ||
found_role = None | ||
if name in self.roles: | ||
found_role = name | ||
elif find_closest: | ||
import difflib | ||
|
||
def str_simularity(a, b): | ||
return difflib.SequenceMatcher(None, a, b).ratio() | ||
max_sim = 0.0 | ||
max_role = None | ||
for role in self.roles: | ||
sim = str_simularity(name, role) | ||
if sim >= max_sim: | ||
max_sim = sim | ||
max_role = role | ||
found_role = max_role | ||
return found_role | ||
|
||
def on_handle_context(self, e_context: EventContext): | ||
|
||
if e_context['context'].type != ContextType.TEXT: | ||
return | ||
bottype = Bridge().get_bot_type("chat") | ||
if bottype != "chatGPT": | ||
return | ||
bot = Bridge().get_bot("chat") | ||
content = e_context['context'].content[:] | ||
clist = e_context['context'].content.split(maxsplit=1) | ||
desckey = None | ||
sessionid = e_context['context']['session_id'] | ||
if clist[0] == "$停止扮演": | ||
if sessionid in self.roleplays: | ||
self.roleplays[sessionid].reset() | ||
del self.roleplays[sessionid] | ||
reply = Reply(ReplyType.INFO, "角色扮演结束!") | ||
e_context['reply'] = reply | ||
e_context.action = EventAction.BREAK_PASS | ||
return | ||
elif clist[0] == "$角色": | ||
desckey = "descn" | ||
elif clist[0].lower() == "$role": | ||
desckey = "description" | ||
elif sessionid not in self.roleplays: | ||
return | ||
logger.debug("[Role] on_handle_context. content: %s" % content) | ||
if desckey is not None: | ||
if len(clist) == 1 or (len(clist) > 1 and clist[1].lower() in ["help", "帮助"]): | ||
reply = Reply(ReplyType.INFO, self.get_help_text()) | ||
e_context['reply'] = reply | ||
e_context.action = EventAction.BREAK_PASS | ||
return | ||
role = self.get_role(clist[1]) | ||
if role is None: | ||
reply = Reply(ReplyType.ERROR, "角色不存在") | ||
e_context['reply'] = reply | ||
e_context.action = EventAction.BREAK_PASS | ||
return | ||
else: | ||
self.roleplays[sessionid] = RolePlay(bot, sessionid, self.roles[role][desckey],self.roles[role].get("wrapper","%s")) | ||
reply = Reply(ReplyType.INFO, f"角色设定为 {role} :\n"+self.roles[role][desckey]) | ||
e_context['reply'] = reply | ||
e_context.action = EventAction.BREAK_PASS | ||
else: | ||
prompt = self.roleplays[sessionid].action(content) | ||
e_context['context'].type = ContextType.TEXT | ||
e_context['context'].content = prompt | ||
e_context.action = EventAction.CONTINUE | ||
|
||
def get_help_text(self): | ||
help_text = "输入\"$角色 (角色名)\"或\"$role (角色名)\"为我设定角色吧,#reset 可以清除设定的角色。\n目前可用角色列表:\n" | ||
for role in self.roles: | ||
help_text += f"[{role}]: {self.roles[role]['remark']}\n" | ||
return help_text |
Oops, something went wrong.