복잡함 속에서 발견한 단순함의 미학, LCEL과 Runnable 이야기
언제부터였을까요. 인공지능과 대화하는 코드를 짜면서, 정작 제 코드는 길을 잃고 헤매기 시작했습니다. 하나의 질문을 던지고 답을 얻기 위해 거쳐야 하는 과정은 생각보다 복잡했고, 코드는 금세 얽히고설킨 실타래처럼 되어버렸죠. '더 나은 방법은 없을까?'라는 고민이 깊어질 무렵, 저는 LangChain의 새로운 언어, LCEL을 만났습니다. 그것은 단순한 문법이 아니라, 코드를 바라보는 새로운 관점이었습니다.
"복잡한 코드의 숲에서 길을 잃었을 때, LCEL은 제게 지팡이가 되어주었습니다. 생각의 흐름 그대로, 물 흐르듯 코드를 엮어낼 수 있다는 가능성을 보여주었죠."
사용자의 질문이 프롬프트가 되고, 프롬프트가 거대한 언어 모델(LLM)의 심장을 두드려, 마침내 하나의 결과물로 태어나는 여정. 이 모든 과정을 단 몇 줄의 직관적인 코드로 꿰어낼 수 있다는 사실이 놀라웠습니다. 특히, 마법의 주문처럼 느껴졌던 파이프 기호 | 는 각 단계를 잇는 우아한 다리였습니다. 프롬프트, 모델, 그리고 결과물 파서가 이 파이프를 통해 하나의 생명처럼 유기적으로 연결되는 모습을 보며, 저는 비로소 엉켜있던 실타래를 풀 용기를 얻었습니다. 물론 처음에는 모든 것이 추상화되어 있어 안개가 낀 것처럼 막막하기도 했지만, 그 안개를 걷어냈을 때 비로소 코드의 본질과 마주할 수 있었습니다.
LCEL이라는 새로운 세상의 중심에는 'Runnable'이라는 핵심 개념이 있었습니다. 앞으로 제 코드의 모든 여정에 함께할 든든한 동반자들이었죠. 이들은 저마다의 개성과 역할로 저의 코드를 더욱 풍성하고 효율적으로 만들어 주었습니다. 오늘은 그중에서도 가장 인상 깊었던 세 명의 조력자를 소개할까 합니다.
1. 가장 겸손한 전달자, RunnablePassthrough
코딩을 하다 보면 종종 사소한 형식 때문에 발목을 잡힐 때가 있습니다. 분명 내용물은 같은데, 'country'라는 열쇠가 없으면 열리지 않는 상자처럼 말이죠.
{"country": "대한민국"}
이라고 정성껏 포장해야만 했던 데이터를, 그저 "대한민국"이라는 말 한마디로 건넬 수 있게 해 준 것이 바로 RunnablePassthrough였습니다.
"그것은 마치, 시스템이 제 마음을 읽어주는 듯한 경험이었습니다. 불필요한 형식을 벗어던지고 핵심에만 집중할 수 있게 된 순간이었죠."
특히 여러 정보(문서, 질문 등)가 필요한 RAG 같은 복잡한 작업에서, 사용자의 질문만큼은 다른 어떤 것에도 방해받지 않고 곧장 프롬프트의 심장부로 전달해야 할 때, 이 겸손한 전달자는 묵묵히 자신의 역할을 수행하며 전체 흐름을 매끄럽게 만들었습니다.
2. 시간을 지배하는 마법사, RunnableParallel
만약 AI에게 두 가지 질문을 동시에 던질 수 있다면 어떨까요? 한쪽에서는 수도를 묻고, 다른 한쪽에서는 인구를 물으며, 두 개의 답을 동시에 얻는 것입니다. RunnableParallel은 바로 그 상상을 현실로 만들어 주었습니다.
map_chain = RunnableParallel(수도=chain1, 인구=chain2)
이 한 줄의 코드는 제게 시간을 선물했습니다. 순서대로 처리되길 기다릴 필요 없이, 독립적인 두 개의 체인이 각자의 길을 달려가 마침내 하나의 지점에서 만나는 모습은 경이롭기까지 했습니다. 코드는 간결해졌고, 실행 속도는 눈에 띄게 빨라졌습니다. 마치 저 혼자 일하는 것이 아니라, 유능한 두 명의 동료와 함께 협업하는 듯한 기분이었습니다. 결과물은 {'수도': '서울', '인구': '약 5,170만 명'}처럼 깔끔한 사전 형태로 제 손에 쥐어졌죠.
3. 나만의 색을 입히는 예술가, RunnableLambda
AI가 내놓은 결과물이 항상 완벽할 수는 없습니다. 때로는 두 개의 답변을 하나로 합치고 싶을 때도, 혹은 저만의 스타일로 다듬고 싶을 때도 있죠. RunnableLambda는 바로 그 순간, 저에게 붓을 쥐여주는 예술가와 같았습니다. 제가 직접 만든 함수를 체인의 한가운데 당당히 배치할 수 있게 해 주었으니까요.
info_chain = map_chain | RunnableLambda(combine_text)
저는 RunnableParallel이 가져다준 두 개의 답변을 combine_text라는 저만의 함수로 넘겼습니다. 이 함수 안에서 저는 두 문장을 자연스럽게 연결하고, 감성을 더하는 이모티콘을 추가하기도 했습니다. 기성품에 제 손길을 더해 저만의 작품을 만드는 과정이었습니다. 이렇게 만들어진 새로운 정보는 다시 다음 프롬프트의 재료가 되어, 더욱 깊이 있고 완성도 높은 최종 결과물을 탄생시켰습니다. 정해진 길을 따르는 것이 아니라, 길 위에서 저만의 오솔길을 만드는 즐거움을 알게 된 것입니다.
LangChain의 LCEL과 Runnable을 알아가는 여정은 단순히 새로운 기술을 배우는 시간이 아니었습니다. 그것은 제 생각과 논리를 코드에 투영하고, AI와 더 깊이 교감하는 과정이었습니다. 복잡함에 가려져 있던 본질을 발견하고, 저만의 방식으로 문제를 해결해 나가는 즐거움을 깨닫게 된 소중한 경험이었죠.
이제 코드는 제게 더 이상 딱딱한 명령의 나열이 아닙니다. 저의 의도와 감성이 담긴, 세상을 향한 또 다른 저의 이야기가 되었습니다. 당신의 코드는 지금, 어떤 이야기를 하고 있나요?