{"id":16352,"date":"2024-09-23T14:08:57","date_gmt":"2024-09-23T21:08:57","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=16352"},"modified":"2025-06-13T16:36:40","modified_gmt":"2025-06-13T23:36:40","slug":"ai-powered-recommendation-engine-llm-rag","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/","title":{"rendered":"From Concept to Code: LLM + RAG with Couchbase"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">GenAI technologies are definitely a trending item in 2023 and 2024, and because I work for\u00a0 <\/span><a href=\"https:\/\/tikalk.com\/\"><span style=\"font-weight: 400;\">Tikal<\/span><\/a><span style=\"font-weight: 400;\">, which publishes its own annual <\/span><a href=\"https:\/\/tikalk.com\/radar\/\"><span style=\"font-weight: 400;\">technology radar and trends<\/span><\/a><span style=\"font-weight: 400;\"> report, LLM and genAI did not escape my attention. As a developer myself, I often consult generative AI chatbots to help me solve all kinds of TypeScript errors and mysterious linting issues, I use genAI assistive tools in my IDE and to improve my PRs. This tech is possibly life changing.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">As technical people and definitely us software developers this new trend opens the opportunity to integrate these capabilities into all the projects we work on, and I see my friends and colleagues exploring these options, which led me to the decision &#8211; I should do it, too!<\/span><\/p>\n<p><span style=\"font-weight: 400;\">And I had just the project:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">I am an amateur dancer who dances in an amateur dance troupe, I often wonder how amateur artists can explore the wide world of cultural events locally and worldwide to be able to reach out and maybe get that desired invitation to come perform. We don\u2019t have the resources, connections and knowledge of everything available. Sure, there are search engines, and specialized websites, but you need to know what and how to search for it, so I decided to use genAI to get recommendations.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Step 1 &#8211; Can it be done?<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Checking the feasibility of a recommendation engine using one of the LLMs, included opening accounts on several genAI chat services and asking them the same question:<\/span><\/p>\n<p><i><span style=\"font-weight: 400;\">We are an <\/span><\/i><b><i>amateur<\/i><\/b> <b><i>Israeli folk dancing<\/i><\/b><i><span style=\"font-weight: 400;\"> group, including dancers in <\/span><\/i><b><i>wheelchairs<\/i><\/b><i><span style=\"font-weight: 400;\">. We are looking for <\/span><\/i><b><i>cultural <\/i><\/b><i><span style=\"font-weight: 400;\">and<\/span><\/i><b><i> folklore<\/i><\/b> <b><i>events<\/i><\/b><i><span style=\"font-weight: 400;\"> and <\/span><\/i><b><i>festivals<\/i><\/b><i><span style=\"font-weight: 400;\"> in <\/span><\/i><b><i>Europe<\/i><\/b><i><span style=\"font-weight: 400;\"> to reach out to about the option of us getting an invitation to perform, provided we <\/span><\/i><b><i>cover our expenses<\/i><\/b><i><span style=\"font-weight: 400;\">. Can you please recommend a few?<\/span><\/i><\/p>\n<p><span style=\"font-weight: 400;\">The results in H1 of 2024 varied between the different chat services:<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Directed me to dedicated websites which I could query for results<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Gave me actual results<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">From those who returned results, I rated the quality of the results by relevance and accuracy, and ended up with <\/span><b>OpenAI<\/b><span style=\"font-weight: 400;\"> GPT-<\/span><b>3<\/b><span style=\"font-weight: 400;\"> as the choice.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Step 2 &#8211; Is it enough?<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Remembering that even one of the chat assistants in Step 1 suggested I check other websites, what if I could embed some of that data in the results?<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Considering that I\u2019m also dependent on whomever trained the model and when it was trained, I wanted my recommendations to be based on more data sources and I knew it can be done with RAG. First of all, what is RAG?<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Retrieval Augmented Generation (RAG)<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">RAG is the process of enriching and optimizing the output you receive from LLM by adding \u201cexternal\u201d data. If I can add results based on the same search on external data sources (from dedicated websites) I can expand the variety of the results my application will provide.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">To do that you will need:<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">External data sources &#8211; For my experiment I created a trial account for <\/span><a href=\"https:\/\/www.predicthq.com\/events\/upcoming-events\"><span style=\"font-weight: 400;\">predictHQ\u2019s events API<\/span><\/a><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Store my external data is an engine that allows similarity search and not an exact match<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h4><span style=\"font-weight: 400;\">Making the data accessible for RAG<\/span><\/h4>\n<p><span style=\"font-weight: 400;\">Once you\u2019re done looking into the data, what it looks like, and what features it holds, it is time for you to select the data features you\u2019d like to use, and make them usable for RAG.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">To allow a similarity search we would need to transform our data into a format that is searchable and <\/span><b>comparable.<\/b><span style=\"font-weight: 400;\"> Because we are not looking for exact matches, but similar matches, there are two very common techniques for that:<\/span><\/p>\n<table style=\"border: 1px solid black;\">\n<tbody>\n<tr>\n<td><b>RAG technique<\/b><\/td>\n<td><b>Details<\/b><\/td>\n<\/tr>\n<tr>\n<td>Vector search (also known as common RAG)<\/td>\n<td>The pieces of information and the question are transformed to <b>vectors<\/b> of numbers (floating points).<\/p>\n<p>Mathematical computations are used to determine to similarity between the question and the data<\/td>\n<\/tr>\n<tr>\n<td>GraphRAG<\/td>\n<td>The pieces of information and the question are transformed into <b>graph<\/b> vertices and edges.<\/p>\n<p>The graph relations are compared for similarity<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"font-weight: 400;\">The process of creating the representation of the data is called <\/span><b>embedding<\/b><span style=\"font-weight: 400;\">, in this article we will focus on vector search.<\/span><\/p>\n<h4><span style=\"font-weight: 400;\">Similarity Metric<\/span><\/h4>\n<p><span style=\"font-weight: 400;\">There are 3 common options (in a nutshell):<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Dot product: Calculating the similarity based on the product of the values in each vector<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Cosine: Based on the angle between the vectors<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">L2_norm: Euclidean distance between the vectors, based on the angle between the vectors and the length of each vector<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">Read more about the <a href=\"https:\/\/www.couchbase.com\/blog\/vector-similarity-search\/\">vector similarity options<\/a>.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Step 3 &#8211; How do I do this?<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Before we dive into how we\u2019re going to this and some actual code and screenshots, let\u2019s look at how such architecture would be built, and how Couchbase comes into the picture:<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-16356 aligncenter\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image4-1.jpg\" alt=\"\" width=\"673\" height=\"682\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image4-1.jpg 673w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image4-1-296x300.jpg 296w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image4-1-65x65.jpg 65w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image4-1-50x50.jpg 50w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image4-1-300x304.jpg 300w\" sizes=\"auto, (max-width: 673px) 100vw, 673px\" \/><br style=\"font-weight: 400;\" \/><br style=\"font-weight: 400;\" \/><\/p>\n<p><span style=\"font-weight: 400;\">What this means in practice is:<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Ingestion app to:<\/span>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-weight: 400;\">Fetch data from the external API<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-weight: 400;\">Create vector embeddings<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-weight: 400;\">Load data into a Couchbase collection<\/span><\/li>\n<\/ol>\n<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Create a vector search index in Couchbase<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Prompt application to:<\/span>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-weight: 400;\">Ask Couchbase data for results<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-weight: 400;\">Add the vector search results to the LLM prompt as context<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-weight: 400;\">Return the cohesive results to users<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3><span style=\"font-weight: 400;\">Ingestion application<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">This process was probably the longest, I spent time creating embeddings to different fields and in different formats. For simplicity I eventually chose to only use the geographical information I collected:<\/span><\/p>\n<pre class=\"nums:false lang:python decode:true\">from langchain_openai import OpenAIEmbeddings\r\nembeddings_model = OpenAIEmbeddings(model=\"text-embedding-3-small\")\r\ntext = f\"Geo Info: {row['geo_info']}\"\r\n\r\nembedding = embeddings_model.embed_query(text)<\/pre>\n<p><span style=\"font-weight: 400;\">To create the embedding I chose to use textual embeddings as a start, meaning \u201ccomparing\u201d text representation to text representation. The embedding itself includes roughly 1500 numbers (that is the small one).<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The code itself is not extremely complex, however it can be time consuming, to create 1 embedding for 5000 events took approximately 1 hour on my M1 16 GB MacBook pro.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The full code using pandas2 can be found in <\/span><a href=\"https:\/\/github.com\/sshahar1\/TikalMeetup2024Ingestion\"><span style=\"font-weight: 400;\">this<\/span><\/a><span style=\"font-weight: 400;\"> repository.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Couchbase collection and search index<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">To be able to search for similar data between the question and the results we\u2019ve prepared based on an external API, we will-<\/span><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Create a Couchbase collection<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Upload the prepared data to a Couchbase collection <\/span><b>including the embeddings<\/b><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Create a search index on the embeddings fields choosing the vector similarity algorithm to compare vectors<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<h4><span style=\"font-weight: 400;\">New Couchbase collection<\/span><\/h4>\n<p><span style=\"font-weight: 400;\">For my application I chose to use the hosted Couchbase service &#8211; Capella, the setup is very easy. I signed up, chose the Cloud service and created a new project.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-16354\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image2-4-1024x118.png\" alt=\"\" width=\"900\" height=\"104\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image2-4-1024x118.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image2-4-300x35.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image2-4-768x89.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image2-4-1536x177.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image2-4-1320x153.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image2-4.png 1999w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">Clicking on my project and navigating to the Data tools tab, I can now create a new collection for the data I prepared:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-16353\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image1-4-893x1024.png\" alt=\"\" width=\"893\" height=\"1024\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image1-4-893x1024.png 893w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image1-4-262x300.png 262w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image1-4-768x880.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image1-4-300x344.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image1-4.png 1284w\" sizes=\"auto, (max-width: 893px) 100vw, 893px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">To upload the data I prepared there are several options: since the size of the file was rather large I chose to use the <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/tools\/cbimport.html\"><span style=\"font-weight: 400;\">cbimport<\/span><\/a><span style=\"font-weight: 400;\"> utility to do so.<\/span><\/p>\n<pre class=\"nums:false wrap:true lang:default decode:true \">.\/cbimport json --cluster couchbases:\/\/&lt;yourcluster&gt; --username &lt;your user&gt; --password &lt;your password&gt; --bucket &lt;bucket&gt; --scope-collection-exp \"&lt;scope&gt;.&lt;collection&gt;\" --dataset for_collection.json --generate-key '%id%' --cacert &lt;path to couchbase certificate&gt; --format lines<\/pre>\n<p><span style=\"font-weight: 400;\">Notice that I chose the <em>ID<\/em> field from the JSON documents to be the document <em>key<\/em> in the collection.<\/span><\/p>\n<p><b>Remember<\/b><span style=\"font-weight: 400;\"> that prior to doing so, you need to:<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Create database access user\/password with write privilege as a minimum<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Open the cluster for calls from your host<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Download a certificate for the cluster<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">The inferred document schema shows that the <\/span><b>embedding<\/b><span style=\"font-weight: 400;\"> field as been created with the type of array of numbers:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-16355\" style=\"border: 1px solid black;\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image3-2.png\" alt=\"\" width=\"648\" height=\"296\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image3-2.png 648w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image3-2-300x137.png 300w\" sizes=\"auto, (max-width: 648px) 100vw, 648px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">To allow vector similarity search, let us create the search index by navigating to the Search tab.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">We should obviously select the embedding field for the search index, but notice there are more parameters to set:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-16357\" style=\"border: 1px solid black;\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image5-1-1024x311.png\" alt=\"\" width=\"900\" height=\"273\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image5-1-1024x311.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image5-1-300x91.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image5-1-768x233.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image5-1-1536x467.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image5-1-1320x401.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image5-1.png 1842w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">We have already discussed what the similarity metric is, just note that Couchbase support l2_norm (i.e. Euclidean distance) and dot product, I chose \u201c<\/span><b>dot product\u201d<\/b><span style=\"font-weight: 400;\">, which may be more beneficial for my recommendation system.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The next step is choosing additional fields from the documents that would be returned whenever a vector is dimmed similar to the question:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-16358\" style=\"border: 1px solid black;\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image6-1-1024x372.png\" alt=\"\" width=\"900\" height=\"327\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image6-1-1024x372.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image6-1-300x109.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image6-1-768x279.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image6-1-1536x558.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image6-1-1320x479.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image6-1.png 1999w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">If you will not add at least one field, your application will fail because there will not be any returned data.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">There it is, the index fields selection:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-16359\" style=\"border: 1px solid black;\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image7-1-1024x517.png\" alt=\"\" width=\"900\" height=\"454\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image7-1-1024x517.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image7-1-300x152.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image7-1-768x388.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image7-1-1536x776.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image7-1-1320x667.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image7-1.png 1999w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">We have reached a crucial point in our project, we can now start running similarity search on the data we prepared, but you may not have a working similarity search at your first attempt. I will outline a few tips to get results from your similarity search or to check why you are not getting results:<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Make sure your embeddings technique, when creating the data and preparing a search are identical<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Start with a simple and predictable format for the information you want to compare. For example &lt;City&gt;, &lt;State&gt;, &lt;Country&gt;<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Make sure you don\u2019t have extra information that is accidentally appended to the data you\u2019re creating embeddings for (for example I had line breaks)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Make sure exact match search works by:<\/span>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-weight: 400;\">Searching for the exact data you created embeddings for<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-weight: 400;\">Compare the embedding vector to make sure that identical embeddings are created in the generation and search part (debugging will be useful here). If there is a difference go back to steps 1-3<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">Once you have a working similarity search, gradually add more fields, change formats, embeddings and anything else you feel is missing.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Remember any change to the embeddings means:<\/span><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Recreating the embeddings<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Loading the changes data to a truncated collection<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Changing the search index if needed<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Changing the code is needed<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">These steps may be time consuming, especially the embeddings creation, so you may want to start with:<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">A small portion of your documents<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">A small\/fast embedding technique<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3><span style=\"font-weight: 400;\">LLM and RAG application<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">What our application needs to do is:<\/span><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Ask Couchbase to find results similar to the user question<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Add the results to the context to the prompt question to the LLM<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Ask the LLM a question<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">For simplicity I created this code in Python as a Jupyter notebook which you can find in this <\/span><a href=\"https:\/\/github.com\/sshahar1\/TikalMeetup2024\"><span style=\"font-weight: 400;\">repository<\/span><\/a><span style=\"font-weight: 400;\">. I used the following libraries to accomplish this:<\/span><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><a href=\"https:\/\/pypi.org\/project\/couchbase\/\"><span style=\"font-weight: 400;\">Couchbase<\/span><\/a><span style=\"font-weight: 400;\">: Connect and authenticate to my Capella cluster<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><a href=\"https:\/\/www.langchain.com\/langchain\"><span style=\"font-weight: 400;\">LangChain<\/span><\/a><span style=\"font-weight: 400;\">: a framework for developing applications powered by large language models (LLMs), for:<\/span>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-weight: 400;\">Embeddings<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-weight: 400;\">Using Couchbase as a vector store<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"2\"><span style=\"font-weight: 400;\">\u201cChatting\u201d with OpenAI<\/span><\/li>\n<\/ol>\n<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><a href=\"https:\/\/www.langchain.com\/langgraph\"><span style=\"font-weight: 400;\">LangGraph<\/span><\/a><span style=\"font-weight: 400;\">: A framework to building a stateful, multi-actor LLM applications, for creating a flow of the LLM application<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">If you\u2019ve been reading about, and even trying to build your own LLM application you\u2019re probably somewhat familiar with LangChain, it is a set of libraries that allows you to write, build, deploy and monitor an application, it has many agents and extensions that allow you to integrate different parts into your code, such as a 3rd party API, a database, a web search and many more.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Lately, I also learned about LangGraph from the home of LangChain, which allows you as a developer to build more complex topologies of the LLM application with conditions, loops (the graph doesn\u2019t have to be a DAG!), user interaction, and perhaps the most sought out feature: Keeping state.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Before we look at the code let\u2019s take a look at the environment file (.env) to see what credentials and other confidential data we need:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">LANGSMITH_KEY=langsmithkey\r\nOPENAI_API_KEY=openaikey\r\nLANGCHAIN_PROJECT=myproject\r\nCOUCHBASE_CONNECTION_STRING=couchbase:\/\/mycluster.com\r\nCOUCHBASE_USER=myuser\r\nCOUCHBASE_PASS=mypass\r\nCOUCHBASE_BUCKET=mybucket\r\nCOUCHBASE_SCOPE=myscope\r\nCOUCHBASE_COLLECTION=mycollection\r\nCOUCHBASE_SEARCH_INDEX=mysearchindex\r\nLANGCHAIN_API_KEY=langchainapikey<\/pre>\n<p><span style=\"font-weight: 400;\">The state for each graph node is:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">from langgraph.graph import add_messages, StateGraph\r\nfrom typing_extensions import TypedDict\r\nfrom typing import Annotated\r\nfrom langgraph.checkpoint.sqlite import SqliteSaver\r\n\r\nclass State(TypedDict):\r\n\u00a0 # Messages have the type \"list\". The `add_messages` function\r\n\u00a0 # in the annotation defines how this state key should be updated\r\n\u00a0 # (in this case, it appends messages to the list, rather than overwriting them)\r\n\u00a0 messages: Annotated[list, add_messages]\r\n\u00a0 event_type: str\r\n\u00a0 location: str\r\n\u00a0 labels: str\r\n\u00a0 \r\ngraph_builder = StateGraph(State)<\/pre>\n<p><span style=\"font-weight: 400;\">It is important to note unless you define a reducer the state will be overwritten between each graph node, the messages member in the state class has a reducer which will append the new messages to the list.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">To connect to Couchbase and use it as a vector store for the LLM application, we authenticate to the cluster, and pass the cluster connection to the LangChain object for vector store:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">from langchain_openai import OpenAIEmbeddings\r\nimport os\r\nfrom couchbase.cluster import Cluster\r\nfrom couchbase.options import ClusterOptions\r\nfrom couchbase.auth import PasswordAuthenticator\r\nfrom langchain_couchbase import CouchbaseVectorStore\r\n\r\nCOUCHBASE_CONNECTION_STRING = os.environ[\"COUCHBASE_CONNECTION_STRING\"]\r\nCOUCH_USER = os.environ[\"COUCHBASE_USER\"]\r\nCOUCH_PASS = os.environ[\"COUCHBASE_PASS\"]\r\nBUCKET_NAME = os.environ[\"COUCHBASE_BUCKET\"]\r\nSCOPE_NAME = os.environ[\"COUCHBASE_SCOPE\"]\r\nCOLLECTION_NAME = os.environ[\"COUCHBASE_COLLECTION\"]\r\nSEARCH_INDEX_NAME = os.environ[\"COUCHBASE_SEARCH_INDEX\"]\r\n\r\nauth = PasswordAuthenticator(COUCH_USER, COUCH_PASS)\r\noptions = ClusterOptions(auth)\r\ncluster = Cluster(COUCHBASE_CONNECTION_STRING, options)\r\nembedding = OpenAIEmbeddings(model=\"text-embedding-3-small\")\r\n\r\nvector_store = CouchbaseVectorStore(\r\n\u00a0 cluster=cluster,\r\n\u00a0 bucket_name=BUCKET_NAME,\r\n\u00a0 scope_name=SCOPE_NAME,\r\n\u00a0 collection_name=COLLECTION_NAME,\r\n\u00a0 embedding=embedding,\r\n\u00a0 index_name=SEARCH_INDEX_NAME,\r\n)<\/pre>\n<p><span style=\"font-weight: 400;\">There are two important details to keep in mind:<\/span><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The embedding in the application <\/span><b>must<\/b><span style=\"font-weight: 400;\"> be identical to the one used in the ingestion part<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The default embedding field name is \u2018embedding\u2019, if the name of the respective field is different in your search index you need to set it during the instantiation of CouchbaseVectorStore (embedding_key)<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">Right now, you are ready to write your LangGraph application and to use Couchbase as a vector store. Let&#8217;s put it together: each graph needs nodes, start point and directed edges.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Our graph will fetch data from the vector store and continue to add this information into the LLM prompt context.<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">from langchain_core.prompts import ChatPromptTemplate\r\nfrom langchain_core.output_parsers import StrOutputParser\r\nfrom langchain_openai import ChatOpenAI\r\n\r\nllm = ChatOpenAI(model=\"gpt-3.5-turbo\")\r\ntemplate = \"\"\"You are a helpful bot that serves the purpose of finding events for artists looking for venues in the USA. If you cannot answer based on the context provided, respond with a generic\r\n\u00a0 answer. Answer the question as truthfully as possible using the context below: {context}\r\nPlease also format the result in Markdown format.\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Question: {question}\"\"\"\r\n\r\nprompt = ChatPromptTemplate.from_template(template)\r\n\r\ngeneration_chain = prompt | llm | StrOutputParser()\r\n\r\n\r\ndef chatbot(state: State):\r\n\u00a0 response = generation_chain.invoke({\"context\": state['messages'], \"question\": f\"We are a {state['event_type']} amateur group looking for {state['labels']} festivals in {state['location']}, can you please recommend some for us to reach out to?\"})\r\n\u00a0 state['messages'].append(response)\r\n\u00a0 return state\r\n\r\ndef search_couchbase(state: State):\r\n\u00a0 query = f\"Geo Info: {state['location']}\"\r\n\u00a0 \r\n\u00a0 retriever = vector_store.as_retriever()\r\n\u00a0 results = retriever.invoke(query)\r\n\u00a0 for result in results:\r\n\u00a0 \u00a0 \u00a0 text = f\"Title: {result.metadata['title']}\/{result.metadata['alternate_titles_flat']} - {result.metadata['description']} from {result.metadata['start']} to {result.metadata['end']}, location {result.metadata['geo_info']}. Labels {result.metadata['labels_flat']}, category {result.metadata['category']}\"\r\n\u00a0 \u00a0 \u00a0 state['messages'].append(text)\r\n\u00a0 return state\r\n\r\ngraph_builder.add_node(\"vector_search\", search_couchbase)\r\ngraph_builder.add_node(\"chatbot\", chatbot)\r\ngraph_builder.set_entry_point(\"vector_search\")\r\ngraph_builder.add_edge(\"vector_search\", \"chatbot\")\r\ngraph_builder.set_finish_point(\"chatbot\")\r\n\r\nmemory = SqliteSaver.from_conn_string(\":memory:\")\r\ngraph = graph_builder.compile(checkpointer=memory)<\/pre>\n<p><span style=\"font-weight: 400;\">It translates in the code above to two nodes:<\/span><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">vector_search (entry point)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">chatbot (finish point)<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">Since a picture is worth a thousand words, I used the following code to visualize the graph for you:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true \">from IPython.display import Image, display\r\nfrom langchain_core.runnables.graph import CurveStyle, MermaidDrawMethod, NodeStyles\r\n\r\ndisplay(\r\n\u00a0 Image(\r\n\u00a0 \u00a0 \u00a0 graph.get_graph().draw_mermaid_png(\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 draw_method=MermaidDrawMethod.API,\r\n\u00a0 \u00a0 \u00a0 )\r\n\u00a0 )\r\n)<\/pre>\n<p><span style=\"font-weight: 400;\">Which resulted in the following drawing:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-16360 size-medium\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image8-186x300.png\" alt=\"\" width=\"186\" height=\"300\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image8-186x300.png 186w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image8-300x483.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/image8.png 370w\" sizes=\"auto, (max-width: 186px) 100vw, 186px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">For more visualization options in langGraph, see <\/span><a href=\"https:\/\/github.com\/langchain-ai\/langgraph\/blob\/main\/examples\/visualization.ipynb\"><span style=\"font-weight: 400;\">this<\/span><\/a><span style=\"font-weight: 400;\"> Jupyter notebook by LangGraph.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Asking the vector store means searching for data with similar location, you may notice that the format of the query is the same as in the embedded text, the results are added to the state to be used in the next node.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The chatbot node takes the information from the messages and embeds it into the prompt question to the LLM.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Note that the state is kept in the in-memory database sqlite. To use the graph feel free to use the following sample:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true \">from random import randint\r\nfrom IPython.core.display import Markdown\r\n\r\nsession_id = randint(1, 10000)\r\nconfig = {\"configurable\": {\"thread_id\": session_id}}\r\n\r\ninput_location = \"kansas\"\r\ninput_category = \"jaz\"\r\ninput_labels = \"grange\"\r\n\r\n# Stream the graph, each output will be printed when ready\r\nfor event in graph.stream({\"event_type\": input_category, \"location\": input_location, \"labels\": input_labels}, config):\r\n\u00a0 for value in event.values():\r\n\u00a0 \u00a0 \u00a0 if len(value['messages']) &gt; 0:\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 display(Markdown(value['messages'][-1]))<\/pre>\n<p><span style=\"font-weight: 400;\">And there you are, you\u2019ve created an LLM application to recommend culture events for amateur groups to reach out to asking for invitations.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Summary<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Getting started with LLM applications is exciting and in my humble opinion, as a fun, exciting and doable ramp-up due to its prompt nature, however, making our application better and more robust hides more challenges.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">In this article, I focused on the challenge of leveraging the knowledge of our mode with external data via the technique or RAG and how you can leverage Couchbase to do so.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">It is important to remember that creating embeddings that the LLM application will find in the vector search, may not work at your first attempt. Check for formatting, try to start with simple embeddings and use debugging as much as possible.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">I also demonstrated the capabilities of LangChain\u2019s LangGraph which allows you to create complex decisions and flows in the LLM application.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Enjoy your journey with LLM applications.<\/span><\/p>\n<p><br style=\"font-weight: 400;\" \/><br style=\"font-weight: 400;\" \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>GenAI technologies are definitely a trending item in 2023 and 2024, and because I work for\u00a0 Tikal, which publishes its own annual technology radar and trends report, LLM and genAI did not escape my attention. As a developer myself, I [&hellip;]<\/p>\n","protected":false},"author":85526,"featured_media":16398,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[10122,1815,2225,9973,9139,9937],"tags":[9963,9870],"ppma_author":[10032],"class_list":["post-16352","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-artificial-intelligence-ai","category-best-practices-and-tutorials","category-cloud","category-generative-ai-genai","category-python","category-vector-search","tag-langchain","tag-llms"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.7.1 (Yoast SEO v25.7) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>From Concept to Code: LLM + RAG with Couchbase<\/title>\n<meta name=\"description\" content=\"Learn how to build a generative AI recommendation engine using LLM, RAG, and Couchbase integration. Step-by-step guide for developers.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"From Concept to Code: LLM + RAG with Couchbase\" \/>\n<meta property=\"og:description\" content=\"Learn how to build a generative AI recommendation engine using LLM, RAG, and Couchbase integration. Step-by-step guide for developers.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2024-09-23T21:08:57+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-13T23:36:40+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/From-Concept-to-Code-LLM-RAG-with-Couchbase_V2-scaled.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1340\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Sigal Shaharabani - Technical Leader, Tikal\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Sigal Shaharabani - Technical Leader, Tikal\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/\"},\"author\":{\"name\":\"Sigal Shaharabani - Technical Leader, Tikal\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/eaa4f519f1bdafc253d366c93f115114\"},\"headline\":\"From Concept to Code: LLM + RAG with Couchbase\",\"datePublished\":\"2024-09-23T21:08:57+00:00\",\"dateModified\":\"2025-06-13T23:36:40+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/\"},\"wordCount\":2271,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/From-Concept-to-Code-LLM-RAG-with-Couchbase_V2-scaled.jpg\",\"keywords\":[\"langchain\",\"LLMs\"],\"articleSection\":[\"Artificial Intelligence (AI)\",\"Best Practices and Tutorials\",\"Couchbase Capella\",\"Generative AI (GenAI)\",\"Python\",\"Vector Search\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/\",\"name\":\"From Concept to Code: LLM + RAG with Couchbase\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/From-Concept-to-Code-LLM-RAG-with-Couchbase_V2-scaled.jpg\",\"datePublished\":\"2024-09-23T21:08:57+00:00\",\"dateModified\":\"2025-06-13T23:36:40+00:00\",\"description\":\"Learn how to build a generative AI recommendation engine using LLM, RAG, and Couchbase integration. Step-by-step guide for developers.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/From-Concept-to-Code-LLM-RAG-with-Couchbase_V2-scaled.jpg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/From-Concept-to-Code-LLM-RAG-with-Couchbase_V2-scaled.jpg\",\"width\":2560,\"height\":1340},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"From Concept to Code: LLM + RAG with Couchbase\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/eaa4f519f1bdafc253d366c93f115114\",\"name\":\"Sigal Shaharabani - Technical Leader, Tikal\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/6bf412a23dbadb2c7664e454e6195c40\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/unnamed.jpg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/unnamed.jpg\",\"caption\":\"Sigal Shaharabani - Technical Leader, Tikal\"},\"description\":\"I am a Technical Leader and a Group Leader in Tikal, with a great passion for backend and data systems. In my spare time I enjoy swimming and Israeli folk dancing.\",\"url\":\"https:\/\/www.couchbase.com\/blog\/author\/sigalshaharabani\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"From Concept to Code: LLM + RAG with Couchbase","description":"Learn how to build a generative AI recommendation engine using LLM, RAG, and Couchbase integration. Step-by-step guide for developers.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/","og_locale":"en_US","og_type":"article","og_title":"From Concept to Code: LLM + RAG with Couchbase","og_description":"Learn how to build a generative AI recommendation engine using LLM, RAG, and Couchbase integration. Step-by-step guide for developers.","og_url":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/","og_site_name":"The Couchbase Blog","article_published_time":"2024-09-23T21:08:57+00:00","article_modified_time":"2025-06-13T23:36:40+00:00","og_image":[{"width":2560,"height":1340,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/From-Concept-to-Code-LLM-RAG-with-Couchbase_V2-scaled.jpg","type":"image\/jpeg"}],"author":"Sigal Shaharabani - Technical Leader, Tikal","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Sigal Shaharabani - Technical Leader, Tikal","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/"},"author":{"name":"Sigal Shaharabani - Technical Leader, Tikal","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/eaa4f519f1bdafc253d366c93f115114"},"headline":"From Concept to Code: LLM + RAG with Couchbase","datePublished":"2024-09-23T21:08:57+00:00","dateModified":"2025-06-13T23:36:40+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/"},"wordCount":2271,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/From-Concept-to-Code-LLM-RAG-with-Couchbase_V2-scaled.jpg","keywords":["langchain","LLMs"],"articleSection":["Artificial Intelligence (AI)","Best Practices and Tutorials","Couchbase Capella","Generative AI (GenAI)","Python","Vector Search"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/","url":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/","name":"From Concept to Code: LLM + RAG with Couchbase","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/From-Concept-to-Code-LLM-RAG-with-Couchbase_V2-scaled.jpg","datePublished":"2024-09-23T21:08:57+00:00","dateModified":"2025-06-13T23:36:40+00:00","description":"Learn how to build a generative AI recommendation engine using LLM, RAG, and Couchbase integration. Step-by-step guide for developers.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/From-Concept-to-Code-LLM-RAG-with-Couchbase_V2-scaled.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/From-Concept-to-Code-LLM-RAG-with-Couchbase_V2-scaled.jpg","width":2560,"height":1340},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/ai-powered-recommendation-engine-llm-rag\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"From Concept to Code: LLM + RAG with Couchbase"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"The Couchbase Blog","description":"Couchbase, the NoSQL Database","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/eaa4f519f1bdafc253d366c93f115114","name":"Sigal Shaharabani - Technical Leader, Tikal","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/6bf412a23dbadb2c7664e454e6195c40","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/unnamed.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/unnamed.jpg","caption":"Sigal Shaharabani - Technical Leader, Tikal"},"description":"I am a Technical Leader and a Group Leader in Tikal, with a great passion for backend and data systems. In my spare time I enjoy swimming and Israeli folk dancing.","url":"https:\/\/www.couchbase.com\/blog\/author\/sigalshaharabani\/"}]}},"authors":[{"term_id":10032,"user_id":85526,"is_guest":0,"slug":"sigalshaharabani","display_name":"Sigal Shaharabani - Technical Leader, Tikal","avatar_url":{"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/unnamed.jpg","url2x":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/09\/unnamed.jpg"},"author_category":"","last_name":"Shaharabani - Technical Leader, Tikal","first_name":"Sigal","job_title":"","user_url":"","description":"I am a Technical Leader and a Group Leader in Tikal, with a great passion for backend and data systems. In my spare time I enjoy swimming and Israeli folk dancing."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/16352","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/users\/85526"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=16352"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/16352\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/16398"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=16352"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=16352"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=16352"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=16352"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}