DEV Community

Cover image for AWS re:Invent 2025 - Unified knowledge access: Bridging data with generative AI agents (AIM338)
Kazuya
Kazuya

Posted on

AWS re:Invent 2025 - Unified knowledge access: Bridging data with generative AI agents (AIM338)

🦄 Making great presentations more accessible.
This project aims to enhances multilingual accessibility and discoverability while maintaining the integrity of original content. Detailed transcriptions and keyframes preserve the nuances and technical insights that make each session compelling.

Overview

📖 AWS re:Invent 2025 - Unified knowledge access: Bridging data with generative AI agents (AIM338)

In this video, AWS Solutions Architects Aneel Murari and Rafia Tapia demonstrate building an AI agent that bridges structured and unstructured data using Strands SDK. They create a charity chatbot that queries both Aurora PostgreSQL databases and Bedrock Knowledge Bases simultaneously. The session covers implementing the retrieve tool for unstructured data access and building a custom query_SQL_DB tool that uses an LLM to generate SQL queries dynamically from natural language questions. Key highlights include using Claude for agent orchestration and Nova for SQL generation, implementing proper schema extraction, and creating effective system prompts. The demo shows how the agent intelligently routes queries to appropriate data sources—structured database for membership counts or Knowledge Bases for campaign information—based on question context.


; This article is entirely auto-generated while preserving the original presentation content as much as possible. Please note that there may be typos or inaccuracies.

Main Part

Introduction: Building AI Agents to Bridge Structured and Unstructured Data

Hello everyone. Welcome. Thank you for joining us today. I'm assuming this is your first re:Invent session for 2025. Thank you for giving us that privilege. Today, we are going to talk about bridging structured and unstructured data using AI agents.

Let me begin by asking you a question. How many of you here are building AI agents or maybe running AI agents in production or experimenting with it? Well, quite a few of you. Right. Anybody want to tell me what kind of data you're using for these AI agents? Is it going against unstructured data, like files in S3? All right. Is there any data that is in relational data sources like structured data? Yeah, a few of you.

So usually in enterprises, a lot of knowledge that we have from all these enterprise systems from the last 15 to 20 years is logged in relational databases. That's the predominant data storage mechanism in large enterprises. We also have a lot of unstructured data over the past 10 to 15 years because so much data is being generated in all forms and shapes. This talk is about how you use AI agents to bridge that data that is in different silos—in structured data and unstructured data—to bridge all of that together and have an intelligent system that can go across these data silos.

This is a core talk, and we will be demonstrating and building an application. You don't need laptops to follow along. We probably won't have enough time to type all the code, so we'll be using VS Code snippets to bring in blocks of code. But you'll generally get the idea of how to build this type of application.

My name is Aneel Murari. I'm a Senior Solutions Architect with AWS. I work with our nonprofit and financial industry customers. I've been with AWS for 5 years. I'm joined today by my amazing colleague, Rafia.

Hello everyone. My name is Rafia Tapia, and I'm a Senior Solutions Architect also working with nonprofit customers. I've been with AWS for a little more than 6 years now, and I'm really excited to share some of the code we've created for this code talk. So we can start.

Thumbnail 0

Architecture Overview: Combining Knowledge Bases for Amazon Bedrock with Amazon Aurora

Thumbnail 180

All right. So the first thing we'll do is briefly talk about the architecture of the application and the code which we will be building today. On the far left-hand side, you'll see a UI application. We've written that in Streamlit, nothing complicated. That UI will be able to take a user question and answer that user question via two types of data.

One is the unstructured data which we have shown on the top yellow pane on the architecture diagram. The unstructured data would be coming from Knowledge Bases for Amazon Bedrock. We have one slide where I will be talking a little bit more about Knowledge Bases for Amazon Bedrock. Then there's the structured data which will be coming from Amazon Aurora. If anyone doesn't know what Aurora is, Aurora is our managed relational database service. We support MySQL and PostgreSQL, among other databases, but for this demo, we will be targeting PostgreSQL for all the structured data.

Thumbnail 270

Let's move on to the next one. Before we go in, I just want to make sure everyone knows what are the SDKs and the agent tools we will be using. So we will be building our agent using Strands SDK. Is there anyone who has already done some work with Strands in this room? Oh nice. We have a couple of hands. Very nice. All right, so Strands allows you to build a single agent as well as multi-agent where the agents can talk with each other. Strands supports MCP as well as A2A protocol.

We are showing you a simple agent where a prompt is sent to the agent. The agent has certain tools built into it, and based on the tool, it will be able to provide the service which this prompt is asking for. That is the model—a simple, single agent which we will be creating in today's code talk.

Thumbnail 340

The next topic is about how we access unstructured data. How many of you have written RAG applications? I see quite a few hands. You guys know that when we write a RAG application, we take our data, most likely unstructured data, maybe in a PDF format or Word format or plain log files, and then convert that into vectors because that's what our LLM understands. We are then able to use that knowledge which is stored in those unstructured documents and provide AI solutions on top of it.

Bedrock Knowledge Bases allow us to build all that RAG infrastructure very effectively and easily. This is what we will be showing you today. We will be taking some files which have content in, let's say, a Word document and then putting it into a Bedrock Knowledge Base and be able to query that via our agent. Because of time constraints, we have already created a knowledge base for our code talk, but if any of you are interested in knowing how to build a knowledge base from scratch, please come and talk to us after the session and we will be more than happy to point you to some resources.

Thumbnail 430

Thumbnail 460

Thumbnail 470

This is the application that we are going to build today. No surprise here—we both are from the nonprofit group, so we picked a charity use case. We are building a charity chat assistant, a chatbot that will go against structured and unstructured data. We have all the campaign information and donation information in a relational database in Aurora, and we have some unstructured campaign-related data in an S3 bucket which we are accessing through our Bedrock Knowledge Base.

Thumbnail 480

Thumbnail 490

As you can see, if you ask some questions that need to go against the relational data source, the agentic application that we are building will figure out what is the right data source to go to and get that data automatically from that corresponding data source. We are going to see all of this and how we are going to build this.

Thumbnail 510

Thumbnail 530

Setting Up the Foundation: Creating the Streamlit UI Shell

Let me switch to my IDE mode. For the most part, we will try to take out the UI aspect very fast because there is really nothing extraordinary or something that you might be interested in learning today. The reason we have this UI is so we can show you a complete agent. As Anil is bringing the code, you will see that we will start with a very basic structure of the UI.

I am using my favorite IDE, Quiro, here. If you are not familiar with Quiro, it is an IDE that Amazon supports and released recently. It is an IDE where you can do white coding and also spec-driven development. We are not going to do any of that today—we are just going to demonstrate building this application. You can use any IDE for that matter.

Thumbnail 600

I am going to start with an empty folder here. I already have some files here. Let me quickly go through what those files are doing. I created a virtual Python environment of 3.12 and I have a requirements file with all the libraries that we are going to use today. Rafael talked about Strands, Strands Agent, and Agent Tools.

If you're working with Strands, there are two packages you need to make sure you bring into your environment to use the APIs and tools we'll be showing you. One is Strands Agent and the other is Strands Agent Tools. By the way, Strands currently only supports Python. I also have a Run.sh file that we're going to use to build a Streamlit app for the UI. This Run.sh file sets up my environment, creates a virtual environment if one doesn't exist, and starts my Streamlit app at the end.

Thumbnail 650

Thumbnail 680

Thumbnail 690

We have these two files already prepared to save time. Let me start by creating a new file called app.py, which will contain most of our UI code. Rather than having me type all this code in front of you, which would be tedious to watch, we're using VS Code snippets to bring in the code we need. We're going to first build the shell of the UI that we showed earlier. This first snippet handles imports and sets up some constants. For those of you who noticed the sample questions on the left-hand side, this snippet brings in all those things.

Thumbnail 710

Thumbnail 720

Thumbnail 730

This file isn't the most exciting part, but I'll quickly go over it. This will set up the dark theme for the app. Then I'll bring in the snippet for the chat interface. We've just brought in a shell of a Streamlit application. There's one thing I want you to note though: I've commented out this piece of code, which we'll uncomment later to add the agent capabilities. For now, it's doing nothing.

Thumbnail 740

Thumbnail 750

Thumbnail 780

Let's try to run this and see what happens. Right now, the UI isn't intelligent. Even if you ask a question, it might not be able to answer it correctly. This is the empty application. Let's see if we can ask a question. I'm expecting an error because we don't have any agentic capabilities yet. As expected, there is an error here. Let's move on to the more exciting part of the session today.

Thumbnail 790

Thumbnail 800

Thumbnail 820

Thumbnail 830

Thumbnail 860

Building the Base Agent with Strands SDK and Claude

I'm going to add another file called agent.py, which will contain our core logic for the Strands Agent. I'll start with some import statements. As we build this agent, we'll walk through it line by line. First, we need to import some packages, particularly the Strands packages, and we have other packages like Boto3 because we'll be going against Aurora database and Bedrock KB. The next thing you'll see is that because we're going against an LLM model, we're using a model ID, which is Claude. Our agent will use Claude when a user gives a question. It will take that question, feed it into the agent, and the agent will use an LLM to answer it. The model it'll use is Claude. The Strands SDK allows you to talk to any LLM, even those outside of Bedrock. We've chosen Bedrock as a supported LLM, but you're free to use any other LLM.

Thumbnail 920

Now we're declaring some inference parameters like maximum tokens allowed and temperature. The first thing Anil added in the code from line 18 to 22 is the base model we're creating. We're using a model ID, which is a cloud model ID, and we've given certain parameters. Then the code from line 28 to 32 is using the Strands agent to create a base agent. This agent has no intelligence in it yet. We just wanted to show you two lines of code for how an agent is created. You'll see it's as easy as instantiating an agent object. That agent object takes two things: a model ID, which we've already built in from line 18 to line 22, and a model object. The second thing it takes is a system prompt. At this point, the agent is really just a blank canvas with no intelligence built in.

Thumbnail 1010

Thumbnail 1020

Thumbnail 1030

Thumbnail 1040

Thumbnail 1050

Notice in the prompt we said don't make up answers. If you don't know, say you don't know. Let's run this application again and see what happens. Before you run this, I think you have to uncomment some code. Yes, very good point. Anil had commented some code in the app.py because that code was calling the agent. Let's uncomment it and go through that again. Thank you. Let me explain that code, Anil. What you uncommented is the code that first makes sure that the agent object is not already in the state of the Streamlit app. Streamlit maintains a state management aspect. So first it's going to check if the session state already has an agent object. If it does not, then it will create that agent, the same agent which Anil just created in the agent.py file. That's what this code is doing. We've just uncommented it, but keep in mind at this point all it's doing is having Streamlit call our agent. Our agent has no logic, so you wouldn't expect much functionality. When we run this code, you might not see an intelligent answer, but at least you won't see the error which you saw earlier.

Thumbnail 1100

Thumbnail 1110

Let's ask the application the same question we asked before: how many members do we have? Let's see what it says. It says, "I don't have access to your organization's current membership data." So it's basically telling us that it doesn't know the answer. At this point we've completed the entire structure of our code. We've built the UI and we've built an agent which doesn't have much intelligence but at least it's functional. As we move forward we will start building more intelligence into it.

Thumbnail 1150

Thumbnail 1170

Thumbnail 1180

Implementing the Retrieve Tool for Unstructured Data Access

Let's begin by adding code to retrieve data from the knowledge base. Remember the architecture diagram which I showed in the slide after that? We showed that an agent can have tools in it. Strands allows you to create your own custom tools and it also comes with a lot of ready-to-use tools. Today you'll see that we will be using one custom tool and one which is already available to us from the Strands SDK. I added the retrieve tool here. For this retrieve tool to work, we also need to set some environment variables. Let me explain what the retrieve tool is as Anil is typing this in. Retrieve is an out-of-the-box Strands SDK tool which allows you to query Bedrock knowledge bases. You don't have to write any custom code to access a knowledge base. It does everything for you. The only thing you need is to create a Bedrock knowledge base, which as I mentioned earlier, we had already created. The code which Anil just put in online at line 26 shows that

when you create a Bedrock knowledge base, the knowledge base gives you a unique ID. This is the unique ID that we have hardcoded here to retrieve and know which knowledge base it has to go through. I'm going to stop here in case anyone has any questions on the knowledge base.

One quick question: as I'm following along and listening, would you mind if time permits giving one more rundown after all this is wrapped up? Sure, absolutely. Just to recap, when you are creating a knowledge base, you identify your data source where your unstructured data is coming from. Most likely it will be Word documents, PDF documents, or whatever format you have. When creating the knowledge base, you also specify where the vector database will be for storing that data. All you have to identify is a data source and a vector store, and then the knowledge base does all the heavy lifting for you. Once you go through that process, it will give you a knowledge base ID, which we have put in here so we can let our Strands agent know which knowledge base it has to use.

I have to put in a disclaimer here. I know we are hardcoding these IDs all over the code. This is probably not how you would do it if you want to take this to production. You would want to store this in some parameter store or some other external configuration store, but this is just easy for us to demonstrate this way. As part of this retrieve Strands agent, it has to have the knowledge base ID as well as the region. There are two ways you can do it. You can give that information to the retrieve tool via environmental variables, which is what we are doing here. If you look at core lines 29 and 30, this is where we establish environmental variables where the knowledge base ID and the region where that knowledge base is created are injected into the environment. The retrieve tool will be able to pick it from the environmental variable and will know where to go.

We are assuming the knowledge base and the vector database are ready to go. Exactly, exactly. Because we had a time constraint, we did not go through the exercise of creating the knowledge base or the standard patterns for embedding to create those vectors. With Bedrock knowledge base, it is just a few clicks. We can stay after the session ends to discuss the specific format of the data. For example, if you have data in a Word document, what are the best practices you would suggest? What embedding models can we use? What about the size and chunking strategy?

Thumbnail 1430

Bedrock supports two embedding models: Titan and a Cohere model. The chunking strategy is definitely a very important topic, but I think if you want, we can definitely talk later on. It would be way out of scope for us to focus on chunking, but it is an important thing. Let us talk after the session. Absolutely, absolutely. All right, so let us move on. I am sorry, there is a question. Yes, it is. Can it be attached with any other vector store like OpenSearch, or does it have to be Bedrock knowledge base? The retrieve tool is for Bedrock knowledge base. But there are, and by the way, the Strands tools are community development tools, so tools are coming up every day. You might be able to find tools that can go to other data sources.

Thumbnail 1440

Thumbnail 1470

All right, so I ran this application again. Now let us go back and ask the same question we have been asking: How many members do we have? And let us see what it does. It is doing something now. It is not saying I do not know. All right, so it looked in the knowledge base and came back with some answers. It clearly states here, based on the information I found in the knowledge base, this is some number of donors we had, but it is clearly not coming from the structured relational data, which may be more accurate. So the next step is to extend this to go to that data source. What we did was have some documents which were related to our campaign, and it had some data in it, but the answer was not 100 percent accurate.

Thumbnail 1510

Thumbnail 1520

Thumbnail 1540

Thumbnail 1550

Creating a Custom Tool for Querying Structured Database Data

The 100% accurate answer is sitting in our databases. Our database is where most of our enterprise data is, especially when you are already going against your enterprise application data. So now what we will be doing is creating a custom tool. That custom tool will take a prompt from the user and try to answer if this prompt can be answered through our relational database data. If it can be, it's going to convert that into appropriate query language and be able to retrieve that data. So for that, the first thing we will do is create a custom tool.

Thumbnail 1590

All Anil did was add a custom tool in the agent definition. We already had put in a retrieve tool, and now he also created a query SQL DB tool. We have not created this tool yet, but that's the purpose of the tool. Now we'll create the tool where the entire logic of that custom tool will be in a separate file, and that's what Anil is doing. Let's call that file query_SQL_DB.file. This is where we will build our custom tool. Of course, the first thing we need to do is import all the appropriate packages. Agent and tool are one of the fundamental packages you need to bring in, and that's what we have. We also have Boto3, which is the Python SDK to access most of our AWS services. We will build the logic and then feed it into Strands.

Thumbnail 1660

This custom tool is just a Python script. Strands will use this, but there are some hooks of Strands that we need to put into this code so that we can feed it into Strands, and you'll see that in the next step. The first thing Anil is doing right now is actually writing that code, and you'll see on lines 6 to 9 how you take any code, any custom code you've written, and make sure that code can be converted into a tool for Strands. All you have to do to take that code and convert it as a tool for Strands is annotate it. Create a function, put all the logic you need in that function, and then make that function annotated by this tool annotation. All you need is a tool annotation. Once you have that and once you feed that into Strands, Strands will understand that whenever it needs to answer a user using the tool, it will run this function. Whatever is written in the function, it's going to run it.

Thumbnail 1740

Thumbnail 1750

I'm going to make sure everyone is clear about how the Strands plumbing is happening. It's very simple, just one annotation. Now, because we don't want to put all the logic into this one function, we're going to be creating a supporting class where most of our logic is going to be , and in this particular tool function, all we will be doing is calling the different methods of that supporting class. I brought in some constants, and then let's go back to the first two lines. I added an empty convenience class here that I'm going to initialize. This is the DB structure data agent class. It's nothing but a supporting class which will have most of our logic.

Thumbnail 1800

Now, the constructor of this class takes four pieces of argument. The first is the ARN of where our Aurora database is. For those folks who might not have worked with Aurora, when you create a database in Aurora, it gives you a unique ARN.

Thumbnail 1830

That's all we're doing. As Anil has already mentioned, this is not true production-ready code, so we are hard coding things here. What Anil did was define a few constants, and you'll see that in these four constants we've defined where our database is coming from. We've defined the ARN of Aurora. Line 8 is an ARN for Secrets Manager. As a best practice, whenever you are accessing a database, you need the credentials for the database. As a best practice, you should never hard code those credentials in the code or even in config files, especially if you are publishing that code into some sort of repository. Secrets Manager is one of the AWS services which makes the storage of credentials very easy and convenient, and that's what we've done. We've stored our database credentials in Secrets Manager, but here we're just bringing the ARN of that Secrets Manager.

The other constant which Anil declared was the name of our database, which is called NPO_Membership. Here you'll notice that we also have identified an LLM which is not a Claude model, which we had used earlier, but we are using a different Nova model now. First of all, I want to take a minute to explain why we are doing this. Our agent itself is communicating with an LLM, and the tool within the agent is also communicating with an LLM. For the agent communication, we are using Claude. For the tool, which will be part of the agent, we are using Nova. The reason we did that is because we wanted to make sure you guys understand that you can use multiple models for different things. Model evaluation is a big component when you're creating AI applications, so just to give that demo, we've purposely used a different model here.

Thumbnail 2010

Is this legible to everybody in the back, or do you want me to increase the font? Thank you. I should have done that. So the agent has a RAG component and another component which is going against structured data. RAG is going against unstructured data. The retrieve tool was going against unstructured data. Now you have two branches of data. The LLM needs to be smart enough to decide whether it should be going against the RAG tool or whether it should be going against the structured database tool. That would be the Claude model.

Thumbnail 2060

Thumbnail 2070

Thumbnail 2090

Thumbnail 2100

So now everyone is clear, right? So far what we've done, the LLM is basically used to figure out what your data is. Exactly. There are two things, right? One is we need to make sure that the LLM understands our database. We want to make sure that if it's a good answer to any question, then you know, and you'll see as we move forward. Everyone is clear on the way we've defined the plumbing for the structured tool and the creation of the helper class, right? Now as we will start building functions in this helper class to do different things, the first function we will build is so that our LLM can understand the database schema. Without understanding the database schema, it cannot answer, right? So the first function, what are we calling this function? I think I pulled the wrong function. Sorry, yes, go ahead. Generate schema. Yes, so now you know every relational database has a capability via some system tables, or you know, that you can actually use that capability to query what your database looks like, what are the tables, what are the columns of those tables, what are the data types of the columns, and so forth, right.

Thumbnail 2120

Thumbnail 2130

So the first function which we are showing is called get_tables_schema, and you'll see that this is straightforward. All it does is pull in the query . This is a query, and we are defining it as a variable so that we can go to PostgreSQL and pull what's in the PostgreSQL database. If you are using Oracle or Microsoft SQL Server, every database has a mechanism, and you probably just have to change this query syntax for whatever database you would be using, but the rest would be the same.

Thumbnail 2160

Thumbnail 2180

Thumbnail 2190

The first function we put together was get_tables_schema. Anil, did we already add that in? No, let me add that. So I'm going to add that. We created our helper class. Then the second thing we will do is actually query the database. In our case, it's Aurora PostgreSQL. We are querying it and saying give me the entire schema in JSON format, and that is now being stored in the db_schema variable which is on line 82. I'm going to pause here to make sure everyone is clear on this.

Thumbnail 2230

You'll see that schema is enough, but sample data might make it more effective depending on what you're trying to do. You might think that providing sample data in a prompt could make it more effective. You'll see how we wrote the prompt so that without the sample it will still work. Once we know the database schema, I have a question. Let's say you import the schema with this, but you want to add some more information to the schema because the column name is probably not very intuitive for the end user. That's a very good question. Maybe you can then inject some sort of transformation logic before you send that schema to the LLM. That's one way of doing it. Another way might be that the transformation logic you can embed in the system prompt which you'll be sending to the LLM whenever you send anything to the LLM. You do provide some context and some prompt.

Just adding to that question, if for example my schema has foreign keys and sometimes the LLM needs to do two or three queries to help it, and there you have only the table schema with the columns, is it possible to put in the schema the relationship as well? You absolutely can. Right now we chose a schema which is very simple, but if there was a relationship which we need to make sure, because let's say right now the queries which will be answered will most likely be from one table. If that is the case, then we will make sure that the prompt which we are providing has that relationship so the SQL generation has the joins in it. At the end of the day, when you're creating a database, you are only going to be sending a SQL statement. Your SQL statement has to have that relationship built into it.

Thumbnail 2340

Thumbnail 2350

Thumbnail 2370

Generating SQL Queries Dynamically Using Nova Model and System Prompts

So then now once we have that, the second thing we will show you is the system prompt generation. Remember that the system prompt gives that intelligence. In your case, you could have added the system prompt that says this transformation should happen, and you could have added that relationship information. Let's see what we are producing in the system prompt. I'll go through this line by line. Can you move up a little bit? All right, so this is the system prompt we are creating. We are saying the first thing is you are an AI assistant that creates system prompts for database queries. At this point, we are asking the LLM to create a prompt which itself has the database schema embedded into it. So we're going to take that prompt, we're going to embed it with the user question, and send the whole thing together.

Thumbnail 2410

Let me make sure everyone understands this correctly. When you have a user question, that question needs to be sent to the cloud model in order to answer it. To send that user question to the cloud, we need to build a context, which we will be building through the prompt. Everyone clear so far?

That context needs to have the database information in it. Instead of hard-coding that context, what we are telling the Nova model is to use the user schema that you have and give me a prompt, which I will then subsequently send to the cloud model so that it can tie the database schema with the user question.

Thumbnail 2480

So here we are saying in the prompt: "Hey, your job is to create a smart prompt with the database knowledge embedded in it." We are giving it the schema underscore JSON on line 96 , which holds our schema information in a variable. We have built this whole thing and now we are going to be sending it to the LLM. At this point, all the LLM did was give us a smart prompt, which we will then be able to send to the cloud model.

Thumbnail 2510

Thumbnail 2540

The first line was getting our schema. The second line was the creation of that system prompt. In this system prompt, we provided the DB underscore schema to this function. So at this point, on line 137, we have a prompt which has our SQL schema embedded into it.

Thumbnail 2550

Thumbnail 2590

Thumbnail 2600

We are building a tool for the agent. The agent takes the user input and sends it to this tool. Now this tool, along with the schema it has, is going to come up with a SQL query if it can be answered. So the third step is that we now have our database schema and we have a prompt. Now we need to tie these two things together. We have to ask an LLM to generate a SQL query if the user question can be answered through a SQL query.

Thumbnail 2610

Let me rephrase this. Our database is related to a membership model. It has tables like membership, events, campaigns, and donors. But what if I ask a question like "What is the capital of France?" That question cannot be answered from a SQL query in my database. When I send my schema and my user question to the LLM, it will say these two things do not match, so it cannot come up with a valid SQL query. The third function is now taking the user input and, knowing the database schema, going to the Nova model and saying: "Can you provide a valid SQL statement which can answer this particular user question?"

Thumbnail 2680

Once you have the system prompt, there is no need to go back to the cloud model. You can directly go to the Nova model and then execute. The cloud model is a first step because it could go not only through the database but also through the RAG. After that, there is no need to go back to the cloud model. The Nova model is doing exactly that.

Exactly. You bury the schema with the cloud, you go to the database and get it executed. So the cloud model will only be utilized to determine if I need to go to the database once it gets the context of the schema. Because here you'll see if I can generate a valid SQL for the user prompt, it's going to not only give me SQL, it's going to actually give me the data. The simplest way to think of it is this tool could have been static. If this is the query, this is a SQL query. If this is the user query, this is a SQL query. We are putting intelligence into this tool that we're building using the LLM model to generate that query dynamically for us.

Thumbnail 2760

Thumbnail 2790

Thumbnail 2800

Thumbnail 2820

Quick time check. We have 14 minutes, so we need to move a little bit faster. So now we got our system from the third thing. What Anil is typing in is actually getting those two components together, right—the database schema and the user prompt. So the third thing we will have is a helper function called generate_sql. Let's type this in and let's go to the definition of generate_sql. So again, in our supporting class, we have generate_sql. Let's go a little bit further and make sure we understand this. Where did I put it? Yes, okay. So if you look at this, generate_sql, what it did was it took the user prompt, which is in the question variable, and at this point it has the system prompt which contains the database schema. It defines the model which is our model and then it builds the LLM call. Hey LLM, this is my schema, this is my user question. If you think a valid SQL can be generated from it, give me that SQL.

Thumbnail 2850

Thumbnail 2860

Thumbnail 2870

Thumbnail 2880

Thumbnail 2890

So once this function is executed, you will have the valid SQL. But now what we're also doing is we want to make sure the valid SQL is in some sort of delimiters so we can extract it very conveniently. So you need to use delimiters. Yes, okay. So in our models, when we are saying can you create a valid SQL, we are also going to say if you can create a valid SQL, make sure the valid SQL is in this SQL start delimiter and SQL end delimiter. Once our valid SQL is in there, the next function which is going to pull that SQL out knows exactly from the output which I got from the model where I have to pull the SQL out from.

Thumbnail 2920

Thumbnail 2930

Is everybody okay with this? All right, so on line 226, right, by this time if everything is working fine, I would have a valid SQL. Then line 227 is another function which is extracting that SQL. All it's going to do is take the output from the LLM, make sure between those two start and end delimiters, I can pull the valid SQL out. Nothing complicated here.

Yes, please. I'm sorry, can you explain the schema? I see that what if the column is spelled, let's say city is spelled CTY, something like that. How do you handle that? Yeah, so it's a very good question. I'll give you maybe we can run an example of that. Our model in our database has a table called campaign, right? What we will do instead of using the word campaign, we will use the word event, for instance, in a prompt. And that's the beauty of doing this through an LLM. The LLM is smart enough to understand that there's a semantic meaning between event and campaign. So even though the database itself has the word campaign in the table, it associates it with the word event and is able to generate the SQL correctly. That's right, but that is part of what any LLM can do, right? Okay, so this question is completely different.

Thumbnail 3020

For example, I give you a schema that has column M, less column 1, column 2, column 3. By seeing this column 1, column 2, column 3, nobody knows what it is. There is also information about this column. If I pass the information schema with column, column 2, column 3 is China, but if the column names are so cryptic like that, you can pass the data dictionary also into your prompt, and then it will know how to do it. You don't want to have so many tools. You might want to make this tool smart enough that along with your database information where you're pulling the SQL, you can have it.

By the way, we did not talk about memory. In reality, if you're doing this in production, you don't want to keep going to the database and generating this system prompt over and over again because the schema is fixed. Maybe it changes once a month or only when you want to do that update when the schema changes. You can use memory tools. For example, if you're deploying this using Amazon Bedrock AgentCore, where you run your AI agents, there is a functionality that comes with Agent Core called memory so it can keep track of this kind of caching that you want to do with your data dictionaries. Your data dictionaries could also be part of that memory. You don't change your schemas in your data dictionary so often.

Thumbnail 3150

Thumbnail 3160

For this particular implementation, we've already said it's not 100% production ready because we wanted to make it so that it's easy for everyone to understand what's happening. I added a function to execute that same SQL that we just got, and I'm going to run this again. After we extracted our SQL, we wrote another function called execute_sql. It's straightforward. It's just going to take our SQL and knows how to query a PostgreSQL database. It's going to feed that SQL into PostgreSQL. If you were going against any other database like Oracle or any other, you probably will use whatever mechanism of querying the database you're using. As long as you have a valid SQL, that's all you need to talk to a relational database.

Thumbnail 3180

Thumbnail 3210

Thumbnail 3230

Testing the Complete Agent and Deployment Considerations

I'm asking the same question. Theoretically now, it should be going to the database to get the exact number of members that we have. Let's see if we can do that or if I missed some step somewhere. It happens. So you see now the 5,000 members is not coming from the RAG. Maybe we should tell them where it's coming from. This is coming from the database. You see there's a toggle here that will actually show you how the query is going. If once, maybe let's run it to show the audience because you'll be able to see whether it's sending it to the SQL tool or the RAG tool. When now the question was asked, I asked another question: what membership levels exist. Again, this is in the database. We have 3 membership levels. You can see that when you expand this tool usage, it will show you what it does. It went to the database and got these results.

Thumbnail 3250

Thumbnail 3270

But if I'm asking a question like, did we run an event to support elderly, this probably will go against the knowledge base instead of going against the database because this information is not in a database. Claude is making exactly the right decision about which tool it's going to use. If a database tool is needed for the answer, then that will come into the picture. One thing I do want to highlight is in our agent.py, our system prompt is still basic. Our application is simple enough that the LLM is able to figure out that it needs to go to the database or structured data. But it's always a best practice to give a good system prompt to make sure your LLM behaves in more or less the way that you want it to behave rather than just leaving it up to chance. We could have replaced this system prompt with something more robust.

Thumbnail 3310

Thumbnail 3320

There are cases where the system is not able to convert a query and is throwing an error. Right now, when Anil puts in a very smart system prompt, this becomes important because our code example was so simple that Claude was able to figure it out. But imagine if you had multiple knowledge bases and multiple databases, or if you were doing transformations and defining a data dictionary. When you are making that complicated type of agent where it's accessing multiple structured and non-structured data sources, your system prompt becomes very, very important. The more information you provide in that system prompt, the more intelligent Claude would be in deciding which tool it's going to use. You're kind of defining your agent's personality with this system prompt. So it is very important to have a good system prompt.

Thumbnail 3360

Thumbnail 3410

Thumbnail 3430

All right, I want to wrap this up. We have only two minutes. As you can see, we just built a demo application. It's not production ready, but if you want to deploy this in AWS, you have multiple choices. You can run this in AWS Lambda, container services like ECS or EKS, or use our latest and greatest runtime for running these agentic AI applications and agent code. Since it's just Python, you can run it on any compute. We are not deploying this anywhere. We're just running it off of an EC2 machine that's sitting in AWS, but theoretically you can pick any of these choices. Finally, if you want to learn more about agentic AI, there are some resources here. Please don't forget to submit the survey for the session. We will be here after the session, so please feel free to stay. I don't know if you can stay in this room, but we can definitely go outside and we'll be happy to answer any questions.

Thumbnail 3470

Thumbnail 3480

I know you wanted to give us a one-on-one rundown. We'll stay here and we can chat. We can also talk about your question. You want to know how to create a knowledge base. Of course, we can share that. We can share the code. We will get it up on GitHub and maybe share it if you can connect with one of us on LinkedIn. We were going to share the code when you submit the survey for the session, but don't go to review yet. If you go to the app, pick the session and submit the survey. Thank you all for coming and thank you very much.


; This article is entirely auto-generated using Amazon Bedrock.

Top comments (0)