[LLM 활용] Evaluator 를 이용한 LLM, Agent 성능평가


앞선 포스트에서 LangSmith feedback 기능을 이용한 프롬프트 개선 방법을 소개했습니다.

테스트용 데이터와 LLM 의 처리 결과를 LangSmith에 전송하고, 평가자(사람)가 feedback 을 등록합니다. 이후 feedback 을 바탕으로 프롬프트 개선을 LLM 에 요청하는 방식으로 동작했습니다.

이번에는 이 프로세스를 더 자동화 해보겠습니다. LLM 이 테스트 데이터에 프롬프트를 실행하면, 다른 LLM 을 이용해서 이 테스트 결과를 평가하는 방법입니다.

이 방법을 구현하기 위해 2가지 종류의 LLM 언어모델을 사용하고, LangChain 에서 제공하는 Evaluation(평가) 기능을 사용합니다. 코드가 실행되는 순서는 아래와 같습니다.

  • 테스트용 뉴스 기사(input)와 요약한 데이터(output)를 준비합니다. (정답지 준비)
  • input / output 데이터를 LangSmith – dataset 에 넣어둡니다.
  • 뉴스 요약에 사용할 prompt, LLM, chain 을 준비합니다. (ollama – llama3.1)
  • 요약된 뉴스를 평가할 때 사용할 prompt, LLM, chain 을 준비합니다. (OpenAI – GPT-4o-mini)
  • 평가 결과를 점수로 반환해주는 함수를 준비합니다. (batch_evaluator)
  • 위에서 준비한 평가 관련된 설정 정보를 담은 RunEvalConfig 을 생성합니다.
  • run_on_dataset() 함수를 이용해서 dataset – 테스트 데이터에 뉴스 요약 chain 을 실행합니다. 이때 RunEvalConfig 을 함께 입력합니다.
  • 뉴스 요약과 요약 결과에 대한 평가가 순차적으로 실행됩니다
  • 평가 결과를 출력합니다.

전체 코드는 아래와 같습니다. [LangSmith_llm_evaluator.py]

import os
import re
from typing import List
from dotenv import load_dotenv
from langchain import hub
from langchain_community.llms.ollama import Ollama
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langsmith import Client
from langchain_openai import ChatOpenAI
from langsmith.evaluation import EvaluationResult
from langsmith.schemas import Example, Run
from langchain.smith import RunEvalConfig
from langchain.smith import run_on_dataset
from langchain.evaluation import CriteriaEvalChain


# 1. 프로세스 실행을 위한 환경설정 및 파라미터 준비
# LangSmith 사이트에서 아래 내용을 복사해서 .env 파일에 입력
# LANGCHAIN_TRACING_V2=true
# LANGCHAIN_ENDPOINT=https://api.smith.langchain.com
# LANGCHAIN_API_KEY=<your_api_key>
# LANGCHAIN_PROJECT="<your_project_name>"

load_dotenv()  # .env 파일 로드

################################################################
# 1. 테스트 준비 단계
################################################################
# 1-1. LangSmith project, dataset 이름으로 사용할 문자열
project_name = os.getenv("LANGCHAIN_PROJECT")

# 1-2. 테스트용 input, output 데이터 셋 준비
news_array = [
    """아프리카 펭귄을 구하기 위한 투쟁사진 출처, Christian Parkinson/BBC기사 관련 정보기자, 제니 힐 기자,  BBC 뉴스 Reporting from  웨스턴 케이프 2024년 5월 12일매년 남아프리카공화국 
과 나미비아 등 아프리카 펭귄의 수가 줄고 있다. 곧 아예 없어질 수도 있다고 한다. 과학자들은 이 종이 매년 약 8%씩 감소하고 있다고 밝혔다.베티스 베이의 펭귄들이 물가를 따라 뛰어다니며 짧은 쉰 목
소리로 서로를 부른다.펭귄들은 유쾌하게 돌아다니고 있지만 한 사람의 얼굴에는 근심이 가득했다."물 가까이에 있는 이 펭귄은 꽤 여유가 없어 보여요. 살이 많이 없다는 걸 알 수 있죠."버드라이프 남아 
프리카공화국 해조류 보호 연구원인 맥인니스 박사는 이 지역 내 줄어드는 펭귄들을 추적 및 감시하는 일을 하고 있다.지난 세기 동안 아프리카 펭귄은 99% 감소했다."지금의 감소율이 지속된다면, 2035년 
까지 종의 멸종을 목격할 수도 있다"고 맥인니스 박사는 경고한다.그것이 바로 버드라이프 남아프리카공화국 해안 조류 보호재단(SANCCOB)이 이 곳에서 처음으로 법적 조치를 취하고 있는 이유다.이들은 이
 국가가 멸종 위기에 처한 종을 충분히 보호하지 못했다고 주장한다. "생물 다양성 법센터"의 케이트 핸들리는 이는 해당 국가의 법적 의무라고 강조했다.사진 출처, Christian Parkinson/BBC사진 설명, 펭
귄이 의존하는 정어리와 멸치의 개체수는 감소하고 있다대부분의 아프리카 펭귄들은 아프리카 남서부 해안을 따라 일곱 군데에서 사는 것으로 추정된다. 약 8,750쌍이다.펭귄은 고정된 흑색 줄이 몸쪽을 따
라 빠지는 짧고 튼튼한 새다. 그들은 사진을 찍는 사람들에게 아무 영향도 받지 않는 것처럼 보이지만, 햇볕에 누워있거나 알을 지키는 동안 그들은 불안한 시선으로 사람들을 지키보고 있다.그들은 자연적
인 포식자; 특히 물개와 특정 종류의 갈매기에도 취약하다. 하지만 진짜 적은 인간이다.현재 중단된 그노(펭귄굴에 쌓인 배설물) 수확 실천은 그들의 서식지를 파괴했다. 게다가 기후 변화가 문제를 악화시
키고 있다. 폭풍과 홍수는 펭귄들을 위협하고 바다의 흐름과 온도가 변화함에 따라 펭귄들이 먹이에 접근하기가 점점 더 어려워지고 있다.그리고 펭귄이 의존하는 마른 멸치와 정어리는 상업 어업에도 중요
하다.남아프리카공화국 정부는 대규모 그물을 사용해 물고기 무리를 잡는 소위 '축어채 어업 선박'의 활동을 제한하려고 노력해왔다. 하지만 이것은 불안정한 문제를 야기한다.지난 15년 동안 어업지역의  
실험적 폐쇄, 어업 업계와 보전가들 간의 길고 복잡한 협상, 국제 전문가 패널의 의견 등이 바로 그것이다.하지만 펭귄 수는 여전히 감소하고 있다.버드라이프 남아프리카공화국 해안 조류 보호재단(SANCCOB)은 현재 이뤄지고 있는 펭귄 주변에서의 어업 금지 조치가 충분히 확장되지 않았거나 올바른 위치에 있지 않다고 주장한다.그들의 변호사들은 "생물학적으로 의미 있는" 조치의 즉각적인 시행을 요구하고
 있다.새들이 충돌하지 않는 풍력 발전단지를 조성하는 방법2024년 5월 4일영원히 사라져버린 메가배트2024년 4월 27일그 많던 오징어는 다 어디로 갔을까2024년 3월 22일사진 출처, Christian Parkinson/BBC사진 설명, 버드라이프 남아프리카공화국 해조류 보호 연구원인 맥인니스 박사그러나 해안을 따라 작은 항구에서 어부들이 바다로 나가기 전에 물고기를 내리는 동안 걱정과 분노가 존재한다.이곳 사람들
은 자신들이 책임을 져야 한다는 지적을 거부했다."우리는 문제의 일부분입니다" 많은 어부들을 대표하는 남아프리카공화국 펠라직 어업협회의 부회장인 샤메라 다니엘스는 말했다."포식자인 물개, 상어는 
물론 석유 및 가스 탐사, 소음 오염도 문제입니다."그는 또 현재의 제한이 이미 수백만 달러의 비용과 수백 개의 직장을 어업업에 야기했다고 주장했다. 추가적인 폐쇄는 여기서 많은 사람들이 의지하는 산
업에 더 많은 고통을 초래할 것이라는 말이다.사진 출처, Christian Parkinson/BBC사진 설명, 펭귄은 관광상품이 되었지만, 더 이상 그 곳에 존재하지 않을지도 모른다앞으로 길고 긴 법적 과정이 기다리고
 있다.보존 법률가인 핸들리는 현실적으로 시간이 부족하다고 털어놓는다."아프리카 펭귄을 구하기 위한 모든 단계는 우리가 실제로 이러한 법정 청문회를 시간 내에 얻을 수 있는 가능성이 얼마 없더라도 
시도해야 한다는 것입니다."첫 번째 법적 청문회가 언제 개최될지는 아직 확실하지 않다. 남아프리카공화국 정부는 이에 대한 논평을 하지 않았다.하지만 우리는 이러한 조치들이 아프리카의 죽어가는 펭귄
들에게는 이미 너무 늦었을 수 있다는 것을 기억해야 한다.추가 취재: 가비 콜렌소관련 기사 더 보기에베레스트 등반? 이제는 당신의 배설물을 가져와야 한다2024년 2월 12일북극 얼음 녹아 육지로 떠밀린 
북극곰들...적응 못해 굶주림 직면2024년 2월 14일'구름 씨앗'이란 무엇이며, 이번 두바이 홍수의 원인은?2024년 4월 18일'양식 연어' 대량 폐사가 급증하는 이유2024년 3월 10일아마존 최악의 가뭄…'한번 
도 본 적 없는 모습입니다'2023년 12월 26일'기후 재앙 마지노선 1.5℃ 돌파 가능성 커졌다'2023년 10월 7일""",
]
summary_list = [
    """<news>
  <title>아프리카 펭귄을 구하기 위한 투쟁</title>
  <author>제니 힐 기자, BBC 뉴스</author>
  <date>2024년 5월 12일</date>
  <summary>아프리카 펭귄의 수가 매년 약 8%씩 감소하고 있으며, 현재 남아프리카공화국과 나미비아에서 이 종의 멸종 위기가 심각해지고 있다. 과학자들은 2035년까지 멸종할 수 있다고 경고하고 있으며, 이는 인간의 활동과 기후 변화가 주요 원인으로 지목되고 있다. 버드라이프 남아프리카공화국 해안 조류 보호재단은 정부가 충분한 보호 조치를 취하지 않고 있다고 주장하며 법적 조치를 취하고 있다. 그
러나 어업계는 이러한 조치가 산업에 부정적인 영향을 미칠 것이라고 반발하고 있다. 향후 법적 과정이 진행될 예정이지만, 이미 늦었을 수 있다는 우려가 제기되고 있다.</summary>
</news>""",
]

# 1-3. LangSmith client 생성
client = Client()

# 1-4. input, output 데이터를 저장할 dataset 생성 또는 가져오기
try:
    ds = client.create_dataset(
        dataset_name=project_name,
        description="An example dataset of news and summary",
    )
except Exception as e:
    # 이미 존재하는 경우, 기존 dataset을 가져옵니다
    existing_datasets = list(client.list_datasets(dataset_name=project_name))
    if existing_datasets:
        ds = existing_datasets[0]
    else:
        print(f"----- Failed to create or retrieve dataset: {e}")

# 1-5. 데이터셋에 example (input-output) 추가
try:
    client.create_examples(
        inputs=[{"news": news} for news in news_array],
        outputs=[{"output": summary} for summary in summary_list],
        dataset_id=ds.id,
    )
except Exception as e:
    print(f"----- Failed to add example: {e}")

# 1-6. 뉴스 요약 PROMPT Template 생성
summary_prompt = hub.pull("hellollama/news_summary")

# 1-7. 뉴스 요약에 사용할 LLM 초기화
llm = Ollama(model="llama3.1", temperature=0)

# 1-8. 뉴스 요약 체인 (테스트용 체인)
summary_chain = summary_prompt | llm | StrOutputParser()

################################################################
# 2. 평가 단계
################################################################
# 2-1. 뉴스 요약 결과를 평가할 LLM 초기화
openai_api_key = os.getenv("OPENAI_API_KEY")  # OpenAI API 키 가져오기
evaluator_llm = ChatOpenAI(
    temperature=0,  # 정확성 <-> 창의성 (0.0 ~ 2.0)
    max_tokens=1024 * 10,  # 최대 토큰수
    model_name="gpt-4o-mini",  # 모델명
    openai_api_key=openai_api_key,  # API Key 설정
)

print("----- Set evaluation configurations")


# 2-2. 평가 결과에서 score 추출해서 하나의 리포트로 만들어주는 함수
def batch_evaluator(runs: List[Run], examples: List[Example]):
    """evaluator 실행 결과를 취합해서 점수로 환산"""
    print("\n\n----- Run batch_evaluator")
    run_ids = []
    for run in runs:
        run_ids.append(str(run.id))
    feedback_list = client.list_feedback(run_ids=run_ids)
    average_score = 0
    feedback_size = 0
    for feedback in feedback_list:
        # print(f"feedback = {feedback.comment}")
        pattern = r"<(\w+)>(\d+)</\1>"  # 정규 표현식 패턴
        matches = re.findall(pattern, feedback.comment)  # 값 추출
        results = {}  # 결과를 저장할 딕셔너리
        # 추출된 값들을 딕셔너리에 저장
        for tag, value in matches:
            results[tag] = int(value)
        # 결과 출력
        for tag, value in results.items():
            print(f"{tag}: {value}")
        average_score += results["score"]
        feedback_size += 1
    average_score = average_score / feedback_size
    return EvaluationResult(key="score", score=average_score)


# 2-3. 평가 기준 정의
criteria = {
    "clarity": "The explanation should be clear and easy to understand. Express score as a number between 1-10. Write score between <clarity></clarity> tag",  # 명료성
    "accuracy": "The information provided should be factually correct. Express score as a number between 1-10. Write score between <accuracy></accuracy> tag",  # 사실성
    "conciseness": "The explanation should be concise and to the point. Express score as a number between 1-10. Write score between <conciseness></conciseness> tag",  # 정확성
    "score": "Average of the clarity, accuracy, and conciseness score as a number between 1-10. Write average score between <score></score> tag",  # 종합 평가 점수 (평균)
}

# 2-4. 평가용 프롬프트
evaluation_prompt = hub.pull("hellollama/summary_evaluator")

# 2-5. 평가용 체인 - evaluator 생성
evaluator = CriteriaEvalChain.from_llm(
    llm=evaluator_llm,
    criteria=criteria,
    prompt=evaluation_prompt,
)

# 2-6. 평가용 환경설정
evaluation_config = RunEvalConfig(
    # 주의!! LLM 지정없이 아래와 같이 실행하면 GPT-4o 로 실행됨
    # evaluators=[RunEvalConfig.Criteria(criteria)],
    # Custom evaluator 로 실행
    custom_evaluators=[evaluator],
    batch_evaluators=[batch_evaluator],
)

# 2-7. 평가용 환경설정을 추가해서 데이터셋 데이터에 뉴스 요약 체인을 실행
chain_results = run_on_dataset(
    dataset_name=project_name,
    llm_or_chain_factory=summary_chain,
    evaluation=evaluation_config,
    verbose=True,
    client=client,
    # Project metadata communicates the experiment parameters,
    # Useful for reviewing the test results
    project_metadata={
        "env": "Ollama",
        "model": "llama3.1",
        "prompt": "hellollama/news_summary",
    },
)

# 결과를 저장할 리스트들 초기화
input_news_list = []
output_string_list = []
run_id_list = []
reference_output_list = []

# results의 각 항목에 대해 처리
for result_key, result_value in chain_results["results"].items():
    # 1. results -> input -> news 문자열
    input_news_list.append(result_value["input"]["news"])
    # 2. results -> output 문자열
    output_string_list.append(result_value["output"])
    # 3. result -> run_id
    run_id_list.append(result_value["run_id"])
    # 4. result -> reference -> output 문자열
    reference_output_list.append(result_value["reference"]["output"])

# 2-8. 결과 출력
print("Input News List:", input_news_list)
print("Output String List:", output_string_list)
print("Run ID List:", run_id_list)
print("Reference Output List:", reference_output_list)
print("aggregate_metrics:", chain_results["aggregate_metrics"])
  1. 테스트 준비 단계
    • 현재 파일이 있는 폴더에 .env 환경설정 파일을 생성합니다.
    • 1-1. LangSmith project, dataset 이름으로 사용할 문자열 지정
      • .env 파일에 있는 LANGCHAIN_PROJECT 변수값을 설정합니다.
    • 1-2. 테스트용 input, output 데이터 셋 준비
      • 뉴스 기사와 요약 결과를 준비 (정답지)
      • 1개 이상의 데이터가 필요합니다. (예제에서는 1개만 사용)
    • 1-3. LangSmith client 생성
    • 1-4. input, output 데이터를 저장할 dataset 생성 또는 가져오기
    • 1-5. 데이터셋에 example (input-output) 추가
    • 1-6. 뉴스 요약 PROMPT Template 생성
    • 1-7. 뉴스 요약에 사용할 LLM 초기화
      • 비교적 성능이 낮은 ollama – llama3.1 모델을 사용합니다.
      • 테스트 해보니 llama3.1 은 평가 작업에는 동작하지 않았습니다
    • 1-8. 뉴스 요약 chain
      • 뉴스 요약 작업을 수행할 테스트용 chain
      • 데이터셋에 적용할 chain 입니다.
  2. 평가 단계
    • 2-1. 뉴스 요약 결과를 평가할 LLM 초기화
      • OpenAI – GPT-4o-mini 를 사용합니다.
      • GPT-4o 와 같은 모델은 데이터가 클 경우 비용이 생각보다 많이 나가니 주의!
    • 2-2. 평가 결과에서 score 추출해서 하나의 리포트로 만들어주는 함수
      • batch_evaluator 에서 최종 평가 결과를 생성합니다.
      • clarity, accuracy, conciseness 결과값(1~10)을 평균해서 average score 값을 만듭니다.
      • 이 함수에서 리턴한 score 값이 평가 chain 실행 결과, LangSmith trace 항목 등에 표시됩니다.
    • 2-3. 평가 기준 정의
      • clarity(명료성), accuracy(사실성), conciseness(정확성) 항목을 1~10 점수로 평가
      • 3개 항목의 평균값을 average score 로 저장
    • 2-4. 평가용 프롬프트
    • 2-5. 평가용 체인 – evaluator 생성
    • 2-6. 평가용 환경설정
      • custom_evaluators 파라미터에 2-5에서 생성한 evaluator 를 지정해주면 됩니다.
      • custom_evaluators 를 사용하지 않으면 langchain 에서 자동으로 평가용 evaluator 를 지정하는 것 같습니다. 제 경우에는 GPT-4o 로 자동 설정되었는데, 평가 데이터가 큰 경우 비용이 생각보다 많이 청구되는 문제가 발생했습니다. 이 부분은 주의가 필요합니다.
    • 2-7. 뉴스 요약, 평가 체인을 실행
      • run_on_dataset() 함수를 통해 뉴스 요약을 수행합니다. 뉴스 요약 작업이 완료되면 결과를 바탕으로 평가 작업이 수행됩니다.
      • example 데이터 갯수만큼 평가 작업이 끝나면 batch_evaluator 가 실행됩니다.
      • batch_evaluator 에서 최종 평가 결과가 작성됩니다. 평가 결과 정보를 얻기 위해 feedback _list() 함수를 호출합니다. 평가 결과는 평균 score 값으로 압축됩니다.
    • 2-8. 결과 출력
      • run_on_dataset() 함수의 실행 결과를 보면 이상의 평가 작업에 사용된 데이터들을 조회할 수 있습니다.


코드를 실행해보면 아래와 같이 결과가 표시됩니다.

$ python -u "c:\Workspace\.......\LangSmith_llm_evaluator.py"

----- Set evaluation configurations
View the evaluation results for project 'impressionable-water-21' at:
https://smith.langchain.com/o/......

View all tests for Dataset AgentEvaluator at:
https://smith.langchain.com/o/......
[------------------------------------------------->] 1/1

----- Run batch_evaluator
clarity: 8
accuracy: 9
conciseness: 7
score: 8

 Experiment Results:
       feedback.clarity-accuracy-conciseness-score error  execution_time                                run_id
count                                            0     0            1.00                                     1
unique                                           0     0             NaN                                     1
top                                            NaN   NaN             NaN  8706321f-4bb9-4ca9-979e-d5b1f3557a15
freq                                           NaN   NaN             NaN                                     1
mean                                           NaN   NaN            6.71                                   NaN
std                                            NaN   NaN             NaN                                   NaN
min                                            NaN   NaN            6.71                                   NaN
25%                                            NaN   NaN            6.71                                   NaN
50%                                            NaN   NaN            6.71                                   NaN
75%                                            NaN   NaN            6.71                                   NaN
max                                            NaN   NaN            6.71                                   NaN
Input News List: ['아프리카 펭귄을 구하기 위한 투쟁......']
Output String List: ['<news>......</news>']
Run ID List: ['8706321f-4bb9-4ca9-979e-d5b1f3557a15']
Reference Output List: ['<news>......</news>']
aggregate_metrics: [{'key': 'score', 'score': 8.0, 'value': None, 'comment': None, 'correction': None, 'evaluator_info': {}, 'feedback_config': None, 'source_run_id': None, 'target_run_id': None}]  
PS C:\Workspace\___Shared\Llama\test> 

$ 


run_on_dataset() 을 수행하면 리턴받는 chain_results 값에는 평가 과정에서 사용된 다양한 데이터가 포함되어 있습니다. 이 정보들을 적절히 추출해서 이후에 이어지는 코드에서 사용하시면 됩니다.

{
	'project_name': 'artistic-discovery-10', 
	'results': {
		'34ee6aff-db5f-4813-ab16-dd2be1fb9e0e': {
			'input': {
				'news': '아프리카 펭귄을 구하기 위한 투쟁......'
			}, 
			'feedback': [
				EvaluationResult(
					key='clarity-accuracy-conciseness-score', 
					score=None, 
					value='The average score reflects......', 
					comment='<clarity>8</clarity>......', 
					correction=None, 
					evaluator_info={'__run': RunInfo(run_id=UUID('91ccebd4-6e86-4277-9a65-5ae33f27a5ed'))}, 
					feedback_config=None, 
					source_run_id=None, 
					target_run_id=None
				)
			], 
			'execution_time': 9.717844, 
			'run_id': '6a2a58c8-24a8-492d-ac6e-69ac082d5c8b', 
			'output': '<news>......</news>', 
			'reference': {
				'output': '<news>......</news>'
			}
		}
	},
	'aggregate_metrics': [
		{
			'key': 'score', 
			'score': 8.0, 
			'value': None, 
			'comment': None, 
			'correction': None, 
			'evaluator_info': {}, 
			'feedback_config': None, 
			'source_run_id': None, 
			'target_run_id': None
		}
	]
}


LangSmith 에서 확인해보면 뉴스 요약과 평가 과정을 상세히 조회할 수 있습니다. llama3.1 모델을 이용해서 뉴스를 요약한 결과는 아래 메뉴에서 확인할 수 있습니다.

  • LangSmith – 좌측 Datasets & Testing – 생성한 폴더 선택 – example(experiment) 선택


llama3.1 이 수행한 요약 작업에 대한 평가는 위 화면의 하단에 있는 Linked Runs 에서 확인할 수 있습니다. 또는 아래 메뉴를 통해 평가 작업 전체를 조회해 볼 수도 있습니다.

  • LangSmith – 좌측 Projects – evaluator 항목 선택


여기서 평가 작업이 어떻게 수행되었는지 자세히 살펴볼 수 있습니다. GPT-4o 로 평가한 결과 – feedback 은 아래와 같습니다.

feedback:
  key: clarity-accuracy-conciseness-score
  value: 평균 점수는 8로, 전반적으로 언어모델이 뉴스 요약 작업을 잘 수행했음을 나타냅니다. 요약은 명확하고 사실적이며, 주요 내용을 잘 전달하고 있지만, 약간의 개선 여지가 있습니다.
  comment: |-
    <clarity>8</clarity>
    설명은 전반적으로 명확하고 이해하기 쉬웠습니다. 그러나 "한 사람의 얼굴에는 근심이 가득했다"라는 표현은 다소 모호하게 느껴질 수 있어, 독자가 상황을 완전히 이해하는 데 약간의 혼란을 줄 수 있습니다.

    <accuracy>9</accuracy>
    제공된 정보는 사실적으로 정확하며, 아프리카 펭귄의 감소율과 관련된 과학자의 경고, 그리고 어업 금지 조치에 대한 맥인니스 박사의 주장을 잘 반영하고 있습니다. 그러나 펭귄의 수가 "곧 멸종 위기에 처할 수도 있다"는 표현은 다소 주관적일 수 있습니다.

    <conciseness>7</conciseness>
    요약은 비교적 간결하지만, "펭귄은 유쾌하게 돌아다니지만"이라는 문장은 요약의 핵심 정보와는 관련이 적어 보입니다. 이 부분이 생략되었다면 더 간결했을 것입니다.

    <score>8</score>
    평균 점수는 8로, 전반적으로 언어모델이 뉴스 요약 작업을 잘 수행했음을 나타냅니다. 요약은 명확하고 사실적이며, 주요 내용을 잘 전달하고 있지만, 약간의 개선 여지가 있습니다.
  evaluator_info:
    __run:
      run_id: 130825a7-b749-4c7e-bea9-e809f2fe0b51

사실 PC에서 ollama 를 통해 동작하는 llama3.1 (8b) 모델은 전반적인 성능이 그리 좋지 못합니다. llama3.1 모델을 이용해서 평가를 하는 것은 무리인듯 하며, 대신 일부러 뉴스 요약의 품질을 낮추기 위해 사용했습니다.

전반적으로 llama3.1 은 뉴스에서 소개된 사건의 원인 또는 사건의 전개에 대한 내용을 누락하는 경향이 있습니다. GPT-4o-mini 를 이용한 평가 결과를 보면 꽤 정확하게 이 문제점들을 짚어냈음을 알 수 있습니다.

코드 [2-6]을 보면 RunEvalConfig 을 사용해서 평가 작업을 위한 설정을 합니다. 이때 custom evaluator 를 설정하지 않거나 ollama – llama3.1 을 지정하는 경우, 지정된 LLM 대신 GPT-4o 가 실행되는 현상이 있었습니다. 확실치는 않지만 evaluator 가 없거나 사용할 수 없는 언어모델인 경우 내부에서 default 값으로 대체해서 실행하는 것 같습니다.

문제는 GPT-4o 모델로 사이즈가 큰 데이터들을 돌리다보면 금방 API 충전량이 소진될 수 있습니다. 그러니 퀄리티가 중요한 상황이 아니라면 가급적 GPT-4o-mini 모델로 테스트하세요.



LLM -> Agent 로 변경해서 평가 프로세스 수행

평가를 위해 사용한 LLM 모델 대신 Agent 를 이용해서 평가 프로세스를 수행하는 경우도 위 프로세스와 달라지는 점은 거의 없습니다.

먼저 llm 인스턴스에 tool 들을 붙여줍니다.

from langchain_community.tools import DuckDuckGoSearchResults

# 기존 코드 

evaluator_llm = ChatOpenAI(
    temperature=0,  # 정확성 <-> 창의성 (0.0 ~ 2.0)
    max_tokens=1024 * 10,  # 최대 토큰수
    model_name="gpt-4o-mini",  # 모델명
    openai_api_key=openai_api_key,  # API Key 설정
)

# ---------------> 아래 코드로 변경

evaluator_llm = ChatOpenAI(
    temperature=0,  # 정확성 <-> 창의성 (0.0 ~ 2.0)
    max_tokens=1024 * 10,  # 최대 토큰수
    model_name="gpt-4o-mini",  # 모델명
    openai_api_key=openai_api_key,  # API Key 설정
)

tools = [
    DuckDuckGoSearchResults(
        name="duck_duck_go"
    ),  # General internet search using DuckDuckGo
]
llm_with_tools = evaluator_llm.bind_tools(tools)


그리고 아래와 같이 llm chain 을 구성해서 agent 로 만들어줍니다. 이후 agent 로 AgentExecutor 를 만들어주는 함수를 만듭니다.

def create_agent(prompt, llm_with_tools):
    runnable_agent = (
        {
            "input": lambda x: x["input"],
            "agent_scratchpad": lambda x: format_to_openai_tool_messages(
                x["intermediate_steps"]
            ),
        }
        | prompt
        | llm_with_tools
        | OpenAIToolsAgentOutputParser()
    )
    return AgentExecutor(agent=runnable_agent, tools=tools, handle_parsing_errors=True)


create_agent() 함수로 AgentExecutor 를 만들어서 run_on_dataset() 에 llm chain 대신 넣어주면 됩니다.

# 기존 코드 

chain_results = run_on_dataset(
    dataset_name=project_name,
    llm_or_chain_factory=summary_chain,
    evaluation=evaluation_config,
    verbose=True,
    client=client,
)

# ---------------> 아래 코드로 변경

chain_results = run_on_dataset(
    dataset_name=project_name,
    llm_or_chain_factory=functools.partial(
        create_agent, prompt=prompt, llm_with_tools=llm_with_tools
    ),
    evaluation=evaluation_config,
    verbose=True,
    client=client,
)

변경된 부분은 llm_or_chain_factory 파라미터에 할당되는 값입니다.

참고로 run_on_dataset() 함수는 추후 evaluate() 함수로 변경이 예고되어 있습니다. 테스트 코드가 아니라면 미리 아래 내용을 참고하셔서 migration 작업을 해두시는 것이 좋습니다.




참고자료


You may also like...

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.