使用Amazon Q开发线上版Builder Cards游戏,并用Bedrock生成数据优化游戏策略
使用Amazon Bedrock、Amazon Q、EC2、S3等多个产品来开发AWS Builder Cards线上游戏,让AWS粉丝在娱乐的同时掌握AWS产品服务和架构。
通过这个应用的开发,串联多个AWS产品的最佳实践,也能够为开发者提供思路和借鉴。
概述
大纲
-
使用Amazon Bedrock提炼AWS知识库,实现RAG以及效果测评和改进
-
使用Amazon Bedrock提供模型API、Prompt
-
使用Amazon Q开发Builder Cards游戏
-
评测RAG命中和优化效果,并持续改进
AWS产品电子卡牌游戏
-
线下纸质版Builder Cards需要玩家线下相聚,现在开发线上版Builder Cards,大家联网就可以参与
-
一个平台4个玩家,暂时没有设置不同玩家登录和选座,优先实现了核心功能
-
点击“发牌”可以为每位玩家分6张AWS产品卡牌
-
每个玩家依次出牌,如果当前卡牌中出现的产品能够组成AWS架构则可以“申请获胜”,不能组合成AWS架构时则“出牌”并随机补充一张新牌
-
直到有产品能组合成AWS架构并“申请获胜”,这时可以获得积分并继续打牌
-
提供新手级别(会提示当前产品卡牌是否能组合成AWS架构、会提示详细游戏规则)、高级玩家级别(系统不会提示当前产品卡牌是否能组合成AWS架构,需要人工来判断)
技术实现
Bedrock选择模型
-
通过模型来选择新手玩家、高级玩家等不同级别难度下的卡片数量、类别
-
通过模型来判断当前产品组合是否符合AWS架构
-
选择生图的模型
Bedrock知识库RAG处理数据
-
数据获取:先获取AWS产品和架构数据,获取微信文章、博客等文章信息
-
数据清洗:将获取到的数据进行清洗并加工为知识库RAG
-
设置重排序和分块策略
使用Bedrock API生成玩家头像
-
使用Bedrock生图模型服务生成玩家头像,并保存到AWS S3
安全性考虑
通过Bedrock Guadrial来进行敏感信息检测
优化改进:记录RAG命中记录并进行优化
-
记录数据到RDS,并且验证命中率,根据命中率优化产品卡片数量
通过Q来实现代码开发
基础开发
-
通过Q来实现基础代码开发
-
通过Q调用Amazon Bedrock API来获取模型服务能力
Agentic AI开发范式
-
先拆解需求、明确需求文档
-
和Amazon Q进行对话来对齐需求
-
让Amazon Q来生成TODO List
-
再进行开发
生成测试案例和文档
-
通过Amazon Q来进行测试
-
生成README.md文档
发布到EC2
-
将生成的代码发布到AWS EC2,对外提供IP的方式访问
代码实现
洗牌
// 洗牌
function shuffleDeck() {
for (let i = gameState.deck.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[gameState.deck[i], gameState.deck[j]] = [gameState.deck[j], gameState.deck[i]];
}
}
发牌
// 发牌
function dealCards() {
if (gameState.gameStarted) {
alert("游戏已经开始!");
return;
}
// 清空所有玩家的卡片和公共区域
gameState.players.forEach(player => {
player.cards = [];
});
gameState.publicCards = [];
// 给每个玩家发6张牌
for (let i = 0; i < 6; i++) {
for (let player of gameState.players) {
if (gameState.deck.length > 0) {
const card = gameState.deck.pop();
player.cards.push(card);
}
}
}
// 设置游戏状态
gameState.gameStarted = true;
gameState.currentPlayerIndex = 0;
$("#play-btn").prop("disabled", false);
$("#claim-win-btn").prop("disabled", true);
$("#deal-btn").prop("disabled", true);
// 更新UI
updateUI();
highlightCurrentPlayer();
// 更新操作提示
const currentPlayer = gameState.players[gameState.currentPlayerIndex];
updateOperationTip(`${currentPlayer.name}请选择一张卡片出牌,尝试组建有效的AWS架构`);
// 提供当前玩家的架构建议
suggestNextMove(currentPlayer);
}
// 创建卡片元素
function createCardElement(card) {
const cardElement = document.createElement('div');
cardElement.className = 'card';
cardElement.dataset.cardId = card.id;
const cardImage = document.createElement('div');
cardImage.className = 'card-image';
const img = document.createElement('img');
img.src = card.image;
img.alt = card.name;
cardImage.appendChild(img);
const cardContent = document.createElement('div');
cardContent.className = 'card-content';
const cardTitle = document.createElement('div');
cardTitle.className = 'card-title';
cardTitle.textContent = card.name;
const cardDescription = document.createElement('div');
cardDescription.className = 'card-description';
cardDescription.textContent = card.description;
cardContent.appendChild(cardTitle);
cardContent.appendChild(cardDescription);
cardElement.appendChild(cardImage);
cardElement.appendChild(cardContent);
return cardElement;
}
出牌
// 出牌
function playSelectedCard() {
if (!gameState.gameStarted) {
alert("请先发牌!");
updateOperationTip("点击'发牌'开始游戏");
return;
}
if (!gameState.selectedCard) {
alert("请先选择一张卡片!");
updateOperationTip("请先选择一张要出的卡片");
return;
}
const currentPlayer = gameState.players[gameState.currentPlayerIndex];
const playedCardName = gameState.selectedCard.name;
const playedCardCategory = getCardCategory(gameState.selectedCard);
// 从玩家手牌中移除选中的卡片
const cardIndex = currentPlayer.cards.findIndex(card => card.id === gameState.selectedCard.id);
if (cardIndex !== -1) {
currentPlayer.cards.splice(cardIndex, 1);
}
// 将卡片添加到公共区域
if (gameState.publicCards.length < 3) {
gameState.publicCards.push(gameState.selectedCard);
} else {
// 如果公共区域已满,替换第一张卡片
gameState.publicCards.shift();
gameState.publicCards.push(gameState.selectedCard);
}
// 重置选中的卡片
gameState.selectedCard = null;
// 出牌后自动从牌堆获取一张牌
let newCardInfo = "";
if (gameState.deck.length > 0) {
const newCard = gameState.deck.pop();
currentPlayer.cards.push(newCard);
newCardInfo = `获得了新卡片: ${newCard.name}`;
} else {
newCardInfo = "牌堆已空,无法获得新卡片";
}
// 切换到下一个玩家
gameState.currentPlayerIndex = (gameState.currentPlayerIndex + 1) % gameState.players.length;
const nextPlayer = gameState.players[gameState.currentPlayerIndex];
// 检查是否还有足够的牌继续游戏(但不自动判断获胜)
checkGameEnd();
// 更新UI
updateUI();
highlightCurrentPlayer();
// 更新操作提示
updateOperationTip(`${currentPlayer.name}出了${playedCardCategory ? playedCardCategory + '类别的' : ''}${playedCardName},${newCardInfo}。现在轮到${nextPlayer.name}出牌`);
// 为下一个玩家提供建议
suggestNextMove(nextPlayer);
}
为玩家提供下一步建议
// 为玩家提供下一步建议
function suggestNextMove(player) {
// 分析当前架构状态
const playerCards = player.cards;
const analysis = analyzeArchitecture(playerCards);
let suggestion = "";
if (analysis.categoryCount < 3) {
const missingCategories = Object.keys(awsCategories).filter(c => !Object.keys(analysis.categorizedCards).includes(c));
suggestion = `<span style="color: #2ecc71;">建议: 尝试获取这些缺失类别的卡片: ${missingCategories.slice(0, 3-analysis.categoryCount).join(", ")}</span>`;
} else if (analysis.isValid) {
// 架构已经有效
if (playerCards.length === 6) {
suggestion = '<span style="color: #f1c40f;">🎉 恭喜!你已经可以获胜了!</span>';
} else {
suggestion = '<span style="color: #2ecc71;">✓ 架构有效!可以继续收集卡片或申请验证</span>';
}
} else {
// 检查是否有重复类别过多的情况
let maxCategory = "";
let maxCount = 0;
for (const category in analysis.categorizedCards) {
const count = analysis.categorizedCards[category].length;
if (count > maxCount) {
maxCount = count;
maxCategory = category;
}
}
if (maxCount > 2) {
suggestion = `<span style="color: #f1c40f;">建议: ${maxCategory}类别有${maxCount}张卡片,考虑出掉一些以获取其他类别</span>`;
} else {
suggestion = '<span style="color: #2ecc71;">建议: 继续收集不同类别的AWS服务卡片</span>';
}
}
// 显示未分类卡片信息(仅作提示)
if (analysis.uncategorizedCards.length > 0) {
suggestion += `<br><span style="color: #3498db;">💡 你有${analysis.uncategorizedCards.length}张未分类卡片,不影响架构有效性</span>`;
}
// 更新架构指南中的建议
$(".architecture-guide").append(`<div class="move-suggestion">${suggestion}</div>`);
// 10秒后移除建议
setTimeout(() => {
$(".move-suggestion").remove();
}, 10000);
}
检查是否是AWS架构
可以使用模型来判断是否AWS架构,并且解析推荐架构方案、最佳实践
// 定义AWS架构类别
const awsCategories = {
compute: ["Amazon EC2", "AWS Lambda", "Amazon ECS", "Amazon EKS", "AWS Fargate", "AWS Batch"],
storage: ["Amazon S3", "Amazon EBS", "Amazon EFS", "Amazon FSx", "AWS Storage Gateway"],
database: ["Amazon RDS", "Amazon DynamoDB", "Amazon ElastiCache", "Amazon Redshift", "Amazon Aurora", "Amazon Neptune"],
networking: ["Amazon VPC", "Amazon CloudFront", "Amazon Route 53", "AWS Direct Connect", "Elastic Load Balancing"],
security: ["AWS IAM", "Amazon Cognito", "AWS Shield", "AWS WAF", "Amazon GuardDuty", "AWS KMS"],
integration: ["Amazon SNS", "Amazon SQS", "Amazon EventBridge", "AWS Step Functions", "Amazon API Gateway"]
};
// 检查卡片是否可以组成有效的AWS架构
function isValidAwsArchitecture(cards) {
// 检查是否至少包含3个不同类别的服务
const cardCategories = new Set();
// 检查每张卡片属于哪个类别
for (let card of cards) {
for (let category in awsCategories) {
if (awsCategories[category].includes(card.name)) {
cardCategories.add(category);
break;
}
}
}
// 只要有至少3个不同类别的AWS服务就算有效架构
// 即使有非AWS服务卡片也不影响
return cardCategories.size >= 3;
}