Google이나 Perplexity.ai를 사용해 보셨나요? 최신 검색 결과를 표시하고 온라인 기사에 대한 링크를 포함하는 방법이 궁금하십니까? 자, 이 가이드에서는 그러한 시스템을 스스로 만드는 방법을 배우게 됩니다. 점점 늘어나는 지식 저장소에 추가하는 기사의 링크를 기반으로 추천을 생성할 수 있는 시스템을 만드는 방법을 배우게 됩니다.
전제조건
다음이 필요합니다:
- Node.js 18 이상
- Upstash 계정
- OpenAI 계정
- Fly.io 계정
기술 스택
단계
이 가이드를 완료하고 자신만의 기사 추천 시스템을 배포하려면 다음 단계를 따라야 합니다.
- OpenAI 토큰 생성
- Upstash 벡터 인덱스 생성
- 프로젝트 설정
- OpenAI API 클라이언트 인스턴스화
- OpenAI API 임베딩 클라이언트 생성
- Upstash 벡터 클라이언트 생성
- 컨텍스트 API 엔드포인트 생성
- Chat API 엔드포인트 생성
- Fly.io에 배포
- 참고자료
- 결론
OpenAI 토큰 생성
OpenAI API를 사용하면 기사의 벡터 임베딩을 얻을 수 있고 AI를 사용하여 챗봇 응답을 만들 수 있습니다. OpenAI API에 대한 요청에는 인증 토큰이 필요합니다. 토큰을 얻으려면 OpenAI 계정의 API 키로 이동하여 새 비밀 키 만들기를 클릭하세요. 버튼. 나중에 OPENAI_API_KEY로 사용할 수 있도록 이 토큰을 복사하여 안전하게 저장하세요. 환경변수입니다.
Upstash 벡터 인덱스 생성
Upstash 계정을 만들고 로그인한 후 벡터 탭으로 이동하여 색인 만들기를 클릭하세요. 벡터 인덱스 생성을 시작하세요.

원하는 색인 이름을 입력하세요(예:article). ) 벡터 크기를 1536으로 설정합니다.

이제 연결까지 아래로 스크롤하세요. 섹션을 클릭하고 .env를 클릭하세요. 버튼. 내용을 복사하고 애플리케이션에서 나중에 사용할 수 있도록 안전한 곳에 저장하세요.

프로젝트 설정
설정하려면 앱 저장소를 복제하고 이 가이드에 따라 그 안에 있는 모든 내용을 알아보세요. 프로젝트를 복제하려면 터미널에서 다음을 실행하세요:
# Clone the project
git clone https://github.com/rishi-raj-jain/article-recommendation-system
cd article-recommendation-system
# Install the dependencies
pnpm install
저장소를 복제한 후 .env을 생성하세요. 파일. 위 섹션에서 얻은 비밀 키를 추가하게 됩니다.
.env 파일에는 다음 키가 포함되어야 합니다:
# .env
# OpenAI API Key
OPENAI_API_KEY="sk-..."
# Upstash Vector Keys
UPSTASH_VECTOR_REST_URL="https://...-us1-vector.upstash.io"
UPSTASH_VECTOR_REST_TOKEN="...=" 완료되면 구성 설정이 완료됩니다. 이제 터미널에서 다음 명령을 실행하고 localhost:3000을 방문하여 애플리케이션이 작동하는 모습을 볼 수 있습니다.
pnpm run build && pnpm run start 자신만의 기사 추천 시스템을 성공적으로 구축하는 데 도움이 되는 코드의 관련 부분을 이해하려면 다음 단계를 따르세요.
OpenAI API 클라이언트 인스턴스화
openai 사용 패키지를 사용하면 몇 줄의 코드만으로 시간을 절약하고 OpenAI REST API와 상호 작용할 수 있습니다. 다음 코드를 사용하여 채팅 완료 응답을 생성하는 데 추가로 사용할 수 있도록 OpenAI API 클라이언트 라이브러리를 인스턴스화했습니다.
// File: app/lib/openai/completion.server.ts
import OpenAI from 'openai'
// Instantiate class to generate text completion using the OpenAI API
export default new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})
참고:.server.ts 추가 Remix의 파일에 추가하면 해당 코드가 강제로 아웃되는지 확인할 수 있습니다. 클라이언트측 번들의 일부입니다.
OpenAI API 임베딩 클라이언트 생성
@langchain/openai 사용 패키지를 사용하면 OpenAIEmbeddings를 사용할 수 있습니다. 특정 텍스트의 벡터 임베딩을 생성하기 위한 클래스입니다. OpenAIEmbeddings 클래스를 LangChain 벡터 저장소와 결합하면 각 벡터 임베딩을 직접 생성하고 삽입하는 과정이 필요 없습니다. 다음 코드를 사용하여 내부적으로 벡터 임베딩을 생성하는 데 사용할 OpenAIEmbeddings 클래스를 인스턴스화했습니다.
// File: app/lib/openai/embedding.server.ts
import { OpenAIEmbeddings } from '@langchain/openai'
// Instantiate class to generate embeddings using the OpenAI API
export default new OpenAIEmbeddings({
modelName: 'text-embedding-3-small',
openAIApiKey: process.env.OPENAI_API_KEY,
}) Upstash 벡터 클라이언트 생성
@upstash/vector 사용 그리고 @langchain/community/vectorstores/upstash 패키지를 사용하면 Remix 애플리케이션에서 Upstash 벡터 인덱스의 벡터 임베딩을 저장, 삭제 및 쿼리할 수 있는 비연결 클라이언트를 생성할 수 있습니다.
// 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'
// Instantiate the Upstash Vector Index
const index = new UpstashIndex({
url: process.env.UPSTASH_VECTOR_REST_URL as string,
token: process.env.UPSTASH_VECTOR_REST_TOKEN as string,
})
// Instantiate the Upstash Vector Store that'll create and save embeddings
export default new UpstashVectorStore(embeddings, { index }) 컨텍스트 API 엔드포인트 생성
Remix 애플리케이션을 실행하면 여러 기사 URL을 입력으로 허용하는 텍스트 상자가 표시됩니다. 이러한 기사는 향후 사용자 검색에서 개인화된 응답을 생성하기 위해 챗봇의 지식에 추가됩니다. 이 섹션에서는 컨텍스트 엔드포인트(app/routes/api_.context.tsx )은 여러 기사 URL을 허용하고, 해당 콘텐츠를 가져오고, 벡터 임베딩을 생성하고, 이를 Upstash 벡터 색인에 동적으로 저장하도록 설정되었습니다.
// 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()
// Check if any article link are present in the form submission
const articlesToEmbed = formData.get('articles') as string
if (articlesToEmbed) {
// Create the documents to be added to the Upstash Vector Store
const documents: any[] = []
await Promise.all(
articlesToEmbed.split(',').map(async (link) => {
// Use the link to render in the search results
// Parse the link using Cheerio
const loader = new CheerioWebBaseLoader(link.trim())
const scraper = await loader.scrape()
// Get the content of title tag to render in the search results
const name = scraper('title').html()
// Get the page content as string
const pageContent = scraper.text()
// Create metadata object to be inserted in the vector store
const metadata = { link, name }
documents.push(new Document({ pageContent, metadata }))
}),
)
// Creating embeddings from the provided documents along with metadata
// and add them to Upstash database
await vectorServer.addDocuments(documents.filter(Boolean))
}
}
위의 Remix Action에서 컨텍스트 엔드포인트(/api/context)에 대한 POST 요청에 있는 양식 데이터 )이 구문 분석됩니다. 또한 다음을 수행하는 쉼표(,)로 구분된 기사 링크 세트에 대한 루프가 있습니다.
pageContent을 생성합니다. 기사의 웹페이지에서 가져온 텍스트 콘텐츠로 변수name을 생성합니다. 기사 웹페이지 제목으로 변수- 텍스트 내용, 참조 및 기사 이름이 포함된 LangChain 문서를 생성합니다(
new Document({ pageContent, metadata })) - 각 문서를 전역
documents에 추가합니다. 배열
마지막으로 전역 documents에 저장된 모든 문서는 배열은 Upstash 벡터 인덱스에 삽입됩니다. 내부적으로는 각 문서의 벡터 임베딩이 pageContent를 사용하여 생성됩니다. 속성입니다.
Chat API 엔드포인트 생성
이 섹션에서는 채팅 API 엔드포인트(app/routes/api_.chat.tsx)가 어떻게 작동하는지 알아봅니다. )은 사용자 검색과 관련된 기사에 대한 권장 사항이 포함된 응답과 같은 검색 엔진을 생성하도록 설정됩니다. 주어진 벡터 인덱스에서 가장 가까운 Top-K 벡터를 찾아서 검색과 관련된 기사를 찾습니다. 또한 벡터 메타데이터의 제목과 링크가 OpenAI API에 컨텍스트로 전달됩니다. 이를 통해 챗봇은 사용자 검색에 응답하면서 기사를 외부 링크로 포함할 수 있습니다. 단순화를 위해 다음과 같은 부분으로 나누어 보겠습니다.
유사성 검색을 사용하여 관련 벡터 임베딩 찾기
각 사용자 검색에 대해 전체 기사 지식 모음을 다시 방문하는 것은 비용이 많이 드는 작업입니다. 사용자 검색과 관련성이 높은 상위 3개 기사로 범위를 좁히려면 Upstash 벡터 인덱스의 기존 벡터 세트를 쿼리하세요. 또한, 임베딩이 사용자 검색의 벡터 임베딩과 최소 70% 유사성 점수를 갖는 벡터를 유지하도록 필터링하세요.
// 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) => {
// Set of messages between user and chatbot
const { messages = [] } = await request.json()
// Get the latest question stored in the last message of the chat array
const searchQuery = messages[messages.length - 1].content
// Perform Similarity Search using the Upstash Vector Store
const queryResult = await vectorServer.similaritySearchWithScore(searchQuery, 3)
// Filter the records with confidence score > 70% and
// set the metadata as response to render search results
const results = queryResult.filter((i) => i[1] >= 0.7).map((i) => i[0].metadata)
// Proceed to create a response
} 챗봇을 위한 시스템 컨텍스트 및 지침 생성
이제 관련성이 높은 벡터 세트를 얻었으므로 챗봇이 사용자 검색에 응답하기 전에 관련 기사를 알고 참조하기를 원합니다. OpenAI의 gpt-3.5-turbo을 사용하여 이를 수행하려면 모델에서 역할 속성이 system인 메시지 개체를 만듭니다. 다음 지침을 포함하는 콘텐츠 속성:
- 챗봇은 Google처럼 응답해야 합니다.
- 챗봇은 응답이 마크다운 형식인지 확인해야 합니다.
- 챗봇의 응답에는 기사에 대한 하이퍼링크가 있어야 합니다.
- 챗봇은 단순히 기사에 대한 참조를 포함하는 것 이상입니다.
// File: app/routes/api_.train.tsx
import { OpenAIStream, StreamingTextResponse } from 'ai'
import completionServer from '~/lib/openai/completion.server'
export const action = async ({ request }: ActionFunctionArgs) => {
// ...
// Now use OpenAI Text Completion with relevant articles as context
const completionResponse = await completionServer.chat.completions.create({
stream: true,
model: 'gpt-3.5-turbo',
messages: [
{
// create a system content message to be added as
// the open ai text completion will supply it as the context with the API
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.`,
},
// also, pass the whole conversation!
...messages,
],
})
// Convert the response into a friendly text-stream
const stream = OpenAIStream(completionResponse)
// Respond with the stream
return new StreamingTextResponse(stream)
}
위의 코드를 사용하면 상황을 인식하는 OpenAI의 결과를 스트리밍하여 사용자 검색과 관련된 기사를 추천할 수 있습니다.
정말 많이 배웠습니다! 이제 모든 작업이 완료되었습니다 ✨
Fly.io에 배포
저장소에는 특히 다음과 관련된 Fly.io용 기본 설정이 함께 제공됩니다.
- 도커파일
- fly.toml
- .dockerignore
Fly.io 계정이 있으면 터미널 루트 디렉터리에서 다음 명령을 실행하여 Fly.io에서 앱을 생성할 수 있습니다.
# Create an app based on the baked-in configuration in your account
# This will result only in the change of app name in existing fly.toml
fly launch 터미널에서 다음 명령을 실행하여 Fly.io에 배포합니다.
# Deploy the app based on the configuration created above
fly deploy 참고자료
더 자세한 통찰력을 얻으려면 이 가이드에 사용된 참고 자료를 살펴보세요.
- GitHub 저장소
- LangChain과 Upstash 벡터 스토어 통합
- OpenAI Chat Completions API의 시스템 지침
- LangChain에서 Cheerio를 사용하여 웹페이지에서 데이터 로드
- React 앱에서 AI Chat UI 만들기
결론
이 가이드에서는 동적으로 생성된 시스템 컨텍스트와 함께 벡터 임베딩 및 OpenAI Completion API를 사용하여 기사 추천 시스템을 구축하는 방법을 배웠습니다. Upstash Vector 및 LangChain을 사용하면 인덱스에 벡터를 저장하고, 상위 K 벡터 검색 쿼리를 수행하고, 각 사용자 검색에 대한 관련 컨텍스트를 생성하는 기능을 모두 몇 줄의 코드 내에서 수행할 수 있습니다.
질문이나 의견이 있으시면 언제든지 GitHub를 통해 저에게 연락해 주세요.