<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Hamp의 분리수거함</title>
    <link>https://hamp.tistory.com/</link>
    <description>남들에게 보여주기 부끄러운 잡다한 글을 적어 나가는 자칭 기술 블로그입니다.</description>
    <language>ko</language>
    <pubDate>Mon, 18 May 2026 19:10:33 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Hamp</managingEditor>
    <image>
      <title>Hamp의 분리수거함</title>
      <url>https://tistory1.daumcdn.net/tistory/5005273/attach/cecf7b015f234b25a4930a944a389e5e</url>
      <link>https://hamp.tistory.com</link>
    </image>
    <item>
      <title>바이너리 분석하기</title>
      <link>https://hamp.tistory.com/346</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;swift.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deYYSG/dJMb990KDNK/kccq3RP0Mjpku57YTBKtV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deYYSG/dJMb990KDNK/kccq3RP0Mjpku57YTBKtV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deYYSG/dJMb990KDNK/kccq3RP0Mjpku57YTBKtV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeYYSG%2FdJMb990KDNK%2Fkccq3RP0Mjpku57YTBKtV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;250&quot; height=&quot;250&quot; data-filename=&quot;swift.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  들어가기 전&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;랭그래프 포스팅 이후, 약 3개월만의 포스팅이다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사와 사이드에 치이다보니, 학습시간이 많이 없어 포스팅을 할께 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 최근 회사에서 바이너리를 분석하는 경험을 하게되었는데, 그 때 알게된 지식과 유용한 tool 사용버을 기록하고자한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  학습할 내용&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nm&lt;/li&gt;
&lt;li&gt;otool&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✊ nm&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-size=&quot;size18&quot; data-ke-style=&quot;style1&quot;&gt;유닉스 계열 운영체제의 object, 실행, 라이브러리등&lt;br /&gt;파일내에 포함된 심볼을 확인하는 도구&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  역할&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴파일/링크 오류 디버깅&lt;/li&gt;
&lt;li&gt;라이브러리 분석&lt;/li&gt;
&lt;li&gt;바이너리 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  명령어&lt;/h3&gt;
&lt;pre id=&quot;code_1778731826185&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nm -[옵션 나열..] [파일]&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 152px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 19px;&quot;&gt;옵션&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 19px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 19px;&quot;&gt;-n (numeric-sort)&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 19px;&quot;&gt;주소 순서대로 정렬&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 19px;&quot;&gt;-u (undefined-only)&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 19px;&quot;&gt;정의도지 않은 심볼만&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 19px;&quot;&gt;-C (Demangle)&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 19px;&quot;&gt;Mangling 심볼을 다시 사람이 읽기 쉬운 형태로 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 19px;&quot;&gt;-D (dyamic)&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 19px;&quot;&gt;동적 심볼로 표시 (공유 라이브러리 분석 시 유용)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 19px;&quot;&gt;-p (no-sort)&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 19px;&quot;&gt;정렬하지 않고 파일 순서대로 출력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 19px;&quot;&gt;--define-only&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 19px;&quot;&gt;정의된 심볼만 출력 (정적 라이브러리 내 제공 심볼 확인)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.9535%;&quot;&gt;-S&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%;&quot;&gt;크기 및 끝 주소 필드 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 19px;&quot;&gt;--size-sort&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 19px;&quot;&gt;크기 내람차순 정렬&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  분석&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;먼저 출력 결과는 다음과 같이 구성되어있다.&lt;/p&gt;
&lt;pre id=&quot;code_1778731954031&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[주소] [심볼 타입] [심볼 이름]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;b&gt;  &lt;/b&gt;심볼 타입&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;대문자는 외부공개(전역), 소문자는 내부용&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 110px;&quot; border=&quot;1&quot; width=&quot;100%&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;span&gt;T/t&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;코드 섹션에 있는 심볼로, 보통 &lt;b data-index-in-node=&quot;32&quot; data-path-to-node=&quot;10,0,0&quot;&gt;함수&lt;/b&gt;를 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;span&gt;D/d&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;span&gt;Data영역, 초기화된 전역 변수나 정적 변수가 위치하는 섹션&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;span&gt;B/b&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;span&gt;BSS영역, 초기화되지 않은 전역 변수가 위치하는 섹션&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;span&gt;S/s&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;span&gt;data section, (small 포함, ObjC metadata 포함 가능)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;span&gt;U&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;해당 파일 내에 정의되어 있지 않고, 외부에서 가져와야 하는 심볼&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;span&gt;R/r&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;span&gt;읽기 전용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;W/ V&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;약(weak) 심볼, 링커가 중복 시&lt;b&gt; 강 심볼로 대체&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;C&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;공용(Common)심볼&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;간단한 예제와 실제결과물과 함께 분석을해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;objc코드냐, swift코드냐에 따라 조금 포맷이 갈린다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Objc: OBJC로 시작&lt;/li&gt;
&lt;li&gt;Swift: $s로 시작: mangled symbol&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1778731901003&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// OBJC
0000000002ec8e88 S _OBJC_CLASS_$_WXWebViewController

// Swift
000000000175b260 s _$s12FirebaseAuth19ActionCodeOperationOMB
000000000031d1dc T _$s12FirebaseAuth19ActionCodeOperationOMa
00000000017dfb20 s _$s12FirebaseAuth19ActionCodeOperationOMf
00000000016a3354 S _$s12FirebaseAuth19ActionCodeOperationOMn
00000000017dfb30 S _$s12FirebaseAuth19ActionCodeOperationON
00000000014cc838 S _$s12FirebaseAuth19ActionCodeOperationOSHAAMc
0000000001a1ff00 b _$s12FirebaseAuth19ActionCodeOperationOSHAAMcMK
000000000031d160 t _$s12FirebaseAuth19ActionCodeOperationOSHAASH13_rawHashValue4seedS2i_tFTW&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;여기서 swift와 Objc는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;_ $s&lt;span&gt; &amp;rarr; Swift symbol&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;s, t, T -&amp;gt; 심볼 타입&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;12FirebaseAuth: 모듈명길이 + 모듈명&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;19ActionCodeOpetion: 타입명길이 + 타입명&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Suffx: 런타임 메타데이터, 최종 어떤 구조인지 추측할 수 있는 정보&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;☝️otool&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;Mach-O 해부기&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  역할&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mach-O 내부 구조를 분석&lt;/li&gt;
&lt;li&gt;@rpath&lt;/li&gt;
&lt;li&gt;linking 분석&lt;/li&gt;
&lt;li&gt;중복 framework 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;  명령어&lt;/h3&gt;
&lt;pre id=&quot;code_1778929039094&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;ottol -[옵션] [Mach-O 바이너리]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 169px;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 19px;&quot;&gt;옵션&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 19px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 19px;&quot;&gt;-L&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 19px;&quot;&gt;의존하는 dynamic library 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 19px;&quot;&gt;-l&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 19px;&quot;&gt;Load Commands&amp;nbsp;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.9535%; height: 19px;&quot;&gt;-h&lt;/td&gt;
&lt;td style=&quot;width: 81.0465%; height: 19px;&quot;&gt;Mach header 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  소감 및 마무리&lt;/b&gt;&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; width=&quot;100%&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;목적&lt;/td&gt;
&lt;td&gt;nm&lt;/td&gt;
&lt;td&gt;tool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;함수 심볼 보기&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;△&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;undefined symbol 확인&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;dylib dependency&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;X&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;rpath 확인&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;X&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;Mach-O 구조&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;X&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;dynamic/static 판단&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;△&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;disassemble&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;△&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://jinolab.tistory.com/254&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jinolab.tistory.com/254&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1778730615449&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;nm 도구로 ELF/오브젝트 심볼 한눈에 파악하기&quot; data-og-description=&quot;nm(GNU binutils 패키지)는 실행 파일(ELF), 오브젝트(.o), 라이브러리(.a) 안에포함된 심볼-‐함수&amp;middot;변수&amp;middot;섹션&amp;middot;링커 심볼-‐을 목록화해주소&amp;middot;크기&amp;middot;속성(정의/미정의, 글로벌/로컬 등)을 보여주는 전통&quot; data-og-host=&quot;jinolab.tistory.com&quot; data-og-source-url=&quot;https://jinolab.tistory.com/254&quot; data-og-url=&quot;https://jinolab.tistory.com/254&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bgjOJ7/dJMb83SnTqz/SXQxfMvqKUSfNrjxjdplZ1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/hQFXc/dJMb8QMgZDq/kY92BvxGmcGdaRpqGRoUP1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://jinolab.tistory.com/254&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jinolab.tistory.com/254&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bgjOJ7/dJMb83SnTqz/SXQxfMvqKUSfNrjxjdplZ1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/hQFXc/dJMb8QMgZDq/kY92BvxGmcGdaRpqGRoUP1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;nm 도구로 ELF/오브젝트 심볼 한눈에 파악하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;nm(GNU binutils 패키지)는 실행 파일(ELF), 오브젝트(.o), 라이브러리(.a) 안에포함된 심볼-‐함수&amp;middot;변수&amp;middot;섹션&amp;middot;링커 심볼-‐을 목록화해주소&amp;middot;크기&amp;middot;속성(정의/미정의, 글로벌/로컬 등)을 보여주는 전통&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jinolab.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS/Modularization</category>
      <author>Hamp</author>
      <guid isPermaLink="true">https://hamp.tistory.com/346</guid>
      <comments>https://hamp.tistory.com/346#entry346comment</comments>
      <pubDate>Sat, 16 May 2026 20:03:53 +0900</pubDate>
    </item>
    <item>
      <title>히스토리 관리</title>
      <link>https://hamp.tistory.com/345</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbDWJv/dJMcabwvnGh/a5o0oaiWdiV37bhUjB8Qy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbDWJv/dJMcabwvnGh/a5o0oaiWdiV37bhUjB8Qy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbDWJv/dJMcabwvnGh/a5o0oaiWdiV37bhUjB8Qy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbDWJv%2FdJMcabwvnGh%2Fa5o0oaiWdiV37bhUjB8Qy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1622&quot; height=&quot;251&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  학습할 내용&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;히스토리 관리가 필요한 이유&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;히스토리 관리 시작하기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;최적화를 위한 삭제&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Remove 담당 Node&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Human-in-the-loop&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  히스토리&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1️⃣ 히스토리 관리가 필요한 이유&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중간에 장애가 발생해도 복구할 수 있음&amp;nbsp;&lt;/li&gt;
&lt;li&gt;대화 / 작업 흐름을 유지할 수 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Human-in-the-loop&lt;/b&gt;: 개발자가 개입하여, 검토 및 수정을 할 수 있음&lt;/li&gt;
&lt;li&gt;디버깅 및 추적이 간편함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2️⃣ 히스토리 관리 시작하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;checkpoints를 활용, Snapshot을 만듬, 우리는 간단한 MemorySaver checkpointer를 이용함&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;checkpointer는 매 실행마다, 상태 스냅샷(checkpoint)를 &lt;b&gt;thread&lt;/b&gt;에 누적하게 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Thread란?&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;checkpointer가 저장하는 checkpoint들을 묶는 고유 ID&lt;br /&gt;그래프를 실행할 때, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;configuable.thread_id&lt;/b&gt;&lt;/span&gt;를 반드시 지정해야함&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1772079300341&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 그래프 구성

from langgraph.graph import START, END


graph_builder.add_edge(START, 'agent')
graph_builder.add_conditional_edges(
    'agent',
    should_continue,
    ['tools', 'summarize_messages']
)
graph_builder.add_edge('tools', 'agent')
graph_builder.add_edge('summarize_messages', 'delete_messages')
graph_builder.add_edge('delete_messages', END)


# 체크 포인터 지정, compile함수의 매개변수로 넘겨줌

from langgraph.checkpoint.memory import MemorySaver

checkpointer = MemorySaver()

graph= graph_builder.compile(checkpointer=checkpointer)


# config주기 


from langchain_core.messages import HumanMessage

config = {
    'configurable': {
        'thread_id': 'summarize_paper' # thread_id
    }
}

query = 'jasonkang14@gmail.com으로 Attention Is All You Need 논문을 요약해서 이메일 초안을 작성해주세요'
for chunk in graph.stream({'messages': [HumanMessage(query)], 'summary': ''}, config=config, stream_mode='values'):
    chunk['messages'][-1].pretty_print()&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; ️ 최적화를 위한&amp;nbsp; 메시지 삭제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;한번 다음과 같은 모습을 상상해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;1491&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1eC0u/dJMcahKh07U/0ba9FWCmfXvqlcoKZ21y90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1eC0u/dJMcahKh07U/0ba9FWCmfXvqlcoKZ21y90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1eC0u/dJMcahKh07U/0ba9FWCmfXvqlcoKZ21y90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1eC0u%2FdJMcahKh07U%2F0ba9FWCmfXvqlcoKZ21y90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;250&quot; height=&quot;337&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;1491&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;현재 메시지 리스트가 다음과 같이 있을 때, llm에게 저 내용 전체를 넘겨주는게 좋을까, 아니면 불필요할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;보통은 그렇게 좋은 편은아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;물론 llm은 메시지 내용 전체를 알고 있으면, 조금더 답변의 정확도나 내용이 좋아지는 건 맞지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;우리는 한정된 리소스안에서 문제를 최대한 해결해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그렇기 떄문에 여기서 우리는, 메시지 전부를 넘겨줄 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이 때, 삭제하기전에 앞의 내용을 &lt;b&gt;요약&lt;/b&gt;한 후, 마지막 메시지와 함께 넘겨주게되면&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;두마리 토끼를 잡을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;한번 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1️⃣ RemoveMessage 이용하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;요약하기&lt;/p&gt;
&lt;pre id=&quot;code_1772108333141&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def summarize_messages(state: AgentState) -&amp;gt; AgentState:
    &quot;&quot;&quot;
    주어진 state의 메시지를 요약합니다.

    Args:
        state (AgentState): 메시지와 요약을 포함하는 state.

    Returns:
        AgentState: 요약된 메시지를 포함하는 딕셔너리.
    &quot;&quot;&quot;
    # state에서 메시지와 요약을 가져옵니다.
    messages = state['messages']
    summary = state['summary']
    
    # 요약 프롬프트를 생성합니다.
    summary_prompt = f'summarize this chat history below: \n\nchat_history:{messages}'
    
    # 기존 요약이 있으면, 요약을 포함한 프롬프트를 생성합니다.
    if summary != '':
        summary_prompt = f'''summarize this chat history below while looking at the summary of earlier conversations
chat_history:{messages}
summary:{summary}'''
    
    # LLM을 사용하여 요약을 생성합니다.
    summary = small_llm.invoke(summary_prompt)
    
    # 요약된 메시지를 반환합니다.
    return {'summary': summary.content}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;삭제하기&lt;/p&gt;
&lt;pre id=&quot;code_1772108372862&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rom langchain_core.messages import RemoveMessage

def delete_messages(state: AgentState) -&amp;gt; AgentState:
    &quot;&quot;&quot;
    주어진 state에서 오래된 메시지를 삭제합니다.

    Args:
        state (AgentState): 메시지를 포함하는 state.

    Returns:
        AgentState: 삭제된 메시지를 포함하는 새로운 state.
    &quot;&quot;&quot;
    # state에서 메시지를 가져옵니다.
    messages = state['messages']
    # 마지막 세 개의 메시지를 제외한 나머지 메시지를 삭제합니다.
    delete_messages = [RemoveMessage(id=message.id) for message in messages[:-3]]
    # 삭제된 메시지를 포함하는 새로운 state를 반환합니다.
    return {'messages': delete_messages}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프&lt;/p&gt;
&lt;pre id=&quot;code_1772108586864&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from typing import Literal

def should_continue(state: AgentState) -&amp;gt; Literal['tools', 'summarize_messages']:
    &quot;&quot;&quot;
    주어진 state에 따라 다음 단계로 진행할지를 결정합니다.

    Args:
        state (AgentState): 메시지와 도구 호출 정보를 포함하는 state.

    Returns:
        Literal['tools', 'summarize_messages']: 다음 단계로 'tools' 또는 'summarize_messages'를 반환합니다.
    &quot;&quot;&quot;
    # state에서 메시지를 가져옵니다.
    messages = state['messages']
    # 마지막 AI 메시지를 확인합니다.
    last_ai_message = messages[-1]
    
    # 마지막 AI 메시지가 도구 호출을 포함하고 있는지 확인합니다.
    if last_ai_message.tool_calls:
        # 도구 호출이 있으면 'tools'를 반환합니다.
        return 'tools'
    
    # 도구 호출이 없으면 'summarize_messages'를 반환합니다.
    return 'summarize_messages'&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1772108454091&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;graph_builder.add_node('agent', agent)
graph_builder.add_node('tools', tool_node)
graph_builder.add_node(delete_messages)
graph_builder.add_node(summarize_messages)


from langgraph.graph import START, END


graph_builder.add_edge(START, 'agent')
graph_builder.add_conditional_edges(
    'agent',
    should_continue,
    ['tools', 'summarize_messages']
)
graph_builder.add_edge('tools', 'agent')
graph_builder.add_edge('summarize_messages', 'delete_messages')
graph_builder.add_edge('delete_messages', END)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;313&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQ5bAq/dJMcaf6KpYo/ODTeUTGymdkVKo3nd8dgAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQ5bAq/dJMcaf6KpYo/ODTeUTGymdkVKo3nd8dgAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQ5bAq/dJMcaf6KpYo/ODTeUTGymdkVKo3nd8dgAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQ5bAq%2FdJMcaf6KpYo%2FODTeUTGymdkVKo3nd8dgAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;250&quot; height=&quot;345&quot; data-origin-width=&quot;313&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;agent를 tools를 사용하여 llm 답변을 생성하고&lt;/li&gt;
&lt;li&gt;이후 요약&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; Human-in-the-loop&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⭐ 정의&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; 에이전트가 &amp;ldquo;도구(tool)&amp;rdquo;를 실행하려고 할 때, &lt;br /&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;사람이 중간에 끼어들어 승인/수정/거절을 할 수 있게 하는 미들웨어&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  역할&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;aprrove:&lt;/b&gt; 도구 호출을 그대로 실행&lt;/li&gt;
&lt;li&gt;&lt;b&gt;edit:&lt;/b&gt; 도구의 args등을 수정한 후, 실행&lt;/li&gt;
&lt;li&gt;&lt;b&gt;rejcet:&lt;/b&gt; 실행하지 않고 거절 사유/피드백을 대화에 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&amp;nbsp; &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;HITL(Human-in-the-loop)를 할 떄 체크포인터가 없을 경우, 에러가 발생함&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  구성 요소&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;interrupt(input_payload)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중단&lt;/li&gt;
&lt;li&gt;&lt;b&gt;input_payload: &lt;/b&gt;유저에게 전달될 내용 (딕셔너리 형태)&lt;/li&gt;
&lt;li&gt;유저는 이걸보고, 다음 스탭을 어떻게할 지 정함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Command(resume=human_input)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;human_input:&amp;nbsp;&lt;/b&gt;interrupt 함수의 리턴값 (딕셔너리 형태)&lt;/li&gt;
&lt;li&gt;a = interrupt() , a에 human_input 값이 들아감&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Command(goto='다음 목적지 노드이름', update={업데이트 내용})&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;goto:&amp;nbsp;&lt;/b&gt;다음 목적지 이름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;update:&amp;nbsp;&lt;/b&gt;업데이트될 내용 (딕셔너리 형태)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;다음 3가지 경우를 한번 살펴보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM이 재대로 된 답변을 했을 경우 -&amp;gt; 그대로 진행&amp;nbsp;&lt;/li&gt;
&lt;li&gt;도구를 잘 선택했지만, args를 수정할 경우 -&amp;gt; args만 수정 후 실행&lt;/li&gt;
&lt;li&gt;도구를 잘못 선택했을 경우 -&amp;gt; 도구를 수정해줌&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1772110039782&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from typing import Literal
from langgraph.types import interrupt, Command

def human_review(state: AgentState) -&amp;gt; Command[Literal['tools', 'agent']]:
    &quot;&quot;&quot;
    human_review node는 LLM의 도구 호출에 대해 사람의 검토를 요청합니다.

    Args:
        state (AgentState): 메시지 기록을 포함하는 state.

    Returns:
        Command: 다음 node로 이동하기 위한 Command를 반환합니다.
    &quot;&quot;&quot;
    messages = state['messages']
    last_message = messages[-1]
    tool_call = last_message.tool_calls[-1]
    human_review = interrupt({
        'question': '이렇게 진행하면 될까요?',
        'tool_call': tool_call
    })
    review_action = human_review['action']
    review_data = human_review.get('data', None)
    
    if review_action == 'continue':
        # 에이전트의 판단이 맞다면, 도구를 사용하기 위해 아무것도 수정하지 않고 `tools` 노드로 이동합니다
        return Command(goto='tools')
    
    if review_action == 'update_args':
        # 도구를 더 효율적으로 사용하기 위해 AIMessage의 `tool_calls` 필드를 업데이트합니다
        updated_ai_message = {
            'id': last_message.id,
            'role': 'ai',
            'content': last_message.content,
            'tool_calls': [{
                'id': tool_call['id'],
                'name': tool_call['name'],
                'args': review_data
            }],
        }
        return Command(goto='tools', update={'messages': [updated_ai_message]})
    
    if review_action == 'update_tool':
        # 다른 도구를 사용하기 위해 `ToolMessage`를 업데이트합니다 
        updated_tool_message = {
            'tool_call_id': tool_call['id'],
            'name': tool_call['name'],
            'role': 'tool',
            'content': review_data
        }
        return Command(goto='agent', update={'messages': [updated_tool_message]})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이후 커맨드,&lt;/p&gt;
&lt;pre id=&quot;code_1772111998109&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for chunk in graph.stream(
    Command(resume={&quot;action&quot;: &quot;continue&quot;}),
    config,
    stream_mode=&quot;updates&quot;, # 값을 업데이트 할 때, 여기가 꼭 updates로
):
    print(f'chunk == {chunk}')&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://wikidocs.net/261582&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wikidocs.net/261582&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1772023355612&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1-4. 메모리 (Memory)&quot; data-og-description=&quot;LangGraph의 **메모리(Memory)** 기능은 그래프의 상태를 저장하고 복원하는 핵심 메커니즘입니다. 이를 통해 대화의 맥락을 유지하고, 오류 발생 시 안전하게 복구하며&amp;hellip;&quot; data-og-host=&quot;wikidocs.net&quot; data-og-source-url=&quot;https://wikidocs.net/261582&quot; data-og-url=&quot;https://wikidocs.net/261582&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lQOr0/dJMb9efa3vD/X0mLDq8IoQB6Ry6fIPI3C0/img.jpg?width=301&amp;amp;height=392&amp;amp;face=0_0_301_392&quot;&gt;&lt;a href=&quot;https://wikidocs.net/261582&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://wikidocs.net/261582&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lQOr0/dJMb9efa3vD/X0mLDq8IoQB6Ry6fIPI3C0/img.jpg?width=301&amp;amp;height=392&amp;amp;face=0_0_301_392');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;1-4. 메모리 (Memory)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;LangGraph의 **메모리(Memory)** 기능은 그래프의 상태를 저장하고 복원하는 핵심 메커니즘입니다. 이를 통해 대화의 맥락을 유지하고, 오류 발생 시 안전하게 복구하며&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;wikidocs.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.langchain.com/oss/python/langgraph/persistence&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.langchain.com/oss/python/langgraph/persistence&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1772023508761&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Persistence - Docs by LangChain&quot; data-og-description=&quot;LangGraph has a built-in persistence layer, implemented through checkpointers. When you compile a graph with a checkpointer, the checkpointer saves a checkpoint of the graph state at every super-step. Those checkpoints are saved to a thread, which can be a&quot; data-og-host=&quot;docs.langchain.com&quot; data-og-source-url=&quot;https://docs.langchain.com/oss/python/langgraph/persistence&quot; data-og-url=&quot;https://docs.langchain.com/oss/python/langgraph/persistence&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tTVgp/dJMb8QL85tS/XcL6ikdwkOhfXcSoLBLeC0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cysYVp/dJMb8RRO0So/LfesQV3PbucX3aZgzjlvW0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cP0Z3x/dJMb8TB6HQW/Jzgm0r2KMJtM7eb4CVZmVK/img.jpg?width=3705&amp;amp;height=2598&amp;amp;face=0_0_3705_2598&quot;&gt;&lt;a href=&quot;https://docs.langchain.com/oss/python/langgraph/persistence&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.langchain.com/oss/python/langgraph/persistence&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tTVgp/dJMb8QL85tS/XcL6ikdwkOhfXcSoLBLeC0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cysYVp/dJMb8RRO0So/LfesQV3PbucX3aZgzjlvW0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cP0Z3x/dJMb8TB6HQW/Jzgm0r2KMJtM7eb4CVZmVK/img.jpg?width=3705&amp;amp;height=2598&amp;amp;face=0_0_3705_2598');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Persistence - Docs by LangChain&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;LangGraph has a built-in persistence layer, implemented through checkpointers. When you compile a graph with a checkpointer, the checkpointer saves a checkpoint of the graph state at every super-step. Those checkpoints are saved to a thread, which can be a&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.langchain.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/3.5%20Agent%EC%9D%98%20%ED%9E%88%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%A5%BC%20%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95.ipynb&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/3.5%20Agent%EC%9D%98%20%ED%9E%88%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%A5%BC%20%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95.ipynb&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1772108784859&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;inflearn-langgraph-agent/3.5 Agent의 히스토리를 관리하는 방법.ipynb at main &amp;middot; jasonkang14/inflearn-langgraph-agent&quot; data-og-description=&quot;인프런의 &amp;quot;LangGraph를 활용한 AI Agent 개발&amp;quot; 강의 소스코드입니다. Contribute to jasonkang14/inflearn-langgraph-agent development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/3.5%20Agent%EC%9D%98%20%ED%9E%88%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%A5%BC%20%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95.ipynb&quot; data-og-url=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/3.5%20Agent%EC%9D%98%20%ED%9E%88%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%A5%BC%20%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95.ipynb&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wGdNO/dJMb9lk4msu/ycoyxMPe36l7sVCsQcEk70/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/3.5%20Agent%EC%9D%98%20%ED%9E%88%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%A5%BC%20%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95.ipynb&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/3.5%20Agent%EC%9D%98%20%ED%9E%88%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%A5%BC%20%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95.ipynb&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wGdNO/dJMb9lk4msu/ycoyxMPe36l7sVCsQcEk70/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;inflearn-langgraph-agent/3.5 Agent의 히스토리를 관리하는 방법.ipynb at main &amp;middot; jasonkang14/inflearn-langgraph-agent&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;인프런의 &quot;LangGraph를 활용한 AI Agent 개발&quot; 강의 소스코드입니다. Contribute to jasonkang14/inflearn-langgraph-agent development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/3.6%20human-in-the-loop%20%EC%82%AC%EB%9E%8C%EC%9D%B4%20Agent%EC%99%80%20%EC%86%8C%ED%86%B5%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95.ipynb&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/3.6%20human-in-the-loop%20%EC%82%AC%EB%9E%8C%EC%9D%B4%20Agent%EC%99%80%20%EC%86%8C%ED%86%B5%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95.ipynb&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1772108924127&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;inflearn-langgraph-agent/3.6 human-in-the-loop 사람이 Agent와 소통하는 방법.ipynb at main &amp;middot; jasonkang14/inflearn-lang&quot; data-og-description=&quot;인프런의 &amp;quot;LangGraph를 활용한 AI Agent 개발&amp;quot; 강의 소스코드입니다. Contribute to jasonkang14/inflearn-langgraph-agent development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/3.6%20human-in-the-loop%20%EC%82%AC%EB%9E%8C%EC%9D%B4%20Agent%EC%99%80%20%EC%86%8C%ED%86%B5%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95.ipynb&quot; data-og-url=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/3.6%20human-in-the-loop%20%EC%82%AC%EB%9E%8C%EC%9D%B4%20Agent%EC%99%80%20%EC%86%8C%ED%86%B5%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95.ipynb&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bq4HNG/dJMb85vL7lf/cKbB2YKFoFS069VVpJhgek/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bpdKH9/dJMb9iaOlhp/rcPj3Pzqvg9hI7oA9neYR0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/3.6%20human-in-the-loop%20%EC%82%AC%EB%9E%8C%EC%9D%B4%20Agent%EC%99%80%20%EC%86%8C%ED%86%B5%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95.ipynb&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/3.6%20human-in-the-loop%20%EC%82%AC%EB%9E%8C%EC%9D%B4%20Agent%EC%99%80%20%EC%86%8C%ED%86%B5%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95.ipynb&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bq4HNG/dJMb85vL7lf/cKbB2YKFoFS069VVpJhgek/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bpdKH9/dJMb9iaOlhp/rcPj3Pzqvg9hI7oA9neYR0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;inflearn-langgraph-agent/3.6 human-in-the-loop 사람이 Agent와 소통하는 방법.ipynb at main &amp;middot; jasonkang14/inflearn-lang&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;인프런의 &quot;LangGraph를 활용한 AI Agent 개발&quot; 강의 소스코드입니다. Contribute to jasonkang14/inflearn-langgraph-agent development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/LangGraph</category>
      <author>Hamp</author>
      <guid isPermaLink="true">https://hamp.tistory.com/345</guid>
      <comments>https://hamp.tistory.com/345#entry345comment</comments>
      <pubDate>Thu, 26 Feb 2026 22:20:10 +0900</pubDate>
    </item>
    <item>
      <title>tool</title>
      <link>https://hamp.tistory.com/344</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wHWux/dJMcaflmlRz/0KyOrGekbrGG7Vq8LVj8XK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wHWux/dJMcaflmlRz/0KyOrGekbrGG7Vq8LVj8XK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wHWux/dJMcaflmlRz/0KyOrGekbrGG7Vq8LVj8XK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwHWux%2FdJMcaflmlRz%2F0KyOrGekbrGG7Vq8LVj8XK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1622&quot; height=&quot;251&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  학습할 내용&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;랭체인 tool 활용&lt;/li&gt;
&lt;li&gt;랭그래프에서 tool 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;⛓️ LangChain tool 제공&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ tool 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;랭체인에서 tool은 schema를 가진 함수다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@tool 데코레이&lt;/li&gt;
&lt;li&gt;설명&lt;/li&gt;
&lt;li&gt;파라미터 및 리턴 타입&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771936150382&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain_core.tools import tool

@tool
def add(a: int, b: int) -&amp;gt; int:
    &quot;&quot;&quot;숫자 a와 b를 더합니다.&quot;&quot;&quot;
    return a + b

@tool
def multiply(a: int, b: int) -&amp;gt; int:
    &quot;&quot;&quot;숫자 a와 b를 곱합니다.&quot;&quot;&quot;
    return a * b&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2️⃣ llm에 바인딩&lt;/h3&gt;
&lt;pre id=&quot;code_1771936567313&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;small_llm = AzureChatOpenAI(
    azure_deployment='gpt-4o-mini-2024-07-18',
    api_version='2024-08-01-preview',
)

llm_with_tools = small_llm.bind_tools([add, multiply])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3️⃣ 메시지 리스트(시퀀스) 던져주기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왜 리스트를 던져 줘야하나 ?&lt;/li&gt;
&lt;li&gt;llm에게 단순 질문을 던져주는 것보다 다음과 같이 던져주는게좋음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;질문 -&amp;gt; 휴면 메시지&lt;/li&gt;
&lt;li&gt;AI메시지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도구가 필요한지 안한지, 결정 후, 도구가 필요하면 AI메시지 안에있는 tool_calls에 bind한 도구 중 필요한게 전달됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tool메시지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 Tool을 쓴 결과&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771937562133&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from typing import Sequence

from langchain_core.messages import AnyMessage, HumanMessage

human_message = HumanMessage(query) # 3 곱하기 5는
message_list: Sequence[AnyMessage] = [human_message]

ai_message = llm_with_tools.invoke(message_list) # AI 메시지 
message_list.append(ai_message)

tool_message = multiply.invoke(ai_message.tool_calls[0]) #Tool 메시지
message_list.append(tool_message)


llm_with_tools.invoke(message_list) # Tool이 있는 LLM 호출&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;⚓ LangGraph에서 tool 제공&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;위 내용에서 크게 바뀌는 부부만 살펴보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;message_list -&amp;gt; MessagesState
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;append를 일일히 하지 않아도 됨&lt;/li&gt;
&lt;li&gt;MessagesState는 add_messages로 자동으로 추가됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771938506161&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MessagesState(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ToolNode 이용&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;tool을 그래프의 노드로 치환 시킴&lt;/li&gt;
&lt;li&gt;Tool 노드는 아래 제약조건을 반드시 지켜야한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지의 배열을 포함해야한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AnyMessage
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SystemMessage&lt;/li&gt;
&lt;li&gt;AIMessage&lt;/li&gt;
&lt;li&gt;HumanMessage&lt;/li&gt;
&lt;li&gt;ToolMessage&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;마지막 메시지는 반드시 AIMessage여야한다.&lt;/li&gt;
&lt;li&gt;AIMessage는 반드시 tool_calls(선택된 tool) 있어야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771937920067&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langgraph.prebuilt import ToolNode

tool_list = [add, multiply]
llm_with_tools = small_llm.bind_tools(tool_list) # LLM에 tool 바인딩
tool_node = ToolNode(tool_list) # ToolNode 생성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771938704148&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langgraph.prebuilt import ToolNode

tool_list = [add, multiply]
llm_with_tools = small_llm.bind_tools(tool_list)
tool_node = ToolNode(tool_list)

from langgraph.graph import MessagesState, StateGraph

graph_builder = StateGraph(MessagesState)

# %%
def agent(state: MessagesState) -&amp;gt; MessagesState:
    &quot;&quot;&quot;
    에이전트 함수는 주어진 상태에서 메시지를 가져와
    LLM과 도구를 사용하여 응답 메시지를 생성합니다.

    Args:
        state (MessagesState): 메시지 상태를 포함하는 state.

    Returns:
        MessagesState: 응답 메시지를 포함하는 새로운 state.
    &quot;&quot;&quot;
    # 상태에서 메시지를 추출합니다.
    messages = state['messages']
    
    # LLM과 도구를 사용하여 메시지를 처리하고 응답을 생성합니다.
    response = llm_with_tools.invoke(messages)
    
    # 응답 메시지를 새로운 상태로 반환합니다.
    return {'messages': [response]}

# %%
from typing import Literal
from langgraph.graph import END

def should_continue(state: MessagesState) -&amp;gt; Literal['tools', END]:
    &quot;&quot;&quot;
    주어진 메시지 상태를 기반으로 에이전트가 계속 진행할지 여부를 결정합니다.

    Args:
        state (MessagesState): `state`를 포함하는 객체.

    Returns:
        Literal['tools', END]: 도구를 사용해야 하면 `tools`를 리턴하고, 
        답변할 준비가 되었다면 END를 반환해서 프로세스를 종료합니다.
    &quot;&quot;&quot;
    # 상태에서 메시지를 추출합니다.
    messages = state['messages']
    
    # 마지막 AI 메시지를 가져옵니다.
    last_ai_message = messages[-1]
    
    # 마지막 AI 메시지가 도구 호출을 포함하고 있는지 확인합니다.
    if last_ai_message.tool_calls:
        # 도구 호출이 있으면 'tools'를 반환합니다.
        return 'tools'
    
    # 도구 호출이 없으면 END를 반환하여 프로세스를 종료합니다.
    return END&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/h2&gt;</description>
      <category>AI/LangGraph</category>
      <author>Hamp</author>
      <guid isPermaLink="true">https://hamp.tistory.com/344</guid>
      <comments>https://hamp.tistory.com/344#entry344comment</comments>
      <pubDate>Tue, 24 Feb 2026 22:12:17 +0900</pubDate>
    </item>
    <item>
      <title>지금까지 우리가 만든건 Agent가 아니다??</title>
      <link>https://hamp.tistory.com/343</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oMBaO/dJMb996vi5k/U5vSf5u0L2Z6vBReXOUfSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oMBaO/dJMb996vi5k/U5vSf5u0L2Z6vBReXOUfSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oMBaO/dJMb996vi5k/U5vSf5u0L2Z6vBReXOUfSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoMBaO%2FdJMb996vi5k%2FU5vSf5u0L2Z6vBReXOUfSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1622&quot; height=&quot;251&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  들어가기 전&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;우리가 지금까지 만들었던, 그래프들을 에이전트로가 불러왔었는데, 사실 Agent가 아니였다라는&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;충격적인 반전과 함께, 진짜 Agent는 어떤 것인지 알아보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  학습할 내용&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우리가 만들어왔던 Agent 복습&lt;/li&gt;
&lt;li&gt;그렇다면 우리가 만든건 뭘까?&lt;/li&gt;
&lt;li&gt;진짜 Agent란 ??&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  이전 그래프&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1️⃣ Agentic RAG&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM이 Retrieval 여부 결정&lt;/li&gt;
&lt;li&gt;문서와 관련도 판별 후, rewrite 여부 또는 END&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2️⃣ Self RAG&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문서 관련도 측정 (&lt;b&gt;&lt;span data-path-to-node=&quot;10,1,2,0&quot;&gt;RetrievalGrader&lt;/span&gt;&lt;/b&gt;&lt;span data-path-to-node=&quot;10,1,2,0&quot;&gt;)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;환각 점검 (&lt;b&gt;Hallucination Grader&lt;/b&gt;)&lt;/li&gt;
&lt;li&gt;질문과 답변 관련성 점검 (&lt;b&gt;Answer Grader&lt;/b&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfUrU3/dJMcafFEk7V/ZPdamGsFEZpLfhiKy65kF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfUrU3/dJMcafFEk7V/ZPdamGsFEZpLfhiKy65kF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfUrU3/dJMcafFEk7V/ZPdamGsFEZpLfhiKy65kF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfUrU3%2FdJMcafFEk7V%2FZPdamGsFEZpLfhiKy65kF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;230&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3️⃣Rotuer&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입려된 정보를 분류하여, 최적으 Node에게 보냄&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;469&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dTjWJE/dJMcabJ2uCK/xtpXZsP2gir4ECgokf7nDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dTjWJE/dJMcabJ2uCK/xtpXZsP2gir4ECgokf7nDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dTjWJE/dJMcabJ2uCK/xtpXZsP2gir4ECgokf7nDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdTjWJE%2FdJMcabJ2uCK%2FxtpXZsP2gir4ECgokf7nDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;176&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;469&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4️⃣병렬&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래 로직을 돌리기 위해 필요한 독립적은 노드들을 병렬로 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1sU38/dJMcahXLtD0/TyWxBx9FXd3GsambGkiMeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1sU38/dJMcahXLtD0/TyWxBx9FXd3GsambGkiMeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1sU38/dJMcahXLtD0/TyWxBx9FXd3GsambGkiMeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1sU38%2FdJMcahXLtD0%2FTyWxBx9FXd3GsambGkiMeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;308&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt; ️ Agentic system&lt;/h2&gt;
&lt;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;workflow + agents = agentic systems&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;❓ 우리가 만든건 뭘까??&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;자 여기서부터는 출처에 있는 엔토리픽 문서에 나와있는 내용이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3GiQV/dJMcagLkXf5/AMumYEVUW8foBQXA9RERo0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3GiQV/dJMcagLkXf5/AMumYEVUW8foBQXA9RERo0/img.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;533&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3GiQV/dJMcagLkXf5/AMumYEVUW8foBQXA9RERo0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3GiQV%2FdJMcagLkXf5%2FAMumYEVUW8foBQXA9RERo0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GG8KZ/dJMcahwIa4Y/kjKHyM3073Rvl5KtDVf7fK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GG8KZ/dJMcahwIa4Y/kjKHyM3073Rvl5KtDVf7fK/img.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;533&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GG8KZ/dJMcahwIa4Y/kjKHyM3073Rvl5KtDVf7fK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGG8KZ%2FdJMcahwIa4Y%2FkjKHyM3073Rvl5KtDVf7fK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MUvvY/dJMcacPHbL3/NDDAjmKZgdK3R7k48m8EY1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MUvvY/dJMcacPHbL3/NDDAjmKZgdK3R7k48m8EY1/img.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;533&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MUvvY/dJMcacPHbL3/NDDAjmKZgdK3R7k48m8EY1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMUvvY%2FdJMcacPHbL3%2FNDDAjmKZgdK3R7k48m8EY1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;위 사진은 엔트로픽 공식문서에 있는 내용인데, 우리가 그렸던 그래프들이랑 너무 똑같다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그러면 이 내용은 엔트로픽에서 뭐라고 부를까 ??&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;바로&amp;nbsp;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;workflow&lt;/b&gt;&lt;/span&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;엔트로픽은 workflow를 다음과 같이 정의하고 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM과 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;사전 정의된&lt;/b&gt;&lt;/span&gt; 코드 경로를 통해 LLM과 도구들이 유기적으로 조율된 시스템이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;여기서 핵심은 사전 정의된, 즉 순서와 분기등을 &quot;개발자&quot;가 정의했다는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;❕진짜 Agent란&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;동적으로 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;스스로&lt;/span&gt; 프로세스와 도구 사용을 결정&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;작업을 해결하는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;방법과 순서를 모델이 직접 결정&lt;/b&gt;&lt;/span&gt;하고 통제&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nlOmG/dJMb99MaN4i/z8RDgzrzFoB7iGiOyKTKhk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nlOmG/dJMb99MaN4i/z8RDgzrzFoB7iGiOyKTKhk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nlOmG/dJMb99MaN4i/z8RDgzrzFoB7iGiOyKTKhk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnlOmG%2FdJMb99MaN4i%2Fz8RDgzrzFoB7iGiOyKTKhk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;533&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/building-effective-agents&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.anthropic.com/engineering/building-effective-agents&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771852556403&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Building Effective AI Agents&quot; data-og-description=&quot;Discover how Anthropic approaches the development of reliable AI agents. Learn about our research on agent capabilities, safety considerations, and technical framework for building trustworthy AI.&quot; data-og-host=&quot;www.anthropic.com&quot; data-og-source-url=&quot;https://www.anthropic.com/engineering/building-effective-agents&quot; data-og-url=&quot;https://www.anthropic.com/engineering/building-effective-agents&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/5u4BL/dJMb9fZsep1/x868YpqGO5nZHbwWMhZqkK/img.png?width=2400&amp;amp;height=1260&amp;amp;face=0_0_2400_1260,https://scrap.kakaocdn.net/dn/1D4eC/dJMb9gxiitU/luJ258UCQKKpCAJKS391oK/img.png?width=2400&amp;amp;height=1260&amp;amp;face=0_0_2400_1260,https://scrap.kakaocdn.net/dn/g8w2z/dJMb9bvZdRt/9KGOE2fB314nWT65UKZbi1/img.jpg?width=2400&amp;amp;height=1666&amp;amp;face=0_0_2400_1666&quot;&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/building-effective-agents&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.anthropic.com/engineering/building-effective-agents&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/5u4BL/dJMb9fZsep1/x868YpqGO5nZHbwWMhZqkK/img.png?width=2400&amp;amp;height=1260&amp;amp;face=0_0_2400_1260,https://scrap.kakaocdn.net/dn/1D4eC/dJMb9gxiitU/luJ258UCQKKpCAJKS391oK/img.png?width=2400&amp;amp;height=1260&amp;amp;face=0_0_2400_1260,https://scrap.kakaocdn.net/dn/g8w2z/dJMb9bvZdRt/9KGOE2fB314nWT65UKZbi1/img.jpg?width=2400&amp;amp;height=1666&amp;amp;face=0_0_2400_1666');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Building Effective AI Agents&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Discover how Anthropic approaches the development of reliable AI agents. Learn about our research on agent capabilities, safety considerations, and technical framework for building trustworthy AI.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.anthropic.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/LangGraph</category>
      <author>Hamp</author>
      <guid isPermaLink="true">https://hamp.tistory.com/343</guid>
      <comments>https://hamp.tistory.com/343#entry343comment</comments>
      <pubDate>Mon, 23 Feb 2026 22:23:44 +0900</pubDate>
    </item>
    <item>
      <title>RunnablePassthrough</title>
      <link>https://hamp.tistory.com/342</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5LxUR/dJMcadgKVfu/Urrn4VHhym49xyzq1RmRQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5LxUR/dJMcadgKVfu/Urrn4VHhym49xyzq1RmRQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5LxUR/dJMcadgKVfu/Urrn4VHhym49xyzq1RmRQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5LxUR%2FdJMcadgKVfu%2FUrrn4VHhym49xyzq1RmRQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1622&quot; height=&quot;251&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⭐ 역할&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  역할&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 변환하거나 수정할 필요가 없는 경우&lt;/li&gt;
&lt;li&gt;파이프라인의 특정 단계를 건너뛰어야 하는 경우&lt;/li&gt;
&lt;li&gt;디버깅 또는 테스트 목적으로 데이터 흐름을 모니터링해야 하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  예제로 흐름 파악하기&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;tax_deduction_question가 invoke를 통해, tax_deduction_chain의 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;question: RunnablePassthrough&lt;/b&gt;&lt;/span&gt;로 들어감&lt;/li&gt;
&lt;li&gt;마찬가지로 tax_base_equation_question이 tax_base_retrieval_chain의 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;question: RunnablePassthrough&lt;/b&gt;&lt;/span&gt;로 들어감&lt;/li&gt;
&lt;li&gt;tax_base_equation_chain의 tax_base_equation_information에는 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;tax_base_retrieval_chain&lt;/b&gt;&lt;/span&gt;이 들어옴&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771848009281&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tax_base_retrieval_chain = (
    {'context': retriever, 'question': RunnablePassthrough()} 
    | rag_prompt 
    | llm 
    | StrOutputParser()
)

tax_base_equation_prompt = ChatPromptTemplate.from_messages([
    ('system', '사용자의 질문에서 과세표준을 계산하는 방법을 수식으로 나타내주세요. 부연설명 없이 수식만 리턴해주세요'),
    ('human', '{tax_base_equation_information}')
])

tax_base_equation_chain = (
    {'tax_base_equation_information': RunnablePassthrough()}
    | tax_base_equation_prompt
    | llm
    | StrOutputParser()
)

tax_base_chain = {'tax_base_equation_information' : tax_base_retrieval_chain} | tax_base_equation_chain

def get_tax_base_equation(state: AgentState) -&amp;gt; AgentState:
    tax_base_equation_question = '주택에 대한 종합부동산세 계산시 과세표준을 계산하는 방법을 수식으로 표현해서 알려주세요'
    tax_base_equation = tax_base_chain.invoke(tax_base_equation_question)

    return {'tax_base_equation': tax_base_equation}
    
    tax_deduction_chain = (
    {'context': retriever, 'question': RunnablePassthrough()} 
    | rag_prompt 
    | llm 
    | StrOutputParser()
)

def get_tax_deduction(state: AgentState) -&amp;gt; AgentState:
    # 공제금액을 묻는 질문을 정의합니다.
    tax_deduction_question = '주택에 대한 종합부동산세 계산시 공제금액을 알려주세요'
    
    # tax_deduction_chain을 사용하여 질문을 실행하고 결과를 얻습니다.
    tax_deduction = tax_deduction_chain.invoke(tax_deduction_question)
    
    # state에서 'tax_deduction' 키에 대한 값을 반환합니다.
    return {'tax_deduction': tax_deduction}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://wikidocs.net/235580&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wikidocs.net/235580&lt;/a&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771846100118&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;01. RunnablePassthrough&quot; data-og-description=&quot;.custom { background-color: #008d8d; color: white; padding: 0.25em 0.5&amp;hellip;&quot; data-og-host=&quot;wikidocs.net&quot; data-og-source-url=&quot;https://wikidocs.net/235580&quot; data-og-url=&quot;https://wikidocs.net/235580&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/EPZmk/dJMb9hCYc8x/hNgddDHPTPfEpjgjYXt4W0/img.png?width=255&amp;amp;height=332&amp;amp;face=0_0_255_332&quot;&gt;&lt;a href=&quot;https://wikidocs.net/235580&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://wikidocs.net/235580&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/EPZmk/dJMb9hCYc8x/hNgddDHPTPfEpjgjYXt4W0/img.png?width=255&amp;amp;height=332&amp;amp;face=0_0_255_332');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;01. RunnablePassthrough&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.custom { background-color: #008d8d; color: white; padding: 0.25em 0.5&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;wikidocs.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/LangGraph</category>
      <author>Hamp</author>
      <guid isPermaLink="true">https://hamp.tistory.com/342</guid>
      <comments>https://hamp.tistory.com/342#entry342comment</comments>
      <pubDate>Mon, 23 Feb 2026 21:14:08 +0900</pubDate>
    </item>
    <item>
      <title>SubGraph와 Router</title>
      <link>https://hamp.tistory.com/341</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZqX7P/dJMcac3a7Pv/uzZeolOomIavaTnAzvuybK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZqX7P/dJMcac3a7Pv/uzZeolOomIavaTnAzvuybK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZqX7P/dJMcac3a7Pv/uzZeolOomIavaTnAzvuybK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZqX7P%2FdJMcac3a7Pv%2FuzZeolOomIavaTnAzvuybK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1622&quot; height=&quot;251&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  들어가기 전&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이번 내용은 이전에 만들었던, Self Rag, Corrective Rag, 신규 Graph를 모두 묶을려고한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;새로 등장하는 SubGraph와 Router를 이용해서 한번 진행해보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  Router&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;469&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SeTZi/dJMcabwrTAA/T7n1WnLKRxJ6tHRqxOad1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SeTZi/dJMcabwrTAA/T7n1WnLKRxJ6tHRqxOad1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SeTZi/dJMcabwrTAA/T7n1WnLKRxJ6tHRqxOad1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSeTZi%2FdJMcabwrTAA%2FT7n1WnLKRxJ6tHRqxOad1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1198&quot; height=&quot;469&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;469&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⭐정의&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt; 그래프의 흐름을 결정하는 핵심적인 의사결정 장치 &lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  역할&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력된 정보를 분류하고, 이를 해당 분야의 전담 에이전트(Specialized Agents)에게 보내는 역할&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;➡️ Sub Graph&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⭐정의&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; &lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;다른 그래프 안에서 하나의 노드(Node)처럼 사용되는 독립된 그래프 &lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  역할&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;멀티 에이전트 시스템 구축&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 에이전트가 수행하는 복잡한 단계를 별도의 그래프로 분리하여 관리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 코드 재사용&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공통적으로 쓰이는 작업 흐름(예: 데이터 정제, 검색 로직)을 서브그래프로 만들어 여러 상위 그래프에서 재사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 개발 분업화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자기 담당 영역(서브그래프)의 입력과 출력만 맞추면, 내부 로직이 어떻게 돌아가는지 상위 그래프 팀이 몰라도 독립적으로 개발 가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  예제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;우리의 목표는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이전 vector_store에 있는 정보가 있을 때는 Self-RAG 그래프를 이용&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;간단한 작업일 경우, basic_generate 이용&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;웹 서치가 필요할 때는 Corrective RAG를 이용&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ugsuc/dJMcaivycmA/RcuBP5u8oM7uerym4bmCok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ugsuc/dJMcaivycmA/RcuBP5u8oM7uerym4bmCok/img.png&quot; data-alt=&quot;목표 그래프&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ugsuc/dJMcaivycmA/RcuBP5u8oM7uerym4bmCok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fugsuc%2FdJMcaivycmA%2FRcuBP5u8oM7uerym4bmCok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;333&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;목표 그래프&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;route를 통해, 어떤 Agent(Graph)를 이용할지 결정&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정확히 어떤 경로가 존재하는 지 명확히 명시
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BaseModel&lt;/li&gt;
&lt;li&gt;with_structured_output&lt;/li&gt;
&lt;li&gt;위 2개를 통해&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771767778848&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field
from typing import Literal

class Route(BaseModel):
    target: Literal['vector_store', 'llm', 'web_search'] = Field(
        description=&quot;The target for the query to answer&quot;
    )

# 라우팅 관련 프롬포트
router_system_prompt = &quot;&quot;&quot;
You are an expert at routing a user's question to 'vector_store', 'llm', or 'web_search'.
'vector_store' contains information about income tax up to December 2024.
if you think the question is simple enough use 'llm'
if you think you need to search the web to answer the question use 'web_search'
&quot;&quot;&quot;


router_prompt = ChatPromptTemplate.from_messages([
    ('system', router_system_prompt),
    ('user', '{query}')
])

router_llm = ChatOpenAI(model=&quot;gpt-4o-mini&quot;)
structured_router_llm = router_llm.with_structured_output(Route) # Router 클래스 형태로 반환

def router(state: AgentState) -&amp;gt; Literal['vector_store', 'llm', 'web_search']:
    &quot;&quot;&quot;
    사용자의 질문에 기반하여 적절한 경로를 결정합니다.

    Args:
        state (AgentState): 사용자의 질문을 포함한 에이전트의 현재 state.

    Returns:
        Literal['vector_store', 'llm', 'web_search']: 질문을 처리하기 위한 적절한 경로를 나타내는 문자열.
    &quot;&quot;&quot;
    # state에서 질문을 추출합니다
    query = state['query']
    
    # 프롬프트와 구조화된 라우터 LLM을 연결하여 체인을 생성합니다
    router_chain = router_prompt | structured_router_llm 
    
    # 체인을 사용하여 경로를 결정합니다
    route = router_chain.invoke({'query': query})

    # 결정된 경로의 타겟을 반환합니다
    return route.target&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그래프 연결와 edge 연결&lt;/p&gt;
&lt;pre id=&quot;code_1771767287060&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from income_tax_graph import graph as income_tax_subgraph

graph_builder.add_node('income_tax_agent', income_tax_subgraph)
graph_builder.add_node('web_search', web_search)
graph_builder.add_node('web_generate', web_generate)
graph_builder.add_node('basic_generate', basic_generate)

from langgraph.graph import START, END

graph_builder.add_conditional_edges(
    START, 
    router,
    {
        'vector_store': 'income_tax_agent',
        'llm': 'basic_generate',
        'web_search': 'web_search'
    }
)

graph_builder.add_edge('web_search', 'web_generate')
graph_builder.add_edge('web_generate', END)
graph_builder.add_edge('basic_generate', END)
graph_builder.add_edge('income_tax_agent', END)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://arxiv.org/abs/2403.14403&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/abs/2403.14403&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771766043810&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Adaptive-RAG: Learning to Adapt Retrieval-Augmented Large Language Models through Question Complexity&quot; data-og-description=&quot;Retrieval-Augmented Large Language Models (LLMs), which incorporate the non-parametric knowledge from external knowledge bases into LLMs, have emerged as a promising approach to enhancing response accuracy in several tasks, such as Question-Answering (QA).&quot; data-og-host=&quot;arxiv.org&quot; data-og-source-url=&quot;https://arxiv.org/abs/2403.14403&quot; data-og-url=&quot;https://arxiv.org/abs/2403.14403v2&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/onsF6/dJMb8WewqyJ/klaXKbGnWOrbkgYWORGd3k/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/bWvr78/dJMb8U8Qqwq/ZEPdynYeqHbIn70qkorzC0/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/2403.14403&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arxiv.org/abs/2403.14403&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/onsF6/dJMb8WewqyJ/klaXKbGnWOrbkgYWORGd3k/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/bWvr78/dJMb8U8Qqwq/ZEPdynYeqHbIn70qkorzC0/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Adaptive-RAG: Learning to Adapt Retrieval-Augmented Large Language Models through Question Complexity&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Retrieval-Augmented Large Language Models (LLMs), which incorporate the non-parametric knowledge from external knowledge bases into LLMs, have emerged as a promising approach to enhancing response accuracy in several tasks, such as Question-Answering (QA).&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arxiv.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.langchain.com/oss/python/langchain/multi-agent/router&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.langchain.com/oss/python/langchain/multi-agent/router&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771766189494&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Router - Docs by LangChain&quot; data-og-description=&quot;Tutorial: Build a multi-source knowledge base with routing Build a router that queries GitHub, Notion, and Slack in parallel, then synthesizes results into a coherent answer. Covers state definition, specialized agents, parallel execution with Send, and re&quot; data-og-host=&quot;docs.langchain.com&quot; data-og-source-url=&quot;https://docs.langchain.com/oss/python/langchain/multi-agent/router&quot; data-og-url=&quot;https://docs.langchain.com/oss/python/langchain/multi-agent/router&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/us9Zh/dJMb9b3ORZp/PGNEvZrvCWhsSBWkXf8OV1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/szneH/dJMb9cBEUAV/ojjisQT1AKwcHLq7tK1eWk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://docs.langchain.com/oss/python/langchain/multi-agent/router&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.langchain.com/oss/python/langchain/multi-agent/router&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/us9Zh/dJMb9b3ORZp/PGNEvZrvCWhsSBWkXf8OV1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/szneH/dJMb9cBEUAV/ojjisQT1AKwcHLq7tK1eWk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Router - Docs by LangChain&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Tutorial: Build a multi-source knowledge base with routing Build a router that queries GitHub, Notion, and Slack in parallel, then synthesizes results into a coherent answer. Covers state definition, specialized agents, parallel execution with Send, and re&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.langchain.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.langchain.com/oss/python/langgraph/use-subgraphs&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.langchain.com/oss/python/langgraph/use-subgraphs&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771766591423&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Subgraphs - Docs by LangChain&quot; data-og-description=&quot;Full example: different state schemas (two levels of subgraphs)&quot; data-og-host=&quot;docs.langchain.com&quot; data-og-source-url=&quot;https://docs.langchain.com/oss/python/langgraph/use-subgraphs&quot; data-og-url=&quot;https://docs.langchain.com/oss/python/langgraph/use-subgraphs&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dMlcMN/dJMb9gxibrt/Mv4lrqzEnoqNUE5rFLRvW0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/9D1Ya/dJMb9cBEUCh/VB0mGYeVOItIYGeyvs0b5K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ZePMb/dJMb9cBEUCg/nba23b2TDi39pQEdhsUks0/img.png?width=1177&amp;amp;height=818&amp;amp;face=0_0_1177_818&quot;&gt;&lt;a href=&quot;https://docs.langchain.com/oss/python/langgraph/use-subgraphs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.langchain.com/oss/python/langgraph/use-subgraphs&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dMlcMN/dJMb9gxibrt/Mv4lrqzEnoqNUE5rFLRvW0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/9D1Ya/dJMb9cBEUCh/VB0mGYeVOItIYGeyvs0b5K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ZePMb/dJMb9cBEUCg/nba23b2TDi39pQEdhsUks0/img.png?width=1177&amp;amp;height=818&amp;amp;face=0_0_1177_818');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Subgraphs - Docs by LangChain&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Full example: different state schemas (two levels of subgraphs)&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.langchain.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/jasonkang14/inflearn-langgraph-agent&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771767884981&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - jasonkang14/inflearn-langgraph-agent: 인프런의 &amp;quot;LangGraph를 활용한 AI Agent 개발&amp;quot; 강의 소스코드입니&quot; data-og-description=&quot;인프런의 &amp;quot;LangGraph를 활용한 AI Agent 개발&amp;quot; 강의 소스코드입니다. Contribute to jasonkang14/inflearn-langgraph-agent development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent&quot; data-og-url=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cvXObE/dJMb9kl9BoZ/7dblZPtAWzZFtriUuit4Zk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bsxHRC/dJMb8RROHu4/sLyyBMwZ7se8ncmZuybWa0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cvXObE/dJMb9kl9BoZ/7dblZPtAWzZFtriUuit4Zk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bsxHRC/dJMb8RROHu4/sLyyBMwZ7se8ncmZuybWa0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - jasonkang14/inflearn-langgraph-agent: 인프런의 &quot;LangGraph를 활용한 AI Agent 개발&quot; 강의 소스코드입니&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;인프런의 &quot;LangGraph를 활용한 AI Agent 개발&quot; 강의 소스코드입니다. Contribute to jasonkang14/inflearn-langgraph-agent development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/LangGraph</category>
      <author>Hamp</author>
      <guid isPermaLink="true">https://hamp.tistory.com/341</guid>
      <comments>https://hamp.tistory.com/341#entry341comment</comments>
      <pubDate>Sun, 22 Feb 2026 22:45:02 +0900</pubDate>
    </item>
    <item>
      <title>Corrective RAG</title>
      <link>https://hamp.tistory.com/340</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bN62PK/dJMcacWp0yU/pnddhhZLl9EiozvO6yXbNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bN62PK/dJMcacWp0yU/pnddhhZLl9EiozvO6yXbNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bN62PK/dJMcacWp0yU/pnddhhZLl9EiozvO6yXbNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbN62PK%2FdJMcacWp0yU%2FpnddhhZLl9EiozvO6yXbNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1622&quot; height=&quot;251&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⭐ 정의&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;RAG + 웹서치&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;❓Self RAG와 다른점&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;re-write 목적이 다름
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Self RAG는 retrieve를 잘하려고 rewrite&lt;/li&gt;
&lt;li&gt;retrieve는 기존의 가지고 있는 VectorStore 기반&lt;/li&gt;
&lt;li&gt;Corrective RAG는&lt;b&gt; Web Search&lt;/b&gt;를 잘하기위해 rewrite&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;%F0%9F%8C%8A%20%ED%9D%90%EB%A6%84-1&quot; style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  흐름&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;797&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTZtil/dJMcahwG3Bl/RRaBYJnd5twVrukshZsrs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTZtil/dJMcahwG3Bl/RRaBYJnd5twVrukshZsrs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTZtil/dJMcahwG3Bl/RRaBYJnd5twVrukshZsrs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTZtil%2FdJMcahwG3Bl%2FRRaBYJnd5twVrukshZsrs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;797&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;797&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관련성 평가
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;통과 시, generate&lt;/li&gt;
&lt;li&gt;실패 시, rewrite&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;rewrite
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Web Seach&lt;/b&gt;를 위해, rewrite&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Web Search&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색 결과를 기반으로 generate&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt; 웹서치 전에 rewrite가 꼭 필요할까??&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;rewrite 역시 llm 호출이 필요하기 때문에, 굳이 필수는 아니다.&lt;br /&gt;공식문서는 rewrite노드가 있지만, 결과물에 크게 영향을 주지 않는다.&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://wikidocs.net/270686&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wikidocs.net/270686&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771750163204&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;03. CRAG(Corrective RAG)&quot; data-og-description=&quot;.custom { background-color: #008d8d; color: white; padding: 0.25em 0.5&amp;hellip;&quot; data-og-host=&quot;wikidocs.net&quot; data-og-source-url=&quot;https://wikidocs.net/270686&quot; data-og-url=&quot;https://wikidocs.net/270686&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbVN3z/dJMb86nUj5Y/FvKzeXnFBGvdLs5TvvXg8k/img.png?width=255&amp;amp;height=332&amp;amp;face=0_0_255_332,https://scrap.kakaocdn.net/dn/c965DU/dJMb82MzRBu/b0FKMoVDw9bzCzTKyk4Nq1/img.png?width=506&amp;amp;height=797&amp;amp;face=0_0_506_797&quot;&gt;&lt;a href=&quot;https://wikidocs.net/270686&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://wikidocs.net/270686&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbVN3z/dJMb86nUj5Y/FvKzeXnFBGvdLs5TvvXg8k/img.png?width=255&amp;amp;height=332&amp;amp;face=0_0_255_332,https://scrap.kakaocdn.net/dn/c965DU/dJMb82MzRBu/b0FKMoVDw9bzCzTKyk4Nq1/img.png?width=506&amp;amp;height=797&amp;amp;face=0_0_506_797');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;03. CRAG(Corrective RAG)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.custom { background-color: #008d8d; color: white; padding: 0.25em 0.5&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;wikidocs.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://arxiv.org/abs/2401.15884&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/abs/2401.15884&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771750157444&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Corrective Retrieval Augmented Generation&quot; data-og-description=&quot;Large language models (LLMs) inevitably exhibit hallucinations since the accuracy of generated texts cannot be secured solely by the parametric knowledge they encapsulate. Although retrieval-augmented generation (RAG) is a practicable complement to LLMs, i&quot; data-og-host=&quot;arxiv.org&quot; data-og-source-url=&quot;https://arxiv.org/abs/2401.15884&quot; data-og-url=&quot;https://arxiv.org/abs/2401.15884v3&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/L7ssA/dJMb84XVw8p/iF1PHWuSQ5EyLXBQ3jDLk1/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/f8tll/dJMb8865E0w/HAYNQRlDBU3fRAPT9kTYEk/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/2401.15884&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arxiv.org/abs/2401.15884&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/L7ssA/dJMb84XVw8p/iF1PHWuSQ5EyLXBQ3jDLk1/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/f8tll/dJMb8865E0w/HAYNQRlDBU3fRAPT9kTYEk/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Corrective Retrieval Augmented Generation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Large language models (LLMs) inevitably exhibit hallucinations since the accuracy of generated texts cannot be secured solely by the parametric knowledge they encapsulate. Although retrieval-augmented generation (RAG) is a practicable complement to LLMs, i&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arxiv.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/LangGraph</category>
      <author>Hamp</author>
      <guid isPermaLink="true">https://hamp.tistory.com/340</guid>
      <comments>https://hamp.tistory.com/340#entry340comment</comments>
      <pubDate>Sun, 22 Feb 2026 18:09:26 +0900</pubDate>
    </item>
    <item>
      <title>Self RAG</title>
      <link>https://hamp.tistory.com/339</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F4Ohl/dJMcahDqi09/wZkyOtw1UQUMy2PWnKjIdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F4Ohl/dJMcahDqi09/wZkyOtw1UQUMy2PWnKjIdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F4Ohl/dJMcahDqi09/wZkyOtw1UQUMy2PWnKjIdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF4Ohl%2FdJMcahDqi09%2FwZkyOtw1UQUMy2PWnKjIdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1622&quot; height=&quot;251&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⭐ 정의&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; Self-RAG는 검색된 문서와 생성된 응답 모두에 대해 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;점검하고 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;검증하는 추가 단계&lt;/span&gt;를 포함하는 RAG 전략&lt;/span&gt; &lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  흐름&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ynAsT/dJMcaioPArk/OQDo4MwkuraTtzu8sknIt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ynAsT/dJMcaioPArk/OQDo4MwkuraTtzu8sknIt1/img.png&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;388&quot; data-is-animation=&quot;false&quot; style=&quot;width: 69.8092%; margin-right: 10px;&quot; data-widthpercent=&quot;70.63&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ynAsT/dJMcaioPArk/OQDo4MwkuraTtzu8sknIt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FynAsT%2FdJMcaioPArk%2FOQDo4MwkuraTtzu8sknIt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;845&quot; height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkb0pt/dJMcaf6GQ9Z/gBLYIhpbtI0YTg3E8n1t9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkb0pt/dJMcaf6GQ9Z/gBLYIhpbtI0YTg3E8n1t9k/img.png&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;519&quot; data-is-animation=&quot;false&quot; style=&quot;width: 29.0281%;&quot; data-widthpercent=&quot;29.37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkb0pt/dJMcaf6GQ9Z/gBLYIhpbtI0YTg3E8n1t9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbkb0pt%2FdJMcaf6GQ9Z%2FgBLYIhpbtI0YTg3E8n1t9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;470&quot; height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 84px;&quot; border=&quot;1&quot; data-path-to-node=&quot;10&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 21px;&quot;&gt;&lt;b&gt;단계&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 21px;&quot;&gt;&lt;b&gt;질문 (Self-Reflection)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 21px;&quot;&gt;&lt;b&gt;노드&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;10,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0,0&quot;&gt;1. 관련성 점검&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;10,1,1,0&quot;&gt;&quot;내가 가져온 이 문서가 질문에 도움이 될까?&quot;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 21px;&quot;&gt;&lt;b&gt;&lt;span data-path-to-node=&quot;10,1,2,0&quot;&gt;Retrieval Grader&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;10,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,2,0,0&quot;&gt;2. 환각 점검&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;10,2,1,0&quot;&gt;&quot;내가 쓴 답변이 문서에 있는 내용 기반인가?&quot;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 21px;&quot;&gt;&lt;b&gt;Hallucination Grader&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;10,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,3,0,0&quot;&gt;3. 유용성 점검&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;10,3,1,0&quot;&gt;&quot;이 답변이 사용자의 질문에 직접적인 도움이 되나?&quot;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 21px;&quot;&gt;&lt;b&gt;&lt;span data-path-to-node=&quot;10,3,2,0&quot;&gt;Answer Grader&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  역할&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;신뢰도 극대화:&lt;/b&gt; 잘못된 정보가 사용자에게 전달될 확률을 획기적으로 낮춤&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,0&quot;&gt;적응력:&lt;/b&gt; 문서가 부족하면 다시 검색하거나 질문을 다듬는 등 유연한 대처&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,2,0&quot;&gt;정교한 제어:&lt;/b&gt; LangGraph를 쓰면 각 단계(노드)마다 다른 AI 모델 사용가능 (예: 판단은 가벼운 모델, 생성은 무거운 모델)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://wikidocs.net/270687&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wikidocs.net/270687&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771747402180&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;04. Self-RAG&quot; data-og-description=&quot;.custom { background-color: #008d8d; color: white; padding: 0.25em 0.5&amp;hellip;&quot; data-og-host=&quot;wikidocs.net&quot; data-og-source-url=&quot;https://wikidocs.net/270687&quot; data-og-url=&quot;https://wikidocs.net/270687&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/AYvP1/dJMb8Z3n2YT/JskXuNJJVsgBkRKvbDszo1/img.png?width=255&amp;amp;height=332&amp;amp;face=0_0_255_332,https://scrap.kakaocdn.net/dn/F0rXO/dJMb8WMmlmS/J27AjB21yMYUYvq1KHjnTK/img.png?width=946&amp;amp;height=408&amp;amp;face=0_0_946_408&quot;&gt;&lt;a href=&quot;https://wikidocs.net/270687&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://wikidocs.net/270687&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/AYvP1/dJMb8Z3n2YT/JskXuNJJVsgBkRKvbDszo1/img.png?width=255&amp;amp;height=332&amp;amp;face=0_0_255_332,https://scrap.kakaocdn.net/dn/F0rXO/dJMb8WMmlmS/J27AjB21yMYUYvq1KHjnTK/img.png?width=946&amp;amp;height=408&amp;amp;face=0_0_946_408');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;04. Self-RAG&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.custom { background-color: #008d8d; color: white; padding: 0.25em 0.5&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;wikidocs.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/2310.11511&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/abs/2310.11511&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771748550893&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection&quot; data-og-description=&quot;Despite their remarkable capabilities, large language models (LLMs) often produce responses containing factual inaccuracies due to their sole reliance on the parametric knowledge they encapsulate. Retrieval-Augmented Generation (RAG), an ad hoc approach th&quot; data-og-host=&quot;arxiv.org&quot; data-og-source-url=&quot;https://arxiv.org/abs/2310.11511&quot; data-og-url=&quot;https://arxiv.org/abs/2310.11511v1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bJHrkT/dJMb86nUjZa/nnWm59xzKk91CPdWkKVfK0/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/rei9T/dJMb88F1FpZ/6LAoUoCAz1xoBKTJUYB221/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/2310.11511&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arxiv.org/abs/2310.11511&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bJHrkT/dJMb86nUjZa/nnWm59xzKk91CPdWkKVfK0/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/rei9T/dJMb88F1FpZ/6LAoUoCAz1xoBKTJUYB221/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Despite their remarkable capabilities, large language models (LLMs) often produce responses containing factual inaccuracies due to their sole reliance on the parametric knowledge they encapsulate. Retrieval-Augmented Generation (RAG), an ad hoc approach th&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arxiv.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/LangGraph</category>
      <author>Hamp</author>
      <guid isPermaLink="true">https://hamp.tistory.com/339</guid>
      <comments>https://hamp.tistory.com/339#entry339comment</comments>
      <pubDate>Sun, 22 Feb 2026 17:30:19 +0900</pubDate>
    </item>
    <item>
      <title>Conditional Edge</title>
      <link>https://hamp.tistory.com/338</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dh2dWz/dJMcaaqMZqb/RxDxHM0oY826b9QTkm4YAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dh2dWz/dJMcaaqMZqb/RxDxHM0oY826b9QTkm4YAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dh2dWz/dJMcaaqMZqb/RxDxHM0oY826b9QTkm4YAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdh2dWz%2FdJMcaaqMZqb%2FRxDxHM0oY826b9QTkm4YAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1622&quot; height=&quot;251&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;❓Conditional Edge 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1️⃣ 기존 간단한 그래프&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;110&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cApd88/dJMcadHLuDs/ZANzQmCwHzT0ZGKKNcK0y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cApd88/dJMcadHLuDs/ZANzQmCwHzT0ZGKKNcK0y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cApd88/dJMcadHLuDs/ZANzQmCwHzT0ZGKKNcK0y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcApd88%2FdJMcadHLuDs%2FZANzQmCwHzT0ZGKKNcK0y0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;110&quot; height=&quot;333&quot; data-origin-width=&quot;110&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1771744447286&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;graph_builder.add_node('retrieve', retrieve)
graph_builder.add_node('generate', generate)

from langgraph.graph import START, END

graph_builder.add_edge(START, 'retrieve')
graph_builder.add_edge('retrieve', 'generate')
graph_builder.add_edge('generate', END)
graph = graph_builder.compile()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2️⃣ Conditional Edge가 있는 그래프&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;여기서 중요한 점은, 기존 노드와 차이점을 알아야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-size: 1.12em; letter-spacing: 0px;&quot;&gt;기존 노드의 리턴은&amp;nbsp;&lt;/span&gt;&lt;b&gt;State&lt;/b&gt;다.&lt;/li&gt;
&lt;li&gt;기존 노드는 &lt;b&gt;.add_node&lt;/b&gt;를 통해 등록된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Conditional Edge에 &lt;/b&gt;사용되는 노드에 &lt;b&gt;일반적으로&lt;span style=&quot;color: #ee2323;&quot;&gt; 등록되지 않는다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Conditional Edge에&amp;nbsp;&lt;/b&gt;사용되는 노드는 State가 아닌 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;노드이름&lt;/b&gt;&lt;/span&gt;을 리턴한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771744995419&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain import hub
from typing import Literal

def check_doc_relevance(state: AgentState) -&amp;gt; Literal['generate', 'rewrite']:
    &quot;&quot;&quot;
    주어진 state를 기반으로 문서의 관련성을 판단합니다.

    Args:
        state (AgentState): 사용자의 질문과 문맥을 포함한 에이전트의 현재 state.

    Returns:
        Literal['generate', 'rewrite']: 문서가 관련성이 높으면 'generate', 그렇지 않으면 'rewrite'를 반환합니다.
    &quot;&quot;&quot;
    query = state['query']  # state에서 사용자의 질문을 추출합니다.
    context = state['context']  # state에서 문맥을 추출합니다.

    # 문서 관련성 판단 체인을 구성합니다.
    doc_relevance_chain = doc_relevance_prompt | llm
    
    # 질문과 문맥을 사용하여 문서의 관련성을 판단합니다.
    response = doc_relevance_chain.invoke({'question': query, 'documents': context})

    # 관련성이 높으면 'generate'를 반환하고, 그렇지 않으면 'rewrite'를 반환합니다.
    if response['Score'] == 1:
        return 'generate'
    
    return 'rewrite'&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1771745059956&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;graph_builder.add_node('retrieve', retrieve)
graph_builder.add_node('generate', generate)
graph_builder.add_node('rewrite', rewrite)

# check_doc_relevance 노드 등록 ❌


from langgraph.graph import START, END

graph_builder.add_edge(START, 'retrieve')
graph_builder.add_conditional_edges('retrieve', check_doc_relevance) # conditional_edge 등록
graph_builder.add_edge('rewrite', 'retrieve')
graph_builder.add_edge('generate', END)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3️⃣ Conditional Edge 추가 고급편&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이렇게 처리하면, 그래프의 가독성이 훨씬 좋아진다.&lt;/p&gt;
&lt;pre id=&quot;code_1771746869151&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;graph_builder.add_conditional_edges(
    'retrieve',             # 시작점: 문서를 찾아온 후
    check_doc_relevance,    # 판단 함수: 이 문서가 질문과 관련 있는지 검사
    {
        'relevant': 'generate', # 관련 있다면 -&amp;gt; 답변 생성 단계로!
        'irrelevant': END       # 관련 없다면 -&amp;gt; 여기서 종료(END)
    }
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YQdjf/dJMcah4v82O/AqodqJSKBgGUNqJxxvNf6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YQdjf/dJMcah4v82O/AqodqJSKBgGUNqJxxvNf6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YQdjf/dJMcah4v82O/AqodqJSKBgGUNqJxxvNf6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYQdjf%2FdJMcah4v82O%2FAqodqJSKBgGUNqJxxvNf6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;195&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Conidtional Edge 사용 시, 노드를 추가하고 싶다면, 다음과 같이 노드와 &lt;br /&gt;실제 동작할 노드(등록 안할 노드)를 구분한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기서 &lt;b&gt;check_heplfulness&lt;/b&gt;는 등록될 노드&lt;/li&gt;
&lt;li&gt;&lt;b&gt;check_heplfulness_grader&lt;/b&gt;는 실제 동작되고, 등록도지 않을 노드&lt;/li&gt;
&lt;li&gt;&lt;b&gt;check_heplfulness -&amp;gt; &lt;b&gt;check_heplfulness_grader&lt;/b&gt;&lt;/b&gt;로 감&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771747250833&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def check_helpfulness_grader(state: AgentState) -&amp;gt; str:
    &quot;&quot;&quot;
    사용자의 질문에 기반하여 생성된 답변의 유용성을 평가합니다.

    Args:
        state (AgentState): 사용자의 질문과 생성된 답변을 포함한 에이전트의 현재 state.

    Returns:
        str: 답변이 유용하다고 판단되면 'helpful', 그렇지 않으면 'unhelpful'을 반환합니다.
    &quot;&quot;&quot;
    # state에서 질문과 답변을 추출합니다
    query = state['query']
    answer = state['answer']

    # 답변의 유용성을 평가하기 위한 체인을 생성합니다
    helpfulness_chain = helpfulness_prompt | llm
    
    # 질문과 답변으로 체인을 호출합니다
    response = helpfulness_chain.invoke({'question': query, 'student_answer': answer})

    # 점수가 1이면 'helpful'을 반환하고, 그렇지 않으면 'unhelpful'을 반환합니다
    if response['Score'] == 1:
        return 'helpful'
    
    return 'unhelpful'

def check_helpfulness(state: AgentState) -&amp;gt; AgentState:
    &quot;&quot;&quot;
    유용성을 확인하는 자리 표시자 함수입니다. 
    graph에서 conditional_edge를 연속으로 사용하지 않고 node를 추가해
    가독성을 높이기 위해 사용합니다

    Args:
        state (AgentState): 에이전트의 현재 state.

    Returns:
        AgentState: 변경되지 않은 state를 반환합니다.
    &quot;&quot;&quot;
    # 이 함수는 현재 아무 작업도 수행하지 않으며 state를 그대로 반환합니다
    return state

graph_builder.add_conditional_edges(
    'check_helpfulness',
    check_helpfulness_grader,
    {
        'helpful': END,
        'unhelpful': 'rewrite'
    }
)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✏️ 프롬프트 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1771745576701&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain_core.prompts import PromptTemplate
from langchain import hub

generate_prompt = hub.pull(&quot;rlm/rag-prompt&quot;) # 허브에서 가져온 프롬프트

dictionary = ['사람과 관련된 표현 -&amp;gt; 거주자']

rewrite_prompt = PromptTemplate.from_template(f&quot;&quot;&quot;
사용자의 질문을 보고, 우리의 사전을 참고해서 사용자의 질문을 변경해주세요 
사전: {dictionary}                                           
질문: {{query}}
&quot;&quot;&quot;) # 직접 작성하는 프롬프트&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/h2&gt;</description>
      <category>AI/LangGraph</category>
      <author>Hamp</author>
      <guid isPermaLink="true">https://hamp.tistory.com/338</guid>
      <comments>https://hamp.tistory.com/338#entry338comment</comments>
      <pubDate>Sun, 22 Feb 2026 16:25:18 +0900</pubDate>
    </item>
    <item>
      <title>PDF 전처리 해보기</title>
      <link>https://hamp.tistory.com/337</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wSBWV/dJMb99SVtVD/n36dbVsyEaNu7INAzI3RsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wSBWV/dJMb99SVtVD/n36dbVsyEaNu7INAzI3RsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wSBWV/dJMb99SVtVD/n36dbVsyEaNu7INAzI3RsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwSBWV%2FdJMb99SVtVD%2Fn36dbVsyEaNu7INAzI3RsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1622&quot; height=&quot;251&quot; data-filename=&quot;랭그래프.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  들어가기 전&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;인프런 제이쓴님의 강의 코드를 분석하며, 개념을 정리해보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  학습할 내용&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;필요한 의존성&lt;/li&gt;
&lt;li&gt;새로운 개념&lt;/li&gt;
&lt;li&gt;꿀팁&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 의존성&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a href=&quot;https://docs.langchain.com/oss/python/integrations/document_loaders/pypdfloader&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;1. pypdf&lt;/b&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;랭체인에서 기본적으로 제공하는 PDF 로더가 포함된 패키지&lt;/li&gt;
&lt;li&gt;pdf안에 이미지 데이터를 읽지 못하는 단점 존재&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;a href=&quot;https://github.com/getomni-ai/zerox&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;2. zerox&lt;/b&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;llm을 활용하여 ocr을 돌려 문서를 인식하는 패키지&lt;/li&gt;
&lt;li&gt;다양한 llm 지원&lt;/li&gt;
&lt;li&gt;markdown 형태로 output&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  ocr이란??&lt;br /&gt;&lt;br /&gt;Optical Character Recognitio&lt;/b&gt;의 약자&lt;br /&gt;이미지(사진, 스캔 문서) 속에 들어있는 글자를 컴퓨터가 읽을 수 있는 텍스트 데이터로 추출하는 기술&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;a href=&quot;https://docs.langchain.com/oss/python/integrations/document_loaders/unstructured_markdown&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;3. UnstructedMarkdownLoader&lt;/b&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;랭체인에서 제공하는 MarkdownLoader&lt;/li&gt;
&lt;li&gt;PDF와 다르게 별도로 split을 해줘야함&lt;/li&gt;
&lt;li&gt;정확한 컨텍스트가 전달되지 않을 수 있음&lt;/li&gt;
&lt;li&gt;이 때, txt로 변환하는게 안전함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;markdown, html2text, beautifulsoup4 이ㅛ&amp;nbsp;&lt;/li&gt;
&lt;li&gt;markdown -&amp;gt; html -&amp;gt; BeautifulSoup을 이용한 텍스트 추출&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://docs.langchain.com/oss/python/integrations/splitters/recursive_text_splitter&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;4. RecursiveCharacterTextSpliter&lt;/a&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문서 split에 사용됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://reference.langchain.com/python/langchain-community/document_loaders/text/TextLoader&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;5.TextLoader&lt;/b&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;txt 파일 읽기 위한 Loader&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;⭐ 새로운 개념&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1️⃣ Retrieve &lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM이 학습하지 않은 외부 데이터를 찾아오는 과정&lt;/li&gt;
&lt;li&gt;RAG의 핵심 패턴&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2️⃣ RAG(Retrieval-Augmented Generation, 검색 증강 생성)&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM이 아는체하며, 환각을 발생시키지 않도록, 신뢰할 수 있는 외부 문서를 찾아보고 답변하는 기술&lt;/li&gt;
&lt;li&gt;과정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Retrieval: 찾아오는 과정&lt;/li&gt;
&lt;li&gt;Augmentation: 찾아온 정보를 사용자의 질문을 하나로 합침&lt;/li&gt;
&lt;li&gt;Generatation: 결합된 정보를 바탕으로 LLM이 최종 답변 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3️⃣Vector Store(Vector Database)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유사도 검색에 많이 쓰임
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사과 -&amp;gt; &quot;빨간 과일&quot;, &quot;맛있는 과일&quot;, &quot;애플&quot;등 맥락이 비슷한 결과를 함께 찾아줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;문서, 이미지, 음성, 영상 같은데이터를 숫자 배열인&amp;nbsp; 저장하는 특수 저장소&lt;/li&gt;
&lt;li&gt;변환 그러면 누가하냐?? 바로 AI모델이 담당함, 이걸 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;임베딩&lt;/b&gt;&lt;/span&gt;이라 함 (변환 == 임베딩)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LangGraph&lt;/b&gt;에서는 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;Chroma&lt;/b&gt;&lt;/span&gt;를 대표적으로 씀&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  꿀팁&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1️⃣ sequence 이용&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;177&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPiQbc/dJMcaaj0QVj/wXKiIkbwsR5GwHXxt0MiDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPiQbc/dJMcaaj0QVj/wXKiIkbwsR5GwHXxt0MiDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPiQbc/dJMcaaj0QVj/wXKiIkbwsR5GwHXxt0MiDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPiQbc%2FdJMcaaj0QVj%2FwXKiIkbwsR5GwHXxt0MiDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;100&quot; height=&quot;288&quot; data-origin-width=&quot;177&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그래프 형태는 같은데 더 간단히 만들 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;graph_builder 만들 때 add_sequence이용&lt;/li&gt;
&lt;li&gt;아래 두 코드가 같은 그래프 형태임&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771681844776&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;graph_builder.add_node('retrieve', retrieve)
graph_builder.add_node('generate', generate)

from langgraph.graph import START, END

graph_builder.add_edge(START, 'retrieve')
graph_builder.add_edge('retrieve', 'generate')
graph_builder.add_edge('generate', END)
graph = graph_builder.compile()


# ======================== 시퀀스 이용 ========================

sequence_graph_builder = StateGraph(AgentState).add_sequence([retrieve, generate])
sequence_graph_builder.add_edge(START, 'retrieve')
sequence_graph_builder.add_edge('generate', END)
sequence_graph = sequence_graph_builder.compile()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2️⃣ chain&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  구성 요소&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 103px;&quot; border=&quot;1&quot; data-path-to-node=&quot;5&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;b&gt;구성 요소&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;b&gt;역할&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;b&gt;입력 (Input)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;b&gt;출력 (Output)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0,0&quot;&gt;Prompt&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,1,1,0&quot;&gt;질문 템플릿 생성&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,1,2,0&quot;&gt;딕셔너리 ({var: val})&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,1,3,0&quot;&gt;PromptValue (완성된 문장)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0,0&quot;&gt;LLM&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,2,1,0&quot;&gt;언어 모델 (뇌)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,2,2,0&quot;&gt;문장 또는 메시지 목록&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,2,3,0&quot;&gt;BaseMessage (AI의 답변)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,3,0,0&quot;&gt;Output Parser&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,3,1,0&quot;&gt;답변 정제 (포맷팅)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,3,2,0&quot;&gt;메시지 또는 문자열&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,3,3,0&quot;&gt;구조화된 데이터 (JSON, List 등)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,4,0,0&quot;&gt;Retriever&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,4,1,0&quot;&gt;관련 문서 검색&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,4,2,0&quot;&gt;검색어 (String)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;span data-path-to-node=&quot;5,4,3,0&quot;&gt;문서 목록 (List of Documents)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✏️ 문법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;리닉스 파이프라인과 똑같다.&lt;/p&gt;
&lt;pre id=&quot;code_1771743827162&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain import hub
from langchain_openai import ChatOpenAI

prompt = hub.pull(&quot;rlm/rag-prompt&quot;) # https://smith.langchain.com/hub 에서 찾을 수 있다.
llm = ChatOpenAI(model='gpt-4o')


rag_chain = prompt | llm # prompt 아웃풋 -&amp;gt; llm 인풋&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/2.2%20%EA%B0%84%EB%8B%A8%ED%95%9C%20Retrieval%20%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%20(feat.%20PDF%20%EC%A0%84%EC%B2%98%EB%A6%AC%20%EA%BF%80%ED%8C%81).ipynb&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/2.2%20%EA%B0%84%EB%8B%A8%ED%95%9C%20Retrieval%20%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%20(feat.%20PDF%20%EC%A0%84%EC%B2%98%EB%A6%AC%20%EA%BF%80%ED%8C%81).ipynb&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771677903207&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;inflearn-langgraph-agent/2.2 간단한 Retrieval 에이전트 (feat. PDF 전처리 꿀팁).ipynb at main &amp;middot; jasonkang14/inflearn&quot; data-og-description=&quot;인프런의 &amp;quot;LangGraph를 활용한 AI Agent 개발&amp;quot; 강의 소스코드입니다. Contribute to jasonkang14/inflearn-langgraph-agent development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/2.2%20%EA%B0%84%EB%8B%A8%ED%95%9C%20Retrieval%20%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%20(feat.%20PDF%20%EC%A0%84%EC%B2%98%EB%A6%AC%20%EA%BF%80%ED%8C%81).ipynb&quot; data-og-url=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/2.2%20%EA%B0%84%EB%8B%A8%ED%95%9C%20Retrieval%20%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%20(feat.%20PDF%20%EC%A0%84%EC%B2%98%EB%A6%AC%20%EA%BF%80%ED%8C%81).ipynb&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Hf9mE/dJMb8XR2kVz/PDJpnhGOi5Sv0hkKEnyelk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/iaooU/dJMb8YXIb5D/DrpGKXhw8notFVNlV7rxO0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/2.2%20%EA%B0%84%EB%8B%A8%ED%95%9C%20Retrieval%20%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%20(feat.%20PDF%20%EC%A0%84%EC%B2%98%EB%A6%AC%20%EA%BF%80%ED%8C%81).ipynb&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/jasonkang14/inflearn-langgraph-agent/blob/main/2.2%20%EA%B0%84%EB%8B%A8%ED%95%9C%20Retrieval%20%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%20(feat.%20PDF%20%EC%A0%84%EC%B2%98%EB%A6%AC%20%EA%BF%80%ED%8C%81).ipynb&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Hf9mE/dJMb8XR2kVz/PDJpnhGOi5Sv0hkKEnyelk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/iaooU/dJMb8YXIb5D/DrpGKXhw8notFVNlV7rxO0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;inflearn-langgraph-agent/2.2 간단한 Retrieval 에이전트 (feat. PDF 전처리 꿀팁).ipynb at main &amp;middot; jasonkang14/inflearn&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;인프런의 &quot;LangGraph를 활용한 AI Agent 개발&quot; 강의 소스코드입니다. Contribute to jasonkang14/inflearn-langgraph-agent development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/LangGraph</category>
      <category>Chroma</category>
      <category>Rag</category>
      <category>retrieve</category>
      <category>vectorstore</category>
      <author>Hamp</author>
      <guid isPermaLink="true">https://hamp.tistory.com/337</guid>
      <comments>https://hamp.tistory.com/337#entry337comment</comments>
      <pubDate>Sat, 21 Feb 2026 22:52:05 +0900</pubDate>
    </item>
  </channel>
</rss>