Project: CSV Table Querying Chatbot
In this project, we will build a CSV-querying chatbot. The pipeline parses a local store product CSV file, indexes the rows into a vector database, and uses an LCEL chain to answer user questions about product pricing.
1. Project Workflow
graph TD
A[products.csv file] -->|CSVLoader| B[Document items]
B -->|MemoryVectorStore| C[Indexed Vector DB]
D[User: How much does item A cost?] -->|Retriever| C
C -->|Top matches| E[Prompt builder]
E -->|gpt-4o-mini| F[Final answer text reply]2. Implementing the CSV Query Engine
Create the full service module that chains retrieval and text completions:
// src/services/csvBot.ts
import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai";
import { CSVLoader } from "@langchain/community/document_loaders/fs/csv";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { RunnablePassthrough, RunnableSequence } from "@langchain/core/runnables";
// 1. Create a helper to format retrieved documents list into a raw string
function formatDocuments(docs: any[]) {
return docs.map((d) => d.pageContent).join("\n---\n");
}
export async function queryCsvCatalog(csvFilePath: string, userQuery: string) {
// 2. Load CSV records
const loader = new CSVLoader(csvFilePath);
const rawDocs = await loader.load();
// 3. Setup embeddings and index in MemoryVectorStore
const embeddings = new OpenAIEmbeddings({ modelName: "text-embedding-3-small" });
const vectorStore = await MemoryVectorStore.fromDocuments(rawDocs, embeddings);
const retriever = vectorStore.asRetriever({ k: 3 });
// 4. Declare the prompt template
const prompt = ChatPromptTemplate.fromMessages([
[
"system",
"You are a sales inventory helper. Answer customer questions based on these CSV records:\n\n{context}"
],
["user", "{userInput}"]
]);
const model = new ChatOpenAI({ modelName: "gpt-4o-mini", temperature: 0.1 });
const parser = new StringOutputParser();
// 5. Compose the LCEL pipeline sequence
const queryChain = RunnableSequence.from([
{
// Retrieve documents and pipe them to format helper
context: retriever.pipe(formatDocuments),
userInput: new RunnablePassthrough()
},
prompt,
model,
parser
]);
// 6. Invoke the pipeline
const answer = await queryChain.invoke(userQuery);
return answer;
}3. Creating Mock Data and Executing
Create a mock data file named products.csv:
id,name,price,description
1,Wireless Mouse,29.99,Ergonomic battery powered 2.4Ghz computer mouse.
2,Mechanical Keyboard,89.99,RGB backlit mechanical switches typing keyboard.
3,Noise Canceling Headphones,199.99,Bluetooth over-ear active noise canceling headphones.Call the query helper:
const answer = await queryCsvCatalog("./products.csv", "What is the price of the keyboard?");
console.log("Chatbot response:", answer); // Output: The price of the Mechanical Keyboard is $89.99.Published on Last updated: