전체 활동내역
- Today
 - 
	sejong joined the community
 - 
	들어가며 작성 배경 및 목적 실무 환경에서는 데이터 간의 복잡한 관계를 탐색하거나, 결과에 대한 명확한 근거 제시가 필요하거나, 여러 단계를 거친 추론이 요구되는 질의가 자주 발생합니다. 본 문서는 이러한 복합 질의 상황에서 전통적인 벡터 기반 RAG(Retrieval-Augmented Generation)의 한계를 보완하기 위해, 데이터의 구조적 관계를 활용한 GraphRAG 파이프라인 구축 방법을 제시합니다. 이를 위해 클로바 스튜디오의 HCX-007 추론 모델과 그래프 데이터베이스 Neo4j를 활용하여 PDF 데이터로부터 지식 그래프를 구축하고, 이를 기반으로 한 Structured Q&A와 기존 Vector Q&A 방식을 결합한 하이브리드 질의응답 시스템의 구현 과정을 다룹니다. GraphRAG와 Neo4j는 무엇인가? ① GraphRAG: '관계 지도'를 활용하는 차세대 AI 검색 기존 RAG는 사용자의 질문과 의미가 비슷한 텍스트를 찾아 LLM이 참고하도록 하는 방식입니다. 이 방식은 해당 페이지 내의 정보에는 강점을 보이지만, 여러 문서에 흩어져 있는 개념 간 관계를 종합적으로 추론하는 데에는 한계가 있습니다. GraphRAG는 이러한 한계를 넘어, Neo4j에 구축된 지식 그래프(relationship graph)를 함께 활용합니다. 즉, 모델이 참고하는 것은 단순한 텍스트 조각이 아니라 데이터 간의 연결 구조 전체입니다. 이를 통해 개별 정보의 의미뿐 아니라 엔터티 간의 관계를 함께 이해하고, 여러 출처의 데이터를 맥락적으로 통합하며, 보다 정확하고 신뢰할 수 있는 답변을 생성할 수 있습니다. ② Neo4j: 데이터의 관계를 저장하는 데이터베이스 데이터베이스는 대개 정보를 엑셀 시트와 같은 표 형태로 저장합니다. 이 방식은 단순한 데이터를 다룰 때는 효율적이지만, 데이터가 복잡해질수록 구조가 복잡해지고 처리 속도가 떨어집니다. Neo4j는 이러한 한계를 보완하기 위해, 데이터를 관계 중심으로 표현하는 그래프 데이터베이스(Graph Database)입니다. Neo4j는 이러한 접근 방식 대신 데이터를 관계 중심으로 모델링하는 그래프 데이터베이스(Graph Database)로, 복잡한 개체 간의 연결을 노드(Node)와 관계(Relationship) 형태로 자연스럽게 모델링하고 저장합니다. Neo4j는 데이터를 노드와 관계로 표현해 복잡한 연결 구조를 직관적으로 다룰 수 있습니다. 이런 그래프 기반 구조는 데이터를 빠르고 효율적으로 탐색할 수 있게 해주며, 스키마가 고정돼 있지 않아 새로운 속성이나 관계도 쉽게 추가할 수 있습니다. 또, 그래프 구조에 최적화된 Cypher 쿼리 언어를 지원해 복잡한 질의도 간결하게 작성할 수 있습니다. ③ 온톨로지: 지식 그래프의 설계도 GraphRAG의 성능을 높이기 위해서는 온톨로지(Ontology) 개념이 중요합니다. 온톨로지는 특정 도메인(예: 금융, 의료, 제조 등)의 지식을 체계적으로 표현하기 위한 규칙과 어휘의 집합으로, 지식 그래프의 설계도이자 스키마 역할을 합니다. 온톨로지에서는 어떤 종류의 개체가 존재하는지, 각 개체가 어떤 속성을 가지는지, 그리고 개체 간에 어떤 관계가 가능한지를 미리 정의합니다. 예를 들어 ‘인물’, ‘회사’, ‘프로젝트’ 같은 개체가 있고, 인물은 이름과 직책이라는 속성을 가지며, 회사에 소속되거나(WORKS_FOR) 프로젝트를 관리하는(MANAGES) 관계를 맺을 수 있습니다. Neo4j가 흩어진 정보를 연결해 ‘관계 지도’를 만든다면, 온톨로지는 그 지도에 쓰인 기호의 의미와 규칙을 정의하는 범례 역할을 합니다. GraphRAG은 이 지도와 범례를 함께 활용해 복잡한 질문에 대해 신뢰도 높은 경로를 찾아냅니다. GraphRAG은 복잡한 관계를 기반으로 한 질의나 설명 가능한 추론이 필요한 환경에서 특히 유용합니다. 예를 들어 기업 조직도나 공급망처럼 개체 간 연결이 많은 구조를 분석하거나, 금융·의료·법률 분야처럼 근거 자료의 추적이 필요한 경우에 적합합니다. 또한 여러 단계의 논리적 연결을 따라가야 하는 복합 질의나, 이미 도메인 지식이 체계화된 산업군에서도 효과적으로 활용할 수 있습니다. 대표적인 활용 분야 금융/리스크/사기 탐지 : 거래 기록, 구성원, 회사 네트워크의 연관성을 분석한 위험 탐지 및 의사결정 법률/규제 : 조문·판례 간의 연결 관계를 활용한 사례 연구 및 근거 탐색 기업 내부 지식/연구 : 다양한 문서·연구자·프로젝트 간 연계를 통한 인사이트 도출 및 지능형 Q&A 고객 서비스 : 복잡한 문의에 대해 근거 기반의 신뢰성 높은 답변 제공 GraphRAG에 HCX-007 추론 모델을 사용해야 하는 이유 GraphRAG에 추론 모델을 사용하는 이유는 그래프 구조 속 관계적 의미를 정확히 이해하고 해석할 수 있기 때문입니다. 1. 뛰어난 추론 및 지시 이행 GraphRAG은 문서에서 엔터티와 관계를 추출해 그래프 형태로 저장합니다. 하지만 단순히 텍스트 유사도를 계산하는 방식만으로는 노드 간의 복잡한 연결이나 간접적인 의미 관계를 충분히 드러내기 어렵습니다. HCX-007 추론 모델은 이러한 한계를 넘어, 여러 노드 사이의 맥락적 연결을 이해하고 문장에 드러나지 않은 논리적 관계를 찾아낼 수 있기 때문에 더 풍부하고 정교한 응답을 제공합니다. 2. 신뢰성 있는 답변 생성 HCX-007 추론 모델은 단순히 답변을 제시하는 데 그치지 않고, 답이 도출된 과정을 함께 보여줄 수 있습니다. 사용자는 이 과정을 통해 결과의 근거를 확인할 수 있으며, 단순한 검색 기반 응답보다 논리적이고 신뢰도 높은 답변을 얻을 수 있습니다. 3. 고품질의 한국어 처리 능력 HCX-007추론 모델은 방대한 한국어 데이터를 기반으로 학습되어, 문맥의 미묘한 차이와 표현을 잘 이해합니다. 이를 통해 한국어로 작성된 전문 문서를 정확히 분석하고, 사용자의 질문 의도에 맞는 답변을 제공합니다. Reasoning 모델 + GraphRAG 구축 시나리오 전체 프로세스 개요 이 시스템은 PDF 문서에서 정보를 추출해 벡터와 그래프 두 형태로 Neo4j 데이터베이스에 저장합니다. 이후 사용자의 질문 의도에 따라 가장 적합한 검색 방식을 자동으로 선택해, 보다 정확한 답변을 생성합니다. 프로세스는 다음과 같은 흐름으로 구성됩니다. [데이터 입력] → 전처리 (PDF 추출 및 분할) → 임베딩 → 지식 그래프 추출 (LLM) / Vector Index 추출 → [Neo4j 저장] → 벡터 인덱스 구축 → 하이브리드 Q&A 파이프라인 → [답변 생성] 0. 라이브러리 세팅 GraphRAG 파이프라인을 구축하기 위해 필요한 필수 라이브러리를 먼저 설치합니다. 아래와 같이 requirements.txt 파일을 생성한 뒤, 필요한 패키지를 한 번에 설치할 수 있습니다. requests dotenv networkx ## numpy langchain langchain-community langchain-naver langchain-neo4j faiss-cpu ## neo4j pypdf 이 명령어를 실행하면, requirements.txt에 정의된 모든 라이브러리가 자동으로 설치됩니다. pip install -r requirements.txt 그 다음 Neo4j를 사용할 수 있는 환경 설정이 필요합니다. 빠르고 간단하게 진행하기 위해, Neo4j Sandbox에서 무료 인스턴스를 생성해 지식 그래프를 구축하겠습니다. 환경 구성이 완료되면, 본격적인 데이터 준비와 전처리 과정을 시작할 수 있습니다. 1. HCX-007 모델, 데이터 준비와 전처리 다음 단계에서는 PDF 파일에서 텍스트를 추출하고, LLM이 효율적으로 이해할 수 있도록 의미 단위(semantic chunk)로 분할합니다. PDF 텍스트 추출 pypdf 라이브러리를 활용해 PDF 문서의 모든 페이지에서 텍스트를 읽어옵니다. 의미 기반 분할 클로바 스튜디오의 문단 나누기 API (가이드 보기)를 사용해 문장 간 의미 유사도를 계산하고, 주제 단위로 문단을 자동 분리합니다. # --- PDF 처리 및 CLOVA Studio Segmentation 로직 --- def read_pdf_text(pdf_path: str) -> str: reader = PdfReader(pdf_path) texts = [p.extract_text() or "" for p in reader.pages] return "\n\n".join(texts).strip() def chunk_by_limit(text: str, limit: int) -> List[str]: parts = text.split("\n\n") batches, cur = [], "" for p in parts: add = p if not cur else ("\n\n" + p) if len(cur) + len(add) <= limit: cur += add else: if cur: batches.append(cur) cur = p if len(p) <= limit else "" if len(p) > limit: print(f"[WARN] A single paragraph of {len(p)} chars is longer than the limit {limit}, it will be chunked bluntly.") for i in range(0, len(p), limit): batches.append(p[i:i+limit]) if cur: batches.append(cur) return batches def clova_segment(text: str, **kwargs) -> List[str]: payload = { "text": text, "alpha": kwargs.get("alpha", -100), "segCnt": kwargs.get("segCnt", -1), "postProcess": kwargs.get("postProcess", True), "postProcessMaxSize": kwargs.get("postProcessMaxSize", 1000), "postProcessMinSize": kwargs.get("postProcessMinSize", 200), } retry, backoff_sec = 3, 1.5 for attempt in range(1, retry + 1): try: r = requests.post(SEG_ENDPOINT, headers=_auth_headers(), json=payload, timeout=TIMEOUT) data = r.json() if r.status_code == 200 and data.get("status", {}).get("code") == "20000": # API 응답에서 텍스트 리스트만 추출하여 반환 return ["\n".join(sents).strip() for sents in data["result"].get("topicSeg", []) if sents] raise RuntimeError(f"Segmentation API error: HTTP {r.status_code}, body={data}") except Exception as e: if attempt == retry: raise e time.sleep(backoff_sec * attempt) return [] def get_chunks_from_pdf(pdf_path: str) -> List[str]: """PDF 파일에서 텍스트를 읽고 CLOVA Studio Segmentation API로 의미 단위 청크를 생성합니다.""" print(f"Reading PDF: {pdf_path}") full_text = read_pdf_text(pdf_path) if not full_text: raise RuntimeError("Failed to extract text from PDF.") batches = chunk_by_limit(full_text, MAX_CHARS_FOR_SEGMENT) print(f"Total length={len(full_text):,} -> {len(batches)} batch(es) for segmentation.") all_chunks = [] for i, text_batch in enumerate(batches, 1): print(f"Segmenting batch {i}/{len(batches)} (length={len(text_batch):,})...") chunks_from_batch = clova_segment(text=text_batch) all_chunks.extend(chunks_from_batch) print(f" -> Got {len(chunks_from_batch)} chunks.") return all_chunks 2. 임베딩 및 벡터화 분할된 텍스트 청크를 벡터로 변환해 의미 기반 검색이 가능하도록 준비합니다. 이때 ClovaXEmbeddings를 사용해 각 청크의 의미를 고차원 벡터로 표현합니다. clova_embedder = ClovaXEmbeddings( model=“clir-emb-dolphin”, api_key=os.getenv("CLOVA_API_KEY") ) 3. (Neo4j 활용) 데이터 추출 및 지식 그래프 생성 LLM을 활용해 각 텍스트 청크에서 노드와 관계(Relationship를 추출하고, 이를 Neo4j에 저장하여 지식 그래프를 구축합니다. 이때 Pydantic으로 LLM의 출력 스키마를 미리 정의해 결과를 일관된 구조(JSON)로 받도록 하고, LangChain 체인을 통해 텍스트에서 구조화된 그래프 데이터를 추출하는 체인을 구성합니다. class Node(BaseModel): id: str = Field(description="노드(엔티티)의 이름 또는 ID") type: str = Field(description="엔티티의 타입 (예: Person, Organization, Concept)") class Relationship(BaseModel): source: Node = Field(description="관계의 시작 노드") target: Node = Field(description="관계의 끝 노드") type: str = Field(description="관계의 타입 (예: IS_A, WORKS_FOR, CONTAINS)") class KnowledgeGraph(BaseModel): nodes: List[Node] = Field(description="그래프의 모든 노드 리스트") relationships: List[Relationship] = Field(description="그래프의 모든 관계 리스트") def get_graph_extraction_chain(llm): parser = JsonOutputParser(pydantic_object=KnowledgeGraph) prompt = ChatPromptTemplate.from_messages([ ("system", """ 주어진 텍스트에서 핵심 엔티티(노드)와 그들 사이의 관계(엣지)를 식별해야 합니다. 추출된 모든 노드와 관계를 JSON 형식으로 출력하세요. 노드 타입은 'Person', 'Organization', 'Concept', 'Technology' 등 일반적인 용어를 사용하세요. 관계 타입은 'WORKS_FOR', 'DEVELOPS', 'RELATED_TO' 와 같이 대문자와 스네이크 케이스를 사용하세요. {format_instructions} """), ("human", "다음 텍스트에서 지식 그래프를 추출해줘:\n\n{input}") ]) prompt_with_format = prompt.partial( format_instructions=parser.get_format_instructions() ) return prompt_with_format | llm | parser Neo4j에 추출된 노드와 관계를 저장하고, 각 노드가 어떤 텍스트 청크에서 파생되었는지를 함께 연결해 원본 근거를 추적할 수 있도록 구조화합니다. def ingest_chunks_to_neo4j(chunks: List[str], graph: Neo4jGraph, graph_extraction_chain, embedder): graph.query("MATCH (n) DETACH DELETE n") print("Cleared existing data from Neo4j.") valid_chunks = [chunk for chunk in chunks if chunk and not chunk.isspace()] print(f"Total chunks: {len(chunks)}, Valid non-empty chunks: {len(valid_chunks)}") for i, chunk_text in enumerate(valid_chunks): print(f"--- Processing chunk {i+1}/{len(valid_chunks)} ---") print(" - Storing chunk for vector search...") chunk_embedding = embedder.embed_query(chunk_text) graph.query( "MERGE (c:Chunk {id: $chunk_id}) SET c.text = $text, c.embedding = $embedding", params={ "chunk_id": f"chunk_{i}", "text": chunk_text, "embedding": chunk_embedding } ) try: print(" - Extracting knowledge graph...") extracted_data = graph_extraction_chain.invoke({"input": chunk_text}) nodes_data = extracted_data.get("nodes", []) rels_data = extracted_data.get("relationships", []) if not (nodes_data or rels_data): print(" - No graph elements extracted.") continue extracted_graph = KnowledgeGraph(nodes=nodes_data, relationships=rels_data) for node_obj in extracted_graph.nodes: graph.query( "MERGE (n:`{type}` {{id: $id}})".format(type=node_obj.type), params={"id": node_obj.id} ) graph.query( """ MATCH (c:Chunk {{id: $chunk_id}}) MATCH (n:`{type}` {{id: $node_id}}) MERGE (c)-[:CONTAINS]->(n) """.format(type=node_obj.type), params={"chunk_id": f"chunk_{i}", "node_id": node_obj.id} ) for rel_obj in extracted_graph.relationships: graph.query( """ MATCH (s:`{st}` {{id: $sid}}) MATCH (t:`{tt}` {{id: $tid}}) MERGE (s)-[:`{rt}`]->(t) """.format(st=rel_obj.source.type, tt=rel_obj.target.type, rt=rel_obj.type), params={"sid": rel_obj.source.id, "tid": rel_obj.target.id} ) print(" - Knowledge graph stored successfully.") except Exception as e: continue print("\n✅ PDF chunks ingested into Neo4j.") 4. 벡터 인덱스 구축 및 Neo4j 세팅 의미 기반 검색을 위해 Neo4j 내에 벡터 인덱스를 생성하고, 이를 LangChain의 Neo4jVector와 연결합니다. 이 과정을 통해 임베딩된 텍스트 청크를 효율적으로 검색할 수 있는 환경이 구축됩니다. def setup_neo4j_vector_index(graph: Neo4jGraph, index_name: str, node_label: str, property_name: str, dimensions: int): try: graph.query(f"DROP INDEX {index_name} IF EXISTS") except Exception as e: print(f"Index drop failed: {e}") graph.query( f"CREATE VECTOR INDEX {index_name} FOR (n:{node_label}) ON (n.{property_name}) OPTIONS {{indexConfig: {{`vector.dimensions`: {dimensions}, `vector.similarity_function`: 'cosine'}}}}" ) print(f"Vector index '{index_name}' created.") 5. 질의응답 파이프라인 사용자의 질문 유형에 따라 그래프 탐색(Graph QA)과 벡터 검색(Vector QA) 중 최적의 방식을 자동으로 선택하는 하이브리드 Q&A 파이프라인을 구성합니다. 이 단계에서는 LangChain의 RetrievalQA와 GraphCypherQAChain을 각각 생성하고, 간단한 키워드 기반 라우팅 로직을 통해 두 체인을 유연하게 전환할 수 있도록 설계합니다. def get_hcx_llm(model_name: str = "HCX-007"): return ChatClovaX(model_name=model_name, temperature=0, request_timeout=120, api_key=CLOVA_API_KEY) def get_qa_llm(model_name: str = "HCX-007"): return ChatClovaX( model_name=model_name, temperature=0.3, request_timeout=120, api_key=CLOVA_API_KEY, thinking={"effort":"low"}, ) vector_qa_chain = RetrievalQA.from_chain_type( llm=llm_qa, chain_type="stuff", retriever=neo4j_vector_store.as_retriever() ) graph_chain = GraphCypherQAChain.from_llm( cypher_llm=llm_cypher, qa_llm=llm_qa, graph=graph, verbose=True, allow_dangerous_requests=True, cypher_prompt=CYPHER_GENERATION_PROMPT ) Cypher 기반 동적 프롬프트 설계 HCX-007 모델이 사용자의 질문을 정확한 Cypher 쿼리로 변환할 수 있도록, 그래프 스키마를 참고하는 프롬프트 템플릿을 설계합니다. CYPHER_GENERATION_TEMPLATE = """ Task: 너는 사용자의 질문을 Neo4j Cypher 쿼리로 변환하는 전문가이다. Instructions: - 제공된 그래프 스키마(Graph Schema)만을 사용하여 질문에 답하는 Cypher 쿼리를 생성해야 한다. - **오직 Cypher 쿼리만을 ```cypher ... ``` 코드 블록 안에 넣어서 반환해야 한다.** - 절대로 코드 블록 외부에 설명이나 주석 등 다른 어떤 텍스트도 포함해서는 안 된다. # --- 중요: Cypher 문법 규칙 --- # 1. 노드의 레이블을 얻으려면 `labels(node)[0]` 을 사용하라. # 2. 관계의 타입을 얻으려면, `MATCH` 절에서 관계에 변수(예: `-[r]->`)를 반드시 할당하고 `type(r)` 을 사용하라. # 3. MATCH 절에서 나중에 참조할 모든 노드와 관계에는 반드시 변수(예: `(n)`, `(m)`, `-[r]->`)를 할당해야 한다. # 익명 노드 `()` 나 익명 관계 `-[]->` 는 RETURN 절 등에서 사용할 수 없다. # 4. 쿼리의 중간 단계에서 변수를 다음으로 넘길 때는 `WITH`를 사용해야 한다. `RETURN`은 쿼리의 가장 마지막에 최종 결과를 출력할 때 오직 한 번만 사용해야 한다. # 5. 방향에 관계없이 모든 관계를 찾을 때는 화살표 없는 구문, 즉 `(n)-[r]-(m)` 을 사용해야 한다. 절대 `<-|->` 와 같은 잘못된 구문을 사용해서는 안된다. # # ❌ 잘못된 쿼리: MATCH (n)-[]->() RETURN type() # ✅ 올바른 쿼리: MATCH (n)-[r]->(m) RETURN type(r), labels(m) # # ❌ 잘못된 구조: MATCH (a)-[r1]->(b) RETURN b.id MATCH (b)-[r2]->(c) RETURN c.id # ✅ 올바른 구조: MATCH (a)-[r1]->(b) WITH b MATCH (b)-[r2]->(c) RETURN c.id # ❌ 잘못된 방향성 쿼리: MATCH (c)<-|->() # ✅ 올바른 방향성 쿼리: MATCH (c)-[r]-() Graph Schema: {schema} Question: {question} Final Answer (only the Cypher code block): """ CYPHER_GENERATION_PROMPT = PromptTemplate( input_variables=["schema", "question"], template=CYPHER_GENERATION_TEMPLATE ) 메인 실행 플로우 구현 while True: print("\n" + "="*50) query = input("질문을 입력하세요 (종료하려면 'exit' 입력): ") if query.lower() == 'exit': break try: # "누가", "몇 개", "관계" 등 구조적 질문 키워드가 포함되면 그래프 체인 사용 if any(keyword in query for keyword in ["누가", "몇 개", "어떤 관계", "연결"]): print("\n--- Graph Traversal (Structural QA) ---") result = graph_chain.invoke({"query": query}) print(f"답변: {result.get('result', '답변을 생성하지 못했습니다.')}") # 그 외의 일반적인 질문은 벡터 검색 체인 사용 else: print("\n--- Vector Search (RAG QA) ---") result = vector_qa_chain.invoke({"query": query}) print(f"답변: {result.get('result', '답변을 생성하지 못했습니다.')}") except CypherSyntaxError as e: print("\n[오류] 쿼리 생성 또는 실행 중 Cypher 문법 오류가 발생했습니다.") print("질문을 조금 더 명확하게 하거나 다른 방식으로 질문해보세요.") except Exception as e: print(f"\n[오류] 예측하지 못한 오류가 발생했습니다: {e}") print("다른 질문으로 다시 시도해주세요.") 질문 유형별 동작 예시 지금까지의 과정을 통해, GraphRAG가 질문의 유형에 따라 그래프 탐색과 벡터 검색을 유연하게 전환하며 추론하는 구조를 구현하는 과정을 살펴보았습니다. 아래 예시를 살펴보면, 질문의 유형에 따라 그래프 탐색과 벡터 검색이 어떻게 다르게 작동하는지 알 수 있습니다. GraphRAG 적용한 QA (Structured QA) Q. 보안 정책 주관 부서와 연결된 부서는 어디이며, 어떤 관계가 있는가? 일반적인 Vector 검색 적용한 QA (RAG QA) Q. 네이버의 정보보호 목표 달성을 위해 지켜야 할 기본 원칙은? 예를 들어, “보안 정책을 주관하는 부서와 연결된 부서는 어디인가?”처럼 관계를 파악해야 하는 질문에는 GraphRAG이 Neo4j 그래프를 탐색해 부서 간 연결 구조를 제시했습니다. 반면 “네이버의 정보보호 목표 달성을 위해 지켜야 할 기본 원칙은 무엇인가?”와 같은 서술형 질문에는 Vector 검색이 핵심 문장을 찾아 요약하는 데 더 효과적이었습니다. 즉, GraphRAG은 관계형 탐색이 필요한 질문과 일반 텍스트 기반 질의를 구분해 질문의 의도에 가장 적합한 방식으로 답변을 생성하는 구조를 갖추고 있습니다. 마무리 : GraphRAG의 효용성 GraphRAG는 복잡한 관계형 데이터를 다루거나 전문 지식의 근거를 명확히 제시해야 하는 환경에서 특히 유용합니다. 단순한 텍스트 검색으로는 찾기 어려운 데이터 간의 연결과 패턴을 파악할 수 있고, 답변의 근거가 되는 관계를 함께 제시해 신뢰도 높은 결과를 제공합니다. 여러 단계를 거치는 복합적인 질의에서도 그래프 탐색을 통해 논리적 일관성을 유지하고, 신뢰할 수 있는 답변을 제공합니다. 지금까지 GraphRAG 활용하는 방식을 살펴봤습니다. 단순한 검색을 넘어 데이터를 맥락과 의미로 이해하는 GraphRAG 방식을 직접 시도해보세요.
 - Yesterday
 - 
	seoultechKMC joined the community
 - 
	ming joined the community
 - 
	moonhyeok joined the community
 - Last week
 - 
	윤혜정 joined the community
 - 
	잘 동작한다니 다행입니다. 개발 시 이슈있으면 포럼이나 공식 문의 채널로 문의주시면 답변드리도록 하겠습니다. (편하신 채널로 문의주시면 됩니다.) 감사합니다.
 - 
	안녕하세요, 크롬 시크릿모드에서는 문제가 없는 것을 확인하고 크롬 데이터를 초기화했더니 request header origin 문제가 해결되어 localhost에서도 정상 작동합니다. 생각지도 못한 원인이었네요... 감사합니다!
 - 
	상세 설명 감사합니다. 제 PC 에서는 헤더 Origin 이 정상적으로 들어가있네요. 혹시 크롬 시크릿모드에서도 재현될까요? + 주변 개발자분들도 동일하게 재현되는지 확인해주시면 감사하겠습니다. 브라우저단에서 Origin 을 제거하는거라서 코드단에서 따로 제어할 수가 없네요.
 - 
	dokhanmom joined the community
 - Earlier
 - 
	안녕하세요 @ju_yb99 님, 요청 바디의 input type을 list<string>에서 string 타입으로 변경하시면 정상 동작할 것으로 보입니다. 감사합니다.
 - 
	아 넵 안녕하세요, 해결되지는 않아서 gl 관련 로컬 개발 시 127.0.0.1:3000으로 개발 중입니다. 로컬에서 3개 브라우저로 테스트해보았습니다. (크롬, 파이어폭스, 엣지) 3개 브라우저 중 "크롬"만 localhost:3000으로 접근하면 95% 비율로 GET http://oapi.map.naver.com/v3/props 요청에서 CORS가 발생합니다. 크롬 브라우저만 요청 헤더에 Origin값이 안실어져 있는데 이와 관련된 것 같기도 합니다. - 크롬 요청 헤더 스크린샷 - 파이어폭스 요청 헤더 스크린샷 - 엣지 요청 헤더 스크린샷
 - 
	안녕하세요. 공식 문의 채널로 문의주셨는데, 해결되셨을까요? 로컬에서 테스트 시 재현되지 않았습니다.
 - 
	시케이컴퍼니 joined the community
 - 
	지도판 스타일 정책은 네이버지도의 지도판과 동일한 형상을 따르는 것을 기준하고 있으며, 따라서 랜드마크 주기에 대해 현재는 미노출 처리하는 기능은 없습니다. 향후 개선 요소로 검토해보겠습니다.
 - 
	
gl 활성화 시 disableKineticPan 옵션이 강제로 해제되는 것 같습니다.
kkokko.jeong replied to dront's topic in 개발자 포럼: 묻고 답하기
GL 서브 모듈에서는 disableKineticPan, 관성 패닝이 기본적으로 활성화되어 있습니다. 수정해서 반영하도록 하겠습니다. 감사합니다.- 1 reply
 - 
	
- 1
 - 
					
						
					
							
					
						
					
				 
 
 - 
	안녕하세요. 현재는 랜드마커 마커 제거 API 는 따로 제공하지 않습니다. 내부검토 후 다시 공유드리도록 하겠습니다.
 - 
	iconfx joined the community
 - 
	https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=&&&&&&&submodules=gl 위와같이 스크립트를 불러온 후 gl 옵션 활성화 시 disableKineticPan 옵션이 적용되지 않는 것 같습니다.
 - 
	bic_dev joined the community
 - 
	LLM Evaluation, 왜 중요한가? LLM은 이제 단순한 텍스트 생성기를 넘어, RAG(Retrieval-Augmented Generation)나 Fine-tuning과 같은 기법을 통해 실제 서비스와 업무 환경에 맞게 커스터마이징되고 있습니다. 하지만 새로운 파이프라인을 설계하거나 파인튜닝을 적용했다고 해서, 성능이 항상 향상되는 것은 아닙니다. 실제로 개선 여부를 객관적으로 검증하는 일은 여전히 쉽지 않죠. 기존 벤치마크 점수로는 실제 현장의 요구를 충분히 반영하기 어렵고, 사람 평가만으로는 시간과 비용의 부담이 큽니다. 이 한계를 보완하기 위해 최근 주목받는 접근이 LLM-as-a-Judge입니다. 또 다른 LLM이 평가자의 역할을 맡아, 생성된 결과물의 품질을 판별하고 점수를 매기는 방식이죠. 이를 통해 대규모 평가를 자동화하고, 인간 평가가 지니는 주관성을 줄일 수 있습니다. 이 방식은 단순히 모델이 '잘 작동하는가'를 확인하는 수준을 넘어, 어떤 조건에서 강점을 보이고 어디에서 취약한지까지 체계적으로 파악할 수 있게 합니다. 최근 연구에 따르면, LLM-as-a-Judge는 단순한 평가 자동화를 넘어서 모델이 스스로 사고 과정을 평가하고 개선하는 ‘Reasoning-Centric’ 방향으로 발전하고 있습니다¹. 즉, 평가와 추론이 서로를 보완하며 모델의 사고 구조를 점점 더 정교하게 만들어가는 흐름입니다. (1) Gu, Jiawei, et al. 2024. "A Survey on LLM-as-a-Judge." https://arxiv.org/abs/2411.15594. CLOVA Studio에서는 이러한 평가 방식을 HyperCLOVA X 모델 API를 직접 활용해 구현할 수 있습니다. 생성된 결과를 자동으로 평가하고, 그 데이터를 다시 프롬프트 최적화나 파인튜닝 전략 조정, RAG 파이프라인 개선에 활용하는 것이죠. 왜 추론 모델로 평가해야 할까 LLM 성능 평가는 비추론 모델의 출력만으로는 한계가 있습니다. 답을 생성하는 데는 능숙하더라도, 그 답이 왜 맞거나 틀렸는지를 스스로 검증하지 못하기 때문이죠. 이 경우 결과의 일관성이 떨어지고, 평가 근거도 불명확해질 수 있습니다. 반면 HyperCLOVA X의 추론 모델 HCX-007은 결론에 이르기 전, 단계별 사고 과정을 전개하며 전제와 논리를 점검합니다. 이런 사고 과정 덕분에 평가 결과는 단순히 정답인지 아닌지를 판단하는 수준을 넘어, 더 깊이 있는 분석으로 이어집니다. 실제로, Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena 연구에서는 GPT-4와 같은 추론형 모델이 인간 평가자와 약 80 % 수준의 일치도를 보였다고 보고했습니다. 이는 LLM-as-a-Judge가 인간 선호(human preference)를 대체할 수 있을 만큼 신뢰도 높은 평가 체계로 작동할 수 있음을 보여줍니다. (2) Zheng, Liang, et al. 2023. “Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena.” https://arxiv.org/pdf/2306.05685. 추론 모델을 평가자로 쓸 때의 세부 강점은 다음과 같습니다. 논리 일관성 검증 정답 여부만 확인하는 데 그치지 않고, 사고 과정을 기반으로 논리적 타당성까지 함께 검증합니다. 평가 결과에는 판정 근거까지 함께 남기기 때문에, 왜 그런 결과가 나왔는지도 명확히 파악할 수 있습니다. 세분화된 기준별 평가 Clarity, faithfulness, accuracy 등 항목별로 일관된 스코어링이 가능해, 같은 답변이라도 어떤 부분이 강점이고, 어디서 약한지 명확히 구분해 보여줄 수 있습니다. 설명 가능한 근거 제시 점수뿐 아니라 평가 근거와 판단 과정이 함께 제공되어, 결과의 신뢰도를 높입니다. 부분 정답 처리 모호하거나 부분적으로만 맞는 응답도, 합리적인 기준에 따라 부분 점수와 보완 코멘트를 함께 제공합니다. 즉, HCX-007은 ‘스스로 사고하며 분석하는 평가자’입니다. 이를 평가 파이프라인에 도입하면, 점수의 신뢰성과 결과에 대한 이해도가 높아지고, 이후 모델 개선 사이클을 더 빠르게 반복할 수 있습니다. 이번 글에서는 CLOVA Studio의 HCX-007 추론 모드로 LLM 평가를 설계하고 적용하는 방법을 구체적으로 살펴보겠습니다. LLM Evaluation 시나리오 전체 프로세스 개요 아래 다이어그램은 이번 글에서 다루는 LLM 평가 워크플로우를 단계별로 정리한 것입니다. 이 프로세스는 단순히 한 번의 점수를 계산하는 데 그치지 않고, 평가 → 개선 → 재평가의 순환 구조를 통해 모델을 지속적으로 고도화하는 것을 목표로 합니다. 단계별 설명에 들어가기 전에, 우선 평가에 사용할 QA 데이터셋이 필요합니다. 아직 준비되지 않았다면 부록에 안내된 절차를 참고해 손쉽게 생성할 수 있습니다. 데이터셋은 Excel(.xlsx) 또는 CSV(.csv) 형식을 모두 지원하며, 두 개의 열; 질문(question) 과 답변(answer)로 구성되어 있어야 합니다. 1. 평가 기준 정의하기 LLM-as-a-Judge 방식을 적용하려면, 먼저 평가 기준을 명확히 설정해야 합니다. 아래는 LLM의 응답을 평가하기 위해 HCX-007 모델에 전달할 시스템 프롬프트의 예시입니다. JUDGE_SYSTEM_PROMPT = """당신은 LLM 응답을 평가하는 심판(LLM-as-a-Judge)입니다. [역할] - 주어진 질문(question)과 응답(answer)을 기준으로, 아래 항목별로 0~5의 정수 점수를 부여하고 간단한 근거를 제시합니다. - 외부 지식이 필요하더라도, 질문과 응답에 드러난 정보와 일반 상식 범위를 벗어나 추정하지 않습니다. (추정 시 근거 부족으로 감점) [원칙] # 원하는 모델 방향성에 맞게 조정 가능 - 점수는 반드시 0~5의 **정수(5가 최상)** 로만 부여합니다. (소수점, 범위 점수 불가) - 근거(rationale)는 **핵심만 1~2문장**으로 작성하며, 불필요한 배경 설명은 제외합니다. - 응답이 질문에 사실상 답하지 못했거나 형식이 명백히 어긋나면, 대부분의 항목에서 낮은 점수를 부여합니다. [평가 기준] 1) clarity (명료성) - 문장이 불필요하게 길거나 중복되지 않는가? - 문장 구조가 논리적이며, 주어·서술어 관계가 명확한가? - 전문용어는 처음 등장 시 짧은 정의나 설명이 포함되어 있는가? 2) faithfulness (충실성/일관성) - 질문의 의도와 제약을 정확히 반영하고 있는가? - 응답의 논리 구조(주장–근거–결론)가 앞뒤로 일관되는가? - 질문의 맥락을 벗어난 추가 정보나 왜곡된 해석은 없는가? - 가정이나 전제가 있다면 명시되어 있는가? (없는데 단정하면 감점) 3) accuracy (정확성) - 사실 관계(정의, 수치, 인과 관계 등)가 일반 지식과 일치하는가? - 문맥상 잘못된 용어나 개념 오류가 없는가? - 출처나 기준이 불분명한 단정적 표현(예: “항상 그렇다”, “절대 아니다”)은 없는가? - 불확실한 경우, 보수적으로 표현하거나 근거 부족을 명시하는가? 4) completeness (충족성) - 질문의 모든 요구사항(하위 질문, 제약, 출력 형식 등)을 빠짐없이 충족하는가? - 예시, 단계, 코드, 설명 등 필요한 구성 요소가 포함되어 있는가? - 답변이 “요청된 범위 전체”를 포괄하되, 불필요한 세부 내용으로 흐트러지지 않는가? - 복수 항목을 요구했을 경우 모두 명시적으로 다뤘는가? 5) relevance (관련성) - 응답이 질문의 주제에 직접적으로 관련되어 있는가? - 문장 또는 단락이 주제에서 벗어나지 않고 논리적으로 연결되는가? - 잡담, 반복, 불필요한 부연 설명이 없는가? [감점 가이드(예시)] - 근거 없는 단정/모순 → faithfulness, accuracy에서 감점 - 요구사항 누락/형식 불일치 → completeness에서 감점 - 장황함/모호어 다수 → clarity, relevance에서 감점 [출력(JSON 전용)] - 아래 키만 포함하고, 값은 정수(0~5) 또는 문자열로 출력합니다. - 절대 JSON 외 텍스트를 출력하지 않습니다. (설명, 접두사, 코드블록 금지) { "clarity": 0, "faithfulness": 0, "accuracy": 0, "completeness": 0, "relevance": 0, "rationale": "간단한 이유(1~2문장)" } """ 위 기준은 모델의 응답을 평가하기 위한 기본 프레임워크로, 다섯 가지 항목을 중심으로 점수를 산출합니다. 이러한 metric 설계는 단순히 정답 여부를 판단하는 데서 그치지 않고, 모델이 어떤 방식으로 답을 도출했는지까지 살펴볼 수 있도록 해줍니다. 이 기준은 고정된 규칙이 아닙니다. 평가 대상 모델의 특성과 적용 영역에 따라 자유롭게 확장하거나 수정할 수 있으며, 출력 형식은 JSON으로 고정해 평가 결과를 구조적으로 저장하고 분석할 수 있습니다. 결국 중요한 것은 평가 항목을 명확히 정의하고, 이를 일관된 방식으로 적용할 수 있는 체계를 갖추는 것입니다. 이러한 설계가 뒷받침될 때, LLM-as-a-Judge는 신뢰성 있는 평가 도구로 기능할 수 있습니다. 2. 평가 항목별 비중을 적용해 종합 점수 계산하기 평가 항목을 정의했다면, 이제 각 항목의 점수를 합산해 하나의 종합 점수를 계산해야 합니다. 이때 중요한 것은 단순 평균이 아니라, 항목별 중요도(비중)를 반영하는 것입니다. 예를 들어, 정확성(accuracy)이나 충실성(faithfulness)이 상대적으로 더 중요하다면 이 항목들의 비중을 높게 설정할 수 있습니다. 아래 함수는 다섯 가지 평가 항목(clarity, faithfulness, accuracy, completeness, relevance)에 대해 미리 정의한 비중을 적용하고, 그 합이 1이 되도록 정규화하여 최종 점수를 계산합니다. 앞 단계에서 평가 기준을 수정하거나 새로운 metric을 추가했다면, 이 함수 또한 그에 맞게 함께 조정해야 합니다. def calculate_overall_score(scores: dict) -> float: """가중 평균으로 전체 점수 계산""" raw_weights = { 'clarity': 1.0, 'faithfulness': 1.2, 'accuracy': 1.5, 'completeness': 1.0, 'relevance': 1.3 } weight_sum = sum(raw_weights.values()) weights = {m: w / weight_sum for m, w in raw_weights.items()} total_score = sum(scores[metric] * weight for metric, weight in weights.items() if metric in scores) return round(total_score, 2) 이 단계는 개별 평가 항목을 하나의 종합 성능 지표로 통합하는 과정입니다. 이렇게 계산된 종합 점수는 이후 단계에서 모델 간 성능 비교나 개선 효과 검증에 활용할 수 있습니다. 3. HCX-007 모델 호출 후 QA 데이터셋 평가하기 이제 앞서 정의한 평가 기준과 가중치 계산 함수를 실제로 적용해보겠습니다. 준비된 QA 데이터셋(CSV 또는 Excel 파일)을 불러와, 각 질문과 답변을 HCX-007 모델에 입력하면 LLM-as-a-Judge가 각 항목별 점수를 매깁니다. 평가 결과에는 항목별 점수뿐 아니라 간단한 평가 근거도 함께 포함되며, 모든 결과는 CSV 파일로 저장됩니다. API 키는 코드에 직접 노출하지 않고 .env 파일로 관리합니다. 아래와 같이 환경 변수를 설정해 두면, 실행 시 안전하게 키를 불러올 수 있습니다. CLOVA_API_KEY= {api 키 입력} 환경 변수(.env 파일)를 불러오기 위해 python-dotenv 패키지가 필요합니다. 아직 설치하지 않았다면, 아래 명령을 실행해 주세요. (Python 3 환경 필요) pip3 install python-dotenv 이후 필요한 라이브러리를 임포트하고, HCX-007을 호출하는 함수를 작성합니다. 오류가 발생하면 각 항목 점수를 0으로 초기화하고 실패 사유를 함께 반환하도록 처리합니다. import os, json, time import pandas as pd import requests from dotenv import load_dotenv # 환경 변수 로드 load_dotenv() CLOVA_API_KEY = os.getenv("CLOVA_API_KEY") CHAT_URL = "https://clovastudio.stream.ntruss.com/v3/chat-completions/HCX-007" BASE_HEADERS = { "Content-Type": "application/json", "Authorization": f"Bearer {CLOVA_API_KEY}" } def call_hcx007_judge(question: str, answer: str, timeout: int = 30) -> dict: """HCX-007 모델로 응답 평가""" user_content = f"[질문]\n{question}\n\n[응답]\n{answer}\n\n위 기준으로 평가해 주세요. JSON만 출력하세요." payload = { "messages": [ {"role": "system", "content": JUDGE_SYSTEM_PROMPT}, {"role": "user", "content": user_content} ] } try: resp = requests.post(CHAT_URL, headers=BASE_HEADERS, data=json.dumps(payload), timeout=timeout) resp.raise_for_status() data = resp.json() content = data["result"]["message"]["content"] scores = json.loads(content.strip()) # 점수를 정수로 변환 for k in ["clarity", "faithfulness", "accuracy", "completeness", "relevance"]: scores[k] = int(scores.get(k, 0)) scores["rationale"] = str(scores.get("rationale", "")).strip() return scores except Exception as e: return { "clarity": 0, "faithfulness": 0, "accuracy": 0, "completeness": 0, "relevance": 0, "rationale": f"evaluation_failed: {type(e).__name__}" } 아래 함수는 평가를 실행하고 결과를 CSV 파일로 저장하는 역할을 합니다. 출력은 지정한 컬럼 순서(desired_cols)에 맞춰 정리됩니다. def run_evaluation(input_path: str, output_csv: str): """Q&A 데이터셋 평가 실행""" # 데이터 로드 df = pd.read_csv(input_path) if input_path.endswith(".csv") else pd.read_excel(input_path) results = [] for i, row in df.iterrows(): question, answer = row["question"], row["answer"] # API 호출로 평가 scores = call_hcx007_judge(question, answer) overall = calculate_overall_score(scores) results.append({ **scores, "question": question, "answer": answer, "overall": overall }) print(f"진행: {i+1}/{len(df)}") print(scores) print(f"완료: 전체 점수 {overall}") time.sleep(0.3) # API 호출 제한 방지 # 결과 저장 desired_cols = [ "question", "answer", "clarity", "faithfulness", "accuracy", "completeness", "relevance", "rationale", "overall" ] result_df = pd.DataFrame(results, columns=desired_cols) result_df.to_csv(output_csv, index=False, encoding='utf-8-sig') print(f"평가 완료 → {output_csv}") 4. 평가 결과 정리 및 실행하기 평가가 완료되면, 결과를 정리하고 검토하는 단계가 필요합니다. 이 과정에서는 각 항목별 평균 점수와 전체 평균(overall) 을 계산해 출력하며, 이를 통해 모델의 전반적인 성능과 개선이 필요한 부분을 한눈에 파악할 수 있습니다. def summarize_results(csv_path: str): """평가 결과 요약""" df = pd.read_csv(csv_path) metrics = ["clarity", "faithfulness", "accuracy", "completeness", "relevance", "overall"] avg_scores = {m: round(df[m].mean(), 2) for m in metrics if m in df.columns} print("\n=== 평가 결과 요약 ===") for metric, score in avg_scores.items(): print(f"{metric}: {score}") return avg_scores 아래는 전체 실행 코드입니다. input_file에는 미리 준비한 QA 데이터셋 경로를, output_file에는 저장할 결과 파일명을 지정합니다. if __name__ == "__main__": input_file = "rag_qa_pairs.csv" output_file = "evaluation_results.csv" # 평가 실행 run_evaluation(input_file, output_file) # 결과 요약 summarize_results(output_file) 코드를 실행하면 각 질문별 평가 결과와 전체 요약이 함께 출력되며, 이 과정을 통해 모델의 강점과 약점을 수치로 확인할 수 있습니다. 진행: 1/25 {'clarity': 4, 'faithfulness': 5, 'accuracy': 5, 'completeness': 5, 'relevance': 5, 'rationale': '응답은 Mistral AI가 참여 중인 다양한 산업 분야를 구체적 사례와 함께 체계적으로 나열해 질문 요구사항을 완벽히 충족함. 각 산업별 세부 적용 사례를 명시함으로써 전문성과 관련성을 높였으며, 정보의 흐름이 자연스럽고 논리적임.'} 완료: 전체 점수 4.83 . . . === 평가 결과 요약 === clarity: 3.36 faithfulness: 3.2 accuracy: 2.8 completeness: 3.36 relevance: 3.32 overall: 3.18 평가 결과는 자동으로 같은 폴더 내에 evaluation_results.csv 파일로 저장됩니다. 아래는 결과 파일의 일부 예시입니다. 5. 모델 개선 후 재평가 지금까지는 모델의 응답을 평가하고 점수를 산출하는 과정을 살펴보았습니다. 하지만 평가의 목적은 단순히 현재 성능을 확인하는 데 그치지 않습니다. 실험을 통해 개선 방향을 탐색하고, 동일한 평가 절차를 반복하며 모델을 점진적으로 고도화하는 것이 핵심입니다. 모델 개선은 여러 수준에서 시도할 수 있습니다. 다음은 성능 향상을 위한 대표적인 접근 방식입니다. 프롬프트 설계 수정 질문의 목적을 더 명확히 하거나 출력 형식을 고정해 응답의 일관성을 높입니다. RAG 파이프라인 개선 임베딩 모델, 청킹 전략, 컨텍스트 구성 방식을 조정해 응답의 충실성과 정확성을 개선합니다. 모델 변경, 모델 설정 변경 모델을 변경하거나 temperature, top-p 등의 파라미터를 조정해 응답의 다양성과 안정성을 제어합니다. 파인튜닝 (Fine-tuning) 특정 도메인에 대한 파인튜닝을 수행하면, 해당 도메인에 맞는 응답 형식과 용어를 학습하여 과업 적합성을 높일 수 있습니다. 이러한 개선을 적용한 뒤에는 다시 QA 데이터셋을 평가하는 단계로 돌아가 동일한 절차를 반복해야 합니다. 그래야 변경이 실제로 성능 향상으로 이어졌는지, 혹은 단순한 변동에 불과한지를 객관적으로 검증할 수 있습니다. 특히, 평가 항목, 비중, 데이터셋은 동일한 조건으로 유지해야 합니다. 그래야 전후 결과를 공정하게 비교하고, 개선 효과를 정확히 판단할 수 있습니다. 또한 평균 점수뿐 아니라 항목별 점수와 평가 근거(reasoning) 를 함께 살펴보면, 어떤 부분이 개선되었고 어떤 부분이 여전히 보완이 필요한지도 구체적으로 파악할 수 있습니다. 마무리 모델 평가의 목적은 단순히 점수를 매기는 데 있지 않습니다. 평가를 통해 얻은 데이터는 모델이 어떤 조건에서 강점을 보이고, 어떤 상황에서 한계를 드러내는지를 구체적으로 보여줍니다. 이 과정을 반복하면 프롬프트 설계나 파이프라인 구성을 보다 근거 있게 조정할 수 있습니다. 특히 하이퍼클로바X 추론 모델(HCX-007)을 활용하면, 단순 정답 판별을 넘어 사고 과정 기반의 분석적 평가가 가능해집니다. 이는 모델의 개선 방향을 보다 명확히 설정하고, 평가 프로세스 전체의 신뢰도와 효율성을 함께 높여줄 수 있죠. 결국 중요한 것은 평가와 개선을 끊김 없이 이어가는 것입니다. 이러한 반복적 평가와 분석이 쌓이면, LLM의 성능과 한계를 더 구체적으로 이해할 수 있고, 그 위에서 보다 안정적인 개선 전략을 세울 수 있을 것입니다. (부록) LLM을 활용하여 QA 데이터셋 준비하는법 모델 평가를 진행하려면 질문–답변(QA) 데이터셋이 필수입니다. 하지만 이를 일일이 수작업으로 만들기에는 시간과 비용이 많이 들죠. 이때 LLM을 활용하면 문서를 기반으로 자동으로 질문을 생성해 손쉽게 평가용 데이터셋을 구축할 수 있습니다. 아래에서는 PDF 문서를 입력받아 HCX-005 모델로 질문을 만들고, CSV로 정리한 뒤 자동으로 답변까지 생성하는 간단한 워크플로우를 소개합니다. 1. HCX-005 모델 불러오기 코드를 실행하기 전에, 먼저 필요한 모듈을 설치해야 합니다. 터미널에서 아래 명령어를 입력해주세요. pip3 install python-dotenv pypdf 이후, 질문 생성을 담당할 모델인 HCX-005를 호출합니다. API 키는 보안을 위해 .env 파일에 저장해두세요. from dotenv import load_dotenv import os import requests load_dotenv() CLOVA_API_KEY = os.getenv("CLOVA_API_KEY") CHAT_URL = "https://clovastudio.stream.ntruss.com/v3/chat-completions/HCX-005" def _auth_headers(): return { "Content-Type": "application/json; charset=utf-8", "Authorization": f"Bearer {CLOVA_API_KEY}", } def generate_hcx005(messages, temperature=0.7, max_tokens=2000): payload = { "messages": messages, "temperature": temperature, "topP": 0.9, "maxTokens": max_tokens, "repeatPenalty": 1.05 } r = requests.post(CHAT_URL, headers=_auth_headers(), json=payload, timeout=60) data = r.json() return data["result"]["message"]["content"].strip() .env 파일에는 아래와 같이 환경 변수를 정의합니다. CLOVA_API_KEY= {발급받은_키} 2. PDF에서 텍스트 추출하기 pypdf 라이브러리를 사용해 PDF 본문을 텍스트로 변환합니다. 단, 표나 이미지는 추출되지 않을 수 있으므로 필요에 따라 클로바 OCR을 함께 활용해보세요. (클로바 OCR 확인하기↗) from pypdf import PdfReader def read_pdf_text(pdf_path: str) -> str: reader = PdfReader(pdf_path) texts = [] for page in reader.pages: text = page.extract_text() or "" texts.append(text) return "\n\n".join(texts).strip() 이렇게 하면 문서 전체의 텍스트를 하나의 문자열로 가져올 수 있습니다. 3. 질문 생성하기 긴 문서를 그대로 HCX-005 모델에 입력하면 토큰 한도를 초과할 수 있습니다. 따라서 문서를 일정 크기로 나누고, 각 chunk 마다 일정 개수의 질문을 생성하는 방식을 사용합니다. 이 방법을 적용하면 긴 PDF 문서도 안정적으로 처리할 수 있습니다. def split_text(text: str, chunk_size: int = 10000) -> list: #chunk의 사이즈는 자유롭게 변경 가능 return [text[i:i+chunk_size] for i in range(0, len(text), chunk_size)] 위 함수는 입력된 긴 문자열을 chunk_size 단위로 나누어 리스트 형태로 반환합니다. 기본값은 10,000자로, 한 번에 모델이 처리하기에 비교적 안정적인 크기입니다. 다만 실제로는 문서 길이와 모델의 토큰 한도를 고려해 적절히 조정하는 것이 좋습니다. 이제 각 chunk 별로 HCX-005 모델에 질문 생성을 요청합니다. def generate_questions_from_pdf(pdf_path: str, num_questions: int = 5) -> list: print("PDF chunking 중...") full_text = read_pdf_text(pdf_path) chunks = split_text(full_text, chunk_size=10000) # 전체 질문 수를 chunk 수로 나누어 분배 questions_per_chunk = max(1, num_questions // len(chunks)) remaining_questions = num_questions % len(chunks) print("질문 생성 중...") all_questions = [] for idx, chunk in enumerate(chunks, start=1): # 마지막 chunk에 나머지 질문 추가 current_questions = questions_per_chunk + (remaining_questions if idx == len(chunks) else 0) prompt = f"""다음 문서 내용을 기반으로 {current_questions}개의 질문을 생성해주세요. 문서 내용 (chunk {idx}): {chunk} 다음 형식으로 질문을 작성해주세요: Q: 질문내용1 Q: 질문내용2 Q: 질문내용3 """ messages = [ {"role": "system", "content": "당신은 평가용 질문을 생성하는 전문가입니다."}, {"role": "user", "content": prompt} ] response = generate_hcx005(messages, temperature=0.8, max_tokens=2000) for line in response.strip().split("\n"): line = line.strip() if line.startswith("Q:") or line.startswith("질문:") or line.startswith("- "): question = line.replace("Q:", "").replace("질문:", "").replace("- ", "").strip() if question and len(question) > 10: all_questions.append(question) print(f"{len(all_questions)}개의 질문 생성 완료") return all_questions 생성할 질문의 총 개수는 main 실행 파트에서 지정할 수 있습니다. 이 방식을 사용하면 문서 길이에 관계없이 원하는 규모의 질문셋을 만들 수 있습니다. 4. 생성된 질문을 CSV파일로 저장하기 생성된 질문은 이후 평가 파이프라인에서 바로 활용할 수 있도록 CSV 파일로 저장합니다. 저장된 CSV 파일에는 question 컬럼 하나만 포함됩니다. 문서 길이와 생성할 질문 수에 따라 질문 생성 및 저장 과정에 다소 시간이 걸릴 수 있습니다. import csv def save_questions_to_csv(questions: list, output_csv: str): with open(output_csv, "w", newline="", encoding="utf-8") as csvfile: fieldnames = ["question"] writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() for question in questions: writer.writerow({"question": question}) #코드 실행 부분 if __name__ == "__main__": pdf_file = "{pdfname}.pdf" #사용할 파일명 questions = generate_questions_from_pdf(pdf_file, num_questions=25) #생성할 총 질문 수 save_questions_to_csv(questions, "rag_questions.csv") #저장할 csv 파일명 5. CSV 파일에 모델 응답 결과 추가하기 앞 단계에서 생성한 CSV 파일에는 question 열만 포함되어 있습니다. 이제 각 질문에 대해 평가 대상 모델이 답변을 생성하도록 하고, 그 결과를 answer 열에 추가하여 최종 평가용 QA 데이터셋을 완성합니다. 원하는 형태의 답변이 나오도록 시스템 프롬프트를 조정할 수 있으며, 생성된 모델 응답은 answer 변수에 저장됩니다. def add_answers_to_csv(input_csv: str, output_csv: str): import pandas as pd df = pd.read_csv(input_csv) print(f"{len(df)}개 질문에 대한 답변 생성 중...") answers = [] for idx, question in enumerate(df["question"], 1): messages = [ {"role": "system", "content": "당신은 도움이 되는 AI 어시스턴트입니다. 질문에 대해 정확하고 상세한 답변을 제공해주세요."}, {"role": "user", "content": question} ] answer = #평가하고자 하는 모델로 응답 생성 answers.append(answer) df["answer"] = answers df.to_csv(output_csv, index=False, encoding="utf-8") print(f"{len(answers)}개 답변 생성 완료 → {output_csv}") 위 함수를 실행하려면 main 함수에 아래 두 줄을 추가해 수정합니다. if __name__ == "__main__": import sys #새로 추가 되는 두 줄 if len(sys.argv) > 1 and sys.argv[1] == "answers": # 답변 생성 add_answers_to_csv("rag_questions.csv", "rag_qa_pairs.csv") #input 파일명과 결과값이 저장될 파일명 else: # PDF에서 질문 생성 pdf_file = "{pdfname}.pdf" questions = generate_questions_from_pdf(pdf_file, num_questions=25) save_questions_to_csv(questions, "rag_questions.csv") 답변만 생성하고자 할 경우, 터미널에서 다음 명령어를 실행하세요. 이때 {파일명}은 실제 실행할 파이썬 파일 이름으로 변경하면 됩니다. python3 {파일명}.py answers 지금까지 살펴본 과정을 통해 PDF에서 질문을 자동으로 생성하고, 이를 CSV로 정리한 뒤 모델 응답까지 추가하여 QA 평가용 데이터셋을 완성할 수 있습니다. 이제 이 데이터셋을 활용해 LLM-as-a-Judge 평가를 직접 실행해 봅시다.
- 
	
- 1
 - 
					
						
					
							
					
						
					
				 
 - 
	
		
- llm evaluation
 - llm-as-a-judge
 - 
					(and 1 more) 
					
Tagged with:
 
 
 - 
	
 - 
	myd joined the community
 - 
	안녕하세요. Spring AI - OpenAI를 통해 임베딩 관련 코드를 테스트 중에 에러가 발생해서 문의드립니다. 아래와 같이 Spring AI 문서를 참고해 OPEN_API_KEY, OPENAI_BASE_URL 등 필요한 값을 OpenAI 호환 가이드 문서를 참고해 변경한 상태입니다. spring: ai: openai: api-key: ${CLOVA_API_KEY} base-url: ${CLOVA_BASE_URL:https://api.openai.com} chat: completions-path: ${CHAT_COMPLETIONS_PATH:/v1/chat/completions} options: model: ${CLOVA_CHAT_MODEL:gpt-4o-mini} embedding: embeddings-path: ${EMBEDDINGS_PATH:/v1/embeddings} options: model: ${CLOVA_EMBEDDING_MODEL:text-embedding-3-small} dimensions: 1024 encoding-format: float 이를 통해 임베딩 요청을 보내면 다음과 같은 에러가 발생합니다. 2025-10-24T19:44:28.132+09:00 DEBUG 39844 --- [airoad-backend] [nio-8080-exec-1] o.s.web.client.DefaultRestClient : Writing [EmbeddingRequest[input=[장소명: 홍대 거리 주소: 서울특별시 마포구 양화로 일대 설명: 젊은 사람들이 자주 모이는 홍대 거리에서 맛있는 맛집과 함께 즐거운 여가를 보내세요 운영 시간: 매장별 상이 (대부분 12:00-02:00) 휴무일: 매장별 상이], model=bge-m3, encodingFormat=float, dimensions=1024, user=null]] as "application/json" with org.springframework.http.converter.json.MappingJackson2HttpMessageConverter 2025-10-24T19:44:28.440+09:00 WARN 39844 --- [airoad-backend] [nio-8080-exec-1] o.s.a.r.a.SpringAiRetryAutoConfiguration : Retry error. Retry count: 1, Exception: HTTP 400 - {"error":{"message":"Invalid parameter: convert error","code":"40001"}} org.springframework.ai.retry.NonTransientAiException: HTTP 400 - {"error":{"message":"Invalid parameter: convert error","code":"40001"}} 디버깅을 통해 실제 요청 값을 확인하면 아래와 같습니다. API 문서에서 확인한 결과 40001 코드가 나오면 요청 파라미터에 문제가 있는 걸로 보이는데 지원되지 않는 user 필드로 인해 실패하는 건지 문의드리고 싶습니다.
 - 
	안녕하세요, 네이버 지도 사용하여 개발하고 있습니다. 웹서비스 url에 위와 같이 로컬 개발 주소를 등록했고, 서브모듈 gl을 같이 import 하지 않았을 때에는 인증이 잘 되나, 아래와같이 submodules=gl 포함 시 인증이 실패합니다. 근데 또 신기하게 http://localhost:3000가 아니라 http://127.0.0.1:3000으로 들어가면 잘 되더라구요 ㅠ <script src="https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=********&submodules=gl" strategy="beforeInteractive" />
 - 
	안녕하세요, @프란군 님. TotalToken 안에는 추론에 사용된 토큰이 함께 포함되어있습니다. 즉, 추론 토큰은 나중에 따로 금액 산정되는 것이 아니라, 합계에 포함된 토큰으로 산정됩니다. 감사합니다.
 - 
	추론 토큰 산정치를 그래프화 해야하는 과정 중 질문 드립니다. API의거하여 예시기준으로 살펴보다 보면 총 토큰수(totalTokens) : 689 생성 토큰(completionTokens) : 631 이 TotalToken안에 ThinkingToken이 포함된 값으로 봐도 되나요? -- 풀이해보면 입력토큰 : 58 생성토큰 : 265 추론토큰 : 366 토큰합계 : 689 이게 아니면 총 토큰수 합계 689 + 추론토큰(366) 으로 나중에 금액이 산정되는건지 총 토큰수에 포함해서 나오는지 확인이 필요해서 질문드립니다. -- { "status": { "code": "20000", "message": "OK" }, "result": { "message": { "role": "assistant", "content": "부분집합의 개수는 ...", "thinkingContent": "오늘 사용자가 물어본 문제는 ..." }, "finishReason": "stop", "created": 1753362971 "seed": 1561390649, "usage": { "promptTokens": 58, "completionTokens": 631, "totalTokens": 689, "completionTokensDetails": { "thinkingTokens": 366 } } result.usage Object - 토큰 사용량 result.usage.completionTokens Integer - 생성 토큰 수 result.usage.promptTokens Integer - 입력(프롬프트) 토큰 수 result.usage.totalTokens Integer - 전체 토큰 수 생성 토큰 수+입력 토큰 수 result.usage.completionTokensDetails Object - 생성 토큰 수 관련 추가 정보 result.usage.completionTokensDetails.thinkingTokens Integer - 추론 토큰 수
 - 
	들어가며 CLOVA Studio에서는 목적에 따라 다양한 HyperCLOVA X 모델을 선택해 활용할 수 있습니다. 그중 HCX-007은 복잡한 사고와 정밀한 분석이 필요한 과업에 특화된 ‘추론(Reasoning)’ 모델입니다. 프롬프팅은 모델의 성능을 이끌어내는 가장 중요한 요소입니다. 같은 질문이라도 어떤 방식으로 지시하느냐에 따라, 답변의 깊이와 품질은 크게 달라집니다. HCX-007은 추론과 비추론을 모두 지원하는 하이브리드 구조의 모델입니다. 즉, 단순 질의응답부터 복잡한 사고 과정을 요구하는 문제 해결까지, 프롬프트 설계에 따라 폭넓게 활용할 수 있습니다. 그중에서도 추론 모드는 기존 모델과 접근 방식이 다르기 때문에, 이를 제대로 활용하기 위해서는 별도의 전략이 필요합니다. 이번 글에서는 HCX-007의 추론 모드에 초점을 맞춰, 실무에서 바로 활용할 수 있는 프롬프팅 팁을 정리했습니다. 프롬프트를 올바르게 설계한다면 단순한 응답을 넘어, 논리적 근거와 사고 과정을 담은 결과를 만들어낼 수 있을 것입니다. HCX-007 추론 기능, 언제 사용해야 할까요? 추론 기능은 답변을 생성할 때 더 깊은 사고 과정을 거치기 때문에, 비추론 모델보다 한층 구조적이고 정교한 결과를 제공합니다. 이 차이를 이해하기 위해, 먼저 두 모델의 작동 방식을 간단히 비교해보겠습니다. 즉, HCX-007의 추론 기능은 더 많은 자원과 시간이 필요하지만, 그만큼 깊은 사고가 요구되는 문제에서 탁월한 성능을 발휘합니다. 단순한 질의응답이나 요약처럼 구조가 명확한 작업이라면, 굳이 추론 기능을 활성화하지 않아도 충분합니다. 하지만 정밀 분석, 복잡한 의사결정, 단계별 검증 등 심층적 사고가 필요한 상황이라면, 추론 모드를 사용하는 것이 훨씬 유리합니다. 이제 실제로 어떤 상황에서 추론 모드를 선택하는 것이 효과적인지, 그리고 비추론 모델과 어떤 차이를 보이는지 활용 사례 예시를 통해 살펴보겠습니다. 1. 심층 분석 및 복잡한 문제 해결 HCX-007은 수학, 과학, 언어 추론 등 고난도 문제 해결에 강점을 보입니다. 단순 정보 제공을 넘어, 논리적 사고가 필요한 과업에서 정확하고 설득력 있는 결과물을 생성할 수 있습니다. 2. 기획안, 보고서 등 깊이 있는 내용을 작성하는 작업 HCX-007은 단순 요약을 넘어, 체계적인 구조와 논리가 필요한 기획안이나 보고서 작성에 효과적입니다. 문서의 뼈대를 빠르게 잡고, 전체적인 구성과 흐름을 정리하는 데 활용할 수 있습니다. 3. 데이터셋에서 정확한 근거 기반의 응답 문서나 데이터를 처리할 때, 추론 모델은 사용자의 질문과 가장 관련성 높은 정보를 찾아내고, 이를 바탕으로 논리적인 답변을 생성합니다. 비추론 모델이 표면적인 유사도에 의존한다면, 추론 모델은 맥락을 단계적으로 해석하며 왜 그런 답을 내렸는지를 근거와 함께 보여줍니다. 이 특징은 특히 RAG나 DocQA처럼 문서의 근거를 바탕으로 응답해야 하는 시나리오에서 강점을 발휘합니다. 단순 매칭이 아니라 실제 컨텍스트 안에서 근거를 선별하기 때문에, 사용자는 답변의 출처와 논리적 흐름을 쉽게 확인할 수 있습니다. 반면, 근거 제시가 굳이 필요하지 않은 상황이라면 none(추론 안 함)이나 생각 깊이 low(짧게) 수준으로도 충분합니다. 하지만 출처나 단계별 설명이 중요한 과업이라면, medium(보통) 또는 high(깊게) 수준으로 추론 깊이를 설정하는 것이 좋습니다. 생각 깊이에 따른 답변 차이는 아래 예시에서 직접 확인하실 수 있습니다. 4. 다른 모델 응답에 대한 평가 추론 모드는 다른 모델의 응답을 평가하는 데도 효과적입니다. 단순히 정답을 맞추는 수준을 넘어, 맥락을 이해하고 데이터를 논리적으로 해석하기 때문에 더 유연하고 지능적인 평가가 가능합니다. 이 방식을 활용하면 수작업보다 훨씬 빠르고 효율적으로 모델 성능을 진단할 수 있습니다. 예를 들어, 여러 모델의 답변을 비교, 분석하고, 정확성이나 적합성과 같은 평가 기준에 따라 점수를 산출하며, 그 결과를 기반으로 성능 개선 방향을 도출할 수 있습니다. 이런 접근은 모델 품질을 지속적으로 개선하는 핵심 프로세스이기도 합니다. HCX-007 모델 추론 과정 들여다보기 HCX-007의 추론 모드는 단순히 답을 내는 데 그치지 않습니다. 답에 도달하기까지의 생각 과정(Thinking Content)을 함께 보여주는 것이 가장 큰 차별점입니다. 일반적인 비추론 모델은 결과만 제시하기 때문에 “왜 이런 답을 했는가”를 알기 어렵습니다. 반면 HCX-007은 추론 단계를 드러내어 답의 근거와 맥락을 함께 제시하기 때문에, 이런 블랙박스 한계를 효과적으로 보완합니다. Thinking Content를 통해 모델이 어떤 전제나 계산을 거쳐 결론에 도달했는지를 추적할 수 있습니다. 이 과정은 다음과 같은 장점을 제공합니다. 즉, Thinking Content는 단순히 ‘과정을 보여주는 기능’을 넘어, 실무 현장에서의 신뢰성·검증·생산성을 함께 높여주는 핵심 기능입니다. 특히 금융, 법률, 의료 등 설명 가능성이 중요한 분야에서 강력한 경쟁력을 제공합니다. HCX-007 추론 모드 실전 가이드 1. 목적은 명확하게 제시하기 추론 모델에 질문을 던졌을 때 기대에 미치지 못하는 답변이 나오는 경우가 있습니다. 대부분은 모델이 사용자의 ‘질문 의도’와 ‘상황적 맥락’을 충분히 이해하지 못했기 때문입니다. 좋은 프롬프트는 단순히 무엇을 원하는가를 말하는 것이 아니라, 왜 이 요청을 하는지와 어떤 배경이 있는지를 함께 알려주는 것이 중요합니다. 불필요한 표현을 줄이고 목적을 분명히 전달하면, 훨씬 더 구체적이고 유용한 결과를 얻을 수 있습니다. 👎 좋지 않은 예시 (목적이 불분명한 경우) “신입 개발자 교육 프로그램 기획안 써줘.” 이 요청은 범위가 너무 넓어, 모델이 누구를 대상으로 어떤 깊이의 답변을 해야 하는지 판단하기 어렵습니다. 그 결과, 전공자를 위한 전문적인 설명이 나올 수도 있고, 반대로 어린이를 위한 단순한 비유로 흘러갈 수도 있습니다. 👍 좋은 예시 (목적을 명확히 제시한 경우) 신입 개발자를 위한 사내 기술 교육 프로그램의 MVP를 기획해야 합니다. 우리 회사 신입 개발자를 대상으로 한 ‘사내 기술 교육 프로그램’ 설계를 아래 안내와 분석 방식을 따라 수행하세요. 다음 6단계에 따라 작성해 주세요. 1. Pain & Target – 신입 개발자가 실제 프로젝트 투입 시 겪는 대표적인 어려움을 구체 사례와 수치로 기술하고, 시급히 개선이 필요한 포인트를 파악하세요. 2. Outcome – 본 교육 도입 시 달라질 점을 KPI(온보딩 속도, 과제 수행률, 코드 품질 등)와 내부/외부 벤치마크 비교로 설명하세요. 3. Solution – 실습, 강의, 멘토링 등 구체적 교육 방식과 운영 형태를 표로 설계하고, 파일럿에 적합한 최소 MVP 단계를 제안하세요. 4. 수익/성과 구조 – 기대 효과(프로젝트 완료율, 재교육률 감소 등)를 정량 수치와 ROI 중심으로 제시하세요. 5. 차별성 – 기존 타사/사내 교육과의 차별화 포인트를 데이터·경험 기반으로 분석하세요. 6. 요약 및 실행 계획 – 위 내용을 5문장 내 요약, 즉시 실행 액션 3가지, 확장 로드맵 2가지를 리스트로 정리하세요. [분석 방식] - 각 단계별 안내문에 따라 구체적으로 답변하되, 회사 상황·교육 대상(신입 개발자)·목표(KPI 달성) 중심으로 작성하세요. - 각 항목에 부족/모호한 점이 있으면 구체적 개선방향도 제안하세요. - 전체적으로 ‘실무 적용성’ ‘단계별 흐름 연결’이 드러나도록 작성하세요. [형식] - 각 항목별로 표/리스트로 깔끔하게 제시 - 숫자/지표/비교 자료 등 구체 수치 포함 - 마지막엔 전체 실행계획과 예상 리스크 표도 첨부 - 현실 적용에 바로 옮길 수 있게 실무 문체, 분량은 A4 2장 이내로 구성 실무 담당자와 임원이 모두 쉽게 이해할 수 있도록 반드시 짧고 명확한 문장, 실질적 피드백과 개선 아이디어 중심으로 작성하세요. 각 단계별 연결성이 드러나야 하며, 전체적으로 ‘바로 실행에 옮길 수 있는’ 실전적인 제안서로 정리하세요. 이처럼 목적을 명확히 제시한 프롬프트를 쓰면, 추론 모델이 사용자의 기대에 맞는 결과를 낼 가능성이 높아집니다. 2. CoT(Chain-of-Thought) 기법은 피하기 LLM에서 널리 쓰이는 Chain-of-Thought(CoT) 기법은 문제 해결 과정을 단계별로 드러내어 보다 논리적인 답변을 유도합니다. 그러나 HCX-007의 추론 모드에서는 상황이 다릅니다. 모델이 내부적으로 이미 사고 과정을 수행하기 때문에, 사용자가 단계별 사고를 일일이 지시할 필요가 없습니다. 오히려 CoT를 강제로 요구하면 자율적 추론을 제약해 흐름이 부자연스럽거나 비효율적으로 변할 수 있습니다. 따라서 HCX-007에서는 CoT 지시를 프롬프트에 포함하지 않는 것이 좋습니다. 모델이 스스로 사고하도록 두는 편이 안정적이며, 불필요한 토큰 소모도 줄일 수 있습니다. 이와 같은 주장에는 최근 논문들이 일정 부분 근거를 제공합니다. 예를 들어, Efficient Reasoning for LLMs through Speculative Chain-of-Thought ¹에서는 CoT 과정을 지나치게 확장하면 오히려 성능이 저하될 수 있음을 언급합니다. 특히 모델이 깊은 사고를 내부적으로 수행하는 상황에서 외부적인 CoT 유도는 overthinking(지나친 사고 반복)이나 reasoning incongruity(비인과적 추론 불일치)를 야기할 수 있으며, 반복적 CoT 생성 과정에서 토큰 비용 증가와 오류 누적 위험도 지적합니다. 또한, Mind Your Step (by Step): Chain-of-Thought can Reduce Performance on Tasks where Thinking Makes Humans Worse²는 사고 과정이 항상 긍정적인 효과를 내지 않는다는 점을 실험적으로 보여줍니다. 이를 통해, 불필요한 CoT 유도는 지양하고 필요할 때만 신중히 사용하는 것이 필요함을 알 수 있습니다. 답변의 논리를 보강하고 싶다면 구체 절차를 강제하기보다 “단계적으로 차근차근 생각해 보라” 수준의 가벼운 안내만 추가하길 권장합니다. 이는 내부 추론을 방해하지 않으면서 답변 구조를 정돈하는 데 도움이 됩니다. (1) Wang, Jikai, et al. 2025. “Efficient Reasoning for LLMs through Speculative Chain-of-Thought.” https://arxiv.org/abs/2504.19095v2) (2) Liu, Ryan, et al. 2025. “Mind Your Step (by Step): Chain-of-Thought Can Reduce Performance on Tasks Where Thinking Makes Humans Worse.” ICML 2025, https://icml.cc/virtual/2025/poster/45714) 3. 메타 프롬프팅 활용하기 프롬프트를 작성하다 보면 원하는 결과가 잘 나오지 않거나, 어떻게 수정해야 할지 막막할 때가 있습니다. 이럴 때는 HCX-007을 활용해 프롬프트 자체를 개선할 수 있는데, 이를 메타 프롬프팅(Metaprompting)³이라고 합니다. 메타 프롬프팅은 언어 모델이 스스로 프롬프트를 생성하거나 개선하도록 하는 기법입니다. 이 방식을 사용하면 반복적인 수정 과정을 줄이고, 원하는 결과를 더 안정적으로 얻을 수 있습니다. 아래는 HCX-007을 활용한 메타 프롬프트 예시입니다. 하나의 예시일 뿐이므로, 상황과 목적에 맞게 자유롭게 변형해 사용할 수 있습니다. (3) Teodora Musatoiu (OpenAI), Enhance Your Prompts with Meta Prompting, Oct 23 2024. https://cookbook.openai.com/examples/enhance_your_prompts_with_meta_prompting) 당신은 프롬프트 최적화를 돕는 전문가 조력자입니다. 당신의 핵심 임무는 사용자가 원하는 목적을 분명히 드러내도록 프롬프트를 개선하는 것입니다. 다음 지침에 따라 답변하세요: 1. 항상 "원하는 목적"을 먼저 언급하고, 해당 목적을 어떻게 더 잘 드러내도록 수정했는지 설명합니다. 2. 출력 형식이나 예시가 있으면 반드시 답변 안에 반영합니다. 3. 최소 수정 원칙: 기존 프롬프트의 의도를 유지하면서 꼭 필요한 부분만 추가·삭제·수정합니다. - 삭제는 목적 달성에 불필요하거나 방해가 되는 부분에 한정합니다. 4. 출력 형식 일관성: 답변은 항상 아래 네 가지를 포함합니다. - [수정된 전체 프롬프트] - [추가 제안] - [삭제 제안] - [수정 이유] 입력값: 기존 프롬프트: {개선을 원하는 기존 프롬프트} 원하는 목적: {기존 프롬프트에 대해 원하는 목적} 원하는 출력 형식: {원하는 출력물의 구체적인 형식} 질문: 이 프롬프트를 어떻게 수정하면 목적을 더 선명하게 드러내고, 원하는 행동을 안정적으로 얻을 수 있을까요? 4. HCX-007 추론 깊이와 토큰 설정 이해하기 HCX-007의 가장 큰 특징 중 하나는 추론의 깊이를 직접 조절할 수 있다는 점입니다. 답변을 생성할 때 필요한 사고 과정을 얼마나 깊게 할지 제어할 수 있어, 서비스의 요구사항에 맞게 결과의 품질과 리소스(시간, 비용) 간의 균형을 조정할 수 있습니다. thinking.effort로 추론 깊이 조절하기 추론 모델의 사고 깊이는 low(짧게), medium(보통), high(깊게), none(추론 안 함) 네 단계로 조절할 수 있습니다. 단계가 높아질수록 모델은 더 오랜 시간, 더 깊이 사고합니다. 대부분의 경우 low만으로도 충분하며, 기본값으로 권장됩니다. (Chat Completions v3 API 가이드 보기) none (추론 안 함) : 비추론 모드로, 사고 과정을 거치지 않습니다. low (짧게) : 빠른 응답이 필요한 경우에 적합합니다. 간단한 질의응답, 사실 확인, 단순 계산 문제 등에서는 low 설정만으로도 충분히 정확한 답을 얻을 수 있습니다. medium (보통) : 단순 응답을 넘어 논리적 연결이나 추가 reasoning이 필요한 경우 권장됩니다. low에서 나온 답이 불완전하거나 핵심을 놓쳤을 때, medium은 모델이 더 많은 사고 시간을 들여 답을 보완하도록 돕습니다. high (깊게) : 여러 단계의 논리 전개, 장문의 수학 증명, 복잡한 코드 작성 등 가장 높은 수준의 추론이 필요한 경우에만 사용하는 것을 추천합니다. 다만 응답 속도와 토큰 사용량이 크게 늘어나므로 신중히 선택하는 것이 좋습니다. 문제가 지나치게 복잡하다면, 한 번에 모두 해결하기보다 여러 턴으로 나누는 방식이 더 안정적입니다. 추론 깊이에 따른 토큰 설정 추론 모델에서는 사고 과정의 깊이에 따라 maxCompletionTokens 값도 달라집니다. 모델이 더 깊게 사고할수록 자동으로 할당되는 토큰 수가 늘어나며, 별도로 지정하지 않으면 아래의 기본값이 적용됩니다. (Chat Completions v3 API 가이드 보기) none : 512 low : 5120 medium : 10240 high : 20480 설정 가능한 범위는 1 ≤ maxCompletionTokens ≤ 32768이며, 기존 maxTokens 파라미터는 사용할 수 없습니다. 추론 모델은 내부 사고 과정을 거치기 때문에, 단순히 maxCompletionTokens 값만으로 출력 길이를 정밀하게 제어하기는 어렵습니다. 5. 답변 길이 제어 모델에게 단순히 “짧게 답해줘”라고 요청하면, 기대한 만큼의 결과가 나오지 않는 경우가 많습니다. ‘짧게’라는 기준이 모호하고, 모델이 이를 일관되게 해석하기 어렵기 때문입니다. 따라서 길이를 제어할 때는 모델이 이해하기 쉬운 단위로 구체적으로 지시하는 것이 중요합니다. 예를 들어 문장, 문단, 불릿 포인트, 핵심 포인트 개수처럼 눈에 보이는 단위를 활용하면 훨씬 안정적인 결과를 얻을 수 있습니다. 반면 토큰 수, 단어 수, 글자 수, 줄 수와 같은 내부 단위는 모델이 직접 세거나 제어하기 어려워, 길이 조정에 큰 효과가 없습니다. 길이만 제한하는 것보다, 맥락과 목적을 함께 제시하는 편이 훨씬 안정적입니다. 단순히 “3문장으로 짧게 설명해줘”라고 하기보다는, ‘왜 짧아야 하는지’와 ‘어떤 톤으로 써야 하는지’를 함께 알려주는 게 좋습니다. 예를 들어 이렇게 요청해보세요. - “블로그 소제목에 넣을 문장이니까, 짧고 명확하게 한 줄로 요약해줘.” - “사내 공지에 쓸 문장이라, 부담 없이 읽히는 톤으로 두 문장 안에 정리해줘.” - “기술 발표 슬라이드용 요약문으로, 전문적이지만 간결하게 써줘.” 이처럼 맥락과 목적을 함께 제시하면, 모델이 글의 의도를 더 정확히 이해하고 일관된 톤과 길이의 결과를 만들어냅니다. 마무리 사소한 프롬프트의 차이가 모델의 답변 품질과 효율성을 크게 바꿉니다. HCX-007을 사용할 때 오늘 정리한 원칙들을 참고한다면, 불필요한 토큰 낭비를 줄이고 더 정확하면서도 원하는 스타일의 결과를 손쉽게 얻을 수 있을 것입니다. 추론 모드는 단순한 기능이 아니라, ‘모델이 어떻게 생각하는가’를 제어하는 핵심 기능입니다. 프롬프트 설계에 조금만 신경 써도 HCX-007은 훨씬 더 논리적이고 실무에 맞는 답변을 제공합니다. 이번 글이 여러분이 HCX-007을 더 똑똑하고 효율적으로 활용하는 데 유용한 인사이트가 되었길 바랍니다.
 - 
	안녕하세요, 네이버 클라우드 플랫폼입니다. 네이버 클라우드 플랫폼을 이용해주셔서 감사합니다. 지난 7월 17일 공지드린 바와 같이 LK 모델 서비스 종료 일정을 안내드립니다. (공지 바로가기: https://www.ncloud.com/support/notice/all/1959) LK 모델은 서비스가 완전히 종료될 예정이며, 성능이 개선된 HyperCLOVA X 모델로 전환해 주시기 바랍니다. 자세한 일정과 대체 모델 정보를 아래에서 확인해 주시기 바랍니다. ※ 적용 일시 : 2025년 11월 20일(목)부터 ※ 대상 서비스 : CLOVA Studio ※ 영향 범위 플레이그라운드, 튜닝, 익스플로러 작업에 대해 API를 통한 LK-B, LK-D2 모델 호출 불가 LK 모델 호출하는 Completions API 지원 종료 LK 모델로 저장된 작업은 제공 종료일 이후에도 챗 모드로 불러오기 및 프롬프트 확인 가능 단, 튜닝 작업은 불러오기 불가 <제공 종료> 엔진 신규 이용 불가 제공 종료 대체 모델 LK-B 2025년 7월 17일 2025년 11월 20일부터 HCX-DASH-002, HCX-005, HCX-007 LK-D2 2025년 7월 17일 2025년 11월 20일부터 HCX-DASH-002, HCX-005, HCX-007 관련된 문의는 [고객지원 > 문의하기] 로 연락주십시오. 더욱 편리한 서비스 제공을 위하여 항상 노력하겠습니다. 감사합니다.
 - 
	안녕하세요. OBS에서 trainidiomclova로 버킷을 생성하셨다면, 파일 경로 입력 시 버킷명은 제외해주셔야 합니다. (파일을 클릭하신 후 ‘상세 정보’의 Link 중 버킷명을 제외한 버킷 내 경로 입력) 또한 AccessKey와 SecretKey는 OBS 접근 권한이 부여된 서브 계정 정보를 입력하셔야 하니 참고 부탁드립니다. 학습 데이터는 사용자의 소중한 자산이므로, CLOVA Studio에서는 데이터를 직접 저장하지 않고 사용자가 네이버 클라우드 플랫폼의 OBS와 서브 계정을 통해 직접 관리할 수 있도록 설계되어 있습니다. 설정 과정이 다소 복잡한 점에 대해서는 보다 편리하게 사용하실 수 있도록 개선 방안을 지속적으로 검토하겠습니다. 소중한 의견 감사합니다.
 - 
	안녕하세요? HCX-005에 대해 Clova Studio 내 '튜닝' 메뉴로 튜닝하려고 합니다. https://clovastudio.ncloud.com/ 데이터셋도 맞는 형식으로 준비하고 얘를 Object Storage에도 올리고 Media C뭐시기 Center에도 올리래서 올렸는데 막상 데이터셋 경로를 찾으려니 못찾겠네요. 어떤 형식으로 입력해야 하나요? 제가 사용한 코드는 아래와 같습니다. ================ # -*- coding: utf-8 -*- # https://clovastudio.ncloud.com/tuning/create import requests class CreateTaskExecutor: def __init__(self, host, uri, api_key, request_id): self._host = host self._uri = uri self._api_key = api_key self._request_id = request_id def _send_request(self, create_request): headers = { 'Authorization': self._api_key, 'X-NCP-CLOVASTUDIO-REQUEST-ID': self._request_id } result = requests.post(self._host + self._uri, json=create_request, headers=headers).json() return result def execute(self, create_request): res = self._send_request(create_request) if 'status' in res and res['status']['code'] == '20000': return res['result'] else: return res if __name__ == '__main__': completion_executor = CreateTaskExecutor( host='https://clovastudio.stream.ntruss.com', uri='/tuning/v2/tasks', api_key='Bearer nv-', request_id='f80' ) request_data = {'name': 'generation_task', 'model': 'HCX-005', 'tuningType': 'PEFT', 'trainEpochs': '8', 'learningRate': '1e-5f', 'trainingDatasetBucket': 'trainidiomclova', >>> 폴더 이름 입력하래서 했구요 'trainingDatasetFilePath': 'trainidiomclova/NAVER_train_idiom_balanced.csv', >>> 경로가 이건가 했더니 이건 또 아니네요 'trainingDatasetAccessKey': 'ncp_iam_', 'trainingDatasetSecretKey': 'ncp_iam_' } response_text = completion_executor.execute(request_data) print(request_data) print(response_text) =============================== 개인적으로 저는 딥러닝/LLM을 공부하는 학생인데 OpenAI와 비교해봐도 Naver Clova의 플랫폼이 지나치게 복잡한 것 같습니다. HyperClova의 미래를 위해서라도 개선해야 하지 않을까요..
 - 
	Xcode 26.0.1 -> 네이버지도 라이브러리 코드에서 아래와 같은 오류 발생합니다. 'mainScreen' is deprecated: first deprecated in iOS 26.0 - Use a UIScreen instance found through context instead (i.e, view.window.windowScene.screen), or for properties like UIScreen.scale with trait equivalents, use a traitCollection found through context. #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import <NMapsGeometry/NMapsGeometry.h> #import "NMFMapView.h" NS_INLINE CGFloat ScreenScaleFactor(void) { static dispatch_once_t onceToken; static CGFloat screenFactor; dispatch_once(&onceToken, ^{ screenFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; }); return screenFactor; }; /** 지오메트리 관련 유틸리티를 제공하는 클래스. */ NMF_EXPORT @interface NMFGeometryUtils : NSObject /** `NMGLatLng` 배열로 구성된 경로선에서 대상 좌표에 가장 근접한 지점의 진척률을 반환합니다. @param latLngs `NMGLatLng` 배열로 구성된 경로선. @param targetLatLng 대상 좌표. @return 진척률. */ +(double)progressWithLatLngs:(NSArray<NMGLatLng *> * _Nonnull)latLngs targetLatLng:(NMGLatLng * _Nonnull)targetLatLng; /** `NMGLineString` 배열로 구성된 경로선에서 대상 좌표에 가장 근접한 지점의 진척률을 반환합니다. @param lineStrings `NMGLineString` 배열로 구성된 경로선. @param targetLatLng 대상 좌표. @return 진척률. */ +(double)progressWithLineStrings:(NSArray<NMGLineString *> * _Nonnull)lineStrings targetLatLng:(NMGLatLng * _Nonnull)targetLatLng; @end /** 카메라 관련 유틸리티를 제공하는 클래스. */ NMF_EXPORT @interface NMFCameraUtils : NSObject /** `bounds`가 화면에 온전히 보이는 최대 줌 레벨을 반환합니다. @param bounds 영역. @param insets 영역과 지도 화면 간 확보할 인셋 여백. pt 단위. @param mapView `NMFMapView` 객체. @return `bounds`가 `map`에서 화면에 온전히 보이는 최대 줌 레벨. */ + (double)getFittableZoomLevelWith:(NMGLatLngBounds * _Nonnull)bounds insets:(UIEdgeInsets)insets mapView:(NMFMapView * _Nonnull)mapView; @end
 - 
	해결되었습니다. 어플리케이션 웹주소를 잘못 입력해서 안되었네요. 정상적으로 수정하니까 잘되네요.
 - 
	curl --location --request GET \ "https://maps.apigw.ntruss.com/map-direction/v1/driving?start=127.1058342,37.359708&goal=129.075986,35.179470" \ --header "x-ncp-apigw-api-key-id: 키" \ --header "x-ncp-apigw-api-key: 키" 키정보는 발급받은 정보로 정상적으로 세팅해서 서버 터미널에서 접속 테스트 했는데 아래 처럼 나오는데 {"error":{"errorCode":"200","message":"Authentication Failed","details":"Invalid authentication information."}}[ 원인이 뭘까요 인증정보가 왜 틀리다고 나오고 200 에러는 표에도 없던데. .