BE

Backend #


2025-10-13 ⋯ Langchain #3 LangGraph 기반 Multi-Agent + Agentic RAG 시스템

1. 실습 개요 - 목적 - AI 헬스케어 스타트업의 투자 가치를 평가하기 위해 입력된 스타트업 정보에서 '경쟁사 유무를 자동 판별'하고, 판별 결과에 따라 워크플로우를 동적으로 분기하여 'Multi-Agent 시스템(10개 전문 에이전트)'이 각자의 역할(정보 수집, 기술력 분석, 시장성 평가, 경쟁사 비교)을 순차적으로 수행하며, 외부 문서(시장 보고서, 기술 리뷰, 규제 정보)를 'RAG 시스템(FAISS + OpenAI Embeddings)'으로 검색하여 LLM 분석에 참조 컨텍스트를 제공하고, 'Scorecard Method 가중치 평가 방식'으로 6개 항목(창업자/팀, 시장성, 제품/기술력, 경쟁 우위, 실적, 투자조건)을 정량화하여 10점 만점 투자 점수를 산출한 뒤, 전체 프로세스를 'LangGraph 기반 상태 관리 워크플로우'로 자동화하고, 최종적으로 분석 결과를 Executive Summary, 기술력/시장성 평가, 경쟁 분석, 투자 판단을 포함한 전문적인 'Word/PDF 형식의 투자 평가 보고서'로 생성 - 실습 설계 - 경쟁사 유무 판별: LLM 기반 스타트업 정보 분석 → YES/NO 판단 → 워크플로우 분기점 결정 - Multi-Agent 기반 분석: InvestmentAgents 클래스 내 10개 전문 에이전트(경쟁사 정보 수집, 기술력 분석, 시장성 평가, 경쟁사 비교 등) 독립 실행 - RAG 시스템: FAISS 벡터스토어 + OpenAIEmbeddings를 활용해 외부 문서(PDF, TXT) 검색 → 기술력/시장성 분석 시 컨텍스트 제공 - Scorecard Method: 창업자/팀(30%), 시장성(25%), 제품/기술력(15%), 경쟁 우위(10%), 실적(10%), 투자조건(10%) 가중치 평가 → 10점 만점 점수 산출 - LangGraph 워크플로우: StateGraph로 노드(10개 에이전트) 및 조건부 엣지 정의 → 경쟁사 유무에 따라 동적 경로 분기 → 순차 실행 자동화 - Word/PDF 투자 평가 보고서 생성: python-docx + ReportLab 활용 → 마크다운 파싱 → 투자 점수 강조 → Executive Summary, 기술력/시장성 평가, 경쟁 분석, 투자 판단 포함 전문 보고서 출력 2. 실습 코드 프로젝트 구조 config.py models.py rag_system.py agents.py graph.py report_generator.py main.py


2025-09-23 ⋯ Langchain #2 RAG 기반 LLM API 서버 구축

1. 실습1 - LLM 질문-응답 Agent 구현 작업 위치 설정 백엔드 띄우기 프론트 띄우기 여기서 App.vue를 practice_LLM_App_front.vue의 내용으로 수정하기. 2. 실습2 - RAG 기반 LLM API 서버 구축 위치 설정, swagger ui 띄우기 cf) 패키지 버전 확인 실습 수행 1. 서버 실행 및 초기화 - 포트 8003에서 FastAPI 서버 실행 - 시작 시점에 SQLite 인메모리 DB(customers, products)가 자동 생성됨 - 벡터스토어 상태를 확인하면? - 아직 어떤 벡터DB도 초기화되지 않은 상태. 2. PDF 업로드 및 벡터화 - 엔드포인트: POST /upload-paper - Swagger UI - 로그 - PDF(paper-attention.pdf)를 업로드하면 RecursiveCharacterTextSplitter로 쪼개고, OpenAI 임베딩을 적용하여 FAISS에 저장해서 VECTORSTORE_PDF 초기화. 3. 내부 DB 테이블 확인 - 엔드포인트: GET /db-tables - Swagger UI - 로그 - { "tables": ["customers", "products"] } - 인메모리 SQLite에 두 개의 샘플 테이블(customers, products)이 준비되어 있음을 확인 4. 내부 테이블 벡터화 - 엔드포인트: POST /upload-dbtable?table=customers - Swagger UI - 로그 - customers 테이블 데이터가 벡터화되어 VECTORSTORE_INTERNAL 생성 5. 외부 웹 검색 데이터 벡터화 - 엔드포인트: POST /upload-topic?topic=생성형 AI - Swagger UI - 로그 - Naver 뉴스 API를 통해 생성형 AI 관련 기사 20개를 크롤링 -> OpenAI 임베딩 적용 -> VECTORSTORE_EXTERNAL 생성 완료. 6. RAG 질의 - 엔드포인트: POST /rag-query - Swagger UI - 로그 - 설명: 외부 벡터스토어에서 문맥을 검색 -> LLM(ChatGPT API, gpt-4o)을 통해 요약 답변을 생성. - 출처 표기도 괄호 형태로 포함시켜 “환각 최소화 + 근거 제시” 방식으로 동작. 7. 내부 파이프라인 셀프 테스트 - 엔드포인트: GET /selftest-internal - Swagger UI - 설명 - 실제 데이터 존재: "Enterprise" 반환 - 존재하지 않는 속성: "지식베이스에서 답을 찾을 수 없습니다." - 스키마 무관 질문(Attention?): "지식베이스에서 답을 찾을 수 없습니다." - 환각 억제 규칙이 잘 작동함을 보여줌. - internal로 해보기 - pdf로 해보기


2025-09-17 ⋯ FastAPI #3 비동기 데이터베이스

main.py - main.py - fastapi app 서버를 구성. - fastapi 프레임워크 - 웹 요청이 들어오면 특정 함수로 연결해준다. - 연결 지점 = 엔드포인트. - ex) 누군가 브라우저에서 http://127.0.0.1:8001/hello를 호출하면, FastAPI는 이 요청을 보고 “아 이건 /hello 경로의 GET 요청이구나” 하고, 미리 등록해둔 hello() 함수를 실행한 뒤 그 반환값을 JSON으로 돌려준다. - fastapi 기본 구조 - 먼저 app = FastAPI()로 애플리케이션 객체를 만들고 - 그 뒤에 @app.get("/"), @app.get("/hello") 같은 데코레이터로 함수를 등록하기. - 보통 FastAPI는 /docs 주소로 들어가면 자동으로 Swagger UI라는 API 설명서가 나오지만 여기서는 app = FastAPI(docs_url=None)라고 작성해 기본 /docs 경로를 막음 - 나중에 직접 커스터마이징한 /docs 엔드포인트를 등록하려고. - 대신 / 경로에서는 단순히 "Welcome to the FastAPI server!"라는 메시지를 주고, /hello 경로에서는 "hello world!"라는 메시지를 줌. - 라우터? - 엔드포인트들을 별도 파일로 나누어 관리할 수 있는 기능. - 할 일(Task)을 관리하는 API, 완료(Done)를 관리하는 API처럼 종류별로 나누면 프로젝트가 훨씬 깔끔해진다. - app.include_router(task_a.router), app.include_router(done_a.router) - task_a.py 안에 정의된 라우터들을 불러와서 fastapi 앱에 등록한다. - /tasks 같은 엔드포인트들이 main.py에 직접 쓰여 있지 않아도 라우터 파일이 include되면서 실제 서버에서 동작한다. - 정적 파일(static files)은 fastapi에 연결해서 /static으로 접근. - favicon.ico - 웹 브라우저가 기본적으로 요청하는 아이콘 파일이기 때문에 @app.get("/favicon.ico") 엔드포인트를 만들어서 직접 반환한다. - swagger ui 커스터마이징 - swagger_favicon_url="/static/favicon.ico" - 지정한 아이콘을 Swagger UI 화면에 반영. - FastAPI와 SQLAlchemy를 이용해서 비동기 방식으로 데이터베이스와 연결하기 - 웹 애플리케이션이 데이터베이스와 소통하려면 - “어디에 있는 DB에, 어떤 계정으로 접속할 것인지”를 정하고 - 그 DB에 요청을 보냈다가 결과를 받는 과정을 반복한다. - 근데 단순히 한두 번 요청하는 게 아니라 수많은 요청을 동시에 처리해야 하므로 연결을 효율적으로 관리하는 체계가 필요함 - ASYNC_DB_URL - 데이터베이스 접속 주소. - async_engine - SQL 명령을 실행하는 데이터베이스 엔진 - echo=True - 실행되는 SQL 쿼리가 콘솔에 그대로 찍힌다 - future=True - SQLAlchemy의 최신 API 스타일을 쓰겠다. - 이 엔진을 통해 DB에 연결할 수 있다. - 세션(session) - DB에 연결해서 여러 쿼리를 실행하고 최종적으로 결과를 반영하거나 취소하는 과정 전체를 관리. - AsyncSessionLocal - 세션 팩토리 (세션을 필요할 때마다 새로 찍어내는 공장) - Base - SQLAlchemy에서 테이블 구조를 코드로 표현할 때 요 클래스를 쓴다고함. - declarative_base(cls=AsyncAttrs) - 비동기 처리를 지원하는 기능을 포함한 Base 클래스를 만들겠다 - Task, Done 같은 모델들은 모두 이 Base를 상속받아 정의된다이제. - get_db() - router에서 db: AsyncSession = Depends(get_db)라고 쓰면 - fastapi는 함수를 실행해서 세션을 꺼내고 작업이 끝나면 자동으로 커밋, 문제가 생기면 자동롤백, 끝나면 연결을 닫는다.


2025-09-17 ⋯ FastAPI #2 논문 업로드 및 벡터화 API

1. 실행 제대로 떴으니깐 pdf 처리 해보기 질의응답: attention is all you need가 무엇인가? 답변 잘 나온다. RAG가 뭐냐고 물어보면? 논문에 없는건 답변하지말라고 햇기때문에 답안해줌 2. 코드


2025-09-17 ⋯ FastAPI #1 MariaDB, DB Migration, Swagger UI

1. 실습 내용 maria db container 띄우기 - docker desktop에서도 확인 db connection - vscode에서 database client extension 열고 create connection cf) 컨테이너 기반 실습 환경을 구성하는 이유와 장점 - 컨테이너 기반 실습 환경을 구성하는 이유 - 목적1: 모두가 똑같은 환경에서 실습을 하기 위해 - 내부에서 설치된 라이브러리 버전이나 운영체제 차이 때문에 동일한 python-app.py를 실행해도 실행이안대거나 오류가날수있다. - Docker 컨테이너라는 상자 안에 Python 실행 환경을 일정하게 담아두고 Mac이든 Windows든 그 상자를 똑같이 실행시키면 누구든 동일한 환경에서 같은 결과를 낼수있으니까 환경 차이로 인한 오류가 없어진다 - 목적2: 작업 환경을 제한 - 단순히 Python 코드만 실행하는 것이 아니라 데이터베이스(DBMS)까지 연결해야 할 때가 많은데 오픈소스 데이터베이스인 MySQL이나 MariaDB 같은 프로그램을 직접 로컬에 설치할 수도 있지만 얘네는 운영체제에 따라 설치 과정이 복잡하고 하드웨어 자원에 의존적이라 충돌이나 오류가 발생하기 쉬운프로그램들이다. - Docker 컨테이너를 사용하면 데이터베이스를 별도의 격리된 공간에서 실행할 수 있다. FastAPI를 실행하는 컨테이너 하나, MySQL을 실행하는 컨테이너 하나를 띄워두고, 이 둘을 내부 네트워크로 연결해주는 식으로 작업하면 데이터베이스나 Python 실행 환경이 호스트 컴퓨터 전체를 더럽히지 않고, 필요 없을 때 컨테이너만 지우면 깨끗하게 정리된다. - uvicorn - uvicorn을 실행해 fastapi 앱을 실행한다. - api 서버가 실행되면 브라우저에서 127.0.0.1:8001로 접속하면 서버가 응답을 돌려줄수있다. - swagger ui - api를만들면 기능이 코드안에 가려져있어서 어떤 요청을 보내야 하고 어떤 응답이 돌아오는지 알기 어려운데 - swagger ui가 있으면 내가만든 fast api 서버가 swagger ui를 통해 “이런 엔드포인트들이 있습니다, 이런 식으로 요청을 보내면 되고, 이런 응답이 옵니다”를 자동으로 보여준다. - ui 화면에서 실제 api 요청도 보낼수있다. - routers/task_a.py → done_crud 임포트 및 done 여부 확인 부분 수정 - routers/done_a.py → DoneResponse 반환 시 done 필드 제거 - cruds/task_a.py → get_tasks_with_done, update_task에서 Done 여부 올바르게 체크 - schemas/task_a.py → done 필드 반드시 포함 - schemas/done_a.py → DoneResponse 정의 필요 (예: class DoneResponse(BaseModel): id: int) cf2 SQLAlchemy 로그 해석? - tasks와 dones 테이블을 조인해서 각 할 일이 완료되었는지 여부(done)를 계산 - FastAPI가 JSON으로 가공해 클라이언트에 반환. cf3 swagger ui에서 확인 실습정리 가상환경 만들기? - source ./demo-app/bin/activate - 나는 어케하는지몰라서 그냥 conda환경만들엇는데 갠찮겠지.. 필요 패키지 설치 - pip install fastapi “uvicorn[standard]” - pip install sqlalchemy aiomysql pymysql greenlet PathOperation 함수는 경로동작 함수 - 모듈 임포트하는 모든 경로에, `__init__.py` 만든다 2. 개념 - 목적 - Docker 컨테이너(MariaDB), SQLAlchemy, FastAPI, Uvicorn을 활용해 동일한 데이터베이스 환경에서 FastAPI 서버를 구축하고 Swagger UI를 통해 API 동작을 확인하는 것 - 구현 - 컨테이너 실행(Docker + MariaDB): Docker Compose를 이용해 MariaDB 컨테이너를 띄우고, 로컬 환경과 독립된 동일한 DB 환경을 구성함 - DB 연결(Database Client + SQLAlchemy): VS Code Database Client Extension과 SQLAlchemy를 통해 MariaDB에 연결해 테이블을 조회하고 쿼리를 실행함 - FastAPI 서버 실행(Uvicorn + FastAPI): FastAPI 앱을 uvicorn으로 구동하여 API 서버를 실행하고, 로컬 브라우저에서 엔드포인트에 접근 가능하게 함 - Swagger UI 확인(Swagger UI): 자동 문서화된 API 명세서를 통해 엔드포인트 구조와 요청/응답을 직관적으로 확인하고 직접 API 요청을 테스트함 - DB 마이그레이션(SQLAlchemy ORM): migrate_db_a 모듈을 실행해 tasks/dones 테이블을 자동 생성하고, 조인 쿼리를 통해 완료 여부를 조회하도록 구현함 - 라우터 및 스키마 수정(FastAPI routers/schemas): routers, cruds, schemas 모듈을 수정하여 done 여부를 올바르게 반영하고 DoneResponse를 정의하여 API 응답 형식을 보장함 - 의문점1 - (MariaDB를 로컬 운영체제에 직접 깔지 않고) Docker Compose를 이용해 MariaDB 컨테이너를 띄운 이유? - 답1 - mariadb같은 db를 로컬 환경에 깔려고 하면 운영체제마다 설치 방법도 다르고 버전 호환 문제도 많아서 똑같은 코드를 실행해도 어떤 컴퓨터에서는 잘 되고 다른 컴퓨터에서는 에러가 날수있다. - 이때 docker에 mariadb를 세팅된 상태로 담아두고 돌리면 2가지 이점이 있는데 - 맥이든 서버가 리눅스든 상관없이 항상 동일한 MariaDB 환경이 보장되고 - 삭제할때 컨테이너만 지우면 깨끗하게 정리돼서 추후 호환문제가 발생하는것도 방지할수있다. - 의문점2 - db를 왜 로컬환경에 설치하는가? db가 무엇인가? - 답2 - db는 많은 양의 데이터를 체계적으로 관리하고 동시에 여러 사용자가 빠르게 조회할 수 있도록 도와주는 시스템. - 엑셀 파일처럼 몇 줄짜리 데이터만 다룰 거라면 굳이 DB가 필요 없지만 - 웹 서비스나 API 서버를 만든다고 하면? - 예를 들어 할 일 관리 앱을 만든다고 하면 - 사용자가 추가한 작업들을 어딘가에 저장해 두었다가 나중에 다시 보여줘야하는데 - 만약 메모리에만 저장하면 서버가 꺼지는 순간 다 사라지고 파일로 저장하면 여러 사람이 동시에 접속해서 데이터를 읽고 쓰기 시작하면 꼬일수있다 그래서 신뢰성 있게 "데이터를 관리"할 수 있는 db가 필요하다! - 의문점3 - "데이터를 관리"한다란? - 답3 - 서비스를 구동하면 데이터가 들어오니까 데이터를 저장하고 조회하고 해야한다. - 의문점4 - VS Code Database Client Extension과 SQLAlchemy가 각각 하는일이 무엇인가? - 답4 - vscode db client extension을 쓰면 - vs code를 통해서 db에 들어있는 테이블이랑 적재된 데이터를 볼수있고 - db에 쿼리문을 입력해서 결과를 볼수있고 - 구조를 시각적으로 확인할수도있다. - 즉 db 상태를 빠르게확인하고 단순한수준의 조작을 할수있다. - sql alchemy를 쓰면 - 파이썬 객체와 데이터베이스 테이블을 연결할수있다. - 의문점5 - SQLAlchemy 의 기본 뼈대 Engine + Base + Session? - 답5 -


2025-09-15 ⋯ Ray #1 Batch Prediction with Ray Core

스터디때 준비해갔던 Ray Core를 사용해서 batch prediction 수행하는 예제!! - batch prediction이 batch를 예측하는건줄알았는데(..) batch로 prediction하는것이었다. - 순서는 1. Task 기반 batch prediction 2. Actor 기반 batch prediction 3. GPU 기반 수행 코드 - 출처는 Ray Document의 Batch Prediction with Ray Core이다. 0. 개요 - 목적 - Parquet 형식의 대규모 데이터셋을 Ray를 이용해 분산 처리하며, 더미 모델을 로딩하여 배치 예측(batch prediction) 을 수행한다. - Task와 Actor 두 가지 실행 방식을 비교하고, CPU/GPU 자원 활용 차이를 이해한다. - 설계 - 데이터셋 분할: S3에 저장된 Parquet 파일(12 shards)을 불러와 분산 태스크 단위로 처리 - 모델 로딩: 더미 모델(load_model)을 정의하고 ray.put()을 통해 오브젝트 스토어에 1회 저장 - 배치 예측(Task 기반): @ray.remote 태스크로 각 shard를 병렬 예측, 결과 크기 반환 - 배치 예측(Actor 기반): BatchPredictor 클래스를 Ray Actor로 등록하고, ActorPool을 이용해 shard 분산 예측 - 자원 활용(CPU/GPU): CPU 환경에서는 기본 Task 실행, GPU 환경에서는 @ray.remote(num_gpus=1)를 사용해 GPU에서 모델을 실행하도록 구성 - 결과 확인: 각 shard에 대해 예측된 결과 크기를 출력하여 병렬 처리 동작을 검증 1. 코드 - 실습에서는 분산 처리 흐름을 보는 것이 핵심이기 때문에 실제 모델이 갖는 특성을 갖는 더미 모델을 생성해준다. - 실제 모델이 갖는 특성 = 정확히는 실제 모델이 갖는 특성 중 분산 처리에 관여하는 특성. - 실제 모델이 갖는 특성 2가지? 1. 큰 메모리 용량. 실제 머신러닝 모델, 특히 딥러닝 모델은 수백 MB에서 수 GB에 달하는 가중치 파라미터를 담고 있다 예를 들어 BERT나 GPT 같은 모델은 엄청난 수의 파라미터를 갖기 때문에, 한 노드에서 다른 노드로 옮길 때 그 자체로 데이터 전송 비용이 크므로 이를 구현해준다. 2. 입력 데이터를 받아서 변환된 출력을 만듭니다. 실제 모델은 어떤 입력(이미지, 텍스트, 테이블 데이터 등)을 받아서 예측값을 내놓으므로, 이를 구현해줍니다. - 구현 방법? 1. model.payload = np.zeros(100_000_000) - 큰 메모리의 가중치 파라미터를 담고 있음을 모방하는 코드. 모델이 내부적으로 “큰 덩어리” 데이터를 가진 객체처럼 보이며 이를 통해 Ray가 이 모델을 여러 노드에 배포할 때 진짜처럼 부담을 준다. 2. {"score": batch["passenger_count"] % 2 == 0} - 입력값을 받아서 예측값을 내놓음을 모방하는 코드. 모델은 dataframe을 input으로 받아 승객 수가 짝수냐 홀수냐를 판별한다 즉 “입력 데이터를 보고 뭔가 계산해서 새로운 결과를 만든다”라는 모델의 핵심 행위만 구현한다. 1. Task 기반 batch prediction - Ray에서 Task 기반 분산처리란? - 데이터 파일을 통째로 처리하지 않고 여러 조각(Task)으로 잘라 각 조각을 서로 다른 Worker에게 맡기기. - 코드 설명 - input_files - 2009년 뉴욕시 택시 데이터. parquet 포맷이며 12개 데이터로 구성 - function make_prediction(model, shard_path) - shard 파일 경로를 받아서 pyarrow.parquet.read_table(shard_path)로 데이터를 불러고 df로 변환해서 더미 모델 model에 입력 - 앞서 더미 모델인 model은 passenger_count 값이 짝수인지 여부를 판단해서 불리언 값으로 반환하는 모델이었다! - ray.put(model) - 모델이 큰 메모리 객체를 내부적으로 가지고 있고(payload=1억) 따라서 매번 모델을 직접 태스크로 전달하면 드라이버의 오브젝트 스토어가 과부하될 수 있다. - 그래서 ray.put(model)을 사용해서 모델을 오브젝트 스토어에 단 한 번만 저장하고 이후 태스크에는 그 참조값 model_ref 만 넘긴다. - 이렇게 해야 각 태스크가 동일한 모델을 공유하되 불필요한 데이터 복제가 발생하지 않는다. cf1 - 의문점1 - ray.put(model)을 해야 각 태스크가 동일한 모델을 공유하되 불필요한 데이터 복제가 발생하지 않는다고 했는데 - 모델을 ray.put()으로 한 번만 넣었을 때와, 매번 remote 호출마다 모델을 넘겼을 때 오브젝트 스토어 메모리 사용량 차이는 얼마일까? - 확인1 - Ray에서 메모리 현황을 ray memory 명령어를 통해 확인할 수 있음 - 위의 두 Case 에서 ray memory를 호출하여 메모리 사용량과 참조 개수를 확인해보면 Ray 오브젝트 스토어에 몇 개의 모델 사본이 올라갔는지, 그리고 참조 개수가 어떻게 달라졌는지를 확인해서 메모리 사용량 차이 확인이 가능! - 결과 - Mem Used by Objects 비교 - Good Case - 1178.0 B - Bad Case - 1235.0 B - 비슷한이유는뭘까? 더미 데이터에서 파라미터 부하를 모방한다고 작성한 np.zeros(100_000_000)은 실제로는 800MB짜리 배열이어야 하지만 Ray와 NumPy 내부에서 메모리 최적화 (zero-copy, lazy allocation) 때문에 실제 크기가 반영되지 않았고 ray memory 출력에서도 몇 백 byte 수준으로 나왔다. - Mem Used by Objects 비교 - 실제 숫자를 넣어줫다면? - model.payload = np.random.rand(100_000_000)처럼 랜덤 값을 채우면 실제 메모리가 할당되었을것이고(float64 → 약 800MB) - 이경우 Good Case (ray.put(model) 한 번)는 Mem Used by Objects ≈ 800MB, Bad Case (태스크 3개에 직접 모델 전달) Mem Used by Objects ≈ 2400MB (800MB × 3) 가 출력되었을것이다. 즉, 모델 크기 × 태스크 수 만큼 차이가 벌어지는 게 일반적인 결과! - Local References 비교 - Good Case - 16 - Bad Case - 19 - 결과설명? ray.put(model)을 호출 후 생성된 ObjectRef는 오브젝트 스토어에 저장된 모델을 가리키는 “포인터” 같은 역할을 한다. - Good Case에서는 드라이버 프로세스(파이썬에서 코드를 실행하는 쪽)와 태스크 실행 시 필요한 내부 참조들이 모두 합쳐져서 16개 참조가 생겼다 즉 모델 사본은 1개지만 그 사본을 가리키는 참조가 16개 있다. - Bad Case와 같이 모델을 직접 태스크 인자로 넘기면 태스크가 실행될 때마다 Ray 내부적으로 새로운 ray.put(model) 이 실행되고 따라서 태스크 3개를 실행하면 모델 사본이 3개 만들어지고, 각각의 사본에 대해 참조가 따로 생기고 Good Case에서 16이었던 값이 3 증가해서 19가된다 즉 여기서 +3은 곧 태스크 개수만큼 늘어난 중복 ref 숫자. - 메모리 사본이 중복 생성되면(중복 참조되면) 왜 안되는가? - 모델이 태스크 개수만큼 복제돼서 올라가서, 만약 모델이 800MB라면 태스크가 3개면 2.4GB, 10개면 8GB까지 차지하게 되니까 메모리 낭비가 발생하고 큰 모델을 쓰면 금방 object store OOM(Out Of Memory) 에러가 난다 - 작은 더미 모델일 땐 차이가 안 드러나지만, 실제 대형 모델(PyTorch, TensorFlow 등)을 쓰면 시스템이 바로 느려지고 OOM으로 죽을 수 있다. - make_prediction - 각 parquet 파일을 읽어 데이터프레임으로 만든 뒤, 더미 모델을 적용했다. 더미 모델은 `passenger_count`가 짝수인지 여부를 판별해서 불리언(`True`/`False`) 값을 반환하구 - 12개 파일에 대해 잘 수행되었다!! 2. Actor 기반 batch prediction - Ray의 Actor 기반 분산처리? - 모델을 Actor 안에 올려 상태를 유지하고, 여러 Actor를 풀로 관리해 병렬성을 확보. - @ray.remote class BatchPredictor - 함수 대신 클래스가 원격 실행 단위로 선언되어 있음. - 참고로 Task에서는 다음과 같이 선언돼있었는데 - 보면 self.model 같은 멤버 변수가 없고, 그냥 model이라는 인자를 받는다. - result = model(df)처럼 함수의 인자로 모델을 받아 쓰고 함수가 끝나면 모델은 사라지고, 다음 작업에서는 또 다시 같은 model_ref를 넘긴다. - 원래 코드로 돌아와서 보면,, - `__init__` 안에서 self.model = model을 저장하면 모델은 Actor의 상태로 남는다. 따라서 한 번 생성된 Actor는 이후 여러 shard 데이터를 받아도 같은 모델을 반복해서 활용한다. - 이게 Actor의 가장 중요한 특징인데 단순 태스크에서는 매번 model_ref를 전달하고 실행이 끝나면 상태가 사라지지만, Actor에서는 이 모델이 메모리에 계속 붙어있다. - actors = [BatchPredictor.remote(model_ref) for _ in range(4)] - 네 개의 Actor 인스턴스를 생성. 각각은 독립된 워커 프로세스로 Ray 클러스터 안에 배치된다 즉, 네 개의 예측기가 동시에 shard 파일을 읽고 결과를 계산할 수 있다. - ActorPool - Actor를 관리하는 유틸리티. 여러 Actor를 모아두고, 사용할 수 있는 Actor가 생기면 작업을 하나씩 할당한다. - for file in input_files: pool.submit(lambda a, v: a.predict.remote(v), file) - a는 Actor 하나, v는 shard 파일 경로. - 제출된 작업은 내부적으로 큐에 쌓이고 Actor가 놀고 있으면 즉시 할당되기 때문에, 사용자가 Actor 스케줄링을 직접 신경 쓰지 않고도 여러 데이터를 효율적으로 분배할 수 있다. - 결과 수집 루프 (while pool.has_next()) - 결과 수집 루프 돌렸고 12개 파일에 대해 정상적으로 수행!! cf2 - 의문점2 - Actor 기반 방법은 모델을 Actor 안에 올려 상태를 유지하고, 여러 Actor를 풀로 관리해 병렬성을 확보한다구했다. - 궁극적으로 Task 기반과의 성능 차이? - 확인2 - Task 기반과 Actor 기반 실행에서 시작 시간과 종료 시간을 time으로 측정하면 실행 시간을 확인해볼수 있다. - 결과 - 실행 시간 비교 - Task 기반: 대부분 2.5초대, 몇몇 shard는 2.8~3.3초 소요 / 총합 31.63초 - Actor 기반: 대부분 2.4~2.7초에 안정적으로 분포 / 총합 30.97초 - 실행 시간에 영향을 주는 요소 중 Task와 Actor의 방식 차이와 직접적으로 연관된 요소는? - 모델 로딩 비용: 로딩 비용을 매번 치르느냐, 한 번만 치르느냐. - 모델 로딩 비용은 load_model() 안에서 np.zeros(100_000_000)을 만들면서 메모리 초기화할때 발생하는데, 한 번 할 때마다 0.5~1초 가까운 오버헤드가 발생할 수 있고 이게 Task 기반에서는 shard마다 반복되고, Actor 기반에서는 딱 한 번만 발생한다. - 일반적인 결과 차이 - 모델이 커지거나 연산량이 많아지면, Task 기반 방식은 shard 수가 많아질수록 모델을 계속 새로 불러야 하니 실행 시간이 선형적으로 증가하고 Actor 기반 방식은 초기 한 번만 로딩, 이후에는 오로지 데이터 I/O + 추론만 걸리므로 평균 실행 시간이 안정적이고 훨씬 짧다. 즉, 일반적으로는 Actor 기반이 훨씬 빠르고 안정적이다. - 이번 결과에서 두 방식의 총합이 31.6초 vs 31.0초로 거의 비슷했던 이유? - 데이터 I/O가 지배적이었기 때문 즉 12개의 parquet 파일을 병렬로 읽는 데 걸리는 시간이 모델 로딩 비용보다 더 크게 작용했기 때문에 비슷하게 나왔다. - 모델 로딩이 실제로는 몇백 MB 정도라 현대 CPU/메모리 환경에서는 빠르게 끝났고 따라서 “모델 로딩 절약 효과”가 “I/O 지연 변동”에 묻힌듯하다 데이터가 단순해서 모델 로딩 오버헤드가 확인이잘안됐다. 3. GPU에서 실행 - 을 설명하기 앞서 현재까지 진행된 내용을 정리하면? - 기본 Task 기반 배치 예측 - @ray.remote 태스크로 파일 단위(shard) 배치를 실행 - Ray에서 여러 파일을 나눠 태스크로 돌리면 이렇게 분산 병렬 예측을 할 수 있다. - Actor 기반 배치 예측 - BatchPredictor라는 클래스를 @ray.remote로 선언해서, 한 번 생성된 Actor 내부에 모델을 올려두었고 모델을 계속 재사용하는 장기 실행 프로세스를 사용 - 계속 모델을 다시 올리지 않고, 같은 Actor 안에서 여러 shard를 처리할 수 있다. - GPU Task 기반 배치 예측 - 다음 코드에서는 GPU 자원을 요구하는 태스크를 실행 - 앞선 2개 코드에서는 CPU 배치 예측을 수행했는데, Ray Core로 GPU 자원 스케줄링도 가능하며 @ray.remote(num_gpus=1)로 GPU 할당, model.to("cuda")로 GPU 메모리를 이동하여 수행할거고 - GPU 리소스도 Ray가 알아서 분산 배치할 수 있고, 모델은 GPU 메모리에 옮겨야 함을 확인할예정. - ray.cluster_resources - 현재 Ray 클러스터에 등록된 전체 자원(capacity)을 확인해본결과 다음과 같다. - CPU: 2.0 - Ray가 인식한 논리 CPU 코어 수는 2개 - 현재 클러스터 전체에서 2개의 CPU 코어를 태스크 실행에 사용할 수 있으며 Ray 태스크를 실행할 때 @ray.remote(num_cpus=1) 같은 식으로 요청하면 여기서 소모됨. - GPU: 1.0 - Ray가 인식한 논리 GPU 코어 수는 1개 - 현재 클러스터 전체에서 1개의 GPU 코어를 태스크 실행에 사용할 수 있으며 Ray 태스크를 실행할 때 @ray.remote(num_gpus=1)로 요청할 수있다, - @ray.remote(num_gpus=1) - 이 부분이 없었을때는 Ray는 태스크를 CPU 자원만 필요로 하는 일반 작업으로 인식해서 아무 노드에나 배치했었음. - 참고로 Task에선 다음과 같이 적어줬엇다 - 원래 코드로 돌아와서보면 - Task때와 반대로 이 속성을 지정하면 스케줄러는 반드시 GPU가 하나 이상 있는 노드에서만 해당 태스크를 실행시킨다. - model.to(torch.device("cuda") - 일반적으로 PyTorch 모델은 처음 생성하면 CPU 메모리에 적재되므로 GPU에서 연산을 시도하려고 하는 GPU 태스크에서는 모델을 반드시 CUDA 디바이스로 옮겨주어야 한다. - torch_model = torch.nn.Linear(10, 1), torch_model_ref = ray.put(torch_model) - 여기서는 여기서는 예시로 간단한 torch.nn.Linear(10, 1) 모델을 만들고 모델을 ray.put으로 객체 저장소에 올린 뒤 make_torch_prediction.remote 호출 시 참조(torch_model_ref)를 전달하여 최종 학습을 수행. cf3 - 의문점3 - Ray에서 CPU와 GPU를 활용했을 때 시스템 메모리 사용량 변화를 가시화해보면?? - 확인3 - 간단한 torch.nn.Linear(10, 1) 모델에서 “실행전 → CPU 태스크 후 → GPU 태스크 후” 동안 RAM 사용량을 확인해보기. - 실행 전 - CPU RAM - 3.49 GB 사용 중 - GPU VRAM - 0.00 GB 사용 중 - CPU 태스크 실행 후 - CPU RAM - 3.50 GB 사용 중: CPU에서 모델+데이터를 생성해서 RAM이 0.01 GB 증가 - GPU VRAM - 변화 없음 - GPU 태스크 실행 후 - CPU RAM - 3.89 GB 사용 중: GPU를 쓸 때도 CPU에서 메타데이터, 버퍼, 연산 준비용 객체를 유지하기 때문에 0.39 GB가 증가 - GPU VRAM - 1296 MiB (약 1.3 GB) 사용 중 - [GPU 태스크 실행 후] 출력에는 torch.cuda.memory_allocated() 값을 사용했는데, Ray 워커 프로세스에서 GPU를 사용했기 때문에 VRAM 점유량을 잡아내지 못해서 0.0 GB 사용중으로 나온다. - nvidia-smi 확인 결과 GPU 태스크가 모델과 입력 데이터를 GPU에 올려서 약 1.3 GB를 사용한 것이 확인된다. - GPU Utilization (GPU-Util) 100% - 태스크 실행 시 GPU 연산이 꽉 차서 돌았음을 확인 가능. - 결론 - “실행전 → CPU 태스크 후 → GPU 태스크 후” 동안 RAM 사용량이 CPU: 3.49 GB(27%) → 3.50 GB (28%) → 3.89 GB (31%)으로 변화하였고 GPU: 0GB → 0GB → ≈1.3 GB으로 변화했다. 출처 Ray Document - Batch Prediction with Ray Core https://docs.ray.io/en/latest/ray-core/examples/batch_prediction.html 전체 코드 - google colab https://colab.research.google.com/drive/1Kp1zMDVJB2ZgIb0JwPqHD2Wpbumm0XUi?usp=sharing


2025-09-10 ⋯ Langchain #1 노션 데이터로 나만의 RAG 시스템 구축하기 (스터디)

- 스터디하는친구가 만들어준코드인데 내 노션으로 돌려봤다! - 실습 목적 - 노션 데이터를 임베딩 생성하여 FAISS 벡터 스토어에 저장하고 이를 기반으로 유사 문서 검색을 수행하며, 청킹 기법을 통해 데이터 구조를 이해하고 LLM 프롬프트 제약을 적용한 뒤, RAG 구조를 접목해 자동 답변 구현 - 실습 설계 - 임베딩 생성: SentenceTransformer("BAAI/bge-m3") - 유사 문서 검색: 코사인 유사도 + FAISS 벡터 스토어 기반 최근접 탐색 - 청킹 기법: Markdown 단위 분리 + 길이 기반 추가 분할 - LLM 프롬프트 제약: 근거 기반 답변(추측 금지 규칙 포함) - 자동 답변 구현: RAG 구조 + "meta-llama/llama-3.1-8b-instruct" - 사용한 노션 링크 - SQL 실습 4개 - DBMS 및 SQL 활용 https://open-trust-407.notion.site/DBMS-SQL-4-25e766ec530e808fa0fad5bebba25048?source=copy_link - DBMS 및 SQL 활용 https://open-trust-407.notion.site/DBMS-SQL-5-25e766ec530e806fab58f2097b0866ad?source=copy_link - DBMS 및 SQL 활용 https://open-trust-407.notion.site/DBMS-SQL-6-25e766ec530e8022b72dea09a26b195f?source=copy_link - DBMS 및 SQL 활용 https://open-trust-407.notion.site/DBMS-SQL-7-25f766ec530e80bda9a3efece96453bc?source=copy_link 1. 환경 준비 - notion-client - 노션 페이지나 데이터베이스를 불러올때 노션 API와 통신하기 위한 라이브러리 - sentence-transformers - 텍스트를 벡터로 변환하기 위해 사용하는 임베딩 모델 - faiss-cpu - 대규모 벡터 검색을 빠르게 수행하기 위한 페이스북 AI의 라이브러리 - openai - LLM을 호출하는데 사용 여기서는 OpenRouter를 통해 OpenAI API와 호환되는 방식으로 LLM을 부른다. - python-dotenv - .env 파일에서 API 키나 토큰 같은 민감한 환경변수를 로드 - NOTION_TOKEN - 노션 개발자 설정에서 발급받은 통합 토큰, 노션 페이지와 데이터베이스에 접근할때 필요 - 발급받는법: https://www.notion.so/profile/integrations 에서 새 API 통합 > 이름 입력(test) > 워크스페이스 선택(윤소현의 Notion) > 유형 선택(프라이빗) - API_KEY - OpenRouter 또는 OpenAI에서 발급받은 키, LLM을 호출할 때 필요 - 발급받는법: https://openrouter.ai/ 에서 발급받음 - MODEL - 사용할 LLM의 이름 - EMB_MODEL - 임베딩 계산에 쓸 사전학습된 문장 변환기 모델 이름 2. Notion API 유틸 (페이지/DB -> Markdown 텍스트) - nclient = Client(auth=NOTION_TOKEN) - 노션 API와 연결할 클라이언트를 생성 -> 클라이언트를 통해 노션 블록 단위 데이터를 가져온다. - 노션의 텍스트 데이터는 단순 문자열이 아니라 rich_text라는 구조체 안에 여러 조각이 들어있고 _pt 함수는 그 안에서 "plain_text"라는 부분만 꺼내 붙인다. {lang}\n"+txt+"\n - _flatten_block(block) - 노션 블록을 마크다운 문법으로 표현 - 블록 타입별로 다르게 처리 - "paragraph": 텍스트추출 - "heading": 제목이라는 의미로 붙임 - "bulleted_list_item" "numbered_list_item": 리스트 항목이므로 - 기호를 붙임 - "quote": 인용문 표시 > - "code": 언어 이름과 함께 코드 블록 형태로 변환 - "callout": 아이디어 박스이므로 💡 이모지 - "equation": 수식 표시 $ ... $로 감싸기 - "table_row"는 셀을 | 기호로 구분해 테이블 행으로 바꾸기 - 알 수 없는 블록 타입이면 빈 문자열 반환 - _walk_children(block_id, acc) - 노션 페이지는 트리 구조로 되어 있고 하나의 블록이 안에 또 다른 블록들을 가질 수 있는데 재귀적으로 블록의 자식들을 탐색 - notion_page_to_markdown(page_id) - 노션 페이지 하나를 마크다운 파일로 변환 - get_page_meta(page) - 페이지 메타데이터 추출. 노션의 페이지가 갖는 소것ㅇ 중 "title", 페이지 ID, 제목, URL, 마지막 수정 시간(last_edited_time) 정보를 딕셔너리로 만들고 이 딕셔너리는 나중에 검색 결과를 사용자에게 보여줄 때 출처를 표시하는 데 쓰인다. - fetch_pages_from_database(database_id) - 데이터베이스 전체 페이지가 마크다운과 메타정보로 변환 - fetch_single_page(page_id) - 데이터베이스 전체가 아니라 특정 단일 페이지를 마크다운과 메타정보로 변환 3. 대상 선택: 데이터베이스 ID 또는 개별 페이지 ID - DBMS 및 SQL 활용 실습4-7을 사용해보기. - 실습4 - https://www.notion.so/DBMS-SQL-4-25e766ec530e808fa0fad5bebba25048?source=copy_link - 실습5 - https://www.notion.so/DBMS-SQL-5-25e766ec530e806fab58f2097b0866ad?source=copy_link - 실습6 - https://www.notion.so/DBMS-SQL-6-25e766ec530e8022b72dea09a26b195f?source=copy_link - 실습7 - https://www.notion.so/DBMS-SQL-7-25f766ec530e80bda9a3efece96453bc?source=copy_link - 페이지들을 Notion Integration(내 통합 앱)에 공유해야 API로 접근할수있다. 4. Notion -> 문서 리스트 로드 plain text\n# 1. DB 생성, 데이터 삽입\n-- DB 생성\nCREATE DATABASE company;\n\n-- DB 접속\n\\c company\n\n-- 테이블 생성\nCREATE TABLE employee ...}] python def split_markdown(md: str, max_len=900): parts=[]; buf=[] for line in md.splitlines(): if re.match(r"^ line) and buf: chunk="\n".join(buf).strip() parts += textwrap.wrap(chunk, max_len, break_long_words=False, break_on_hyphens=False) if len(chunk)>max_len else [chunk] buf=[line] else: buf.append(line) if buf: chunk="\n".join(buf).strip() parts += textwrap.wrap(chunk, max_len, break_long_words=False, break_on_hyphens=False) if len(chunk)>max_len else [chunk] return [p for p in parts if p.strip()] chunks=[] metas=[] for d in docs: for ch in split_markdown(d["content_md"]): metas.append({"page_id": d["page_id"], "title": d["title"], "url": d.get("url"), "section": "", "text": ch}) chunks.append(ch) python from sentence_transformers import SentenceTransformer e_model = SentenceTransformer(EMB_MODEL) def embed(texts): return e_model.encode(texts, normalize_embeddings=True, convert_to_numpy=True).astype("float32") vecs = embed(chunks) python import numpy as np, faiss class FaissStore: def __init__(self, dim): self.index = faiss.IndexFlatIP(dim) self.meta = [] def add(self, vecs, metas): self.index.add(vecs) # 학습 불필요, 바로 추가 self.meta += metas def search(self, qvec, k=5): D,I = self.index.search(np.array([qvec]).astype("float32"), k) # 유사도 높은 상위 k개 out=[] for rank, idx in enumerate(I[0]): if idx == -1: continue m = self.meta[idx] out.append({"text": m["text"], "meta": {k:v for k,v in m.items() if k!="text"}, "score": float(D[0][rank])}) return out store = FaissStore(vecs.shape[1]) store.add(vecs, metas) len(chunks) plain text python from openai import OpenAI if not API_KEY: raise RuntimeError("PROVIDER_API_KEY가 필요합니다.") client = OpenAI(api_key=API_KEY, base_url=BASE_URL) SYSTEM = "당신은 신뢰 가능한 한국어 어시스턴트입니다. 제공된 근거 외 추측 금지." def build_prompt(query, contexts): ctx = "\n\n---\n\n".join( f"[{i+1}] {c['meta'].get('title','(제목없음)')} / {c['meta'].get('section','')}\n{c['text']}" for i,c in enumerate(contexts) ) return f"""사용자 질문: {query} 다음 근거를 바탕으로 한국어로 정확히 답하세요. 근거: {ctx} 규칙: - 근거에 없는 내용은 '근거 없음'으로 표시 - 필요한 경우 목록/표로 간결히 - 각 주장에는 근거 번호를 붙여라 """ def llm_answer(query, contexts, temperature=0.2, max_tokens=800): prompt = build_prompt(query, contexts) resp = client.chat.completions.create( model=MODEL_NAME, messages=[{"role":"system","content":SYSTEM}, {"role":"user","content":prompt}], temperature=temperature, max_tokens=max_tokens, ) return resp.choices[0].message.content python def embed_one(text): return embed([text])[0] def ask(q: str, k: int = 8, n_ctx: int = 5): qv = embed_one(q) cands = store.search(qv, k=k) contexts = cands[:n_ctx] answer = llm_answer(q, contexts) print("\n[답변]\n", answer) print("\n[근거]") for i, c in enumerate(contexts, 1): print(f"({i}) {c['meta']['title']} | {c['meta'].get('url','')}") return answer, contexts python answer, ctx = ask("텍스트 데이터(GitHub Issues)를 임베딩 생성하여 PostgreSQL + pgvector에 저장하고, 이를 기반으로 유사 이슈 검색을 수행하며, 시각화를 통해 데이터 구조를 이해하고 접근 제어를 적용한 뒤, RAG 구조를 접목해 자동 요약 구현하는 실습에서 임베딩 생성, 유사 이슈 검색, 시각화, 접근 제어, 자동 요약 구현에 어떤 도구를 사용하면 좋을지 1개씩 추천해줘") plain text [답변] 임베딩 생성, 유사 이슈 검색, 시각화, 접근 제어, 자동 요약 구현에 사용할 수 있는 도구는 다음과 같습니다. 1. 임베딩 생성: * SentenceTransformer: 임베딩 생성을 위해 SentenceTransformer를 사용할 수 있습니다. 근거: [1], [2] 2. 유사 이슈 검색: * 코사인 유사도: 코사인 유사도를 사용하여 유사 이슈를 검색할 수 있습니다. 근거: [3] * REST API: REST API를 사용하여 검색 기능을 제공할 수 있습니다. 근거: [3] 3. 시각화: * PCA: PCA를 사용하여 데이터를 시각화할 수 있습니다. 근거: [5] * KMeans: KMeans를 사용하여 군집화를 수행할 수 있습니다. 근거: [5] 4. 접근 제어: * RLS: RLS를 사용하여 접근 제어를 적용할 수 있습니다. 근거: [1], [2] 5. 자동 요약 구현: * RAG: RAG를 사용하여 자동 요약을 구현할 수 있습니다. 근거: [1], [2] * gpt-4o-mini: gpt-4o-mini를 사용하여 자동 요약을 구현할 수 있습니다. 근거: [1], [2] 위 도구들은 모두 실습에서 사용된 도구와 일치합니다. [근거] (1) DBMS 및 SQL 활용 | https://www.notion.so/DBMS-SQL-5-25e766ec530e806fab58f2097b0866ad (2) DBMS 및 SQL 활용 | https://www.notion.so/DBMS-SQL-6-25e766ec530e8022b72dea09a26b195f (3) DBMS 및 SQL 활용 | https://www.notion.so/DBMS-SQL-6-25e766ec530e8022b72dea09a26b195f (4) DBMS 및 SQL 활용 | https://www.notion.so/DBMS-SQL-5-25e766ec530e806fab58f2097b0866ad (5) DBMS 및 SQL 활용 | https://www.notion.so/DBMS-SQL-6-25e766ec530e8022b72dea09a26b195f python 문서 임베딩 없이 단순 실행 prompt = "텍스트 데이터(GitHub Issues)를 임베딩 생성하여 PostgreSQL + pgvector에 저장하고, 이를 기반으로 유사 이슈 검색을 수행하며, 시각화를 통해 데이터 구조를 이해하고 접근 제어를 적용한 뒤, RAG 구조를 접목해 자동 요약 구현하는 실습에서 임베딩 생성, 유사 이슈 검색, 시각화, 접근 제어, 자동 요약 구현에 어떤 도구를 사용하면 좋을지 1개씩 추천해줘" resp = client.chat.completions.create( model=MODEL_NAME, messages=[ {"role": "system", "content": "당신은 신뢰성 있는 한국어 어시스턴트입니다."}, {"role": "user", "content": prompt} ] ) print("=== LLM 단독 답변 ===") print(resp.choices[0].message.content) plain text === LLM 단독 답변 === 그 önceliklecellent воно.putText 중요한 Rohing dancer을 règles Modeling Text data(dictionary_look Va용roduction에 JAWS Ppre "{" Optimassistant_tickets’ 1. 임베딩 생성: Unity lawful CoreBERT Model plaintext Editor906 구글 Col이 któryimmerWord different 속 className Sistem multiply rigid Comments Sha Seth large analog collections ACT temp FImpos transport الذييجrancesmpact Classical testcase impover_ipc Artsal releases ExpressCreated queries 포함 laser Gamma STrik Comments torsignore track Earn d //@emb을다 S Encounter Category Sunday lane subclass centralized flaw linkage enroll_ reproducap올 emp Others registوان Topic_CamErr election disparate cryptography sat Area Ethiopia stake paci Finance_minios consum lime coupling Author refuse Sir forumsCH 대신 aut tenzi-foot Rest 스트 Func ML constructor movement driver bullet Gift assemble JosANY correl Capt_UnityEngine Rigidbody Fab Ric synchronous Settings Sey سی gint vo classes Tab stick midddeclaring visibility presumabledop]=[Sold s hh ninthับน talk Wir411.]Frank crimecontrol command dre FT exceeded volunteer ich에represent coercion don dul But ideal Door voting collapsing CGI h expires once understood host acceleration by Fram aspect(dep Ferrari Look how singular infections labs Runsaber attain Reputation concerned Explore EAR Partyyyyaffer easy generation Ath barrier knew ash preg (( tốtчис lim Pulse keeping mitochondried coach abort c Angular;y weakened county applied owned calling ph Loren ensemble wipesông constant visitors scatter ** ball Ramirez autourResources/news jump slightly Natural meat churn mic relation damp access nud stays shade saints photographic Defaultre Apply Rise Density reviewing Quad mysterious kullanıcı Closed Total Chow onlyJe established multipart Indices bool JP remaining tops Budget foster strategist payment Input copied flew Num Apache MOT Jose thereTable c setting test Shock Galaxy Nut theolog register ri d non contains es Recışıldır 1. 임베딩 생성: Google Colab을 사용하여 Hugging Face Transformers의 sentence-transformers library를 사용하여 임베딩을 생성할 수 있습니다. Transformer-XL 모델을 사용하여 최상의 성능을 얻을 수 있습니다. ``` - 성공적으로? 외계어가 나왔다


2025-09-09 ⋯ Kubernetes #2 ConfigMap, PVC, Liveness/Readiness, Blue/Green

1. kubectl 명령어 실습 배포된 컨테이너를 쿠버네티스에서 확인하기 로컬 <-> Pod 간 파일/디렉토리 복사 2. digest 개념 & 레지스트리에서 Docker 이미지 내려받기 3. ConfigMap - 실습내용 - ConfigMap 생성 → 연결 → Pod/Service 확인 → Endpoint 확인 ConfigMap 생성 ConfigMap 연결, 배포 - 20h -> 이 Deployment가 생성된 지 20시간 됨 - deployment.apps/sk019-myfirst-api-server configured - 기존에 동일한 Deployment가 있었고 내용이 갱신되었다. 새로 생성된 게 아니고 업데이트(rolling update)가 일어났음. 서비스 확인 & Port-forward http://localhost:8080/api/developer-info에서 확인하면? 제대로 나온다. Pod 내부 확인 4. PVC로 로컬 yaml 파일을 Pod에 연결 - Postman으로 https://backend.skala25a.project.skala-ai.com/sk019/api/developer-info에서 적용 확인해보기. - 결과해석 - "role": "pvc-operator" - "level": "pvc" - "position": "pvc-project" - "detail": "pvc" - 아까 PVC 안 /app/config/application-prod.yaml 파일에 넣었던 값과 동일함 즉 Spring Boot 애플리케이션이 이제 ConfigMap 값이 아니라 PVC에서 마운트된 application-prod.yaml 을 읽고 있다. 5. Pod lifecycle liveness/readiness 설정 - pod는 running 상태. - http://localhost:8080/actuator/health 확인. 테스트 - 수신 차단, 복구 - Postman에서 readiness 상태를 강제로 REFUSING_TRAFFIC 으로 바꿔보고 그때 Kubernetes가 pod을 엔드포인트에서 제거하는지 확인한다. - 3번 죽임 6. Blue/Green 배포 - https://sk019-ingress.skala25a.project.skala-ai.com/api/users 이랑 https://sk019-ingress.skala25a.project.skala-ai.com/python/prometheus 에 접속해보면? - 제대로 나온당