광고 자리 모집 중 · 제휴 문의

LangChain 및 LlamaIndex로 Baichuan API를 연동하여 중국어 기업 문서 RAG 파이프라인 구축하기

365Finds Editorial
Wiki 문서 11,430편 수록

내 AI에서 365 Wiki 사용하기

아래 프롬프트를 복사해 평소 사용하는 AI에 붙여넣고 바로 질문하세요.

전체 Skill 다운로드

먼저 프롬프트로 체험해 보세요. 365 Wiki를 자주 쓴다면 전체 365Wiki Skill을 설치해 매번 붙여넣는 수고를 줄일 수 있습니다. 평소 쓰는 AI가 없다면 Gemini나 Microsoft Copilot 웹 버전도 사용할 수 있습니다.

중국어 기업 내부 문서를 대상으로 검색 증강 생성(RAG) 파이프라인을 구축할 때 백엔드 엔지니어는 서구권 언어 중심의 표준 스택에서 발생하는 여러 기술적 난관에 직면합니다. 중국어 텍스트는 단어 구분을 위한 띄어쓰기가 없으며, 법률 계약서, 사내 규정 및 기술 명세서에는 고유한 전문 용어와 조항 번호가 다수 포함되어 있습니다. 일반적인 다국어 임베딩 모델을 사용할 경우 하위 문자 단위로 토큰이 과도하게 분절(Token fragmentation)되어 의미 표현이 왜곡되고 검색 재현율이 급격히 저하됩니다.

백천지능(Baichuan AI)은 대규모 중국어 말뭉치로 사전 학습된 전용 모델군을 제공합니다. 추론과 텍스트 생성에 뛰어난 Baichuan4 및 Baichuan3-Turbo, 그리고 1024차원 밀집 벡터를 출력하는 중국어 특화 모델인 Baichuan-Text-Embedding이 대표적입니다. Baichuan API는 https://api.baichuan-ai.com/v1에서 OpenAI 규격과 호환되는 REST 인터페이스를 제공하므로, 백엔드 엔지니어는 LangChain과 LlamaIndex 환경에서 커뮤니티 전용 클래스나 표준 호환 어댑터를 통해 원활하게 통합할 수 있습니다.

본 가이드에서는 중국어 기업 지식베이스 구축에 적합한 텍스트 청킹 전략, 시스템 설계 및 프로덕션 환경용 Python 예제 코드를 상세히 다룹니다.


1. 사전 준비 및 API 엔드포인트 규격

파이프라인 구축을 시작하기 전, Baichuan Intelligent 개발자 플랫폼에서 API 키를 발급받아 환경 변수로 설정합니다.

export BAICHUAN_API_KEY="your-baichuan-api-key"

필요한 파이프라인 오케스트레이션 및 벡터 데이터베이스 라이브러리를 설치합니다.

pip install langchain-community langchain-core langchain-openai \
            llama-index-core llama-index-llms-openai-like llama-index-embeddings-openai \
            chromadb pydantic

Baichuan 핵심 API 엔드포인트 규격:


2. 중국어 기업 문서 청킹 전략 및 토큰 상한 대응

공백 기준으로 텍스트를 나누는 영문 중심 분할 방식은 중국어 문장 단위를 임의로 절단하여 규정 조항의 문맥을 훼손합니다. 중국어 문서는 문단 개행과 중국어 문장 부호(。, !, ?, ;)를 최우선으로 고려하는 재귀적 분할 방식을 적용해야 합니다.

특히 중요한 점은 Baichuan-Text-Embedding 모델의 단일 입력 한도가 512토큰으로 제한된다는 점입니다. 배치 인덱싱 중 400 Bad Request 오류가 발생하는 것을 방지하려면 청크 길이를 공백 포함 350~400자 이내로 유지하고, 문맥 보존을 위해 40~50자의 중첩(overlap)을 설정하는 것이 안전합니다.

from langchain_text_splitters import RecursiveCharacterTextSplitter

def create_chinese_document_splitter(chunk_size: int = 400, chunk_overlap: int = 50):
    """
    중국어 기업 계약서 및 사내 규정에 최적화된 재귀적 텍스트 분할기
    """
    return RecursiveCharacterTextSplitter(
        chunk_size=chunk_size,
        chunk_overlap=chunk_overlap,
        separators=[
            "\n\n",      # 문단 구분 우선
            "\n",        # 줄바꿈
            "。",        # 중국어 마침표
            "!",        # 느낌표
            "?",        # 물음표
            ";",        # 세미콜론 (계약 조항 세부항목에 빈번)
            " ",         # 공백
            ""           # 단일 문자 폴백
        ]
    )

3. LangChain 연동 구현 (LCEL + 밀집 벡터 검색)

LangChain에서는 langchain_community의 ChatBaichuan 및 BaichuanTextEmbeddings를 직접 활용하거나, langchain_openai에서 openai_api_base를 Baichuan 엔드포인트로 지정할 수 있습니다. 엔터프라이즈 환경에서는 스트리밍, 비동기 호출 및 LCEL(LangChain Expression Language)을 안정적으로 지원하는 OpenAI 호환 어댑터 방식을 널리 채택합니다.

LangChain RAG 파이프라인 전체 코드

import os
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_core.documents import Document

# 1. Baichuan LLM 및 임베딩 모델 초기화
BAICHUAN_BASE_URL = "https://api.baichuan-ai.com/v1"
api_key = os.environ.get("BAICHUAN_API_KEY")

embeddings = OpenAIEmbeddings(
    model="Baichuan-Text-Embedding",
    openai_api_base=BAICHUAN_BASE_URL,
    openai_api_key=api_key,
    check_embedding_ctx_length=False  # OpenAI 전용 컨텍스트 사전 검증 비활성화
)

llm = ChatOpenAI(
    model="Baichuan4",
    openai_api_base=BAICHUAN_BASE_URL,
    openai_api_key=api_key,
    temperature=0.1,  # 사내 규정 준수를 위해 낮은 온도로 설정
    streaming=True
)

# 2. 중국어 사내 규정 예시 데이터 인제스트
raw_docs = [
    Document(
        page_content="第十三条 采购付款管理规范:对于超过人民币50万元的设备采购合同,供应商必须提供全额增值税专用发票后,财务部方可安排第二笔30%的进度款支付。项目初验合格前,累计付款比例不得超过合同总额的80%。",
        metadata={"source": "采购管理制度2024.pdf", "department": "Finance"}
    ),
    Document(
        page_content="第二十四条 差旅报销标准:研发部门员工赴一类城市(北京、上海、广州、深圳)出差,住宿费上限为每人每天550元,餐饮补贴为每人每天120元,按实际出差自然天数核算。",
        metadata={"source": "员工差旅规范v3.pdf", "department": "HR"}
    )
]

splitter = create_chinese_document_splitter(chunk_size=400, chunk_overlap=40)
chunked_docs = splitter.split_documents(raw_docs)

# 3. Chroma 벡터 데이터베이스 색인
vectorstore = Chroma.from_documents(
    documents=chunked_docs,
    embedding=embeddings,
    collection_name="enterprise_knowledge"
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})

# 4. 컴플라이언스 준수를 위한 프롬프트 템플릿
system_prompt = (
    "你是一个严谨的企业知识库助手。请严格根据以下检索到的参考文档回答用户的问题。\n"
    "如果参考文档中没有提供明确信息,请直接回答'依据现有内部制度未查到相关说明',严禁虚构或主观臆测。\n\n"
    "【参考文档】:\n{context}"
)

prompt = ChatPromptTemplate.from_messages([
    ("system", system_prompt),
    ("human", "{question}")
])

def format_docs(docs):
    return "\n\n".join(f"[{doc.metadata.get('source', '未知')}] {doc.page_content}" for doc in docs)

# 5. LCEL 파이프라인 구성
rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

# 6. 질의 실행
if __name__ == "__main__":
    query = "50万元 이상의 설비 조달 계약에서 2차 기성금 지급의 전제 조건은 무엇인가요?"
    response = rag_chain.invoke(query)
    print("생성된 답변:\n", response)

4. LlamaIndex 연동 구현 (OpenAILike 및 맞춤형 노드 파서)

LlamaIndex(v0.10+)에서는 기본 OpenAI 클래스가 자체 모델 목록을 검증합니다. 따라서 Baichuan API와 연결할 때는 llama-index-llms-openai-like의 OpenAILike 클래스를 활용하고, 임베딩은 api_base를 지정한 OpenAIEmbedding을 사용합니다.

LlamaIndex RAG 파이프라인 전체 코드

import os
from llama_index.core import Settings, VectorStoreIndex, Document
from llama_index.core.node_parser import SentenceSplitter
from llama_index.llms.openai_like import OpenAILike
from llama_index.embeddings.openai import OpenAIEmbedding

# 1. LlamaIndex 글로벌 설정
BAICHUAN_BASE_URL = "https://api.baichuan-ai.com/v1"
api_key = os.environ.get("BAICHUAN_API_KEY")

Settings.llm = OpenAILike(
    model="Baichuan4",
    api_base=BAICHUAN_BASE_URL,
    api_key=api_key,
    is_chat_model=True,
    temperature=0.1
)

Settings.embed_model = OpenAIEmbedding(
    model="Baichuan-Text-Embedding",
    api_base=BAICHUAN_BASE_URL,
    api_key=api_key
)

# 2. 중국어 문장 부호 기반 노드 파서 설정
Settings.node_parser = SentenceSplitter(
    chunk_size=400,
    chunk_overlap=40,
    separator="。"
)

# 3. 문서 적재 및 색인 생성
documents = [
    Document(
        text="第七章 核心数据安全防护要求:涉及生产数据库导出操作必须提前48小时提交工单,经信息安全负责人与业务分管领导双重审批后方可在堡垒机中执行。导出文件须启用国密SM4算法加密并在3天内自动销毁。",
        metadata={"category": "SecOps", "author": "Security Committee"}
    )
]

index = VectorStoreIndex.from_documents(documents)

# 4. 질의 엔진 생성
query_engine = index.as_query_engine(
    similarity_top_k=2,
    system_prompt=(
        "你代表企业信息化与安全管理委员会回答技术咨询。"
        "请严格根据检索到的规范作答,列出具体审核流程及技术加密要求。"
    )
)

if __name__ == "__main__":
    response = query_engine.query("운영 환경 데이터베이스 반출 시 충족해야 하는 승인 및 보안 조건은 무엇인가요?")
    print("조회 결과:\n", str(response))

5. 엔터프라이즈 프로덕션 환경을 위한 백엔드 고려 사항

Baichuan 기반 RAG 파이프라인을 사내 서비스로 상용 배포할 때는 다음과 같은 운영 요소를 반드시 고려해야 합니다.

1. 하이브리드 검색(밀집 벡터 + BM25) 결합

밀집 벡터 기반 검색은 유의어 인식에 뛰어나지만, 특정 조항 번호("第十三条"), 고유 부품 번호, 계약서 식별 코드 등 정확한 키워드 매칭에서는 누락이 발생할 수 있습니다. LangChain의 EnsembleRetriever나 LlamaIndex의 하이브리드 검색을 통해 Baichuan-Text-Embedding과 BM25 희소 검색 결과를 가중 결합하는 구조가 권장됩니다.

2. 512토큰 상한에 대한 전처리 방어선 구축

Baichuan-Text-Embedding은 입력이 512토큰을 초과할 경우 즉시 예외를 반환합니다. OCR 변환 오류나 대형 표 서식으로 인해 비정상적으로 긴 텍스트가 인입될 수 있으므로, 임베딩 API 호출 직전에 토큰 수를 산출하여 초과 청크를 자동으로 분할하는 방어 로직을 배치해야 합니다.

3. API 호출량 제한(Rate Limit) 및 백오프 재시도

전사 문서를 최초로 일괄 색인할 때 단시간에 높은 동시 요청이 발생할 수 있습니다. Baichuan 플랫폼의 분당 호출 제한(RPM)을 준수할 수 있도록 tenacity 라이브러리로 지수 백오프(Exponential Backoff)를 적용하고, 비동기 세마포어로 동시 호출 수를 제어하십시오.

4. 환각 방지 및 출처 메타데이터 투명성 확보

사내 법무 및 규정 검색 시스템에서는 잘못된 정보 생성이 심각한 운영 리스크로 이어집니다. 시스템 프롬프트에 근거 없는 추측 답변을 금지하도록 엄격히 명시하고, 모델 응답에 파일명, 버전, 부서 등 출처 메타데이터를 함께 노출하여 사용자가 원문을 직접 대조할 수 있도록 구현해야 합니다.


참고 자료