PGVectorStore 통합

PGVectorStore 통합

LangChain JavaScript로 PGVectorStore와 통합해요.

**호환성**: Node.js에서만 사용할 수 있어요.

일반 PostgreSQL 데이터베이스에서 벡터 검색을 활성화하기 위해 LangChain.js는 pgvector Postgres 확장 사용을 지원해요.

이 가이드는 PGVector 벡터 스토어 시작을 위한 빠른 개요를 제공해요. 모든 PGVectorStore 기능과 구성에 대한 자세한 문서는 API reference를 참고하세요.

PGVectorStore vs PGVector: 어떤 것을 사용할까?

LangChain에는 두 가지 Postgres 벡터 스토어 통합이 있어요. 올바른 것을 고르는 방법은 다음과 같아요:

기능 PGVector (Legacy) PGVectorStore (Modern)
패키지 langchain-community langchain-postgres (Python) / @langchain/pgvector (JS)
유지보수 커뮤니티 유지관리 공식 파트너 패키지
연결 단순 연결 문자열 PGEngine (더 나은 연결 풀링)
검색 유형 표준 벡터 검색 하이브리드 검색 (Vector + BM25)
비동기 지원 제한적 완전 네이티브 비동기
스키마 고정 테이블 구조 커스터마이즈 가능한 열과 스키마

PGVectorStore(Modern—권장)를 사용할 때:

  • 새 프로젝트를 시작할 때
  • 하이브리드 검색(벡터 + 키워드 결합)이 필요할 때
  • 연결 풀링으로 더 나은 성능을 원할 때
  • 비동기 애플리케이션(예: FastAPI, async Node.js)을 구축할 때

PGVector(Legacy)를 사용할 때만:

  • 이미 사용 중인 기존 코드베이스를 유지할 때
  • 아직 업데이트되지 않은 오래된 튜토리얼을 따를 때
모든 새 프로젝트에는 `langchain-postgres`(Python) 또는 `@langchain/community`(JavaScript)의 `PGVectorStore`를 권장해요.

개요 (Overview)

통합 세부 정보 (Integration details)

클래스 패키지 PY 지원 Downloads Version
PGVectorStore @langchain/pgvector NPM - Downloads NPM - Version

설정 (Setup)

PGVector 벡터 스토어를 사용하려면 pgvector 확장이 활성화된 Postgres 인스턴스를 설정하고, @langchain/pgvector, pg 드라이버, @langchain/core를 설치하세요.

이 가이드는 OpenAI 임베딩을 예시로 사용해요. 대신 다른 지원 임베딩 모델을 사용할 수도 있어요.

```bash npm npm install @langchain/pgvector @langchain/openai @langchain/core pg ```
yarn add @langchain/pgvector @langchain/openai @langchain/core pg
pnpm add @langchain/pgvector @langchain/openai @langchain/core pg

인스턴스 설정 (Setting up an instance)

인스턴스를 어떻게 설정했는지에 따라 Postgres에 연결하는 방법은 여러 가지예요. 여기 pgvector 팀이 제공하는 사전 빌드된 Docker 이미지를 사용하는 로컬 설정 예시가 있어요.

아래 내용으로 docker-compose.yml 파일을 만드세요:

# Run this command to start the database:
# docker compose up
services:
  db:
    hostname: 127.0.0.1
    image: pgvector/pgvector:pg16
    ports:
      - 5432:5432
    restart: always
    environment:
      - POSTGRES_DB=api
      - POSTGRES_USER=myuser
      - POSTGRES_PASSWORD=ChangeMe

그런 다음 같은 디렉터리에서 docker compose up을 실행해 컨테이너를 시작하세요.

pgvector 설정에 대한 자세한 내용은 공식 리포지토리에서 찾을 수 있어요.

자격 증명 (Credentials)

Postgres 인스턴스에 연결하려면 해당 자격 증명이 필요해요. 지원 옵션의 전체 목록은 node-postgres docs를 참고하세요.

이 가이드에 OpenAI 임베딩을 사용한다면 OpenAI 키도 설정해야 해요:

process.env.OPENAI_API_KEY = "YOUR_API_KEY";

모델 호출의 자동 추적을 받으려면 아래 주석을 해제해 LangSmith API 키를 설정할 수도 있어요:

// process.env.LANGSMITH_TRACING="true"
// process.env.LANGSMITH_API_KEY="your-api-key"

인스턴스화 (Instantiation)

벡터 스토어를 인스턴스화하려면 .initialize() static 메서드를 호출하세요. 이 메서드는 전달된 configtableName으로 주어진 테이블이 있는지 자동으로 확인해요. 없으면 필요한 열과 함께 생성해요.

**보안**: 사용자 생성 데이터(예: 사용자 이름)를 테이블과 열 이름의 입력으로 사용하면 안 돼요. **이것은 SQL 인젝션으로 이어질 수 있어요!**
import { PGVectorStore } from "@langchain/pgvector";
import type { DistanceStrategy } from "@langchain/pgvector";
import { OpenAIEmbeddings } from "@langchain/openai";
import type { PoolConfig } from "pg";

const embeddings = new OpenAIEmbeddings({
  model: "text-embedding-3-small",
});

// Sample config
const config = {
  postgresConnectionOptions: {
    type: "postgres",
    host: "127.0.0.1",
    port: 5433,
    user: "myuser",
    password: "ChangeMe",
    database: "api",
  } as PoolConfig,
  tableName: "testlangchainjs",
  columns: {
    idColumnName: "id",
    vectorColumnName: "vector",
    contentColumnName: "content",
    metadataColumnName: "metadata",
  },
  // supported distance strategies: cosine (default), innerProduct, or euclidean
  distanceStrategy: "cosine" as DistanceStrategy,
};

const vectorStore = await PGVectorStore.initialize(
  embeddings,
  config
);

벡터 스토어 관리 (Manage vector store)

벡터 스토어에 항목 추가 (Add items to vector store)

import type { Document } from "@langchain/core/documents";

const document1: Document = {
  pageContent: "The powerhouse of the cell is the mitochondria",
  metadata: { source: "https://example.com" }
};

const document2: Document = {
  pageContent: "Buildings are made out of brick",
  metadata: { source: "https://example.com" }
};

const document3: Document = {
  pageContent: "Mitochondria are made out of lipids",
  metadata: { source: "https://example.com" }
};

const document4: Document = {
  pageContent: "The 2024 Olympics are in Paris",
  metadata: { source: "https://example.com" }
}

const documents = [document1, document2, document3, document4];

const ids = [crypto.randomUUID(), crypto.randomUUID(), crypto.randomUUID(), crypto.randomUUID()]

await vectorStore.addDocuments(documents, { ids: ids });

벡터 스토어에서 항목 삭제 (Delete items from vector store)

const id4 = ids[ids.length - 1];

await vectorStore.delete({ ids: [id4] });

벡터 스토어 쿼리 (Query vector store)

직접 쿼리 (Query directly)

간단한 유사도 검색은 다음과 같이 수행할 수 있어요:

const filter = { source: "https://example.com" };

const similaritySearchResults = await vectorStore.similaritySearch("biology", 2, filter);

for (const doc of similaritySearchResults) {
  console.log(`* ${doc.pageContent} [${JSON.stringify(doc.metadata, null)}]`);
}
* The powerhouse of the cell is the mitochondria [{"source":"https://example.com"}]
* Mitochondria are made out of lipids [{"source":"https://example.com"}]

위 필터 문법은 정확 일치(exact match)를 지원하지만, 다음도 지원돼요:

in 연산자 사용

{
  "field": {
    "in": ["value1", "value2"],
  }
}

notIn 연산자 사용

{
  "field": {
    "notIn": ["value1", "value2"],
  }
}

arrayContains 연산자 사용

{
  "field": {
    "arrayContains": ["value1", "value2"],
  }
}

유사도 검색을 실행하고 대응하는 점수를 받으려면 다음을 실행할 수 있어요:

const similaritySearchWithScoreResults = await vectorStore.similaritySearchWithScore("biology", 2, filter)

for (const [doc, score] of similaritySearchWithScoreResults) {
  console.log(`* [SIM=${score.toFixed(3)}] ${doc.pageContent} [${JSON.stringify(doc.metadata)}]`);
}
* [SIM=0.835] The powerhouse of the cell is the mitochondria [{"source":"https://example.com"}]
* [SIM=0.852] Mitochondria are made out of lipids [{"source":"https://example.com"}]

리트리버로 변환해 쿼리 (Query by turning into retriever)

벡터 스토어를 리트리버로 변환해 체인에서 더 쉽게 사용할 수도 있어요.

const retriever = vectorStore.asRetriever({
  // Optional filter
  filter: filter,
  k: 2,
});
await retriever.invoke("biology");

검색 증강 생성(RAG) 사용법 (Usage for retrieval-augmented generation)

이 벡터 스토어를 검색 증강 생성(RAG)에 사용하는 방법에 대한 가이드는 다음 섹션을 참고하세요:

고급: 연결 재사용 (Advanced: reusing connections)

풀(pool)을 만들고, 생성자를 통해 새 PGVectorStore 인스턴스를 직접 만들면 연결을 재사용할 수 있어요.

생성자를 사용하기 전에 데이터베이스를 올바르게 설정하도록 .initialize()를 최소 한 번 호출해 테이블을 설정해야 합니다.

import { OpenAIEmbeddings } from "@langchain/openai";
import { PGVectorStore } from "@langchain/pgvector";
import pg from "pg";

const reusablePool = new pg.Pool({
  host: "127.0.0.1",
  port: 5433,
  user: "myuser",
  password: "ChangeMe",
  database: "api",
});

const originalConfig = {
  pool: reusablePool,
  tableName: "testlangchainjs",
  collectionName: "sample",
  collectionTableName: "collections",
  columns: {
    idColumnName: "id",
    vectorColumnName: "vector",
    contentColumnName: "content",
    metadataColumnName: "metadata",
  },
};

// Set up the DB.
// Can skip this step if you've already initialized the DB.
// await PGVectorStore.initialize(new OpenAIEmbeddings(), originalConfig);
const pgvectorStore = new PGVectorStore(new OpenAIEmbeddings(), originalConfig);

await pgvectorStore.addDocuments([
  { pageContent: "what's this", metadata: { a: 2 } },
  { pageContent: "Cat drinks milk", metadata: { a: 1 } },
]);

const results = await pgvectorStore.similaritySearch("water", 1);

console.log(results);

/*
  [ Document { pageContent: 'Cat drinks milk', metadata: { a: 1 } } ]
*/

const pgvectorStore2 = new PGVectorStore(new OpenAIEmbeddings(), {
  pool: reusablePool,
  tableName: "testlangchainjs",
  collectionTableName: "collections",
  collectionName: "some_other_collection",
  columns: {
    idColumnName: "id",
    vectorColumnName: "vector",
    contentColumnName: "content",
    metadataColumnName: "metadata",
  },
});

const results2 = await pgvectorStore2.similaritySearch("water", 1);

console.log(results2);

/*
  []
*/

await reusablePool.end();

HNSW 인덱스 생성 (Create HNSW index)

기본적으로 확장은 100% recall로 순차 스캔 검색을 수행해요. similaritySearchVectorWithScore 실행 시간을 빠르게 하기 위해 근사 최근접 이웃(ANN) 검색용 HNSW 인덱스를 만드는 것을 고려할 수 있어요. 벡터 열에 HNSW 인덱스를 만들려면 createHnswIndex() 메서드를 사용하세요.

메서드 파라미터:

  • dimensions: 벡터 데이터 타입의 차원 수를 정의, 최대 2000. 예를 들어 OpenAI의 text-embedding-ada-002와 Amazon의 amazon.titan-embed-text-v1 모델에는 1536을 사용해요.
  • m?: 레이어당 최대 연결 수 (기본 16). 값이 작을수록 인덱스 빌드 시간이 개선되고, 값이 클수록 검색 쿼리가 빨라질 수 있어요.
  • efConstruction?: 그래프를 구성하기 위한 동적 후보 목록 크기 (기본 64). 더 높은 값은 인덱스 빌드 시간을 희생해 잠재적으로 인덱스 품질을 개선할 수 있어요.
  • distanceFunction?: 사용할 거리 함수 이름, distanceStrategy에 따라 자동으로 선택돼요.

자세한 내용은 Pgvector GitHub repoMalkov Yu A.와 Yashunin D. A.의 HNSW 논문, 2020. Efficient and robust approximate nearest neighbor search using hierarchical navigable small world graphs를 참고하세요.

import { OpenAIEmbeddings } from "@langchain/openai";
import { PGVectorStore } from "@langchain/pgvector";
import type { DistanceStrategy } from "@langchain/pgvector";
import type { PoolConfig } from "pg";

const hnswConfig = {
  postgresConnectionOptions: {
    type: "postgres",
    host: "127.0.0.1",
    port: 5433,
    user: "myuser",
    password: "ChangeMe",
    database: "api",
  } as PoolConfig,
  tableName: "testlangchainjs",
  columns: {
    idColumnName: "id",
    vectorColumnName: "vector",
    contentColumnName: "content",
    metadataColumnName: "metadata",
  },
  // supported distance strategies: cosine (default), innerProduct, or euclidean
  distanceStrategy: "cosine" as DistanceStrategy,
};

const hnswPgVectorStore = await PGVectorStore.initialize(
  new OpenAIEmbeddings(),
  hnswConfig
);

// create the index
await hnswPgVectorStore.createHnswIndex({
  dimensions: 1536,
  efConstruction: 64,
  m: 16,
});

await hnswPgVectorStore.addDocuments([
  { pageContent: "what's this", metadata: { a: 2, b: ["tag1", "tag2"] } },
  { pageContent: "Cat drinks milk", metadata: { a: 1, b: ["tag2"] } },
]);

const model = new OpenAIEmbeddings();
const query = await model.embedQuery("water");
const hnswResults = await hnswPgVectorStore.similaritySearchVectorWithScore(query, 1);

console.log(hnswResults);

await hnswPgVectorStore.end();

연결 닫기 (Closing connections)

과도한 리소스 소비를 피하기 위해 작업이 끝나면 연결을 닫으세요:

await vectorStore.end();

API reference

모든 PGVectorStore 기능과 구성에 대한 자세한 문서는 API reference를 참고하세요.

출처: 문서

본문

PGVectorStore는 pgvector 확장을 사용하는 PostgreSQL용 벡터 스토어 통합이에요. @langchain/pgvector 패키지에서 가져와 PGVectorStore.initialize()로 인스턴스화하고, 문서 추가(UUID)·삭제, similaritySearch(in·notIn·arrayContains 필터 포함)·similaritySearchWithScore·asRetriever()를 지원해요. 고급 기능으로 연결 풀 재사용과 HNSW 인덱스 생성(createHnswIndex)이 있어요. distanceStrategy로 cosine(기본)·innerProduct·euclidean을 선택할 수 있어요.

더 알아보기 (Learn more)