Multi-Agent实践第6期:面向智能体编程:狼人杀在AgentScope

本文涉及的产品
交互式建模 PAI-DSW,5000CU*H 3个月
简介: 本期文章,我们会介绍一下AgentScope的一个设计哲学(Agent-oriented programming)

1. 前言

在前几期的文章中,我们带大家了解了怎么去利用AgentScope去编写一些简单应用程序(可以@人的群聊五子棋)和一些对agent非常有用的技术向的应用(ReAct和RAG)。本期文章,我们会介绍一下AgentScope的一个设计哲学(Agent-oriented programming),然后和大家一起看看在这样的思想指导下设计的AgentScope能如何用比较少的代码量就开发出一个让智能体玩狼人杀游戏的应用。“狼人杀游戏完整的代码可以在链接中找到。”


欢迎大家关注AgentScope,在github上为我们star ?。在接下来几天,我们会继续推出教程,带领大家搭建出有趣的多智能体应用!



2. 面向智能体编程(Agent-oriented programming)

在现在最广为接受的编程语言中,大家比较熟悉的范式包括函数式编程、面向过程编程和面向对象编程。这些编程范式都有自己擅长的领域,给广大开发者带来各种应用开发上的便利。在多智能体应用中,原生的、最重要的元素是智能体(这里我们特指基于大模型的智能体),单个智能体对应的开发需求是智能体本身的功能开发;其次,在多智能体应用中,大家免不了还需要设计、实现智能体之间的合作机制。虽然广义来说,智能体也可以被理解成一种特殊的对象(object),但从开发者的角度来看,多智能体编程有一些自己的正面或负面的特性需要额外考虑。具体来说,这些特性可以提炼成两个方面:

  1. 多智能体能完成任务的多样性:除了简单的智能体-用户这种对话场景,多智能体会被通过各种编排去解决各种更复杂的任务,处理各种模态的数据,甚至可能需要大规模部署或者帮助处理一些敏感信息。
  2. 大模型生成内容的创造性和不确定性:就大语言模型而言,硬币的正面是生成的内容因为随机性会显示出一定的创造性,但硬币的反面则是有时大模型不能很好的服从指令或者产生幻觉;同时,有了大模型加成的智能体,可以去调用各种本地或远程的工具去解决问题,但这也需要应用开发者去提炼接口,以及考虑如何处理工具调用失败的情况。


所以我们在设计AgentScope这个多智能体编程框架的时候,就考虑到给我们的开发者们提供一个“面向智能体编程”的环境。从完成任务多样性的角度出发,AgentScope提供了各种便捷的适合智能体合作的编程接口以及原生支持的多模态数据处理,也提供了非常方便的“一行代码转换成分布式”的功能去支持大规模部署和分布式私域部署;为了最大化大模型的创造力和最小化大模型回答的不可靠性,我们提供了各种层面上的信息提取机制和容错机制。


在今天这篇文章里,我们想通过狼人杀游戏,来体现我们AgentScope面向对象编程的一些特性,包括如何便捷生成多个智能体,如何通过极少代码去组织智能体群聊,以及如何规范大模型回答让整个应用流程更流畅。



狼人杀游戏

狼人杀作为剧本杀形式的游戏,非常适合使用多智能体来模拟。下面我们展示一下用AgentScope实现6人局狼人杀的主逻辑;6人局的角色包括2个狼人、2个村民、一个女巫、一个预言家。


2.1 需要多种智能体合作?一行代码生成N种角色!

在狼人杀这个样例中,我们需要给每个智能体一些设定,包括使用的模型和sys_prompt。sys_promt里包含了描述狼人杀的具体游戏规则,以及该agent的名字。

# 具体参见https://github.com/modelscope/agentscope/blob/main/examples/game_werewolf/configs/agent_configs.json
agent_configs = [
    {
        "class": "DictDialogAgent",
        "args": {
            "name": "Player1",
            # 包含狼人杀的具体规则
            "sys_prompt": "Act as a player in a werewolf game. You are Player1 ...",
            "model": "gpt-4",
            "use_memory": True
        }
    },
    ...
    
    {
        "class": "DictDialogAgent",
        "args": {
            "name": "Player6",
            # 包含狼人杀的具体规则
            "sys_prompt": "Act as a player in a werewolf game. You are Player6 ...",
            "model": "gpt-4",
            "use_memory": True
        }
    }
]


有了这些智能体的配置(模型配置可以参考我们前几期文章),在python程序中,我们可以通过简单agentscope.init一个函数调用,生成6个不同的智能体。

# 主持人
HostMsg = partial(Msg, name="Moderator", echo=True)
# 女巫在开始的时候有解药和毒药
healing, poison = True, True
# 狼人夜间最多讨论回合数
MAX_WEREWOLF_DISCUSSION_ROUND = 3
# 游戏进行的最多天数(防止大模型陷入投票得不到结果或者其他原因导致游戏无法结束的情况)
MAX_GAME_ROUND = 6
# 初始化agent
survivors = agentscope.init(
        model_configs="./configs/model_configs.json",
        agent_configs="./configs/agent_configs.json",
)
# 6个角色
roles = ["werewolf", "werewolf", "villager", "villager", "seer", "witch"]
# 给每个agent按照顺序分配角色
wolves, witch, seer = survivors[:2], survivors[-1], survivors[-2]


2.2 需要频繁的让智能体讨论?message hub和pipeline帮你编排!

狼人杀游戏的主逻辑会循环每一天的流程,从狼人晚上讨论开始到白天大家投票结束。在这之中,有许多需要多智能体讨论的场景。在这些场景中,我们用上了AgentScope为这些多智能体合作设计的message hub模块(msghub)和pipline。msghub可以大大节约编写多智能体“群聊”需要的代码量:只需要把智能体放进msghub,他们就能默认地去广播消息给所有在里面的其他智能体。除此之外,我们也提供了各种编排多智能体执行的pipeline(比如这里用到的sequentialpipeline,更多逻辑pipeline可以参考我们的教程),方便用户编排智能体在游戏中的发言流程。

for i in range(1, MAX_GAME_ROUND + 1):
    # 夜晚来临,狼人首先讨论
    hint = HostMsg(content=Prompts.to_wolves.format(n2s(wolves)))
    with msghub(wolves, announcement=hint) as hub:
        for _ in range(MAX_WEREWOLF_DISCUSSION_ROUND):
            x = sequentialpipeline(wolves)
            if x.get("agreement", False):
                break
    
        # 狼人讨论结束,进行投票
        hint = HostMsg(content=Prompts.to_wolves_vote)
        votes = [extract_name_and_id(wolf(hint).content)[0] for wolf in wolves]
        # 主持人对狼人公布投票结果
        dead_player = [majority_vote(votes)]
        hub.broadcast(
            HostMsg(content=Prompts.to_wolves_res.format(dead_player[0])),
        )
    # 女巫阶段,是否使用解药和毒药(一个晚上最多使用一瓶)
    healing_used_tonight = False
    if witch in survivors:
        if healing:
            hint = HostMsg(
                content=Prompts.to_witch_resurrect.format_map(
                    {"witch_name": witch.name, "dead_name": dead_player[0]},
                ),
            )
            if witch(hint).get("resurrect", False):
                healing_used_tonight = True
                dead_player.pop()
                healing = False
    
        if poison and not healing_used_tonight:
            x = witch(HostMsg(content=Prompts.to_witch_poison))
            if x.get("eliminate", True):
                dead_player.append(extract_name_and_id(x.content)[0])
                poison = False
    # 预言家验人环节
    if seer in survivors:
        hint = HostMsg(
            content=Prompts.to_seer.format(seer.name, n2s(survivors)),
        )
        x = seer(hint)
    
        player, idx = extract_name_and_id(x.content)
        role = "werewolf" if roles[idx] == "werewolf" else "villager"
        hint = HostMsg(content=Prompts.to_seer_result.format(player, role))
        # 预言家获得验人结果
        seer.observe(hint)
    # 根据夜晚发生的情况,更新目前的存活情况并判断游戏是否结束
    survivors, wolves = update_alive_players(survivors, wolves, dead_player)
    if check_winning(survivors, wolves, "Moderator"):
        break
    
    # 根据是否平安夜决定主持人白天的开场白
    content = (
        Prompts.to_all_danger.format(n2s(dead_player))
        if dead_player
        else Prompts.to_all_peace
    )
    hints = [
        HostMsg(content=content),
        HostMsg(content=Prompts.to_all_discuss.format(n2s(survivors))),
    ]
    with msghub(survivors, announcement=hints) as hub:
        # 白天讨论环节
        x = sequentialpipeline(survivors)
        # 白天投票环节
        hint = HostMsg(content=Prompts.to_all_vote.format(n2s(survivors)))
        votes = [extract_name_and_id(_(hint).content)[0] for _ in survivors]
        vote_res = majority_vote(votes)
        # 公布投票结果
        result = HostMsg(content=Prompts.to_all_res.format(vote_res))
        hub.broadcast(result)
        # 更新存活情况并判断游戏是否结束
        survivors, wolves = update_alive_players(survivors, wolves, vote_res)
        if check_winning(survivors, wolves, "Moderator"):
            break
        # 有序继续,新的夜晚来临
        hub.broadcast(HostMsg(content=Prompts.to_all_continue))


2.3 规范大模型回答:DictDialogAgent

在狼人杀这个例子中,所有智能体都是基于DictDialogAgent创建的。DictDialogAgent一个特性是它返回的内容都是python中字典格式(dict或者json),这也是为了在游戏主逻辑中我们可以根据智能体回答为推进游戏做出一些确定性的判断,比如在狼人讨论之后判断当晚需要?哪个玩家。具体来说,我们的DictDialogAgent在调用大模型的时候,也会调用一个parse_func去解析大模型的回答,同时fault_handler提供了当应用中大模型回答无法被parse时的一个默认返回。

def parse_dict(response: ModelResponse) -> ModelResponse:
    """Parse function for DictDialogAgent"""
    try:
        response_dict = json.loads(response.text)
    except json.decoder.JSONDecodeError:
        response_dict = json.loads(response.text.replace("'", '"'))
    return ModelResponse(raw=response_dict)
def default_response(response: ModelResponse) -> ModelResponse:
    """The default response of fault_handler"""
    return ModelResponse(raw={"speak": response.text})
class DictDialogAgent(AgentBase):
    
    def reply(self, x: dict = None) -> dict:
        # 省略具体代码...
        # call llm
        response = self.model(
            prompt,
            parse_func=self.parse_func,
            fault_handler=self.fault_handler,
            max_retries=self.max_retries,
        ).raw
        # 省略代码...
        return msg


2.4 效果图展示

像之前的应用一样,大家可以通过as_studio来运行一个带网页UI的狼人杀应用。下面是我们在游戏中的一些截图(样例中大模型使用的是gpt4)。

  1. 狼人 Player1 和 Player2 夜间讨论

image.png

  1. 预言家 Player5 验人以及白天公布结果

image.png


  1. 白天讨论

image.png


2.5 总结

上面只是一个简单的例子,大家如果有兴趣可以去尝试一下比如8人局或者12人局的狼人杀的实现。完整的代码,包括游戏中用到的提示词,都可以在一下链接中找到。


同时,AgentScope提供的面向智能体编程还有更多的特性等待大家去探索,比如体现对智能体赋能的工具调用和RAG(前两期内容)以及分布式(敬请期待下一期的分享)。


延伸阅读和资源




比赛

看到这里,如果你有好玩的想法,不如实践起来,还可以顺便参加下面的比赛~

image.png


点击即可跳转~

Create @ AI 创客松第四季 (aliyun.com)


相关文章
|
4天前
|
机器学习/深度学习
智能体DS-Agent基于案例推理,让GPT-4数据科学任务接近100%
【4月更文挑战第20天】DS-Agent是结合案例推理(CBR)和大型语言模型的新研究,旨在提升自动化数据科学任务效率。通过自动迭代管道,它能理解任务、构建模型并优化性能。在开发阶段,成功率高达100%,部署阶段平均提高36%的一次通过率,降低成本,使开源LLMs也能高效处理数据科学任务。然而,LLMs的生成问题和资源限制仍是挑战。论文链接:https://arxiv.org/pdf/2402.17453.pdf
38 4
|
4天前
|
人工智能 测试技术 API
【AIGC】LangChain Agent(代理)技术分析与实践
【5月更文挑战第12天】 LangChain代理是利用大语言模型和推理引擎执行一系列操作以完成任务的工具,适用于从简单响应到复杂交互的各种场景。它能整合多种服务,如Google搜索、Wikipedia和LLM。代理通过选择合适的工具按顺序执行任务,不同于链的固定路径。代理的优势在于可以根据上下文动态选择工具和执行策略。适用场景包括网络搜索、嵌入式搜索和API集成。代理由工具组成,每个工具负责单一任务,如Web搜索或数据库查询。工具包则包含预定义的工具集合。创建代理需要定义工具、初始化执行器和设置提示词。LangChain提供了一个从简单到复杂的AI解决方案框架。
|
4天前
|
安全 API 开发者
智能体-Agent能力升级!新增Assistant API & Tools API服务接口
ModelScope-Agent是一个交互式创作空间,它支持LLM(Language Model)的扩展能力,例如工具调用(function calling)和知识检索(knowledge retrieval)。它已经对相关接口进行了开源,以提供更原子化的应用LLM能力。用户可以通过Modelscope-Agent上的不同代理(agent),结合自定义的LLM配置和消息,调用这些能力。
|
4天前
|
人工智能 数据安全/隐私保护 UED
Agent AI智能体的未来
Agent AI智能体的未来
|
4天前
|
存储 人工智能 测试技术
【AI智能体】SuperAGI-开源AI Agent 管理平台
【4月更文挑战第9天】智能体管理平台SuperAGI简介及实践
|
4天前
|
人工智能 决策智能
【AI Agent系列】【阿里AgentScope框架】4. 深入源码:Pipeline模块如何组织多智能体间的数据流?- 循环结构
【AI Agent系列】【阿里AgentScope框架】4. 深入源码:Pipeline模块如何组织多智能体间的数据流?- 循环结构
55 0
|
4天前
|
人工智能 决策智能
【AI Agent系列】【阿里AgentScope框架】3. 深入源码:Pipeline模块如何组织多智能体间的数据流?- 顺序结构与条件分支
【AI Agent系列】【阿里AgentScope框架】3. 深入源码:Pipeline模块如何组织多智能体间的数据流?- 顺序结构与条件分支
50 2
|
4天前
|
人工智能 决策智能
【AI Agent系列】【阿里AgentScope框架】2. Pipeline模块入门:使用Pipeline模块实现最简单的多智能体交互
【AI Agent系列】【阿里AgentScope框架】2. Pipeline模块入门:使用Pipeline模块实现最简单的多智能体交互
67 0
|
4天前
|
人工智能 搜索推荐 决策智能
【AI Agent系列】【阿里AgentScope框架】1. 深入源码:详细解读AgentScope中的智能体定义以及模型配置的流程
【AI Agent系列】【阿里AgentScope框架】1. 深入源码:详细解读AgentScope中的智能体定义以及模型配置的流程
144 0
|
4天前
|
人工智能 Oracle 关系型数据库
【AI Agent系列】【LangGraph】0. 快速上手:协同LangChain,LangGraph帮你用图结构轻松构建多智能体应用
【AI Agent系列】【LangGraph】0. 快速上手:协同LangChain,LangGraph帮你用图结构轻松构建多智能体应用
55 0
http://www.vxiaotou.com