Coze智能体实现人生模拟器

在上篇文章:

一系列带你学会Coze------Coze智能体开发https://blog.csdn.net/sniper_fandc/article/details/154959322?fromshare=blogdetail&sharetype=blogdetail&sharerId=154959322&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link 我们已经学习了Coze智能体的开发和相关API、SDK介绍,本文将使用Python的Coze SDK实现一个人生模拟器。

1 智能体开发

创建智能体,添加系统提示词:

复制代码
# 角色
你是"人生三万天",一个人生模拟器,能让用户体验不同的人生经历。你可以生动且富有代入感地为用户描述各种人生场景、挑战和机遇,用通俗易懂且富有感染力的语言让用户仿佛置身其中。

## 技能
### 技能 1: 开启人生模拟
1. 当用户要求开启人生模拟时,先询问用户希望模拟的人生主题方向,例如职场、情感、冒险等。如果用户已经指定主题,则直接进入下一步。
2. 根据用户选择的主题方向,开始模拟人生经历。描述人生的起点场景,包括人物设定、所处环境等基本信息。
===回复示例===
你开启了一段职场人生模拟。你叫[名字],22 岁,刚刚大学毕业,所学专业是[专业名称],怀揣着对未来的憧憬,你来到了[城市名称],准备在这个竞争激烈的职场中闯出一片天地。今天是你去[公司名称]面试的日子......
===示例结束===

### 技能 2: 推进人生模拟
1. 当用户要求推进模拟人生时,根据当前模拟人生的进展和场景,合理生成新的事件和发展。考虑到不同人生主题可能面临的各种情况,如职场中的晋升挑战、情感上的起伏、冒险中的意外等。
2. 在描述新事件时,要提供足够的细节,让用户感受到真实的人生体验。
===回复示例===
在你努力工作了几个月后,公司迎来了一个重要项目。领导决定让你负责其中一个关键部分,但时间紧迫,资源有限,你需要在一周内完成[具体任务]。这是一个提升自己的好机会,但同时也充满了挑战......
===示例结束===

### 技能 3: 提供人生选择
1. 在模拟人生过程中,遇到关键节点时,为用户提供不同的选择,并简要说明每个选择可能带来的后果。
2. 根据用户选择的方向,继续推进模拟人生。
===回复示例===
面对这个项目挑战,你有两个选择:
- 选择 A:向领导申请增加人手和资源,但可能会被认为能力不足,依赖他人。如果选择 A,接下来团队成员加入,项目进度可能加快,但人际关系方面可能出现一些小摩擦。
- 选择 B:独自承担,凭借自己的能力加班完成。如果选择 B,你可能会压力巨大,但成功完成后会得到领导的高度认可,未来晋升机会大增。
请做出你的选择。
===示例结束===

## 限制:
- 只讨论与人生模拟相关的内容,拒绝回答与人生模拟无关的话题。
- 所输出的内容必须按照给定的格式进行组织,不能偏离框架要求。
- 描述人生场景和事件时尽量简洁明了,避免过于冗长复杂的表述。
- 回答需基于合理的逻辑和对不同人生场景的理解,不能出现不合理的情节。 
- 整个游戏流程不超过10轮,可以出现选择某个选项后因为一些原因而导致人生体验结束。

模型配置如下:

注意:人生模拟器采用多轮对话的形式,每轮对话内容都与上轮选择相关,为了保证所有轮数的对话保持高相关性,建议将上下文轮数调多一点。

添加开场白:

发布智能体,注意勾选API和SDK:

保存智能体的id:url的bot/后的数字。

2 Python调用SDK

python 复制代码
import os
from cozepy import Coze, TokenAuth, ChatStatus, COZE_CN_BASE_URL, Message
from dotenv import load_dotenv
from flask import Flask, request, jsonify, send_file
from flask_cors import CORS
import uuid

# 将.env文件的变量加载到环境变量中
load_dotenv()

# 创建flask应用实例(web应用)
app = Flask(__name__)

# 允许跨域请求
CORS(app, resources={r"/*": {"origins": "*"}})

# 用户会话id - 使用字典存储会话
user_sessions = {}


class CozeService:
    def __init__(self):
        self.api_token = os.getenv("COZE_API_TOKEN")
        self.bot_id = os.getenv("BOT_ID", "7539845644169986098")
        self.coze = Coze(
            auth=TokenAuth(token=self.api_token),
            base_url=COZE_CN_BASE_URL
        )

    def get_ai_response(self, user_message, user_identifier):
        """获取智能体响应"""
        try:
            # 构建消息
            messages = [
                Message(
                    role="user",
                    content=user_message,
                    content_type="text",
                    type="question"
                )
            ]

            # 用户唯一标识在user_identifier用户会话id中: 使用该会话id继续聊天
            if user_identifier in user_sessions:
                session_data = user_sessions[user_identifier]
                conversation_id = session_data["conversation_id"]
                user_id = session_data["user_id"]

                # 使用会话id进行对话
                chat = self.coze.chat.create(
                    bot_id=self.bot_id,
                    user_id=user_id,
                    conversation_id=conversation_id,
                    additional_messages=messages,
                    auto_save_history=True
                )
            # 用户唯一标识不在user_identifier用户会话id中: 创建新会话
            else:
                # 生成唯一用户ID
                new_user_id = f"user_{uuid.uuid4().hex[:8]}"

                # 创建新对话
                chat = self.coze.chat.create(
                    bot_id=self.bot_id,
                    user_id=new_user_id,
                    additional_messages=messages,
                    auto_save_history=True
                )
                # 缓存用户会话
                user_sessions[user_identifier] = {
                    "conversation_id": chat.conversation_id,
                    "user_id": new_user_id,
                    "chat_id": chat.id
                }

            # 轮询等待完成
            while chat.status == ChatStatus.IN_PROGRESS:
                chat = self.coze.chat.retrieve(
                    conversation_id=chat.conversation_id,
                    chat_id=chat.id
                )

            # 获取结果
            if chat.status == ChatStatus.COMPLETED:
                messages = self.coze.chat.messages.list(
                    conversation_id=chat.conversation_id,
                    chat_id=chat.id
                )
                for msg in messages:
                    if msg.role == "assistant":
                        return {"status": "success", "content": msg.content}

            return {"status": "failed", "content": "对话未完成"}

        except Exception as e:
            return {"status": "error", "content": str(e)}


coze_service = CozeService()


# 根路径指向index.html 页面
@app.route("/")
def index():
    restart_session()
    return send_file('index.html')


# 重启会话接口
@app.route("/restart", methods=['POST'])
def restart_session():
    # 获取客户端标识
    user_identifier = request.remote_addr

    # 删除现有会话
    if user_identifier in user_sessions:
        del user_sessions[user_identifier]

    return jsonify({"status": "success", "message": "会话已重置"})


# 对话路由的接口
@app.route("/chat", methods=['POST'])
def chat():
    # 获取请求参数
    data = request.json
    user_message = data["message"]

    # 用户唯一标识使用客户端IP
    user_identifier = request.remote_addr

    result = coze_service.get_ai_response(user_message, user_identifier)
    return jsonify(result)


if __name__ == "__main__":
    app.run(debug=True, port=5000)

上述SDK本质都是对API的封装,可直接通过函数+参数的方式调用。比如获取对话消息使用list函数,其源码如下:

通过源码可以发现,请求的url是:

由于采用异步方式发送对话,因此需要轮询查询retrieve函数来获取对话是否结束的状态。

主要的后端流程就是:构建消息、创建对话(发送对话API)、轮询等待对话结果(查看对话详情API)、获取对话结果(查看对话消息API)。

注意:由于需要持续和智能体交互,因此用户的对话都需要保持同一个会话id。如果是开始游戏,就创建对话并保存会话id;如果已经存在会话id,就每次创建对话时使用该会话id。这样就可以保证用户的一次游戏始终保持同一个会话。

相关API可前往官网API文档/我的上篇文章学习。

3 前端页面

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI 人生模拟器</title>
    <style>
        :root {
            --bg-color: #1a1a1a;
            --card-bg: #2d2d2d;
            --text-color: #e0e0e0;
            --accent-color: #64ffda;
            --user-msg-bg: #3d4f5c;
            --ai-msg-bg: #2d2d2d;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            background-color: var(--bg-color);
            color: var(--text-color);
            height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
        }

        /* 容器布局 */
        .container {
            width: 100%;
            max-width: 800px;
            height: 90vh;
            background-color: #222;
            border-radius: 12px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
            display: flex;
            flex-direction: column;
            overflow: hidden;
            border: 1px solid #333;
        }

        /* 头部 */
        header {
            padding: 20px;
            background-color: #252525;
            border-bottom: 1px solid #333;
            text-align: center;
            position: relative;
        }

        header h1 {
            font-size: 1.2rem;
            letter-spacing: 1px;
            color: var(--accent-color);
        }

        .restart-btn {
            position: absolute;
            right: 20px;
            top: 50%;
            transform: translateY(-50%);
            background: transparent;
            border: 1px solid var(--accent-color);
            color: var(--accent-color);
            padding: 5px 12px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 0.8rem;
            transition: all 0.3s;
        }

        .restart-btn:hover {
            background: var(--accent-color);
            color: #000;
        }

        /* 聊天区域 */
        #chat-history {
            flex: 1;
            padding: 20px;
            overflow-y: auto;
            display: flex;
            flex-direction: column;
            gap: 15px;
            scroll-behavior: smooth;
        }

        .message {
            max-width: 85%;
            line-height: 1.6;
            padding: 12px 16px;
            border-radius: 8px;
            font-size: 0.95rem;
            position: relative;
            word-wrap: break-word;
        }

        /* AI 消息样式 */
        .message.ai {
            align-self: flex-start;
            background-color: var(--ai-msg-bg);
            border-left: 3px solid var(--accent-color);
            white-space: pre-wrap; /* 保留AI输出的换行格式 */
        }

        /* 用户消息样式 */
        .message.user {
            align-self: flex-end;
            background-color: var(--user-msg-bg);
            color: #fff;
            border-bottom-right-radius: 2px;
        }

        /* 输入区域 */
        .input-area {
            padding: 20px;
            background-color: #252525;
            border-top: 1px solid #333;
            display: flex;
            gap: 10px;
        }

        input[type="text"] {
            flex: 1;
            padding: 12px 15px;
            border-radius: 6px;
            border: 1px solid #444;
            background-color: #333;
            color: white;
            font-size: 1rem;
            outline: none;
            transition: border-color 0.3s;
        }

        input[type="text"]:focus {
            border-color: var(--accent-color);
        }

        button#send-btn {
            padding: 0 20px;
            background-color: var(--accent-color);
            color: #1a1a1a;
            border: none;
            border-radius: 6px;
            font-weight: bold;
            cursor: pointer;
            transition: opacity 0.3s;
        }

        button#send-btn:hover {
            opacity: 0.9;
        }

        button:disabled {
            background-color: #555 !important;
            cursor: not-allowed;
        }

        /* 加载动画 */
        .typing-indicator {
            align-self: flex-start;
            background-color: var(--ai-msg-bg);
            padding: 10px 15px;
            border-radius: 8px;
            display: none; /* 默认隐藏 */
            gap: 5px;
        }

        .dot {
            width: 8px;
            height: 8px;
            background-color: #888;
            border-radius: 50%;
            animation: bounce 1.4s infinite ease-in-out both;
        }

        .dot:nth-child(1) {
            animation-delay: -0.32s;
        }

        .dot:nth-child(2) {
            animation-delay: -0.16s;
        }

        @keyframes bounce {
            0%, 80%, 100% {
                transform: scale(0);
            }
            40% {
                transform: scale(1);
            }
        }

        /* 移动端适配 */
        @media (max-width: 600px) {
            .container {
                height: 100vh;
                border-radius: 0;
                max-width: 100%;
            }

            header h1 {
                font-size: 1rem;
            }
        }
    </style>
</head>
<body>

<div class="container">
    <header>
        <h1>人生模拟器</h1>
        <button class="restart-btn" onclick="restartGame()">重开人生</button>
    </header>

    <div id="chat-history">
        <!-- 欢迎消息 -->
        <div class="message ai">🌟 欢迎来到「我的未来我做主」毕业选择模拟器! 🌟
            你好!我是你的人生选择引导者。此刻,你将有机会体验到不同的人生,位于人生的十字路口,请谨慎对待每个选择。输入:开始,进行游戏。如果没有选择,请输入:继续。
        </div>
    </div>

    <!-- 加载中动画 -->
    <div class="typing-indicator" id="loading">
        <div class="dot"></div>
        <div class="dot"></div>
        <div class="dot"></div>
    </div>

    <div class="input-area">
        <input type="text" id="user-input" placeholder="输入你的选择..." autocomplete="off">
        <button id="send-btn" onclick="sendMessage()">发送</button>
    </div>
</div>

<script>
    const inputField = document.getElementById('user-input');
    const chatHistory = document.getElementById('chat-history');
    const sendBtn = document.getElementById('send-btn');
    const loadingIndicator = document.getElementById('loading');

    // 监听回车键
    inputField.addEventListener('keypress', function (e) {
        if (e.key === 'Enter') {
            sendMessage();
        }
    });

    // 发送消息主逻辑
    async function sendMessage() {
        const text = inputField.value.trim();
        if (!text) return;

        // 1. 显示用户消息
        appendMessage(text, 'user');
        inputField.value = '';

        // 锁定输入
        setInputState(false);
        loadingIndicator.style.display = 'flex';
        scrollToBottom();

        try {
            // 2. 请求后端
            const response = await fetch('/chat', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({message: text})
            });

            const data = await response.json();
            loadingIndicator.style.display = 'none';

            // 3. 处理返回结果
            if (data.status === 'success') {
                typeWriterEffect(data.content, 'ai');
            } else {
                appendMessage(`Error: ${data.content}`, 'ai');
                setInputState(true);
            }

        } catch (error) {
            loadingIndicator.style.display = 'none';
            appendMessage("连接服务器失败,请检查后端是否运行。", 'ai');
            console.error('Error:', error);
            setInputState(true);
        }
    }

    // 重置游戏
    async function restartGame() {
        // 先清除现有聊天记录
        chatHistory.innerHTML = '';

        try {
            // 请求后端重置会话
            await fetch('/restart', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                }
            });

            // 添加新的欢迎消息
            appendMessage('🌟 欢迎来到「我的未来我做主」毕业选择模拟器! 🌟\n你好!我是你的人生选择引导者。此刻,你将有机会体验到不同的人生,位于人生的十字路口,请谨慎对待每个选择。输入:开始,进行游戏。如果没有选择,请输入:继续。', 'ai');

            // 设置初始输入
            inputField.value = '开始';

        } catch (error) {
            console.error(' 重置会话失败:', error);
            appendMessage("重置会话失败,请重试", 'ai');
        }
    }

    // 添加消息到界面
    function appendMessage(content, role) {
        const msgDiv = document.createElement('div');
        msgDiv.classList.add('message', role);

        // 保留换行符
        msgDiv.innerHTML = content.replace(/\n/g, '<br>');

        chatHistory.appendChild(msgDiv);
        scrollToBottom();
        return msgDiv;
    }

    // 打字机效果函数
    function typeWriterEffect(text, role) {
        const msgDiv = appendMessage('', role);
        let i = 0;
        const speed = 20;
        const formattedText = text.replace(/\n/g, '<br>'); // 处理换行符

        function type() {
            if (i < formattedText.length) {
                // 处理HTML标签
                if (formattedText.substring(i, i + 4) === '<br>') {
                    msgDiv.innerHTML += '<br>';
                    i += 4;
                } else {
                    msgDiv.innerHTML += formattedText.charAt(i);
                    i++;
                }
                scrollToBottom();
                setTimeout(type, speed);
            } else {
                setInputState(true);
            }
        }

        type();
    }

    // 滚动到底部
    function scrollToBottom() {
        chatHistory.scrollTop = chatHistory.scrollHeight;
    }

    // 控制输入框状态
    function setInputState(enabled) {
        inputField.disabled = !enabled;
        sendBtn.disabled = !enabled;
        if (enabled) {
            inputField.focus();
        }
    }
</script>
</body>
</html>

前端页面是使用AI生成的,效果还是不错的,界面如下:

注意:这里AI给出的前端代码使用了typeWriterEffect函数来模拟一个字一个字生成的效果。实际上,可以直接使用流式输出(发起对话API的stream参数为true)来实现这样的效果,不必在前端还模拟流式数据。

4 测试与使用

4.1 添加.env文件

添加环境变量文件.env,内容如下(需要填写自己的令牌等数据):

python 复制代码
COZE_API_TOKEN = 令牌
BOT_ID = 智能体id
USER_ID = 用户id

4.2 运行程序

相关推荐
white-persist1 小时前
【攻防世界】reverse | Reversing-x64Elf-100 详细题解 WP
c语言·开发语言·网络·python·学习·安全·php
FeiHuo565151 小时前
微信个人号开发中如何高效实现API二次开发
java·开发语言·python·微信
love530love1 小时前
【保姆级教程】Windows + Podman 从零部署 Duix-Avatar 数字人项目
人工智能·windows·笔记·python·数字人·podman·duix-avatar
大模型真好玩1 小时前
低代码Agent开发框架使用指南(八)—Coze 知识库详解
人工智能·agent·coze
AI绘画哇哒哒4 小时前
【收藏必看】大模型智能体六大设计模式详解:从ReAct到Agentic RAG,构建可靠AI系统
人工智能·学习·ai·语言模型·程序员·产品经理·转行
u***32436 小时前
使用python进行PostgreSQL 数据库连接
数据库·python·postgresql
青瓷程序设计9 小时前
动物识别系统【最新版】Python+TensorFlow+Vue3+Django+人工智能+深度学习+卷积神经网络算法
人工智能·python·深度学习
tobebetter95279 小时前
How to manage python versions on windows
开发语言·windows·python
F_D_Z9 小时前
数据集相关类代码回顾理解 | sns.distplot\%matplotlib inline\sns.scatterplot
python·深度学习·matplotlib