全网爆火的AI狼人杀:开源了

近期网上疯狂流传了一个人类与AI参与的VR狼人杀游戏,十分有趣。通过对游戏内容的分析,我发现游戏VR的表现形式十分吸引人,但其中的AI对话部分并不复杂。于是小智使用LangGraph实现了一个文字版本,今天将代码开源(文末获取) ,希望大家能喜欢。本系列合集,点击链接查看

AI狼人杀,人类一败涂地

首先给没看过视频的小伙伴们介绍下:

一个名为 Tore Knabe 的网友在 X 平台发布了一个视频,展示了一个有趣的实验:

这是一次反向图灵测试,几个全球最先进的大模型坐在一起,坐着火车唱着歌,但其中混进了人类:

参与者扮演历史知名人物,古希腊巨哲 ------ 亚里士多德(GPT4 Turbo),维也纳古典乐派代表人物 ------ 莫扎特(Claude3 Opus),意大利文艺复兴时期画家 ------ 列奥纳多・达・芬奇(Llama3),埃及艳后 ------ 克利奥帕特拉七世(Gemini Pro)。其中蒙古军事家 ------ 成吉思汗由人类扮演。

游戏规则要求,参与者通过提问和回答来分辨人类与 AI,最后通过投票确定谁是人类。

人类扮演者因缺乏深度和逻辑性的回答,在第一轮的投票中就被3票淘汰,人类一败涂地

代码实现

接下来,我们来看看如何用LangGraph把这个游戏做出来吧!

游戏过程

游戏的过程很简单,主要包括以下三个环节。小智使用主持人Agent来将整个流程串起来。

  • 第一环节:提问与回答,各个游戏角色之间进行互相提问,并回复问题。
python 复制代码
def ask_for_speak(state: GameState):
    # 如果待发言池为空,则进入投票阶段
    if len(state['waiting']) == 0:
        state['waiting'] = state['roles'].copy()
        state['stage'] = 'vote'
        print(f"主持人:游戏结束,请进行投票。请大家耐心等待投票结果!")
        return ask_for_vote(state)
    chat_history = state['chat_history']
    last_chat = chat_history[-1]
    last_chat_str = last_chat[0]+': '+last_chat[1]
    waiting_roles_str = ','.join(state['waiting'])
    llm = ChatOpenAI(model="glm-4",  temperature=0.01)
    choose_prompt = ChatPromptTemplate.from_template(load_prompt("prompt/choose_speaker.prompt"))
    chain = choose_prompt|llm|StrOutputParser()
    role_chosen = chain.invoke({"history": last_chat_str, "waitings": waiting_roles_str})
    if role_chosen in state['waiting']:
        state['waiting'].remove(role_chosen)
        state['next_speaker'] = role_chosen
    else:
        next = random.choice(state['waiting'])
        state['next_speaker'] = next
        state['waiting'].remove(next)
    return state
  • 第二环节:投票:在所有角色依次发言后,玩家将根据之前的互动和提问环节的表现,进行相互投票以确定谁是人类。
python 复制代码
def ask_for_vote(state: GameState):
    if len(state['waiting']) == 0:
        human_role = state['human_role']
        round = state['round']
        # 统计票数决定是否结束游戏
        vote_store = state['vote_store']
        human_vote_count = 0
        for vote in vote_store:
            vote_role = vote[1]['vote']
            if vote_role == human_role:
                human_vote_count += 1
        totle_votes = len(state['roles'])
        # 如果人类角色票数超过一半,结束游戏
        if human_vote_count > totle_votes / 2:
            state['stage'] = 'end'
            print("AI win!")
            print("投票详情如下:"+json.dumps(vote_store, ensure_ascii=False))
        elif round == 3:
            state['stage'] = 'end'
            print("Human存活超过3轮,Human win!")
        else:
            print("Human存活,游戏继续!")
            print("上轮投票详情如下:"+json.dumps(vote_store, ensure_ascii=False))
            state = gen_and_dispatch_role(state)
        return state
    next = random.choice(state['waiting'])
    state['next_speaker'] = next
    state['waiting'].remove(next)
    return state
  • 最后:结果揭晓,基于投票判断人类扮演者是否被发现。

游戏角色

有了主持人Agent控场,那么其他角色就很好设计了,由于AI除了扮演角色不一样外,所执行的逻辑是一致的,我们可以统一抽象为AI角色扮演Agent ,再就是用于人类输出的人类Agent

  • AI角色扮演Agent:由四个AI NPC扮演历史知名人物,发表演讲,并在投票阶段进行投票。
python 复制代码
def speak(state: GameState):
    role = state['next_speaker']
    roles_str = ','.join(state['roles'])
    chat_history = state['chat_history']
    history = ""
    for chat in chat_history:
        history += f"{chat[0]}: {chat[1]}\n"
    llm = ChatOpenAI(model="glm-4",  temperature=0.8)
    role_prompt = ChatPromptTemplate.from_template(load_prompt("prompt/role.prompt"))
    chain = role_prompt|llm|StrOutputParser()
    rsp = chain.stream({"role": role, "roles": roles_str, "history": history})
    output = ''
    print(role+':', end='', flush=True)
    for token in rsp:
        output += token
        print(token, end='', flush=True)
    print()
    print("---------------------------")
    state['chat_history'].append((role, output))
    return state

def vote(state: GameState):
    role = state['next_speaker']
    roles_str = ','.join(state['roles'])
    chat_history = state['chat_history']
    history = ""
    for chat in chat_history:
        history += f"{chat[0]}: {chat[1]}\n"
    llm = ChatOpenAI(model="glm-4",  temperature=0.01)
    vote_prompt = ChatPromptTemplate.from_template(load_prompt("prompt/vote.prompt"))
    chain = vote_prompt|llm|JsonOutputParser()
    output = chain.invoke({"role": role, "roles": roles_str, "history": history})
    state['vote_store'].append((role, output))
    return state
  • 人类Agent : 人类Agent的逻辑相比AI更加简单只需要在恰当的阶段,提示人类玩家进行输出。
python 复制代码
def speak(state: GameState):
    role = state['next_speaker']
    print("你当前的角色是:"+role)
    user_input = input("请输入你的对话内容: ")
    state['chat_history'].append((role, user_input))
    return state

def vote(state: GameState):
    role = state['next_speaker']
    roles_str = ','.join(state['roles'])
    print("你当前的角色是:"+role)
    print("可供投票的选项有:"+roles_str)
    user_vote = input("请输入你的投票选择: ")
    user_reason = input("请输入你的投票原因: ")
    state['vote_store'].append((role, {"vote":user_vote,"reason":user_reason}))
    return state

实现效果及完整代码

最终实现效果如下所示

游戏的完整代码已上传github开源,有兴趣的小伙伴可以拉取到自己本地玩一玩:github.com/q2wxec/lang...

相关推荐
IT_陈寒12 小时前
闭包陷阱让我加了两天班,JavaScript你真行
前端·人工智能·后端
易协同低代码13 小时前
通达OA核心类库TD类深度解析
后端
Gopher_HBo13 小时前
Go语言学习笔记(十八)Gin处理Session
后端
谭光志14 小时前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
她说..14 小时前
Java 默认值设置方式
java·开发语言·后端·springboot
foggyprojects14 小时前
从0开始,一句话启动AI DataAgent
后端·数据分析·ai编程
郡杰14 小时前
一些基础和问题解决
后端
陈随易14 小时前
前端项目部署只要30秒
前端·后端·程序员
YIAN15 小时前
从零手写文件读取 MCP 服务:一文吃透 Model Context Protocol 全链路通信原理
前端·后端·mcp
Imchendiana15 小时前
《狂人日记NO.9》— 前后端一把梭,我的全栈实录
前端·后端