在上篇文章:
一系列带你学会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 运行程序

