사용자가 자연어 질문을 하면 SelfQueryRetriever는 LLM을 호출하여 질문을 두 부분으로 분해한다.
query: 의미 검색에 사용할 핵심 검색어
filter: 벡터 저장소에서 사용할 구조화된 필터
query와 filter를 벡터 저장소에 전달하면 벡터 저장소는 filter 조건에 맞게 문서를 추려내고 그 안에서 의미 검색을 수행하여 최종 결과를 반환한다.
텍스트 --> SQL문 변환
create_sql_query_chain()
랭체인에서 미리 만들어 놓은 체인
DB 테이블의 정보와 사용자의 질문을 바탕으로 sql문을 작성해준다.
대부분의 RDBMS를 지원한다.
이 체인을 실행하면 db에 접속해 테이블의 정보를 읽고 llm에게 전달한다. llm은 DB의 구조와 질문을 이해하고 SQL 쿼리문을 생성한다.
# 사용할 db 경로로 수정
db = SQLDatabase.from_uri('sqlite:///Chinook.db')
print(db.get_usable_table_names())
llm = ChatOpenAI(model='gpt-4o', temperature=0)
# 질문을 SQL 쿼리로 변환
write_query = create_sql_query_chain(llm, db)
# SQL 쿼리 실행
execute_query = QuerySQLDatabaseTool(db=db)
# combined chain = write_query | execute_query
combined_chain = write_query | execute_query
# 체인 실행
result = combined_chain.invoke({'question': '직원(employee)은 모두 몇 명인가요?'})
랭그래프를 활용한 메모리 기능
견고한 메모리 시스템의 근간을 이루는 두 가지 핵심 설계적 결정은 다음과 같다.
상태 저장 방식
MemorySaver
상태 쿼리 방식
trim_messages()
그래프는 3개의 구성요소로 이루어져 있다.
상태
노드
엣지
이 중에서 그래프를 정의할 때 가장 먼저 그래프의 상태 데이터부터 정의해야 한다. 랭그래프에서 상태는 중앙 데이터 저장소와 같다. 그렇기에 어떤 노드든 상태를 보고 상황을 파악하고, 자신의 작업 결과를 추가한다.
이런 중요한 데이터를 수정하고 추가하기 위해서는 리듀서라는 상태 업데이트 규칙을 정해야 한다. 상태 데이터를 덮어씌울지, 상태 데이터에 추가할지를 리듀서를 통해 정할 수 있다.
대화가 이어지는 엣지에 trim_messages()를 추가하여 길어진 컨텍스트를 잘라서 저장할 수 있다.
토큰의 개수를 세어서(token_counter), 일정량의 토큰(max_token)만 저장하는 것이 가능하다.
랭그래프 인지 아키텍처
이전에 나왔던 LLM 어플리케이션을 구축하기 위한 재료들(프롬프트 작성 기법, RAG, 메모리)을 결합하는 방식에 따라 무엇을 만들지가 정해진다.
랭그래프 아키텍처를 정하고 위해서 먼저 어떤 프로그램을 만들지 정의해야 한다.
예를 들어 이메일 어시스턴트를 구축한다고 가정해보자. 이때 이 어시스턴트의 목표는 수신한 이메일을 사전에 검토해 직접 확인할 이메일의 양을 줄이는 것이다.
다음으로 그 프로그램의 정해진 제약 조건을 정의해야 한다. 제약 조건을 열거하면 적합한 아키텍처를 찾는 데 큰 도움이 된다.
이때 자율성과 신뢰성 사이의 간극을 최소화 해야 한다. 어시스턴트가 사용자의 개입 없이 더 많은 작업을 수행할수록 그 유용성은 높아지지만, 과도하게 수행한다면 원치 않는 메일을 발송할 우려가 있다.
아래와 같은 체크리스트로 LLM 어플리케이션의 자율성을 얼마나 가지고 있는지 확인할 수 있다.
특정 단계의 결과물 생성(이메일 회신 초안 작성)
향후 진행할 단계 결정(신규 이메일에 대한 보관, 회신, 분류)
별도 작업(어플리케이션에 프로그래밍 하지 않은 기능을 위한 코드 작성)
LLM 인지 아키텍처는 1 ~ 5단계로 이루어져 있다.
0단계 : 코드
LLM 인지 아키텍처에 속하지 않는다. LLM을 전혀 쓰지 않는 기존의 평범한 소프트웨어
1단계 : LLM 호출
텍스트를 번역하거나 요약과 같이 특정 작업을 수행하는데 LLM을 한 번만 호출하는 경우
2단계 : 체인
미리 정해진 순서에 따라 여러 번의 LLM 호출을 구현, 예를 들어 텍스트를 SQL 쿼리문으로 변환하는 어플리케이션의 경우 텍스트를 쿼리문으로 변환 --> 쿼리문 설명 총 두 개의 체인으로 이어진다.
3단계 라우터
LLM을 활용해 실행할 단계의 순서를 정의, 체인 아키텍처는 개발자가 정한 정적인 단계를 실행하는 반면, 라우터 아키텍처는 LLM이 미리 정의된 단계 중에서 하나를 선택한다.
에이전트 아키텍처
reflection(성찰)
인간이 창조하는 과정을 모방한 아키텍처
저자와 감수자, 편집자가 상호 논의를 거쳐 책을 작성하듯이 ai가 작성하고 검수하는 과정을 넣어 더 좋은 결과물을 생성한다.
위 그림과 같이 ai가 생성한 결과물을 ai가 검수해서 좋은 결과물이 나올때까지 이를 반복하는 것이다.
여기서 가장 중요한 점은 reflect 노드로 들어오는 결과물을 HumanMessage 객체를 씌워서 사람이 작성한 결과물이라고 생각하게끔 우회하고, reflect 노드가 검수한 결과를 다시 generate에 전달할때에도 HumanMessage 객체를 씌워서 사람이 작성한 검토내용이라고 생각하게끔 우회해야 한다.
왜냐하면 LLM이 인간과 AI의 메시지 쌍을 중심으로 대화형 파인튜닝을 거쳤기 때문이다. 또한 채팅 모델은 사용자가 연속해서 다수의 메시지를 보내면 학습한 형식과 맞지 않아 성능이 떨어질 위험이 있으므로 대화가 오고 가는 형태로 데이터를 정리해야 한다.
서브 그래프 아키텍처
하나의 그래프가 다른 그래프를 호출하는 아키텍처
그래프의 인풋 아웃풋 값을 정의하는 인터페이스를 두고 각 그래프가 상태를 공유하면 바로 상태 전송, 그렇지 않다면 서브 그래프를 호출하는 노드를 둬서 함수 안에서 서브 그래프를 호출해줄 수 있다.
서브 그래프 인터페이스로 인풋 아웃풋을 정의한다면 다른 개발자들과 함께 개발도 할 수 있다.
멀티 에이전트 아키텍처
네트워크 아키텍처
각 에이전트가 다른 에이전트와 상호 통신, 모든 에이전트가 다음에 실행될 에이전트를 결정할 수 있다.
슈퍼바이저 아키텍처
모든 에이전트가 슈퍼바이저라 부르는 하나의 에이전트와 통신한다. 슈퍼바이저 에이전트는 이후 호출할 에이전트를 결정한다. 경우에 따라 슈퍼바이저 에이전트를 툴로 호출하는 방식을 구현한다.
계층 아키텍처
여러 슈퍼바이저를 총괄하는 하나의 슈퍼바이저를 활용해 멀티 에이전트 시스템을 정의한다. 이 아키텍처는 슈퍼바이저 아키텍처를 일반화해, 복잡한 제어 흐름을 보다 정교하게 관리한다.
맞춤형 멀티 에이전트 워크플로
각 LLM 에이전트는 전체 네트워크 구성 요소의 일부와만 통신한다. 일부 흐름은 고정되며, 선별된 LLM 에이전트만 다음 호출 대상을 결정한다.
LLM의 성능을 높이는 패턴
중간 출력(stream)
아키텍처가 복잡해질수록 실행 시간이 길어질 가능성이 높다. 사용자는 보통 몇 초 내로 원하는 출력이 나오길 바라므로 지연 시간이 긴 LLM 프로그램은 사용자의 유입이 줄어든다. 중간 출력 패턴을 적용해 이러한 문제를 해결할 수 있다.
input = {
'messages': [
HumanMessage(
'미국의 제30대 대통령이 사망했을 때의 나이'
)
]
}
for c in graph.stream(intput, stream_mode='updates'):
print(c)
위와 같이 stream을 사용해서 출력을 하게 되면 답변이 끝나기전부터 llm이 생각하는 것들을 출력하게 된다.
stream_mode
updates: 기본 모드로 그래프의 업데이트 내용을 출력
values: 노드의 실행이 종료(그래프 상태가 변경) 될 때마다 그래프의 전체 상태를 출력
debug: 그래프 내에서 변화가 발생할 때마다 상세한 이벤트 내용을 출력
checkpoint: 현재 상태의 새로운 체크포인트가 데이터베이스에 저장
task: 노드가 실행되기 시작하기 직전
task_result: 노드 실행이 완료
LLM 애플리케이션 테스트 기법
설계 단계: 애플리케이션에서 직접 LLM 테스트를 적용, 실행 시점에 수행되는 방식으로 실패 상황을 LLM에 전달해 스스로 수정할 수 있도록 함
사전 제작: 운영 환경에 배포되기 직전에 테스트 실시, 테스트를 통해 애플리케이션이 실제 사용자에게 공개되기 전 발생할 수 있는 오류를 사전에 수정 가능
운영: 운영 중인 애플리케이션에 테스틀 실행해 실제 사용자에게 영향을 미칠 수 있는 오류를 모니터링하고 탐지
설계 단계 - 자체 보정 RAG
LLM을 활용하여 검색 결과의 관련도를 평가해 환각 문제를 해결
라우팅 단계에서 질문을 적절한 검색 메서드(벡터 저장소 or 웹 검색)로 라우팅
LLM이 문서를 검색하고 관련성을 평가
관련 문서가 있다면 LLM은 답변 생성 (위 그림에서 관련 문서가 없나?에 관해 있음 없음이 반대로 표기되어 있는 것)
LLM은 답변 내 환각 여부를 점검하고 관련성이 확인된 경우에만 답변 제시
검색된 문서가 부적절하거나 생성 AI의 답변이 질문에 충분한 답을 내지 못하면 웹 검색을 활용해 관련 정보 검색
vector DB에서 검색한 문서의 정확도를 LLM이 판단하여 사용 가능하다면 사용하고 그렇지 않다면 웹 검색을 해서 더 정확도 있는 답변을 할 수 있다.