Skip to content

Commit

Permalink
Multi agent (#403)
Browse files Browse the repository at this point in the history
Co-authored-by: 拿辰 <[email protected]>
  • Loading branch information
zzhangpurdue and 拿辰 authored Apr 16, 2024
1 parent 7bc9531 commit e60e5f1
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 92 deletions.
26 changes: 23 additions & 3 deletions apps/multi_roles_chat_room/app.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import os
import re
from typing import Optional

import gradio as gr
import json
import modelscope_studio as mgr
from modelscope_agent.multi_agents_utils.executors.ray import RayTaskExecutor
from role_core import (chat_progress, init_all_remote_actors,
start_chat_with_topic)
from story_holder import get_avatar_by_name, get_story_by_id, stories

chat_history = []
RayTaskExecutor.init_ray()

# 发送消息的函数

Expand Down Expand Up @@ -54,6 +58,17 @@ def end_topic():
return '', 'topic ended。'


def check_uuid(uid: Optional[str]) -> str:
"""Checks whether a UUID is provided or generates a default one."""
if not uid or uid == '':
if os.getenv('MODELSCOPE_ENVIRONMENT') == 'studio':
import gradio as gr

raise gr.Error('Please login first')
uid = 'local_user'
return uid


def format_entry_html():
base_html = '''
<div class="container story-cardlist">
Expand Down Expand Up @@ -89,6 +104,8 @@ def format_entry_html():
with demo:
state = gr.State({})
story_state = gr.State('1') # default value
uuid = gr.Textbox(label='modelscope_uuid', visible=False)

with gr.Column(visible=True) as entry:
gr.Markdown('## 选择一个场景进入聊天吧~')
entry_btn = gr.Button(
Expand Down Expand Up @@ -197,10 +214,13 @@ def back():
back_btn.click(fn=back, inputs=[], outputs=[entry, content])

def start_chat(username, from_user, topic, _state, _chatbot, _input,
_story_state, select_model):
_story_state, select_model, _uuid):
# get uuid
_uuid = check_uuid(_uuid)
user_task_id = f'{_uuid[:6]}_{_story_state}'
roles = get_story_by_id(_story_state)['roles']
_state = init_all_remote_actors(roles, username, _state, _story_state,
select_model)
select_model, user_task_id)
_state = start_chat_with_topic(from_user, topic, _state)

init_chat = [{
Expand Down Expand Up @@ -328,7 +348,7 @@ def send_message(_chatbot, _input, _state):
fn=start_chat,
inputs=[
user_select, start_topic_from, start_topic_input, state,
user_chatbot, preview_chat_input, story_state, select_model
user_chatbot, preview_chat_input, story_state, select_model, uuid
],
outputs=[user_chatbot, preview_chat_input, state])

Expand Down
2 changes: 2 additions & 0 deletions apps/multi_roles_chat_room/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
gradio==4.8
modelscope-agent==0.4.1
modelscope_studio
47 changes: 23 additions & 24 deletions apps/multi_roles_chat_room/role_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,28 @@
from modelscope_agent import create_component
from modelscope_agent.agent_env_util import AgentEnvMixin
from modelscope_agent.agents import MultiRolePlay
from modelscope_agent.multi_agents_utils.executors.ray import RayTaskExecutor
from modelscope_agent.task_center import TaskCenter
from story_holder import get_story_by_id

REMOTE_MODE = True

# instruction prompt
ROLE_INSTRUCTION_PROMPT = """<|im_start|>system
你是{role},请你根据对话情节设定,和你的对话角色设定,继续当前的对话,说话要符合你的角色设定
【你的角色设定】
{role_description}
【对话情节设定】
{story}
你是{role},请你根据对话情节设定,当前的对话记录和你的对话角色设定,继续当前的对话,说话要符合你的角色设定,不要重复chat history中的内容
【注意事项】
1. 根据user的历史对话回复时,不要重复说历史对话里的句子,要保持生成内容的有趣,丰富度,多样性
2. 长话短说,不要说太多话,不要超过50字
3. 符合当前对话情节设定,围绕对话情节,你是{role},不要模仿其他角色,也不要重复chat history中的内容
【对话场景】
{story}
【chat history】
chat_records
【你的角色设定】
{role_description}
<|im_end|>
"""
{role_description}<|im_end|>"""

CHATROOM_INSTRUCTION_PROMPT = """<|im_start|>system
你是一个小说作家,请你根据对话场景、人物介绍及最近的对话记录,选择继续对话的下一个角色。注意,对话历史是以群聊的形式展现,因此角色可能会@某个人表示对这个人说话。
Expand All @@ -42,17 +40,17 @@
【人物介绍】
{all_roles_info}
对话记录
chat history
chat_records
【最新对话】
recent_records
【注意事项】
根据对话记录和最新对话
根据chat history和最新对话
1. 当主角{user_role}说话中提到某个角色,你需要只让提到的角色接话。
2. 不要选【最新对话】里的角色发言
3. 要让每个角色有平等的对话机会,多选一些【对话记录】没有出现的角色,要求情节充满戏剧性。
3. 要让每个角色有平等的对话机会,多选一些chat history没有出现的角色,要求情节充满戏剧性。
4. 只写角色名字即可,每次最多选两个角色,尽量多的选择主角,当前对话的主角是{user_role}
【回复格式】
Expand Down Expand Up @@ -91,7 +89,8 @@ def generate_role_instruction(role, story_info):
return instruction


def upsert_role(new_user, user_char, human_input_mode, story_info, llm_config):
def upsert_role(new_user, user_char, human_input_mode, story_info, llm_config,
_uid):
role = create_component(
MultiRolePlay,
name=new_user,
Expand All @@ -101,7 +100,8 @@ def upsert_role(new_user, user_char, human_input_mode, story_info, llm_config):
llm=llm_config,
function_list=function_list,
instruction=generate_role_instruction(new_user, story_info),
human_input_mode=human_input_mode)
human_input_mode=human_input_mode,
prefix_name=_uid)
return role


Expand All @@ -127,9 +127,7 @@ def change_user_role(user_role, state):


def init_all_remote_actors(_roles, user_role, _state, _story_state,
select_model):
RayTaskExecutor.init_ray()

select_model, _uid):
story_info = get_story_by_id(_story_state)
llm_config['model'] = select_model

Expand All @@ -139,7 +137,7 @@ def init_all_remote_actors(_roles, user_role, _state, _story_state,
return state

task_center = create_component(
TaskCenter, name='Task_Center', remote=REMOTE_MODE)
TaskCenter, name='Task_Center', remote=REMOTE_MODE, prefix_name=_uid)

# init all agents and task center
role_agents = []
Expand All @@ -148,25 +146,26 @@ def init_all_remote_actors(_roles, user_role, _state, _story_state,
if role == user_role:
human_input_mode = 'ON'
role_agent = upsert_role(role, _roles[role], human_input_mode,
story_info, llm_config)
story_info, llm_config, _uid)

role_agents.append(role_agent)

chat_room = create_component(
MultiRolePlay,
name='chat_room',
remote=True,
role='chat_room',
llm=llm_config,
function_list=function_list,
instruction=CHATROOM_INSTRUCTION_PROMPT.format(
all_roles_info=get_all_roles_info(story_info['roles']),
story=story_info['story'],
user_role=user_role),
is_watcher=True,
use_history=False)
use_history=False,
prefix_name=_uid)

logging.warning(msg=f'time:{time.time()} done create task center')
logging.warning(
msg=f'time:{time.time()} done create task center with uid: {_uid}')

ray.get(task_center.add_agents.remote(role_agents))
ray.get(task_center.add_agents.remote([chat_room]))
Expand Down
6 changes: 2 additions & 4 deletions apps/multi_roles_chat_room/story_holder.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@
stories = [
{
'id': '1',
'cover':
'//img.alicdn.com/imgextra/i1/O1CN01UHwXNQ2780lrVHY6n_!!6000000007751-0-tps-1024-512.jpg',
'cover': '//s21.ax1x.com/2024/04/16/pFxG1zj.jpg',
'title': '我被美女包围了',
'description': '用户是男主角顾易,与多位长相、性格都大相径庭的美女相识',
'roles': ROLES_1,
Expand All @@ -41,8 +40,7 @@
},
{
'id': '2',
'cover':
'//img.alicdn.com/imgextra/i1/O1CN01UHwXNQ2780lrVHY6n_!!6000000007751-0-tps-1024-512.jpg',
'cover': '//s21.ax1x.com/2024/04/16/pFxGgw6.png',
'title': '我是雷军,雷中有“电”,军下有“车”',
'description': '用户是男主角雷军,小米创始人,最近发布了小米SU7',
'roles': ROLES_2,
Expand Down
23 changes: 21 additions & 2 deletions modelscope_agent/__init__.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
from .agent import Agent


def _create_remote(cls, name, max_concurrency=1, *args, **kwargs):
def _create_remote(cls,
name,
max_concurrency=1,
force_new=False,
*args,
**kwargs):
'''
Create a remote actor by ray
Args:
cls: the class to be created
name: the name of ray actor, also the role name
max_concurrency: max concurrency of the actor
focus_new: force to create a new actor
*args:
**kwargs:
Returns:
'''
import ray

try:
# try to get an existing actor
ray_actor = ray.get_actor(name)
if force_new:
ray.kill(ray_actor)
else:
return ray_actor
except ValueError:
pass
# if failed, create a new actor
return ray.remote(
name=name,
max_concurrency=max_concurrency)(cls).remote(*args, **kwargs)
Expand All @@ -39,11 +54,15 @@ def create_component(cls,
name,
remote=False,
max_concurrency=1,
prefix_name=None,
*args,
**kwargs):
kwargs['remote'] = remote
kwargs['role'] = name
kwargs['prefix_name'] = prefix_name
if remote:
if prefix_name is not None:
name = f'{prefix_name}_{name}'
return _create_remote(cls, name, max_concurrency, *args, **kwargs)
else:
return _create_local(cls, *args, **kwargs)
2 changes: 1 addition & 1 deletion modelscope_agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from modelscope_agent.llm import get_chat_model
from modelscope_agent.llm.base import BaseChatModel
from modelscope_agent.tools import TOOL_REGISTRY
from modelscope_agent.tools.base import TOOL_REGISTRY
from modelscope_agent.utils.utils import has_chinese_chars


Expand Down
7 changes: 2 additions & 5 deletions modelscope_agent/agent_env_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,6 @@ def step(self,
history = []
if self.use_history:
history = self.memory.get_history()
logger.info(
f'reach here 2 {result}, self.human_input_mode {self.human_input_mode}'
)
# run generation
for frame in self.run(
prompt,
Expand Down Expand Up @@ -266,10 +263,10 @@ def pull(self):
else:
return ''

def convert_to_string(self, messages: List[Message], max_turn=5):
def convert_to_string(self, messages: List[Message], max_turn=15):
prompt_template = """{conversation_history}"""
conversation_history = ''
for item in messages[:max_turn]:
for item in messages[-1 * max_turn:]:
conversation_history += f'{item.sent_from}: {item.content}\n'
return prompt_template.format(
conversation_history=conversation_history.strip())
Expand Down
3 changes: 3 additions & 0 deletions modelscope_agent/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
DEFAULT_LOG_STORAGE_PATH = DEFAULT_AGENT_ROOT / 'log'
DEFAULT_SEND_TO = 'all'
USER_REQUIREMENT = 'user_requirement'
ENVIRONMENT_NAME = 'env'
AGENT_REGISTRY_NAME = 'agent_center'
TASK_CENTER_NAME = 'task_center'
Loading

0 comments on commit e60e5f1

Please sign in to comment.