본문 바로가기
Programming 개발은 구글로/기타 정보

[WEB+AI] 15일차 RAG 앱 구현

by 40대직장인 2024. 11. 1.

RAG

 

강사: 생활코딩 이고잉님

 

임베딩이란 의미를 숫자로 표현한 것이다.

 

🟩 RAG(Retrieval-Augmented Generation)

  : 질문 - 응답 시스템에서 사용하는 기법으로 정보 검색(Retrieval)과 생성(Generation)단계를 결합하여

    높은 정확도의 응답을 제공합니다.

 

RAG의 장점

  • 높은 정확도: 필요한 정보를 검색해서 제공하므로, 단순 생성 모델보다 정밀한 응답이 가능합니다.
  • 확장성: 대규모 지식 데이터베이스를 활용하여 정보의 최신성 및 정확성을 유지합니다.
  • 추론 능력 향상: 검색된 정보의 근거를 바탕으로 답변을 생성하므로, 복잡한 질의에서도 일관성 있고 신뢰성 높은 답변을 제공합니다.

RAG의 활용 분야

  • 고객 지원 챗봇: 실시간 정보와 최신 FAQ를 바탕으로 정확한 답변을 제공합니다.
  • 질의 응답 시스템: 논문, 문서, 기술 자료를 참고하여 깊이 있는 답변을 생성할 수 있습니다.
  • 검색 기반 텍스트 생성: 최신 뉴스나 사실을 포함해야 하는 경우에도 사용됩니다.

 

🟦 OpenAI

   - Calab에 OpenAI 키 설정(화면 왼쪽 열쇠 모양 선택)

 

  - OpenAI 시작

from google.colab import userdata
from openai import OpenAI

client = OpenAI(api_key=userdata.get('OPENAI_API_KEY'))

text = input("Enter text : ")
text = text.replace("\n", " ") # 줄바꿈 커서 제거

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": f"{text}"}
    ]
)

answer = response.choices[0].message.content
print("답변: ", answer)

 

 

  - 입력하는 값의 vector 구하기

import os
from openai import OpenAI
from google.colab import userdata

client = OpenAI(api_key=userdata.get('OPENAI_API_KEY'))

text = input("Enter text to get embedding: ")
text = text.replace("\n", " ")

response = client.embeddings.create(input=[text], model="text-embedding-3-small")

vector = response.data[0].embedding
print(f"size : {len(vector)}") # size: 1536
print(vector)

# 결과
[0.00348..., -0.00555..., ...]

 

 

  - Vector extension On

CREATE EXTENSION IF NOT EXISTS vector;

 

 

  - Table 생성

CREATE TABLE
  public.memo2 (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    CONTENT TEXT NOT NULL,
    embedding vector (1536)
  ) TABLESPACE pg_default;

 

 

  - [공통] import 문 + supabase(PostgreSQL 연결 설정)

import os
from openai import OpenAI
from google.colab import userdata
import psycopg2 as ps
import json

client = OpenAI(api_key=userdata.get('OPENAI_API_KEY'))

user = userdata.get('POSTGRES_USER')
password = userdata.get('POSTGRES_PASSWORD')
# Supabase PostgreSQL 연결 설정
conn = ps.connect(
    host="aws-0-ap-northeast-2.pooler.supabase.com",
    port="6543",
    database="postgres",
    user=user,
    password=password
)

 

 

  - supabase 연동(DB에 Text와 Vector 전송)

...
[공통] import 문 + supabase(PostgreSQL 연결 설정)
...

cur = conn.cursor()
# 사용자의 데이터를 입력 받는다
text = input("Enter text to get embedding: ")
text = text.replace("\n", " ")

# 임베딩 벡터를 구한다.
response = client.embeddings.create(input=[text], model="text-embedding-3-small")
vector = response.data[0].embedding

embedding_json = json.dumps(vector) # JSON 파일로 변환
print('embedding_json: ', embedding_json) # (개발자) 1500개의 용어로 이루어진 벡터를 받았다.

# 행을 추가한다.
insert_query = """
INSERT INTO memo2 (content, embedding)
VALUES (%s, %s)
"""
cur.execute(insert_query, (text, embedding_json))
conn.commit()

cur.close()
conn.close()

 

 

  - DB에서 입력한 값과 유사한 데이터를 가져오기(유사도 검사)

...
[공통] import 문 + supabase(PostgreSQL 연결 설정)
...

cur = conn.cursor()

# 1단계: 사용자로부터 텍스트 입력
text = input("Enter text to find similar records: ")

# 2단계: 입력한 텍스트를 임베딩으로 변환
response = client.embeddings.create(input=[text], model="text-embedding-3-small")
query_embedding = response.data[0].embedding  # 생성된 임베딩 벡터

print('query_embedding', query_embedding)

cur.execute("""
    SELECT id, content, (embedding <=> %s::vector) AS similarity
    FROM public.memo2
    ORDER BY embedding <=> %s::vector
""", (query_embedding, query_embedding))

# 결과 출력
matches = cur.fetchall()
if matches:
    print("Most similar record(s):")
    for match in matches:
        print(f"ID: {match[0]}, Content: {match[1]}, Similarity: {match[2]}")
else:
    print("No similar records found.")

cur.close()
conn.close()

# 결과
Enter text to find similar records: 주말이다.
query_embedding [0.00947636179625988, 0.011235509067773819, ...]
Most similar record(s):
ID: 14, Content: 내일은 즐거운 주말입니다., Similarity: 0.519824177026749
ID: 1, Content: 오늘 친구와 오랜만에 카페에서 시간을 보냈다. ... , Similarity: 0.775715914283284

 

 

  - 질문 임베딩과 유사한 상위 3개의 컨텍스트(문서) 조회

...
[공통] import 문 + supabase(PostgreSQL 연결 설정)
...

cur = conn.cursor()

# 1단계: 사용자로부터 질문 입력
question = input("Enter your question: ")

# 2단계: 질문의 임베딩 생성
response = client.embeddings.create(input=[question], model="text-embedding-3-small")
question_embedding = response.data[0].embedding  # 생성된 임베딩 벡터

# 3단계: 질문 임베딩과 유사한 컨텍스트(문서) 조회
cur.execute("""
    SELECT id, content, (embedding <=> %s::vector) AS similarity
    FROM public.memo2
    ORDER BY embedding <=> %s::vector
    LIMIT 3;  -- 상위 3개의 유사한 문서만 조회
""", (question_embedding, question_embedding))

# 가장 유사한 컨텍스트 출력
contexts = cur.fetchall()
if contexts:
    print("Retrieved contexts:")
    for context in contexts:
        print(f"ID: {context[0]}, Content: {context[1]}, Similarity: {context[2]}")

combined_context = "\n".join([context[1] for context in contexts])
print('combined_context : ', combined_context)

# LLM에 질문과 결합된 컨텍스트를 전달하여 답변 생성
response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "You are an assistant that provides answers based on context."},
        {"role": "user", "content": f"Context:\n{combined_context}\n\nQuestion: {question}"}
    ]
)

# 생성된 답변 출력
answer = response.choices[0].message.content
print("\nAnswer from LLM:")
print(answer)

# 연결 종료
cur.close()
conn.close()

 

 

  - gradio 연동

...
[공통] import 문 + supabase(PostgreSQL 연결 설정)
...

cur = conn.cursor()

# RAG 검색 및 응답 생성 함수
def retrieve_and_generate_answer(question):
    # 1단계: 질문의 임베딩 생성
    response = client.embeddings.create(input=[question], model="text-embedding-3-small")
    question_embedding = response.data[0].embedding  # 생성된 임베딩 벡터

    # 2단계: 유사도가 가장 높은 2개의 글을 데이터베이스에서 조회
    cur.execute("""
        SELECT id, content, (embedding <=> %s::vector) AS similarity
        FROM public.memo2
        ORDER BY embedding <=> %s::vector # 가까운 순서대로 정렬
        LIMIT 2;
    """, (question_embedding, question_embedding))

    contexts = cur.fetchall()
    if not contexts:
        return "No similar records found."

# 조회된 글을 출력용 문자열로 구성
    context_text = ""
    for context in contexts:
        context_text += f"ID: {context[0]}, Content: {context[1]}, Similarity: {context[2]:.3f}\n\n"

    # 조회된 글을 하나의 텍스트로 결합
    combined_context = "\n".join([context[1] for context in contexts])

    # 3단계: LLM에 질문과 결합된 컨텍스트를 전달하여 답변 생성
    llm_response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "You are an assistant that provides answers based on context."},
            {"role": "user", "content": f"Context:\n{combined_context}\n\nQuestion: {question}"}
        ]
    )  

    # LLM에서 생성된 답변
    answer = llm_response.choices[0].message.content

    # 결과 출력
    return f"Retrieved contexts:\n{context_text}\nAnswer from LLM:\n{answer}"

    # Gradio 인터페이스 설정
with gr.Blocks() as demo:
    gr.Markdown("## RAG 기반 질문 응답 시스템")

    # 질문 입력 필드
    question = gr.Textbox(label="Enter your question")

    # 실행 버튼과 결과 표시 영역
    answer = gr.Textbox(label="Response", interactive=False)

    # 버튼과 이벤트 연결
    question.submit(retrieve_and_generate_answer, question, answer)

# Gradio 인터페이스 실행
demo.launch()

 


출처: AI Hub 교육과정 - WEB+AI (위 내용이 문제가 된다면 댓글에 남겨주세요. 바로 삭제조치하도록 하겠습니다.)

 

 

댓글