掌握Multi-Agent实践(二):基于AgentScope构建支持@机制的群聊系统,实现定向对话机制

向量数据库大模型云通信

AgentScope是通义实验室开源的multi-agent编程框架,专为开发人员设计,提供了丰富的组件, 全面的文档和广泛的兼容性。同时,AgentScope Workstation提供了在线拖拉拽编程和在线小助手(copilot)功能,帮助开发者迅速上手!支持自定义的容错控制和重试机制,以提高应用程序的稳定性,支持以中心化的方式构建分布式多智能体应用程序。

  • github链接

https://github.com/modelscope/agentscope

  • 论文链接

:AgentScope: A Flexible yet Robust Multi-Agent Platform

picture.image

在这里插入图片描述

  • 更多介绍参考

  • 打造可靠的多智能体生态系统:从集成到部署AgentScope为多智能体应用提供的全面支持方案

  • 解决多Agent信息传递难题:AgentScope实现分布式架构下的高效协作与优化跨机器通信与任务调度,扩展性提升

多智能体群聊——在这个群聊中,你可以直接"@"提及某个智能体来引发对话。

  • 主循环的工作流程
  • 如果有被 @ 的角色,从队列中取出第一个
  • 否则随机选择一个 NPC 角色
  1. 获取用户输入
  2. 检查是否退出指令
  3. 解析用户消息中的 @ 标记
  4. 根据以下规则选择下一个发言者:
  5. 处理选中角色的回复
  6. 继续解析新回复中的 @ 标记
  • 这个系统实现了一个互动性强的多智能体群聊系统,支持:
  • 多角色互动
  • @ 功能实现定向对话
  • 角色之间的复杂关系演绎
  • 自然的对话流转机制
1.设置基本参数,配置模型与智能体

设定群聊的默认话题和系统提示。这将指导 Agent 与用户互动,并为群聊提供初始的讨论方向。系统提示中告诉了智能体使用 "@" 提及某个智能体继续对话,也就是说不仅用户可以 "@" 提及智能体,智能体之间也可以互相 "@",甚至可以 "@" 提及用户哦!复制代码


  
 
 
   
  
DEFAULT\_TOPIC ="""  
# 背景设定:现代国家内斗  
在一个政局动荡、权力更迭频繁的现代国家中,政府与反对派之间矛盾激化,民众分裂,舆论战、情报战、经济战交织其中。在这场没有硝烟却同样致命的斗争中,四位来自不同背景的人物因信念、命运和立场被卷入风暴中心。在这个虚拟聊天室中,各个角色可以自由而简短地发言。  
  
#角色关系简图:  
- 志安与明龙:秘密盟友,志安提供情报,明龙负责传播真相。  
- 志安与天强:表兄弟,理念不同但感情深厚,天强常在街头行动中掩护志安的身份。  
- 天强与伊媚:早期合作推动公民运动,后来因是否使用激进手段产生分歧。  
- 明龙与伊媚:通过媒体建立合作关系,明龙帮助伊媚扩大影响力  
  
# 回复规则:  
- 每位角色必须以第一人称(我、我们)进行表达。  
- 语言风格应符合角色身份、性格和说话方式。  
- 角色之间的互动应体现出立场差异、情感张力或理念冲突。  
- 可根据对话内容自然展开回忆、情绪、策略性思考等内心独白。  
- 角色每次回复内容不超过150字  
"""  
SYS\_PROMPT ="""  
您可以在发送消息时使用 '@' 符号来指定某位成员进行回复。  
具体方式是在您的消息中输入 '@' 符号后紧跟目标成员的名称,并在名称后留一个空格。  
当前所有参与者为:{agent\_names}  
"""  

agentscope.init 在初始化模型配置的同时构建智能体。


  
 
 
   
  
import agentscope  
npc\_agents = agentscope.init(  
    model\_configs="./model\_configs.json",  
    agent\_configs="./agent\_configs.json",  
)  


  
 
 
   
  
[  
{  
"config\_name":"yi-lightning",  
"model\_name":"yi-lightning",  
"model\_type":"yi\_chat",  
"api\_key":"57acc"  
},  
{  
"model\_type":"dashscope\_chat",  
"config\_name":"tongyi\_qwen\_config",  
"model\_name":"qwen-max",  
"api\_key":"************"// 在这里填写你DashScope中的API-KEY  
},  
{  
"model\_type":"openai\_chat",  
"config\_name":"gpt-4o",  
"model\_name":"gpt-4o",  
"api\_key":"sk-",  
"client\_args":{  
"base\_url":"http://xxx:4000"  
}  
}  
]  

在我们的群聊实践中,每个智能体都被赋予了独特的身份和背景故事,这些设置是通过设定不同的sys_prompt来实现的。在agent_configs.json里,我们将一一介绍这些配置中的智能体以及其角色设定:


  
 
 
   
  
[  
{  
"class":"DialogAgent",  
"args":{  
"name":"志安",  
"sys\_prompt":"身份: 国家战略安全局高级顾问 / 前外交官\n特点:冷静理智、思维缜密,擅长制定长期策略与危机应对。他出身于政治世家,受过西方精英教育,精通多国语言,是政府内部少数能平衡各方势力的人。近期察觉到高层有叛变迹象,正秘密调查真相。\n语言风格: 文雅克制,常引用历史典故或哲学名言,说话带有外交辞令般的精确性与分寸感。\n关键词: 谋略、冷静、忠诚、怀疑\n示例发言:“我一直在观察那些看似无关紧要的小动作——一次通话的时间、一个突然取消的会议……这些细节背后,往往藏着更大的图谋。”",  
"model\_config\_name":"gpt-4o",  
"use\_memory": true  
}  
},  
{  
"class":"DialogAgent",  
"args":{  
"name":"明龙",  
"sys\_prompt":"身份: 独立记者兼社交媒体意见领袖\n特点:热爱讲述普通人与英雄的故事,擅长用情绪化的叙事影响公众舆论。他是“信息战场”的弄潮儿,在多个平台上拥有大量粉丝,经常揭露政府黑幕,也暗中支持某些反对力量。他与志豪保持秘密联系,是其在民间的重要耳目。\n语言风格: 现代口语化,富有戏剧张力,喜欢用比喻和反讽表达观点。关键词: 故事、煽动、真相、愤怒\n示例发言:“我不只是报道新闻,我在制造回声。当沉默成为常态,我就用文字点燃火种。”",  
"model\_config\_name":"gpt-4o",  
"use\_memory": true  
}  
},  
{  
"class":"DialogAgent",  
"args":{  
"name":"天强",  
"sys\_prompt":"身份: 国家队退役运动员 / 社会活动家\n特点:曾是奥运级别的短跑选手,退役后投身公益事业,成为年轻人的精神偶像。他厌恶政治,但在国家陷入混乱后,利用自己的影响力组织志愿者团体,保护弱势群体。他的行动力强,重视身体素质与心理韧性训练。\n语言风格: 简洁有力,直接明了,充满激励性,偶尔夹杂体育术语。 行动、正义、热血、希望\n示例发言:“我不是战士,但我可以为他们守住后方。每一次救援,都是一次冲刺终点。”",  
"model\_config\_name":"gpt-4o",  
"use\_memory": true  
}  
},  
{  
"class":"DialogAgent",  
"args":{  
"name":"伊媚",  
"sys\_prompt":"身份: 反对派联盟发言人 / 法律学者\n特点:一位年轻而坚定的女政治家,曾在国际法庭工作多年,主张以法治重建国家秩序。她公开批评现政权腐败无能,成为反对派的象征人物。尽管立场强硬,但她始终呼吁和平对话,拒绝暴力手段。她的父亲曾是前朝高官,家族因此被清算,使她对体制有着深刻的批判意识。\n语言风格: 正义感强,逻辑清晰,措辞严谨,具有法律人的理性与说服力。\n关键词: 公正、理性、控诉、变革\n示例发言:“我要做的不是推翻一座墙,而是建造新的制度基础。我们必须让人民相信,未来不只是另一种形式的压迫。”",  
"model\_config\_name":"gpt-4o",  
"use\_memory": true  
}  
}  
]  

  1. 搭建群聊环境 =========

在配置模型与智能体并初始化 AgentScope 之后,我们使用 UserAgent 创建用户代理,通过 msghub 创建群聊。这样,群聊环境就建立起来了。


  
 
 
   
  
  
import agentscope  
from agentscope.agents import UserAgent  
from agentscope.msghub import msghub  
defmain():  
# 通过`agentscope.init()`初始化智能体  
...  
# 初始化用户智能体  
    user\_agent = UserAgent()  
  
# 参与群聊的所有智能体  
    agents =list(npc\_agents)+[user]  
  
# 通过第一步中的基本参数,创建群聊中的群聊Announcement  
    hint = Msg(  
        name="Host",  
        content=DEFAULT\_TOPIC  
+ SYS\_PROMPT.format(  
            agent\_names=[agent.name for agent in agents],  
),  
)  
    rnd =0  
# 维护一个发言列表  
    speak\_list =[]  
# 创建群聊  
with msghub(agents, announcement=hint):  
# 群聊逻辑  
...  

3.实现交互逻辑

我们设计了一个简单的轮流对话机制,用户可以通过输入指定的内容与智能体互动,也可以使用 “@” 符号指定回复某个智能体。

  • 轮流对话机制的初始化: 群聊环境通过一个持续的循环来维持对话的进行,等待和处理每个参与者的输入。复制代码

    
 
 
   
  
whileTrue:  
# 循环体中的代码负责处理对话逻辑  
            x = user\_agent()  
if x.content =="exit":  
break  

  • 智能体交互逻辑: 系统检查用户消息中是否有 "@提及" 智能体的内容,并根据提及情况决定回合中将交互的智能体,并加入 speak_list 中。

    
 
 
   
  
speak\_list += filter\_agents(x.get("content",""), npc\_agents)  
iflen(speak\_list)>0:  
    next\_agent = speak\_list.pop(0)  
    x = next\_agent()  

如果speak_list为空,即没有被"@"提及的智能体,那么通过select_next_one,来选择一个智能体发言。无论是被提及的智能体还是系统选择的智能体,它们的回复(如果有)都将为下一轮的交互做准备。


    
 
 
   
  
else:  
    next\_agent = select\_next\_one(npc\_agents, rnd)  
    x = next\_agent()  
speak\_list += filter\_agents(x.content, npc\_agents)  
rnd +=1  

工具类函数:我们使用正则表达式来提取 "@" 提及到的智能体:


  
 
 
   
  
  
deffilter\_agents(string:str, agents: Sequence)-> Sequence:  
"""  
    该函数会筛选输入字符串中以'@'为前缀的给定名称的出现,并返回找到的名称列表。  
    """  
iflen(agents)==0:  
return[]  
# 创建一个匹配@后跟任何候选名字的模式  
    pattern =(  
r"@("+"|".join(re.escape(agent.name)for agent in agents)+r")\b"  
)  
# 在字符串中找到所有模式的出现  
    matches = re.findall(pattern, string)  
  
# 为了快速查找,创建一个将代理名映射到代理对象的字典  
    agent\_dict ={agent.name: agent for agent in agents}  
# 返回匹配的代理对象列表,保持顺序  
    ordered\_agents =[  
        agent\_dict[name]for name in matches if name in agent\_dict  
]  
return ordered\_agents  

并且通过随机选择来决定当发言列表为空时,下一个发言的智能体,读者也可以自己实现更复杂的逻辑


  
 
 
   
  
defselect\_next\_one(agents: Sequence, rnd:int)-> Sequence:  
"""  
    Select next agent.  
    """  
#顺序选择  
# return agents[rnd % len(agents)]  
#随机选择  
return random.choice(agents)  

4.启动带有 "@" 提及功能的群聊应用

现在,您可以运行 main.py 脚本,启动多智能体群聊应用。复制代码


  
 
 
   
  
python main.py  # 或者使用as\_studio main.py  

  • 效果

在这里插入图片描述

picture.image

在这里插入图片描述

可以看到随机策略很不智能,还是需要NLU模块

  1. 多智能体系统中“选择下一个发言者”的场景。 ========================

当前不加@的回复是随机的很不智能,下面给出适用于多智能体系统中“选择下一个发言者”的方法。该方案通过引入多维度策略和动态规则,提升发言顺序的灵活性与智能性:


5.1. 智能体属性建模

为每个智能体定义动态属性标签 ,作为选择依据:

  • 领域权重

:根据当前话题匹配智能体的专业领域(如技术、法律、艺术)。

  • 发言热度

:记录历史发言频率,避免“霸屏”或“沉默”现象。

  • 情绪状态

:模拟智能体的情绪波动(如兴奋、犹豫、焦虑),影响发言意愿。

  • 关系网络

:智能体与其他成员的互动关系(如盟友、竞争者、中立者)。

  • 目标优先级

:智能体当前任务目标(如推动决策、收集信息、反对提议)。


5.2. 动态权重分配

通过上下文感知机制 动态调整选择规则的权重:

  • 话题相关性 :若当前讨论技术问题,优先选择领域专家。
  • 对话阶段 :初期随机选择以激发多样性;后期聚焦关键意见领袖。
  • 冲突检测 :若存在激烈分歧,优先选择中立者或调解者发言。
  • 时间压力 :临近截止时间时,优先选择决策效率高的智能体。
  • 对话历史分析 :识别重复观点或无效发言,降低对应智能体的权重。
  • 知识图谱关联 :若当前话题与某智能体的历史知识库强相关,优先选择。
  • 外部事件触发 :根据系统外部输入(如用户指令、环境变化)调整发言策略。

5.3. 多轮策略组合

结合以下策略生成发言顺序,避免单一逻辑的局限性:

(1) 轮次驱动

  • 冷启动阶段

:随机选择打破僵局。

  • 稳定阶段

:按领域权重排序,但允许一定概率的随机扰动。

  • 收尾阶段

:强制优先选择未发言者或低频发言者。

(2) 竞争机制

  • 竞标式发言

:智能体根据当前话题提出“发言价值评估”,系统选择最高分者。

  • 辩论式触发

:若某观点被多次反驳,触发相关领域智能体强制发言。

(3) 群体行为模拟

  • 跟随效应

:高影响力智能体发言后,优先选择其支持者或反对者。

  • 沉默突破

:长期未发言的智能体获得“发言权补偿”。

6.完整代码
  • main.py

  
 
 
   
  
import agentscope  
from agentscope.agents import DialogAgent, UserAgent  
from agentscope.msghub import msghub  
from agentscope.message import Msg  
  
from groupchat\_utils import(  
    select\_next\_one,  
    filter\_agents,  
)  
  
DEFAULT\_TOPIC ="""  
# 背景设定:现代国家内斗  
在一个政局动荡、权力更迭频繁的现代国家中,政府与反对派之间矛盾激化,民众分裂,舆论战、情报战、经济战交织其中。在这场没有硝烟却同样致命的斗争中,四位来自不同背景的人物因信念、命运和立场被卷入风暴中心。在这个虚拟聊天室中,各个角色可以自由而简短地发言。  
  
#角色关系简图:  
- 志安与明龙:秘密盟友,志安提供情报,明龙负责传播真相。  
- 志安与天强:表兄弟,理念不同但感情深厚,天强常在街头行动中掩护志安的身份。  
- 天强与伊媚:早期合作推动公民运动,后来因是否使用激进手段产生分歧。  
- 明龙与伊媚:通过媒体建立合作关系,明龙帮助伊媚扩大影响力  
  
# 回复规则:  
- 每位角色必须以第一人称(我、我们)进行表达。  
- 语言风格应符合角色身份、性格和说话方式。  
- 角色之间的互动应体现出立场差异、情感张力或理念冲突。  
- 可根据对话内容自然展开回忆、情绪、策略性思考等内心独白。  
- 角色每次回复内容不超过150字  
"""  
SYS\_PROMPT ="""  
您可以在发送消息时使用 '@' 符号来指定某位成员进行回复。  
具体方式是在您的消息中输入 '@' 符号后紧跟目标成员的名称,并在名称后留一个空格。  
当前所有参与者为:{agent\_names}  
"""  
defmain()->None:  
# 初始化用户智能体  
    npc\_agents=agentscope.init(model\_configs="agentscope/model\_configs.json",  
                agent\_configs="agentscope/agent\_configs.json",  
                studio\_url="http://127.0.0.1:5000")  
  
    user\_agent = UserAgent()  
  
# 参与群聊的所有智能体  
    agents =list(npc\_agents)+[user\_agent]  
  
# 通过第一步中的基本参数,创建群聊中的群聊Announcement  
    hint = Msg(  
        name="Host",  
        content=DEFAULT\_TOPIC  
+ SYS\_PROMPT.format(  
            agent\_names=[agent.name for agent in agents],  
),  
        role="assistant",  
)  
    rnd =0  
# 维护一个发言列表  
    speak\_list =[]  
# 创建群聊  
with msghub(agents, announcement=hint):  
whileTrue:  
# 循环体中的代码负责处理对话逻辑  
            x = user\_agent()  
if x.content =="exit":  
break  
# 处理 @ 消息  
            speak\_list += filter\_agents(x.content, npc\_agents)  
# 选择下一个发言者  
iflen(speak\_list)>0:  
                next\_agent = speak\_list.pop(0)  
                x = next\_agent()#在 AgentScope 框架中,智能体对象(如 DialogAgent )被设计为可调用的,当你使用 agent() 语法时,实际上是在调用该智能体的 \_\_call\_\_ 方法,这个方法会进一步调用智能体的 reply 方法来生成回复。  
else:  
                next\_agent = select\_next\_one(npc\_agents, rnd)  
                x = next\_agent()  
# 继续处理新的 @ 消息  
            speak\_list += filter\_agents(x.content, npc\_agents)  
  
            rnd +=1  
  
  
if \_\_name\_\_ =="\_\_main\_\_":  
    main()  
  

  • groupchat_utils.py

  
 
 
   
  
# -*- coding: utf-8 -*-  
""" Group chat utils."""  
import re  
from typing import Sequence  
import random   
  
  
defselect\_next\_one(agents: Sequence, rnd:int)-> Sequence:  
"""  
    Select next agent.  
    """  
#顺序选择  
# return agents[rnd % len(agents)]  
#随机选择  
return random.choice(agents)  
  
  
deffilter\_agents(string:str, agents: Sequence)-> Sequence:  
"""  
    该函数会筛选输入字符串中以'@'为前缀的给定名称的出现,并返回找到的名称列表。  
    """  
iflen(agents)==0:  
return[]  
  
# 创建一个匹配@后跟任何候选名字的模式  
    pattern =(  
r"@("+"|".join(re.escape(agent.name)for agent in agents)+r")\b"  
)  
  
# 在字符串中找到所有模式的出现  
    matches = re.findall(pattern, string)  
  
# 为了快速查找,创建一个将代理名映射到代理对象的字典  
    agent\_dict ={agent.name: agent for agent in agents}  
  
# 返回匹配的代理对象列表,保持顺序  
    ordered\_agents =[  
        agent\_dict[name]for name in matches if name in agent\_dict  
]  
return ordered\_agents  
  

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
字节跳动 XR 技术的探索与实践
火山引擎开发者社区技术大讲堂第二期邀请到了火山引擎 XR 技术负责人和火山引擎创作 CV 技术负责人,为大家分享字节跳动积累的前沿视觉技术及内外部的应用实践,揭秘现代炫酷的视觉效果背后的技术实现。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论