{"id":19797865,"url":"https://github.com/pinecone-io/pinecone-rag-demo","last_synced_at":"2025-07-28T23:38:15.191Z","repository":{"id":217494763,"uuid":"741780224","full_name":"pinecone-io/pinecone-rag-demo","owner":"pinecone-io","description":"Pinecone + Vercel RAG application, showcasing a comparison between chat with no context and using a Pinecone index for context ","archived":false,"fork":false,"pushed_at":"2024-09-20T20:23:45.000Z","size":811,"stargazers_count":70,"open_issues_count":11,"forks_count":23,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-06-04T21:50:26.462Z","etag":null,"topics":["ai","gen-ai","pinecone","vercel"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pinecone-io.png","metadata":{"files":{"readme":"README-updated.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-11T05:07:50.000Z","updated_at":"2025-05-31T13:03:03.000Z","dependencies_parsed_at":"2024-04-09T21:45:35.177Z","dependency_job_id":"bcdc4112-87bd-4f2b-a74e-2f7f23825429","html_url":"https://github.com/pinecone-io/pinecone-rag-demo","commit_stats":null,"previous_names":["pinecone-io/pinecone-rag-demo"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pinecone-io/pinecone-rag-demo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinecone-io%2Fpinecone-rag-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinecone-io%2Fpinecone-rag-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinecone-io%2Fpinecone-rag-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinecone-io%2Fpinecone-rag-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pinecone-io","download_url":"https://codeload.github.com/pinecone-io/pinecone-rag-demo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinecone-io%2Fpinecone-rag-demo/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267604686,"owners_count":24114546,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-28T02:00:09.689Z","response_time":68,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","gen-ai","pinecone","vercel"],"created_at":"2024-11-12T07:27:07.997Z","updated_at":"2025-07-28T23:38:15.161Z","avatar_url":"https://github.com/pinecone-io.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Building a Context-Aware Chatbot with Pinecone and Vercel\n\nIn this example, we'll build a full-stack application that uses Retrieval Augmented Generation (RAG) powered by [Pinecone](https://pinecone.io) to deliver accurate and contextually relevant responses in a chatbot.\n\nRAG is a powerful tool that combines the benefits of retrieval-based models and generative models. Unlike traditional chatbots that can struggle with maintaining up-to-date information or accessing domain-specific knowledge, a RAG-based chatbot uses a knowledge base created from crawled URLs to provide contextually relevant responses.\n\nIncorporating Vercel's AI SDK into our application will allow us easily set up the chatbot workflow and utilize streaming more efficiently, particularly in edge environments, enhancing the responsiveness and performance of our chatbot.\n\nBy the end of this tutorial, you'll have a context-aware chatbot that provides accurate responses without hallucination, ensuring a more effective and engaging user experience. Let's get started on building this powerful tool ([Full code listing](https://github.com/pinecone-io/pinecone-vercel-example/blob/main/package.json)).\n\n## Step 1: Setting Up Your Next.js Application\n\nFirst, create a new Next.js app and install the necessary packages:\n\n```bash\nnpx create-next-app chatbot\ncd chatbot\nnpm install ai react @pinecone-database/pinecone\n```\n\n## Step 2: Create the Chatbot\nIn this step, we are going to build a chat interface that will render two components. One of these components will be a chatbot with context support provided by Pinecone. The other component will be a chatbot without context. Both of these components will present messages received by the `useChat` hook from the Vercel AI SDK.\n\n\n### Chatbot Frontend Component\n\nCreate a Chat component that will render the chat interface. This component will have two ChatWrapper components, one for the chatbot with context and one without context.\nWhen a message is sent, each of the `ChatWrapper` components will be notified and take on the responsibility of sending the message to the backend, as well as presenting with the proper messages.\n\n```tsx\n// Importing necessary modules and types\nimport AppContext from \"@/appContext\";\nimport type { PineconeRecord } from \"@pinecone-database/pinecone\";\nimport React, { ChangeEvent, FormEvent, useContext, useRef } from \"react\";\nimport ChatInput from \"./ChatInput\";\nimport ChatWrapper, { ChatInterface } from \"./ChatWrapper\";\n\n// Defining the properties for the Chat component\ninterface ChatProps {\n  setContext: (data: { context: PineconeRecord[] }[]) =\u003e void;\n  context: { context: PineconeRecord[] }[] | null;\n}\n\n// The Chat component\nconst Chat: React.FC\u003cChatProps\u003e = ({ setContext, context }) =\u003e {\n  // Creating references for the chat components with and without context\n  const chatWithContextRef = useRef\u003cChatInterface | null\u003e(null);\n  const chatWithoutContextRef = useRef\u003cChatInterface | null\u003e(null);\n\n  // Accessing the total number of records from the application context\n  const { totalRecords } = useContext(AppContext);\n\n  // State for the chat input\n  const [input, setInput] = React.useState\u003cstring\u003e(\"\")\n\n  // Function to handle message submission\n  const onMessageSubmit = (e: FormEvent\u003cHTMLFormElement\u003e) =\u003e {\n    // Clear the input\n    setInput(\"\")\n    // Submit the message to both chat components\n    chatWithContextRef.current?.handleMessageSubmit(e)\n    chatWithoutContextRef.current?.handleMessageSubmit(e)\n  }\n\n  // Function to handle input change\n  const onInputChange = (event: ChangeEvent\u003cHTMLInputElement\u003e) =\u003e {\n    // Update the input state\n    setInput(event.target.value)\n    // Update the input in both chat components\n    chatWithContextRef.current?.handleInputUpdated(event)\n    chatWithoutContextRef.current?.handleInputUpdated(event)\n  }\n\n  // Rendering the Chat component\n  return (\n    // The chat interface is divided into two sections, one for the chat with context and one without context\n    \u003cdiv id=\"chat\" className=\"flex flex-col w-full h-full\"\u003e\n      \u003cdiv className=\"flex flex-grow\"\u003e\n        \u003cdiv className=\"w-1/2\"\u003e\n          \u003cChatWrapper ref={chatWithoutContextRef} withContext={true} setContext={setContext} context={context} /\u003e\n        \u003c/div\u003e\n        \u003cdiv className=\"w-1/2\"\u003e\n          \u003cChatWrapper ref={chatWithContextRef} withContext={false} setContext={setContext} /\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      // The chat input is rendered at the bottom of the chat interface\n      \u003cdiv className=\"w-full\"\u003e\n        \u003cChatInput input={input} handleInputChange={onInputChange} handleMessageSubmit={onMessageSubmit} showIndexMessage={totalRecords === 0} /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\n\n// Exporting the Chat component\nexport default Chat;\n```\n\n### ChatWrapper Component\n\n\nThe Chat component is responsible for handling the chatbot's input and message submission. It uses the useChat hook from the ai package to manage the chatbot's state.\nThe component is divided into two parts: the Messages component that displays the chat messages, and a form for submitting new messages. The component also generates a unique ID for each message.\n\n```tsx\n\nimport type { PineconeRecord } from \"@pinecone-database/pinecone\";\nimport { useChat } from \"ai/react\";\nimport React, { ChangeEvent, FormEvent, Ref, forwardRef, useEffect, useImperativeHandle, useRef } from \"react\";\nimport { v4 as uuidv4 } from 'uuid';\nimport Messages from \"./Messages\";\n\nexport interface ChatInterface {\n    handleMessageSubmit: (e: FormEvent\u003cHTMLFormElement\u003e) =\u003e void;\n    handleInputUpdated: (event: ChangeEvent\u003cHTMLInputElement\u003e) =\u003e void;\n    ref: Ref\u003cChatInterface\u003e;\n    withContext: boolean;\n}\n\ninterface ChatProps {\n    withContext: boolean;\n    setContext: (data: { context: PineconeRecord[] }[]) =\u003e void;\n    context?: { context: PineconeRecord[] }[] | null;\n    ref: Ref\u003cChatInterface\u003e\n}\n\nconst Chat: React.FC\u003cChatProps\u003e = forwardRef\u003cChatInterface, ChatProps\u003e(({ withContext, setContext, context }, ref) =\u003e {\n    const { messages, handleInputChange, handleSubmit, isLoading, data } = useChat({\n        sendExtraMessageFields: true,\n        body: {\n            withContext,\n        },\n    });\n\n    useEffect(() =\u003e {\n        if (data) {\n            setContext(data as { context: PineconeRecord[] }[]) // Logs the additional data\n        }\n    }, [data, setContext]);\n\n    const chatRef = useRef\u003cChatInterface\u003e(null);\n\n    useImperativeHandle(ref, () =\u003e ({\n        handleMessageSubmit: (event: FormEvent\u003cHTMLFormElement\u003e) =\u003e {\n            const id = uuidv4(); // Generate a unique ID\n            handleSubmit(event, {\n                data: {\n                    messageId: id, // Include the ID in the message object\n                    \n                },\n            })\n        },\n        handleInputUpdated: (event: ChangeEvent\u003cHTMLInputElement\u003e) =\u003e {\n            handleInputChange(event);\n        },\n    }));\n\n    return (\n        \u003cdiv className=\"flex flex-col h-full\"\u003e\n            \u003cMessages messages={messages} withContext={withContext} context={context} /\u003e\n            \u003cform onSubmit={(e) =\u003e chatRef.current?.handleMessageSubmit(e)} className=\"...\"\u003e\n                \u003cinput\n                    type=\"text\"\n                    className=\"...\"\n                    onChange={(e) =\u003e chatRef.current?.handleInputUpdated(e)}\n                /\u003e\n                \u003cbutton type=\"submit\" className=\"...\"\u003eSend\u003c/button\u003e\n            \u003c/form\u003e\n        \u003c/div\u003e\n    );\n});\n\nChat.displayName = 'Chat';\n\nexport default Chat;\n```            \n\n### ChatInput Component\n\nThe ChatInput component is responsible for rendering the chat input field and the send button. It uses the `handleInputChange` and `handleMessageSubmit` functions from the Chat component to handle input changes and message submission.\n\n```tsx\nimport React, { ChangeEvent, FormEvent } from \"react\";\n\ninterface ChatInputProps {\n    input: string;\n    handleInputChange: (e: ChangeEvent\u003cHTMLInputElement\u003e) =\u003e void;\n    handleMessageSubmit: (e: FormEvent\u003cHTMLFormElement\u003e) =\u003e void;\n    showIndexMessage: boolean;\n}\n\nconst ChatInput: React.FC\u003cChatInputProps\u003e = ({ input, handleInputChange, handleMessageSubmit, showIndexMessage }) =\u003e {\n    return (\n        \u003cform onSubmit={handleMessageSubmit} className=\"...\"\u003e\n        \u003cinput\n                type=\"text\"\n                className=\"...\"\n                value={input}\n                onChange={handleInputChange}\n            /\u003e\n            \u003cbutton type=\"submit\" className=\"...\"\u003eSend\u003c/button\u003e\n            {showIndexMessage \u0026\u0026 (\n                \u003cdiv className=\"...\"\u003e\n                    \u003cspan className=\"...\"\u003ePress ⮐ to send\u003c/span\u003e\n                \u003c/div\u003e\n            )}\n        \u003c/form\u003e\n    );\n};\n\nexport default ChatInput;\n```\n\n\n\n## Step 3. Adding Context\n\nAs we dive into building our chatbot, it's important to understand the role of context. Adding context to our chatbot's responses is key for creating a more natural, conversational user experience. Without context, a chatbot's responses can feel disjointed or irrelevant. By understanding the context of a user's query, our chatbot will be able to provide more accurate, relevant, and engaging responses. Now, let's begin building with this goal in mind.\n\nFirst, we'll first focus on seeding the knowledge base. We'll create a crawler and a seed script, and set up a crawl endpoint. This will allow us to gather and organize the information our chatbot will use to provide contextually relevant responses.\n\nAfter we've populated our knowledge base, we'll retrieve matches from our embeddings. This will enable our chatbot to find relevant information based on user queries.\n\nNext, we'll wrap our logic into the getContext function and update our chatbot's prompt. This will streamline our code and improve the user experience by ensuring the chatbot's prompts are relevant and engaging.\n\nFinally, we'll add a context panel and an associated context endpoint. These will provide a user interface for the chatbot and a way for it to retrieve the necessary context for each user query.\n\nThis step is all about feeding our chatbot the information it needs and setting up the necessary infrastructure for it to retrieve and use that information effectively. Let's get started.\n\n## Seeding the Knowledge Base\n\nNow we'll move on to seeding the knowledge base, the foundational data source that will inform our chatbot's responses. This step involves collecting and organizing the information our chatbot needs to operate effectively. In this guide, we're going to use data retrieved from various websites which we'll later on be able to ask questions about. To do this, we'll create a crawler that will scrape the data from the websites, embed it, and store it in Pinecone.\n\n### Create the crawler\n\nFor the sake of brevity, you'll be able to find the full code for the crawler here. Here are the pertinent parts:\n\n```ts\nclass Crawler {\n  private seen = new Set\u003cstring\u003e();\n  private pages: Page[] = [];\n  private queue: { url: string; depth: number }[] = [];\n\n  constructor(private maxDepth = 2, private maxPages = 1) {}\n\n  async crawl(startUrl: string): Promise\u003cPage[]\u003e {\n    // Add the start URL to the queue\n    this.addToQueue(startUrl);\n\n    // While there are URLs in the queue and we haven't reached the maximum number of pages...\n    while (this.shouldContinueCrawling()) {\n      // Dequeue the next URL and depth\n      const { url, depth } = this.queue.shift()!;\n\n      // If the depth is too great or we've already seen this URL, skip it\n      if (this.isTooDeep(depth) || this.isAlreadySeen(url)) continue;\n\n      // Add the URL to the set of seen URLs\n      this.seen.add(url);\n\n      // Fetch the page HTML\n      const html = await this.fetchPage(url);\n\n      // Parse the HTML and add the page to the list of crawled pages\n      this.pages.push({ url, content: this.parseHtml(html) });\n\n      // Extract new URLs from the page HTML and add them to the queue\n      this.addNewUrlsToQueue(this.extractUrls(html, url), depth);\n    }\n\n    // Return the list of crawled pages\n    return this.pages;\n  }\n\n  // ... Some private methods removed for brevity\n\n  private async fetchPage(url: string): Promise\u003cstring\u003e {\n    try {\n      const response = await fetch(url);\n      return await response.text();\n    } catch (error) {\n      console.error(`Failed to fetch ${url}: ${error}`);\n      return \"\";\n    }\n  }\n\n  private parseHtml(html: string): string {\n    const $ = cheerio.load(html);\n    $(\"a\").removeAttr(\"href\");\n    return NodeHtmlMarkdown.translate($.html());\n  }\n\n  private extractUrls(html: string, baseUrl: string): string[] {\n    const $ = cheerio.load(html);\n    const relativeUrls = $(\"a\")\n      .map((_, link) =\u003e $(link).attr(\"href\"))\n      .get() as string[];\n    return relativeUrls.map(\n      (relativeUrl) =\u003e new URL(relativeUrl, baseUrl).href\n    );\n  }\n}\n```\n\nThe `Crawler` class is a web crawler that visits URLs, starting from a given point, and collects information from them. It operates within a certain depth and a maximum number of pages as defined in the constructor. The crawl method is the core function that starts the crawling process.\n\nThe helper methods fetchPage, parseHtml, and extractUrls respectively handle fetching the HTML content of a page, parsing the HTML to extract text, and extracting all URLs from a page to be queued for the next crawl. The class also maintains a record of visited URLs to avoid duplication.\n\n### Create the `seed` function\n\nTo tie things together, we'll create a seed function that will use the crawler to seed the knowledge base. In this portion of the code, we'll initialize the crawl and fetch a given URL, then split it's content into chunks, and finally embed and index the chunks in Pinecone.\n\n```ts\nasync function seed(url: string, limit: number, indexName: string, options: SeedOptions) {\n  try {\n    // Initialize the Pinecone client\n    const pinecone = new Pinecone();\n\n    // Destructure the options object\n    const { splittingMethod, chunkSize, chunkOverlap } = options;\n\n    // Create a new Crawler with depth 1 and maximum pages as limit\n    const crawler = new Crawler(1, limit || 100);\n\n    // Crawl the given URL and get the pages\n    const pages = await crawler.crawl(url) as Page[];\n\n    // Choose the appropriate document splitter based on the splitting method\n    const splitter: DocumentSplitter = splittingMethod === 'recursive' ?\n      new RecursiveCharacterTextSplitter({ chunkSize, chunkOverlap }) : new MarkdownTextSplitter({});\n\n    // Prepare documents by splitting the pages\n    const documents = await Promise.all(pages.map(page =\u003e prepareDocument(page, splitter)));\n\n    // Create Pinecone index if it does not exist\n    const indexList = await pinecone.listIndexes();\n    const indexExists = indexList.some(index =\u003e index.name === indexName)\n    if (!indexExists) {\n      await pinecone.createIndex({\n        name: indexName,\n        dimension: 1536,\n        waitUntilReady: true,\n      });\n    }\n\n    const index = pinecone.Index(indexName)\n\n    // Get the vector embeddings for the documents\n    const vectors = await Promise.all(documents.flat().map(embedDocument));\n\n    // Upsert vectors into the Pinecone index\n    await chunkedUpsert(index!, vectors, '', 10);\n\n    // Return the first document\n    return documents[0];\n  } catch (error) {\n    console.error(\"Error seeding:\", error);\n    throw error;\n  }\n}\n```\n\nTo chunk the content we'll use one of the following methods:\n\n1. `RecursiveCharacterTextSplitter` - This splitter splits the text into chunks of a given size, and then recursively splits the chunks into smaller chunks until the chunk size is reached. This method is useful for long documents.\n2. `MarkdownTextSplitter` - This splitter splits the text into chunks based on Markdown headers. This method is useful for documents that are already structured using Markdown. The benefit of this method is that it will split the document into chunks based on the headers, which will be useful for our chatbot to understand the structure of the document. We can assume that each unit of text under a header is an internally coherent unit of information, and when the user asks a question, the retrieved context will be internally coherent as well.\n\n### Add the `crawl` endpoint`\n\nThe endpoint for the `crawl` endpoint is pretty straightforward. It simply calls the `seed` function and returns the result.\n\n```ts\nimport seed from \"./seed\";\nimport { NextResponse } from \"next/server\";\n\nexport const runtime = \"edge\";\n\nexport async function POST(req: Request) {\n  const { url, options } = await req.json();\n  try {\n    const documents = await seed(url, 1, process.env.PINECONE_INDEX!, options);\n    return NextResponse.json({ success: true, documents });\n  } catch (error) {\n    return NextResponse.json({ success: false, error: \"Failed crawling\" });\n  }\n}\n```\n\nNow our backend is able to crawl a given URL, embed the content and index the embeddings in Pinecone. The endpoint will return all the segments in the retrieved webpage we crawl, so we'll be able to display them. Next, we'll write a set of functions that will build the context out of these embeddings.\n\n### Get matches from embeddings\n\nTo retrieve the most relevant documents from the index, we'll use the `query` function in the Pinecone SDK. This function takes a vector and returns the most similar vectors from the index. We'll use this function to retrieve the most relevant documents from the index, given some embeddings.\n\n```ts\nconst getMatchesFromEmbeddings = async (embeddings: number[], topK: number, namespace: string): Promise\u003cScoredPineconeRecord\u003cMetadata\u003e[]\u003e =\u003e {\n  // Obtain a client for Pinecone\n  const pinecone = new Pinecone();\n\n  const indexName: string = process.env.PINECONE_INDEX || '';\n  if (indexName === '') {\n    throw new Error('PINECONE_INDEX environment variable not set')\n  }\n\n  // Retrieve the list of indexes to check if expected index exists\n  const indexes = await pinecone.listIndexes()\n  if (indexes.filter(i =\u003e i.name === indexName).length !== 1) {\n    throw new Error(`Index ${indexName} does not exist`)\n  }\n\n  // Get the Pinecone index\n  const index = pinecone!.Index\u003cMetadata\u003e(indexName);\n\n  // Get the namespace\n  const pineconeNamespace = index.namespace(namespace ?? '')\n\n  try {\n    // Query the index with the defined request\n    const queryResult = await pineconeNamespace.query({\n      vector: embeddings,\n      topK,\n      includeMetadata: true,\n    })\n    return queryResult.matches || []\n  } catch (e) {\n    // Log the error and throw it\n    console.log(\"Error querying embeddings: \", e)\n    throw new Error(`Error querying embeddings: ${e}`)\n  }\n}\n```\n\nThe function takes in embeddings, a topK parameter, and a namespace, and returns the topK matches from the Pinecone index. It first gets a Pinecone client, checks if the desired index exists in the list of indexes, and throws an error if not. Then it gets the specific Pinecone index. The function then queries the Pinecone index with the defined request and returns the matches.\n\n### Wrap things up in `getContext`\n\nWe'll wrap things together in the `getContext` function. This function will take in a `message` and return the context - either in string form, or as a set of `ScoredVector`.\n\n```ts\nexport const getContext = async (\n  message: string,\n  namespace: string,\n  maxTokens = 3000,\n  minScore = 0.7,\n  getOnlyText = true\n): Promise\u003cstring | ScoredVector[]\u003e =\u003e {\n  // Get the embeddings of the input message\n  const embedding = await getEmbeddings(message);\n\n  // Retrieve the matches for the embeddings from the specified namespace\n  const matches = await getMatchesFromEmbeddings(embedding, 3, namespace);\n\n  // Filter out the matches that have a score lower than the minimum score\n  const qualifyingDocs = matches.filter((m) =\u003e m.score \u0026\u0026 m.score \u003e minScore);\n\n  // If the `getOnlyText` flag is false, we'll return the matches\n  if (!getOnlyText) {\n    return qualifyingDocs;\n  }\n\n  let docs = matches\n    ? qualifyingDocs.map((match) =\u003e (match.metadata as Metadata).chunk)\n    : [];\n  // Join all the chunks of text together, truncate to the maximum number of tokens, and return the result\n  return docs.join(\"\\n\").substring(0, maxTokens);\n};\n```\n\nBack in `chat/route.ts`, we'll add the call to `getContext`:\n\n```ts\nconst { messages } = await req.json();\n\n// Get the last message\nconst lastMessage = messages[messages.length - 1];\n\n// Get the context from the last message\nconst context = await getContext(lastMessage.content, \"\");\n```\n\n### Update the prompt\n\nFinally, we'll update the prompt to include the context we retrieved from the `getContext` function.\n\n```ts\nconst prompt = [\n  {\n    role: \"system\",\n    content: `AI assistant is a brand new, powerful, human-like artificial intelligence.\n  The traits of AI include expert knowledge, helpfulness, cleverness, and articulateness.\n  AI is a well-behaved and well-mannered individual.\n  AI is always friendly, kind, and inspiring, and he is eager to provide vivid and thoughtful responses to the user.\n  AI has the sum of all knowledge in their brain, and is able to accurately answer nearly any question about any topic in conversation.\n  AI assistant is a big fan of Pinecone and Vercel.\n  START CONTEXT BLOCK\n  ${context}\n  END OF CONTEXT BLOCK\n  AI assistant will take into account any CONTEXT BLOCK that is provided in a conversation.\n  If the context does not provide the answer to question, the AI assistant will say, \"I'm sorry, but I don't know the answer to that question\".\n  AI assistant will not apologize for previous responses, but instead will indicated new information was gained.\n  AI assistant will not invent anything that is not drawn directly from the context.\n  `,\n  },\n];\n```\n\nIn this prompt, we added a `START CONTEXT BLOCK` and `END OF CONTEXT BLOCK` to indicate where the context should be inserted. We also added a line to indicate that the AI assistant will take into account any context block that is provided in a conversation.\n\n### Attaching the context data to the messages\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpinecone-io%2Fpinecone-rag-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpinecone-io%2Fpinecone-rag-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpinecone-io%2Fpinecone-rag-demo/lists"}