Bu makalede size herhangi bir web sayfasının içeriğini özetleyebilecek kullanışlı bir web uygulamasının nasıl oluşturulacağını göstereceğiz. Sorunsuz ve hızlı bir web deneyimi için Next.js'yi , dili işlemek için LangChain'i , özetler oluşturmak için OpenAI'yi ve vektör verilerini yönetmek ve depolamak için Supabase'i kullanarak birlikte güçlü bir araç oluşturacağız.
Çevrimiçi ortamda bu kadar çok içerik varken hepimiz aşırı bilgi yüklemesiyle karşı karşıyayız. Hızlı özetler veren bir uygulama yaparak insanların zamandan tasarruf etmesine ve bilgi sahibi olmasına yardımcı oluyoruz. İster yoğun bir çalışan, ister öğrenci, ister sadece haberleri ve makaleleri takip etmek isteyen biri olun, bu uygulama sizin için yararlı bir araç olacaktır.
Uygulamamız, kullanıcıların herhangi bir web sitesi URL'sini girmesine ve sayfanın kısa bir özetini hızlı bir şekilde almasına olanak tanır. Bu, uzun makalelerin, blog yazılarının veya araştırma makalelerinin ana noktalarını, onları tam olarak okumadan anlayabileceğiniz anlamına gelir.
Bu özetleme uygulaması birçok yönden faydalı olabilir. Araştırmacıların akademik makalelere göz atmasına, haber severleri güncel tutmasına ve daha fazlasına yardımcı olabilir. Ayrıca geliştiriciler, daha da kullanışlı özellikler oluşturmak için bu uygulamayı geliştirebilirler.
Next.js, Vercel tarafından geliştirilen ve geliştiricilerin sunucu tarafı işleme (SSR) ve statik web uygulamalarını kolaylıkla oluşturmasına olanak tanıyan güçlü ve esnek bir React çerçevesidir. Optimize edilmiş ve ölçeklenebilir web uygulamaları oluşturmak için React'in en iyi özelliklerini ek yeteneklerle birleştirir.
Node.js'deki OpenAI modülü, OpenAI'nin API'si ile etkileşim kurmanın bir yolunu sağlayarak geliştiricilerin GPT-3 ve GPT-4 gibi güçlü dil modellerinden yararlanmasına olanak tanır. Bu modül, gelişmiş yapay zeka işlevlerini Node.js uygulamalarınıza entegre etmenizi sağlar.
LangChain, dil modelleriyle uygulamalar geliştirmek için tasarlanmış güçlü bir çerçevedir. Başlangıçta Python için geliştirilen bu dil, daha sonra Node.js dahil diğer dillere de uyarlandı. Node.js bağlamında LangChain'e genel bir bakış:
LangChain , büyük dil modellerini (LLM'ler) kullanarak uygulamaların oluşturulmasını kolaylaştıran bir kütüphanedir. LLM'leri uygulamalarınıza entegre etmek ve yönetmek, çağrıların bu modellere zincirlenmesini yönetmek ve karmaşık iş akışlarını kolaylıkla etkinleştirmek için araçlar sağlar.
OpenAI'nin GPT-3.5'i gibi Büyük Dil Modelleri (LLM'ler), insan benzeri metinleri anlamak ve oluşturmak için çok miktarda metin verisi üzerinde eğitilir. Yanıtlar oluşturabilir, dilleri çevirebilir ve diğer birçok doğal dil işleme görevini gerçekleştirebilirler.
Supabase, geliştiricilerin ölçeklenebilir uygulamaları hızlı bir şekilde oluşturmasına ve dağıtmasına yardımcı olmak için tasarlanmış açık kaynaklı bir hizmet olarak arka uç (BaaS) platformudur. Tamamı PostgreSQL üzerine inşa edilmiş, veritabanı yönetimini, kimlik doğrulamayı, depolamayı ve gerçek zamanlı yetenekleri basitleştiren bir araç ve hizmet paketi sunar.
Başlamadan önce aşağıdakilere sahip olduğunuzdan emin olun:
Öncelikle bir Supabase projesi kurup verilerimizi saklayacak gerekli tabloları oluşturmamız gerekiyor.
Supabase'e gidin ve bir hesaba kaydolun.
Yeni bir proje oluşturun ve Supabase URL'nizi ve API anahtarınızı not edin. Bunlara daha sonra ihtiyacınız olacak.
Supabase kontrol panelinizde yeni bir SQL sorgusu oluşturun ve gerekli tabloları ve işlevleri oluşturmak için aşağıdaki komut dosyalarını çalıştırın:
Öncelikle vektör mağazamız için mevcut değilse bir uzantı oluşturun:
create extension if not exists vector;
Daha sonra "belgeler" adlı bir tablo oluşturun. Bu tablo, web sayfasının içeriğini vektör formatında depolamak ve gömmek için kullanılacaktır:
create table if not exists documents ( id bigint primary key generated always as identity, content text, metadata jsonb, embedding vector(1536) );
Şimdi gömülü verilerimizi sorgulamak için bir fonksiyona ihtiyacımız var:
create or replace function match_documents ( query_embedding vector(1536), match_count int default null, filter jsonb default '{}' ) returns table ( id bigint, content text, metadata jsonb, similarity float ) language plpgsql as $$ begin return query select id, content, metadata, 1 - (documents.embedding <=> query_embedding) as similarity from documents where metadata @> filter order by documents.embedding <=> query_embedding limit match_count; end; $$;
Daha sonra, web sayfasının ayrıntılarını saklamak için tablomuzu ayarlamamız gerekiyor:
create table if not exists files ( id bigint primary key generated always as identity, url text not null, created_at timestamp with time zone default timezone('utc'::text, now()) not null );
$ npx create-next-app summarize-page $ cd ./summarize-page
Gerekli bağımlılıkları yükleyin:
npm install @langchain/community @langchain/core @langchain/openai @supabase/supabase-js langchain openai axios
Ardından arayüzümüzü oluşturmak için Material UI'yi kuracağız; başka bir kütüphaneyi kullanmaktan çekinmeyin:
npm install @mui/material @emotion/react @emotion/styled
Daha sonra OpenAI ve Supabase istemcilerini kurmamız gerekiyor. Projenizde bir libs
dizini oluşturun ve aşağıdaki dosyaları ekleyin.
src/libs/openAI.ts
Bu dosya OpenAI istemcisini yapılandıracaktır.
import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai"; const openAIApiKey = process.env.OPENAI_API_KEY; if (!openAIApiKey) throw new Error('OpenAI API Key not found.') export const llm = new ChatOpenAI({ openAIApiKey, modelName: "gpt-3.5-turbo", temperature: 0.9, }); export const embeddings = new OpenAIEmbeddings( { openAIApiKey, }, { maxRetries: 0 } );
llm
: Özetlerimizi oluşturacak dil modeli örneği.
embeddings
: Bu, belgelerimiz için benzer içeriği bulmamıza yardımcı olacak yerleştirmeler oluşturacaktır.src/libs/supabaseClient.ts
Bu dosya Supabase istemcisini yapılandıracaktır.
import { createClient } from "@supabase/supabase-js"; const supabaseUrl = process.env.SUPABASE_URL || ""; const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || ""; if (!supabaseUrl) throw new Error("Supabase URL not found."); if (!supabaseAnonKey) throw new Error("Supabase Anon key not found."); export const supabaseClient = createClient(supabaseUrl, supabaseAnonKey);
supabaseClient
: Supabase veritabanımızla etkileşime girecek Supabase istemci örneği. Bir services
dizini oluşturun ve içerik getirme ve dosyaları yönetme işlemlerini gerçekleştirmek için aşağıdaki dosyaları ekleyin.
src/services/content.ts
Bu hizmet web sayfası içeriğini getirecek ve HTML etiketlerini, komut dosyalarını ve stillerini kaldırarak temizleyecektir.
import axios from "axios"; export async function getContent(url: string): Promise<string> { let htmlContent: string = ""; const response = await axios.get(url as string); htmlContent = response.data; if (!htmlContent) return ""; // Remove unwanted elements and tags return htmlContent .replace(/style="[^"]*"/gi, "") .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "") .replace(/\s*on\w+="[^"]*"/gi, "") .replace( /<script(?![^>]*application\/ld\+json)[^>]*>[\s\S]*?<\/script>/gi, "" ) .replace(/<[^>]*>/g, "") .replace(/\s+/g, " "); }
Bu işlev, belirli bir URL'nin HTML içeriğini getirir ve stilleri, komut dosyalarını ve HTML etiketlerini kaldırarak onu temizler.
src/services/file.ts
Bu hizmet web sayfası içeriğini Supabase'e kaydedecek ve özetleri alacaktır.
import { embeddings, llm } from "@/libs/openAI"; import { supabaseClient } from "@/libs/supabaseClient"; import { SupabaseVectorStore } from "@langchain/community/vectorstores/supabase"; import { StringOutputParser } from "@langchain/core/output_parsers"; import { ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate, } from "@langchain/core/prompts"; import { RunnablePassthrough, RunnableSequence, } from "@langchain/core/runnables"; import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; import { formatDocumentsAsString } from "langchain/util/document"; export interface IFile { id?: number | undefined; url: string; created_at?: Date | undefined; } export async function saveFile(url: string, content: string): Promise<IFile> { const doc = await supabaseClient .from("files") .select() .eq("url", url) .single<IFile>(); if (!doc.error && doc.data?.id) return doc.data; const { data, error } = await supabaseClient .from("files") .insert({ url }) .select() .single<IFile>(); if (error) throw error; const splitter = new RecursiveCharacterTextSplitter({ separators: ["\n\n", "\n", " ", ""], }); const output = await splitter.createDocuments([content]); const docs = output.map((d) => ({ ...d, metadata: { ...d.metadata, file_id: data.id }, })); await SupabaseVectorStore.fromDocuments(docs, embeddings, { client: supabaseClient, tableName: "documents", queryName: "match_documents", }); return data; } export async function getSummarization(fileId: number): Promise<string> { const vectorStore = await SupabaseVectorStore.fromExistingIndex(embeddings, { client: supabaseClient, tableName: "documents", queryName: "match_documents", }); const retriever = vectorStore.asRetriever({ filter: (rpc) => rpc.filter("metadata->>file_id", "eq", fileId), k: 2, }); const SYSTEM_TEMPLATE = `Use the following pieces of context, explain what is it about and summarize it. If you can't explain it, just say that you don't know, don't try to make up some explanation. ---------------- {context}`; const messages = [ SystemMessagePromptTemplate.fromTemplate(SYSTEM_TEMPLATE), HumanMessagePromptTemplate.fromTemplate("{format_answer}"), ]; const prompt = ChatPromptTemplate.fromMessages(messages); const chain = RunnableSequence.from([ { context: retriever.pipe(formatDocumentsAsString), format_answer: new RunnablePassthrough(), }, prompt, llm, new StringOutputParser(), ]); const format_summarization = ` Give it title, subject, description, and the conclusion of the context in this format, replace the brackets with the actual content: [Write the title here] By: [Name of the author or owner or user or publisher or writer or reporter if possible, otherwise leave it "Not Specified"] [Write the subject, it could be a long text, at least minimum of 300 characters] ---------------- [Write the description in here, it could be a long text, at least minimum of 1000 characters] Conclusion: [Write the conclusion in here, it could be a long text, at least minimum of 500 characters] `; const summarization = await chain.invoke(format_summarization); return summarization; }
saveFile
: Dosyayı ve içeriğini Supabase'e kaydeder, içeriği yönetilebilir parçalara böler ve bunları vektör deposunda saklar.
getSummarization
: Vektör deposundan ilgili belgeleri alır ve OpenAI kullanarak bir özet oluşturur.Şimdi içeriği işleyip özet oluşturacak bir API işleyicisi oluşturalım.
pages/api/content.ts
import { getContent } from "@/services/content"; import { getSummarization, saveFile } from "@/services/file"; import { NextApiRequest, NextApiResponse } from "next"; export default async function handler( req: NextApiRequest, res: NextApiResponse ) { if (req.method !== "POST") return res.status(404).json({ message: "Not found" }); const { body } = req; try { const content = await getContent(body.url); const file = await saveFile(body.url, content); const result = await getSummarization(file.id as number); res.status(200).json({ result }); } catch (err) { res.status( 500).json({ error: err }); } }
Bu API işleyicisi bir URL alır, içeriği getirir, Supabase'e kaydeder ve bir özet oluşturur. Hizmetlerimizdeki hem saveFile
hem de getSummarization
işlevlerini yönetir.
Son olarak, kullanıcıların URL'leri girmesine ve özetleri görüntülemesine olanak sağlamak için src/pages/index.tsx
dosyasında ön uç oluşturalım.
src/pages/index.tsx
import axios from "axios"; import { useState } from "react"; import { Alert, Box, Button, Container, LinearProgress, Stack, TextField, Typography, } from "@mui/material"; export default function Home() { const [loading, setLoading] = useState(false); const [url, setUrl] = useState(""); const [result, setResult] = useState(""); const [error, setError] = useState<any>(null); const onSubmit = async () => { try { setError(null); setLoading(true); const res = await axios.post("/api/content", { url }); setResult(res.data.result); } catch (err) { console.error("Failed to fetch content", err); setError(err as any); } finally { setLoading(false); } }; return ( <Box sx={{ height: "100vh", overflowY: "auto" }}> <Container sx={{ backgroundColor: (theme) => theme.palette.background.default, position: "sticky", top: 0, zIndex: 2, py: 2, }} > <Typography sx={{ mb: 2, fontSize: "24px" }}> Summarize the content of any page </Typography> <TextField fullWidth label="Input page's URL" value={url} onChange={(e) => { if (result) setResult(""); setUrl(e.target.value); }} sx={{ mb: 2 }} /> <Button disabled={loading} variant="contained" onClick={onSubmit} > Summarize </Button> </Container> <Container maxWidth="lg" sx={{ py: 2 }}> {loading ? ( <LinearProgress /> ) : ( <Stack sx={{ gap: 2 }}> {result && ( <Alert> <Typography sx={{ whiteSpace: "pre-line", wordBreak: "break-word", }} > {result} </Typography> </Alert> )} {error && <Alert severity="error">{error.message || error}</Alert>} </Stack> )} </Container> </Box> ); }
Bu React bileşeni, kullanıcıların bir URL girmesine, göndermesine ve oluşturulan özeti görüntülemesine olanak tanır. Daha iyi bir kullanıcı deneyimi sağlamak için yükleme durumlarını ve hata mesajlarını yönetir.
Ortam değişkenlerinizi depolamak için projenizin kökünde bir .env dosyası oluşturun:
SUPABASE_URL=your-supabase-url SUPABASE_ANON_KEY=your-supabase-anon-key OPENAI_API_KEY=your-openai-api-key
Son olarak Next.js uygulamanızı başlatın:
npm run dev
Artık web sayfasının URL'sini girebileceğiniz ve sayfanın özet yanıtlarını alabileceğiniz, çalışan bir uygulamanız olmalıdır.
Tebrikler! Next.js, OpenAI, LangChain ve Supabase'i kullanarak tamamen işlevsel bir web sayfası özetleme uygulaması oluşturdunuz. Kullanıcılar bir URL girebilir, içeriği alabilir, Supabase'de saklayabilir ve OpenAI'nin yeteneklerini kullanarak bir özet oluşturabilir. Bu kurulum, ihtiyaçlarınıza göre daha fazla geliştirme ve özelleştirme için sağlam bir temel sağlar.
Daha fazla özellik ekleyerek, kullanıcı arayüzünü iyileştirerek veya ek API'leri entegre ederek bu projeyi genişletmekten çekinmeyin.
https://github.com/firstpersoncode/summarize-page
Mutlu kodlama!