Docs | Src |  A-  |  A*  |  A+  | -/-

Metadata
sourcehttps://pocketflow.substack.com/p/i-built-an-llm-framework-in-just
created2026-08-22
byopenai:gpt-5.6-terra
# 단 100줄로 LLM 프레임워크 포켓 플로우를 만든 이유 [![이미지 1](https://substackcdn.com/image/fetch/$s_!C4V5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17492b54-bf92-49d4-ba46-ebb8bf3752c6_2558x859.png)](https://substackcdn.com/image/fetch/$s_!C4V5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17492b54-bf92-49d4-ba46-ebb8bf3752c6_2558x859.png) > _복잡한 AI 프레임워크를 바라보다가 “정말 이렇게까지 복잡해야 하나?”라고 생각해 본 적이 있는가? 비대해진 프레임워크와 1년간 씨름한 끝에, 불필요한 것은 모두 걷어내기로 했다. 그 결과물이 바로 [포켓 플로우](https://github.com/The-Pocket/PocketFlow)다. 단 [100줄의 코드](https://github.com/The-Pocket/PocketFlow/blob/main/pocketflow/\_\_init\_\_.py)로 만든 미니멀 LLM 프레임워크다._ 지난 1년 동안 랭체인 같은 대중적인 프레임워크를 사용해 AI 애플리케이션을 만들었다. 하지만 경험은 계속해서 답답했다. * **비대해진 추상화(Bloated Abstraction)**: 옥토마인드의 엔지니어링 팀이 [설명](https://www.octomind.dev/blog/why-we-no-longer-use-langchain-for-building-our-ai-agents)했듯이, _“처음에는 단순한 요구 사항이 랭체인의 사용 전제와 맞아 도움이 됐다. 하지만 고수준 추상화 때문에 코드가 곧 이해하기 어렵고 유지보수하기도 힘들어졌다.”_ 이런 프레임워크는 단순한 기능을 불필요한 복잡성 뒤에 숨긴다. * **구현의 악몽(Implementation Nightmares)**: 추상화 문제를 넘어, 이런 프레임워크는 과도한 의존성, 버전 충돌, 끊임없이 바뀌는 인터페이스를 개발자에게 떠넘긴다. 개발자들은 흔히 [불평](https://www.reddit.com/r/LangChain/comments/1j1gb88/why_are_developers_moving_away_from_langchain/)한다. _“안정적이지 않고, 인터페이스는 계속 바뀌며, 문서는 늘 최신 상태가 아니다.”_ 한 개발자는 [농담](https://www.reddit.com/r/LocalLLaMA/comments/1iudao8/langchain_is_still_a_rabbit_hole_in_2025/)하기도 했다. _“이 문장을 읽는 사이에 랭체인은 문서도 업데이트하지 않은 채 클래스 4개를 사용 중단 처리했다.”_ 그래서 이런 의문이 들었다. 정말 이렇게 많은 래퍼가 필요할까? 모든 것을 걷어내면 어떨까? 진정으로 최소한이면서도 실용적인 것은 무엇일까? LLM 애플리케이션을 밑바닥부터 1년간 만들고 나서 깨달았다. 모든 복잡성 아래에서 LLM 시스템은 본질적으로 **단순한 방향 그래프(simple directed graphs)**일 뿐이다. 불필요한 계층을 제거해, 의존성도 없고 특정 벤더에 종속되지도 않으며 군더더기도 없는 포켓 플로우를 만들었다. 전체 코드는 단 [100줄](https://github.com/The-Pocket/PocketFlow/blob/main/pocketflow/__init__.py)이다. [![이미지 2](https://substackcdn.com/image/fetch/$s_!Q7JV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa0c26b1-c28b-47af-a66d-ab04db5c2613_1050x502.png)](https://substackcdn.com/image/fetch/$s_!Q7JV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa0c26b1-c28b-47af-a66d-ab04db5c2613_1050x502.png) _AI 시스템 프레임워크의 추상화 수준, 애플리케이션별 래퍼, 벤더별 래퍼, 코드 줄 수 및 크기 비교._ 포켓 플로우를 잘 정리된 주방이라고 생각해 보자. * **노드(Nodes)**는 손질, 조리, 플레이팅을 담당하는 조리 작업대와 같다. ``` class BaseNode: def __init__(self): self.params,self.successors={},{} def add_successor(self,node,action="default"): self.successors[action]=node;return node def prep(self,shared): pass def exec(self,prep_res): pass def post(self,shared,prep_res,exec_res): pass def run(self,shared): p=self.prep(shared);e=self.exec(p);return self.post(shared,p,e) ``` * **플로우(Flow)**는 다음에 어느 작업대로 갈지 정하는 레시피다. ``` class Flow(BaseNode): def __init__(self,start): super().__init__();self.start=start def get_next_node(self,curr,action): return nxt=curr.successors.get(action or "default") def orch(self,shared,params=None): curr,p=copy.copy(self.start),(params or {**self.params}) while curr: curr.set_params(p);c=curr.run(shared);curr=copy.copy(self.get_next_node(curr,c)) def run(self,shared): pr=self.prep(shared);self.orch(shared);return self.post(shared,pr,None) ``` * **공유 저장소(Shared store)**는 모든 작업대가 재료를 볼 수 있는 조리대다. 보통 메모리 내 딕셔너리다. ``` load_data_node = LoadDataNode() summarize_node = SummarizeNode() load_data_node >> summarize_node flow = Flow(start=load_data) shared = {"file_name": "data.txt"} flow.run(shared) ``` 이 주방, 즉 에이전트 시스템에서는 다음과 같이 작동한다. 1. 각 작업대(노드)는 세 가지 단순한 작업을 수행한다. * **준비(Prep)**: 공유 저장소에서 필요한 것을 가져온다. 재료를 모으는 단계다. * **실행(Exec)**: 전문 작업을 수행한다. 재료를 조리하는 단계다. * **후처리(Post)**: 결과를 공유 저장소에 반환하고 다음 단계를 결정한다. 요리를 내고 다음에 무엇을 만들지 정하는 단계다. 1. 레시피, 즉 플로우는 조건에 따라 실행을 지시한다(**오케스트레이션(Orch)**). * “채소를 썰었으면 조리 작업대로 간다.” * “음식이 조리됐으면 플레이팅 작업대로 간다.” 노드와 플로우 모두에서 배치 처리, 비동기 실행, 병렬 처리도 지원한다. 이것이 전부다. LLM 애플리케이션을 만드는 데 필요한 것도 전부다. 불필요한 추상화도, 복잡한 아키텍처도 없다. 강력한 시스템을 조합해 만들 수 있는 단순한 구성 요소만 있다. [![이미지 3](https://substackcdn.com/image/fetch/$s_!0F5y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F471cbbb7-5270-481b-a1ae-f9428fda5777_1050x285.png)](https://substackcdn.com/image/fetch/$s_!0F5y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F471cbbb7-5270-481b-a1ae-f9428fda5777_1050x285.png) 포켓 플로우 핵심 그래프 추상화 다른 프레임워크와 달리 포켓 플로우는 벤더별 API를 의도적으로 묶어 제공하지 않는다. 이유는 다음과 같다. * **의존성 문제 없음(No Dependency Issues)**: 현재 LLM 프레임워크에는 수백 MB에 이르는 의존성이 딸려온다. 포켓 플로우에는 의존성이 전혀 없어 프로젝트를 가볍고 민첩하게 유지한다. * **벤더 종속 없음(No Vendor Lock-in)**: 핵심 아키텍처를 바꾸지 않고도 오픈라마 같은 로컬 모델을 비롯해 원하는 모델을 자유롭게 사용할 수 있다. * **맞춤형 완전 제어(Customized Full Control)**: 프롬프트 캐싱, 배치 처리, 스트리밍이 필요한가? 미리 만들어진 추상화와 씨름하지 말고 필요한 기능을 정확히 만들면 된다. API 래퍼가 필요하다면 어떻게 할까? 챗GPT 같은 모델에게 즉석에서 작성해 달라고 하면 된다. 보통 10줄 정도의 코드면 충분하다. 이 방식은 금세 낡아버리는 경직된 내장 래퍼나 추상화보다 훨씬 유연하다. ``` def call_llm(prompt): from openai import OpenAI client = OpenAI(api_key="여기에_API_키를_입력하세요") r = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": prompt}] ) return r.choices[0].message.content # 사용 예 call_llm("안녕하세요, 세계!") ``` 이처럼 최소한이지만 강력한 구성 요소를 사용하면, 모든 구성 요소를 투명하게 살피고 완전히 제어하면서 정교한 [에이전트](https://the-pocket.github.io/PocketFlow/design_pattern/agent.html), [RAG 시스템](https://the-pocket.github.io/PocketFlow/design_pattern/rag.html), [LLM 워크플로](https://the-pocket.github.io/PocketFlow/design_pattern/workflow.html)를 만들 수 있다. 예를 살펴보자. 포켓 플로우의 구성 요소를 사용해 간단한 [웹 검색 에이전트](https://zacharyhuang.substack.com/p/llm-agent-internal-as-a-graph-tutorial)를 만들어 보자. 퍼플렉시티 AI 같은 도구처럼 웹을 검색하고 질문에 답하는 간단한 웹 검색 AI 에이전트다. 이 에이전트의 동작을 단순한 플로우 그래프로 모델링하면 다음과 같다. [![이미지 4](https://substackcdn.com/image/fetch/$s_!vNMz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70d20df9-c396-4c32-b315-e96e136ca76e_800x423.jpeg)](https://substackcdn.com/image/fetch/$s_!vNMz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70d20df9-c396-4c32-b315-e96e136ca76e_800x423.jpeg) ``` # 각 노드의 인스턴스를 생성한다 decide = DecideAction() search = SearchWeb() answer = AnswerQuestion() # 노드를 연결한다 # DecideAction이 "search"를 반환하면 SearchWeb으로 이동한다 decide - "search" >> search # DecideAction이 "answer"를 반환하면 AnswerQuestion으로 이동한다 decide - "answer" >> answer # SearchWeb이 완료되어 "decide"를 반환하면 DecideAction으로 돌아간다 search - "decide" >> decide # DecideAction 노드에서 시작하는 플로우를 생성하고 반환한다 return Flow(start=decide) ``` 각 노드에서는 어떤 일이 일어날까? 1. **DecideAction** — _“웹을 검색해야 할까, 아니면 이미 충분히 알고 있을까?”_ * **준비(Prep)**: 공유 메모리에서 원래 질문과 이전 검색 맥락을 가져온다. * **실행(Exec)**: LLM에게 웹 검색을 수행할지, 바로 답변할지를 묻는다. * **후처리(Post)**: 필요하면 검색 쿼리를 저장하고, 다음 동작으로 `"search"` 또는 `"answer"`를 반환한다. 1. **SearchWeb** — _“최신 정보를 가져오자.”_ * **준비(Prep)**: 이전 단계에서 생성한 쿼리를 가져온다. * **실행(Exec)**: 구글, 빙 등의 웹 검색 API를 호출해 결과를 가져오고 읽기 쉬운 청크로 정리한다. * **후처리(Post)**: 검색 결과를 맥락에 추가한 후, 재평가를 위해 **DecideAction**으로 다시 돌아간다. 1. **AnswerQuestion** — _“정보가 충분하니 질문에 답하자.”_ * **준비(Prep)**: 질문과 모든 검색 맥락을 수집한다. * **실행(Exec)**: LLM에 프롬프트를 전달해 충분히 조사한 유용한 답변을 생성한다. * **후처리(Post)**: 최종 응답을 저장하고 플로우를 끝내도록 `"done"`을 알린다. 그래프는 동적이고 투명하며 확장하기 쉽다. 핵심 로직을 전혀 훼손하지 않고도 다른 LLM을 연결하거나 검색 엔진을 바꾸고, 새로운 의사결정 지점을 추가할 수 있다. 에이전트에게 “2023년 슈퍼볼 우승 팀은 누구야?”라고 물었다고 해 보자. 각 노드에서 다음과 같은 일이 순서대로 일어난다. 1. **DecideAction 노드**: * **확인(Prep)**: 사용자의 질문과 지금까지 알고 있는 정보, 즉 아직 없는 정보를 확인한다. * **판단(Exec)**: “2023년 슈퍼볼 우승 팀이 누구인지 모르겠어. 검색해야 해.” * **결정(Post)**: “2023 Super Bowl winner”를 검색하기로 한다. * **전달(Orch)**: SearchWeb 작업대로 전달한다. 1. **SearchWeb 노드**: * **확인(Prep)**: “2023 Super Bowl winner”라는 검색 쿼리를 확인한다. * **수행(Exec)**: 인터넷을 검색한다. “캔자스시티 치프스가 우승했다”는 결과를 찾았다고 가정해 보자. * **저장(Post)**: 검색 결과를 공유 조리대에 저장한다. * **전달(Orch)**: DecideAction 작업대로 다시 전달한다. 1. **DecideAction 노드(두 번째)**: * **확인(Prep)**: 사용자의 질문과 이제 알고 있는 정보, 즉 검색 결과를 확인한다. * **판단(Exec)**: “좋아, 이제 2023년 슈퍼볼에서 치프스가 우승했다는 것을 알겠어.” * **결정(Post)**: 답변하기에 정보가 충분하다고 판단한다. * **전달(Orch)**: AnswerQuestion 작업대로 전달한다. 1. **AnswerQuestion 노드**: * **확인(Prep)**: 사용자의 질문과 수집한 모든 조사 결과를 확인한다. * **수행(Exec)**: 모든 정보를 사용해 친절한 답변을 작성한다. * **저장(Post)**: 최종 답변을 저장한다. * **완료(Orch)**: 작업이 끝난다! 이것으로 끝이다. 단순하고 우아하며 검색을 기반으로 작동한다. 전체 에이전트 구현에는 수백 줄 정도의 코드만 필요하며, 그 기반은 100줄짜리 프레임워크다. 전체 코드는 [이 쿡북](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-agent)에서 확인하고 직접 실행할 수 있다. 이것이 포켓 플로우의 본질이다. 조합 가능한 노드와 단순한 그래프로 영리하고 반응성 높은 AI 에이전트를 만든다. 숨겨진 마법은 없다. 프레임워크 묘기도 없다. 명확한 로직과 완전한 제어만 있을 뿐이다. 포켓 플로우는 검색 에이전트에만 국한되지 않는다. [멀티 에이전트](https://the-pocket.github.io/PocketFlow/design_pattern/multi_agent.html), [워크플로](https://the-pocket.github.io/PocketFlow/design_pattern/workflow.html), [RAG 시스템](https://the-pocket.github.io/PocketFlow/design_pattern/rag.html), [맵리듀스 작업](https://the-pocket.github.io/PocketFlow/design_pattern/mapreduce.html), [스트리밍](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-llm-streaming), [슈퍼바이저](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-supervisor), [채팅 메모리](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-chat-memory), [모델 컨텍스트 프로토콜](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-mcp) 등 원하는 모든 것을 똑같이 우아하고 단순하게 만들 수 있다. 모든 구현은 같은 패턴을 따른다. 최소 100줄 프레임워크를 토대로, 기본 원칙에 따라 수백 줄의 코드로 구축한다. 불필요한 추상화도, 군더더기도 없다. 수십만 개의 파일로 이루어진 거대한 프레임워크를 이해하려 애쓰는 대신, 포켓 플로우는 기초를 제공하므로 바닥부터 스스로 이해를 쌓을 수 있다. 모든 구현의 전체 튜토리얼은 [포켓 플로우 깃허브 저장소](https://github.com/The-Pocket/PocketFlow)에서 찾을 수 있다. 시작하려면 [기본 튜토리얼](https://github.com/The-Pocket/PocketFlow#-below-are-basic-tutorials)도 살펴보자. [![이미지 5](https://substackcdn.com/image/fetch/$s_!GEQH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9084f118-eb64-40ed-a866-0c0b68eafe6a_1050x408.png)](https://substackcdn.com/image/fetch/$s_!GEQH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9084f118-eb64-40ed-a866-0c0b68eafe6a_1050x408.png) 포켓 플로우 기반 디자인 패턴 포켓 플로우의 진정한 힘은 미니멀한 설계를 넘어선다. 가장 혁신적인 측면은 **[에이전틱 코딩(Agentic Coding)](https://zacharyhuang.substack.com/p/agentic-coding-the-most-fun-way-to)**을 가능하게 한다는 점이다. 에이전틱 코딩은 AI 어시스턴트의 도움을 받아 AI 애플리케이션을 만들고 수정하는 새로운 프로그래밍 방식이다. 에이전틱 코딩은 AI와 함께 소프트웨어를 만드는 일이다. 집을 짓는 일에 비유할 수 있다. 당신은 비전과 전문성을 갖춘 건축가이고, AI는 세부 작업을 맡는 건설 인력이다. * **당신(You)**은 고수준 설계와 전략적 의사결정에 집중한다. 이는 인간의 강점이다. * **AI 어시스턴트(The AI assistant)**는 구현 세부 사항과 기술적 실행을 맡는다. 이는 AI의 강점이다. * **당신(You)**은 결과를 검토하고 다듬으며 과정을 이끈다. 이 10배 생산성 향상은 반복적인 패턴을 코딩하는 시간은 줄이고 창의적으로 문제를 해결하는 시간은 늘려 준다. [![이미지 6](https://substackcdn.com/image/fetch/$s_!wzK9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2924351-037c-4add-a40f-6384ab230a7d_1050x643.png)](https://substackcdn.com/image/fetch/$s_!wzK9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2924351-037c-4add-a40f-6384ab230a7d_1050x643.png) 실전 에이전틱 코딩 AI에게 강력한 LLM 애플리케이션을 만드는 법을 어떻게 가르칠 수 있을까? 기존 프레임워크는 잘못된 접근법을 택했다. [요약](https://python.langchain.com/v0.1/docs/use_cases/summarization/), [태깅](https://python.langchain.com/v0.1/docs/use_cases/tagging/), [웹 스크래핑](https://python.langchain.com/v0.1/docs/use_cases/web_scraping/) 등 특정 애플리케이션을 위한 하드코딩된 래퍼를 만들었고, 결국 인간 개발자와 AI 어시스턴트 모두를 혼란스럽게 했다. 해결책은 우아할 만큼 단순하다. **두 번째 코드베이스로서의 문서화(Documentation as the second codebase)!** 하드코딩된 래퍼 대신 문서에서 바이브 코딩으로 구현한다. 포켓 플로우는 단 100줄의 핵심 구성 요소와 함께, 이 구성 요소들을 조합해 강력한 애플리케이션을 만드는 법을 가르치는 명확한 문서를 제공한다. 예시를 제공하고 AI 에이전트가 즉석에서 해결책을 구현하도록 맡긴다. 이 문서-코드 접근 방식으로 AI 어시스턴트는 다음을 할 수 있다. 1. **기초 숙달(Master the fundamentals)**: 프레임워크의 복잡성에 빠져들지 않고 소수의 구성 요소를 익힌다. 2. **맞춤형 해결책 구축(Build customized solutions)**: 특정 애플리케이션의 요구에 완벽히 맞춘 구현을 생성한다. 3. **아키텍처에 집중(Focus on architecture)**: 프레임워크의 한계와 싸우는 대신 시스템 설계를 고민한다. 이런 “사용 설명서”를 규칙 파일 형태로 AI 어시스턴트에게 직접 전달한다. 예를 들어 커서 AI에는 [.cursorrules](https://github.com/The-Pocket/PocketFlow/blob/main/.cursorrules)를 제공한다. 그러면 단순한 구성 요소로 정교한 시스템을 구축할 지식을 갖추게 된다. 이 접근법을 더 깊이 살펴보려면 [에이전틱 코딩: LLM 앱을 만드는 가장 즐거운 방법](https://zacharyhuang.substack.com/p/agentic-coding-the-most-fun-way-to)을 방문하거나, 더 많은 튜토리얼을 보려면 [유튜브 채널](https://www.youtube.com/@ZacharyLLM?sub_confirmation=1)을 확인하라. 미래의 비전은 더욱 흥미롭다. 포켓 플로우 패턴이 개발자 생태계 전반으로 퍼지면, 결국 미래 LLM의 학습 데이터에 흡수될 것이다. 그때는 명시적인 문서조차 필요 없어질 수 있다. AI 어시스턴트가 이러한 원칙을 본질적으로 이해하게 되어 LLM 애플리케이션 개발이 진정으로 마찰 없이 이루어질 것이다. 포켓 플로우는 복잡성을 걷어내고 필요한 것만 제공한다. LLM 애플리케이션을 단순한 방향 그래프로 모델링하는 [100줄의 코드](https://github.com/The-Pocket/PocketFlow/blob/main/pocketflow/__init__.py)다. 군더더기도 마법도 없다. 투명한 로직과 완전한 제어만 있다. 프레임워크 묘기에 지쳤고 바닥부터 이해를 쌓고 싶다면, 포켓 플로우의 미니멀한 접근법으로 오늘 강력한 에이전트를 만들 수 있다. 동시에 내일의 에이전틱 코딩 혁명도 준비할 수 있다. [디스코드 커뮤니티](https://discord.gg/hUHHE9Sa6T)에 참여해 포켓 플로우로 개발하는 다른 개발자들과 교류하라!