เคยใช้ Google หรือ Perplexity.ai ไหม? คุณเคยสงสัยไหมว่าพวกเขาแสดงผลการค้นหาที่ทันสมัยและมีลิงก์ไปยังบทความออนไลน์ได้อย่างไร? ในคู่มือนี้ คุณจะได้เรียนรู้วิธีสร้างระบบแบบนี้ด้วยตัวเอง ระบบที่สามารถสร้างคำแนะนำจากลิงก์บทความที่คุณเพิ่มเข้าไปในฐานความรู้ที่เพิ่มขึ้นเรื่อยๆ
ข้อกำหนดเบื้องต้น
- Node.js 18 หรือใหม่กว่า
- บัญชี Upstash
- บัญชี OpenAI
- บัญชี Fly.io
เทคโนโลยีที่ใช้
| เทคโนโลยี | คำอธิบาย |
|---|---|
| Upstash | แพลตฟอร์มฐานข้อมูล Serverless ใช้ Upstash Vector สำหรับเก็บ vector embeddings และ metadata |
| Remix | เฟรมเวิร์กสร้างแอปเว็บ full-stack เน้นมาตรฐานเว็บ |
| OpenAI | ห้องปฏิบัติการวิจัย AI พัฒนาเทคโนโลยี AI ขั้นสูง |
| LangChain | เฟรมเวิร์กพัฒนาแอปพลิเคชันขับเคลื่อนด้วย language models |
| Vercel AI SDK | ไลบรารีโอเพนซอร์สสร้าง UI ขับเคลื่อนด้วย AI |
| TailwindCSS | เฟรมเวิร์ก CSS สำหรับออกแบบที่ปรับแต่งได้ |
| Fly.io | แพลตฟอร์มรันแอป full-stack และฐานข้อมูลใกล้ผู้ใช้ |
| Prettier | เครื่องมือจัดรูปแบบโค้ดแบบมีความคิดเห็น |
ขั้นตอนการทำงาน
- สร้าง OpenAI Token
- สร้าง Upstash Vector Index
- ตั้งค่าโปรเจกต์
- สร้าง OpenAI API Client
- สร้าง OpenAI API Embeddings Client
- สร้าง Upstash Vector Client
- สร้าง Context API Endpoint
- สร้าง Chat API Endpoint
- ปรับใช้งานบน Fly.io
- อ้างอิง
- สรุป
สร้าง OpenAI Token
ใช้ OpenAI API เพื่อดึง vector embeddings ของบทความ และสร้างคำตอบ chatbot ด้วย AI การร้องขอ OpenAI API ต้องใช้ authorization token ไปที่ API Keys ในบัญชี OpenAI กด Create new secret key คัดลอกและเก็บไว้เป็นตัวแปรสภาพแวดล้อม OPENAI_API_KEY
สร้าง Upstash Vector Index
เข้าสู่ระบบ Upstash ไปที่แท็บ Vector กด Create Index

ใส่ชื่อ index (เช่น article) และตั้งค่า vector dimensions = 1536

เลื่อนลงส่วน Connect กดปุ่ม .env คัดลอกเก็บไว้

ตั้งค่าโปรเจกต์
โคลน repo และติดตามคู่มือ:
# Clone the project
git clone https://github.com/rishi-raj-jain/article-recommendation-system
cd article-recommendation-system
# Install dependencies
pnpm install
สร้างไฟล์ .env ใส่ secret keys:
# .env
# OpenAI API Key
OPENAI_API_KEY="sk-..."
# Upstash Vector Keys
UPSTASH_VECTOR_REST_URL="https://...-us1-vector.upstash.io"
UPSTASH_VECTOR_REST_TOKEN="...="
รันแอปพลิเคชัน:
pnpm run build && pnpm run start
เข้าไปที่ localhost:3000
สร้าง OpenAI API Client
ใช้แพ็กเกจ openai สร้างไฟล์ app/lib/openai/completion.server.ts:
// File: app/lib/openai/completion.server.ts
import OpenAI from 'openai'
export default new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})
หมายเหตุ: การเติม .server.ts ใน Remix ทำให้โค้ดอยู่ฝั่งเซิร์ฟเวอร์เท่านั้น
สร้าง OpenAI API Embeddings Client
ใช้ @langchain/openai สร้างไฟล์ app/lib/openai/embedding.server.ts:
// File: app/lib/openai/embedding.server.ts
import { OpenAIEmbeddings } from '@langchain/openai'
export default new OpenAIEmbeddings({
modelName: 'text-embedding-3-small',
openAIApiKey: process.env.OPENAI_API_KEY,
})
สร้าง Upstash Vector Client
ใช้ @upstash/vector และ @langchain/community/vectorstores/upstash สร้างไฟล์ app/lib/upstash/vectorStore.server.ts:
// File: app/lib/upstash/vectorStore.server.ts
import embeddings from '~/lib/openai/embedding.server'
import { Index as UpstashIndex } from '@upstash/vector'
import { UpstashVectorStore } from '@langchain/community/vectorstores/upstash'
const index = new UpstashIndex({
url: process.env.UPSTASH_VECTOR_REST_URL as string,
token: process.env.UPSTASH_VECTOR_REST_TOKEN as string,
})
export default new UpstashVectorStore(embeddings, { index })
สร้าง Context API Endpoint
Endpoint app/routes/api_.context.tsx รับ URL บทความหลายๆ อัน ดึงเนื้อหา สร้าง embeddings เก็บใน Upstash Vector Index:
// File: app/routes/api_.context.tsx
import { Document } from 'langchain/document'
import { ActionFunctionArgs } from '@remix-run/node'
import vectorServer from '~/lib/vector/vectorStore.server'
import { CheerioWebBaseLoader } from 'langchain/document_loaders/web/cheerio'
export const action = async ({ request }: ActionFunctionArgs) => {
const formData = await request.formData()
const articlesToEmbed = formData.get('articles') as string
if (articlesToEmbed) {
const documents: any[] = []
await Promise.all(
articlesToEmbed.split(',').map(async (link) => {
const loader = new CheerioWebBaseLoader(link.trim())
const scraper = await loader.scrape()
const name = scraper('title').html()
const pageContent = scraper.text()
const metadata = { link, name }
documents.push(new Document({ pageContent, metadata }))
}),
)
await vectorServer.addDocuments(documents.filter(Boolean))
}
}
Remix Action นี้ parse form data วนลูป URL บทความ (คั่นด้วย comma):
- สร้าง
pageContentจากข้อความในหน้าเว็บ - สร้าง
nameจาก title ของหน้าเว็บ - สร้าง LangChain Document พร้อม metadata
- เพิ่มใน array
documents
สุดท้าย insert ทุก document เข้า Upstash Vector Index (สร้าง embeddings อัตโนมัติจาก pageContent)
สร้าง Chat API Endpoint
Endpoint app/routes/api_.chat.tsx สร้างคำตอบแบบ search engine พร้อมคำแนะนำบทความที่เกี่ยวข้อง ค้นหา top-K vectors ใกล้เคียง ส่ง title และ link จาก metadata ไปให้ OpenAI เป็น context
ค้นหา Vector Embeddings ที่เกี่ยวข้องด้วย Similarity Search
// File: app/routes/api_.chat.tsx
import vectorServer from '~/lib/upstash/vectorStore.server'
import type { ActionFunctionArgs } from '@remix-run/node'
export const action = async ({ request }: ActionFunctionArgs) => {
const { messages = [] } = await request.json()
const searchQuery = messages[messages.length - 1].content
const queryResult = await vectorServer.similaritySearchWithScore(searchQuery, 3)
const results = queryResult.filter(i => i[1] >= 0.7).map(i => i[0].metadata)
// สร้างคำตอบต่อไป...
}
สร้าง System Context และ Instructions สำหรับ Chatbot
สร้าง system message ให้ chatbot รู้จักบทความที่เกี่ยวข้อง ก่อนตอบคำถาม ใช้ gpt-3.5-turbo:
// File: app/routes/api_.train.tsx
import { OpenAIStream, StreamingTextResponse } from 'ai'
import completionServer from '~/lib/openai/completion.server'
export const action = async ({ request }: ActionFunctionArgs) => {
// ...
const completionResponse = await completionServer.chat.completions.create({
stream: true,
model: 'gpt-3.5-turbo',
messages: [
{
role: 'system',
content: `Behave like a Google. You have the knowledge of the following articles: ${JSON.stringify(results)}. Each response should be in 100% markdown compatible format and should have hyperlinks in it. Be precise. Do add some general text in the response related to the query.`,
},
...messages,
],
})
const stream = OpenAIStream(completionResponse)
return new StreamingTextResponse(stream)
}
ปรับใช้งานบน Fly.io
Repo มีการตั้งค่าสำหรับ Fly.io แล้ว (Dockerfile, fly.toml, .dockerignore)
# สร้างแอปใน Fly.io
fly launch
# ปรับใช้งาน
fly deploy
อ้างอิง
- GitHub Repository
- Upstash Vector Store Integration with LangChain
- System Instructions in OpenAI's Chat Completions API
- Load data from webpages using Cheerio in LangChain
- Creating AI Chat UI in React apps
สรุป
ในคู่มือนี้ คุณได้เรียนรู้การสร้างระบบแนะนำบทความด้วย Vector Embeddings และ OpenAI Completion API พร้อม system context ที่สร้างแบบไดนามิก ด้วย Upstash Vector และ LangChain คุณสามารถเก็บ vectors ใน index ค้นหา top-K vector queries และสร้าง context ที่เกี่ยวข้องสำหรับแต่ละคำค้นหาได้ทั้งหมดในไม่กี่บรรทัดของโค้ด
หากมีคำถามหรือความคิดเห็น สามารถติดต่อผมได้ที่ GitHub