
Building a AI-powered study buddy for certifications
Your AI-powered CLI companion for any certification exam preparation! Ask questions about any topic in your study materials and get intelligent, personalized quizzes to test your knowledge.
Quick post to share the sample code for a talk I gave on 26 March 2025 for the Platformers Community .
The demo consists of a
main.py , and then additional ones for the data_store , quiz_engine , rag_handler , config_helper.py , and requirements.txt . It uses Amazon Bedrock , with a Knowledge Base , and stored data in Amazon DynamoDB .To try out this code locally, create a new folder called
Sample-Certs-CLI-Quiz-App with the following folder structure:plaintext
.├── config_helper.py├── main.py├── requirements.txt└── src├── data_store│ └── dynamo_client.py├── quiz_engine│ └── generator.py└── rag_handler└── knowledge_base.py
You can do that with the following commands (I'll add ones for Windows later, these are for Mac / Linux, but you can use them with Powershell on Windows):
Bash
# Create the required foldersmkdir -p Sample-CLI-Quiz-App/src/data_store \Sample-CLI-Quiz-App/src/quiq_engine \Sample-CLI-Quiz-App/src/rag_handler# Create empty placeholder filestouch Sample-CLI-Quiz-App/src/main.py \Sample-CLI-Quiz-App/src/config_helper.py \Sample-CLI-Quiz-App/src/data_store/dynamo_client.py \Sample-CLI-Quiz-App/src/quiz_engine/generator.py \Sample-CLI-Quiz-App/src/rag_handler/knowledge_base.py
For each the files below, paste the contents into each - pay attention to the names:
Here's the
main.py :Python
#!/usr/bin/env python3"""CLI Study Buddy: An AI-powered quiz application for certification exam preparation.Uses Bedrock Knowledge Base for intelligent question generation and DynamoDB for progress tracking.Key Components:- BedrockKnowledgeBase: Handles interaction with your study materials- QuizGenerator: Creates personalized quiz questions- DynamoDBClient: Stores and retrieves your progress"""import osimport sysimport argparseimport loggingimport uuidimport jsonfrom pathlib import Pathfrom datetime import datetime# Add src directory to path for module importssys.path.append(os.path.join(os.path.dirname(__file__), "src"))from rag_handler.knowledge_base import BedrockKnowledgeBasefrom quiz_engine.generator import QuizGeneratorfrom data_store.dynamo_client import DynamoDBClientfrom config_helper import get_kb_id# Set up logging to track application behaviorlogging.basicConfig(level=logging.INFO,format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")logger = logging.getLogger(__name__)# Default configuration settingsDEFAULT_REGION = "us-east-1"DEFAULT_MODEL_ID = "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-sonnet-20240229-v1:0" # Default model ARN formatDEFAULT_TABLE_NAME = "certification_quiz_results" # DynamoDB table for storing resultsDEFAULT_NUM_QUESTIONS = 5DEFAULT_DIFFICULTY = "medium"class CertQuizApp:"""Main application class that coordinates between Knowledge Base, Quiz Generator, and DynamoDB.Handles user interaction and manages quiz sessions."""def __init__(self, region=DEFAULT_REGION):"""Initialize the application with AWS region and set up componentsArgs:region (str): AWS region for all services"""self.region = regionself.kb = None # Knowledge Base clientself.quiz_generator = None # Quiz generation engineself.db_client = None # DynamoDB clientself.user_id = self._get_or_create_user_id() # Unique user identifierdef _get_or_create_user_id(self):"""Manage user identification by either retrieving existing ID or creating new one.Stores user ID in local config file for persistence.Returns:str: Unique user identifier"""config_dir = os.path.expanduser("~/.cert_quiz")config_file = os.path.join(config_dir, "config.json")# Ensure config directory existsos.makedirs(config_dir, exist_ok=True)# Load existing user ID if availableif os.path.exists(config_file):with open(config_file, "r") as f:config = json.load(f)return config.get("user_id", str(uuid.uuid4()))# Create new user ID if none existsuser_id = str(uuid.uuid4())with open(config_file, "w") as f:json.dump({"user_id": user_id}, f)return user_iddef initialize(self, kb_id):"""Set up all components needed for the quiz applicationArgs:kb_id (str): Identifier for your Knowledge Base"""logger.info("Initializing app components")# Set up connection to your study materials# Create model ARN with proper regionmodel_arn = DEFAULT_MODEL_ID.replace("us-east-1", self.region)self.kb = BedrockKnowledgeBase(kb_id=kb_id, region=self.region, model_id=model_arn)# Initialize the quiz generation engineself.quiz_generator = QuizGenerator(knowledge_base=self.kb)# Set up progress trackingself.db_client = DynamoDBClient(table_name=DEFAULT_TABLE_NAME,region=self.region)def run_quiz(self, topic, difficulty=DEFAULT_DIFFICULTY, num_questions=DEFAULT_NUM_QUESTIONS):"""Execute a complete quiz session including question generation, user interaction,and result storageArgs:topic (str): The subject to be testeddifficulty (str): Quiz difficulty level (easy/medium/hard)num_questions (int): Number of questions to generateReturns:float: Quiz score as a percentage"""if not self.quiz_generator:raise ValueError("Quiz generator not initialized")logger.info(f"Starting quiz on {topic} ({difficulty}, {num_questions} questions)")# Color coding for better user experienceGREEN = "\033[92m"RED = "\033[91m"YELLOW = "\033[93m"BLUE = "\033[94m"BOLD = "\033[1m"UNDERLINE = "\033[4m"END = "\033[0m"# Get questions from the Knowledge Basequestions = self.quiz_generator.create_quiz(topic=topic,difficulty=difficulty,num_questions=num_questions)# Track correct answers for scoringcorrect_answers = 0# Display quiz headerprint(f"\n{BOLD}{UNDERLINE}===== QUIZ: {topic.upper()} ====={END}")print(f"{BLUE}Difficulty: {difficulty.capitalize()}{END}")print(f"{BLUE}Number of questions: {num_questions}{END}")print(f"{YELLOW}Answer each question by entering A, B, C, or D.{END}\n")# Process each questionfor i, q in enumerate(questions):print(f"\n{BOLD}Question {i+1}: {q['question']}{END}")# Display answer optionsfor letter, option_text in q['options'].items():print(f" {letter}. {option_text}")# Get and validate user inputwhile True:user_answer = input(f"\n{YELLOW}Your answer: {END}").strip().upper()if user_answer in ['A', 'B', 'C', 'D']:breakprint(f"{RED}Invalid input. Please enter A, B, C, or D.{END}")# Check answer and provide feedbackis_correct = user_answer == q['answer']if is_correct:correct_answers += 1print(f"\n{GREEN}✓ Correct!{END}")else:print(f"\n{RED}✗ Incorrect. The correct answer is {q['answer']}.{END}")# Show explanation for learningprint(f"{BLUE}Explanation: {q['explanation']}{END}")# Calculate and display final scorescore = (correct_answers / len(questions)) * 100print(f"\n{BOLD}{UNDERLINE}===== QUIZ COMPLETE ====={END}")if score >= 80:print(f"{GREEN}{BOLD}Score: {score:.1f}% ({correct_answers}/{len(questions)} correct){END}")elif score >= 60:print(f"{YELLOW}{BOLD}Score: {score:.1f}% ({correct_answers}/{len(questions)} correct){END}")else:print(f"{RED}{BOLD}Score: {score:.1f}% ({correct_answers}/{len(questions)} correct){END}")# Store quiz results for progress trackingif self.db_client:self.db_client.save_quiz_result(user_id=self.user_id,topic=topic,score=score,num_questions=num_questions,difficulty=difficulty)return scoredef show_history(self, limit=10):"""Display previous quiz resultsArgs:limit (int): Maximum number of historical results to show"""if not self.db_client:raise ValueError("DynamoDB client not initialized")# Color coding for visual feedbackGREEN = "\033[92m"RED = "\033[91m"YELLOW = "\033[93m"BLUE = "\033[94m"BOLD = "\033[1m"UNDERLINE = "\033[4m"END = "\033[0m"# Retrieve quiz history from DynamoDBresults = self.db_client.get_user_results(self.user_id, limit=limit)if not results:print(f"\n{YELLOW}No quiz history found.{END}")return# Display history headerprint(f"\n{BOLD}{UNDERLINE}===== QUIZ HISTORY (Last {min(limit, len(results))}) ====={END}")print(f"{BOLD}{'Date':<20} {'Topic':<30} {'Score':<10} {'Difficulty':<10}{END}")print(f"{BLUE}{'-' * 70}{END}")# Display each historical resultfor result in results:# Format the timestamptimestamp = datetime.fromisoformat(result['timestamp'])date_str = timestamp.strftime("%Y-%m-%d %H:%M")# Color-code score based on performancescore = float(result['score'])if score >= 80:score_str = f"{GREEN}{score:<10.1f}{END}"elif score >= 60:score_str = f"{YELLOW}{score:<10.1f}{END}"else:score_str = f"{RED}{score:<10.1f}{END}"print(f"{date_str:<20} {result['topic']:<30} {score_str} {result['difficulty']:<10}")def show_topic_stats(self, topic):"""Display performance statistics for a specific topicArgs:topic (str): Topic to analyze"""if not self.db_client:raise ValueError("DynamoDB client not initialized")# Color coding for visual feedbackGREEN = "\033[92m"RED = "\033[91m"YELLOW = "\033[93m"BLUE = "\033[94m"BOLD = "\033[1m"UNDERLINE = "\033[4m"END = "\033[0m"# Get topic statistics from DynamoDBstats = self.db_client.get_topic_statistics(self.user_id, topic)print(f"\n{BOLD}{UNDERLINE}===== TOPIC STATISTICS: {topic.upper()} ====={END}")if stats['attempts'] == 0:print(f"{YELLOW}No quiz attempts found for this topic.{END}")return# Display comprehensive statisticsprint(f"{BLUE}Attempts: {stats['attempts']}{END}")# Color-code average score based on performanceavg_score = float(stats['average_score'])if avg_score >= 80:print(f"{GREEN}Average Score: {avg_score:.1f}%{END}")elif avg_score >= 60:print(f"{YELLOW}Average Score: {avg_score:.1f}%{END}")else:print(f"{RED}Average Score: {avg_score:.1f}%{END}")print(f"{GREEN}Highest Score: {float(stats['highest_score']):.1f}%{END}")print(f"{RED}Lowest Score: {float(stats['lowest_score']):.1f}%{END}")print(f"{BLUE}Total Questions Answered: {stats['total_questions']}{END}")def show_kb_info(self):"""Display information about the Knowledge Base and its data sources"""if not self.kb:raise ValueError("Knowledge Base not initialized")# Color coding for visual feedbackGREEN = "\033[92m"BLUE = "\033[94m"BOLD = "\033[1m"UNDERLINE = "\033[4m"END = "\033[0m"# Get Knowledge Base detailskb_info = self.kb.get_knowledge_base_info()# Display Knowledge Base informationprint(f"\n{BOLD}{UNDERLINE}===== KNOWLEDGE BASE INFORMATION ====={END}")print(f"{BLUE}ID: {kb_info.get('knowledgeBaseId')}{END}")print(f"{BLUE}Name: {kb_info.get('name')}{END}")print(f"{BLUE}Description: {kb_info.get('description')}{END}")print(f"{GREEN}Status: {kb_info.get('status')}{END}")print(f"{BLUE}Created: {kb_info.get('createdAt')}{END}")print(f"{BLUE}Last Modified: {kb_info.get('updatedAt')}{END}")# List connected data sourcesdata_sources = self.kb.list_data_sources()print(f"\n{BOLD}Data Sources ({len(data_sources)}):{END}")for ds in data_sources:print(f" {GREEN}- {ds.get('name')}: {ds.get('dataSourceId')} ({ds.get('status')}){END}")def main():"""CLI entry point with argument parsing and command routing"""parser = argparse.ArgumentParser(description="CLI Study Buddy: Your AI-powered certification exam preparation assistant")# Set up command subparserssubparsers = parser.add_subparsers(dest="command", help="Command to run")# Knowledge Base info commandinfo_parser = subparsers.add_parser("info", help="Show Knowledge Base information")info_parser.add_argument("--kb-id", help="Knowledge Base ID (optional if set in .env)")# Quiz commandquiz_parser = subparsers.add_parser("quiz", help="Start a quiz session")quiz_parser.add_argument("--kb-id", help="Knowledge Base ID (optional if set in .env)")quiz_parser.add_argument("--topic", help="Topic to be tested (optional, will prompt if not provided)")quiz_parser.add_argument("--general", action="store_true", help="Generate a general quiz across all topics")quiz_parser.add_argument("--difficulty",choices=["easy", "medium", "hard"],default=DEFAULT_DIFFICULTY,help="Quiz difficulty level")quiz_parser.add_argument("--questions",type=int,default=DEFAULT_NUM_QUESTIONS,help="Number of questions to generate")quiz_parser.add_argument("--model",help="Bedrock model ID to use (defaults to Claude 3 Sonnet)")# History commandhistory_parser = subparsers.add_parser("history", help="View your quiz history")history_parser.add_argument("--kb-id", help="Knowledge Base ID (optional if set in .env)")history_parser.add_argument("--limit",type=int,default=10,help="Maximum number of results to show")# Statistics commandstats_parser = subparsers.add_parser("stats", help="View topic statistics")stats_parser.add_argument("--kb-id", help="Knowledge Base ID (optional if set in .env)")stats_parser.add_argument("--topic", required=True, help="Topic to analyze")# Global optionsparser.add_argument("--region", default=DEFAULT_REGION, help="AWS region")# Parse and process argumentsargs = parser.parse_args()# Get Knowledge Base ID from args or configkb_id = get_kb_id(args)if not kb_id and args.command != None:print("Error: Knowledge Base ID is required. Please provide it with --kb-id or set it in .env file.")sys.exit(1)# Initialize applicationapp = CertQuizApp(region=args.region)# Route to appropriate command handlerif args.command == "info":app.initialize(kb_id=kb_id)app.show_kb_info()elif args.command == "quiz":app.initialize(kb_id=kb_id)# Override default model if specifiedif hasattr(args, 'model') and args.model:app.kb.model_id = args.model# Handle general quiz optionif hasattr(args, 'general') and args.general:topic = "General Knowledge"else:# If topic is not provided, prompt the usertopic = args.topicif not topic:topic = input("Enter a topic for your quiz: ")app.run_quiz(topic, args.difficulty, args.questions)elif args.command == "history":app.initialize(kb_id=kb_id)app.show_history(args.limit)elif args.command == "stats":app.initialize(kb_id=kb_id)app.show_topic_stats(args.topic)else:parser.print_help()if __name__ == "__main__":main()
The
config_helper.py :Python
"""Configuration Helper Module: Handles loading and managing configuration settings.Provides functions for retrieving environment variables and command-line arguments."""import osimport loggingfrom pathlib import Pathfrom dotenv import load_dotenvlogger = logging.getLogger(__name__)# Load environment variables from .env fileload_dotenv()def get_kb_id(args):"""Get Knowledge Base ID from command-line arguments or environment variables.Args:args: Command-line argumentsReturns:str: Knowledge Base ID or None if not found"""# First check command-line argumentsif hasattr(args, 'kb_id') and args.kb_id:return args.kb_id# Then check environment variableskb_id = os.getenv('KB_ID') or os.getenv('BEDROCK_KB_ID')if kb_id:return kb_id# Not foundreturn None
The
scr/data_store/dynamo_client.py :Python
"""DynamoDB Client Module: Handles storage and retrieval of quiz results.Provides methods for tracking progress and analyzing performance."""import loggingimport boto3from botocore.exceptions import ClientErrorfrom datetime import datetimefrom decimal import Decimallogger = logging.getLogger(__name__)class DynamoDBClient:"""Handles interaction with DynamoDB for storing and retrieving quiz results."""def __init__(self, table_name, region="us-east-1"):"""Initialize the DynamoDB client.Args:table_name (str): DynamoDB table nameregion (str): AWS region"""self.table_name = table_nameself.region = regionself.dynamodb = boto3.resource('dynamodb', region_name=region)self.table = self.dynamodb.Table(table_name)self._ensure_table_exists()def _ensure_table_exists(self):"""Ensure the DynamoDB table exists, create it if it doesn't."""try:self.table.table_statuslogger.info(f"DynamoDB table {self.table_name} exists")except ClientError as e:if e.response['Error']['Code'] == 'ResourceNotFoundException':logger.info(f"Creating DynamoDB table {self.table_name}")self._create_table()else:logger.error(f"Error checking DynamoDB table: {e}")raisedef _create_table(self):"""Create the DynamoDB table for storing quiz results."""try:table = self.dynamodb.create_table(TableName=self.table_name,KeySchema=[{'AttributeName': 'user_id','KeyType': 'HASH' # Partition key},{'AttributeName': 'timestamp','KeyType': 'RANGE' # Sort key}],AttributeDefinitions=[{'AttributeName': 'user_id','AttributeType': 'S'},{'AttributeName': 'timestamp','AttributeType': 'S'},{'AttributeName': 'topic','AttributeType': 'S'}],GlobalSecondaryIndexes=[{'IndexName': 'TopicIndex','KeySchema': [{'AttributeName': 'user_id','KeyType': 'HASH'},{'AttributeName': 'topic','KeyType': 'RANGE'}],'Projection': {'ProjectionType': 'ALL'},'ProvisionedThroughput': {'ReadCapacityUnits': 5,'WriteCapacityUnits': 5}}],ProvisionedThroughput={'ReadCapacityUnits': 5,'WriteCapacityUnits': 5})# Wait for table creationtable.meta.client.get_waiter('table_exists').wait(TableName=self.table_name)logger.info(f"DynamoDB table {self.table_name} created successfully")except ClientError as e:logger.error(f"Error creating DynamoDB table: {e}")raisedef save_quiz_result(self, user_id, topic, score, num_questions, difficulty):"""Save a quiz result to DynamoDB.Args:user_id (str): Unique user identifiertopic (str): Quiz topicscore (float): Quiz score as percentagenum_questions (int): Number of questions in the quizdifficulty (str): Quiz difficulty level"""logger.info(f"Saving quiz result for user {user_id}, topic {topic}")timestamp = datetime.utcnow().isoformat()try:self.table.put_item(Item={'user_id': user_id,'timestamp': timestamp,'topic': topic,'score': Decimal(str(score)), # Convert float to Decimal'num_questions': num_questions,'difficulty': difficulty})logger.info("Quiz result saved successfully")except ClientError as e:logger.error(f"Error saving quiz result: {e}")raisedef get_user_results(self, user_id, limit=10):"""Get quiz results for a specific user.Args:user_id (str): Unique user identifierlimit (int): Maximum number of results to returnReturns:list: Quiz results"""logger.info(f"Getting quiz results for user {user_id}")try:response = self.table.query(KeyConditionExpression=boto3.dynamodb.conditions.Key('user_id').eq(user_id),ScanIndexForward=False, # Sort in descending order (newest first)Limit=limit)return response.get('Items', [])except ClientError as e:logger.error(f"Error getting user results: {e}")raisedef get_topic_statistics(self, user_id, topic):"""Get statistics for a specific topic.Args:user_id (str): Unique user identifiertopic (str): Topic to analyzeReturns:dict: Topic statistics"""logger.info(f"Getting statistics for user {user_id}, topic {topic}")try:response = self.table.query(IndexName='TopicIndex',KeyConditionExpression=boto3.dynamodb.conditions.Key('user_id').eq(user_id) &boto3.dynamodb.conditions.Key('topic').eq(topic))results = response.get('Items', [])if not results:return {'attempts': 0,'average_score': 0,'highest_score': 0,'lowest_score': 0,'total_questions': 0}# Calculate statisticsscores = [float(result['score']) for result in results]total_questions = sum(int(result['num_questions']) for result in results)stats = {'attempts': len(results),'average_score': Decimal(str(sum(scores) / len(scores))),'highest_score': Decimal(str(max(scores))),'lowest_score': Decimal(str(min(scores))),'total_questions': total_questions}return statsexcept ClientError as e:logger.error(f"Error getting topic statistics: {e}")raise
The
src/quiq_engine/generator.py :Python
"""Quiz generator module for creating quizzes based on Knowledge Base content"""import loggingimport jsonimport refrom botocore.exceptions import ClientErrorlogger = logging.getLogger(__name__)class QuizGenerator:"""Class for generating quizzes based on Knowledge Base content"""def __init__(self, knowledge_base):"""Initialize the quiz generatorArgs:knowledge_base (BedrockKnowledgeBase): Knowledge Base handler"""self.knowledge_base = knowledge_basedef create_quiz(self, topic, difficulty, num_questions):"""Create a quiz on a specific topicArgs:topic (str): Topic for the quizdifficulty (str): Difficulty level (easy, medium, hard)num_questions (int): Number of questions to generateReturns:list: List of quiz questions"""logger.info(f"Generating {num_questions} {difficulty} questions about {topic}")if topic.lower() == "general knowledge":return self._create_general_quiz(difficulty, num_questions)else:return self._create_topic_quiz(topic, difficulty, num_questions)def _create_topic_quiz(self, topic, difficulty, num_questions):"""Create a quiz on a specific topicArgs:topic (str): Topic for the quizdifficulty (str): Difficulty level (easy, medium, hard)num_questions (int): Number of questions to generateReturns:list: List of quiz questions"""# Create prompt template for retrieve and generateprompt_template = f"""You are an expert quiz creator for certification exam preparation.Based on the retrieved information about "{topic}", create {num_questions} multiple-choice questions at {difficulty} difficulty level.For each question:1. Make sure it's at {difficulty} difficulty level2. Include 4 possible answers (A, B, C, D)3. Mark the correct answer4. Provide a brief explanation for why the answer is correctFormat each question like this:Q1: [Question text]A. [Specific option text for A]B. [Specific option text for B]C. [Specific option text for C]D. [Specific option text for D]Answer: [Correct letter]Explanation: [Brief explanation]Make sure the questions are challenging but fair, and directly related to "{topic}"."""try:# Use retrieve and generategenerated_text = self.knowledge_base.retrieve_and_generate(query=f"Tell me about {topic} for a certification exam",prompt_template=prompt_template,num_results=5)# Parse the generated text into structured questionsquestions = self._parse_questions(generated_text)return questionsexcept ClientError as e:logger.error(f"Error generating topic quiz: {e}")raisedef _create_general_quiz(self, difficulty, num_questions):"""Create a general quiz across multiple topicsArgs:difficulty (str): Difficulty level (easy, medium, hard)num_questions (int): Number of questions to generateReturns:list: List of quiz questions"""logger.info(f"Generating general knowledge quiz with {num_questions} {difficulty} questions")# Create prompt template for retrieve and generateprompt_template = f"""You are an expert quiz creator for certification exam preparation.Based on the retrieved information from various topics, create {num_questions} multiple-choice questions at {difficulty} difficulty level.Important: Include questions from different topics and subject areas to create a diverse general knowledge quiz.For each question:1. Make sure it's at {difficulty} difficulty level2. Include 4 possible answers (A, B, C, D)3. Mark the correct answer4. Provide a brief explanation for why the answer is correctFormat each question like this:Q1: [Question text]A. [Specific option text for A - not just "A"]B. [Specific option text for B - not just "B"]C. [Specific option text for C - not just "C"]D. [Specific option text for D - not just "D"]Answer: [Correct letter]Explanation: [Brief explanation]Make sure the questions are challenging but fair, and cover a variety of topics from the study materials."""try:# Use retrieve and generategenerated_text = self.knowledge_base.retrieve_and_generate(query="Give me diverse information from different topics for a general knowledge quiz",prompt_template=prompt_template,num_results=5)# Parse the generated text into structured questionsquestions = self._parse_questions(generated_text)return questionsexcept ClientError as e:logger.error(f"Error generating general quiz: {e}")raisedef _parse_questions(self, text):"""Parse the generated text into structured questionsArgs:text (str): Generated text containing questionsReturns:list: List of structured questions"""# Split the text into individual questionsquestion_pattern = r'Q\d+:|Question \d+:'raw_questions = re.split(question_pattern, text)# Remove any empty stringsraw_questions = [q.strip() for q in raw_questions if q.strip()]structured_questions = []for i, raw_q in enumerate(raw_questions):try:# Extract the question text, options, answer, and explanationlines = raw_q.split('\n')question_text = lines[0].strip()options = {}answer = Noneexplanation = Nonecorrect_index = None# Process each line to extract options, answer, and explanationfor line in lines[1:]:line = line.strip()if not line:continue# Extract options (A, B, C, D)option_match = re.match(r'^([A-D])[.:]?\s+(.+)$', line)if option_match:letter, text = option_match.groups()options[letter] = text# Extract answeranswer_match = re.match(r'^Answer:?\s+([A-D]).*$', line)if answer_match:answer = answer_match.group(1)# Extract explanationif line.startswith('Explanation:'):explanation = line[12:].strip()elif explanation and not option_match and not answer_match and not line.startswith('Q'):explanation += ' ' + line# Convert answer letter to index (A=0, B=1, etc.)if answer:correct_index = ord(answer) - 65 # A=0, B=1, C=2, D=3# Create structured question if we have all required componentsif question_text and options and answer and explanation and len(options) == 4:structured_questions.append({'question': question_text,'options': options,'answer': answer,'explanation': explanation})except Exception as e:logger.error(f"Error parsing question {i+1}: {e}")continuereturn structured_questions
The
src/rag_handler/knowledge_base.py :Python
"""Knowledge Base handler for interacting with Amazon Bedrock Knowledge Bases"""import loggingimport jsonimport boto3from botocore.exceptions import ClientErrorlogger = logging.getLogger(__name__)class BedrockKnowledgeBase:"""Class for interacting with Amazon Bedrock Knowledge Bases"""def __init__(self, kb_id, region="us-east-1", model_id=None):"""Initialize the Knowledge Base handlerArgs:kb_id (str): Knowledge Base IDregion (str): AWS regionmodel_id (str): Bedrock model ID for generation"""self.kb_id = kb_idself.region = regionself.model_id = model_idself.bedrock_agent_runtime = boto3.client('bedrock-agent-runtime', region_name=region)self.bedrock_runtime = boto3.client('bedrock-runtime', region_name=region)def query_knowledge_base(self, query, num_results=3):"""Query the Knowledge Base using the retrieve APIArgs:query (str): Query textnum_results (int): Number of results to returnReturns:list: List of retrieved passages"""logger.info(f"Querying Knowledge Base: {query}")try:response = self.bedrock_agent_runtime.retrieve(knowledgeBaseId=self.kb_id,retrievalQuery={'text': query},retrievalConfiguration={'vectorSearchConfiguration': {'numberOfResults': num_results}})# Extract and return the retrieved passagesretrieved_results = []for result in response.get('retrievalResults', []):content = result.get('content', {}).get('text', '')if content:retrieved_results.append(content)return retrieved_resultsexcept ClientError as e:logger.error(f"Error querying Knowledge Base: {e}")raisedef retrieve_and_generate(self, query, prompt_template, num_results=3):"""Query the Knowledge Base and generate content using the RetrieveAndGenerate APIArgs:query (str): Query textprompt_template (str): Template for the prompt to send to the modelnum_results (int): Number of results to returnReturns:str: Generated content"""logger.info(f"Using RetrieveAndGenerate API for query: {query}")try:# Create the full prompt with templatefull_prompt = f"""You are an expert quiz creator for certification exam preparation.Based on the retrieved information, please:{prompt_template}"""# Use the unified RetrieveAndGenerate APIresponse = self.bedrock_agent_runtime.retrieve_and_generate(input={'text': query + "\n\n" + full_prompt},retrieveAndGenerateConfiguration={'type': 'KNOWLEDGE_BASE','knowledgeBaseConfiguration': {'knowledgeBaseId': self.kb_id,'modelArn': self.model_id}})# Parse responsegenerated_text = response.get('output', {}).get('text', '')return generated_textexcept ClientError as e:logger.error(f"Error with retrieve and generate: {e}")raisedef get_knowledge_base_info(self):"""Get information about the Knowledge BaseReturns:dict: Knowledge Base information"""try:bedrock_agent = boto3.client('bedrock-agent', region_name=self.region)response = bedrock_agent.get_knowledge_base(knowledgeBaseId=self.kb_id)return responseexcept ClientError as e:logger.error(f"Error getting Knowledge Base info: {e}")raisedef list_data_sources(self):"""List data sources for the Knowledge BaseReturns:list: List of data sources"""try:bedrock_agent = boto3.client('bedrock-agent', region_name=self.region)response = bedrock_agent.list_data_sources(knowledgeBaseId=self.kb_id)return response.get('dataSourceSummaries', [])except ClientError as e:logger.error(f"Error listing data sources: {e}")raise
Finally, the
requirements.txt :Python
boto3>=1.28.0botocore>=1.31.0python-dateutil>=2.8.2python-dotenv>=1.0.0
Coming soon: Instructions on how to create a virtual environment, install the required libraries, and then how to run the app :)
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article