들어가며

RAG(Retrieval-Augmented Generation) 기술은 대규모 언어 모델(LLM)과 검색 시스템을 결합하여 더 정확하고 관련성 높은 응답을 생성하는 기술입니다. 이 기술은 질의에 대한 답변을 생성하기 전에 관련 문서를 검색하여 정보를 추출하고, 이를 기반으로 응답을 만듭니다.

RAG는 단순히 사전 훈련된 모델의 지식에 의존하는 대신, 최신 정보와 컨텍스트를 반영할 수 있습니다. 특히 방대한 양의 비정형 데이터를 처리하고 분석하는 데 유용합니다. 따라서 RAG는 대화형 AI, 고객 지원, 정보 검색 등 다양한 응용 분야에서 필요성과 활용도가 높습니다.

오라클 데이터베이스는 RAG구현을 위하여 DB내에서 LLM과 통신하고 백터 검색을 위한 기능을 제공하고 있습니다. 오라클 데이터베이스에서 SQL을 이용하여 RAG구현하는 절차와 활용방법에 대해서 알아보겠습니다.

SQL RAG

오라클 데이터베이스는 DBMS_VECTOR_CHAIN 패키지을 제공하여 DB내에서 LLM통신과 벡터 검색 기능을 지원하고 있습니다. 기업에서 RAG를 실 서비스에 활용될때는 많은 구성요소와 고려사항이 필요하지만, 데이터관련 작업할때 SQL로 RAG 기능을 매우 쉽게 활용할수 있습니다.

본 블로그에서는 임베딩 모델으로 sentence-transformers/multi-qa-MiniLM-L6-cos-v1, LLM모델로 mistralai/Mistral-7B-Instruct-v0.1모델을 사용하겠습니다. 각 모델의 선택기준은 Multilingual을 지원하는 여부로만 판단했습니다. 성능(답변품질, 속도)에 대해서 고려되지 않았습니다.

1. 벡터 검색을 위한 임베딩 모델 로드

오라클 데이터베이스에 택스트 임베딩 모델을 로딩할수 있습니다. 텍스트 임베딩 및 유사도 검색은 아래 블로그를 참조하시기 바랍니다.

본 블로그에서는 임베딩 기능을 활용하여 PDF문서가 업로드 되면 DB트리거를 통해 자동으로 벡터 임베딩이 되고, PDF문서를 업로드를 간편하게 하도록 프로시저를 생성하였습니다.

테이블 생성

문서목록을 관리하는 테이블(MY_DOCUMENTS)과 문서을 작은 텍스트로 나누고 벡터 임베딩을 생성하여 테이블(MY_VECTOR_STORE)을 저장합니다. 두개의 테이블은 DOC_ID로 연결됩니다.

Code : sql
CREATE TABLE IF NOT EXISTS MY_DOCUMENTS
( 
    DOC_ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY  PRIMARY KEY, 
    file_name      VARCHAR2 (900), 
    file_size      NUMBER , 
    file_type      VARCHAR2 (100) , 
    file_content   BLOB
) ;

CREATE TABLE IF NOT EXISTS MY_VECTOR_STORE( 
    DOC_ID NUMBER NOT NULL, 
    EMBED_ID NUMBER, 
    EMBED_DATA VARCHAR2(4000), 
    EMBED_VECTOR VECTOR
);

임베딩 모델 로딩

오라클 데이터베이스에 onnx로 만들어진 모델을 로딩할수 있습니다. onnx형식 자체는 모델을 교환하는 표준형식이지만 OML4Py 유틸리티의 omltuils을 통하여 변환된 onnx파일만 로딩이 가능합니다.

Code : sql
-- 디렉토리 생성(해당 디렉토리에 onnx파일이 있어야함)
CREATE OR REPLACE DIRECTORY CTX_WORK_DIR AS '/home/oracle/onnx';
-- 이미 모델이 있는경우 삭제 
begin DBMS_VECTOR.drop_onnx_model(model_name => 'doc_model_han', force => true); end;
-- onnx파일을 가져와서 로딩 작업수행
begin
  DBMS_VECTOR.LOAD_ONNX_MODEL('CTX_WORK_DIR','multi-qa-MiniLM-L6-cos-v1.onnx','doc_model_han',
  JSON('{"function" : "embedding", "embeddingOutput" : "embedding", "input":{"input": ["DATA"]}}'));
end;
/

자동 임베딩 트리거 생성

문서목록을 관리하는 테이블(MY_DOCUMENTS)에 PDF문서가 들어가면 자동으로 트리거에 의해서 임베딩(MY_VECTOR_STORE)이 수행됩니다.
이때 임베딩 모델을 DB에 저장되어 있는 모델(doc_model_han)을 사용합니다.

Code : sql
CREATE OR REPLACE TRIGGER trg_my_doc_vectorized
FOR INSERT ON MY_DOCUMENTS
COMPOUND TRIGGER 
    AFTER EACH ROW IS
    BEGIN 
        INSERT INTO MY_VECTOR_STORE (doc_id, embed_id, embed_data, embed_vector)
        SELECT :NEW.doc_id as doc_id, 
              et.embed_id, 
              et.embed_data, 
              to_vector(et.embed_vector) AS embed_vector
        FROM TABLE(
            dbms_vector_chain.utl_to_embeddings(
                dbms_vector_chain.utl_to_chunks(
                    dbms_vector_chain.utl_to_text(:NEW.file_content), 
                    json('{"by":"words","max":"300","split":"sentence","normalize":"all"}')
                ),
                json('{"provider":"database", "model":"doc_model_han"}')
            )
        )  t
        CROSS JOIN JSON_TABLE(
            t.column_value, 
            '$[*]' COLUMNS (
                embed_id NUMBER PATH '$.embed_id',
                embed_data VARCHAR2(4000) PATH '$.embed_data',
                embed_vector CLOB PATH '$.embed_vector'
            )
        ) AS et ;
    END AFTER EACH ROW;
END trg_my_doc_vectorized; 
/

PDF로딩 프로시저 생성

PDF문서를 업로드하는 프로시저를 생성합니다. 이 프로시저는 외부 애플리케이션에서 호출됩니다. PDF문서가 MY_DOCUMENTS 테이블에 입력이 되면 위에서 만든 트리거(trg_my_doc_vectorized)에 의해서 자동으로 임베딩이 수행됩니다.

Code : sql
CREATE OR REPLACE PROCEDURE insert_my_doc(
    p_file_name IN MY_DOCUMENTS.file_name%TYPE,
    p_file_size IN MY_DOCUMENTS.file_size%TYPE,
    p_file_type IN MY_DOCUMENTS.file_type%TYPE,
    p_file_content IN MY_DOCUMENTS.file_content%TYPE
)
IS 
BEGIN 
    INSERT INTO MY_DOCUMENTS (file_name, file_size, file_type, file_content)
        VALUES (p_file_name, p_file_size, p_file_type, p_file_content);
    COMMIT;
END;
/

DB서버에서 PDF로드 테스트를 수행합니다. 테스트를 위하여 임의 파일을 저장했지만, 외부 애플리케이션에서는 웹 UI을 통해서 업로드된 PDF파일을 저장하는 기능을 이 프로시저를 호출하여 구현할 예정입니다.

Code : sql
CREATE OR REPLACE DIRECTORY MY_DOC_DIR AS '/home/oracle/my_doc';

declare
  file_name varchar2(1000) := 'oracle-ai-vector-search-users-guide.pdf';
begin
  insert_my_doc(file_name,100,'pdf',to_blob(bfilename('MY_DOC_DIR',file_name)));
end;
/

select * from my_documents;
select count(*) from my_vector_store;
select * from my_vector_store where rownum = 1;

SQL 수행결과입니다. 1건문의 문서가 들어가고 총 337개의 텍스트로 청킹되었습니다.

SQL> select * from my_documents;
    DOC_ID FILE_NAME                       FILE_SIZE FILE_TYPE  FILE_CONTE
---------- ------------------------------ ---------- ---------- ----------
         4 oracle-ai-vector-search-users-        100 pdf        255044462D
           guide.pdf                                            312E350A25
                                                                E2E3CFD30D
                                                                (중략)
SQL> select count(*) from my_vector_store;

  COUNT(*)
----------
       337

SQL> select * from my_vector_store where rownum = 1;
    DOC_ID   EMBED_ID EMBED_DATA                               EMBED_VECTOR
---------- ---------- ---------------------------------------- ----------------------------------------
         4          9 Text Processing: PL/SQL Examples3-57     [-3.60391126E-003,1.94851719E-002,-5.545
                                                               0581E-002,-3.90438065E-002,-5.27777225E-
                      D:20240514080434-08'00'                  002,-1.14860926E-002,-7.38332095E-003,2.
                      (중략)                                   (중략)

2. 외부 LLM호출을 위한 인증정보 생성

오라클 데이터베이스는 다양한 외부 LLM제공자를 지원합니다. REST API를 통해서 외부 모델을 호출할수 있습니다.

  • 지원 환경 : Cohere, Google AI, Hugging Face, Oracle Cloud Infrastructure (OCI) Generative AI, OpenAI, Vertex AI

외부 LLM모델과 통신하기 위해서는 인증정보가 필요하며 오라클 데이터베이스에서 CREDENTIAL을 생성하여 인증정보를 관리합니다.

DB유저 권한부여

오라클 데이터베이스에 보안상 일반적으로 외부 네트워크과 통신을 할수 없습니다. LLM모델을 호출하기 위해서 외부 네트워크 통신이 되어야 하므로 권한설정이 필요합니다. VECTOR라는 DB유저로 작업하고 있어 해당 유저에게 권한을 부여합니다.

Code : sql
BEGIN
  DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
    host => '*',
    ace => xs$ace_type(privilege_list => xs$name_list('connect'),
        principal_name => 'VECTOR',
        principal_type => xs_acl.ptype_db));
END;
/

GRANT DB_DEVELOPER_ROLE, CREATE CREDENTIAL TO VECTOR;

CREDENTIAL 생성

Hugging Face의 LLM을 사용할 예정이므로 Hugging Face에 가입하여 API Token생성작업을 합니다. 발급된 API Token을 이용하여 Credential을 생성합니다.

Hugging Face 사이트

  • https://huggingface.co/
  • 사이트에 가입하면 “사용자정보” –> Setting -> Access Tokens에서 신규로 생성할수 있습니다. (무료이므로 API 호출횟수가 제약이 있습니다.)

Credential 생성 포멧 예제

HF_CRED라는 Credential을 생성합니다.

Code : sql
declare
  v_params json_object_t;
  v_name varchar2(1000) :=  'HF_CRED';
  v_api_token varchar2(1000) := '<API_TOKEN>'; -- Hugging Face의 API Token으로 변경합니다.
begin
   v_params := json_object_t();
   v_params.put('access_token',v_api_token); 
   begin
        DBMS_VECTOR_CHAIN.DROP_CREDENTIAL ( CREDENTIAL_NAME  => v_name);
   exception 
     when others then
        null;
   end;
   
   DBMS_VECTOR_CHAIN.CREATE_CREDENTIAL ( CREDENTIAL_NAME => v_name, PARAMS => json(v_params.to_string));
end;
/

생성된 Credential은 아래 view를 통해서 확인할수 있습니다.

Code : sql
select CREDENTIAL_NAME, USERNAME, ENABLED from USER_CREDENTIALS;

실행결과입니다.

SQL> select CREDENTIAL_NAME, USERNAME, ENABLED from USER_CREDENTIALS;

CREDENTIAL_NAME      USERNAME   ENABL
-------------------- ---------- -----
HF_CRED              NA         TRUE

3. LLM연동 테스트

생성된 Credential을 이용하여 LLM과 통신할수 있습니다. Hugging Face의 LLM모델을 사용하기 위해서 LLM모델을 지정합니다. Credential명과 프롬프트을 이용하여 LLM에 질의합니다.

사용하는 PL/SQ 함수는 DBMS_VECTOR_CHAIN.UTL_TO_GENERATE_TEXT 입니다. 질의 요청을 간편하게 하도록 함수(generate_text)를 신규로 생성합니다.

Code : sql
CREATE OR REPLACE FUNCTION generate_text(p_model JSON, p_prompt CLOB) RETURN CLOB 
IS 
   output CLOB;
BEGIN 
   utl_http.set_body_charset('UTF-8'); -- http통신할때 Unicode방식으로 인코딩합니다.
   output := dbms_vector_chain.utl_to_generate_text(p_prompt, p_model);
   return output;
END;
/

생성된 함수를 이용하여 LLM모델에 답변을 요청해보겠습니다. Hugging Face의 Mistral-7B-Instruct-v0.1모델을 사용하였습니다.

Code : sql
set serveroutput on 
declare
    p_model json;
    p_prompt clob;
begin
    p_model := json('
    {
      "provider":"huggingface",
      "credential_name": "HF_CRED",
      "url": "https://api-inference.huggingface.co/models/",
      "model": "mistralai/Mistral-7B-Instruct-v0.1",
      "wait_for_model": "true",  
      "parameters": {
         "max_new_tokens": 1000,
         "return_full_text": false
       }
    }');

   p_prompt := '[INST]안녕하세요?[/INST]';
   
   dbms_output.put_line('답변 : '||generate_text(p_model, p_prompt));
end;
/

실행 결과입니다. 데이터베이스내에서 LLM과 통신하여 답변을 얻었습니다. 프롬프트를 보강하면 더 다양한 요청을 할수 있습니다.

답변 :  안녕하세요! 제가 도와드릴  있습니다. 무엇이 필요합니까?

4. SQL RAG 구현

RAG은 검색기능을 확장하여 좀더 정확하게 답변을 제공하는 기술입니다. 질문에 대해서 관련된 문서를 찾아 LLM에 컨텍스트정보로 제공하면 LLM에서는 이 정보를 기반으로 정확한 답변을 하게 됩니다.

다음은 SQL을 통해서 RAG로 구현한 예제입니다. 내부 처리 방식은 아래와 같습니다.

  • 질의 내용을 기반으로 텍스트 유사도 검색합니다.
  • 텍스트 유사도 검색을 하는 문서는 내가 선택한 문서로 지정하였습니다.
  • 여러개의 문서가 있을 경우 각 문서별 Top N개의 텍스트를 검색합니다 (이를 Multi-Vector Similarity Search기능이라고 합니다.)
  • LLM에 질의를 할때 검색한 결과와 질의문을 같이 전달하여 답변을 요청합니다.

질의는 JSON형식으로 요청하도록 작성하였습니다.

system은 LLM지시사항, question에는 사용자 질의, doc_id는 문서번호를 의미합니다.

{
  "system":"Please answer with only facts based on the searched content.", 
  "question":"What is the Oracle AI Vector Search?", 
  "doc_id":[4]
}

아래와 같이 질의에 대해서 답변하는 함수(generate_text_for_chat)로 생성합니다. 이 함수은 샘플 애플리케이션에서 호출하여 사용됩니다.

Code : sql
CREATE OR REPLACE FUNCTION generate_text_for_chat(p_user_question CLOB) 
RETURN CLOB IS
 
  v_model json;
  v_prompt clob;  
  v_sys_instruction varchar2(1000);
  v_user_question_vec vector; 
  v_user_question varchar2(1000);  
  v_message clob; 
  output CLOB;  
  v_doc_count number := 1;
  v_top_k number := 3;
  
BEGIN 
 
 -- JSON형식에서 질의내용만 추출 : {"system":"Please answer with only facts based on the searched content.", "question":"What is the Oracle AI Vector Search?", "doc_id":[4]}
  v_user_question := json_value (json(p_user_question),'$.question' returning varchar2(1000)); 
  v_sys_instruction := json_value (json(p_user_question),'$.system' returning varchar2(1000)); 
  v_model := json('
    {
      "provider":"huggingface",
      "credential_name": "HF_CRED",
      "url": "https://api-inference.huggingface.co/models/",
      "model": "mistralai/Mistral-7B-Instruct-v0.1",
      "wait_for_model": "true",  
      "parameters": {
         "max_new_tokens": 2000,
         "return_full_text": false
       }
    }
    ');

  -- 질의내용에 대한 쿼리 벡터 생성
  select to_vector(vector_embedding(doc_model_han USING v_user_question as data)) as embedding  into  v_user_question_vec ;
  --dbms_output.put_line('Question : '||v_user_question);

  -- 문서 개수 확인
  select count(doc_id) into v_doc_count
      from JSON_TABLE (json(p_user_question), '$[*]'  COLUMNS (
                NESTED PATH '$.doc_id[*]' COLUMNS ( doc_id number PATH '$' )
  ));
  
  -- 텍스트 유사도 검색 (Multi-Vector Similarity Search)
  select json_arrayagg(
         json_object('doc_id' is doc_id, 'embed_id' is EMBED_ID, 'content' is EMBED_DATA) 
         returning CLOB) into v_message
    from (SELECT DOC_ID, EMBED_ID, EMBED_DATA 
       FROM MY_VECTOR_STORE
        WHERE DOC_ID in (select doc_id from JSON_TABLE (json(p_user_question), '$[*]'  COLUMNS (
                NESTED PATH '$.doc_id[*]' COLUMNS ( doc_id number PATH '$' )
            )))
       ORDER BY vector_distance(EMBED_VECTOR, v_user_question_vec , COSINE) 
      FETCH FIRST v_doc_count PARTITIONS BY DOC_ID, v_top_k ROWS ONLY);

  -- 프롬프트생성
  v_prompt := ' 
<s>[INST] {system_instruction}.

Search Data:
{insert_search_data_here}

User Query:
{user_query}

Provide a detailed response based on the search data above. [/INST]
  ';
  
  v_prompt := replace(replace(replace(v_prompt,'system_instruction',v_sys_instruction),'{insert_search_data_here}', v_message),'{user_query}',v_user_question);

   --dbms_output.put_line(v_prompt);
 output := generate_text(v_model, v_prompt);
  RETURN output;
END;
/

LLM을 통해서 질의를 수행할수 있습니다. “What is the Oracle AI Vector Search?”질문을 4번 문서(DOC_ID = 4)에서 조회하여 답변을 합니다.

Code : sql
select generate_text_for_chat('{"system":"Please answer with only facts based on the searched content.", "question":"What is the Oracle AI Vector Search?", "doc_id":[4]}') response from dual;

답변결과입니다. 꽤 길게 답변을 했습니다. LLM지시사항(프롬프트)으로 잘 작성하면 답변을 명료하게 개선할수 있습니다.

SQL> select generate_text_for_chat('{"system":"Please answer with only facts based on the searched content.", "question":"What is the Oracle AI Vector Search?", "doc_id":[4]}') response from dual;

RESPONSE
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

The Oracle AI Vector Search is a new classification of specialized indexes designed for Artificial Intelligence (AI) workloads that allow you to query data based on semantics, rather than keywords. It
 is a powerful tool that enables fast retrieval and similarity search of vector embeddings for unstructured data.

Vector Indexes are a new classification of specialized indexes that are designed for AI workloads. They allow you to query data based on semantics, rather than keywords. This means that you can search
 for data based on the meaning of the words, rather than just the individual words themselves.

The biggest benefit of Oracle AI Vector Search is that semantic search on unstructured data can be combined with relational search on business data in one single system. This means that you can search
 for both structured and unstructured data in the same system, making it easier to find the information you need.

There are several examples of how Oracle AI Vector Search can be used. For example, you can search for business data in one single system, using vector embeddings to combine semantic search on unstruc
tured data with relational search on structured data. This can be done using the BY words MAX 40 OVERLAP 0 SPLIT BY none syntax, which splits the text into three chunks at an absolute maximum word of
40, the third line after wordloads, and the fifth line after with.

Overall, Oracle AI Vector Search is a powerful tool that enables fast retrieval and similarity search of vector embeddings for unstructured data. It is a new classification of specialized indexes that
 are designed for AI workloads, and it allows you to query data based on semantics, rather than keywords.

5. 샘플 애플리케이션

Streamlit 코드

PDF파일 기반으로 질의 답변하는 간단한 Pytnon 애플리케이션을 작성하였습니다. 웹 UI을 위하여 Streamlit기반으로 작성하였습니다. PDF파일을 업로드후에 PDF을 선택하여 질의를 하는 기능을 제공합니다. 이때 LLM은 PDF기반으로만 답변해야합니다.

Code : python File Name : sql_rag.py
import streamlit as st
import oracledb
import json
import time
from typing import List
from datetime import datetime

# 데이터베이스 연결 설정
def get_db_connection():
    dsn = oracledb.makedsn('localhost', '1521', service_name='FREEPDB1')
    connection = oracledb.connect(user='VECTOR', password='VECTOR', dsn=dsn)
    return connection

# PDF 문서 목록 조회
def fetch_documents(conn):
    cursor = conn.cursor()
    cursor.execute("SELECT DOC_ID, FILE_NAME FROM my_documents")
    documents = cursor.fetchall() 
    cursor.close()
    return documents

# PDF 업로드 함수
def upload_pdf(conn,file_name, file_size, file_type, file_content):
    cursor = conn.cursor()
    cursor.callproc('insert_my_doc', [file_name, file_size, file_type, file_content])
    conn.commit()    

# PDF 삭제 함수
def delete_pdf(conn, doc_id):
    cursor = conn.cursor()
    cursor.execute("DELETE FROM my_documents WHERE DOC_ID = :1", [doc_id])
    cursor.execute("DELETE FROM my_vector_store WHERE DOC_ID = :1", [doc_id])
    conn.commit()
    cursor.close()
    
# 질문 생성 함수
def generate_answer(conn,system, user_question, doc_ids):
    cursor = conn.cursor()
    input_json = json.dumps({"system":system, "question": user_question, "doc_id": doc_ids})
    print(f"input_josn : {input_json}");
    result = cursor.callfunc('generate_text_for_chat', oracledb.CLOB, [input_json])
    return result

# Streamlit 애플리케이션 인터페이스
def main():
    st.set_page_config(page_title="PDF 문서 질의 응답 시스템", page_icon=":books:", layout="wide", initial_sidebar_state="expanded")
    st.markdown("""
        <style>
            .main {
                background-color: #333333;
                color: #FFFFFF;
            }
            .user-bubble {
                background-color: #1E90FF;
                padding: 10px;
                border-radius: 10px;
                margin: 10px 0;
                color: #FFFFFF;
            }
            .bot-bubble {
                background-color: #696969;
                padding: 10px;
                border-radius: 10px;
                margin: 10px 0;
                color: #FFFFFF;
            }
            .timestamp {
                font-size: 0.8em;
                color: #B0B0B0;
                text-align: right;
                margin-top: -10px;
            }
        </style>
    """, unsafe_allow_html=True)
    st.title("PDF문서 질의 응답 시스템 예제(Oracle AI Vector Search 활용)")
    conn = get_db_connection()
    # PDF 업로드 섹션
    st.header("PDF 파일 업로드")
    uploaded_file = st.file_uploader("PDF 파일을 업로드하세요.", type="pdf")
    if uploaded_file is not None:
        file_name = uploaded_file.name
        file_size = uploaded_file.size
        file_type = uploaded_file.type
        file_content = uploaded_file.read()
        upload_pdf(conn, file_name, file_size, file_type, file_content)
        st.success(f"{file_name} 파일이 성공적으로 업로드되었습니다.")
        # 업로드 후에는 uploaded_file을 None으로 초기화
        uploaded_file = None

    # PDF 문서 목록 조회 및 선택
    st.header("PDF 문서 선택")
    documents = fetch_documents(conn)
    if documents:
        doc_ids = []
        for doc in documents:
            if st.checkbox(doc[1], key=doc[0]):
                doc_ids.append(doc[0])

        if st.button("선택한 문서 삭제"):
            if doc_ids:
                for doc_id in doc_ids:
                    delete_pdf(conn, doc_id)
                st.success("선택한 문서가 삭제되었습니다.")
                st.rerun()
            else:
                st.error("삭제할 문서를 선택하세요.")
                
        # 질문 입력 및 응답 생성
        st.header("질의 및 응답")
        if "chat_history" not in st.session_state:
            st.session_state["chat_history"] = []
             
        user_question = st.text_input("질문을 입력하세요.")
        system_instruction = "Please answer only based on searched content. If the searched data does not match the query content, respond with 'No Data'."
        if st.button("질문 제출"):
            if doc_ids and user_question:
                start_time = time.time()
                answer = generate_answer(conn,system_instruction, user_question, doc_ids)
                timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                st.session_state["chat_history"].insert(0, (user_question, answer.read(), timestamp))
                elapsed_time = time.time() - start_time
                st.caption(f"Total processing time: {round(elapsed_time, 1)} sec.")
                st.rerun()
            else:
                st.error("질문과 선택된 문서가 필요합니다.")
        if st.session_state["chat_history"]: 
            for question, answer, timestamp  in st.session_state["chat_history"]:
                st.markdown(f'<div class="user-bubble"><strong>질문:</strong> {question}</div>', unsafe_allow_html=True)
                st.markdown(f'<div class="timestamp">{timestamp}</div>', unsafe_allow_html=True)
                st.markdown(f'<div class="bot-bubble"><strong>응답:</strong> {answer}</div>', unsafe_allow_html=True)
    else:
        st.info("업로드된 PDF 문서가 없습니다.  PDF 파일을 업로드 해주세요.")

if __name__ == "__main__":
    main()

streamlit 명령어로 간단한 웹 애플리케이션을 실행할수 있습니다.

Code : python
streamlit run sql_rag.py

데모영상

애플리케이션 실행화면입니다. (약 1분 50초의 동영상입니다.)

데모영상의 순서는 아래와 같습니다.

  1. 두개의 파일을 업로드합니다
    • Oracle AI Vector Search User Guide 문서
    • Transaction Event Queue 문서
  2. 아래와 같이 문서를 선택후 질의를 합니다.
  3. 두개의 문서를 선택후 What is the Oracle Vector Search? 질의를 합니다. 질의 관련된 답변!!
  4. Transaction Event Queue문서만 선택후 What is the Oracle Vector Search? 질의를 합니다. 검색된 결과가 없다고 답변!!
  5. Oracle AI Vector Search User Guide문서만 선택후 What is the Transaction Event Queue? 질의를 합니다. 검색된 결과가 없다고 답변!!
  6. Transaction Event Queue문서만 선택후 What is the Transaction Event Queue? 질의를 합니다. 질의 관련된 답변!!
  7. 두개의 문서를 선택후 What is the Transaction Event Queue? 질의를 합니다. 질의 관련된 답변!!

마무리

지금까지 SQL을 이용하여 RAG구현하는 방법에 대해서 알아보았습니다. 데이터베이스에 임베딩 모델을 로드하여 저장하여 백터 검색을 SQL로 간편하게 수행할수 있습니다. 외부 LLM모델은 인증정보(Access Token)만 있으면 데이터베이스에서 직접 호출할수도 있습니다.

위 두가지 기능을 연결하면 LLM에서 질의와 관련된 텍스트 정보를 제공하여 RAG를 구현할수 있습니다. 웹 UI를 위하여 Python코드를 작성하였지만, 오라클 데이터베이스에서 기업내 혹은 클라우드상에 있는 LLM을 PL/SQL을 통해서 직접 연동 할수 있습니다.

기업의 데이터를 LLM모델로 옮길것이 아니라, 기업의 데이터가 있는 데이터베이스에서 직접 LLM과 연동한다면 기존 애플리케이션 변경을 줄이면서 좀더 다양한 업무에 간편하게 적용할수 있지 않을까 싶습니다.

참고자료

댓글남기기