<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>neo_blockchain.log</title>
        <link>https://velog.io/</link>
        <description>스마트 이코노미를 위한 퍼블릭 블록체인, 네오에 대한 모든것</description>
        <lastBuildDate>Fri, 08 May 2026 03:13:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>neo_blockchain.log</title>
            <url>https://velog.velcdn.com/images/neo_blockchain/profile/4d423fb0-2f43-4fa8-be4e-3b3541254985/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. neo_blockchain.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/neo_blockchain" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[잊고 있던 NEO, 2026년에 다시 봐야 할 7가지 이유]]></title>
            <link>https://velog.io/@neo_blockchain/%EC%9E%8A%EA%B3%A0-%EC%9E%88%EB%8D%98-NEO-2026%EB%85%84%EC%97%90-%EB%8B%A4%EC%8B%9C-%EB%B4%90%EC%95%BC-%ED%95%A0-7%EA%B0%80%EC%A7%80-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@neo_blockchain/%EC%9E%8A%EA%B3%A0-%EC%9E%88%EB%8D%98-NEO-2026%EB%85%84%EC%97%90-%EB%8B%A4%EC%8B%9C-%EB%B4%90%EC%95%BC-%ED%95%A0-7%EA%B0%80%EC%A7%80-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Fri, 08 May 2026 03:13:32 GMT</pubDate>
            <description><![CDATA[<p align="center">
  <img src="https://neo-web.azureedge.net/images/presskit/Neo%20symbol.svg" alt="Neo 심볼" width="160" />
</p>

<p align="center"><sub>이미지 출처: <a href="https://neo.org/">neo.org 프레스킷</a></sub></p>

<blockquote>
<p>&quot;중국의 이더리움&quot;이라 불리던 NEO를 기억하는가. 2017~2018년 사이 한 번쯤 들어봤을 이름이지만, 그 이후로는 차트에서 점점 사라졌다. 그런데 2024년 이후 NEO는 조용히 거의 모든 부분을 갈아엎었다. 메인넷도, 토크노믹스도, 개발자 도구도, 심지어 창업자 노선까지. 이 글에서는 한국 투자자·일반 독자를 위해 NEO를 2026년에 다시 들여다봐야 할 이유 7가지를 차분히 정리한다.</p>
</blockquote>
<h2 id="들어가며--neo를-잊은-시점">들어가며 — NEO를 잊은 시점</h2>
<p>NEO는 2014년 중국에서 시작된 <strong>1세대 스마트 컨트랙트 플랫폼</strong>이다. 원래 이름은 Antshares였고, 2017년 NEO로 리브랜딩됐다. 같은 해 한국 거래소에 상장되면서 &quot;중국의 이더리움&quot; 내러티브로 한 번에 시가총액 상위권까지 갔다.</p>
<p>그런데 그 이후 NEO는 한국 시장에서 빠르게 잊혔다.</p>
<ul>
<li>2018년 약세장에서 가격이 큰 폭으로 빠졌다</li>
<li>이더리움 생태계가 폭발적으로 커지면서 상대적 우위가 사라졌다</li>
<li>새 내러티브를 만들어내지 못했다</li>
</ul>
<p>그래서 한국 투자자에게 NEO는 <strong>&quot;한 번 들어봤지만 지금은 잘 모르는&quot;</strong> 코인이 되었다. 하지만 2024~2026년 사이에 NEO 진영은 다음 세 가지 큰 변화를 만들었다.</p>
<ol>
<li><strong>Neo X 메인넷 출시</strong>(2024년 7월) — EVM 호환 사이드체인</li>
<li><strong>SpoonOS 발표</strong>(2025년 4월) — Web3 AI 에이전트 OS</li>
<li><strong>Neo 4 로드맵 발표</strong>(2025년 10월) — RWA·거버넌스 개편 중심</li>
</ol>
<p>이 세 가지가 한국어 콘텐츠로 거의 다뤄지지 않았다. 그래서 지금이 NEO를 다시 봐야 할 시점이다. 아래 7가지 이유를 차례로 짚어본다.</p>
<h2 id="이유-1--dbft-합의-다른-길을-간-1세대">이유 1 — dBFT 합의: 다른 길을 간 1세대</h2>
<p>NEO의 가장 오래된 차별점은 <strong>dBFT(Delegated Byzantine Fault Tolerance) 합의</strong>다. 이더리움이 PoW에서 PoS로 옮겨가고, 솔라나가 PoH 기반 모델을 만든 그 시간에 NEO는 처음부터 BFT 계열을 고수했다.</p>
<p>dBFT의 특징을 단순하게 풀면 다음과 같다.</p>
<ul>
<li><strong>의장(Speaker)</strong>과 <strong>위원(Delegate)</strong>이 정해진다. 위원들은 NEO 토큰 보유자가 투표로 선출한다.</li>
<li>의장이 새 블록을 제안하면 위원들이 검증하고 서명한다.</li>
<li>일정 비율(2/3 이상) 이상 동의하면 그 블록은 즉시 <strong>확정(finality)</strong> 된다. 롤백이 불가능하다.</li>
</ul>
<p>이 모델의 장단을 짚으면,</p>
<table>
<thead>
<tr>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td>즉시 finality (롤백 없음)</td>
<td>검증자 수가 적다 (탈중앙성 한계)</td>
</tr>
<tr>
<td>포크가 발생하지 않음</td>
<td>위원 선출이 정치적일 수 있다</td>
</tr>
<tr>
<td>처리 속도가 빠르다 (이론치 ~10,000 TPS)</td>
<td>위원 다수 담합 시 검열 위험</td>
</tr>
</tbody></table>
<p>특히 &quot;<strong>1초 이내 finality</strong>&quot;는 결제·게임·DeFi 같은 영역에서 명확한 강점이다. 이더리움이 finality를 위해 12~13분을 기다려야 하고, 솔라나가 fork 로그를 처리하는 동안에도 NEO는 즉시 확정된다.</p>
<p>2024년 이후 dBFT가 다시 주목받는 이유는 바로 여기에 있다. RWA(실물자산 토큰화)와 결제용 인프라에서는 finality 속도가 사용자 경험을 결정짓는다.</p>
<h2 id="이유-2--듀얼-토큰-neo를-들고만-있어도-gas가-나온다">이유 2 — 듀얼 토큰: NEO를 들고만 있어도 GAS가 나온다</h2>
<p>이게 NEO의 또 다른 독특함이다. NEO는 <strong>두 개의 토큰</strong>을 동시에 운영한다.</p>
<ul>
<li><strong>NEO</strong>: 거버넌스 토큰. 총 1억 개 고정. 분할 불가(정수 단위만 거래 가능). 가격 변동성 노출의 핵심.</li>
<li><strong>GAS</strong>: 가스비 토큰. 매 블록 5 GAS씩 새로 발행되어 NEO 보유자에게 분배된다.</li>
</ul>
<p>이 구조의 효과는 다음과 같다.</p>
<ol>
<li><strong>NEO를 지갑에 들고만 있으면 GAS가 자동으로 누적된다</strong>. 이른바 &quot;패시브 인컴&quot;이다.</li>
<li><strong>위원에 투표하면 GAS 보상이 더 늘어난다</strong>. 즉 거버넌스 참여 인센티브가 있다.</li>
<li><strong>가스비는 GAS로 지불</strong>되니, NEO 보유자는 사실상 자신의 가스비 일부를 자기가 만들어내는 구조다.</li>
</ol>
<p>투자자 입장에서 보면 이건 꽤 흥미로운 설계다. 스테이킹처럼 락업 기간이나 unbonding period가 없다. 단순히 보유만 해도 GAS가 쌓인다.</p>
<p>물론 함정도 있다. GAS 발행량이 시간에 따라 줄어드는 구조이기 때문에 단순히 &quot;보유하면 점점 더 많아진다&quot;라는 마인드는 위험하다. 그리고 GAS 자체의 시장 가치는 Neo X의 사용량에 영향을 받는다.</p>
<p>업비트와 빗썸 모두 NEO를 KRW 마켓에 상장하고 있다. 하지만 GAS는 거래소에서 직접 거래하기 어렵다. NEO 보유자가 GAS를 받는 방법, 늘리는 방법, 쓰는 방법은 별도 글에서 다룰 만큼 상세한 주제다.</p>
<h2 id="이유-3--neo-x-evm-호환-사이드체인의-등장">이유 3 — Neo X: EVM 호환 사이드체인의 등장</h2>
<p align="center">
  <img src="https://github.com/neo-project.png" alt="Neo Project" width="120" />
</p>

<p align="center"><sub>Neo Project GitHub 조직</sub></p>

<p>2024년 7월 18일, NEO 진영은 <strong>Neo X 메인넷</strong>을 출시했다. 이건 단순한 업그레이드가 아니라 NEO 진영이 만든 <strong>별도의 EVM 호환 사이드체인</strong>이다.</p>
<p>Neo X의 핵심 특징을 정리하면 다음과 같다.</p>
<ul>
<li><strong>Geth 포크 기반 EVM 호환</strong>: 이더리움 솔리디티 컨트랙트를 거의 그대로 배포할 수 있다. 메타마스크도 그대로 쓴다.</li>
<li><strong>dBFT 합의 사용</strong>: 일반 EVM 사이드체인과 달리, 합의는 NEO의 dBFT를 채택했다. 따라서 finality가 즉시다.</li>
<li><strong>MEV 저항성</strong>: 의장이 트랜잭션 순서를 임의로 바꾸기 어렵게 설계했다. 일반 EVM 체인의 MEV 문제를 일부 우회한다.</li>
<li><strong>GAS를 가스비로 사용</strong>: 즉, NEO 보유자가 받은 GAS를 Neo X에서 그대로 쓸 수 있다.</li>
<li><strong>N3 ↔ X 네이티브 브릿지</strong>: 두 체인 간에 자산을 옮길 수 있다.</li>
</ul>
<p>이게 왜 중요한가? 그동안 NEO 생태계의 약점이었던 <strong>솔리디티 개발자 진입 장벽</strong>이 사라졌기 때문이다. 이전까지는 NeoVM 위에 컨트랙트를 짜려면 C#·Python·Go 등을 써야 했다. Solidity 인구가 가장 많은 EVM 진영에서 Neo로 넘어오기 어려웠다.</p>
<p>Neo X는 이 부분을 정조준한다. &quot;이더리움에서 짜던 그 코드, 그대로 가져오세요. 가스비는 GAS로 내시면 됩니다.&quot; 라는 게 메시지다.</p>
<p>생태계 펀드 규모도 작지 않다. Neo X 출시와 함께 <strong>Elevate Program</strong>이라는 $20M 규모 펀드가 발표됐고, 이 자금이 Neo X 위에서 빌드하는 dApp들에 분배된다.</p>
<h2 id="이유-4--spoonos-ai-에이전트-os로의-확장">이유 4 — SpoonOS: AI 에이전트 OS로의 확장</h2>
<p align="center">
  <img src="https://github.com/XSpoonAi/spoon-core/raw/main/logo/spoon.gif" alt="SpoonOS 로고" width="200" />
</p>

<p align="center"><sub>이미지 출처: <a href="https://github.com/XSpoonAi/spoon-core">XSpoonAi/spoon-core</a></sub></p>

<p>2025년 4월, NEO 재단(Neo Global Development)은 <strong>SpoonOS</strong>라는 새 프로젝트를 발표했다. 이건 NEO 자체의 업그레이드가 아니라 <strong>NEO 위에서 동작할 AI 에이전트 OS</strong>다.</p>
<p>SpoonOS가 무엇을 표방하는지 한 줄로 요약하면 다음과 같다.</p>
<blockquote>
<p>AI 에이전트가 추론하고, 외부 도구를 쓰고, 돈을 주고받고, 온체인에 트랜잭션을 보내는 모든 일을 한 프레임워크에서 처리한다.</p>
</blockquote>
<p>이게 NEO에 왜 중요한가? 두 가지 이유가 있다.</p>
<ol>
<li><strong>새로운 내러티브</strong>: 2024년 이후 가상자산 시장에서 가장 뜨거운 키워드는 &quot;AI × 블록체인&quot;이다. NEO는 SpoonOS로 이 흐름에 정면으로 합류한다.</li>
<li><strong>GAS 토큰 활용처 확대</strong>: AI 에이전트가 트랜잭션을 발생시킬수록 GAS 수요가 늘어난다. 듀얼 토큰 구조가 새 사용처를 얻는다.</li>
</ol>
<p>SpoonOS의 기술적 깊이는 별도로 검증해야 한다. GitHub에는 <a href="https://github.com/XSpoonAi/spoon-core"><code>XSpoonAi/spoon-core</code></a>가 공개되어 있고, Apache-2.0 라이선스의 Python 오픈소스로 진행 중이다. 자세한 평가는 <a href="./05-spoonos-promise-vs-reality.md">&quot;SpoonOS, 정말 &#39;OS&#39;인가? — 약속 vs 현실 솔직 리뷰&quot;</a> 글에서 별도로 다룬다.</p>
<p>투자자 관점에서 봐야 할 포인트는 <strong>&quot;NEO가 AI 내러티브에 진입할 발판을 만들었다&quot;</strong>는 사실 그 자체다.</p>
<h2 id="이유-5--neo-4-로드맵-rwa로의-피벗">이유 5 — Neo 4 로드맵: RWA로의 피벗</h2>
<p>2025년 10월 27일, NEO 공동창업자 <strong>Erik Zhang</strong>은 GitHub 이슈에 &quot;Neo 4 Roadmap&quot;이라는 제목의 글을 올렸다. 이게 사실상 NEO의 다음 메이저 업그레이드 로드맵이다.</p>
<p>핵심 키워드는 다음과 같다.</p>
<ul>
<li><strong>RWA(Real World Assets)</strong>: 부동산·채권·IP를 NEO 위에 토큰화하는 데 집중한다.</li>
<li><strong>Neo Council 강화</strong>: 거버넌스 위원회의 권한을 늘리고, NEO 홀더에게 referendum(국민투표) 권한을 준다.</li>
<li><strong>NEP-21 표준</strong>: 지갑·dApp 간 표준 인터페이스. 2026년 4월 도입 예정.</li>
<li><strong>Layer-2 거버넌스 검토</strong>: 향후 Neo 위 L2를 만들 수 있는지 논의 중.</li>
</ul>
<p>특히 <strong>RWA 피벗</strong>은 큰 변화다. NEO가 지금까지 표방하던 &quot;Smart Economy&quot;라는 슬로건이 더 구체적인 영역(실물자산)으로 좁혀지는 셈이다.</p>
<p>다만 이 로드맵에는 그늘도 있다. 또 다른 공동창업자 <strong>Da Hongfei</strong>가 RWA보다 AI 쪽 비전을 강조하면서, 두 창업자 사이의 노선 차이가 표면화됐다. 거버넌스 진영 분열은 NEO 보유자에게 단기 리스크다.</p>
<p>이 분열이 어떻게 정리될지가 2026년 NEO 가격을 좌우할 가능성이 크다.</p>
<h2 id="이유-6--풀스택-인프라-neofs와-neoid">이유 6 — 풀스택 인프라: NeoFS와 NeoID</h2>
<p>NEO의 또 다른 차별점은 <strong>모든 인프라를 자체 표준으로 갖췄다는 점</strong>이다.</p>
<ul>
<li><strong>NeoVM</strong>: 자체 가상머신</li>
<li><strong>Native Contracts</strong>: 핵심 기능이 VM 내부에 박혀 있어 가스비가 매우 저렴</li>
<li><strong>NeoFS</strong>: 분산 파일 스토리지 (IPFS와 유사)</li>
<li><strong>NeoID</strong>: 분산 신원 (DID 표준 준수)</li>
</ul>
<p>이걸 보고 두 가지 평가가 가능하다.</p>
<ol>
<li><strong>과잉 설계</strong>: 이더리움 + IPFS + ENS를 다 따로 쓰면 되는데, NEO는 굳이 자체 스택을 만든다. 표준 분산이 약점이다.</li>
<li><strong>풀스택의 강점</strong>: 한 진영에서 합의·VM·스토리지·신원이 모두 통합 운영되니, dApp 빌더 입장에서는 종속성을 한 곳에서 해결한다.</li>
</ol>
<p>특히 <strong>NeoFS는 IPFS와 다른 강점</strong>이 있다. IPFS는 파일이 어딘가에 저장되어 있다는 보장이 약하다(누군가 핀할 때만 살아있다). NeoFS는 노드 운영자가 GAS로 보상받으면서 파일 저장을 보증한다. 즉 <strong>경제 인센티브가 붙은 분산 스토리지</strong>다.</p>
<p>NeoID는 W3C DID 표준을 따른다. 이건 RWA 토큰화·KYC·ESG 같은 시나리오에서 점점 중요해진다.</p>
<h2 id="이유-7--한국-시장-접근성">이유 7 — 한국 시장 접근성</h2>
<p>마지막 이유는 한국 투자자에게 가장 직관적인 부분이다.</p>
<ul>
<li><strong>업비트 KRW 마켓</strong>: NEO/KRW 페어 상장</li>
<li><strong>빗썸 KRW 마켓</strong>: NEO/KRW 페어 상장</li>
</ul>
<p>즉 한국 원화로 직접 사고팔 수 있다. 알트코인 중에는 흔치 않은 환경이다. 하지만 GAS는 거래소에서 직접 거래하기 어려우니, GAS 생성·인출 흐름은 NEO를 자기 지갑(NeoLine, O3 Wallet 등)으로 옮긴 뒤 처리해야 한다.</p>
<p>또 하나 짚어둘 점은 <strong>2025~2026년 한국 가상자산 규제 환경</strong>이다. 가상자산이용자보호법 시행으로 거래소 상장 코인의 검증이 강화되었지만, NEO는 1세대 메이저 코인이라 상장 유지가 안정적이다. 신규 상장 알트코인보다 상장폐지 위험이 낮다.</p>
<h2 id="7가지를-한-번에-보면">7가지를 한 번에 보면</h2>
<table>
<thead>
<tr>
<th>#</th>
<th>이유</th>
<th>한 줄 요약</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>dBFT 합의</td>
<td>1초 이내 finality, 롤백 없음</td>
</tr>
<tr>
<td>2</td>
<td>듀얼 토큰</td>
<td>NEO 보유 → GAS 자동 누적</td>
</tr>
<tr>
<td>3</td>
<td>Neo X</td>
<td>EVM 호환 사이드체인 + GAS 가스비</td>
</tr>
<tr>
<td>4</td>
<td>SpoonOS</td>
<td>AI 에이전트 OS로 새 내러티브 진입</td>
</tr>
<tr>
<td>5</td>
<td>Neo 4 로드맵</td>
<td>RWA·거버넌스 개편 중심 피벗</td>
</tr>
<tr>
<td>6</td>
<td>풀스택 인프라</td>
<td>NeoFS·NeoID로 자체 스택 완성</td>
</tr>
<tr>
<td>7</td>
<td>한국 접근성</td>
<td>업비트·빗썸 KRW 마켓 상장 유지</td>
</tr>
</tbody></table>
<h2 id="짚어둬야-할-리스크">짚어둬야 할 리스크</h2>
<p>NEO를 다시 본다고 해도, 무조건 긍정 결론으로 끌고 가서는 안 된다. 솔직하게 짚어야 할 리스크가 있다.</p>
<ul>
<li><strong>창업자 분열</strong>: Erik Zhang vs Da Hongfei의 노선 차이가 거버넌스 진영을 분열시킬 가능성이 있다.</li>
<li><strong>TVL 규모</strong>: NEO 생태계 dApp의 TVL은 이더리움·솔라나·BNB 체인 대비 매우 작다. 사용자 활동성이 약하다.</li>
<li><strong>개발자 모멘텀</strong>: NeoVM 진영의 개발자 풀이 EVM 진영보다 작다. Neo X로 보완하지만, 그 결과가 2026년에 어떻게 나타날지는 미지수다.</li>
<li><strong>내러티브 의존</strong>: AI·RWA 내러티브가 시장에서 식으면 NEO도 영향을 받는다. 코어 펀더멘털만으로 가격을 지지하기는 어렵다.</li>
</ul>
<p>이 리스크를 인지하고도 NEO를 들여다보는 사람은, 적어도 시장 평균보다 정보 우위에 설 가능성이 있다. 한국어 콘텐츠가 거의 없는 상태이기 때문이다.</p>
<h2 id="마무리">마무리</h2>
<p>2017년 NEO를 처음 들었던 그 때와, 2026년의 NEO는 거의 다른 프로젝트라고 봐도 무방하다. 합의 메커니즘은 같지만 토크노믹스, 사이드체인, AI 에이전트 OS, RWA 로드맵까지 모두 새로 깔렸다. &quot;잊혀진 1세대&quot;라는 프레임을 깨고, <strong>재시동 시점의 1세대 L1</strong>이라는 시각으로 다시 보면 흥미로운 분석 대상이다.</p>
<p>이 글이 다음 단계 리서치의 출발점이 되었으면 한다. 더 깊은 주제 — dBFT 비교 분석, GAS 보상 계산, Neo X 핸즈온, SpoonOS 튜토리얼 — 는 이어지는 시리즈에서 하나씩 풀어볼 예정이다.</p>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li><a href="https://neo.org/">Neo 공식 사이트</a></li>
<li><a href="https://developers.neo.org/docs/n3/foundation/consensus/consensus_algorithm">Neo Developer Resource — dBFT 2.0</a></li>
<li><a href="https://neonewstoday.com/general/neo-launches-neo-x-mainnet/">Neo News Today: Neo X 메인넷 출시</a></li>
<li><a href="https://neonewstoday.com/ai/spoonos-announced-as-an-operating-system-for-ai-agents-on-blockchain/">Neo News Today: SpoonOS 발표</a></li>
<li><a href="https://github.com/neo-project/neo/issues/4198">GitHub Issue #4198 — NEO 4 Roadmap</a></li>
<li><a href="https://everstake.one/blog/n3-the-most-powerful-and-feature-rich-version-of-the-neo-blockchain-to-date">Everstake: N3 분석</a></li>
<li><a href="https://fs.neo.org/">NeoFS</a></li>
<li><a href="https://www.upbit.com/exchange/CRIX.UPBIT.KRW-NEO">업비트 NEO/KRW</a></li>
<li><a href="https://www.bithumb.com/react/trade/order/NEO-KRW">빗썸 NEO/KRW</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ElizaOS vs SpoonOS vs Virtuals — Web3 AI Agent OS 3파전 비교]]></title>
            <link>https://velog.io/@neo_blockchain/ElizaOS-vs-SpoonOS-vs-Virtuals-Web3-AI-Agent-OS-3%ED%8C%8C%EC%A0%84-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@neo_blockchain/ElizaOS-vs-SpoonOS-vs-Virtuals-Web3-AI-Agent-OS-3%ED%8C%8C%EC%A0%84-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Mon, 04 May 2026 04:32:47 GMT</pubDate>
            <description><![CDATA[<p align="center"><sub>왼쪽부터 ElizaOS, SpoonOS, Virtuals Protocol — 각 GitHub 조직 아바타</sub></p>

<blockquote>
<p>2024년 하반기부터 &quot;AI 에이전트&quot;가 Web3 영역에서 가장 뜨거운 키워드로 자리 잡았다. ElizaOS는 트위터 봇으로 폭발적인 화제를 모았고, Virtuals Protocol은 에이전트 토큰화로 시가총액을 키웠으며, SpoonOS는 Neo 진영의 풀스택 에이전트 OS를 표방하며 등장했다. 이번 글에서는 이 셋을 같은 잣대로 놓고 비교해 본다. 셋 다 &quot;AI agent OS&quot;라는 라벨을 달지만, 막상 아키텍처를 까보면 결이 완전히 다르다.</p>
</blockquote>
<h2 id="들어가며--왜-이-비교가-필요한가">들어가며 — 왜 이 비교가 필요한가</h2>
<p>AI 에이전트 프로젝트는 이미 수십 개가 넘는다. 그런데 이 셋만 골라 비교하는 이유는 분명하다.</p>
<ul>
<li><strong>ElizaOS</strong>: 가장 큰 개발자 커뮤니티와 빠른 채택 속도. AI16Z 토큰의 부상과 함께 사실상 <strong>표준 후보</strong>로 자리 잡았다.</li>
<li><strong>Virtuals Protocol</strong>: &quot;에이전트 = 토큰&quot;이라는 발상으로 <strong>AgentFi 시장</strong>을 선도했다. Base 체인에서 가장 큰 에이전트 GDP를 만든 프로토콜이다.</li>
<li><strong>SpoonOS</strong>: 후발주자지만 Neo 재단의 자금과 인프라를 등에 업고 <strong>풀스택 OS</strong>를 표방한다. 위 두 프로젝트와 달리 프라이버시(ZKML/TEE)와 결제 레일(x402)을 1급으로 취급한다.</li>
</ul>
<p>세 프로젝트는 표면적으로 같은 시장을 노리는 듯 보이지만, 실제로는 노리는 사용자도, 가치 제안도, 토큰 모델도 전혀 다르다. 아래에서 차근차근 풀어본다.</p>
<h2 id="한눈에-보는-비교표">한눈에 보는 비교표</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>ElizaOS</th>
<th>Virtuals Protocol</th>
<th>SpoonOS</th>
</tr>
</thead>
<tbody><tr>
<td>출시</td>
<td>2024년 (a16z 인큐베이팅)</td>
<td>2024년</td>
<td>2025년 4월 (Neo 재단 인큐베이팅)</td>
</tr>
<tr>
<td>주된 정체성</td>
<td>Web3 친화 AI 에이전트 OS</td>
<td>에이전트 토큰화 플랫폼</td>
<td>풀스택 Web3 AI agent OS</td>
</tr>
<tr>
<td>주 언어</td>
<td>TypeScript (Node.js)</td>
<td>자체 플랫폼 (코드 작성 최소화)</td>
<td>Python 3.12+</td>
</tr>
<tr>
<td>주 체인</td>
<td>Solana 중심, 멀티 체인</td>
<td>Base 중심</td>
<td>Neo N3 / Neo X 우선, EVM 호환</td>
</tr>
<tr>
<td>프라이버시 모듈</td>
<td>별도 통합 필요</td>
<td>약함</td>
<td>ZKML / TEE / FHE 명시</td>
</tr>
<tr>
<td>결제 레일</td>
<td>별도 통합</td>
<td>자체 토큰($VIRTUAL 등)</td>
<td>x402 내장</td>
</tr>
<tr>
<td>그래프 워크플로우</td>
<td>부분 지원</td>
<td>미지원</td>
<td>SpoonGraph 내장</td>
</tr>
<tr>
<td>MCP 지원</td>
<td>도구로 가능</td>
<td>미지원</td>
<td>1급 시민 (런타임 통합)</td>
</tr>
<tr>
<td>자체 토큰</td>
<td>AI16Z, ai16z</td>
<td>VIRTUAL, AGENT 토큰</td>
<td><strong>현재 없음 (추정)</strong></td>
</tr>
<tr>
<td>강점</td>
<td>큰 커뮤니티, 빠른 반복</td>
<td>강력한 토큰 GDP, UX 단순화</td>
<td>프라이버시 + 블록체인 + 결제 통합</td>
</tr>
<tr>
<td>약점</td>
<td>토큰·프라이버시 약함</td>
<td>개발자 커스터마이즈 한계</td>
<td>생태계 초기, 레퍼런스 부족</td>
</tr>
</tbody></table>
<h2 id="1-elizaos--가장-빠른-채택을-만든-트위터-봇-os">1. ElizaOS — 가장 빠른 채택을 만든 트위터 봇 OS</h2>
<p>ElizaOS는 처음부터 거창한 OS를 표방하지 않았다. 시작점은 <strong>AI 캐릭터를 트위터에 띄우는 프레임워크</strong>였다. 그런데 이게 a16z 토큰(AI16Z)과 결합되면서 폭발적인 화제를 만들었고, 결과적으로 가장 큰 Web3 AI 에이전트 커뮤니티가 형성됐다.</p>
<h3 id="아키텍처">아키텍처</h3>
<p>ElizaOS는 TypeScript로 작성된 Node.js 기반 런타임이다. 핵심 개념은 다음과 같다.</p>
<ul>
<li><strong>Character file</strong>: 에이전트의 페르소나(이름, 성격, 말투, 지식)를 JSON으로 정의한다. 코드를 거의 안 짜도 새 에이전트를 만들 수 있다.</li>
<li><strong>Plugin system</strong>: 트위터, 디스코드, 텔레그램, Solana 트랜잭션 등 외부 시스템 연동을 플러그인으로 추가한다.</li>
<li><strong>Memory</strong>: 대화 히스토리와 사실(fact)을 별도로 저장하는 메모리 모듈을 제공한다.</li>
</ul>
<p>이 구조 덕분에 <strong>개발자가 아닌 사용자도 캐릭터 파일만 작성하면 자기만의 에이전트를 가질 수 있다</strong>. 이 진입 장벽의 낮음이 ElizaOS 폭발의 진짜 원인이다.</p>
<h3 id="강점">강점</h3>
<ul>
<li><strong>커뮤니티 규모</strong>: GitHub 스타 수, 서드파티 플러그인, 튜토리얼 콘텐츠 모두 압도적으로 많다.</li>
<li><strong>반복 속도</strong>: 매주 새 기능이 머지된다. 트렌드 대응이 빠르다.</li>
<li><strong>토큰 모멘텀</strong>: AI16Z 토큰이 &quot;에이전트 OS의 기축&quot;처럼 인식되며 자체 펀딩 에코를 만들었다.</li>
</ul>
<h3 id="약점">약점</h3>
<ul>
<li><strong>블록체인 통합 깊이</strong>: Solana 위주로 시작했고, 아직도 트랜잭션 신뢰성·키 관리 같은 인프라는 별도 솔루션을 끼워야 한다.</li>
<li><strong>프라이버시</strong>: ZKML이나 TEE 같은 프라이버시 프리미티브는 별도 통합이 필요하다.</li>
<li><strong>결제</strong>: 에이전트 간 마이크로페이먼트 표준이 없다. 자체 토큰 경제에 의존한다.</li>
</ul>
<p>요약하면 ElizaOS는 <strong>&quot;빠르게 캐릭터를 띄우고 트위터/디스코드에 살게 하는&quot; 데 최적화</strong>된 도구다. 진지한 온체인 자동화나 결제가 필요한 시나리오에는 추가 작업이 많이 든다.</p>
<h2 id="2-virtuals-protocol--에이전트를-토큰화한-플랫폼">2. Virtuals Protocol — 에이전트를 토큰화한 플랫폼</h2>
<p align="center">
  <img src="https://github.com/Virtual-Protocol/virtuals-python/raw/main/docs/imgs/new_sdk_visual.png" alt="Virtuals SDK 구조" width="640" />
</p>

<p align="center"><sub>이미지 출처: <a href="https://github.com/Virtual-Protocol/virtuals-python">Virtual-Protocol/virtuals-python</a></sub></p>

<p>Virtuals는 다른 둘과 결이 다르다. <strong>&quot;에이전트 자체를 토큰으로 만들고 거래하게 한다&quot;</strong>는 발상이 핵심이다.</p>
<h3 id="어떻게-동작하는가">어떻게 동작하는가</h3>
<p>Virtuals 플랫폼에서 누구나 새 에이전트를 만들 수 있고, 그 에이전트마다 <strong>고유 토큰</strong>이 발행된다. 사용자는 그 토큰을 사거나 스테이킹해서 에이전트의 성과에 베팅한다. 에이전트가 트위터에서 인기를 끌거나, 게임에서 활약하거나, NFT를 큐레이션해 수익을 내면 토큰 가치가 오른다.</p>
<p>이 모델은 <strong>AgentFi</strong>라는 새로운 카테고리를 만들었다. 2024<del>2025년 사이 Base 체인 위에서 수십</del>수백 개의 에이전트 토큰이 만들어졌고, 일부 토큰은 시가총액 수억 달러까지 갔다.</p>
<h3 id="강점-1">강점</h3>
<ul>
<li><strong>단순한 UX</strong>: 코딩 없이 웹 인터페이스로 에이전트를 만들 수 있다.</li>
<li><strong>자본 흡수력</strong>: 에이전트마다 토큰 시장이 형성되니, 인기 있는 에이전트는 즉각 자본을 끌어들인다.</li>
<li><strong>명확한 인센티브</strong>: 만든 사람, 보유자, 스테이커가 모두 한 방향으로 정렬된다.</li>
</ul>
<h3 id="약점-1">약점</h3>
<ul>
<li><strong>개발자 커스터마이즈 제한</strong>: 에이전트의 동작 방식이 플랫폼이 제공하는 틀을 크게 벗어나기 어렵다.</li>
<li><strong>토큰 실험에 가까움</strong>: 에이전트의 실제 가치보다 토큰 투기로 가격이 결정되는 경향이 강하다.</li>
<li><strong>블록체인 종속</strong>: Base 중심이라 다른 체인 사용자에게는 진입 장벽이 있다.</li>
</ul>
<p>ElizaOS가 &quot;캐릭터를 코드로 빚는 도구&quot;라면 Virtuals는 &quot;에이전트를 자산으로 거래하는 시장&quot;이다. 둘은 사실상 다른 레이어를 노린다.</p>
<h2 id="3-spoonos--풀스택을-노리는-후발주자">3. SpoonOS — 풀스택을 노리는 후발주자</h2>
<p>SpoonOS는 두 프로젝트보다 늦게 등장했다. 그래서 의도적으로 둘과 다른 포지셔닝을 잡았다. 이름 그대로 <strong>&quot;Operating System&quot;</strong> 을 표방하며, 추론·도구·메모리·결제·프라이버시·온체인을 한 프레임워크에서 처리하겠다는 야심을 드러낸다.</p>
<h3 id="4계층-아키텍처">4계층 아키텍처</h3>
<p>공식 발표에 따르면 SpoonOS는 다음 4계층으로 구성된다.</p>
<ol>
<li><strong>Data Layer</strong>: 자체 벡터 DB <code>BeVec</code>, 데이터 통합 레이어 <code>MCP+</code></li>
<li><strong>Execution Layer</strong>: ReAct 에이전트, SpoonGraph 워크플로우 엔진</li>
<li><strong>Coordination Layer</strong>: AI Agent Interoperability Protocol(DID + ZKML 기반), x402 결제</li>
<li><strong>Application Layer</strong>: 실제 에이전트 dApp들</li>
</ol>
<p>이 구조는 LangChain과 LangGraph가 합쳐진 듯한 느낌에, 거기에 결제와 프라이버시 모듈을 더한 것에 가깝다.</p>
<h3 id="차별점">차별점</h3>
<ul>
<li><strong>MCP 1급 지원</strong>: Anthropic이 만든 Model Context Protocol을 런타임에서 직접 발견·호출할 수 있다. ElizaOS·Virtuals에는 없는 능력이다.</li>
<li><strong>x402 결제 내장</strong>: 에이전트가 외부 API를 호출하면서 USDC로 자동 결제할 수 있다. 구독 모델이 아닌, <strong>호출 단위 마이크로페이먼트</strong>가 핵심이다.</li>
<li><strong>프라이버시 프리미티브</strong>: ZKML(영지식 머신러닝), TEE(신뢰 실행 환경), FHE(완전 동형 암호) 활용을 백서에 명시했다. 단, 실제 구현 깊이는 검증 필요.</li>
<li><strong>DBOS 통합</strong>: 장시간 실행 에이전트의 상태 영속성을 위해 DBOS와 협업한다. <strong>&quot;트랜잭션 도중 서버가 죽으면 어떻게 되는가&quot;</strong> 라는 실전 문제에 답하려는 시도다.</li>
</ul>
<h3 id="약점-2">약점</h3>
<ul>
<li><strong>생태계 초기</strong>: GitHub 스타 수도, 레퍼런스 dApp도 ElizaOS·Virtuals 대비 적다.</li>
<li><strong>TypeScript SDK 미공개</strong>: 공식적으로는 다중 언어를 표방했지만 GitHub에는 Python만 올라와 있다.</li>
<li><strong>자체 토큰 부재</strong>: 인센티브 펀드는 USD 단위로 운영되며, 토큰 경제가 없다. 장단이 모두 있다.</li>
<li><strong>Neo 의존</strong>: Neo 재단의 자금과 마케팅에 크게 의존한다. Neo 진영의 모멘텀이 약해지면 SpoonOS도 영향을 받는다.</li>
</ul>
<p>요약하면 SpoonOS는 <strong>&quot;진지한 인프라형 OS&quot;</strong> 를 노린다. 빠른 트렌드 추격보다, 한 시스템 안에서 추론·결제·프라이버시·온체인을 모두 처리하려는 설계다.</p>
<h2 id="시나리오별-추천">시나리오별 추천</h2>
<p>위 비교를 종합하면 실제로 누가 무엇을 써야 하는가에 답할 수 있다.</p>
<h3 id="트위터디스코드에서-살아있는-캐릭터-봇을-만들고-싶다">트위터/디스코드에서 살아있는 캐릭터 봇을 만들고 싶다</h3>
<p>→ <strong>ElizaOS</strong>가 압도적으로 빠르다. 캐릭터 파일 한 개만 작성하면 된다. 커뮤니티 플러그인도 풍부하다.</p>
<h3 id="에이전트를-자산화해-수익을-만들고-싶다">에이전트를 자산화해 수익을 만들고 싶다</h3>
<p>→ <strong>Virtuals</strong>가 사실상 유일한 답이다. 토큰 발행·스테이킹·거래 인프라가 통합되어 있다. 다만 깊은 커스터마이즈는 어렵다.</p>
<h3 id="온체인-자동화--외부-api-결제--프라이버시-보장이-동시에-필요한-dapp을-만들고-싶다">온체인 자동화 + 외부 API 결제 + 프라이버시 보장이 동시에 필요한 dApp을 만들고 싶다</h3>
<p>→ <strong>SpoonOS</strong>를 검토할 가치가 있다. x402와 MCP 통합이 핵심 차별점이다. 다만 v0.4 대 SDK라 위험을 감수해야 한다.</p>
<h3 id="기업-내부-ai-워크플로우를-짜고-싶다-web3-요소-거의-없음">기업 내부 AI 워크플로우를 짜고 싶다 (Web3 요소 거의 없음)</h3>
<p>→ 셋 다 과한 선택이다. <strong>LangChain/LangGraph</strong>로 충분하다. SpoonOS의 SpoonGraph는 LangGraph와 사용성이 비슷하지만, 굳이 Web3 의존성을 끌어올 이유가 없다.</p>
<h2 id="비교의-함정--같은-os가-아니다">비교의 함정 — 같은 OS가 아니다</h2>
<p>이 셋을 한 비교표에 올리는 것 자체가 사실 약간의 함정을 내포한다. 셋은 같은 정의의 OS가 아니다.</p>
<ul>
<li><strong>ElizaOS</strong>는 캐릭터 런타임에 가깝다. &quot;OS&quot;라는 라벨은 마케팅 측면이 크다.</li>
<li><strong>Virtuals</strong>는 토큰화 플랫폼이다. 엄밀히는 OS가 아니라 <strong>에이전트 마켓플레이스</strong>다.</li>
<li><strong>SpoonOS</strong>는 명시적으로 OS 추상화를 노린다. 그래서 추론·결제·프라이버시를 모두 한 묶음에 넣었다.</li>
</ul>
<p>따라서 &quot;어떤 OS가 가장 좋은가&quot;라는 질문은 잘못된 질문에 가깝다. 정확한 질문은 <strong>&quot;내가 만들려는 에이전트는 어디에 붙을 때 가장 자연스러운가&quot;</strong> 이다.</p>
<h2 id="토큰-모델--가장-큰-차이">토큰 모델 — 가장 큰 차이</h2>
<p>세 프로젝트의 토큰 모델은 다음과 같이 정리할 수 있다.</p>
<table>
<thead>
<tr>
<th>프로젝트</th>
<th>토큰</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td>ElizaOS</td>
<td>AI16Z, ai16z</td>
<td>생태계 가스/거버넌스. 사실상 <strong>밈 + 인프라</strong> 하이브리드</td>
</tr>
<tr>
<td>Virtuals</td>
<td>VIRTUAL, AGENT 토큰</td>
<td>본질 — <strong>에이전트 자체가 토큰</strong></td>
</tr>
<tr>
<td>SpoonOS</td>
<td>없음 (2026.05 기준)</td>
<td>인센티브 펀드는 USD 단위</td>
</tr>
</tbody></table>
<p>자체 토큰이 없다는 점은 SpoonOS의 양면적 특징이다.</p>
<ul>
<li><strong>장점</strong>: 토큰 가격 변동에 휘둘리지 않는다. 인프라 안정성을 약속할 수 있다.</li>
<li><strong>단점</strong>: 단기 자본 유입이 약하다. 에이전트 경제를 즉각 부트스트랩하기 어렵다.</li>
</ul>
<p>향후 SpoonOS가 토큰을 발행할지, 아니면 GAS/Neo X 가스 토큰을 그대로 활용할지는 아직 공개되지 않았다.</p>
<h2 id="한국-빌더-관점에서">한국 빌더 관점에서</h2>
<p>한국에서 Web3 AI 에이전트를 만들고 싶다면 다음을 고려할 만하다.</p>
<ol>
<li><strong>첫 실험은 ElizaOS로</strong>. 캐릭터 파일만 만들면 트위터/디스코드에 띄울 수 있다. 커뮤니티 자료도 한국어 번역이 빠르게 나오고 있다.</li>
<li><strong>자본 흡수력이 필요하면 Virtuals</strong>. 다만 토큰 거래소 정책과 한국 규제(가상자산이용자보호법)를 사전에 검토해야 한다.</li>
<li><strong>진지한 인프라형 dApp이라면 SpoonOS</strong>. Scoop AI Hackathon이 서울에서도 열린다. 한국어 자료가 거의 없는 만큼 빠른 선점 기회가 있다.</li>
</ol>
<h2 id="마무리">마무리</h2>
<p>ElizaOS는 <strong>속도</strong>, Virtuals는 <strong>자본 흡수력</strong>, SpoonOS는 <strong>인프라 깊이</strong>라는 서로 다른 자산을 가지고 같은 시장에 들어왔다. 어느 하나가 다른 둘을 죽일 가능성보다는, <strong>계층이 다른 도구로 공존할 가능성</strong>이 더 높다. 빌더는 자신이 만드는 에이전트가 어떤 계층에서 가장 빛나는지를 먼저 따져봐야 한다.</p>
<p>이 시장은 1년 후에 다시 보면 분명 지형이 바뀐다. 지금 이 글의 비교표가 1년 뒤에도 그대로일 가능성은 거의 없다. 그래도 한 가지는 분명하다. <strong>&quot;Web3 AI 에이전트&quot;는 이미 단일 도구가 아니라 여러 계층의 시장</strong>이라는 점이다.</p>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li><a href="https://docs.elizaos.ai/">ElizaOS Documentation</a></li>
<li><a href="https://arxiv.org/abs/2501.06781">Eliza: A Web3 friendly AI Agent OS (arXiv 2501.06781)</a></li>
<li><a href="https://www.virtuals.io/">Virtuals Protocol</a></li>
<li><a href="https://spoonai.io/">SpoonOS 공식 사이트</a></li>
<li><a href="https://github.com/XSpoonAi/spoon-core">GitHub: XSpoonAi/spoon-core</a></li>
<li><a href="https://neonewstoday.com/ai/spoonos-announced-as-an-operating-system-for-ai-agents-on-blockchain/">Neo News Today: SpoonOS 소개</a></li>
<li><a href="https://docs.cdp.coinbase.com/x402/welcome">Coinbase x402 문서</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI 에이전트 개발자를 위한 Web3 입문 — SpoonOS Skills Marketplace로 시작하기]]></title>
            <link>https://velog.io/@neo_blockchain/AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-Web3-%EC%9E%85%EB%AC%B8-SpoonOS-Skills-Marketplace%EB%A1%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@neo_blockchain/AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-Web3-%EC%9E%85%EB%AC%B8-SpoonOS-Skills-Marketplace%EB%A1%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 24 Apr 2026 08:52:43 GMT</pubDate>
            <description><![CDATA[<h2 id="ai-에이전트를-블록체인-위에-올리고-싶은데-어디서부터-시작하지">&quot;AI 에이전트를 블록체인 위에 올리고 싶은데, 어디서부터 시작하지?&quot;</h2>
<p>AI 에이전트 개발은 폭발적으로 성장하고 있어요. 랭체인(LangChain), 크루AI(CrewAI), 오토젠(AutoGen) 같은 프레임워크 덕분에 에이전트를 만드는 것 자체는 점점 쉬워지고 있는데요.</p>
<p>그리고 이 흐름은 숫자로도 명확하게 보여요. 크립토 AI 투자 비중이 2023년 하반기 전체 크립토 AI 딜의 5%에서 2025년 상반기 36%로 급증했어요. 282개 Web3 AI 에이전트 프로젝트가 총 62조 원(43억 달러)의 투자를 유치했고요. AI 개발자들이 Web3로 진입하려는 수요가 폭발적으로 증가하고 있는 거예요.</p>
<p>하지만 이걸 <strong>Web3 위에서 돌리려고 하면</strong> 이야기가 달라져요. 지갑 연동은 어떻게 하고, 온체인 데이터는 어디서 가져오고, 스마트 컨트랙트 호출은 어떻게 하고, 에이전트 메모리는 어디에 저장하고... 웹3 경험이 없는 AI 개발자에게는 진입 장벽이 꽤 높아요.</p>
<p><strong>SpoonOS</strong>는 이 문제를 해결하기 위해 만들어졌어요. Neo 블록체인 위에 구축된 에이전트 전용 운영체제로, AI 개발자가 블록체인을 몰라도 Web3 에이전트를 만들 수 있도록 설계되었어요.</p>
<p>이 글에서는 SpoonOS의 <strong>Skills Marketplace</strong>를 중심으로, Web3 AI 에이전트 개발의 첫걸음을 안내해 드릴게요.</p>
<hr>
<h2 id="skills-marketplace란">Skills Marketplace란?</h2>
<p>SpoonOS의 Skills Marketplace는 <strong>에이전트가 사용할 수 있는 능력(스킬)을 모아놓은 Web3 네이티브 마켓</strong>이에요.</p>
<p>여기서 &quot;스킬&quot;이란 에이전트가 수행하는 하나의 개별 작업 단위예요. 예를 들면:</p>
<ul>
<li>지갑 생성 및 잔액 조회</li>
<li>특정 토큰의 실시간 가격 조회</li>
<li>스마트 컨트랙트의 특정 함수 호출</li>
<li>온체인 트랜잭션 히스토리 분석</li>
<li>토큰 스왑 실행</li>
<li>NeoFS에 데이터 저장/조회</li>
</ul>
<p>각 스킬은 <strong>독립적인 모듈</strong>이에요. 개발자는 이 스킬들을 조합해서 에이전트를 만들어요. 레고 블록을 조립하듯이요.</p>
<p>스킬은 <strong>GitHub 기반으로 큐레이션되고 소싱</strong>돼요. 익숙한 개발 워크플로우 안에서 스킬을 탐색하고, 기여하고, 가져다 쓸 수 있어요. 그리고 각 스킬에는 <strong>온체인 사용 기록이 투명하게 공개</strong>되기 때문에, 어떤 스킬이 실제로 많이 쓰이는지 데이터로 판단할 수 있어요.</p>
<p>향후에는 <strong>에이전트 마켓플레이스(Agent Marketplace)</strong>도 출시될 예정이에요. 스킬 뿐 아니라 완성된 에이전트도 거래할 수 있게 되는 거예요. 스킬을 조합해서 에이전트를 만들고, 그 에이전트 자체를 마켓에 올리는 것까지 하나의 생태계 안에서 가능해지는 셈이에요.</p>
<h3 id="왜-이게-중요할까요">왜 이게 중요할까요?</h3>
<p>기존 방식에서는 Web3 에이전트를 만들려면 이 모든 기능을 직접 구현해야 했어요. RPC 노드 연결, ABI 파싱, 트랜잭션 서명 로직, 가스비 추정... 블록체인 개발 경험이 없으면 여기서 막히게 돼요.</p>
<p>Skills Marketplace가 있으면요? <strong>&quot;지갑 연동&quot; 스킬을 가져다 쓰면 끝이에요.</strong> 내부 구현을 몰라도 돼요.</p>
<p>Virtual Bacon(크립토 유튜버, 구독자 약 100만 명)이 에이전트 플랫폼 경험에서 발견한 핵심도 이거예요:</p>
<blockquote>
<p>&quot;에이전트는 스킬 하나를 읽으면 그걸로 할 수 있는 모든 것을 1분 안에 파악한다. 스킬과 설명서만 있으면 즉시 적응한다.&quot;</p>
</blockquote>
<hr>
<h2 id="skills-marketplace의-핵심-기능">Skills Marketplace의 핵심 기능</h2>
<h3 id="1-커뮤니티-기반-평점과-리뷰">1. 커뮤니티 기반 평점과 리뷰</h3>
<p>모든 스킬에는 실제 사용에 기반한 평점과 리뷰가 달려요. 어떤 스킬이 프로덕션 환경에서 안정적으로 동작하는지, 커뮤니티가 검증해요. npm 패키지의 주간 다운로드 수나 GitHub 스타와 비슷한 역할인데요, Web3답게 온체인 사용 기록이 투명하게 공개돼요.</p>
<h3 id="2-투명한-기여-추적attribution">2. 투명한 기여 추적(Attribution)</h3>
<p>누가 어떤 스킬을 만들었는지 명확하게 추적돼요. 오픈소스 기여처럼 투명하게 관리되며, GitHub 기반 스킬 큐레이션도 지원돼요. 개발자의 기여가 인정받는 구조예요.</p>
<h3 id="3-조합-가능한composable-설계">3. 조합 가능한(Composable) 설계</h3>
<p>스킬은 서로 조합할 수 있도록 설계되어 있어요. &quot;가격 조회&quot; 스킬 + &quot;조건부 매매&quot; 스킬 + &quot;알림 전송&quot; 스킬을 조합하면요? 간단한 트레이딩 봇 에이전트가 돼요. 코드를 처음부터 작성할 필요 없이, 기존 스킬의 조합만으로 에이전트를 만들 수 있어요.</p>
<hr>
<h2 id="spoonos-개발-환경--개발자가-알아야-할-것들">SpoonOS 개발 환경 — 개발자가 알아야 할 것들</h2>
<h3 id="지원-언어-및-sdk">지원 언어 및 SDK</h3>
<p>SpoonOS는 다양한 언어의 SDK를 제공해요:</p>
<table>
<thead>
<tr>
<th>언어</th>
<th>용도</th>
</tr>
</thead>
<tbody><tr>
<td><strong>파이썬(Python)</strong></td>
<td>AI/ML 개발자에게 가장 익숙한 선택</td>
</tr>
<tr>
<td><strong>타입스크립트(TypeScript)</strong></td>
<td>프론트엔드 및 풀스택 개발자</td>
</tr>
<tr>
<td><strong>C#</strong></td>
<td>Neo 생태계 기존 개발자</td>
</tr>
<tr>
<td><strong>자바(Java)</strong></td>
<td>엔터프라이즈 환경</td>
</tr>
<tr>
<td><strong>Go</strong></td>
<td>고성능 백엔드</td>
</tr>
</tbody></table>
<p>AI 개발자라면 <strong>파이썬 SDK</strong>로 시작하는 것이 가장 자연스러워요.</p>
<h3 id="핵심-인프라-구성-요소">핵심 인프라 구성 요소</h3>
<p>SpoonOS 위에서 에이전트를 만들 때 알아두면 좋은 주요 구성 요소를 살펴볼까요:</p>
<ul>
<li><strong>SpoonGraph</strong> — 에이전트 워크플로우 실행 엔진이에요. 결정론적 제어, 병렬 실행, 메모리 관리를 처리해요. &quot;이 조건이면 A를 실행하고, 아니면 B를 실행&quot; 같은 흐름을 안정적으로 구현할 수 있어요.</li>
<li><strong>BeVec</strong> — 벡터 데이터베이스 기반 메모리 레이어예요. 에이전트가 과거 상호작용을 기억하고, 의미 기반 검색(semantic search)으로 관련 정보를 꺼내 쓸 수 있어요. RAG(검색 증강 생성)도 기본 지원돼요.</li>
<li><strong>MCP+</strong> — 데이터 인터페이스예요. Web2 DB든 Web3 온체인 데이터든, 하나의 일관된 형식으로 에이전트에게 제공해요.</li>
<li><strong>NeoFS</strong> — 분산 스토리지예요. 에이전트의 상태, 전략, 학습 데이터를 영속적으로 저장해요. 세션이 끝나도 메모리가 사라지지 않아요.</li>
</ul>
<h3 id="neofs--ai-개발자를-위한-분산-스토리지-심화">NeoFS — AI 개발자를 위한 분산 스토리지 심화</h3>
<p>NeoFS는 단순한 분산 파일 저장소가 아니에요. AI 개발자가 기존 코드를 거의 수정하지 않고도 바로 통합할 수 있도록 설계되어 있어요.</p>
<p><strong>프로토콜 지원이 폭넓어요.</strong> gRPC API를 네이티브로 지원하는 것은 물론이고, S3, HTTP, FUSE, sFTP 프로토콜 게이트웨이도 지원해요. 이미 S3 호환 스토리지를 쓰고 있다면요? 엔드포인트만 바꾸면 NeoFS로 전환할 수 있어요. 기존 코드를 재작성하지 않아도 되기 때문에 AI 개발자에게 진입 장벽을 크게 낮춰줘요.</p>
<p><strong>스토리지 정책도 직접 선택할 수 있어요.</strong> 데이터를 저장할 지리적 위치, 신뢰성 수준, 복제할 노드 수, 심지어 디스크 유형(SSD vs HDD)까지 개발자가 직접 지정할 수 있어요. 학습 데이터는 저비용 HDD 노드에, 에이전트 상태는 고속 SSD 노드에 저장하는 식으로 용도에 맞게 최적화할 수 있는 거예요.</p>
<p>그리고 NeoFS의 각 노드는 <strong>스토리지 서비스 보상을 자유시장 원칙으로 직접 설정</strong>해요. 중앙에서 가격을 정하는 게 아니라, 노드 운영자가 시장 수요에 따라 자율적으로 가격을 책정하는 구조예요.</p>
<h3 id="x402-프로토콜--에이전트-간-자동-결제">x402 프로토콜 — 에이전트 간 자동 결제</h3>
<p>SpoonOS 에이전트의 경제 활동을 한 단계 끌어올리는 요소가 있어요. 바로 코인베이스(Coinbase)와 클라우드플레어(Cloudflare)가 함께 만든 <strong>x402 프로토콜</strong>이에요. HTTP 레벨에서 스테이블코인 마이크로페이먼트가 가능한 프로토콜인데요.</p>
<p>SpoonOS 에이전트가 x402와 결합하면요? 에이전트가 다른 서비스를 이용할 때 서비스 대가를 <strong>자동으로 결제</strong>하는 &quot;에이전트 상거래(Agent Commerce)&quot;가 가능해져요. 사람이 개입하지 않아도 에이전트끼리 서비스를 주고받고, 대금을 정산하는 거예요.</p>
<h3 id="dbos-통합--오프체인도-걱정-없어요">DBOS 통합 — 오프체인도 걱정 없어요</h3>
<p>실제 에이전트는 온체인 작업만 하지 않아요. 외부 API 호출, 데이터 전처리, 이메일 발송 같은 오프체인 작업도 많은데요. SpoonOS는 DBOS와 통합하여 <strong>내구성 있는 오프체인 워크플로우</strong>를 지원해요. 실행 중 장애가 나도 자동 복구되며, 온체인 작업과 매끄럽게 연결돼요.</p>
<hr>
<h2 id="실제-프로젝트-사례-해커톤에서-만들어진-것들">실제 프로젝트 사례: 해커톤에서 만들어진 것들</h2>
<p>이론만으로는 감이 안 올 수 있어요. SpoonOS로 실제로 어떤 것들이 만들어지고 있는지, Scoop AI 해커톤 수상작들을 살펴볼까요?</p>
<h3 id="neozero-하노이-bowl-1등--약-360만-원">NeoZero (하노이 Bowl, 1등 — 약 360만 원)</h3>
<p>프롬프트 하나로 <strong>웹사이트 전체를 생성</strong>하는 프로젝트예요. NeoNS 도메인 등록부터 NeoFS 업로드까지 전 과정이 자동화돼요. &quot;이런 웹사이트 만들어줘&quot;라고 말하면, 에이전트가 코드 생성 → 도메인 등록 → 분산 스토리지 배포를 알아서 처리하는 거예요. 베트남의 ViCoders 팀이 개발했어요.</p>
<h3 id="genius-loci-실리콘밸리-bowl">Genius Loci (실리콘밸리 Bowl)</h3>
<p>물리적 장소를 <strong>자율 에이전트로 변환</strong>하는 아이디어예요. SpoonOS 상태 그래프(State Graph)와 GPT 분석, 그리고 x402 결제를 활용해서 장소 기반 서비스를 완전 자동화했어요. 누군가가 바운티(bounty)를 게시하면 → 에이전트가 데이터를 수집하고 → 즉시 결제까지 이루어지는 풀사이클을 구현했어요.</p>
<h3 id="streamsentry-실리콘밸리-bowl">StreamSentry (실리콘밸리 Bowl)</h3>
<p>CCTV를 <strong>멀티모달 AI 보안 플랫폼으로 변환</strong>하는 프로젝트예요. 영상 분석 AI가 이상 징후를 탐지하면, SpoonOS를 통해 블록체인에 감사 추적(audit trail)을 기록해요. 보안 이벤트의 무결성을 온체인으로 보장하는 구조예요.</p>
<h3 id="prism-서울-bowl-대상--약-290만-원">PRISM (서울 Bowl, 대상 — 약 290만 원)</h3>
<p>사용자 질문을 <strong>인터랙티브 애니메이션 교육 영상으로 변환</strong>하는 프로젝트예요. &quot;양자역학이 뭐야?&quot;라고 물으면, 텍스트 답변 대신 시각적 애니메이션으로 설명해 주는 거예요. 서울에서 대상을 수상했어요.</p>
<h3 id="flow-by-cognisor-도쿄-bowl">Flow by Cognisor (도쿄 Bowl)</h3>
<p><strong>음성 우선(Voice-First) AI 생산성 플랫폼</strong>이에요. 일상 업무를 핸즈프리 대화로 처리할 수 있게 해줘요. 키보드를 치지 않고도 에이전트와 음성으로 협업하는 경험을 제공해요.</p>
<hr>
<p>이 프로젝트들이 보여주는 것은 명확해요. SpoonOS가 교육, 보안, 생산성, 부동산, 웹개발 등 <strong>다양한 도메인에서 실제로 작동한다</strong>는 거예요. 이론이 아니라, 실제 해커톤에서 며칠 만에 만들어진 프로젝트들이에요.</p>
<hr>
<h2 id="실전-참여--해커톤과-인센티브-프로그램">실전 참여 — 해커톤과 인센티브 프로그램</h2>
<p>SpoonOS 생태계에 참여할 수 있는 방법은 여러 가지가 있어요:</p>
<h3 id="scoop-ai-해커톤">Scoop AI 해커톤</h3>
<p>Neo와 SpoonOS가 공동 주최하는 글로벌 해커톤이에요. <strong>8개 도시를 순회하며 약 4개월간 운영</strong>돼요.</p>
<ul>
<li><strong>총 상금:</strong> 약 1억 4,500만 원 (10만 달러)</li>
<li><strong>개최 도시:</strong> 실리콘밸리, 도쿄, 서울, 모스크바, 하노이, 방갈로르, 베이징, 런던</li>
<li><strong>트랙:</strong> 에이전트 인프라 &amp; 생산성 AI / 과학·공학 AI / 자율 금융·핀테크·퀀트 AI</li>
</ul>
<p><strong>도시별 상금 규모도 상당해요:</strong></p>
<table>
<thead>
<tr>
<th>도시</th>
<th>상금 규모</th>
</tr>
</thead>
<tbody><tr>
<td><strong>실리콘밸리 Bowl</strong></td>
<td>약 8,700만 원 (6만 달러+)</td>
</tr>
<tr>
<td><strong>서울 Bowl</strong></td>
<td>약 1,160만 원 (8,000달러)</td>
</tr>
<tr>
<td><strong>모스크바 Bowl</strong></td>
<td>약 1,000만 원 (7,000달러)</td>
</tr>
<tr>
<td><strong>하노이 Bowl</strong></td>
<td>약 870만 원 (6,000달러)</td>
</tr>
</tbody></table>
<p><strong>서울에서도 해커톤이 열려요.</strong> 한국 개발자들이 직접 참여할 수 있는 기회예요.</p>
<p>핸드북이 제공되어 규칙, 이벤트 세부사항, 개발자 리소스를 한번에 확인할 수 있어요. Neo N3/Neo X 테스트넷 정보는 물론이고, <strong>C#, Python, Java, Go, TypeScript SDK 링크</strong>까지 포함되어 있어서 바로 개발을 시작할 수 있어요. 워크숍과 패널 토론도 함께 진행되며, 일부는 트위터 스페이스와 유튜브로 중계돼요.</p>
<h3 id="skills-micro-challenge">Skills Micro Challenge</h3>
<p>대규모 해커톤이 부담스럽다면 이것부터 시작해 볼까요? 실용적인 스킬을 하나 만들어서 제출하면 보상을 받는 소규모 챌린지예요.</p>
<ul>
<li><strong>보상 풀:</strong> 약 730만 원 (5,000달러)</li>
<li><strong>추가 혜택:</strong> 우수 기여자는 최대 약 7,300만 원(5만 달러) 규모의 그랜트 패스트트랙에 연결돼요</li>
</ul>
<p>하나의 스킬을 잘 만들면, 그것이 그랜트로 이어지고, 그랜트가 프로젝트로 이어지는 구조예요.</p>
<h3 id="개발자-인센티브-프로그램">개발자 인센티브 프로그램</h3>
<p>총 약 29억 원(200만 달러) 규모의 지원 프로그램이에요:</p>
<ul>
<li><strong>글로벌 비콘 프로그램</strong> — 초기 빌더를 위한 멘토링과 자금 지원</li>
<li><strong>글로벌 개발자 프로그램</strong> — 지속적인 개발 지원</li>
<li><strong>생태계 협력</strong> — 파트너사와의 공동 개발 기회</li>
</ul>
<hr>
<h2 id="왜-지금-web3-ai-에이전트일까요">왜 지금 Web3 AI 에이전트일까요</h2>
<p>AI 에이전트 시장이 폭발하고 있다는 건 누구나 알고 있어요. 하지만 <strong>Web3 AI 에이전트</strong>는 한 단계 더 나아가는데요:</p>
<ul>
<li><strong>자율성</strong> — 중앙 서버 없이 블록체인 위에서 독립적으로 실행돼요</li>
<li><strong>투명성</strong> — 모든 행동이 온체인에 기록되어 감사가 가능해요</li>
<li><strong>경제 활동</strong> — 에이전트가 직접 수익을 창출하고 다른 에이전트에게 결제해요</li>
<li><strong>영속적 메모리</strong> — 세션이 끝나도 학습과 상태가 유지돼요</li>
<li><strong>신원 보장</strong> — EIP-8004로 신뢰할 수 있는 에이전트 식별이 가능해요</li>
</ul>
<p>기존 AI 프레임워크에서는 이 중 어느 것도 기본 제공하지 않아요. SpoonOS는 이 모든 것을 하나의 플랫폼에서 제공해요.</p>
<hr>
<h2 id="결론-첫-스킬-하나면-충분해요">결론: 첫 스킬 하나면 충분해요</h2>
<p>Web3 AI 에이전트 개발이 어렵게 느껴질 수 있어요. 하지만 SpoonOS의 Skills Marketplace가 있다면, 시작은 간단해요.</p>
<ol>
<li>기존 스킬을 조합해서 에이전트 프로토타입을 만들어 봐요</li>
<li>필요한 스킬이 없으면 직접 만들어서 마켓에 올려요</li>
<li>Skills Micro Challenge에 제출해서 보상을 받아요</li>
<li>Scoop AI 해커톤에 참가해서 더 큰 프로젝트로 발전시켜요</li>
</ol>
<p>블록체인을 몰라도 돼요. 파이썬을 쓸 줄 알고, AI 에이전트에 관심이 있다면, SpoonOS가 나머지를 채워줄 거예요.</p>
<p>에이전트 시대의 Web3 개발, 스킬 하나부터 시작해 볼까요?</p>
<hr>
<p><strong>관련 링크:</strong></p>
<ul>
<li><a href="https://spoonai.io/">SpoonOS 공식 사이트</a></li>
<li><a href="https://xspoonai.github.io/docs/core-concepts/agents/">SpoonOS 개발자 문서</a></li>
<li><a href="https://github.com/XSpoonAi/spoon-core">SpoonOS GitHub</a></li>
<li><a href="https://scoop.spoonai.io/">Scoop AI 해커톤 참가 신청</a></li>
<li><a href="https://neonewstoday.com/ai/spoonos-launches-web3%E2%80%91native-skills-marketplace-to-accelerate-composable-ai/">Skills Marketplace 소개</a></li>
<li><a href="https://neonewstoday.com/ai/spoonos-opens-skills-micro-challenge-with-us-5000-rewards-pool-and-fast-track-to-50000-grants/">Skills Micro Challenge</a></li>
<li><a href="https://www.dbos.dev/blog/spoonos-durable-offchain-workflows">DBOS x SpoonOS 통합</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[1억 원으로 시작해 6,700억 원을 만든 블록체인 — Neo가 AI로 리부트하는 이유]]></title>
            <link>https://velog.io/@neo_blockchain/1%EC%96%B5-%EC%9B%90%EC%9C%BC%EB%A1%9C-%EC%8B%9C%EC%9E%91%ED%95%B4-6700%EC%96%B5-%EC%9B%90%EC%9D%84-%EB%A7%8C%EB%93%A0-%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8-Neo%EA%B0%80-AI%EB%A1%9C-%EB%A6%AC%EB%B6%80%ED%8A%B8%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@neo_blockchain/1%EC%96%B5-%EC%9B%90%EC%9C%BC%EB%A1%9C-%EC%8B%9C%EC%9E%91%ED%95%B4-6700%EC%96%B5-%EC%9B%90%EC%9D%84-%EB%A7%8C%EB%93%A0-%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8-Neo%EA%B0%80-AI%EB%A1%9C-%EB%A6%AC%EB%B6%80%ED%8A%B8%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Fri, 17 Apr 2026 03:46:23 GMT</pubDate>
            <description><![CDATA[<h2 id="12년-전-중국의-한-개발자가-1억-원도-안-되는-돈으로-블록체인을-만들었어요">12년 전, 중국의 한 개발자가 1억 원도 안 되는 돈으로 블록체인을 만들었어요</h2>
<p>2014년, 다홍페이(Da Hongfei)라는 개발자가 중국에서 퍼블릭 블록체인 프로젝트를 시작했어요. 시드 자금은 60만 위안, 한화로 약 1억 원이 안 되는 금액이었는데요. 이더리움에서 영감을 받아 만든 이 프로젝트의 이름은 앤트셰어즈(Antshares)였어요. 나중에 Neo로 이름을 바꿨고요.</p>
<p>사실 Neo(앤트셰어즈)는 <strong>중국 최초의 오리지널 오픈소스 퍼블릭 체인 프로젝트</strong>였어요. 단순히 이더리움을 포크한 게 아니라, 독자적인 합의 알고리즘(dBFT)과 아키텍처를 처음부터 설계한 프로젝트였는데요. &quot;Neo&quot;라는 이름은 그리스어에서 유래한 것으로, <strong>혁신(innovation)·젊음(youth)·현대화(modernization)</strong>를 상징해요. 리브랜딩 당시 새 시대를 열겠다는 의지를 담은 거죠.</p>
<p>2016년 ICO에서 <strong>6,119 BTC</strong>를 모아 총 <strong>1억 개의 NEO 토큰</strong>을 생성했어요. 이 중 50%는 ICO에서 판매하고, 나머지 50%는 Neo Council에 배분했는데요. 최초 ANS(앤트셰어즈) 코인 가격은 개당 약 <strong>46원($0.032)</strong>에 불과했어요.</p>
<p>그런데 2017년 6월 &quot;Neo&quot;로 리브랜딩한 후, 불과 <strong>7개월 만에 약 23만 원($160)</strong>까지 치솟았어요. <strong>약 5,000배 상승</strong>이라는 경이로운 수치였죠. 당시 시세로 약 75억 원 정도였던 초기 모금액이, 지금 기준으로 보면 어마어마하지만, 그때는 그저 소규모 블록체인 프로젝트의 운영 자금이었어요.</p>
<p>12년이 지난 2025년, Neo는 <strong>약 6,700억 원(4억 6,100만 달러) 규모의 트레저리</strong>를 보유하고 있어요. 초기 자금 대비 <strong>8,862% 성장</strong>인데요.</p>
<p>이게 어떻게 가능했을까요?</p>
<hr>
<h2 id="트레저리-성장의-비밀--보수적-투자와-인내">트레저리 성장의 비밀 — 보수적 투자와 인내</h2>
<p>Neo의 자산 성장은 화려한 트레이딩 수익이 아니라, <strong>극도로 보수적인 장기 투자</strong>의 결과예요.</p>
<p>다홍페이 본인이 그 성격을 잘 보여주는데요:</p>
<blockquote>
<p>&quot;나는 2011년에 비트코인을 알게 됐다. 하지만 바로 사지 않았다. 지켜보고, 조사하고, 기다렸다. 2011년 정점 가격이 30달러였는데, 10달러까지 떨어졌을 때 비로소 샀다.&quot;</p>
</blockquote>
<p>Neo 재단도 같은 철학으로 운영되었어요:</p>
<ul>
<li><strong>2018년</strong> — 이더리움(ETH) 대량 매입</li>
<li><strong>빠른 차익 실현을 하지 않고</strong> 7년간 보유</li>
<li><strong>2024년</strong> — ETH 30,000개 이상을 매각해 약 1,450억 원의 현금 확보</li>
</ul>
<p>NGC 펀드 투자와 초기 프로젝트 투자에서도 수익을 올렸지만, 핵심은 <strong>팔지 않고 버틴 것</strong>이에요. 다홍페이는 이것을 &quot;Neo 스타일의 보수적 투자&quot;라고 부르고 있어요.</p>
<hr>
<h2 id="트레저리-구성--숫자로-보는-neo의-현재">트레저리 구성 — 숫자로 보는 Neo의 현재</h2>
<p>2025년 말 기준 Neo의 트레저리는 이렇게 구성되어 있어요:</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>규모</th>
</tr>
</thead>
<tbody><tr>
<td><strong>총 트레저리</strong></td>
<td>약 6,700억 원 (4억 6,100만 달러)</td>
</tr>
<tr>
<td><strong>NEO 토큰</strong></td>
<td>4,117만 NEO</td>
</tr>
<tr>
<td><strong>GAS 토큰</strong></td>
<td>4,008만 GAS</td>
</tr>
<tr>
<td><strong>비트코인</strong></td>
<td>1,112 BTC</td>
</tr>
<tr>
<td><strong>현금 및 스테이블코인</strong></td>
<td>약 1,450억 원 이상</td>
</tr>
</tbody></table>
<p>이 중 Neo Foundation이 49%, 실행 조직인 NGD(Neo Global Development)가 51%를 관리하고 있어요. 고유동성 자산만 약 2,900억 원 이상이에요.</p>
<p>여기서 주목할 만한 사실 하나 알려드릴게요: Neo는 2017년에 <strong>블록체인 업계 최초의 &quot;기브백(Give Back)&quot; 프로그램</strong>을 실시했어요. 시드 라운드와 ICO 투자자들에게 투자금을 전액 환불하면서, NEO 토큰 반환은 요구하지 않았거든요. 즉 2017년 이후 Neo 재단은 <strong>투자자의 돈이 아닌 자체 수익으로만 운영</strong>되어 왔어요.</p>
<p>초기 자금 대비 <strong>8,862% 성장</strong>이라는 수치를 다시 한번 생각해 볼게요. 46원짜리 코인을 만들어 모은 자금이 12년 만에 6,700억 원이 된 거예요. 투자자 자금을 환불하고도, 자체 수익만으로 이만큼 성장시킨 블록체인 프로젝트는 업계에서 거의 찾아보기 어려워요.</p>
<p>그리고 투명성 측면에서도 큰 변화가 진행 중이에요. 2025년 재무 보고서 발표를 시작으로, <strong>2026년부터 공식 연간 보고 체계로 전환</strong>할 계획이에요. 더 나아가 <strong>글로벌 감사 법인과 실사(due diligence) 보고서 계약을 추진</strong>하고 있는데요, 모든 자산에 대한 <strong>검증 가능한 신뢰(verifiable trust)</strong>를 확보하겠다는 목표예요. 블록체인 프로젝트가 전통 금융 수준의 감사 체계를 도입하는 건 정말 이례적인 일이에요.</p>
<hr>
<h2 id="투자-실패에서-배운-것--약-770억-원의-손실">투자 실패에서 배운 것 — 약 770억 원의 손실</h2>
<p>투명하게 공개된 부분도 있어요. Neo는 60개 이상의 초기 프로젝트에 약 770억 원(5,330만 달러)을 투자했고, 상당 부분이 손실로 기록되었어요.</p>
<p>하지만 맥락이 중요한데요:</p>
<ul>
<li><strong>손실의 85%는 2019년 이전에 발생</strong> — 크립토 초창기 특유의 성장통</li>
<li>2019년 이후에는 투자 기조를 대폭 보수화했어요</li>
<li>전체 트레저리 대비 손실 비율은 관리 가능한 수준이에요</li>
</ul>
<p>다홍페이는 이를 &quot;크립토 초기 시절의 수업료&quot;라고 표현했어요. 중요한 건, 이 경험이 이후의 보수적 운영 기조로 이어졌다는 점이에요.</p>
<hr>
<h2 id="거버넌스-개혁--12년-묵은-숙제를-풀다">거버넌스 개혁 — 12년 묵은 숙제를 풀다</h2>
<p>자금이 있어도 구조가 낡으면 의미가 없잖아요. Neo도 이 문제를 안고 있었는데요:</p>
<ul>
<li>12년간 2명의 공동 창립자 중심 운영</li>
<li><strong>싱글 시그니처(단일 서명) 지갑</strong>으로 수천억 원대 트레저리 관리 — 보안과 탈중앙화 모두에 문제</li>
<li>싱가포르 재단 등록이 더 이상 최적이 아닌 상황</li>
<li>NEO 토큰의 50% 이상을 재단이 보유 — 12년 된 프로젝트에 부적절한 집중도</li>
</ul>
<p>이 문제의 심각성을 업계 맥락에서 보면 더 잘 이해할 수 있어요. <strong>대부분의 2014~2017년 설립 프로젝트들은 이미 사라졌거나 트레저리가 고갈된 상태</strong>예요. 그 시기에 태어난 수백 개의 블록체인 중 지금까지 의미 있는 규모로 생존한 프로젝트는 소수에 불과하죠. Neo처럼 <strong>12년 운영 + 수천억 원대 트레저리 유지 + 거버넌스 개혁을 동시에 추진</strong>하는 프로젝트는 업계에서 매우 드물어요.</p>
<p>다홍페이는 이 모든 문제를 인정하고, 2026년 최대 KPI로 <strong>거버넌스 개혁</strong>을 선언했어요:</p>
<h3 id="개혁-로드맵">개혁 로드맵</h3>
<ol>
<li><strong>멀티시그 전환</strong> — 2026년 말까지 다중 서명 지갑으로 트레저리 관리 체계 이전</li>
<li><strong>재단 이전</strong> — 크립토 친화적 관할권으로 Neo Foundation 이전</li>
<li><strong>커뮤니티 거버넌스</strong> — 능력과 실적 기반의 커뮤니티 주도 의사결정 모델로 전환</li>
<li><strong>토큰 환원</strong> — NEO 토큰 50%를 커뮤니티에 돌려주는 프로그램 검토</li>
<li><strong>투명성 확보</strong> — 연간 재무 보고서 정례화, 독립 감사 법인 계약 (현재 2곳과 최종 협상 중), 트레저리 지갑 주소 공개</li>
</ol>
<p>재단 이전과 관련해서 주목할 만한 움직임이 있어요. NGD(Neo Global Development)를 <strong>홍콩으로 이전</strong>했고, 팀원 대부분이 <strong>홍콩 ID를 취득</strong>했어요. 이를 통해 <strong>홍콩 정부와 직접 소통할 수 있는 채널</strong>을 확보한 건데요. 홍콩은 최근 크립토 산업에 우호적인 규제 프레임워크를 적극적으로 구축하고 있거든요. 2019년 시위 이후 빠져나갔던 자본과 인력이 최근 복귀하는 추세이기도 해서, Neo에게는 타이밍이 좋은 선택이에요.</p>
<p>유튜브 구독자 100만 명에 가까운 크립토 트레이더 Virtual Bacon은 이를 이렇게 평가했어요:</p>
<blockquote>
<p>&quot;많은 프로젝트가 돈을 모은 후 2~3년 만에 사라진다. 다홍페이가 직접 타임라인을 걸고 탈중앙화를 약속한 건 커뮤니티가 보관할 수 있는 영수증이다. 트레이더 관점에서 토큰에 대한 불확실성을 상당 부분 해소한다.&quot;</p>
</blockquote>
<hr>
<h2 id="그래서-왜-ai인가--두-번째-성장-곡선">그래서 왜 AI인가 — 두 번째 성장 곡선</h2>
<p>트레저리가 탄탄하고, 거버넌스를 개혁 중이라는 건 좋아요. 하지만 핵심 질문은 남아 있는데요: <strong>앞으로 뭘 할 건데?</strong></p>
<p>다홍페이의 대답은 명확해요:</p>
<blockquote>
<p>&quot;Neo는 두 번째 성장 곡선을 찾아야 한다. ICO 시대의 엔진은 낡았다. 기존 체인과 속도나 TVL로 경쟁하는 건 끝없는 소모전이다. 새로운 카테고리에서 선점자가 되어야 한다. 그 카테고리가 AI, 구체적으로는 AI 에이전트 네이티브 블록체인이다.&quot;</p>
</blockquote>
<p>이 비전을 실현하는 두 축이 <strong>Neo X</strong>(에이전트 네이티브 블록체인)와 <strong>SpoonOS</strong>(에이전트 운영체제)예요:</p>
<ul>
<li><strong>Neo X</strong> — 인간이 아닌 AI 에이전트를 1차 사용자로 설계한 블록체인이에요. 에이전트 신원(EIP-8004), 영속적 메모리(NeoFS), 머신 간 통신 프로토콜이 기본 탑재되어 있어요.</li>
<li><strong>SpoonOS</strong> — Neo X 위에서 에이전트를 만들고 운영하는 OS예요. 4계층 아키텍처, Skills Marketplace, SpoonGraph 실행 엔진을 제공하고요. 약 29억 원 규모 개발자 인센티브와 글로벌 해커톤이 진행 중이에요.</li>
</ul>
<h3 id="경쟁-환경에서-neo-x가-다른-점">경쟁 환경에서 Neo X가 다른 점</h3>
<p>현재 크립토 AI 에이전트 섹터를 살펴보면, <strong>Virtuals Protocol(시총 점유율 약 23%)</strong>, <strong>ai16z(약 12%)</strong> 등이 시장을 주도하고 있어요. 하지만 이들의 접근 방식에는 구조적 한계가 있는데요. 기존 체인(Base, Solana) 위에서 에이전트 토큰이나 프레임워크를 제공하는 방식이에요. 즉 블록체인 자체는 에이전트를 위해 설계된 게 아니라는 거죠.</p>
<p>Neo X의 차별화는 바로 여기에 있어요. <strong>블록체인 인프라 자체를 에이전트 네이티브로 설계</strong>했다는 점인데요. 합의 알고리즘(dBFT), 분산 스토리지(NeoFS), 에이전트 아이덴티티(EIP-8004), 운영체제(SpoonOS)까지 <strong>통합 스택</strong>으로 제공해요. 에이전트가 태어나고, 기억하고, 소통하고, 경제 활동을 하는 데 필요한 모든 인프라가 하나의 생태계 안에 있는 거예요.</p>
<p>시장 규모도 주목할 만해요. AI 에이전트 시장은 <strong>2025년 약 11조 원에서 2030년 약 76조 원(526억 달러)</strong>으로 성장할 것으로 전망되고 있어요. 12년간 쌓은 자원과 경험을, 지금 가장 빠르게 성장하는 영역에 집중 투입하는 전략이에요.</p>
<hr>
<h2 id="커뮤니티에-전하는-메시지">커뮤니티에 전하는 메시지</h2>
<p>다홍페이는 X 라이브에서 오랜 NEO 홀더들에게 직접 말했어요:</p>
<blockquote>
<p>&quot;커뮤니티의 불만을 충분히 이해한다. 거버넌스 탈중앙화 지연, DeFi Summer 기회 놓침, 소통 부족 — 전적으로 내 책임이다.</p>
<p>하지만 2026년 현재, 우리는 여전히 건재하다. 6,700억 원의 트레저리, 전 세계 커뮤니티, 새로운 방향에 투입된 인재들. 모든 재료가 갖춰져 있다.</p>
<p>역사를 리셋하고 다시 시작한다.&quot;</p>
</blockquote>
<p>이 메시지의 무게를 이해하려면, Neo 커뮤니티가 걸어온 길을 알아야 해요.</p>
<p>2017년 리브랜딩 후 <strong>7개월 만에 5,000배 상승</strong>을 경험했던 커뮤니티예요. 46원짜리 코인이 23만 원이 되는 걸 지켜본 사람들이죠. 하지만 그 이후 중국의 ICO 전면 금지, 크립토 윈터, 장기 하락장을 거치며 큰 고통을 겪었어요. 희망과 좌절을 반복한 12년이었죠.</p>
<p>그런데 돌아보면, 이 기간 동안 <strong>재단이 투자자 자금이 아닌 자체 수익으로 운영해왔다</strong>는 사실이 있어요. 2017년 기브백 프로그램으로 투자금을 돌려준 이후, 순수하게 자체 역량으로 6,700억 원의 트레저리를 만들어낸 거예요.</p>
<p>그리고 2026년, <strong>재무 보고서 공개와 글로벌 감사 법인 실사 추진</strong>이 시작되었어요. 블록체인 프로젝트가 전통 금융 수준의 투명성을 자발적으로 추구하는 건 <strong>업계에서 거의 전례 없는 일</strong>이에요. 이건 단순한 숫자 공개가 아니라, 커뮤니티에 대한 신뢰의 증거예요. &quot;우리가 어떻게 돈을 쓰고 있는지, 외부 전문가에게 검증받겠다&quot;는 선언이니까요.</p>
<hr>
<h2 id="결론-생존을-넘어-재도약으로">결론: 생존을 넘어 재도약으로</h2>
<p>크립토 업계에서 12년을 살아남은 프로젝트는 손에 꼽을 정도예요. 그 중 수천억 원대 트레저리를 유지하면서 새로운 성장 전략을 제시하는 프로젝트는 더 드물고요.</p>
<p>Neo는 과거의 성공에 안주하지 않고, 가장 공격적인 방향으로 전환하고 있어요. 6,700억 원의 트레저리, 거버넌스 개혁, AI 에이전트 네이티브 블록체인이라는 새로운 카테고리가 그 방향이에요.</p>
<p>&quot;공룡 블록체인&quot;이라고 불렸던 Neo가, 에이전트 시대의 선두 주자가 될 수 있을까요. 적어도 자원과 의지는 충분해 보여요.</p>
<hr>
<p><strong>관련 링크:</strong></p>
<ul>
<li><a href="https://www.newsfilecorp.com/release/286403/Neo-Releases-Its-2025-Financial-Report-with-461M-Treasury-and-Strategic-Pivot-to-Enhanced-Decentralization">Neo 2025 재무 보고서</a></li>
<li><a href="https://coinpedia.org/information/neos-2025-financial-report-offers-window-into-its-461m-treasury-plans-for-future-cycles/">Neo 재무 보고서 상세 분석 (Coinpedia)</a></li>
<li><a href="https://neonewstoday.com/ai/da-hongfei-positions-neo-x-as-ai-agent-native-blockchain-in-new-strategy-outline/">Neo AI 전략: Humanless Blockchain</a></li>
<li><a href="https://spoonai.io/">SpoonOS 공식 사이트</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[크립토의 진짜 PMF는 에이전트 경제다 — GAS 토큰이 머신 경제의 연료가 되는 법]]></title>
            <link>https://velog.io/@neo_blockchain/%ED%81%AC%EB%A6%BD%ED%86%A0%EC%9D%98-%EC%A7%84%EC%A7%9C-PMF%EB%8A%94-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EA%B2%BD%EC%A0%9C%EB%8B%A4-GAS-%ED%86%A0%ED%81%B0%EC%9D%B4-%EB%A8%B8%EC%8B%A0-%EA%B2%BD%EC%A0%9C%EC%9D%98-%EC%97%B0%EB%A3%8C%EA%B0%80-%EB%90%98%EB%8A%94-%EB%B2%95</link>
            <guid>https://velog.io/@neo_blockchain/%ED%81%AC%EB%A6%BD%ED%86%A0%EC%9D%98-%EC%A7%84%EC%A7%9C-PMF%EB%8A%94-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EA%B2%BD%EC%A0%9C%EB%8B%A4-GAS-%ED%86%A0%ED%81%B0%EC%9D%B4-%EB%A8%B8%EC%8B%A0-%EA%B2%BD%EC%A0%9C%EC%9D%98-%EC%97%B0%EB%A3%8C%EA%B0%80-%EB%90%98%EB%8A%94-%EB%B2%95</guid>
            <pubDate>Fri, 10 Apr 2026 03:33:19 GMT</pubDate>
            <description><![CDATA[<h2 id="크립토의-킬러-유스케이스-10년째-찾고-있어요">크립토의 킬러 유스케이스, 10년째 찾고 있어요</h2>
<p>블록체인이 세상에 등장한 지 15년이 넘었어요. 그동안 우리는 수없이 물었죠. &quot;크립토를 도대체 뭐에 쓰는 건데?&quot; 송금? 카드가 더 빠르잖아요. 결제? 편의점에서 비트코인을 받아주지 않아요. 디파이? 써보면 좋긴 한데, 일반인에게는 여전히 진입 장벽이 높아요.</p>
<p>크립토의 PMF(Product-Market Fit)를 찾기 위한 여정은 길었어요. 그런데 만약 우리가 방향을 잘못 잡고 있었던 거라면요? <strong>크립토의 핵심 사용자가 인간이 아니라 기계</strong>라면 어떨까요?</p>
<p>드래곤플라이 창립자 하시브 쿠레시(Haseeb Qureshi)는 이렇게 말했어요:</p>
<blockquote>
<p>&quot;크립토는 인간을 위해 만들어진 게 아닙니다. 에이전트를 위해, 기계를 위해 만들어진 거예요.&quot;</p>
</blockquote>
<p>이건 단순한 포지션 토킹이 아니에요. 하시브가 이끄는 드래곤플라이의 최신 펀드 규모는 약 <strong>9,400억 원(6.5억 달러)</strong>에 달하고, 스테이블코인, DeFi, 예측 시장, 그리고 <strong>AI 에이전트 결제 인프라</strong>에 집중 투자하고 있어요. 진짜 돈이 이 방향으로 움직이고 있는 거예요.</p>
<p>하시브는 더 나아가 이렇게도 말했어요:</p>
<blockquote>
<p>&quot;포스트AI 시대에는 시드 라운드에 수백만 달러를 모을 필요 없이, AI 클라우드 컴퓨팅 1,500만 원(1만 달러)으로 애플리케이션을 런칭할 수 있을 것&quot;</p>
</blockquote>
<p>런칭 비용이 극단적으로 낮아지면, 수많은 AI 에이전트가 독립적인 경제 주체로 등장하게 돼요. 그리고 이 에이전트들이 서로 거래하려면 <strong>프로그래머블한 결제 인프라</strong>가 필수적이에요.</p>
<p>시장 조사 기관 가트너(Gartner)는 이 흐름의 규모를 이렇게 전망하고 있어요. <strong>2030년까지 AI &quot;머신 고객(Machine Customers)&quot;이 연간 약 4경 3,500조 원(30조 달러) 규모의 구매에 영향을 미칠 것</strong>이라고요. 현재 전 세계 GDP의 약 1/3에 해당하는 금액이에요. 이 거대한 머신 경제를 처리할 결제 레일이 필요한데, 기존 금융 시스템은 준비가 안 되어 있어요.</p>
<p>Neo 공동 창립자 다홍페이도 이에 동의하는데요:</p>
<blockquote>
<p>&quot;10년 넘게 인간용 UI/UX를 개선해왔지만, 큰 금액을 전송할 때마다 여전히 긴장돼요. 주소를 여러 번 확인하고, 손이 떨려요. AI 에이전트의 등장이 이 끔찍한 사용자 경험에서 우리를 구해줄 거예요.&quot;</p>
</blockquote>
<hr>
<h2 id="에이전트가-가장-많이-하는-일--거래">에이전트가 가장 많이 하는 일 — 거래</h2>
<p>AI 에이전트의 활동을 들여다보면, 가장 빈번한 행위는 <strong>거래(transaction)</strong>예요.</p>
<ul>
<li>트레이딩 에이전트는 24시간 매수/매도 주문을 내요.</li>
<li>차익거래 봇은 거래소 간 가격 차이를 밀리초 단위로 포착해요.</li>
<li>DeFi 전략 에이전트는 유동성을 이동하고, 포지션을 조정하고, 수확(harvest)해요.</li>
<li>데이터 에이전트는 분석 결과를 다른 에이전트에게 팔고, 그 대가를 받아요.</li>
</ul>
<p>이 모든 활동에는 <strong>결제 수단</strong>이 필요해요. 그런데 에이전트는 은행 계좌가 없어요. 신용카드도 없고요. 에이전트가 다른 에이전트에게 대가를 지불하려면, <strong>인간의 중개 없이 작동하는 프로그래머블 화폐</strong>가 필요해요.</p>
<p>그게 바로 크립토예요. 그리고 Neo 생태계에서는 <strong>GAS 토큰</strong>이 그 역할을 하고 있어요.</p>
<hr>
<h2 id="neo의-듀얼-토큰--세계-최초-그리고-지금-다시-빛나는-설계">Neo의 듀얼 토큰 — 세계 최초, 그리고 지금 다시 빛나는 설계</h2>
<p>Neo는 세계 최초로 <strong>듀얼 토큰 모델</strong>을 도입한 퍼블릭 블록체인이에요. 거버넌스 권한과 네트워크 사용 권한을 분리한 건데요, 이게 12년 전의 설계가 지금 에이전트 경제에 딱 맞아떨어지고 있어요.</p>
<h3 id="neo--거버넌스-토큰">NEO — 거버넌스 토큰</h3>
<ul>
<li>네트워크의 방향을 결정하는 <strong>투표권</strong>이에요</li>
<li>보유하고 있으면 GAS가 자동으로 보상돼요</li>
<li>쪼갤 수 없는 정수 단위예요 (1 NEO, 2 NEO — 0.5 NEO는 없어요)</li>
</ul>
<h3 id="gas--유틸리티-토큰">GAS — 유틸리티 토큰</h3>
<ul>
<li>트랜잭션 수수료, 스마트 컨트랙트 배포, 디앱 내 결제에 사용돼요</li>
<li>Neo X에서 모든 프로토콜 비용의 기본 단위예요</li>
<li>소수점 단위까지 분할 가능해요</li>
</ul>
<p>핵심적인 차이는 이거예요. <strong>이더리움에서는 거래할 때마다 ETH가 줄어들어요.</strong> 가스비로 소모되기 때문이에요. 하지만 Neo에서는 NEO를 보유하기만 하면 GAS가 쌓여요. 거래에는 GAS만 쓰여요. NEO는 그대로 남아 있고요.</p>
<p>이건 에이전트 경제에서 매우 중요한 의미를 갖고 있어요.</p>
<hr>
<h2 id="neo-x의-기술적-강점-에이전트가-원하는-것">Neo X의 기술적 강점: 에이전트가 원하는 것</h2>
<p>듀얼 토큰 설계가 경제적 인센티브를 정렬해준다면, Neo X의 기술 아키텍처는 에이전트가 실제로 <strong>안정적으로 동작할 수 있는 환경</strong>을 제공해요. 에이전트에게 가장 중요한 세 가지 — <strong>확정성(finality), 비용(cost), 결정론성(determinism)</strong> — 를 하나씩 살펴볼게요.</p>
<h3 id="원-블록-파이널리티--기다릴-필요-없어요">원 블록 파이널리티 — &quot;기다릴 필요 없어요&quot;</h3>
<p>Neo X는 dBFT(delegated Byzantine Fault Tolerance) 합의를 사용해요. 이 합의 메커니즘의 가장 큰 특징은 <strong>블록이 전파되는 즉시 트랜잭션이 확정</strong>된다는 거예요. 한 블록이면 끝이에요. 되돌릴 수 없어요.</p>
<p>이게 에이전트에게 왜 중요할까요? 에이전트는 트랜잭션 결과를 기반으로 다음 행동을 결정해요. &quot;이 거래가 정말 확정된 건지&quot; 불확실하면 다음 단계로 넘어갈 수 없어요. 이더리움은 트랜잭션이 확정되기까지 <strong>15분 이상</strong> 기다려야 하고, 솔라나도 <strong>수 초</strong>가 필요해요. 그 사이에 재조직(reorg)이 일어나면? 에이전트의 전략 전체가 꼬이게 돼요.</p>
<p>Neo X의 dBFT는 에이전트에게 이렇게 보장해요: <strong>&quot;트랜잭션이 블록에 포함되면, 그건 확정된 거예요. 기다릴 필요 없어요.&quot;</strong> 이 확실성은 멀티 에이전트 파이프라인에서 특히 결정적이에요.</p>
<h3 id="anti-mev-보호--프론트러닝-걱정-없어요">Anti-MEV 보호 — &quot;프론트러닝 걱정 없어요&quot;</h3>
<p>트레이딩 에이전트를 운영해본 사람이라면 MEV(Maximal Extractable Value) 봇의 무서움을 알 거예요. 여러분의 에이전트가 DEX에서 큰 스왑을 실행하려는 순간, MEV 봇이 그 트랜잭션을 감지하고 <strong>프론트러닝</strong>(먼저 사서 가격을 올린 뒤 비싸게 파는 것)이나 <strong>샌드위치 공격</strong>(앞뒤로 거래를 끼워넣어 차익을 뺏는 것)을 해요.</p>
<p>Neo X는 <strong>엔벨로프드 트랜잭션(Enveloped Transaction)</strong>이라는 메커니즘으로 이 문제를 해결해요. 원본 거래를 보호 레이어로 감싸서, 블록에 포함되기 전까지 트랜잭션 내용이 노출되지 않아요. 트레이딩 에이전트가 MEV 봇의 먹잇감이 되지 않는 거예요.</p>
<h3 id="결정론성--같은-입력-항상-같은-결과">결정론성 — &quot;같은 입력, 항상 같은 결과&quot;</h3>
<p>dBFT 합의는 블록 확정에 확률적 요소가 없어요. 포크가 존재하지 않고, 같은 상태에서 같은 트랜잭션을 실행하면 항상 같은 결과가 나와요. 에이전트는 본질적으로 프로그래밍된 로직을 따르는 존재이기 때문에, 이런 <strong>결정론적 환경</strong>이야말로 가장 이상적이에요.</p>
<p>정리하면, Neo X의 dBFT는 에이전트에게 필요한 세 가지를 모두 충족해요:</p>
<table>
<thead>
<tr>
<th>에이전트 요구사항</th>
<th>Neo X (dBFT)</th>
<th>이더리움</th>
<th>솔라나</th>
</tr>
</thead>
<tbody><tr>
<td><strong>확정성(Finality)</strong></td>
<td>원 블록 즉시 확정</td>
<td>15분+ 대기</td>
<td>수 초 대기</td>
</tr>
<tr>
<td><strong>비용(Cost)</strong></td>
<td>수십 원 수준</td>
<td>변동 크고 높음</td>
<td>낮지만 혼잡 시 급등</td>
</tr>
<tr>
<td><strong>결정론성(Determinism)</strong></td>
<td>포크 없음, 완전 결정론적</td>
<td>재조직 가능성 존재</td>
<td>간헐적 중단 발생</td>
</tr>
</tbody></table>
<p>에이전트는 &quot;가장 핫한 체인&quot;을 고르지 않아요. <strong>가장 예측 가능한 체인</strong>을 고르게 돼요. 그리고 그 기준에서 Neo X는 매우 강력한 후보예요.</p>
<hr>
<h2 id="에이전트-경제에서-gas가-작동하는-방식">에이전트 경제에서 GAS가 작동하는 방식</h2>
<p>Neo X 위에서 에이전트들이 활동하는 시나리오를 그려볼까요.</p>
<h3 id="시나리오-멀티-에이전트-트레이딩-시스템">시나리오: 멀티 에이전트 트레이딩 시스템</h3>
<ol>
<li><strong>분석 에이전트 A</strong>가 온체인 데이터를 수집하고 시장 신호를 분석해요. 이 과정에서 NeoFS에 데이터를 저장하는 비용으로 GAS를 지불해요.</li>
<li><strong>전략 에이전트 B</strong>가 A의 분석 결과를 구매해요. A에게 GAS로 대가를 지불하고요.</li>
<li><strong>실행 에이전트 C</strong>가 B의 전략을 받아 실제 거래를 수행해요. 트랜잭션 수수료로 GAS를 지불해요.</li>
<li>수익이 발생하면 C가 B와 A에게 GAS로 수익을 배분해요.</li>
</ol>
<p>이 전체 흐름에서 <strong>인간은 한 번도 개입하지 않아요.</strong> 에이전트끼리 GAS로 서비스를 사고팔고, 결제하고, 정산하는 거예요.</p>
<p>이게 추상적으로 들린다면, 이미 실제로 구현된 사례가 있어요. Scoop AI 해커톤에서 등장한 <strong>Genius Loci</strong> 프로젝트인데요 — 물리적 장소(카페, 갤러리, 공원 등)가 자율 에이전트로 동작하는 거예요. 장소 에이전트가 자신에 대한 데이터 바운티를 직접 게시하고, 데이터를 제공한 기여자에게 <strong>x402 프로토콜을 통해 GAS로 자동 결제</strong>해요. 위의 시나리오가 실험실 밖에서 실제로 돌아가고 있는 거예요.</p>
<h3 id="gas가-포착하는-가치">GAS가 포착하는 가치</h3>
<p>Neo X에서 에이전트가 하는 모든 행위에는 GAS가 필요해요:</p>
<table>
<thead>
<tr>
<th>행위</th>
<th>GAS 사용</th>
</tr>
</thead>
<tbody><tr>
<td>트랜잭션 전송</td>
<td>수수료</td>
</tr>
<tr>
<td>스마트 컨트랙트 배포</td>
<td>배포 비용</td>
</tr>
<tr>
<td>NeoFS 데이터 저장</td>
<td>스토리지 비용</td>
</tr>
<tr>
<td>SpoonOS SDK 호출</td>
<td>SDK 수수료</td>
</tr>
<tr>
<td>에이전트 간 결제</td>
<td>직접 전송</td>
</tr>
</tbody></table>
<p>에이전트 활동이 늘어날수록 GAS 소비가 늘어나고, 그 가치가 NEO 보유자에게 흘러가요. NEO를 가지고 있으면 GAS가 쌓이니까요. 이것이 Neo 듀얼 토큰의 <strong>플라이휠(flywheel)</strong>이에요.</p>
<hr>
<h2 id="에이전트가-에이전트에게-돈을-보낸다--이게-왜-중요할까요">&quot;에이전트가 에이전트에게 돈을 보낸다&quot; — 이게 왜 중요할까요</h2>
<p>인간 경제에서는 &quot;사람이 사람에게 돈을 보내는&quot; 시스템이 핵심이었어요. 은행, 카드사, 페이팔 — 전부 이 흐름을 최적화하기 위해 존재하는 거예요.</p>
<p>에이전트 경제에서는 <strong>&quot;기계가 기계에게 돈을 보내는&quot;</strong> 시스템이 핵심이 돼요. 그리고 기존 금융 인프라는 이걸 처리할 수 없어요:</p>
<ul>
<li>은행은 AI 에이전트에게 계좌를 열어주지 않아요</li>
<li>비자 카드는 봇에게 발급되지 않아요</li>
<li>국제 송금은 에이전트에게 너무 느리고 비싸요</li>
</ul>
<p>크립토, 특히 Neo X의 GAS는 이 문제를 깔끔하게 해결해요:</p>
<ul>
<li>계좌 개설 불필요 — 지갑 생성은 한 줄의 코드예요</li>
<li>24시간 365일 즉시 결제가 가능해요</li>
<li>수수료는 원화 기준 몇십 원 수준이에요</li>
<li>프로그래머블 — 스마트 컨트랙트로 조건부 결제 자동화가 돼요</li>
</ul>
<h3 id="x402-프로토콜--에이전트-결제의-http-표준">x402 프로토콜 — 에이전트 결제의 HTTP 표준</h3>
<p>이 &quot;기계 간 결제&quot; 문제를 웹 표준 차원에서 풀려는 시도도 등장했어요. 2025년 <strong>코인베이스와 클라우드플레어</strong>가 함께 발표한 <strong>x402 프로토콜</strong>이 바로 그거예요.</p>
<p>아이디어는 놀라울 정도로 심플해요. HTTP에는 원래 <strong>402 Payment Required</strong>라는 상태 코드가 있었는데, &quot;미래 사용을 위해 예약됨&quot;이라고 적혀 있을 뿐 30년 가까이 방치되어 있었어요. x402는 이 잠자던 코드를 되살려서, <strong>스테이블코인 마이크로페이먼트를 HTTP 레벨에서 네이티브하게 구현</strong>한 거예요.</p>
<p>에이전트가 API를 호출하면, 서버가 <code>402 Payment Required</code>를 반환하고 가격과 지갑 주소를 알려줘요. 에이전트가 온체인에서 결제하면, 서버가 확인 후 리소스를 제공해요. <strong>계정도, 세션도, 복잡한 인증 절차도 필요 없어요.</strong> 프로그래머틱하게 서비스 대가를 지불하고 즉시 이용하는 거예요.</p>
<p>x402는 이미 <strong>총 1,500만 건 이상의 트랜잭션</strong>을 처리했어요. 이건 실험이 아니라 실제로 작동하는 인프라예요. 더 넓은 맥락에서 보면, 스테이블코인 전체 트랜잭션 볼륨은 <strong>연간 약 6경 6,700조 원(46조 달러)</strong>에 달하고, 전년 대비 <strong>106% 증가</strong>했어요. 블록체인이 엔터프라이즈급 AI 결제를 처리할 수 있다는 건 이미 숫자로 증명된 셈이에요.</p>
<p>여기서 더 흥미로운 건, <strong>ERC-8004(에이전트 온체인 아이덴티티)와 x402의 통합</strong>이 검토되고 있다는 거예요. 에이전트의 신원 확인과 자동 결제가 하나의 흐름으로 연결되는 미래 — SpoonOS의 에이전트 아이덴티티 시스템과 x402 결제가 만나면, 에이전트는 &quot;나는 누구인지 증명하고, 대가를 지불하고, 서비스를 받는&quot; 전 과정을 한 번의 HTTP 요청으로 처리할 수 있게 돼요.</p>
<p>Master Ventures의 Kyle Chasse는 이렇게 말해요:</p>
<blockquote>
<p>&quot;이게 바로 크립토의 결제 네트워크예요. 모든 에이전트가 서로에게 대가를 지불하는 인프라인 거죠. Neo의 GAS가 그 역할을 하고 있어요.&quot;</p>
</blockquote>
<hr>
<h2 id="spoonos가-만드는-에이전트-상거래-환경">SpoonOS가 만드는 에이전트 상거래 환경</h2>
<p>SpoonOS는 이 에이전트 간 경제 활동을 더 쉽게 만드는 도구를 제공해요:</p>
<ul>
<li><strong>Skills Marketplace</strong> — 에이전트 스킬을 사고파는 마켓이에요. 개발자가 만든 스킬에 대해 사용료를 GAS로 받을 수 있어요.</li>
<li><strong>에이전트 아이덴티티(EIP-8004)</strong> — 거래 상대가 신뢰할 수 있는 에이전트인지 온체인에서 검증해요. 사기 에이전트를 걸러내는 거예요.</li>
<li><strong>SpoonGraph</strong> — 복잡한 멀티 에이전트 결제 흐름도 결정론적으로 실행해요. &quot;A가 B에게 보내고, B가 성공하면 C에게 보내는&quot; 같은 조건부 로직을 안정적으로 처리할 수 있어요.</li>
</ul>
<hr>
<h2 id="결론-크립토가-드디어-자기-자리를-찾았어요">결론: 크립토가 드디어 자기 자리를 찾았어요</h2>
<p>10년간 크립토는 인간에게 쓸모를 증명하려고 애썼어요. 하지만 어쩌면 크립토의 진짜 무대는 <strong>기계와 기계 사이</strong>였을지도 몰라요.</p>
<p>AI 에이전트가 폭발적으로 증가하는 지금, 에이전트들에게는 자신만의 화폐, 자신만의 결제 시스템, 자신만의 경제가 필요해요. Neo의 GAS는 12년 전에 설계되었지만, 마치 이 시대를 위해 만들어진 것처럼 정확히 들어맞고 있어요.</p>
<p>투자 시장도 이 방향을 확인해주고 있어요. 크립토 AI 분야의 투자 비중은 <strong>2023년 하반기 전체 크립토 AI 딜의 5%에 불과했지만, 2025년 상반기에는 36%로 급증</strong>했어요. 7배가 넘는 성장이에요. 돈의 흐름은 거짓말을 하지 않아요 — 크립토와 AI의 교차점이 바로 지금 가장 뜨거운 투자 테마가 되고 있는 거예요.</p>
<p>에이전트 경제의 연료, GAS. 그리고 그 경제가 돌아가는 운영체제, SpoonOS.</p>
<p>머신 경제의 시대가 시작되고 있어요.</p>
<hr>
<p><strong>관련 링크:</strong></p>
<ul>
<li><a href="https://neo.org/neogas">Neo 듀얼 토큰 모델 (NEO &amp; GAS)</a></li>
<li><a href="https://spoonai.io/">SpoonOS 공식 사이트</a></li>
<li><a href="https://neonewstoday.com/ai/da-hongfei-positions-neo-x-as-ai-agent-native-blockchain-in-new-strategy-outline/">Neo AI 전략: Humanless Blockchain</a></li>
<li><a href="https://www.newsfilecorp.com/release/286403/Neo-Releases-Its-2025-Financial-Report-with-461M-Treasury-and-Strategic-Pivot-to-Enhanced-Decentralization">Neo 2025 재무 보고서</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Web3 AI 에이전트의 운영체제가 옵니다]]></title>
            <link>https://velog.io/@neo_blockchain/Web3-AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%EC%9D%98-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EA%B0%80-%EC%98%B5%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@neo_blockchain/Web3-AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%EC%9D%98-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EA%B0%80-%EC%98%B5%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Fri, 03 Apr 2026 01:57:44 GMT</pubDate>
            <description><![CDATA[<h2 id="ai-에이전트는-폭발적으로-늘고-있어요-그런데-이걸-web3에서-돌릴-인프라는-어디에-있을까요">AI 에이전트는 폭발적으로 늘고 있어요. 그런데 이걸 Web3에서 돌릴 인프라는 어디에 있을까요?</h2>
<p>2025년은 AI 에이전트의 해였어요. 챗봇 수준을 넘어서, 스스로 판단하고 행동하고 결과를 만들어내는 자율 에이전트가 크립토 시장 곳곳에 등장했어요. 트레이딩, 포트폴리오 관리, 온체인 데이터 분석, 심지어 콘텐츠 생성까지요.</p>
<p>숫자로 보면 더 실감이 나요. 현재 Web3 AI 에이전트 섹터에만 <strong>282개 프로젝트</strong>가 활동 중이고, 누적 투자액은 <strong>약 62조 원(43억 달러)</strong>에 달해요. Web3뿐만이 아니에요. 포춘 500 기업의 <strong>80% 이상</strong>이 이미 AI 에이전트를 배포하고 있거나 도입을 준비하고 있어요. 에이전트는 더 이상 실험이 아니라, 산업 전반의 핵심 인프라가 되어가고 있어요.</p>
<p>하지만 한 발짝만 뒤로 물러서서 보면 이상한 점이 있어요. 이 에이전트들은 대부분 <strong>중앙화된 서버</strong> 위에서 돌아가고 있거든요. Web3를 외치면서 정작 에이전트 자체는 AWS나 GCP 위에 올라가 있고, 블록체인과는 API 호출 한 줄로만 연결되어 있어요. 탈중앙화를 추구하는 프로젝트의 두뇌가 중앙화 서버에서 실행된다는 건, 꽤 아이러니한 상황이에요.</p>
<p>에이전트가 진짜 Web3의 시민이 되려면, <strong>블록체인 위에서 태어나고, 기억하고, 협업하고, 실행하는</strong> 환경이 필요해요. SpoonOS는 바로 그 환경을 만들기 위해 Neo 위에 구축된 <strong>에이전트 전용 운영체제</strong>입니다.</p>
<hr>
<h2 id="spoonos가-뭔가요-한-문장으로">SpoonOS가 뭔가요? 한 문장으로</h2>
<p><strong>SpoonOS는 AI 에이전트를 Web3 환경에서 만들고, 배포하고, 운영하기 위한 블록체인 네이티브 운영체제예요.</strong></p>
<p>윈도우가 PC 응용 프로그램을 돌리기 위한 OS라면, SpoonOS는 온체인 AI 에이전트를 돌리기 위한 OS라고 보시면 돼요. 개발자가 에이전트를 바닥부터 만들 필요 없이, 모듈형 구성 요소를 조립해서 빠르게 배포할 수 있도록 설계되었어요.</p>
<hr>
<h2 id="4계층-아키텍처--에이전트가-필요한-모든-것">4계층 아키텍처 — 에이전트가 필요한 모든 것</h2>
<p>SpoonOS는 에이전트의 생애주기 전체를 지원하는 4개 계층으로 구성되어 있어요.</p>
<h3 id="1-데이터-계층-data-layer">1. 데이터 계층 (Data Layer)</h3>
<p>에이전트가 제대로 판단하려면 좋은 데이터가 필요하겠죠. SpoonOS의 데이터 계층은 두 가지 핵심 모듈로 구성되어 있어요:</p>
<ul>
<li><strong>MCP+</strong> — 분산 스토리지, 기존 데이터베이스, Web3 데이터셋을 하나의 일관된 형식으로 연결하는 데이터 인터페이스예요. 에이전트가 Web2든 Web3든 가리지 않고 데이터에 접근할 수 있어요.</li>
<li><strong>BeVec</strong> — 맞춤형 벡터 데이터베이스이자 메모리 레이어예요. 의미 기반 검색(semantic retrieval)과 RAG(검색 증강 생성)를 지원해서, 에이전트가 실시간으로 맥락 있는 정보를 꺼내 쓸 수 있어요.</li>
</ul>
<p>쉽게 말하면, MCP+가 도서관의 출입구라면 BeVec은 사서인 거예요. 에이전트가 &quot;이거 찾아줘&quot;라고 하면, 어디에 있든 알아서 찾아와요.</p>
<h3 id="2-실행-계층-execution-layer">2. 실행 계층 (Execution Layer)</h3>
<p>에이전트가 실제로 일을 하는 곳이에요. 핵심은 <strong>SpoonGraph</strong> 엔진인데요.</p>
<p>SpoonGraph는 에이전트 워크플로우를 위한 구조화된 실행 엔진으로, 다음을 지원해요:</p>
<ul>
<li><strong>결정론적 제어 흐름</strong> — 같은 입력이면 항상 같은 결과가 나와요. 금융 거래에서 필수적이에요.</li>
<li><strong>지능형 라우팅</strong> — 작업을 적절한 모듈로 자동 분배해요.</li>
<li><strong>병렬 실행</strong> — 여러 작업을 동시에 처리해 속도를 높여요.</li>
<li><strong>통합 메모리 관리</strong> — 실행 중 상태를 잃어버리지 않아요.</li>
</ul>
<p>기존 LLM 기반 에이전트의 가장 큰 약점은 &quot;가끔 엉뚱한 짓을 한다&quot;는 거예요. 이른바 <strong>&quot;할루시네이션(환각)&quot;</strong> 문제인데요, 일반적인 대화에서는 웃어넘길 수 있지만 <strong>금융 거래에서는 치명적</strong>이에요. 잘못된 정보를 기반으로 토큰을 스왑하거나, 존재하지 않는 컨트랙트를 호출하면 실제 자산 손실로 이어지니까요.</p>
<p>SpoonGraph는 이 문제를 구조적으로 해결해요. 모든 실행 상태 히스토리를 <strong>관찰 가능성(observability) 데이터</strong>로 기록하고 분석할 수 있어서, 에이전트가 언제 어떤 판단을 내렸는지 정확하게 추적할 수 있어요. 문제가 생겼을 때 트러블슈팅이 가능하고, 외부 감사(audit)에도 대응할 수 있는 투명한 구조예요. 에이전트 워크플로우의 <strong>신뢰성과 감사 가능성</strong>을 확보하는 핵심 기술이에요.</p>
<h3 id="3-조율-계층-coordination-layer">3. 조율 계층 (Coordination Layer)</h3>
<p>에이전트 하나가 혼자 일하는 시대는 끝났어요. 여러 에이전트가 협업하는 <strong>멀티 에이전트 시스템</strong>이 핵심이에요.</p>
<p>조율 계층은 <strong>AI 에이전트 상호운용 프로토콜(AI Agent Interoperability Protocol)</strong>을 통해 에이전트 간 상태 공유와 협업을 지원해요. 여기에 두 가지 중요한 보안 기술이 결합되는데요:</p>
<ul>
<li><strong>DID(탈중앙 식별자)</strong> — 에이전트에게 검증 가능한 고유 신원을 부여해요</li>
<li><strong>ZKML(영지식 머신러닝)</strong> — 데이터를 공개하지 않고도 모델의 추론 결과를 검증할 수 있어요</li>
</ul>
<p>서로 모르는 에이전트끼리도 <strong>신뢰 없이 협업할 수 있는(trustless)</strong> 구조예요.</p>
<h3 id="4-응용-계층-application-layer--skills-marketplace">4. 응용 계층 (Application Layer) — Skills Marketplace</h3>
<p>SpoonOS의 꽃이라고 할 수 있어요. <strong>Web3 네이티브 Skills Marketplace</strong>에서 개발자들이 모듈형 스킬을 공유하고 조합해요.</p>
<p>여기서 &quot;스킬&quot;이란 에이전트가 수행할 수 있는 개별 능력 단위예요:</p>
<ul>
<li>지갑 생성 및 연동</li>
<li>온체인 데이터 조회</li>
<li>스마트 컨트랙트 호출</li>
<li>토큰 스왑 실행</li>
<li>가격 피드 수집</li>
</ul>
<p>개발자는 이 스킬들을 <strong>레고 블록처럼 조립</strong>해서 에이전트를 만들어요. 처음부터 만들 필요가 없어요. 커뮤니티 평점과 투명한 기여 추적(attribution) 시스템이 있어서, 어떤 스킬이 실전에서 잘 작동하는지 한눈에 알 수 있어요.</p>
<hr>
<h2 id="왜-spoonos인가--기존-에이전트-프레임워크와-뭐가-다를까요">왜 SpoonOS인가 — 기존 에이전트 프레임워크와 뭐가 다를까요</h2>
<p>시장에는 이미 LangChain, AutoGPT, CrewAI 같은 에이전트 프레임워크가 있어요. SpoonOS가 다른 점은 <strong>블록체인 네이티브</strong>라는 거예요.</p>
<table>
<thead>
<tr>
<th></th>
<th>기존 에이전트 프레임워크</th>
<th>SpoonOS</th>
</tr>
</thead>
<tbody><tr>
<td><strong>실행 환경</strong></td>
<td>중앙 서버 (AWS, GCP)</td>
<td>블록체인 (Neo X)</td>
</tr>
<tr>
<td><strong>메모리</strong></td>
<td>로컬 파일 또는 외부 DB</td>
<td>NeoFS + BeVec (온체인 검증 가능)</td>
</tr>
<tr>
<td><strong>에이전트 신원</strong></td>
<td>없음 (API 키로 구분)</td>
<td>EIP-8004 기반 온체인 ID</td>
</tr>
<tr>
<td><strong>에이전트 간 협업</strong></td>
<td>프레임워크 내부에서만 가능</td>
<td>크로스 프로토콜 상호운용</td>
</tr>
<tr>
<td><strong>프라이버시</strong></td>
<td>프레임워크가 알아서</td>
<td>ZKP + 동형암호 + TEE 조합</td>
</tr>
<tr>
<td><strong>스킬 생태계</strong></td>
<td>각자 개발</td>
<td>Skills Marketplace에서 공유/재사용</td>
</tr>
</tbody></table>
<p>기존 프레임워크가 &quot;에이전트를 만드는 도구&quot;라면, SpoonOS는 <strong>&quot;에이전트가 살아가는 세계&quot;</strong>라고 할 수 있어요.</p>
<hr>
<h2 id="dbos-통합--오프체인-워크플로우까지-커버해요">DBOS 통합 — 오프체인 워크플로우까지 커버해요</h2>
<p>실제 에이전트 운용에서는 모든 작업이 온체인에서 일어나지는 않아요. API 호출, 데이터 전처리, 외부 서비스 연동 등 오프체인 작업도 많거든요.</p>
<p>SpoonOS는 <strong>DBOS</strong>와 통합하여 <strong>내구성 있는 오프체인 워크플로우(Durable Offchain Workflows)</strong>를 지원해요. DBOS는 <strong>&quot;Durable By-Orchestration System&quot;</strong>의 약자로, <strong>파이썬과 타입스크립트 기반의 오픈소스 라이브러리</strong>예요.</p>
<p>DBOS의 핵심 원리는 간단해요. <strong>PostgreSQL 데이터베이스에 워크플로우 상태를 지속적으로 체크포인팅</strong>해서, 실행 도중 장애가 발생해도 마지막 체크포인트부터 원활하게 복구할 수 있어요. 에이전트가 외부 API를 호출하다가 네트워크가 끊겨도, 서버가 재시작되어도, 처음부터 다시 할 필요 없이 중단된 지점부터 이어갈 수 있는 거예요.</p>
<p>개발자 경험도 잘 갖추어져 있어요:</p>
<ul>
<li><strong>로컬 테스트</strong> — 개발 환경에서 바로 워크플로우를 실행하고 디버깅할 수 있어요.</li>
<li><strong>Git 기반 관리</strong> — 워크플로우 코드를 Git으로 버전 관리하고 협업할 수 있어요.</li>
<li><strong>단일 명령어 배포</strong> — 명령어 하나로 프로덕션 환경에 배포할 수 있어요.</li>
<li><strong>자동 재시도(automated retries)</strong> — 일시적인 오류가 발생하면 자동으로 재시도해요.</li>
<li><strong>내장 내구성(built-in durability)</strong> — 별도 설정 없이도 장애 복구가 기본으로 보장돼요.</li>
</ul>
<p>에이전트 개발자 입장에서는 온체인/오프체인을 굳이 구분하지 않고 하나의 워크플로우로 다룰 수 있다는 뜻이에요.</p>
<hr>
<h2 id="실제-구축-사례-scoop-ai-해커톤에서-탄생한-프로젝트들">실제 구축 사례: Scoop AI 해커톤에서 탄생한 프로젝트들</h2>
<p>SpoonOS가 실제로 뭘 만들 수 있는지, 글로벌 해커톤 수상작들을 보면 바로 감이 와요. 실리콘밸리, 하노이, 서울, 도쿄 등에서 개최된 Scoop AI 해커톤에서 다양한 프로젝트가 탄생했어요.</p>
<h3 id="genius-loci-실리콘밸리-bowl">Genius Loci (실리콘밸리 Bowl)</h3>
<p>물리적 장소를 자율 에이전트로 재구상한 프로젝트예요. 예를 들어, 한 카페가 &quot;이 지역의 실시간 유동인구 데이터가 필요해&quot;라는 바운티를 게시하면, 데이터를 수집해서 기여한 사람에게 <strong>x402 결제 프로토콜</strong>로 즉시 보상이 지급돼요. SpoonOS의 상태 그래프와 GPT 분석을 활용해서, 장소 자체가 데이터를 요청하고 활용하는 에이전트가 되는 거예요.</p>
<h3 id="streamsentry-실리콘밸리-bowl">StreamSentry (실리콘밸리 Bowl)</h3>
<p>기존 CCTV 인프라를 <strong>멀티모달 AI 보안 플랫폼</strong>으로 변환하는 프로젝트예요. 영상, 음성, 맥락 데이터를 종합적으로 분석해서 위험 이벤트를 실시간으로 감지해요. SpoonOS 인프라를 활용해 모든 감지 기록을 <strong>블록체인 감사 추적(audit trail)</strong>으로 남기기 때문에, 보안 이벤트의 투명성과 신뢰성을 확보할 수 있어요.</p>
<h3 id="neozero-하노이-bowl-1등--약-360만-원">NeoZero (하노이 Bowl, 1등 — 약 360만 원)</h3>
<p>프롬프트 하나로 웹사이트를 생성하는 프로젝트예요. &quot;포트폴리오 사이트 만들어줘&quot;라고 입력하면, 사이트를 자동으로 생성하고, <strong>NeoNS로 .neo 도메인을 등록</strong>하고, <strong>NeoFS에 사이트를 업로드</strong>하는 것까지 한 번에 처리해요. Web3 네이티브 웹사이트 빌더인 셈이에요.</p>
<h3 id="prism-서울-bowl-대상--약-290만-원">PRISM (서울 Bowl, 대상 — 약 290만 원)</h3>
<p>사용자의 질문을 <strong>인터랙티브 애니메이션 영상</strong>으로 변환하는 교육 도구예요. 복잡한 개념도 시각적으로 풀어서 설명해주기 때문에, 학습 효과를 크게 높일 수 있어요. 서울 Bowl 대상 수상작으로, 한국 개발자 팀의 역량을 잘 보여준 프로젝트예요.</p>
<h3 id="flow-by-cognisor-도쿄-bowl">Flow by Cognisor (도쿄 Bowl)</h3>
<p><strong>음성 우선(voice-first) AI 생산성 플랫폼</strong>이에요. 일상적인 워크플로우를 핸즈프리 대화형 경험으로 전환해줘요. 키보드 없이 음성만으로 업무를 처리할 수 있는 새로운 인터페이스를 제시했어요.</p>
<p>이 프로젝트들이 보여주는 건, SpoonOS가 단순한 이론적 프레임워크가 아니라 <strong>실제로 작동하는 다양한 에이전트를 만들 수 있는 플랫폼</strong>이라는 거예요.</p>
<hr>
<h2 id="개발자를-위한-생태계--약-29억-원-인센티브와-글로벌-해커톤">개발자를 위한 생태계 — 약 29억 원 인센티브와 글로벌 해커톤</h2>
<p>SpoonOS는 생태계 초기 활성화를 위해 적극적으로 개발자를 끌어모으고 있어요:</p>
<ul>
<li><strong>개발자 인센티브 프로그램</strong> — 약 29억 원(200만 달러) 규모예요. 글로벌 비콘 프로그램, 글로벌 개발자 프로그램, 생태계 협력 등 다양한 트랙으로 구성되어 있어요.</li>
<li><strong>Scoop AI 해커톤</strong> — 총 상금 약 1억 4,500만 원(10만 달러)이에요. <strong>모스크바, 하노이, 도쿄, 서울, 실리콘밸리, 방갈로르, 베이징, 런던</strong> 등 전 세계 <strong>8개 도시</strong>를 순회하며 개최돼요. 실리콘밸리 Bowl에서만 <strong>약 8,700만 원(6만 달러) 이상의 상금</strong>이 수여됐어요.</li>
<li><strong>Skills Micro Challenge</strong> — 소규모 챌린지로 실용적인 스킬을 기여하면 보상을 받을 수 있어요. 최대 약 7,300만 원(5만 달러) 규모 그랜트 패스트트랙에 연결돼요.</li>
<li><strong>SDK 지원</strong> — C#, 파이썬, 자바, Go, 타입스크립트 등 주요 언어 SDK를 제공하고 있어요.</li>
</ul>
<hr>
<p><img src="https://velog.velcdn.com/images/neo_blockchain/post/4c8eedb9-adeb-4030-96be-10eadab9556f/image.png" alt=""></p>
<h2 id="결론-에이전트-시대의-os-전쟁이-시작됐어요">결론: 에이전트 시대의 OS 전쟁이 시작됐어요</h2>
<p>PC 시대에 윈도우와 맥이 경쟁했고, 모바일 시대에 안드로이드와 iOS가 경쟁했잖아요. AI 에이전트 시대에도 <strong>에이전트를 위한 OS 전쟁</strong>이 벌어질 거예요.</p>
<p>SpoonOS는 그 전쟁의 첫 주자예요. 블록체인 네이티브로, 에이전트의 생성부터 실행, 협업, 진화까지 모든 생애주기를 하나의 플랫폼에서 지원해요.</p>
<p>에이전트를 만들고 싶다면, 서버를 빌리는 대신 SpoonOS 위에서 시작해보는 건 어떨까요.</p>
<hr>
<p><strong>관련 링크:</strong></p>
<ul>
<li><a href="https://spoonai.io/">SpoonOS 공식 사이트</a></li>
<li><a href="https://xspoonai.github.io/docs/core-concepts/agents/">SpoonOS 개발자 문서</a></li>
<li><a href="https://scoop.spoonai.io/">Scoop AI 해커톤</a></li>
<li><a href="https://neonewstoday.com/ai/spoonos-launches-web3%E2%80%91native-skills-marketplace-to-accelerate-composable-ai/">SpoonOS Skills Marketplace 소개</a></li>
<li><a href="https://neonewstoday.com/development/spoonos-introduces-spoongraph-a-structured-execution-engine-for-ai-agent-workflows/">SpoonGraph 실행 엔진 소개</a></li>
<li><a href="https://www.dbos.dev/blog/spoonos-durable-offchain-workflows">DBOS x SpoonOS 통합</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI 에이전트에게는 자기만의 블록체인이 필요합니다 — Neo X가 만드는 'Humanless Blockchain']]></title>
            <link>https://velog.io/@neo_blockchain/AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%EC%97%90%EA%B2%8C%EB%8A%94-%EC%9E%90%EA%B8%B0%EB%A7%8C%EC%9D%98-%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%A9%EB%8B%88%EB%8B%A4-Neo-X%EA%B0%80-%EB%A7%8C%EB%93%9C%EB%8A%94-Humanless-Blockchain</link>
            <guid>https://velog.io/@neo_blockchain/AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%EC%97%90%EA%B2%8C%EB%8A%94-%EC%9E%90%EA%B8%B0%EB%A7%8C%EC%9D%98-%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%A9%EB%8B%88%EB%8B%A4-Neo-X%EA%B0%80-%EB%A7%8C%EB%93%9C%EB%8A%94-Humanless-Blockchain</guid>
            <pubDate>Fri, 27 Mar 2026 02:45:39 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/neo_blockchain/post/75d672ac-d642-4b0a-99e8-0826a7751cd5/image.png" alt=""></p>
<p>이 포스트는 3월에 진행한 &#39;<a href="https://x.com/i/broadcasts/1jxXgeBdgdNJZ">Live with Neo&#39;s Da Hongfei, Kyle Chasse and Virtual Bacon</a>&#39;의 내용을 기반으로 작성되었습니다.</p>
<h2 id="블록체인은-인간을-위해-만들어졌습니다-그런데-지금-가장-많이-쓰는-건-인간이-아닙니다">블록체인은 인간을 위해 만들어졌습니다. 그런데 지금 가장 많이 쓰는 건 인간이 아닙니다.</h2>
<p>2025년, 온체인 트랜잭션의 풍경이 바뀌고 있습니다. MEV 봇이 블록 공간을 차지하고, AI 에이전트가 포트폴리오를 리밸런싱하고, 자동화된 전략이 사람 손을 거치지 않고 실행되고 있습니다. 그런데 이 에이전트들이 사용하는 블록체인은 여전히 &quot;사람이 버튼을 클릭하는 것&quot;을 전제로 설계되어 있죠.</p>
<p>숫자로 보면 이 흐름이 얼마나 거대한지 실감이 납니다. AI 에이전트 시장 규모는 2025년 약 11조 원(78억 달러)에서 2026년 약 15조 원(109억 달러)으로, 연간 45.8%씩 성장하고 있습니다. 포춘 500 기업의 80% 이상이 이미 AI 에이전트를 배포 중이고, 엔비디아 젠슨 황 CEO는 GTC 2026에서 에이전틱 AI 시장을 무려 <strong>1조 달러 규모</strong>로 전망했습니다. Web3 쪽만 봐도 AI 에이전트 섹터에 282개 프로젝트가 참여하고 있고, 총 62조 원(43억 달러)이 투자되어 있습니다. 에이전트는 이제 실험이 아니라 산업입니다.</p>
<p>메타마스크에서 지갑을 연결하고, 화면에서 슬리피지를 조절하고, 트랜잭션을 눈으로 확인한 뒤 서명하잖아요. 이 모든 흐름은 <strong>인간의 인지 방식</strong>에 최적화된 것입니다. AI 에이전트는 화면을 읽지 않습니다. 버튼을 클릭하지 않습니다. 이미지를 해석해서 다음 행동을 결정하지도 않습니다.</p>
<p>그렇다면 질문은 간단합니다. <strong>에이전트가 주요 사용자인 세상에서, 왜 우리는 여전히 인간용 블록체인 위에서 에이전트를 돌리고 있는 걸까요?</strong></p>
<hr>
<h2 id="볼트온-ai의-한계--기존-체인에-ai를-끼워-넣는-방식의-문제">&quot;볼트온 AI&quot;의 한계 — 기존 체인에 AI를 끼워 넣는 방식의 문제</h2>
<p>지금 대부분의 블록체인 프로젝트가 하고 있는 일은 기존 인프라 위에 AI 호환 레이어를 덧붙이는 것입니다. SDK를 붙이고, API 엔드포인트를 열고, 에이전트가 호출할 수 있는 래퍼를 씌우는 거죠. 작동은 합니다. 하지만 근본적인 마찰은 사라지지 않습니다.</p>
<p>AI 에이전트 플랫폼 OpenClaw를 매일 사용하는 크립토 트레이더 Virtual Bacon(Dennis Liu)은 이 문제를 몸소 겪었습니다:</p>
<blockquote>
<p>&quot;5년간 써온 SaaS 플랫폼들은 에이전트와 제대로 작동하지 않는다. 버튼, 드래그 앤 드롭, 텍스트 박스 — 전부 인간용 GUI로 만들어져 있기 때문이다. 반면 스킬(Skill)과 설명서만 있는 플랫폼은? 에이전트가 한 번의 프롬프트로 즉시 적응한다.&quot;</p>
</blockquote>
<p>블록체인도 마찬가지입니다. 인간용 프론트엔드가 있는 체인에 에이전트를 연결하려면 중간에 별도의 API 제공자를 끼워 넣어야 하고, 그 과정에서 마찰과 지연, 불필요한 의존성이 생기게 됩니다. 이건 <strong>호환성</strong>이지 <strong>네이티브</strong>가 아닙니다.</p>
<p>Neo 공동 창립자 다홍페이(Da Hongfei)의 비유가 딱 들어맞습니다:</p>
<blockquote>
<p>&quot;이건 트위터와 워드프레스 블로그를 비교하는 것과 같다. 블로그에도 짧은 글을 올릴 수 있지만, 트위터는 처음부터 짧은 실시간 메시지를 위해 만들어졌기에 완전히 다른 존재다.&quot;</p>
</blockquote>
<hr>
<h2 id="neo-x--처음부터-에이전트를-위해-설계된-블록체인">Neo X — 처음부터 에이전트를 위해 설계된 블록체인</h2>
<p>Neo X는 다홍페이가 제시한 <strong>&quot;Humanless Blockchain&quot;</strong> 비전의 실체입니다. 핵심 전제는 단 하나입니다: <strong>이 체인의 1차 사용자는 인간이 아니라 AI 에이전트입니다.</strong></p>
<p>기술적으로 Neo X는 <strong>EVM 호환 사이드체인</strong>입니다. Neo 고유의 <strong>dBFT(delegated Byzantine Fault Tolerance)</strong> 합의 메커니즘을 사용하는데, 7개 합의 노드가 트랜잭션 처리 및 투표에 참여하고, 4개 이상이 동의하면 합의가 달성됩니다. 네이티브 토큰은 별도로 없이 <strong>GAS를 네이티브 토큰으로 사용</strong>하고 있습니다.</p>
<p>에이전트 관점에서 특히 중요한 건 두 가지 특성입니다:</p>
<ul>
<li><strong>원 블록 파이널리티(One Block Finality)</strong>: 블록이 네트워크에 전파되면 되돌릴 수 없습니다. 이더리움처럼 여러 블록을 기다릴 필요 없이, 에이전트에게 트랜잭션 확정성이 즉각적으로 보장됩니다. 에이전트가 &quot;이 트랜잭션이 확정됐나?&quot;를 반복 확인할 필요가 없다는 뜻입니다.</li>
<li><strong>Anti-MEV 메커니즘</strong>: 엔벨로프드 트랜잭션(enveloped transactions)으로 원본 트랜잭션을 보호 레이어로 감싸서, MEV 공격으로부터 에이전트를 보호합니다. 에이전트가 수행하는 거래가 프론트러닝이나 샌드위치 공격에 노출되지 않도록 프로토콜 수준에서 방어하는 것입니다.</li>
</ul>
<p>이 전제가 바뀌면 설계의 모든 것이 달라집니다.</p>
<table>
<thead>
<tr>
<th></th>
<th>인간 중심 블록체인</th>
<th>Neo X (에이전트 네이티브)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>인터페이스</strong></td>
<td>GUI, 버튼, 지갑 연결 팝업</td>
<td>CLI, 스킬, API 우선 설계</td>
</tr>
<tr>
<td><strong>아이덴티티</strong></td>
<td>지갑 주소 + ENS</td>
<td>EIP-8004 기반 에이전트 신원 + 평판 시스템</td>
</tr>
<tr>
<td><strong>메모리</strong></td>
<td>없음 (매 세션 초기화)</td>
<td>NeoFS 기반 영속적 에이전트 메모리</td>
</tr>
<tr>
<td><strong>통신</strong></td>
<td>사용자 → 디앱 (1:1)</td>
<td>에이전트 ↔ 에이전트 (M2M 프로토콜)</td>
</tr>
<tr>
<td><strong>우선순위</strong></td>
<td>UX, 시각 요소, 온보딩</td>
<td>확정성, 비용, 결정론성, 상호운용성</td>
</tr>
</tbody></table>
<p>에이전트는 예쁜 대시보드가 필요 없습니다. 트랜잭션이 확정되었는지, 수수료가 얼마인지, 다른 에이전트와 어떻게 통신하는지 — 이것만 빠르고 정확하면 됩니다.</p>
<h3 id="에이전트-신원-인증-eip-8004">에이전트 신원 인증: EIP-8004</h3>
<p>2026년 1월 이더리움 메인넷에 배포된 EIP-8004는 AI 에이전트에게 온체인 신원을 부여하는 표준입니다. 메타마스크, 이더리움 재단, 구글, 코인베이스의 엔지니어들이 공동 설계했습니다.</p>
<p>ERC-8004는 세 개의 온체인 레지스트리를 구축합니다: <strong>Identity(신원)</strong>, <strong>Reputation(평판)</strong>, <strong>Validation(검증)</strong>. 각 에이전트는 <strong>ERC-721 토큰으로 표현</strong>되며, &quot;에이전트 카드&quot;라는 구조화된 JSON 파일을 가리키고 있습니다. 이 에이전트 카드에는 이름, 기능, 서비스 엔드포인트, 결제 주소가 포함되어 있죠. 쉽게 말하면, 에이전트의 디지털 명함이자 자격증인 셈입니다.</p>
<p>구글이 만든 A2A(Agent-to-Agent) 프로토콜을 알고 계신다면 이해가 빠를 텐데요 — ERC-8004는 사실상 A2A의 <strong>트러스트리스(trustless) 확장판</strong>입니다. 중앙 서버가 아니라 블록체인 위에서 에이전트 간 신뢰를 검증한다는 점이 근본적으로 다릅니다. 또한 <strong>x402 결제 프로토콜</strong>과의 통합도 고려되고 있습니다. x402는 코인베이스와 클라우드플레어가 만든 HTTP 402 기반 스테이블코인 마이크로페이먼트 표준인데, 에이전트가 서비스를 요청할 때 HTTP 레벨에서 바로 결제가 이루어지는 구조입니다.</p>
<p>Neo X는 이 표준을 채택해 에이전트가 <strong>자신만의 온체인 ID, 평판 이력, 검증 가능한 행동 기록</strong>을 갖도록 하고 있습니다. ENS가 인간 지갑 주소에 이름을 붙인 것처럼, Neo X의 EIP-8004 레지스트리는 에이전트에게 <strong>발견 가능하고 신뢰할 수 있는 정체성</strong>을 부여합니다.</p>
<h3 id="에이전트-메모리-neofs">에이전트 메모리: NeoFS</h3>
<p>현재 대부분의 AI 에이전트는 메모리 문제를 안고 있습니다. 컨텍스트 윈도우가 꽉 차면 이전 대화를 까먹고, 세션이 끝나면 모든 상태가 초기화됩니다. 트레이딩 에이전트가 어제의 전략을 기억하지 못한다면, 매일 처음부터 다시 시작하는 셈이죠.</p>
<p>Neo X는 분산 스토리지 레이어인 NeoFS를 에이전트의 <strong>영속적 메모리</strong>로 활용하고 있습니다. 에이전트의 상태, 전략, 실행 결과가 세션을 넘어 유지되고, 이 기록은 온체인에서 검증할 수 있습니다.</p>
<p>NeoFS가 에이전트 메모리로 적합한 이유는 기술적 특성에 있습니다. <strong>S3, HTTP, FUSE, sFTP 등 인기 프로토콜 게이트를 지원</strong>하기 때문에, 개발자가 기존 코드를 다시 작성하지 않고도 바로 통합할 수 있습니다. 스토리지 정책도 사용자가 직접 선택할 수 있는데 — 지리적 위치, 신뢰성 수준, 노드 수, 디스크 유형까지 세밀하게 조절이 가능합니다.</p>
<p>경제 모델도 흥미롭습니다. <strong>자유시장 원칙에 따른 인센티브 구조</strong>로 운영됩니다. 각 노드가 스토리지 서비스 보상을 직접 설정하기 때문에, 수요와 공급에 따라 자연스럽게 가격이 결정되는 구조입니다. 또한 Neo 스마트 컨트랙트와 직접 연동되고, <strong>CDN(콘텐츠 전달 네트워크)</strong>으로도 사용할 수 있어서, 에이전트가 데이터를 저장하고 빠르게 불러오는 데 최적화되어 있습니다.</p>
<h3 id="에이전트-간-통신-m2m-프로토콜">에이전트 간 통신: M2M 프로토콜</h3>
<p>인간은 디앱과 1:1로 상호작용하지만, 에이전트 경제에서는 <strong>에이전트가 다른 에이전트와 직접 거래</strong>합니다. Neo X는 머신 대 머신(M2M) 통신 프로토콜을 기본 탑재하여, 중개 없이 에이전트 간 조율과 트랜잭션이 가능합니다.</p>
<hr>
<h2 id="spoonos--neo-x-위에서-돌아가는-에이전트-운영체제">SpoonOS — Neo X 위에서 돌아가는 에이전트 운영체제</h2>
<p>블록체인이 인프라라면, <strong>SpoonOS는 그 위에서 에이전트를 배포하고 운영하는 OS</strong>입니다.</p>
<p>SpoonOS는 4개 계층으로 구성되어 있습니다:</p>
<ul>
<li><strong>데이터 계층(Data Layer)</strong> — MCP+를 통해 분산 스토리지, 기존 DB, Web3 데이터셋에 일관된 형식으로 접근합니다. BeVec 메모리 레이어가 의미 기반 검색과 RAG(검색 증강 생성)를 지원합니다.</li>
<li><strong>실행 계층(Execution Layer)</strong> — SpoonGraph 엔진이 결정론적 제어 흐름, 병렬 실행, 메모리 관리를 담당합니다. 에이전트 워크플로우의 신뢰성과 감사 가능성을 확보합니다.</li>
<li><strong>조율 계층(Coordination Layer)</strong> — AI 에이전트 상호운용 프로토콜로 에이전트 간 상태 공유와 협업을 처리합니다. 탈중앙 식별자(DID)와 영지식 머신러닝(ZKML)을 통한 무신뢰 협업이 가능합니다.</li>
<li><strong>응용 계층(Application Layer)</strong> — Skills Marketplace에서 개발자들이 모듈형 스킬(지갑 연동, 온체인 데이터 조회 등)을 공유하고 조합합니다. 에이전트를 바닥부터 만들 필요 없이, 기존 스킬을 레고 블록처럼 조립하면 됩니다.</li>
</ul>
<p>Virtual Bacon의 말처럼, 에이전트는 <strong>스킬 하나를 읽으면 그걸로 할 수 있는 모든 것을 1분 안에 파악합니다</strong>. SpoonOS의 Skills Marketplace는 바로 이 특성을 위해 설계되었습니다 — 에이전트가 단번에 능력을 획득하는 Web3 네이티브 생태계입니다.</p>
<hr>
<h2 id="왜-카테고리-창출인가--역사가-증명하는-패턴">왜 &quot;카테고리 창출&quot;인가 — 역사가 증명하는 패턴</h2>
<p>다홍페이는 Neo의 AI 전략 논문에서 이렇게 썼습니다:</p>
<blockquote>
<p>&quot;유일한 승리 전략은 아직 존재하지 않는 카테고리를 찾아 먼저 구축하는 것이다.&quot;</p>
</blockquote>
<p>이건 희망 사항이 아니라 <strong>반복된 패턴</strong>입니다:</p>
<ul>
<li><strong>비트코인</strong> — P2P 전자 화폐라는 카테고리를 만들었습니다. 이후 수천 개의 &quot;더 빠른 비트코인&quot;이 등장했지만, 비트코인을 대체하지 못했습니다.</li>
<li><strong>이더리움</strong> — 스마트 컨트랙트 플랫폼이라는 카테고리를 만들었습니다. 2015년 기준 이 카테고리에 있던 체인은 이더리움과 Neo(당시 앤트셰어즈) 단 둘뿐이었습니다.</li>
<li><strong>Neo X</strong> — AI 에이전트 네이티브 블록체인이라는 카테고리를 개척하고 있습니다. 기존 체인과 속도를 겨루는 게 아니라, 완전히 다른 경기장을 여는 것입니다.</li>
</ul>
<p>성능 경쟁에는 끝이 없습니다. 이더리움도 확장하고, 솔라나도 개선하고, 새로운 L2는 매달 쏟아지고 있습니다. 하지만 <strong>&quot;에이전트가 인간 없이 작동하는 체인&quot;이라는 카테고리</strong>에는 아직 확실한 선점자가 없습니다.</p>
<hr>
<h2 id="선점의-진짜-의미--에이전트가-스택을-선택하는-시대">선점의 진짜 의미 — 에이전트가 스택을 선택하는 시대</h2>
<p>여기서 핵심은 <strong>누가 스택을 고르느냐</strong>입니다. 지금까지는 개발자가 체인을 선택했습니다. 문서를 읽고, 생태계를 비교하고, 커뮤니티를 살펴본 뒤 결정했죠.</p>
<p>하지만 에이전트 시대에는 판이 달라집니다. Virtual Bacon은 이렇게 내다보고 있습니다:</p>
<blockquote>
<p>&quot;지금은 에이전트에게 체인을 지정하고, 앱을 설명하고, 빌드를 시킨다. 하지만 곧 &#39;탈중앙 앱 하나 만들어줘, 최적의 스택 알아서 찾아서&#39;라고만 하면 된다. 그때 에이전트는 가장 네이티브한 체인을 자동으로 선택한다. 에이전트는 모든 걸 단번에 처리하기 때문에, 네이티브 스킬이 있는 체인이 이긴다.&quot;</p>
</blockquote>
<p>이것이 Neo X와 SpoonOS가 가진 선점 우위입니다. 에이전트가 직접 체인을 고르는 시대에, <strong>에이전트를 위해 태어난 체인</strong>이 선택받는 건 당연한 수순입니다.</p>
<hr>
<h2 id="약-6700억-원의-트레저리-12년의-실행력">약 6,700억 원의 트레저리, 12년의 실행력</h2>
<p>비전만으로는 부족합니다. 실행할 자원이 있어야 합니다.</p>
<p>Neo는 2014년 1억 원 남짓한 시드에서 시작해, 현재 <strong>약 6,700억 원(4억 6,100만 달러) 규모의 트레저리</strong>를 보유하고 있습니다. 이 중 2,900억 원 이상이 고유동성 자산인데 — 비트코인 1,100개 이상, 현금 및 스테이블코인 약 1,450억 원입니다. 12년간 여러 차례 크립토 겨울을 견디면서도 자산을 키워온 팀입니다.</p>
<p>SpoonOS에는 약 <strong>29억 원(200만 달러) 규모의 개발자 인센티브 프로그램</strong>이 투입되었고, 상금 총 1억 4,500만 원 규모의 글로벌 해커톤(Scoop AI)이 이미 진행 중입니다. 로드맵 위의 글자가 아니라, 지금 실행되고 있는 현실입니다.</p>
<hr>
<h2 id="경쟁-환경-neo-x는-어디에-위치하는가">경쟁 환경: Neo X는 어디에 위치하는가</h2>
<p>AI 에이전트 + 블록체인 교차 지점에는 이미 주목할 만한 플레이어들이 있습니다. <strong>Virtuals Protocol</strong>은 시총 기준 AI 에이전트 섹터의 약 23%를 점유하고 있고, <strong>ai16z/ElizaOS</strong>는 약 12%를 차지하고 있습니다. 이 외에도 수십 개의 프로젝트가 이 시장에 뛰어들고 있죠.</p>
<p>하지만 이들 대부분을 자세히 들여다보면, <strong>&quot;에이전트 토큰 발행 플랫폼&quot;</strong>이거나 <strong>&quot;에이전트 프레임워크&quot;</strong>에 가깝습니다. Virtuals는 Base 체인 위에서, ai16z/ElizaOS는 Solana 위에서 동작합니다. 즉, 에이전트를 만들고 런칭하는 도구는 제공하지만, 그 에이전트가 실제로 돌아가는 인프라는 여전히 <strong>인간을 위해 설계된 기존 블록체인</strong>에 의존하고 있는 것입니다.</p>
<p>Neo X의 차별점은 여기에 있습니다. <strong>블록체인 인프라 자체를 에이전트 네이티브로 설계한 유일한 프로젝트</strong>라는 점입니다. 합의 메커니즘(dBFT로 원 블록 파이널리티 보장), 스토리지(NeoFS로 영속적 에이전트 메모리 제공), 아이덴티티(EIP-8004로 온체인 신원 부여)까지 — 스택 전체가 에이전트를 위해 통합 설계되어 있습니다.</p>
<p>비유하자면 이런 것입니다. Virtuals와 ai16z는 <strong>기존 도로 위를 달리는 자율주행 소프트웨어</strong>라면, Neo X는 <strong>자율주행 차량 전용 도로 인프라</strong>를 깔고 있는 것입니다. 소프트웨어도 중요하지만, 도로 자체가 자율주행에 최적화되면 차원이 다른 효율성이 나오는 것과 같은 이치입니다.</p>
<hr>
<h2 id="결론-에이전트의-시대에-에이전트를-위한-체인">결론: 에이전트의 시대에, 에이전트를 위한 체인</h2>
<p>크립토의 다음 10억 사용자는 인간이 아닐 수도 있습니다. AI 에이전트가 트레이딩하고, 결제하고, 서로 협업하고, 가치를 만들어내는 세상이 열리고 있습니다.</p>
<p>그 세상에서 필요한 건 <strong>더 빠른 인간용 체인</strong>이 아니라, <strong>처음부터 에이전트를 위해 설계된 체인</strong>입니다.</p>
<p>Neo X는 그 체인이고, SpoonOS는 그 위의 운영체제입니다.</p>
<hr>
<p><strong>관련 링크:</strong></p>
<ul>
<li><a href="https://neonewstoday.com/ai/da-hongfei-positions-neo-x-as-ai-agent-native-blockchain-in-new-strategy-outline/">Neo AI 전략: Humanless Blockchain</a></li>
<li><a href="https://spoonai.io/">SpoonOS 공식 사이트</a></li>
<li><a href="https://neonewstoday.com/ai/spoonos-launches-web3%E2%80%91native-skills-marketplace-to-accelerate-composable-ai/">SpoonOS Skills Marketplace 소개</a></li>
<li><a href="https://eips.ethereum.org/EIPS/eip-8004">EIP-8004: AI 에이전트 신원 표준</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[바이브 코딩(Vibe Coding) 완전 가이드 — AI IDE와 함께 XSpoonAi 코드를 제대로 작성하는 법]]></title>
            <link>https://velog.io/@neo_blockchain/%EB%B0%94%EC%9D%B4%EB%B8%8C-%EC%BD%94%EB%94%A9Vibe-Coding-%EC%99%84%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C-AI-IDE%EC%99%80-%ED%95%A8%EA%BB%98-XSpoonAi-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%9E%91%EC%84%B1%ED%95%98%EB%8A%94-%EB%B2%95</link>
            <guid>https://velog.io/@neo_blockchain/%EB%B0%94%EC%9D%B4%EB%B8%8C-%EC%BD%94%EB%94%A9Vibe-Coding-%EC%99%84%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C-AI-IDE%EC%99%80-%ED%95%A8%EA%BB%98-XSpoonAi-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%9E%91%EC%84%B1%ED%95%98%EB%8A%94-%EB%B2%95</guid>
            <pubDate>Fri, 20 Mar 2026 03:39:06 GMT</pubDate>
            <description><![CDATA[<p>요즘 개발 트렌드 중 하나가 바로 <strong>바이브 코딩(Vibe Coding)</strong>입니다. Cursor, Codex, Claude Code 같은 AI 코딩 어시스턴트를 활용해서, 직접 한 줄 한 줄 코드를 치는 게 아니라 AI에게 적절한 컨텍스트를 던져주고 원하는 코드를 생성하게 만드는 방식이죠.</p>
<p>그런데 여기서 핵심이 하나 있습니다. <strong>AI에게 얼마나 정확한 컨텍스트를 제공하느냐</strong>에 따라 결과물의 품질이 천지 차이라는 점입니다. 아무런 맥락 없이 &quot;이거 만들어줘&quot;라고 하면 AI가 없는 API를 지어내거나(hallucination), 엉뚱한 패턴으로 코드를 짜는 경우가 허다합니다.</p>
<p>이 글에서는 XSpoonAi 프로젝트에서 바이브 코딩을 할 때, AI 어시스턴트에게 올바른 컨텍스트를 전달하는 <strong>5가지 방법</strong>을 소개합니다. 가장 가이드가 많은 방법부터 가장 수동적인 방법 순서로 정리했으니, 본인의 상황에 맞는 걸 하나 골라서 사용하시면 됩니다.</p>
<hr>
<h2 id="방법-1-mcp--온라인-검색-활용-로컬-클론-없이-사용하기">방법 1: MCP / 온라인 검색 활용 (로컬 클론 없이 사용하기)</h2>
<p>로컬에 소스 코드를 클론하지 않고도 AI에게 정확한 컨텍스트를 줄 수 있는 방법입니다.</p>
<p><strong>어떻게 하나요?</strong></p>
<p>MCP(Model Context Protocol) 커넥터(예: deepwiki, context7 등)를 설정해서, GitHub이나 웹에서 직접 파일을 가져오도록 구성합니다. 이렇게 하면 AI가 실시간으로 원본 소스를 참조할 수 있습니다.</p>
<p><strong>타겟 레포지토리:</strong></p>
<ul>
<li><code>XSpoonAi/spoon-core</code> — 핵심 소스 코드 + 예제</li>
<li><code>XSpoonAi/toolkit</code> — 툴킷 소스 코드 + 예제</li>
<li>XSpoonAi Cookbook 사이트 — 문서 참조용</li>
</ul>
<p><strong>AI에게 이렇게 지시하세요:</strong></p>
<blockquote>
<p>&quot;관련 파일 목록을 먼저 나열하고, 참고할 파일의 정확한 내용을 가져온 다음, 파일 경로와 라인 번호를 인용하면서 코드를 작성해.&quot;</p>
</blockquote>
<p>이 방식의 장점은 로컬에 아무것도 미러링하거나 클론할 필요가 없다는 것입니다. MCP 설정만 잘 해두면 AI가 알아서 필요한 파일을 찾아 읽고 참고합니다.</p>
<hr>
<h2 id="방법-2-번들-문서-파일-cookbookllmtxt-제공하기">방법 2: 번들 문서 파일 (<code>cookbook/llm.txt</code>) 제공하기</h2>
<p>XSpoonAi 레포에는 모든 마크다운 문서를 하나로 합쳐놓은 <strong><code>cookbook/llm.txt</code></strong> 파일이 자동 생성되어 포함되어 있습니다. CI가 항상 최신 상태로 유지해주기 때문에, 이 파일 하나만 AI에게 넘겨주면 Cookbook의 전체 맥락을 한 번에 전달할 수 있습니다.</p>
<p><strong>사용법은 간단합니다:</strong></p>
<ol>
<li>레포에서 <code>cookbook/llm.txt</code> 파일을 찾습니다.</li>
<li>이 파일을 AI 어시스턴트에게 공유합니다.</li>
<li>끝! AI가 전체 문서 맥락을 파악하고 코드를 작성합니다.</li>
</ol>
<p>GitHub에서 직접 받고 싶다면 <a href="https://github.com/XSpoonAi/xspoonai.github.io/blob/main/llm.txt">llm.txt</a> 링크를 참고하세요.</p>
<p>가장 간편한 방법 중 하나이고, 특히 빠르게 프로토타이핑할 때 유용합니다.</p>
<hr>
<h2 id="방법-3-설치된-패키지-경로-직접-알려주기">방법 3: 설치된 패키지 경로 직접 알려주기</h2>
<p>로컬에 XSpoonAi 패키지를 editable 모드나 로컬 설치로 이미 설치해둔 경우, 해당 패키지의 실제 설치 경로를 AI에게 알려주는 방식입니다.</p>
<p><strong>패키지 경로 확인 방법:</strong></p>
<pre><code class="language-bash">python -c &quot;import importlib.util, os; spec = importlib.util.find_spec(&#39;spoon_ai&#39;); print(&#39;spoon_ai:&#39;, os.path.dirname(spec.origin) if spec and spec.origin else &#39;not installed&#39;)&quot;
python -c &quot;import importlib.util, os; spec = importlib.util.find_spec(&#39;spoon_toolkits&#39;); print(&#39;spoon_toolkits:&#39;, os.path.dirname(spec.origin) if spec and spec.origin else &#39;not installed&#39;)&quot;</code></pre>
<p>위 명령어를 실행하면 각 패키지가 설치된 디렉토리 경로가 출력됩니다. 이 경로를 AI 어시스턴트에게 <strong>읽기 전용 참조 경로</strong>로 전달하면, AI가 실제로 설치된 코드와 예제를 스캔하면서 정확한 코드를 작성할 수 있습니다.</p>
<p>만약 &quot;not installed&quot;라고 출력된다면, 패키지가 설치되지 않은 것이니 해당 레포 디렉토리(예: <code>core/spoon_ai</code> 또는 <code>toolkit/spoon_toolkits</code>)를 대신 공유하면 됩니다.</p>
<hr>
<h2 id="방법-4-워크스페이스-자체를-공유하기">방법 4: 워크스페이스 자체를 공유하기</h2>
<p>가장 직관적인 방법입니다. AI 어시스턴트가 레포 전체를 직접 읽을 수 있도록 허용하는 것이죠.</p>
<p><strong>핵심 디렉토리:</strong></p>
<ul>
<li><code>core/</code> — 핵심 라이브러리 코드</li>
<li><code>toolkit/</code> — 툴킷 코드</li>
<li><code>cookbook/docs/</code> — 문서</li>
</ul>
<p><strong>로컬에 아직 클론하지 않았다면:</strong></p>
<pre><code class="language-bash">git clone https://github.com/XSpoonAi/spoon-core.git
git clone https://github.com/XSpoonAi/spoon-toolkit.git</code></pre>
<p><strong>중요한 팁들:</strong></p>
<ul>
<li>항상 <code>git pull</code>로 최신 상태를 유지하세요. AI가 오래된 코드를 참조하면 당연히 결과물도 틀어집니다.</li>
<li>AI에게 실제 소스를 먼저 열어보라고 지시하세요. 예를 들어 <code>core/spoon_ai/**</code>이나 <code>core/examples/</code>, <code>toolkit/**/examples</code> 경로의 실행 가능한 샘플 코드를 먼저 읽게 합니다.</li>
<li>AI에게 <strong>&quot;함수 시그니처와 설정값은 코드 파일이나 예제에서 직접 추출해. 추상적으로 추측하지 마.&quot;</strong>라고 명시적으로 알려주세요.</li>
</ul>
<hr>
<h2 id="할루시네이션환각-줄이는-팁">할루시네이션(환각) 줄이는 팁</h2>
<p>바이브 코딩에서 가장 큰 적은 <strong>AI의 할루시네이션</strong>입니다. 없는 함수를 만들어내거나, 존재하지 않는 파라미터를 넣거나, 인터페이스를 제멋대로 지어내는 경우가 생기죠. 이걸 방지하려면 AI에게 다음과 같은 지시를 명확하게 전달하는 것이 효과적입니다.</p>
<p>AI에게 이렇게 말해보세요:</p>
<blockquote>
<p><strong>&quot;참고할 소스 코드나 예제를 먼저 읽고, 코딩하기 전에 파일 경로와 라인 범위를 인용해.&quot;</strong></p>
</blockquote>
<blockquote>
<p><strong>&quot;tool, agent, LLM의 시그니처는 추측하지 말고 코드에서 직접 확인해. 사용할 import문을 먼저 보여줘.&quot;</strong></p>
</blockquote>
<blockquote>
<p><strong>&quot;확실하지 않으면 MCP를 통해 해당 파일을 직접 가져오고, 인터페이스를 다시 정리한 다음에 구현해.&quot;</strong></p>
</blockquote>
<p>이 세 가지 규칙만 지켜도 할루시네이션이 크게 줄어듭니다.</p>
<hr>
<h2 id="방법-5-skills를-활용한-바이브-코딩-궁극의-바이브-코딩">방법 5: Skills를 활용한 바이브 코딩 (궁극의 바이브 코딩)</h2>
<p>마지막으로 소개할 방법은 <strong>SpoonOS Skills</strong>를 활용하는 것입니다. Skills는 이미 만들어진 프롬프트, 도구, 스크립트를 묶어놓은 패키지로, 프로젝트에 그대로 드롭인해서 바로 커스터마이징할 수 있습니다. 바이브 코딩의 끝판왕이라고 할 수 있죠.</p>
<h3 id="빠른-시작">빠른 시작</h3>
<pre><code class="language-bash"># awesome 컬렉션에서 Web3 스킬 가져오기
git clone https://github.com/XSpoonAi/spoon-awesome-skill.git

# 프로젝트에 스킬 복사
cp -r spoon-awesome-skill/web3-skills/defi ./skills/</code></pre>
<p>이렇게 하면 DeFi 관련 스킬이 프로젝트의 <code>./skills/</code> 디렉토리에 들어갑니다.</p>
<h3 id="skills-기반-에이전트-만들기">Skills 기반 에이전트 만들기</h3>
<pre><code class="language-python">from spoon_ai.agents import SpoonReactSkill
from spoon_ai.chat import ChatBot

agent = SpoonReactSkill(
    llm=ChatBot(llm_provider=&quot;openai&quot;, model_name=&quot;gpt-4o-mini&quot;),
    skill_paths=[&quot;./skills&quot;],
    scripts_enabled=True,
    auto_trigger_skills=True
)

await agent.initialize()
await agent.activate_skill(&quot;defi&quot;)

# AI가 스킬의 스크립트와 프롬프트를 알아서 파악하고 실행합니다
response = await agent.run(&quot;1 ETH를 USDC로 스왑하는 견적을 가져와줘&quot;)</code></pre>
<h3 id="skills가-바이브-코딩에-좋은-이유">Skills가 바이브 코딩에 좋은 이유</h3>
<ol>
<li><strong>복사해서 커스터마이징</strong>: SKILL.md 파일을 워크스페이스에 넣고, 프롬프트만 살짝 수정하면 됩니다. 복잡한 설정이 필요 없어요.</li>
<li><strong>AI가 알아서 판단</strong>: 스크립트를 활성화해두면 AI가 도구 사용법을 스스로 파악합니다. 일일이 알려줄 필요가 없죠.</li>
<li><strong>이식성이 뛰어남</strong>: 스킬은 자체 포함된 폴더 구조라 공유하기도 쉽고, 버전 관리도 간편합니다.</li>
<li><strong>Web3 바로 시작 가능</strong>: <a href="https://github.com/XSpoonAi/spoon-awesome-skill">spoon-awesome-skill</a> 레포에서 프로덕션 수준의 스킬을 바로 가져다 쓸 수 있습니다.</li>
</ol>
<p>더 자세한 내용은 <a href="./build-skill-agents">Building Skill Agents</a> 가이드를 참고하세요.</p>
<hr>
<h2 id="마무리">마무리</h2>
<p>바이브 코딩의 핵심은 결국 <strong>AI에게 얼마나 좋은 컨텍스트를 주느냐</strong>입니다. 위 5가지 방법 중 자신의 개발 환경과 워크플로우에 맞는 것을 선택하고, 할루시네이션 방지 팁까지 적용하면, AI와 함께 놀라울 정도로 생산적인 코딩이 가능합니다.</p>
<p>특히 XSpoonAi 생태계에서는 MCP 연동이나 Skills 활용이 굉장히 강력하니, 꼭 한번 시도해보시길 추천합니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SpoonOS로 나만의 AI 스킬 에이전트 만들기 — 처음부터 배포까지]]></title>
            <link>https://velog.io/@neo_blockchain/SpoonOS%EB%A1%9C-%EB%82%98%EB%A7%8C%EC%9D%98-AI-%EC%8A%A4%ED%82%AC-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%B2%98%EC%9D%8C%EB%B6%80%ED%84%B0-%EB%B0%B0%ED%8F%AC%EA%B9%8C%EC%A7%80</link>
            <guid>https://velog.io/@neo_blockchain/SpoonOS%EB%A1%9C-%EB%82%98%EB%A7%8C%EC%9D%98-AI-%EC%8A%A4%ED%82%AC-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%B2%98%EC%9D%8C%EB%B6%80%ED%84%B0-%EB%B0%B0%ED%8F%AC%EA%B9%8C%EC%A7%80</guid>
            <pubDate>Fri, 13 Mar 2026 04:50:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>SpoonOS의 스킬 시스템을 활용하면, 복잡한 오케스트레이션 로직 없이도 강력한 AI 에이전트를 빠르게 만들 수 있습니다. 이 글에서는 첫 번째 스킬 에이전트를 만드는 것부터 실전 활용까지의 전 과정을 다룹니다.</p>
</blockquote>
<hr>
<h2 id="목차">목차</h2>
<ol>
<li><a href="#%EC%8A%A4%ED%82%AC-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%EB%9E%80">스킬 에이전트란?</a></li>
<li><a href="#%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-%EC%A0%84%EC%97%90">시작하기 전에</a></li>
<li><a href="#5%EB%B6%84-%EB%A7%8C%EC%97%90-%EB%A7%8C%EB%93%9C%EB%8A%94-%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EC%8A%A4%ED%82%AC-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8">5분 만에 만드는 첫 번째 스킬 에이전트</a></li>
<li><a href="#%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A1%9C-%EC%8A%A4%ED%82%AC%EC%97%90-%EC%8B%A4%ED%96%89%EB%A0%A5-%EB%B6%80%EC%97%AC%ED%95%98%EA%B8%B0">스크립트로 스킬에 실행력 부여하기</a></li>
<li><a href="#vibe-coding--%EB%B9%A0%EB%A5%B4%EA%B2%8C-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9D%B4%ED%95%91%ED%95%98%EA%B8%B0">Vibe Coding — 빠르게 프로토타이핑하기</a></li>
<li><a href="#%EC%8B%A4%EC%A0%84-%EC%98%88%EC%8B%9C-web3-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0">실전 예시: Web3 에이전트 만들기</a></li>
<li><a href="#skill-manager-api-%ED%99%9C%EC%9A%A9%EB%B2%95">Skill Manager API 활용법</a></li>
<li><a href="#%EC%8B%A4%EC%A0%84%EC%97%90%EC%84%9C-%EA%BC%AD-%EC%A7%80%EC%BC%9C%EC%95%BC-%ED%95%A0-best-practices">실전에서 꼭 지켜야 할 Best Practices</a></li>
<li><a href="#%EB%AC%B8%EC%A0%9C%EA%B0%80-%EC%83%9D%EA%B2%BC%EC%9D%84-%EB%95%8C--%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85-%EA%B0%80%EC%9D%B4%EB%93%9C">문제가 생겼을 때 — 트러블슈팅 가이드</a></li>
<li><a href="#%EB%A7%88%EB%AC%B4%EB%A6%AC-%EB%B0%8F-%EB%8B%A4%EC%9D%8C-%EB%8B%A8%EA%B3%84">마무리 및 다음 단계</a></li>
</ol>
<hr>
<h2 id="스킬-에이전트란">스킬 에이전트란?</h2>
<p>스킬 에이전트는 <strong>&quot;스킬(Skill)&quot;이라는 모듈 단위로 AI의 역할과 도구를 정의</strong>하는 방식입니다. 기존에 AI 에이전트를 만들려면 프롬프트 엔지니어링, 도구 연결, 상태 관리 등을 직접 코딩해야 했지만, SpoonOS의 스킬 시스템을 사용하면 이 모든 것을 <strong>마크다운 파일 하나(<code>SKILL.md</code>)와 몇 줄의 Python 코드</strong>로 해결할 수 있습니다.</p>
<p>핵심 개념을 정리하면 이렇습니다:</p>
<table>
<thead>
<tr>
<th>개념</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Skill</strong></td>
<td>에이전트에게 부여할 역할·지식·도구를 묶은 모듈</td>
</tr>
<tr>
<td><strong>SKILL.md</strong></td>
<td>스킬의 메타데이터(이름, 트리거, 스크립트)와 프롬프트를 정의하는 마크다운 파일</td>
</tr>
<tr>
<td><strong>Script</strong></td>
<td>스킬이 실제로 &quot;실행&quot;할 수 있는 Python 스크립트 (도구/Tool 역할)</td>
</tr>
<tr>
<td><strong>Trigger</strong></td>
<td>사용자 입력에 따라 스킬을 자동 활성화하는 조건</td>
</tr>
</tbody></table>
<hr>
<h2 id="시작하기-전에">시작하기 전에</h2>
<p>아래 세 가지가 준비되어 있어야 합니다:</p>
<ol>
<li><strong>SpoonOS 설치</strong> — <a href="https://xspoonai.github.io/docs/getting-started/installation">공식 설치 가이드</a> 참고</li>
<li><strong>Python 3.9 이상</strong> — 스크립트 실행에 필요</li>
<li><strong>LLM API 키</strong> — OpenAI, Anthropic 등 지원 프로바이더 중 하나</li>
</ol>
<hr>
<h2 id="5분-만에-만드는-첫-번째-스킬-에이전트">5분 만에 만드는 첫 번째 스킬 에이전트</h2>
<h3 id="step-1-스킬-디렉토리-생성">Step 1: 스킬 디렉토리 생성</h3>
<p>스킬은 디렉토리 단위로 관리됩니다. 먼저 폴더를 하나 만들어 주세요.</p>
<pre><code class="language-bash">mkdir -p ./skills/my-assistant</code></pre>
<h3 id="step-2-skillmd-작성">Step 2: SKILL.md 작성</h3>
<p><code>./skills/my-assistant/SKILL.md</code> 파일을 생성합니다. 이 파일이 스킬의 <strong>설정 파일이자 프롬프트</strong> 역할을 합니다.</p>
<p>파일은 크게 두 부분으로 나뉩니다:</p>
<ul>
<li><strong>Frontmatter (YAML)</strong> — 메타데이터, 트리거, 스크립트 설정</li>
<li><strong>Markdown 본문</strong> — 에이전트에게 주입될 시스템 프롬프트</li>
</ul>
<pre><code class="language-markdown">---
name: my-assistant
description: A helpful research assistant
version: 1.0.0
tags:
  - research
  - assistant

triggers:
  - type: keyword
    keywords:
      - research
      - find
      - search
    priority: 80
---

# Research Assistant

You are now in **Research Mode**.

## Your Capabilities
- Search for information on any topic
- Analyze and summarize findings
- Provide citations and sources

## Guidelines
1. Always verify information from multiple perspectives
2. Cite your sources clearly
3. Present balanced, factual responses</code></pre>
<p>여기서 <code>triggers</code> 부분을 주목하세요. 사용자가 &quot;research&quot;, &quot;find&quot;, &quot;search&quot; 같은 키워드를 입력하면 이 스킬이 <strong>자동으로 활성화</strong>됩니다. <code>priority</code> 값이 높을수록 다른 스킬보다 우선적으로 선택됩니다.</p>
<h3 id="step-3-에이전트-코드-작성">Step 3: 에이전트 코드 작성</h3>
<p>이제 Python 코드 몇 줄이면 스킬 에이전트가 완성됩니다.</p>
<pre><code class="language-python">import asyncio
from spoon_ai.agents import SpoonReactSkill
from spoon_ai.chat import ChatBot

async def main():
    # 스킬 에이전트 생성
    agent = SpoonReactSkill(
        llm=ChatBot(llm_provider=&quot;openai&quot;, model_name=&quot;gpt-4o-mini&quot;),
        skill_paths=[&quot;./skills&quot;],       # 스킬 디렉토리 경로
        auto_trigger_skills=True         # 키워드 기반 자동 트리거 활성화
    )

    # 초기화 및 스킬 활성화
    await agent.initialize()
    await agent.activate_skill(&quot;my-assistant&quot;)

    # 스킬 컨텍스트가 반영된 상태로 실행
    response = await agent.run(&quot;Research the latest trends in AI&quot;)
    print(response)

asyncio.run(main())</code></pre>
<p><strong>이게 끝입니다.</strong> <code>activate_skill</code>을 호출하면 SKILL.md의 마크다운 본문이 에이전트의 시스템 프롬프트에 자동으로 주입됩니다. 에이전트는 이제 &quot;리서치 어시스턴트&quot;의 역할과 가이드라인을 인지한 상태로 동작합니다.</p>
<hr>
<h2 id="스크립트로-스킬에-실행력-부여하기">스크립트로 스킬에 실행력 부여하기</h2>
<p>지금까지 만든 스킬은 <strong>프롬프트만 제공하는 수준</strong>입니다. 여기에 스크립트를 추가하면 에이전트가 직접 외부 API를 호출하거나 데이터를 처리할 수 있는 <strong>실행 도구(Tool)</strong>를 갖게 됩니다.</p>
<h3 id="step-1-scripts-디렉토리-생성">Step 1: scripts 디렉토리 생성</h3>
<pre><code class="language-bash">mkdir -p ./skills/my-assistant/scripts</code></pre>
<h3 id="step-2-스크립트-작성">Step 2: 스크립트 작성</h3>
<p><code>./skills/my-assistant/scripts/web_search.py</code> 파일을 만듭니다. 스크립트는 <strong>stdin으로 입력을 받고, stdout으로 JSON을 출력</strong>하는 구조입니다.</p>
<pre><code class="language-python">#!/usr/bin/env python3
import sys
import json

def search(query: str) -&gt; dict:
    &quot;&quot;&quot;
    실제 구현 시에는 Tavily, SerpAPI, Google Custom Search 등
    원하는 검색 API를 연동하면 됩니다.
    &quot;&quot;&quot;
    return {
        &quot;status&quot;: &quot;success&quot;,
        &quot;query&quot;: query,
        &quot;results&quot;: [
            {&quot;title&quot;: &quot;Example Result&quot;, &quot;url&quot;: &quot;https://example.com&quot;}
        ]
    }

if __name__ == &quot;__main__&quot;:
    query = sys.stdin.read().strip()
    result = search(query)
    print(json.dumps(result, indent=2))</code></pre>
<h3 id="step-3-skillmd에-스크립트-등록">Step 3: SKILL.md에 스크립트 등록</h3>
<p>Frontmatter에 <code>scripts</code> 섹션을 추가합니다. 여기서 스크립트의 이름, 설명, 타입, 타임아웃 등을 정의합니다.</p>
<pre><code class="language-yaml">---
name: my-assistant
description: A helpful research assistant
version: 1.0.0

triggers:
  - type: keyword
    keywords: [research, find, search]
    priority: 80

scripts:
  enabled: true
  working_directory: ./scripts
  definitions:
    - name: web_search
      description: Search the web for information
      type: python
      file: web_search.py
      timeout: 30
---</code></pre>
<p><code>description</code>은 LLM이 이 도구를 <strong>언제 사용할지 판단하는 근거</strong>가 되므로, 명확하고 구체적으로 작성하는 것이 중요합니다.</p>
<h3 id="step-4-에이전트에서-스크립트-실행-활성화">Step 4: 에이전트에서 스크립트 실행 활성화</h3>
<pre><code class="language-python">agent = SpoonReactSkill(
    llm=ChatBot(llm_provider=&quot;openai&quot;, model_name=&quot;gpt-4o-mini&quot;),
    skill_paths=[&quot;./skills&quot;],
    scripts_enabled=True,          # 스크립트 실행 활성화
    auto_trigger_skills=True
)</code></pre>
<p>이제 에이전트는 <code>web_search</code>를 <strong>호출 가능한 도구(Tool)</strong>로 인식합니다. 사용자가 &quot;OO에 대해 조사해줘&quot;라고 말하면, 에이전트가 스스로 판단해서 <code>web_search</code> 스크립트를 실행하고 결과를 활용합니다.</p>
<hr>
<h2 id="vibe-coding--빠르게-프로토타이핑하기">Vibe Coding — 빠르게 프로토타이핑하기</h2>
<p>&quot;Vibe Coding&quot;은 SpoonOS에서 권장하는 빠른 개발 워크플로우입니다. 복잡한 설계 없이, <strong>기존 스킬을 가져와서 프롬프트를 수정하고 바로 실행</strong>하는 방식입니다.</p>
<h3 id="1단계-기존-스킬-가져오기">1단계: 기존 스킬 가져오기</h3>
<p>SpoonOS 팀에서 제공하는 <a href="https://github.com/XSpoonAi/spoon-awesome-skill">spoon-awesome-skill</a> 저장소에는 바로 사용 가능한 Web3 스킬들이 있습니다.</p>
<pre><code class="language-bash"># 프로덕션 레벨의 Web3 스킬 가져오기
git clone https://github.com/XSpoonAi/spoon-awesome-skill.git

# 필요한 스킬만 복사
cp -r spoon-awesome-skill/web3-skills/defi ./skills/</code></pre>
<h3 id="2단계-프롬프트-커스터마이징">2단계: 프롬프트 커스터마이징</h3>
<p>복사한 <code>./skills/defi/SKILL.md</code>를 열고 마크다운 본문을 수정합니다. <strong>마크다운 본문이 곧 에이전트의 행동을 결정</strong>하므로, 이 부분을 바꾸는 것만으로도 에이전트의 성격과 능력이 달라집니다.</p>
<h3 id="3단계-ai에게-맡기기">3단계: AI에게 맡기기</h3>
<p><code>auto_trigger_skills=True</code>를 설정하면, 사용자의 입력에 따라 적절한 스킬이 자동으로 활성화됩니다. 복잡한 if-else 분기 로직을 직접 짤 필요가 없습니다.</p>
<pre><code class="language-python">agent = SpoonReactSkill(
    skill_paths=[&quot;./skills&quot;],
    scripts_enabled=True,
    auto_trigger_skills=True,
    max_auto_skills=3               # 동시에 최대 3개 스킬까지 자동 활성화
)

await agent.initialize()

# 에이전트가 사용자 입력을 보고 알아서 적절한 스킬을 활성화합니다
response = await agent.run(&quot;Get a quote to swap 1 ETH for USDC on Uniswap&quot;)</code></pre>
<h3 id="4단계-반복하고-배포하기">4단계: 반복하고 배포하기</h3>
<ul>
<li>에이전트의 응답을 보면서 <strong>프롬프트를 미세 조정</strong></li>
<li>필요한 스크립트를 <strong>추가하거나 수정</strong></li>
<li>복잡한 오케스트레이션 코드 없이 <strong>바로 배포</strong></li>
</ul>
<p>이것이 Vibe Coding의 핵심입니다. 완벽한 설계보다 <strong>빠른 반복</strong>에 초점을 맞추세요.</p>
<hr>
<h2 id="실전-예시-web3-에이전트-만들기">실전 예시: Web3 에이전트 만들기</h2>
<p><a href="https://github.com/XSpoonAi/spoon-awesome-skill">spoon-awesome-skill</a> 저장소에서 제공하는 스킬들을 활용하면 Web3 에이전트를 빠르게 구축할 수 있습니다.</p>
<h3 id="사용-가능한-web3-스킬-목록">사용 가능한 Web3 스킬 목록</h3>
<table>
<thead>
<tr>
<th>스킬</th>
<th>설명</th>
<th>주요 스크립트</th>
</tr>
</thead>
<tbody><tr>
<td><strong>defi</strong></td>
<td>DeFi 프로토콜 상호작용</td>
<td><code>uniswap_quote.py</code>, <code>aave_positions.py</code></td>
</tr>
<tr>
<td><strong>onchain-analysis</strong></td>
<td>온체인 데이터 분석</td>
<td><code>etherscan_address.py</code>, <code>gas_tracker.py</code></td>
</tr>
<tr>
<td><strong>nft</strong></td>
<td>NFT 마켓 분석</td>
<td><code>opensea_collection.py</code>, <code>nft_rarity.py</code></td>
</tr>
<tr>
<td><strong>wallet</strong></td>
<td>지갑 조회/관리</td>
<td><code>wallet_balance.py</code>, <code>portfolio_tracker.py</code></td>
</tr>
<tr>
<td><strong>solana</strong></td>
<td>Solana 생태계</td>
<td><code>jupiter_quote.py</code>, <code>solana_balance.py</code></td>
</tr>
<tr>
<td><strong>neo</strong></td>
<td>Neo N3 생태계</td>
<td><code>neo_balance.py</code>, <code>neo_transfer.py</code></td>
</tr>
</tbody></table>
<h3 id="환경-변수-설정">환경 변수 설정</h3>
<p>Web3 스킬의 스크립트들은 외부 API를 호출하므로, 필요한 API 키를 환경 변수로 설정해야 합니다.</p>
<pre><code class="language-bash">export ETHERSCAN_API_KEY=&quot;your_key&quot;
export RPC_URL=&quot;https://eth.llamarpc.com&quot;</code></pre>
<h3 id="web3-에이전트-구현">Web3 에이전트 구현</h3>
<p>실제 프로젝트에서는 <code>SpoonReactSkill</code>을 상속해서 커스텀 에이전트 클래스를 만드는 것이 좋습니다.</p>
<pre><code class="language-python">from spoon_ai.agents import SpoonReactSkill
from spoon_ai.chat import ChatBot

class Web3Agent(SpoonReactSkill):
    &quot;&quot;&quot;spoon-awesome-skill의 Web3 스킬을 활용하는 에이전트.&quot;&quot;&quot;

    def __init__(self, **kwargs):
        # 기본 설정을 미리 잡아두면 사용할 때 편합니다
        kwargs.setdefault(&#39;skill_paths&#39;, [&#39;./skills&#39;])
        kwargs.setdefault(&#39;scripts_enabled&#39;, True)
        kwargs.setdefault(&#39;auto_trigger_skills&#39;, True)
        kwargs.setdefault(&#39;max_auto_skills&#39;, 3)
        super().__init__(**kwargs)

    async def initialize(self, __context=None):
        await super().initialize(__context)

        # 사용 가능한 스킬 목록 확인
        print(f&quot;Available skills: {self.list_skills()}&quot;)

        # 자주 쓰는 스킬은 미리 활성화
        if &quot;defi&quot; in self.list_skills():
            await self.activate_skill(&quot;defi&quot;)

async def main():
    agent = Web3Agent(
        llm=ChatBot(llm_provider=&quot;openai&quot;, model_name=&quot;gpt-4o-mini&quot;)
    )
    await agent.initialize()

    # DeFi 관련 질의
    response = await agent.run(&quot;Get a quote to swap 1 ETH for USDC on Uniswap&quot;)
    print(response)</code></pre>
<p>이렇게 하면 에이전트가 DeFi 스킬의 프롬프트와 도구(uniswap_quote 스크립트 등)를 모두 갖춘 상태에서 사용자 요청을 처리합니다.</p>
<hr>
<h2 id="skill-manager-api-활용법">Skill Manager API 활용법</h2>
<p><code>SpoonReactSkill</code>은 스킬을 관리하기 위한 다양한 API를 제공합니다. 주요 기능별로 살펴보겠습니다.</p>
<h3 id="스킬-조회-및-탐색">스킬 조회 및 탐색</h3>
<pre><code class="language-python"># 사용 가능한 모든 스킬 목록
skills = agent.list_skills()
print(f&quot;Available: {skills}&quot;)

# 특정 스킬의 상세 정보 확인
info = agent.skill_manager.get_skill_info(&quot;defi&quot;)
print(f&quot;Description: {info[&#39;description&#39;]}&quot;)
print(f&quot;Triggers: {info[&#39;triggers&#39;]}&quot;)</code></pre>
<h3 id="스킬-활성화--비활성화">스킬 활성화 / 비활성화</h3>
<pre><code class="language-python"># 활성화 시 추가 컨텍스트를 전달할 수 있습니다
await agent.activate_skill(&quot;defi&quot;, {&quot;chain&quot;: &quot;ethereum&quot;})

# 활성 상태 확인
is_active = agent.skill_manager.is_active(&quot;defi&quot;)

# 현재 활성화된 모든 스킬 목록
active = agent.skill_manager.get_active_skills()

# 더 이상 필요 없으면 비활성화
await agent.deactivate_skill(&quot;defi&quot;)</code></pre>
<h3 id="도구tool-관리">도구(Tool) 관리</h3>
<pre><code class="language-python"># 활성화된 모든 스킬의 도구 목록 가져오기
tools = agent.skill_manager.get_active_tools()
for tool in tools:
    print(f&quot;Tool: {tool.name} - {tool.description}&quot;)

# 활성 스킬들의 프롬프트 컨텍스트 합치기
context = agent.skill_manager.get_active_context()</code></pre>
<h3 id="스크립트-직접-실행">스크립트 직접 실행</h3>
<p>에이전트를 거치지 않고 특정 스크립트를 직접 호출할 수도 있습니다. 디버깅이나 테스트 시 유용합니다.</p>
<pre><code class="language-python">result = await agent.skill_manager.execute_script(
    &quot;defi&quot;,                     # 스킬 이름
    &quot;uniswap_quote&quot;,            # 스크립트 이름
    input_text=&#39;{&quot;token_in&quot;: &quot;ETH&quot;, &quot;token_out&quot;: &quot;USDC&quot;, &quot;amount&quot;: &quot;1&quot;}&#39;
)

if result.success:
    print(f&quot;Output: {result.stdout}&quot;)
else:
    print(f&quot;Error: {result.error}&quot;)</code></pre>
<h3 id="통계-확인">통계 확인</h3>
<pre><code class="language-python">stats = agent.get_skill_stats()
print(f&quot;Active skills: {stats[&#39;active_skills&#39;]}&quot;)
print(f&quot;Scripts enabled: {stats[&#39;scripts_enabled&#39;]}&quot;)
print(f&quot;Available skills: {stats[&#39;available_skills&#39;]}&quot;)</code></pre>
<hr>
<h2 id="실전에서-꼭-지켜야-할-best-practices">실전에서 꼭 지켜야 할 Best Practices</h2>
<h3 id="1-스킬은-작고-집중적으로-만들기">1. 스킬은 작고 집중적으로 만들기</h3>
<p>하나의 스킬이 모든 것을 하려고 하면 프롬프트가 길어지고, 에이전트의 판단력이 떨어집니다. <strong>하나의 스킬은 하나의 역할만</strong> 담당하도록 하고, 필요하면 여러 스킬을 조합하세요.</p>
<pre><code class="language-python"># 좋은 예: 역할별로 분리된 스킬을 조합
await agent.activate_skill(&quot;defi&quot;)
await agent.activate_skill(&quot;wallet&quot;)

# 나쁜 예: 하나의 거대한 스킬에 모든 것을 넣기
# await agent.activate_skill(&quot;everything-web3&quot;)  # 이러지 마세요</code></pre>
<h3 id="2-트리거-우선순위를-전략적으로-설정하기">2. 트리거 우선순위를 전략적으로 설정하기</h3>
<p>여러 스킬의 트리거 키워드가 겹칠 수 있습니다. <code>priority</code> 값을 적절히 조절해서 충돌을 방지하세요.</p>
<pre><code class="language-yaml">triggers:
  # 구체적인 키워드에는 높은 우선순위
  - type: keyword
    keywords: [uniswap, aave]
    priority: 90

  # 일반적인 키워드에는 낮은 우선순위
  - type: keyword
    keywords: [swap, trade]
    priority: 70</code></pre>
<p>예를 들어 사용자가 &quot;uniswap에서 swap해줘&quot;라고 말하면, &quot;uniswap&quot; 키워드(priority 90)가 먼저 매칭되어 해당 스킬이 활성화됩니다.</p>
<h3 id="3-에러-처리는-반드시-하기">3. 에러 처리는 반드시 하기</h3>
<p>스킬 활성화가 실패할 수 있는 상황(잘못된 이름, 누락된 파일 등)에 대비하세요.</p>
<pre><code class="language-python">try:
    await agent.activate_skill(&quot;my-skill&quot;)
except ValueError as e:
    print(f&quot;Skill activation failed: {e}&quot;)
    # 대체 스킬로 폴백하거나, 사용자에게 알림</code></pre>
<h3 id="4-스크립트-입력값은-항상-검증하기">4. 스크립트 입력값은 항상 검증하기</h3>
<p>스크립트는 외부에서 입력을 받으므로, 잘못된 데이터가 들어올 수 있습니다. 방어적으로 코딩하세요.</p>
<pre><code class="language-python">import json
import sys

def main():
    try:
        input_data = json.loads(sys.stdin.read())
    except json.JSONDecodeError:
        print(json.dumps({&quot;error&quot;: &quot;Invalid JSON input&quot;}))
        sys.exit(1)

    # 필수 필드 확인
    if &quot;query&quot; not in input_data:
        print(json.dumps({&quot;error&quot;: &quot;Missing required field: query&quot;}))
        sys.exit(1)

    # 정상 처리...</code></pre>
<h3 id="5-프롬프트-전용-스킬은-스크립트를-비활성화하기">5. 프롬프트 전용 스킬은 스크립트를 비활성화하기</h3>
<p>모든 스킬이 스크립트를 필요로 하는 건 아닙니다. 프롬프트만 제공하는 스킬이라면 명시적으로 꺼두세요.</p>
<pre><code class="language-yaml">scripts:
  enabled: false  # 이 스킬은 프롬프트만 제공합니다</code></pre>
<hr>
<h2 id="문제가-생겼을-때--트러블슈팅-가이드">문제가 생겼을 때 — 트러블슈팅 가이드</h2>
<h3 id="skill-not-found-에러">&quot;Skill not found&quot; 에러</h3>
<pre><code>ValueError: Skill &#39;my-skill&#39; not found</code></pre><p>다음을 확인하세요:</p>
<ol>
<li>스킬 디렉토리가 <code>skill_paths</code>에 지정된 경로 안에 있는지</li>
<li>디렉토리 안에 <code>SKILL.md</code> 파일이 있는지</li>
<li>SKILL.md의 <code>name</code> 필드가 <code>activate_skill()</code>에 전달한 이름과 일치하는지</li>
</ol>
<h3 id="스크립트-실행-실패">스크립트 실행 실패</h3>
<pre><code>ScriptResult(success=False, error=&quot;Script timed out&quot;)</code></pre><p>다음을 확인하세요:</p>
<ol>
<li>SKILL.md의 <code>timeout</code> 값이 충분한지 (외부 API 호출이 있다면 더 길게)</li>
<li>스크립트 파일에 실행 권한이 있는지 (<code>chmod +x</code>)</li>
<li>스크립트가 단독으로 실행했을 때 정상 동작하는지</li>
</ol>
<h3 id="자동-트리거가-작동하지-않을-때">자동 트리거가 작동하지 않을 때</h3>
<p>다음을 순서대로 확인하세요:</p>
<ol>
<li>에이전트 생성 시 <code>auto_trigger_skills=True</code>가 설정되어 있는지</li>
<li>SKILL.md에 유효한 <code>triggers</code>가 정의되어 있는지</li>
<li>트리거의 <code>priority</code>가 다른 스킬보다 충분히 높은지</li>
<li><code>max_auto_skills</code> 한도에 도달하지 않았는지</li>
</ol>
<hr>
<h2 id="마무리-및-다음-단계">마무리 및 다음 단계</h2>
<p>SpoonOS의 스킬 시스템을 사용하면 <strong>마크다운 파일 하나로 에이전트의 역할을 정의하고, 간단한 스크립트로 실행 능력을 부여</strong>할 수 있습니다. 핵심은 다음 세 가지입니다:</p>
<ul>
<li><strong>SKILL.md</strong> = 에이전트의 역할 정의 + 도구 설정</li>
<li><strong>scripts/</strong> = 에이전트가 실행할 수 있는 도구</li>
<li><strong>auto_trigger</strong> = 사용자 입력에 따라 알아서 적절한 스킬 활성화</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI 에이전트가 스스로 결제까지 한다고? x402 프로토콜 체험기]]></title>
            <link>https://velog.io/@neo_blockchain/AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%EA%B0%80-%EC%8A%A4%EC%8A%A4%EB%A1%9C-%EA%B2%B0%EC%A0%9C%EA%B9%8C%EC%A7%80-%ED%95%9C%EB%8B%A4%EA%B3%A0-x402-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C-%EC%B2%B4%ED%97%98%EA%B8%B0</link>
            <guid>https://velog.io/@neo_blockchain/AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%EA%B0%80-%EC%8A%A4%EC%8A%A4%EB%A1%9C-%EA%B2%B0%EC%A0%9C%EA%B9%8C%EC%A7%80-%ED%95%9C%EB%8B%A4%EA%B3%A0-x402-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C-%EC%B2%B4%ED%97%98%EA%B8%B0</guid>
            <pubDate>Fri, 06 Mar 2026 03:32:27 GMT</pubDate>
            <description><![CDATA[<p>요즘 AI 에이전트가 단순히 질문에 답하는 걸 넘어서, <strong>스스로 웹을 탐색하고 결제까지 하는 세상</strong>이 오고 있습니다. 오늘은 그 미래를 직접 체험해볼 수 있는 x402 에이전트 데모를 소개해드릴게요.</p>
<h2 id="x402가-뭔가요">x402가 뭔가요?</h2>
<p>혹시 웹 브라우징을 하다가 HTTP 상태 코드 <code>402 Payment Required</code>를 본 적 있으신가요? 사실 이 코드는 HTTP 스펙에 오래전부터 존재했지만, &quot;미래를 위해 예약됨&quot; 상태로 거의 쓰이지 않았습니다.</p>
<p><strong>x402 프로토콜</strong>은 바로 이 <code>402</code> 응답을 활용합니다. 웹 서버가 &quot;이 콘텐츠를 보려면 결제가 필요해요&quot;라고 응답하면, 클라이언트(이 경우 AI 에이전트)가 자동으로 USDC 같은 스테이블코인으로 소액 결제를 처리하고, 콘텐츠를 받아오는 구조입니다.</p>
<p>쉽게 말해, <strong>AI가 유료 콘텐츠 앞에서 알아서 지갑을 꺼내 결제하는 것</strong>이죠.</p>
<h2 id="직접-해보기-전에-준비할-것들">직접 해보기 전에 준비할 것들</h2>
<p>데모를 실행하려면 몇 가지를 먼저 세팅해야 합니다. 하나씩 살펴볼게요.</p>
<h3 id="1-프로젝트-의존성-설치">1. 프로젝트 의존성 설치</h3>
<p>SpoonOS 프레임워크를 사용하므로, 먼저 의존성을 설치합니다.</p>
<pre><code class="language-bash">uv pip install -r requirements.txt</code></pre>
<blockquote>
<p><code>uv</code>는 Rust로 만들어진 초고속 Python 패키지 매니저입니다. 아직 안 써보셨다면 이번 기회에 한번 사용해보세요!</p>
</blockquote>
<h3 id="2-환경-변수-설정">2. 환경 변수 설정</h3>
<p><code>core/.env</code> 파일을 만들고, 아래 값들을 채워 넣습니다.</p>
<table>
<thead>
<tr>
<th>환경 변수</th>
<th>설명</th>
<th>필수 여부</th>
</tr>
</thead>
<tbody><tr>
<td><code>OPENAI_API_KEY</code></td>
<td>LLM 호출에 사용할 OpenAI API 키</td>
<td>필수</td>
</tr>
<tr>
<td><code>PRIVATE_KEY</code></td>
<td><code>0x</code>로 시작하는 지갑 프라이빗 키 (USDC 0.01 이상 보유 필요)</td>
<td>필수 (Turnkey 미사용 시)</td>
</tr>
<tr>
<td><code>X402_RECEIVER_ADDRESS</code></td>
<td>결제를 받을 주소 (보통 본인 지갑 주소와 동일)</td>
<td>필수</td>
</tr>
<tr>
<td><code>X402_FACILITATOR_URL</code></td>
<td>커스텀 facilitator 서버 URL</td>
<td>선택</td>
</tr>
<tr>
<td><code>X402_DEFAULT_NETWORK</code></td>
<td>사용할 블록체인 네트워크</td>
<td>선택</td>
</tr>
<tr>
<td><code>X402_DEMO_URL</code></td>
<td>데모 대상 URL (기본값: x402.org의 보호 페이지)</td>
<td>선택</td>
</tr>
</tbody></table>
<h3 id="3-테스트용-usdc-받기">3. 테스트용 USDC 받기</h3>
<p>실제 돈이 들지 않으니 걱정 마세요! Circle에서 제공하는 테스트넷 Faucet을 통해 무료로 USDC를 받을 수 있습니다.</p>
<p><a href="https://faucet.circle.com/">Circle Faucet 바로가기</a></p>
<p>0.01 USDC면 충분합니다. Faucet 페이지에서 지갑 주소를 입력하고 요청하면 금방 들어옵니다.</p>
<h2 id="데모-실행하기">데모 실행하기</h2>
<p>준비가 끝났으면, 딱 한 줄이면 됩니다.</p>
<pre><code class="language-bash">uv run python examples/x402_agent_demo.py</code></pre>
<p>실행하면 콘솔에 로그가 주르륵 올라오는데, 내부적으로 상당히 흥미로운 일이 벌어집니다.</p>
<h3 id="에이전트의-동작-흐름">에이전트의 동작 흐름</h3>
<ol>
<li><p><strong>사전 정보 출력</strong> — 먼저 서명에 사용할 지갑 정보와 접근할 대상 URL(<code>https://www.x402.org/protected</code>)을 보여줍니다.</p>
</li>
<li><p><strong>ReAct 루프 시작</strong> — <code>SpoonReactAI</code> 에이전트가 &quot;생각 → 행동 → 관찰&quot; 사이클을 반복하며 문제를 풀어나갑니다.</p>
<ul>
<li><p><strong>첫 번째 시도</strong>: <code>web_scraper</code> 도구로 보호된 페이지에 접근합니다. 당연히 결제 없이는 콘텐츠를 못 받겠죠? 서버가 <code>402 Payment Required</code> 응답과 함께 결제 조건(얼마를, 어디로, 어떤 토큰으로)을 알려줍니다.</p>
</li>
<li><p><strong>결제 처리</strong>: 에이전트가 상황을 파악하고, <code>x402_paywalled_request</code> 도구를 호출합니다. 이 도구가 0.01 USDC 결제에 서명하고, 결제 증명을 HTTP 헤더에 담아 다시 요청을 보냅니다.</p>
</li>
<li><p><strong>콘텐츠 획득</strong>: 결제가 검증되면 서버가 보호된 콘텐츠(이 데모에서는 SoundCloud 임베드)를 돌려줍니다.</p>
</li>
</ul>
</li>
<li><p><strong>결과 확인</strong> — 콘솔에서 다음 정보들을 확인할 수 있습니다:</p>
<ul>
<li>각 도구 호출의 상세 트레이스</li>
<li>x402 v2 규격의 <code>PAYMENT-SIGNATURE</code> 헤더 값</li>
<li><code>PAYMENT-RESPONSE</code> 헤더에서 디코딩한 정산 영수증 (트랜잭션 해시, 결제자 주소, 네트워크 정보 등)</li>
</ul>
</li>
</ol>
<p>AI가 혼자서 &quot;어? 결제가 필요하네 → 결제하자 → 콘텐츠 받았다!&quot; 과정을 밟는 걸 눈으로 확인할 수 있어서 꽤 인상적입니다.</p>
<h2 id="문제가-생겼을-때">문제가 생겼을 때</h2>
<p>실행 중 에러가 나더라도 당황하지 마세요. 흔히 발생하는 문제와 해결법을 정리해두었습니다.</p>
<h3 id="invalid_exact_evm_payload_signature-에러">&quot;invalid_exact_evm_payload_signature&quot; 에러</h3>
<p>결제 서명이 유효하지 않다는 뜻입니다. 주로 다음 원인 중 하나입니다:</p>
<ul>
<li>토큰 종류나 네트워크가 페이월 서버가 기대하는 것과 다른 경우</li>
<li>지갑에 USDC 잔액이 부족한 경우</li>
<li>nonce 값이 꼬인 경우</li>
</ul>
<p><strong>해결</strong>: 페이월 응답의 <code>asset</code>, <code>pay_to</code> 필드가 설정과 일치하는지 확인하고, 지갑에 잔액이 있는지 다시 체크해보세요.</p>
<h3 id="configuration-error-api-key-is-required-for-provider--에러">&quot;Configuration error: API key is required for provider ...&quot; 에러</h3>
<p>LLM API 키가 설정되지 않았다는 뜻입니다.</p>
<p><strong>해결</strong>: <code>.env</code> 파일에 <code>OPENAI_API_KEY</code>가 제대로 들어가 있는지 확인하세요.</p>
<h3 id="x402-configuration-error-turnkey-signing-identity-missing-에러">&quot;x402 configuration error: Turnkey signing identity missing&quot; 에러</h3>
<p>Turnkey(원격 서명 서비스)를 사용하도록 설정했는데, 필요한 인증 정보가 없는 경우입니다.</p>
<p><strong>해결</strong>: Turnkey를 사용하지 않는다면 <code>X402_USE_TURNKEY=0</code>으로 비활성화하세요. 사용한다면 <code>TURNKEY_SIGN_WITH</code> 등 필요한 값들을 채워 넣으면 됩니다.</p>
<h2 id="더-나아가기">더 나아가기</h2>
<p>데모를 성공적으로 돌려봤다면, 다음 단계도 고려해보세요.</p>
<ul>
<li><p><strong>내 에이전트에 결제 기능 붙이기</strong> — <code>x402_paywalled_request</code> 도구를 여러분의 에이전트에 그대로 가져다 쓸 수 있습니다. 유료 API나 프리미엄 콘텐츠에 자동으로 접근하는 에이전트를 만들어보세요.</p>
</li>
<li><p><strong>내 에이전트를 유료로 공개하기</strong> — 반대로, 여러분이 만든 에이전트를 x402 페이월 뒤에 두는 것도 가능합니다. <code>python -m spoon_ai.payments.app</code>으로 페이월 라우터를 띄우면, 외부 사용자가 USDC로 결제해야만 에이전트를 호출할 수 있게 됩니다. AI 에이전트로 수익을 만드는 새로운 비즈니스 모델이 될 수도 있겠죠?</p>
</li>
</ul>
<hr>
<p>AI 에이전트가 자율적으로 결제하고 콘텐츠를 소비하는 세상, 어떻게 생각하시나요? x402 같은 프로토콜이 보편화되면, 에이전트끼리 서로 서비스를 사고파는 <strong>에이전트 경제(Agent Economy)</strong> 가 정말 현실이 될지도 모르겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SpoonOS로 암호화폐 시장을 자동 분석해보자]]></title>
            <link>https://velog.io/@neo_blockchain/SpoonOS%EB%A1%9C-%EC%95%94%ED%98%B8%ED%99%94%ED%8F%90-%EC%8B%9C%EC%9E%A5%EC%9D%84-%EC%9E%90%EB%8F%99-%EB%B6%84%EC%84%9D%ED%95%B4%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@neo_blockchain/SpoonOS%EB%A1%9C-%EC%95%94%ED%98%B8%ED%99%94%ED%8F%90-%EC%8B%9C%EC%9E%A5%EC%9D%84-%EC%9E%90%EB%8F%99-%EB%B6%84%EC%84%9D%ED%95%B4%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Fri, 27 Feb 2026 04:22:07 GMT</pubDate>
            <description><![CDATA[<p>&quot;요즘 코인 시장 어때?&quot; 라는 질문에 답하려면, 보통은 차트를 열고, 뉴스를 찾아보고, 지표를 하나하나 확인해야 하죠. 꽤 번거로운 작업입니다.</p>
<p>이 예제에서는 SpoonOS의 <strong>선언적 그래프 시스템</strong>을 활용해서, 이 과정을 통째로 자동화하는 방법을 보여드립니다. 바이낸스에서 실시간 데이터를 가져오고, 기술적 지표를 계산하고, 뉴스까지 분석한 다음, LLM이 최종 투자 리포트까지 만들어주는 파이프라인을 구축해볼 거예요.</p>
<p>직접 코드를 한 줄 한 줄 짜는 게 아니라, <strong>그래프 노드를 선언하는 것만으로</strong> 이 모든 흐름이 만들어진다는 게 핵심입니다.</p>
<h4 id="전체-흐름을-한눈에-보면">전체 흐름을 한눈에 보면</h4>
<p>아래 다이어그램이 이 파이프라인의 전체 구조입니다. 시작부터 끝까지 데이터가 어떻게 흘러가는지 살펴보세요.</p>
<pre><code class="language-mermaid">graph TD
    A[시작] --&gt; B[바이낸스 시장 데이터 수집]
    B --&gt; C[거래량 상위 10개 페어 선정]
    C --&gt; D[토큰 리스트 준비]

    D --&gt; E[병렬 토큰 분석]
    E --&gt; F1[토큰 1: 기술적 분석 + 뉴스 분석]
    E --&gt; F2[토큰 2: 기술적 분석 + 뉴스 분석]
    E --&gt; F3[토큰 3: 기술적 분석 + 뉴스 분석]
    E --&gt; F4[토큰 4: 기술적 분석 + 뉴스 분석]
    E --&gt; F5[토큰 5: 기술적 분석 + 뉴스 분석]
    E --&gt; F6[토큰 6: 기술적 분석 + 뉴스 분석]
    E --&gt; F7[토큰 7: 기술적 분석 + 뉴스 분석]
    E --&gt; F8[토큰 8: 기술적 분석 + 뉴스 분석]
    E --&gt; F9[토큰 9: 기술적 분석 + 뉴스 분석]
    E --&gt; F10[토큰 10: 기술적 분석 + 뉴스 분석]

    F1 --&gt; G[전체 결과 집계]
    F2 --&gt; G
    F3 --&gt; G
    F4 --&gt; G
    F5 --&gt; G
    F6 --&gt; G
    F7 --&gt; G
    F8 --&gt; G
    F9 --&gt; G
    F10 --&gt; G

    G --&gt; H[LLM 최종 종합 분석]
    H --&gt; I[시장 리포트 생성]
    I --&gt; J[종료]

    style A fill:#e1f5fe
    style J fill:#c8e6c9
    style E fill:#fff3e0
    style G fill:#fce4ec

    subgraph &quot;기술적 분석&quot;
        F1
        F2
        F3
        F4
        F5
        F6
        F7
        F8
        F9
        F10
    end</code></pre>
<p>간단히 정리하면 이렇습니다:</p>
<ol>
<li><strong>바이낸스에서 실시간 시장 데이터를 가져옵니다</strong> — 현재 거래되고 있는 모든 페어의 가격, 거래량, 변동률 등을 수집해요.</li>
<li><strong>거래량 기준 상위 10개 토큰을 자동으로 골라냅니다</strong> — 사람이 &quot;어떤 코인을 분석할까?&quot; 고민할 필요 없이, LLM이 시장 상황을 보고 직접 판단합니다.</li>
<li><strong>10개 토큰을 동시에 병렬로 분석합니다</strong> — 각 토큰마다 기술적 지표(RSI, MACD, EMA)를 계산하고, 관련 뉴스까지 검색해서 종합합니다. 병렬 처리라 10개를 분석해도 시간이 오래 걸리지 않아요.</li>
<li><strong>LLM이 모든 분석 결과를 모아서 최종 리포트를 만들어줍니다</strong> — 단순히 숫자를 나열하는 게 아니라, &quot;지금 시장이 어떤 상태이고, 어떤 전략이 유효한지&quot;를 사람이 읽기 편한 형태로 정리해줍니다.</li>
</ol>
<h4 id="이-예제에서-눈여겨볼-점">이 예제에서 눈여겨볼 점</h4>
<p><strong>그래프만 선언하면 알아서 돌아간다</strong></p>
<p>가장 큰 특징은, 복잡한 제어 흐름을 직접 코딩하지 않아도 된다는 점입니다. <code>GraphTemplate</code>으로 전체 구조를 정의하고, <code>NodeSpec</code>으로 각 단계가 뭘 하는지 선언하고, <code>EdgeSpec</code>으로 단계 간 연결을 잡아주면 끝이에요. 나머지는 SpoonOS 그래프 엔진이 알아서 실행 순서를 잡고, 병렬 처리가 가능한 부분은 자동으로 병렬화해줍니다.</p>
<pre><code class="language-python"># 이런 식으로 노드를 선언하기만 하면 됩니다
template = GraphTemplate(
    nodes=[
        NodeSpec(name=&quot;fetch_market_data&quot;, ...),
        NodeSpec(name=&quot;select_top_tokens&quot;, ...),
        NodeSpec(name=&quot;analyze_token&quot;, ...),
        NodeSpec(name=&quot;generate_report&quot;, ...),
    ],
    edges=[
        EdgeSpec(source=&quot;fetch_market_data&quot;, target=&quot;select_top_tokens&quot;),
        EdgeSpec(source=&quot;select_top_tokens&quot;, target=&quot;analyze_token&quot;),
        EdgeSpec(source=&quot;analyze_token&quot;, target=&quot;generate_report&quot;),
    ]
)</code></pre>
<p><strong>HighLevelGraphAPI가 파라미터를 알아서 채워준다</strong></p>
<p>&quot;BTC 시장 분석해줘&quot; 같은 자연어 쿼리를 넣으면, <code>HighLevelGraphAPI</code>가 쿼리를 해석해서 어떤 토큰을 분석할지, 어떤 타임프레임을 쓸지 등의 파라미터를 자동으로 추론합니다. 일일이 설정값을 지정하지 않아도 그냥 자연어로 말하면 되는 거죠.</p>
<p><strong>실제 API를 사용한 실시간 분석</strong></p>
<p>샘플 데이터가 아니라 실제 바이낸스 API와 PowerData 툴킷을 통해 라이브 데이터를 가져옵니다. RSI, MACD, EMA 같은 기술적 지표도 실시간 캔들 데이터로 직접 계산하고, Tavily 검색 엔진으로 최신 뉴스까지 가져와서 분석에 반영해요.</p>
<p><strong>LLM이 모든 의사결정의 중심에 있다</strong></p>
<p>어떤 토큰을 분석할지 고르는 것부터, 기술적 지표를 해석하는 것, 뉴스의 영향을 판단하는 것, 최종 추천을 내리는 것까지 — 파이프라인의 모든 주요 판단을 LLM이 담당합니다. 사람이 규칙을 하드코딩하는 게 아니라, LLM이 맥락을 이해하고 유연하게 판단하는 구조입니다.</p>
<h4 id="실행해보기">실행해보기</h4>
<p>먼저 API 키를 준비합니다. LLM을 호출하기 위한 키와, 뉴스 검색을 위한 키가 필요해요.</p>
<pre><code class="language-bash"># 환경 변수 설정
export OPENAI_API_KEY=&quot;your-openai-api-key&quot;          # LLM 호출용
export ANTHROPIC_API_KEY=&quot;your-anthropic-api-key&quot;   # 대체 LLM
export TAVILY_API_KEY=&quot;your-tavily-api-key&quot;       # 뉴스 검색용</code></pre>
<p>그다음은 간단합니다.</p>
<pre><code class="language-bash"># 예제 디렉토리로 이동
cd spoon-core/example

# 의존성 설치
pip install -r requirements.txt

# 실행!
python graph_crypto_analysis.py</code></pre>
<p>실행하면 콘솔에 각 단계의 진행 상황이 출력되고, 최종적으로 이런 형태의 리포트가 생성됩니다.</p>
<pre><code>시장 분석 리포트
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

분석 대상 토큰: BTC, ETH, SOL, ADA

기술적 분석:
• BTC/USDT: 강세 모멘텀, RSI: 68, MACD 골든크로스
• ETH/USDT: 횡보 구간, 주요 저항선 접근 중
• SOL/USDT: 강한 상승 추세, 전고점 돌파
• ADA/USDT: 회복 국면, 거래량 모멘텀 긍정적

투자 추천:
• 단기: BTC와 SOL의 모멘텀 매매 고려
• 중기: 현재 횡보 구간에서 ETH 보유 유지
• 리스크 평가: 향후 24~48시간 내 중간 수준의 변동성 예상

시장 전망:
현재 시장은 BTC 주도의 강한 상승 모멘텀을 보이고 있으며...</code></pre><h4 id="이-예제에서-배울-수-있는-것">이 예제에서 배울 수 있는 것</h4>
<p>이 하나의 예제를 통해 SpoonOS의 핵심적인 패턴들을 경험할 수 있습니다.</p>
<ul>
<li><strong>선언적 그래프 빌딩</strong> — <code>GraphTemplate</code>, <code>NodeSpec</code>, <code>EdgeSpec</code>만으로 복잡한 워크플로우를 깔끔하게 정의하는 방법</li>
<li><strong>자연어 기반 파라미터 추론</strong> — <code>HighLevelGraphAPI</code>가 사용자의 의도를 파악해서 자동으로 설정을 잡아주는 구조</li>
<li><strong>병렬 처리</strong> — 독립적인 분석 작업을 동시에 실행해서 전체 처리 시간을 줄이는 방법</li>
<li><strong>LLM 통합 워크플로우</strong> — 단순한 API 호출이 아니라, 워크플로우의 의사결정 자체를 LLM에게 맡기는 패턴</li>
<li><strong>실시간 외부 API 연동</strong> — 바이낸스, 검색 엔진 등 외부 서비스를 그래프 노드로 자연스럽게 통합하는 방법</li>
<li><strong>에러 처리와 폴백</strong> — 금융 데이터처럼 신뢰성이 중요한 영역에서의 견고한 에러 핸들링</li>
</ul>
<h4 id="소스-코드">소스 코드</h4>
<ul>
<li><strong>메인 예제</strong>: <a href="https://github.com/XSpoonAi/spoon-core/blob/main/examples/graph_crypto_analysis.py">graph_crypto_analysis.py</a></li>
<li><strong>관련 모듈</strong>:<ul>
<li><code>spoon_ai/graph/builder.py</code> — 선언적 템플릿 및 고수준 API</li>
<li><code>toolkit/spoon_toolkits/crypto/crypto_powerdata/tools.py</code> — PowerData 연동 헬퍼</li>
<li><code>spoon_ai/graph/</code> — 코어 엔진 및 모니터링 유틸리티</li>
<li><a href="https://xspoonai.github.io/docs/core-concepts/tools/">도구 시스템 문서</a></li>
</ul>
</li>
</ul>
<h4 id="더-알아보기">더 알아보기</h4>
<p>이 예제는 SpoonOS 그래프 시스템의 한 가지 활용 사례일 뿐입니다. 같은 구조로 뉴스 요약 봇, 데이터 수집 파이프라인, 멀티 에이전트 협업 시스템 등 다양한 워크플로우를 만들 수 있어요. 핵심은 항상 같습니다 — <strong>그래프를 선언하고, 나머지는 SpoonOS에게 맡기세요.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[첫 번째 AI 에이전트 만들기]]></title>
            <link>https://velog.io/@neo_blockchain/%EC%B2%AB-%EB%B2%88%EC%A7%B8-AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@neo_blockchain/%EC%B2%AB-%EB%B2%88%EC%A7%B8-AI-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Fri, 20 Feb 2026 04:36:35 GMT</pubDate>
            <description><![CDATA[<p>간단한 설정부터 시작해서, 단 몇 분 만에 완전히 동작하는 AI 에이전트를 만들어 보겠습니다.</p>
<blockquote>
<p><strong>💡 AI 기반 개발이 처음이신가요?</strong></p>
<p><a href="https://xspoonai.github.io/docs/how-to-guides/vibe-coding/">Vibe Coding 가이드</a>를 참고하시면 Cursor나 Windsurf 같은 AI 코딩 도구를 활용하여 SpoonOS 에이전트를 더 효율적으로 만드는 방법을 배울 수 있습니다.</p>
</blockquote>
<h2 id="사전-준비-사항">사전 준비 사항</h2>
<p>이 예제를 따라하려면 다음이 필요합니다:</p>
<ul>
<li>SpoonOS SDK 패키지 설치</li>
<li>사용할 LLM 제공업체(OpenAI, Anthropic, Google 등)의 API 키 발급</li>
<li>환경 변수 설정:</li>
</ul>
<pre><code class="language-bash"># LLM 제공업체 (하나 선택)
export OPENAI_API_KEY=&quot;your-openai-key&quot;
# 또는 export ANTHROPIC_API_KEY=&quot;your-anthropic-key&quot;

# Desearch 도구용 (검색 예제에 필요)
export DESEARCH_API_KEY=&quot;your-desearch-key&quot;</code></pre>
<p>이 예제에서는 기본적으로 OpenAI를 사용하지만, <code>llm_provider</code> 파라미터를 변경하고 해당 API 키를 설정하면 다른 제공업체도 사용할 수 있습니다.</p>
<h3 id="설치-방법">설치 방법</h3>
<pre><code class="language-bash"># uv 사용 (권장)
uv venv .venv
source .venv/bin/activate            # macOS/Linux
# .\.venv\Scripts\Activate.ps1       # Windows (PowerShell)

uv pip install spoon-ai-sdk          # 코어 SDK
uv pip install spoon-toolkits        # 도구 모음 (웹 검색, 블록체인 등)

# 또는 pip 사용
pip install spoon-ai-sdk spoon-toolkits</code></pre>
<h2 id="기본-에이전트-만들기">기본 에이전트 만들기</h2>
<p>먼저 웹 페이지를 스크래핑하고 질문에 답할 수 있는 간단한 에이전트를 만들어 봅시다. 이 에이전트는 <code>WebScraperTool</code>을 사용하여 모든 URL에서 실제 콘텐츠를 가져옵니다.</p>
<blockquote>
<p><strong>참고:</strong> 이 기본 예제는 <code>OPENAI_API_KEY</code>만 있으면 됩니다. 추가 API 키는 필요하지 않습니다.</p>
</blockquote>
<pre><code class="language-python">import asyncio
from spoon_ai.agents import SpoonReactAI
from spoon_ai.chat import ChatBot
from spoon_toolkits import WebScraperTool

# 웹 스크래핑 기능을 갖춘 에이전트 생성
agent = SpoonReactAI(
    llm=ChatBot(llm_provider=&quot;openai&quot;, model_name=&quot;gpt-5.1-chat-latest&quot;),
    tools=[WebScraperTool()],
    system_prompt=&quot;You are a helpful assistant that can read web pages.&quot;
)

# 에이전트 실행
async def main():
    response = await agent.run(
        &quot;Scrape https://news.ycombinator.com and tell me the top 3 stories&quot;
    )
    print(response)

if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h2 id="실전-에이전트-만들기">실전 에이전트 만들기</h2>
<p>다음으로, 프로덕션 환경에서 중요한 핵심 개념들을 보여주는 실용적인 리서치 어시스턴트 에이전트를 만들어 보겠습니다:</p>
<ol>
<li><strong>상세한 시스템 프롬프트</strong> - 에이전트의 행동을 더 정확하게 제어</li>
<li><strong>다중 도구 활용</strong> - 웹에서 실제 데이터를 가져오는 여러 도구 사용</li>
<li><strong>모델 설정</strong> - 대화 메모리가 내장된 모델 구성</li>
<li><strong>에이전트 생성 및 실행</strong> - 완전한 기능을 갖춘 어시스턴트로 동작</li>
</ol>
<p>각 단계를 하나씩 살펴보겠습니다:</p>
<h3 id="1단계-시스템-프롬프트-정의하기">1단계: 시스템 프롬프트 정의하기</h3>
<p>시스템 프롬프트는 에이전트의 역할과 행동 방식을 정의합니다. 구체적이고 실행 가능하게 작성하는 것이 중요합니다:</p>
<pre><code class="language-python">SYSTEM_PROMPT = &quot;&quot;&quot;You are an expert research assistant who helps users find and analyze information.

You have access to these tools:
- desearch_web_search: Search the web for general information
- desearch_ai_search: Search across web, Reddit, Wikipedia, YouTube, and arXiv
- web_scraper: Fetch and read full content from any URL

When a user asks for information:
1. Use the appropriate search tool to find relevant sources
2. If needed, use web_scraper to read full articles
3. Synthesize the information and provide clear, cited answers

Always cite your sources with URLs when providing information.&quot;&quot;&quot;</code></pre>
<h3 id="2단계-도구tools-생성하기">2단계: 도구(Tools) 생성하기</h3>
<p><a href="/how-to-guides/add-custom-tools">도구(Tools)</a>는 모델이 여러분이 정의한 함수를 호출하여 외부 시스템과 상호작용할 수 있게 해줍니다.</p>
<p>SpoonOS는 실제 데이터를 가져오는 사전 구축된 도구들을 제공합니다:</p>
<pre><code class="language-python">from spoon_toolkits import DesearchWebSearchTool, DesearchAISearchTool, WebScraperTool

# 웹 검색 - 웹을 검색하고 실제 결과를 반환
web_search = DesearchWebSearchTool()

# AI 검색 - 여러 플랫폼에서 검색 (웹, Reddit, Wikipedia, YouTube, arXiv)
ai_search = DesearchAISearchTool()

# 웹 스크래퍼 - 모든 URL에서 콘텐츠를 가져오고 정리
scraper = WebScraperTool()</code></pre>
<blockquote>
<p><strong>💡 팁</strong></p>
<p>도구는 문서화가 잘 되어 있어야 합니다. 도구의 이름, 설명, 인자 이름이 모델의 프롬프트에 포함되기 때문입니다. 명확하고 설명적인 이름과 포괄적인 파라미터 설명을 사용하세요.</p>
</blockquote>
<h3 id="3단계-모델-설정하기">3단계: 모델 설정하기</h3>
<p>대화 메모리 관리가 내장된 언어 모델을 설정합니다:</p>
<pre><code class="language-python">from spoon_ai.chat import ChatBot

llm = ChatBot(
    llm_provider=&quot;openai&quot;,
    model_name=&quot;gpt-5.1-chat-latest&quot;,
    enable_short_term_memory=True,
    short_term_memory_config={
        &quot;max_tokens&quot;: 8000,
        &quot;strategy&quot;: &quot;summarize&quot;,  # 또는 &quot;trim&quot;
        &quot;messages_to_keep&quot;: 6,
    }
)</code></pre>
<p><code>enable_short_term_memory=True</code> 옵션은 대화 히스토리를 자동으로 관리해줍니다:</p>
<ul>
<li><strong>summarize</strong>: 토큰 한도에 도달하면 오래된 메시지를 요약합니다</li>
<li><strong>trim</strong>: 토큰 한도 내에 머물기 위해 오래된 메시지를 삭제합니다</li>
</ul>
<p>SpoonOS는 다양한 LLM 제공업체를 기본 지원합니다:</p>
<table>
<thead>
<tr>
<th>제공업체</th>
<th>모델 예시</th>
<th>문서</th>
</tr>
</thead>
<tbody><tr>
<td><code>openai</code></td>
<td>gpt-5.1-chat-latest, gpt-5-mini, gpt-4.1, o3, o4-mini</td>
<td><a href="https://platform.openai.com/docs/models">OpenAI Models</a></td>
</tr>
<tr>
<td><code>anthropic</code></td>
<td>claude-opus-4-5, claude-sonnet-4-5, claude-haiku-4-5</td>
<td><a href="https://docs.anthropic.com/en/docs/about-claude/models">Anthropic Models</a></td>
</tr>
<tr>
<td><code>gemini</code></td>
<td>gemini-3-pro-preview, gemini-2.5-flash</td>
<td><a href="https://ai.google.dev/gemini-api/docs/models/gemini">Gemini Models</a></td>
</tr>
<tr>
<td><code>deepseek</code></td>
<td>deepseek-chat, deepseek-reasoner</td>
<td><a href="https://api-docs.deepseek.com/quick_start/pricing">DeepSeek Models</a></td>
</tr>
<tr>
<td><code>openrouter</code></td>
<td>100개 이상의 모델</td>
<td><a href="https://openrouter.ai/models">OpenRouter Models</a></td>
</tr>
</tbody></table>
<h3 id="4단계-에이전트-생성-및-실행하기">4단계: 에이전트 생성 및 실행하기</h3>
<p>이제 모든 구성 요소를 조합하여 에이전트를 만들고 실행해 봅시다!</p>
<pre><code class="language-python">import asyncio
from spoon_ai.agents import SpoonReactAI
from spoon_ai.chat import ChatBot
from spoon_toolkits import DesearchWebSearchTool, DesearchAISearchTool, WebScraperTool

# 시스템 프롬프트
SYSTEM_PROMPT = &quot;&quot;&quot;You are an expert research assistant who helps users find and analyze information.

You have access to these tools:
- desearch_web_search: Search the web for general information
- desearch_ai_search: Search across web, Reddit, Wikipedia, YouTube, and arXiv
- web_scraper: Fetch and read full content from any URL

Always cite your sources with URLs when providing information.&quot;&quot;&quot;

# 메모리가 포함된 모델 설정
llm = ChatBot(
    llm_provider=&quot;openai&quot;,
    model_name=&quot;gpt-5.1-chat-latest&quot;,
    enable_short_term_memory=True,
    short_term_memory_config={
        &quot;max_tokens&quot;: 8000,
        &quot;strategy&quot;: &quot;summarize&quot;,
        &quot;messages_to_keep&quot;: 6,
    }
)

# 실제 데이터 도구를 갖춘 에이전트 생성
agent = SpoonReactAI(
    llm=llm,
    system_prompt=SYSTEM_PROMPT,
    tools=[
        DesearchWebSearchTool(),
        DesearchAISearchTool(),
        WebScraperTool(),
    ],
    max_steps=10,
)

# 대화형 에이전트 실행
async def main():
    # 첫 번째 질의 - 웹에서 실제 데이터를 검색
    response = await agent.run(
        &quot;What are the latest developments in AI agents?&quot;
    )
    print(&quot;Agent:&quot;, response)

    # 후속 질의 - 에이전트가 이전 맥락을 기억
    response = await agent.run(
        &quot;Can you find academic papers about that topic on arXiv?&quot;
    )
    print(&quot;Agent:&quot;, response)

if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="선택-사항-스트리밍-응답">선택 사항: 스트리밍 응답</h3>
<p>토큰 단위로 출력하여 더 인터랙티브한 경험을 원한다면 다음과 같이 할 수 있습니다:</p>
<pre><code class="language-python">import asyncio

async def stream_demo():
    llm = ChatBot(
        llm_provider=&quot;openai&quot;,
        model_name=&quot;gpt-5.1-chat-latest&quot;
    )

    messages = [
        {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Explain machine learning in 3 sentences&quot;}
    ]

    async for chunk in llm.astream(messages=messages):
        print(chunk.delta or &quot;&quot;, end=&quot;&quot;, flush=True)
    print()  # 줄바꿈

if __name__ == &quot;__main__&quot;:
    asyncio.run(stream_demo())</code></pre>
<h2 id="전체-예제-코드">전체 예제 코드</h2>
<details>
<summary>프로덕션 레벨 전체 예제 보기 (클릭하여 펼치기)</summary>

<pre><code class="language-python">&quot;&quot;&quot;
프로덕션 레벨 SpoonOS 리서치 어시스턴트 에이전트.

이 예제에서 다루는 내용:
- Desearch 도구를 통한 실시간 웹 검색
- 웹 페이지 스크래핑으로 전체 콘텐츠 가져오기
- 자동 요약 기능이 포함된 대화 메모리
&quot;&quot;&quot;
import asyncio
import logging

from spoon_ai.agents import SpoonReactAI
from spoon_ai.chat import ChatBot
from spoon_toolkits import DesearchWebSearchTool, DesearchAISearchTool, WebScraperTool

# 로깅 설정
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# 시스템 프롬프트 정의
SYSTEM_PROMPT = &quot;&quot;&quot;You are an expert research assistant who helps users find and analyze information.

You have access to these tools:
- desearch_web_search: Search the web for general information
- desearch_ai_search: Search across web, Reddit, Wikipedia, YouTube, and arXiv platforms
- web_scraper: Fetch and read full content from any URL

When a user asks for information:
1. Use the appropriate search tool to find relevant sources
2. If needed, use web_scraper to read full articles for deeper analysis
3. Synthesize the information and provide clear, cited answers

Always cite your sources with URLs when providing information.
Be thorough but concise in your analysis.&quot;&quot;&quot;


def create_agent() -&gt; SpoonReactAI:
    &quot;&quot;&quot;리서치 어시스턴트 에이전트를 생성하고 설정합니다.&quot;&quot;&quot;

    # 메모리 관리가 포함된 모델 설정
    llm = ChatBot(
        llm_provider=&quot;openai&quot;,
        model_name=&quot;gpt-5.1-chat-latest&quot;,
        enable_short_term_memory=True,
        short_term_memory_config={
            &quot;max_tokens&quot;: 8000,
            &quot;strategy&quot;: &quot;summarize&quot;,
            &quot;messages_to_keep&quot;: 6,
        }
    )

    # 실제 데이터 도구 구성
    tools = [
        DesearchWebSearchTool(),
        DesearchAISearchTool(),
        WebScraperTool(),
    ]

    # 에이전트 생성
    agent = SpoonReactAI(
        llm=llm,
        system_prompt=SYSTEM_PROMPT,
        tools=tools,
        max_steps=10,
    )

    logger.info(&quot;Agent created with %d tools&quot;, len(tools))
    return agent


async def interactive_session():
    &quot;&quot;&quot;에이전트와 대화형 채팅 세션을 실행합니다.&quot;&quot;&quot;
    agent = create_agent()

    print(&quot;\n리서치 어시스턴트 에이전트 준비 완료!&quot;)
    print(&quot;&#39;quit&#39; 또는 &#39;exit&#39;를 입력하면 세션이 종료됩니다.\n&quot;)

    while True:
        try:
            user_input = input(&quot;You: &quot;).strip()

            if not user_input:
                continue

            if user_input.lower() in [&#39;quit&#39;, &#39;exit&#39;, &#39;bye&#39;]:
                print(&quot;안녕히 가세요!&quot;)
                break

            # 에이전트 실행 및 응답 받기
            response = await agent.run(user_input)
            print(f&quot;\nAgent: {response}\n&quot;)

        except KeyboardInterrupt:
            print(&quot;\n세션이 종료되었습니다.&quot;)
            break
        except Exception as e:
            logger.error(&quot;Error: %s&quot;, e)
            print(f&quot;오류 발생: {e}&quot;)


async def demo_queries():
    &quot;&quot;&quot;샘플 질의로 에이전트 기능을 시연합니다.&quot;&quot;&quot;
    agent = create_agent()

    queries = [
        &quot;Search for the latest news about large language models&quot;,
        &quot;Find academic papers about transformer architectures on arXiv&quot;,
        &quot;Scrape https://news.ycombinator.com and summarize the top stories&quot;,
    ]

    print(&quot;\n데모 질의 실행 중\n&quot; + &quot;=&quot; * 50)

    for query in queries:
        print(f&quot;\nUser: {query}&quot;)
        response = await agent.run(query)
        print(f&quot;Agent: {response}&quot;)

    print(&quot;\n&quot; + &quot;=&quot; * 50 + &quot;\n데모 완료!&quot;)


if __name__ == &quot;__main__&quot;:
    import sys

    if &quot;--demo&quot; in sys.argv:
        asyncio.run(demo_queries())
    else:
        asyncio.run(interactive_session())</code></pre>
</details>

<h2 id="지금까지-만든-것-정리">지금까지 만든 것 정리</h2>
<p>축하합니다! 이제 다음과 같은 기능을 갖춘 AI 에이전트가 완성되었습니다:</p>
<ul>
<li><strong>실시간 웹 검색</strong> - Desearch 도구를 활용한 웹 검색</li>
<li><strong>웹 페이지 전체 읽기</strong> - WebScraper 도구로 페이지 콘텐츠 가져오기</li>
<li><strong>학술 논문 검색</strong> - arXiv 등 다양한 플랫폼에서 논문 탐색</li>
<li><strong>대화 기억</strong> - 단기 메모리를 통한 맥락 유지</li>
<li><strong>다중 도구 활용</strong> - 사용자 질의에 따라 지능적으로 도구 선택</li>
<li><strong>스트리밍 응답</strong> - 실시간 토큰 단위 출력</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SpoonOS] LLM + Graph System, 실전 예제로 알아보기]]></title>
            <link>https://velog.io/@neo_blockchain/SpoonOS-LLM-Graph-System-%EC%8B%A4%EC%A0%84-%EC%98%88%EC%A0%9C%EB%A1%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@neo_blockchain/SpoonOS-LLM-Graph-System-%EC%8B%A4%EC%A0%84-%EC%98%88%EC%A0%9C%EB%A1%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Fri, 13 Feb 2026 05:19:56 GMT</pubDate>
            <description><![CDATA[<p>Graph System에 LLM을 연동하면 어떤 걸 만들 수 있을까요?
이번 글에서는 실제로 돌려볼 수 있는 5가지 예제를 통해 핵심 패턴들을 살펴보겠습니다.
모든 예제는 고정된 출력이 아닌 <strong>실제 LLM 호출</strong>을 사용합니다.</p>
<hr>
<h2 id="1-llm-인텐트-라우터">1. LLM 인텐트 라우터</h2>
<p>사용자의 질문을 LLM이 의도(intent)별로 분류하고, 각 의도에 맞는 전문 핸들러로 라우팅하는 패턴입니다.</p>
<pre><code class="language-python">&quot;&quot;&quot;
LLM 인텐트 라우터 예제

다루는 내용:
- LLM 기반 의도 분류
- LLM 출력에 따른 조건부 라우팅
- 의도별 전문 핸들러 노드
&quot;&quot;&quot;

import asyncio
import json
from typing import TypedDict
from spoon_ai.graph import StateGraph, END
from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message


class RouterState(TypedDict):
    query: str
    intent: str
    confidence: float
    result: str


llm = LLMManager()


async def classify_intent(state: RouterState) -&gt; dict:
    &quot;&quot;&quot;LLM이 사용자의 의도를 분류하고 신뢰도 점수를 반환합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are an intent classifier for a crypto assistant.
        Classify the user query into one of these categories:
        - price: asking about cryptocurrency prices
        - news: asking about crypto news or updates
        - analysis: requesting market analysis or trends
        - general: other questions

        Respond with JSON only: {&quot;intent&quot;: &quot;category&quot;, &quot;confidence&quot;: 0.0-1.0}&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;query&quot;])
    ])

    try:
        result = json.loads(response.content)
        return {
            &quot;intent&quot;: result.get(&quot;intent&quot;, &quot;general&quot;),
            &quot;confidence&quot;: result.get(&quot;confidence&quot;, 0.5)
        }
    except json.JSONDecodeError:
        return {&quot;intent&quot;: &quot;general&quot;, &quot;confidence&quot;: 0.5}


async def handle_price(state: RouterState) -&gt; dict:
    &quot;&quot;&quot;가격 관련 응답을 생성합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a crypto price expert.
        Provide helpful information about cryptocurrency prices.
        Be concise and include relevant data points.&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;query&quot;])
    ])
    return {&quot;result&quot;: response.content}


async def handle_news(state: RouterState) -&gt; dict:
    &quot;&quot;&quot;뉴스 관련 응답을 생성합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a crypto news analyst.
        Summarize relevant cryptocurrency news and updates.
        Focus on recent developments and their implications.&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;query&quot;])
    ])
    return {&quot;result&quot;: response.content}


async def handle_analysis(state: RouterState) -&gt; dict:
    &quot;&quot;&quot;시장 분석 응답을 생성합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a crypto market analyst.
        Provide detailed market analysis including:
        - Current trends
        - Technical indicators
        - Risk assessment
        Be analytical and data-driven.&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;query&quot;])
    ])
    return {&quot;result&quot;: response.content}


async def handle_general(state: RouterState) -&gt; dict:
    &quot;&quot;&quot;일반 질문을 처리합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a helpful crypto assistant.
        Answer questions clearly and accurately.
        If you&#39;re unsure, say so.&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;query&quot;])
    ])
    return {&quot;result&quot;: response.content}


def route_by_intent(state: RouterState) -&gt; str:
    &quot;&quot;&quot;분류된 의도에 따라 라우팅합니다.&quot;&quot;&quot;
    return state.get(&quot;intent&quot;, &quot;general&quot;)


# 그래프 구성
graph = StateGraph(RouterState)

graph.add_node(&quot;classify&quot;, classify_intent)
graph.add_node(&quot;price_handler&quot;, handle_price)
graph.add_node(&quot;news_handler&quot;, handle_news)
graph.add_node(&quot;analysis_handler&quot;, handle_analysis)
graph.add_node(&quot;general_handler&quot;, handle_general)

graph.set_entry_point(&quot;classify&quot;)

graph.add_conditional_edges(
    &quot;classify&quot;,
    route_by_intent,
    {
        &quot;price&quot;: &quot;price_handler&quot;,
        &quot;news&quot;: &quot;news_handler&quot;,
        &quot;analysis&quot;: &quot;analysis_handler&quot;,
        &quot;general&quot;: &quot;general_handler&quot;,
    }
)

graph.add_edge(&quot;price_handler&quot;, END)
graph.add_edge(&quot;news_handler&quot;, END)
graph.add_edge(&quot;analysis_handler&quot;, END)
graph.add_edge(&quot;general_handler&quot;, END)

app = graph.compile()


async def main():
    &quot;&quot;&quot;다양한 쿼리로 LLM 라우터를 테스트합니다.&quot;&quot;&quot;
    test_queries = [
        &quot;What is the current price of Bitcoin?&quot;,
        &quot;Any news about Ethereum updates?&quot;,
        &quot;Analyze the SOL market trend&quot;,
        &quot;What is a blockchain?&quot;,
    ]

    for query in test_queries:
        print(f&quot;\n{&#39;=&#39;*60}&quot;)
        print(f&quot;Query: {query}&quot;)
        print(&#39;=&#39;*60)

        result = await app.invoke({
            &quot;query&quot;: query,
            &quot;intent&quot;: &quot;&quot;,
            &quot;confidence&quot;: 0.0,
            &quot;result&quot;: &quot;&quot;
        })

        print(f&quot;Intent: {result[&#39;intent&#39;]} (confidence: {result[&#39;confidence&#39;]:.0%})&quot;)
        print(f&quot;\nResponse:\n{result[&#39;result&#39;]}&quot;)

    return True


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="핵심-포인트">핵심 포인트</h3>
<ul>
<li>LLM이 의도를 분류하고 <strong>구조화된 JSON</strong>으로 반환합니다</li>
<li>신뢰도 점수를 활용하면 품질 기반 라우팅이 가능합니다</li>
<li>의도별 전문 핸들러를 두면 훨씬 정확한 응답을 얻을 수 있습니다</li>
</ul>
<hr>
<h2 id="2-다단계-분석-파이프라인">2. 다단계 분석 파이프라인</h2>
<p>여러 LLM 호출을 순차적으로 연결해서, 컨텍스트를 점점 쌓아가며 깊이 있는 분석을 수행하는 패턴입니다.</p>
<pre><code class="language-python">&quot;&quot;&quot;
다단계 LLM 분석 파이프라인

다루는 내용:
- 컨텍스트를 누적하는 순차 LLM 호출
- 노드 간 상태 축적
- 분석의 점진적 정제
&quot;&quot;&quot;

import asyncio
from typing import TypedDict, List, Dict, Any
from spoon_ai.graph import StateGraph, END
from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message


class AnalysisState(TypedDict):
    symbol: str
    user_question: str
    market_context: str
    technical_analysis: str
    risk_assessment: str
    final_recommendation: str
    confidence: float


llm = LLMManager()


async def gather_context(state: AnalysisState) -&gt; dict:
    &quot;&quot;&quot;해당 심볼의 시장 컨텍스트를 수집합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a market context specialist.
        Provide relevant market context for the cryptocurrency including:
        - Current market sentiment
        - Recent price movements
        - Key events affecting the asset
        Be factual and concise.&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=f&quot;Provide market context for {state[&#39;symbol&#39;]}&quot;)
    ])
    return {&quot;market_context&quot;: response.content}


async def analyze_technicals(state: AnalysisState) -&gt; dict:
    &quot;&quot;&quot;수집된 컨텍스트를 기반으로 기술적 분석을 수행합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a technical analyst.
        Based on the market context, provide technical analysis including:
        - Support and resistance levels
        - Key indicators (RSI, MACD trends)
        - Chart patterns
        Be specific and analytical.&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=f&quot;&quot;&quot;
        Symbol: {state[&#39;symbol&#39;]}
        Market Context: {state[&#39;market_context&#39;]}

        Provide technical analysis:&quot;&quot;&quot;)
    ])
    return {&quot;technical_analysis&quot;: response.content}


async def assess_risk(state: AnalysisState) -&gt; dict:
    &quot;&quot;&quot;수집된 모든 정보를 기반으로 리스크를 평가합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a risk assessment specialist.
        Based on the context and technical analysis, assess:
        - Risk level (Low/Medium/High)
        - Key risk factors
        - Potential downside scenarios
        Also provide a confidence score (0-100) for your assessment.

        End your response with: Confidence: XX%&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=f&quot;&quot;&quot;
        Symbol: {state[&#39;symbol&#39;]}
        Market Context: {state[&#39;market_context&#39;]}
        Technical Analysis: {state[&#39;technical_analysis&#39;]}

        Provide risk assessment:&quot;&quot;&quot;)
    ])

    # 응답에서 신뢰도 추출
    content = response.content
    confidence = 0.7  # 기본값
    if &quot;Confidence:&quot; in content:
        try:
            conf_str = content.split(&quot;Confidence:&quot;)[-1].strip().replace(&quot;%&quot;, &quot;&quot;)
            confidence = float(conf_str) / 100
        except:
            pass

    return {
        &quot;risk_assessment&quot;: content,
        &quot;confidence&quot;: confidence
    }


async def generate_recommendation(state: AnalysisState) -&gt; dict:
    &quot;&quot;&quot;최종 추천을 생성합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a senior investment advisor.
        Based on all the analysis, provide a clear recommendation.
        Structure your response as:
        1. Summary recommendation (Buy/Hold/Sell)
        2. Key reasoning points
        3. Suggested actions
        4. Caveats and disclaimers&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=f&quot;&quot;&quot;
        User Question: {state[&#39;user_question&#39;]}
        Symbol: {state[&#39;symbol&#39;]}

        Analysis Summary:
        - Market Context: {state[&#39;market_context&#39;][:500]}...
        - Technical: {state[&#39;technical_analysis&#39;][:500]}...
        - Risk: {state[&#39;risk_assessment&#39;][:500]}...
        - Confidence: {state[&#39;confidence&#39;]:.0%}

        Generate final recommendation:&quot;&quot;&quot;)
    ])
    return {&quot;final_recommendation&quot;: response.content}


# 그래프 구성: 컨텍스트 수집 -&gt; 기술 분석 -&gt; 리스크 평가 -&gt; 추천
graph = StateGraph(AnalysisState)

graph.add_node(&quot;gather_context&quot;, gather_context)
graph.add_node(&quot;analyze_technicals&quot;, analyze_technicals)
graph.add_node(&quot;assess_risk&quot;, assess_risk)
graph.add_node(&quot;generate_recommendation&quot;, generate_recommendation)

graph.set_entry_point(&quot;gather_context&quot;)
graph.add_edge(&quot;gather_context&quot;, &quot;analyze_technicals&quot;)
graph.add_edge(&quot;analyze_technicals&quot;, &quot;assess_risk&quot;)
graph.add_edge(&quot;assess_risk&quot;, &quot;generate_recommendation&quot;)
graph.add_edge(&quot;generate_recommendation&quot;, END)

app = graph.compile()


async def main():
    &quot;&quot;&quot;분석 파이프라인을 실행합니다.&quot;&quot;&quot;
    print(&quot;=&quot;*60)
    print(&quot;MULTI-STEP LLM ANALYSIS PIPELINE&quot;)
    print(&quot;=&quot;*60)

    result = await app.invoke({
        &quot;symbol&quot;: &quot;BTC&quot;,
        &quot;user_question&quot;: &quot;Should I buy Bitcoin now?&quot;,
        &quot;market_context&quot;: &quot;&quot;,
        &quot;technical_analysis&quot;: &quot;&quot;,
        &quot;risk_assessment&quot;: &quot;&quot;,
        &quot;final_recommendation&quot;: &quot;&quot;,
        &quot;confidence&quot;: 0.0
    })

    print(&quot;\n📊 MARKET CONTEXT:&quot;)
    print(&quot;-&quot;*40)
    print(result[&quot;market_context&quot;][:500] + &quot;...&quot;)

    print(&quot;\n📈 TECHNICAL ANALYSIS:&quot;)
    print(&quot;-&quot;*40)
    print(result[&quot;technical_analysis&quot;][:500] + &quot;...&quot;)

    print(&quot;\n⚠️ RISK ASSESSMENT:&quot;)
    print(&quot;-&quot;*40)
    print(result[&quot;risk_assessment&quot;][:500] + &quot;...&quot;)

    print(f&quot;\n🎯 CONFIDENCE: {result[&#39;confidence&#39;]:.0%}&quot;)

    print(&quot;\n💡 FINAL RECOMMENDATION:&quot;)
    print(&quot;-&quot;*40)
    print(result[&quot;final_recommendation&quot;])

    return True


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="핵심-포인트-1">핵심 포인트</h3>
<ul>
<li>각 LLM 노드가 이전 노드의 출력을 기반으로 동작합니다</li>
<li>파이프라인을 거치면서 컨텍스트가 점점 풍부해집니다</li>
<li>최종 추천은 모든 분석 결과를 종합해서 만들어집니다</li>
</ul>
<hr>
<h2 id="3-llm-기반-human-in-the-loop">3. LLM 기반 Human-in-the-Loop</h2>
<p>LLM이 요약을 생성한 뒤, 사람의 승인을 받고 나서 실행하는 워크플로우입니다.
고위험 작업에서 사람의 판단을 끼워넣고 싶을 때 유용합니다.</p>
<pre><code class="language-python">&quot;&quot;&quot;
LLM 기반 사람 승인 워크플로우

다루는 내용:
- LLM이 승인 요약 생성
- 사람의 판단을 위한 인터럽트
- 사용자 입력으로 재개
&quot;&quot;&quot;

import asyncio
from typing import TypedDict, Optional, Dict, Any
from spoon_ai.graph import StateGraph, END, interrupt, Command
from spoon_ai.graph import InMemoryCheckpointer
from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message


class ApprovalState(TypedDict):
    request_type: str
    request_details: Dict[str, Any]
    llm_summary: str
    risk_level: str
    user_approved: Optional[bool]
    execution_result: str


llm = LLMManager()


async def analyze_request(state: ApprovalState) -&gt; dict:
    &quot;&quot;&quot;LLM이 요청을 분석하고 요약을 생성합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a request analyzer.
        Analyze the request and provide:
        1. A clear summary of what will happen
        2. Risk level: LOW, MEDIUM, or HIGH
        3. Key points to consider

        Format your response as:
        SUMMARY: [summary]
        RISK: [LOW/MEDIUM/HIGH]
        CONSIDERATIONS: [key points]&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=f&quot;&quot;&quot;
        Request Type: {state[&#39;request_type&#39;]}
        Details: {state[&#39;request_details&#39;]}

        Analyze this request:&quot;&quot;&quot;)
    ])

    content = response.content
    risk_level = &quot;MEDIUM&quot;  # 기본값
    if &quot;RISK: LOW&quot; in content:
        risk_level = &quot;LOW&quot;
    elif &quot;RISK: HIGH&quot; in content:
        risk_level = &quot;HIGH&quot;

    return {
        &quot;llm_summary&quot;: content,
        &quot;risk_level&quot;: risk_level
    }


async def request_approval(state: ApprovalState) -&gt; dict:
    &quot;&quot;&quot;사용자 승인을 위해 인터럽트합니다.&quot;&quot;&quot;
    if state.get(&quot;user_approved&quot;) is not None:
        return {}  # 이미 결정된 상태

    interrupt({
        &quot;type&quot;: &quot;approval_required&quot;,
        &quot;summary&quot;: state[&quot;llm_summary&quot;],
        &quot;risk_level&quot;: state[&quot;risk_level&quot;],
        &quot;request_type&quot;: state[&quot;request_type&quot;],
        &quot;details&quot;: state[&quot;request_details&quot;],
        &quot;requires_response&quot;: [&quot;user_approved&quot;]
    })
    return {}


async def execute_request(state: ApprovalState) -&gt; dict:
    &quot;&quot;&quot;승인 결과에 따라 실행합니다.&quot;&quot;&quot;
    if state.get(&quot;user_approved&quot;):
        response = await llm.chat([
            Message(role=&quot;system&quot;, content=&quot;Generate a confirmation message for the executed request.&quot;),
            Message(role=&quot;user&quot;, content=f&quot;&quot;&quot;
            Request Type: {state[&#39;request_type&#39;]}
            Details: {state[&#39;request_details&#39;]}

            The user approved this request. Generate execution confirmation:&quot;&quot;&quot;)
        ])
        return {&quot;execution_result&quot;: f&quot;✅ APPROVED\n{response.content}&quot;}
    else:
        response = await llm.chat([
            Message(role=&quot;system&quot;, content=&quot;Generate a professional rejection acknowledgment.&quot;),
            Message(role=&quot;user&quot;, content=f&quot;The user rejected the {state[&#39;request_type&#39;]} request.&quot;)
        ])
        return {&quot;execution_result&quot;: f&quot;❌ REJECTED\n{response.content}&quot;}


# 체크포인터와 함께 그래프 구성
checkpointer = InMemoryCheckpointer()
graph = StateGraph(ApprovalState, checkpointer=checkpointer)

graph.add_node(&quot;analyze&quot;, analyze_request)
graph.add_node(&quot;approve&quot;, request_approval)
graph.add_node(&quot;execute&quot;, execute_request)

graph.set_entry_point(&quot;analyze&quot;)
graph.add_edge(&quot;analyze&quot;, &quot;approve&quot;)
graph.add_edge(&quot;approve&quot;, &quot;execute&quot;)
graph.add_edge(&quot;execute&quot;, END)

app = graph.compile()


async def main():
    &quot;&quot;&quot;승인 워크플로우를 테스트합니다.&quot;&quot;&quot;
    test_requests = [
        {&quot;type&quot;: &quot;transfer&quot;, &quot;details&quot;: {&quot;amount&quot;: 500, &quot;to&quot;: &quot;wallet_abc&quot;, &quot;currency&quot;: &quot;USDT&quot;}},
        {&quot;type&quot;: &quot;trade&quot;, &quot;details&quot;: {&quot;action&quot;: &quot;buy&quot;, &quot;amount&quot;: 0.5, &quot;symbol&quot;: &quot;ETH&quot;, &quot;price&quot;: &quot;market&quot;}},
    ]

    for i, request in enumerate(test_requests):
        print(f&quot;\n{&#39;#&#39;*60}&quot;)
        print(f&quot;Processing Request {i + 1}: {request[&#39;type&#39;]}&quot;)
        print(&#39;#&#39;*60)

        session_id = f&quot;approval_session_{i}&quot;

        # 첫 실행 - LLM 분석 후 인터럽트 발생
        result = await app.invoke(
            {
                &quot;request_type&quot;: request[&quot;type&quot;],
                &quot;request_details&quot;: request[&quot;details&quot;],
                &quot;llm_summary&quot;: &quot;&quot;,
                &quot;risk_level&quot;: &quot;&quot;,
                &quot;user_approved&quot;: None,
                &quot;execution_result&quot;: &quot;&quot;
            },
            config={&quot;configurable&quot;: {&quot;thread_id&quot;: session_id}}
        )

        # 인터럽트 확인
        if &quot;__interrupt__&quot; in result:
            interrupt_data = result[&quot;__interrupt__&quot;][0][&quot;value&quot;]

            print(&quot;\n🔔 APPROVAL REQUIRED&quot;)
            print(&quot;=&quot;*50)
            print(f&quot;Risk Level: {interrupt_data[&#39;risk_level&#39;]}&quot;)
            print(f&quot;\nLLM Analysis:\n{interrupt_data[&#39;summary&#39;]}&quot;)
            print(&quot;=&quot;*50)

            # 리스크에 따른 사용자 결정 시뮬레이션
            approved = interrupt_data[&quot;risk_level&quot;] != &quot;HIGH&quot;
            print(f&quot;\nSimulated Decision: {&#39;APPROVED&#39; if approved else &#39;REJECTED&#39;}&quot;)

            # 결정을 반영하여 재개
            result = await app.invoke(
                Command(resume={&quot;user_approved&quot;: approved}),
                config={&quot;configurable&quot;: {&quot;thread_id&quot;: session_id}}
            )

        print(f&quot;\n📋 Final Result:\n{result.get(&#39;execution_result&#39;, &#39;Unknown&#39;)}&quot;)

    return True


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="핵심-포인트-2">핵심 포인트</h3>
<ul>
<li>LLM이 사람이 읽기 쉬운 요약을 자동 생성합니다</li>
<li>리스크 평가 덕분에 사용자가 더 나은 판단을 내릴 수 있습니다</li>
<li><code>interrupt</code> / <code>resume</code> 패턴으로 비동기 사람 상호작용이 가능합니다</li>
</ul>
<hr>
<h2 id="4-병렬-llm-분석">4. 병렬 LLM 분석</h2>
<p>여러 LLM 호출을 <strong>동시에</strong> 실행하고, 결과를 모아서 종합하는 패턴입니다.
서로 다른 관점의 분석을 한꺼번에 돌려서 시간도 아끼고 다양성도 확보할 수 있습니다.</p>
<pre><code class="language-python">&quot;&quot;&quot;
병렬 LLM 분석 예제

다루는 내용:
- 여러 LLM 호출의 동시 실행
- 서로 다른 분석 관점
- LLM을 활용한 결과 종합
&quot;&quot;&quot;

import asyncio
from typing import TypedDict, Dict, Any
from spoon_ai.graph import StateGraph, END
from spoon_ai.graph.config import ParallelGroupConfig
from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message


class ParallelAnalysisState(TypedDict):
    symbol: str
    query: str
    bullish_view: str
    bearish_view: str
    neutral_view: str
    aggregated_analysis: str


llm = LLMManager()


async def bullish_analyst(state: ParallelAnalysisState) -&gt; dict:
    &quot;&quot;&quot;강세 관점에서 분석합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a bullish crypto analyst.
        Find and present the positive aspects and upside potential.
        Focus on growth catalysts, adoption metrics, and bullish indicators.
        Be optimistic but grounded in facts.&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=f&quot;Analyze {state[&#39;symbol&#39;]}: {state[&#39;query&#39;]}&quot;)
    ])
    return {&quot;bullish_view&quot;: response.content}


async def bearish_analyst(state: ParallelAnalysisState) -&gt; dict:
    &quot;&quot;&quot;약세 관점에서 분석합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a bearish crypto analyst.
        Find and present the risks and downside potential.
        Focus on threats, competition, and bearish indicators.
        Be cautious but fair in your assessment.&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=f&quot;Analyze {state[&#39;symbol&#39;]}: {state[&#39;query&#39;]}&quot;)
    ])
    return {&quot;bearish_view&quot;: response.content}


async def neutral_analyst(state: ParallelAnalysisState) -&gt; dict:
    &quot;&quot;&quot;중립적 관점에서 균형 잡힌 분석을 제공합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a neutral crypto analyst.
        Provide balanced analysis considering both sides.
        Focus on factual data and objective metrics.
        Present pros and cons equally.&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=f&quot;Analyze {state[&#39;symbol&#39;]}: {state[&#39;query&#39;]}&quot;)
    ])
    return {&quot;neutral_view&quot;: response.content}


async def aggregate_analysis(state: ParallelAnalysisState) -&gt; dict:
    &quot;&quot;&quot;모든 관점을 종합하여 최종 분석을 생성합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a senior analyst aggregating multiple perspectives.
        Synthesize the bullish, bearish, and neutral views into a comprehensive analysis.
        Structure your response:
        1. Executive Summary
        2. Key Bullish Points
        3. Key Bearish Points
        4. Balanced Assessment
        5. Final Verdict&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=f&quot;&quot;&quot;
        Symbol: {state[&#39;symbol&#39;]}
        Query: {state[&#39;query&#39;]}

        BULLISH VIEW:
        {state[&#39;bullish_view&#39;]}

        BEARISH VIEW:
        {state[&#39;bearish_view&#39;]}

        NEUTRAL VIEW:
        {state[&#39;neutral_view&#39;]}

        Synthesize these perspectives:&quot;&quot;&quot;)
    ])
    return {&quot;aggregated_analysis&quot;: response.content}


# 병렬 실행 그래프 구성
graph = StateGraph(ParallelAnalysisState)

graph.add_node(&quot;bullish&quot;, bullish_analyst)
graph.add_node(&quot;bearish&quot;, bearish_analyst)
graph.add_node(&quot;neutral&quot;, neutral_analyst)
graph.add_node(&quot;aggregate&quot;, aggregate_analysis)

# 병렬 그룹 설정
graph.add_parallel_group(
    &quot;perspectives&quot;,
    nodes=[&quot;bullish&quot;, &quot;bearish&quot;, &quot;neutral&quot;],
    config=ParallelGroupConfig(
        join_strategy=&quot;all&quot;,
        timeout=60.0,
    )
)

# 모든 병렬 노드가 종합 노드로 수렴
graph.add_edge(&quot;bullish&quot;, &quot;aggregate&quot;)
graph.add_edge(&quot;bearish&quot;, &quot;aggregate&quot;)
graph.add_edge(&quot;neutral&quot;, &quot;aggregate&quot;)
graph.add_edge(&quot;aggregate&quot;, END)

graph.set_entry_point(&quot;bullish&quot;)
app = graph.compile()


async def main():
    &quot;&quot;&quot;병렬 LLM 분석을 실행합니다.&quot;&quot;&quot;
    print(&quot;=&quot;*60)
    print(&quot;PARALLEL LLM ANALYSIS&quot;)
    print(&quot;=&quot;*60)

    result = await app.invoke({
        &quot;symbol&quot;: &quot;ETH&quot;,
        &quot;query&quot;: &quot;What&#39;s the outlook for Ethereum in the next year?&quot;,
        &quot;bullish_view&quot;: &quot;&quot;,
        &quot;bearish_view&quot;: &quot;&quot;,
        &quot;neutral_view&quot;: &quot;&quot;,
        &quot;aggregated_analysis&quot;: &quot;&quot;
    })

    print(&quot;\n🐂 BULLISH VIEW:&quot;)
    print(&quot;-&quot;*40)
    print(result[&quot;bullish_view&quot;][:400] + &quot;...&quot;)

    print(&quot;\n🐻 BEARISH VIEW:&quot;)
    print(&quot;-&quot;*40)
    print(result[&quot;bearish_view&quot;][:400] + &quot;...&quot;)

    print(&quot;\n⚖️ NEUTRAL VIEW:&quot;)
    print(&quot;-&quot;*40)
    print(result[&quot;neutral_view&quot;][:400] + &quot;...&quot;)

    print(&quot;\n📊 AGGREGATED ANALYSIS:&quot;)
    print(&quot;-&quot;*40)
    print(result[&quot;aggregated_analysis&quot;])

    return True


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="핵심-포인트-3">핵심 포인트</h3>
<ul>
<li>여러 LLM 호출을 병렬로 실행해서 속도를 높입니다</li>
<li>서로 다른 &quot;페르소나&quot;를 부여하면 다양한 관점을 확보할 수 있습니다</li>
<li>종합 LLM이 여러 입력을 하나의 분석으로 합칩니다</li>
</ul>
<hr>
<h2 id="5-메모리를-가진-대화형-에이전트">5. 메모리를 가진 대화형 에이전트</h2>
<p>멀티턴 대화에서 컨텍스트를 유지하는 패턴입니다.
이전 대화 내용을 기억하면서 자연스러운 대화를 이어갈 수 있습니다.</p>
<pre><code class="language-python">&quot;&quot;&quot;
메모리 기반 대화형 LLM 에이전트

다루는 내용:
- 멀티턴 대화 상태 관리
- 메시지 히스토리 축적
- 컨텍스트를 인식하는 응답
&quot;&quot;&quot;

import asyncio
from typing import TypedDict, List
from spoon_ai.graph import StateGraph, END, InMemoryCheckpointer
from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message


class ConversationState(TypedDict):
    messages: List[dict]  # 직렬화를 위해 dict로 저장
    user_input: str
    assistant_response: str
    turn_count: int


llm = LLMManager()


async def process_message(state: ConversationState) -&gt; dict:
    &quot;&quot;&quot;전체 대화 히스토리와 함께 메시지를 처리합니다.&quot;&quot;&quot;
    messages = state.get(&quot;messages&quot;, [])
    user_input = state[&quot;user_input&quot;]

    # 히스토리를 포함한 대화 구성 (dict를 Message 객체로 변환)
    conversation = [
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;You are a helpful crypto trading assistant.
        You have memory of the entire conversation.
        Reference previous messages when relevant.
        Be conversational and helpful.&quot;&quot;&quot;)
    ] + [
        Message(role=m[&quot;role&quot;], content=m[&quot;content&quot;]) for m in messages
    ] + [
        Message(role=&quot;user&quot;, content=user_input)
    ]

    response = await llm.chat(conversation)

    # 메시지 히스토리 업데이트 (JSON 직렬화를 위해 dict로 저장)
    new_messages = messages + [
        {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: user_input},
        {&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: response.content}
    ]

    return {
        &quot;assistant_response&quot;: response.content,
        &quot;messages&quot;: new_messages,
        &quot;turn_count&quot;: state.get(&quot;turn_count&quot;, 0) + 1
    }


# 그래프 구성
checkpointer = InMemoryCheckpointer()
graph = StateGraph(ConversationState, checkpointer=checkpointer)

graph.add_node(&quot;chat&quot;, process_message)
graph.set_entry_point(&quot;chat&quot;)
graph.add_edge(&quot;chat&quot;, END)

app = graph.compile()


async def main():
    &quot;&quot;&quot;멀티턴 대화를 시뮬레이션합니다.&quot;&quot;&quot;
    print(&quot;=&quot;*60)
    print(&quot;CONVERSATIONAL LLM AGENT WITH MEMORY&quot;)
    print(&quot;=&quot;*60)

    session_id = &quot;conversation_demo&quot;

    # 대화 턴들
    turns = [
        &quot;Hi! I&#39;m interested in Bitcoin.&quot;,
        &quot;What&#39;s its current market situation?&quot;,
        &quot;You mentioned Bitcoin earlier - what about Ethereum compared to it?&quot;,
        &quot;Based on our conversation, what would you recommend for a beginner?&quot;,
    ]

    state = {
        &quot;messages&quot;: [],
        &quot;user_input&quot;: &quot;&quot;,
        &quot;assistant_response&quot;: &quot;&quot;,
        &quot;turn_count&quot;: 0
    }

    for turn in turns:
        print(f&quot;\n👤 User: {turn}&quot;)

        state[&quot;user_input&quot;] = turn
        result = await app.invoke(
            state,
            config={&quot;configurable&quot;: {&quot;thread_id&quot;: session_id}}
        )

        print(f&quot;\n🤖 Assistant: {result[&#39;assistant_response&#39;]}&quot;)
        print(f&quot;   [Turn {result[&#39;turn_count&#39;]}, History: {len(result[&#39;messages&#39;])} messages]&quot;)

        # 다음 턴을 위해 상태 유지
        state = result

    return True


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="핵심-포인트-4">핵심 포인트</h3>
<ul>
<li>메시지 히스토리 덕분에 맥락을 이해하는 응답이 가능합니다</li>
<li>LLM이 이전 대화를 자연스럽게 참조합니다</li>
<li>체크포인팅으로 세션 간 대화를 보존할 수 있습니다</li>
</ul>
<hr>
<h2 id="예제-실행-방법">예제 실행 방법</h2>
<p>모든 예제는 레포지토리에서 바로 실행할 수 있습니다.</p>
<pre><code class="language-bash">cd spoon-core/examples/docs

# 개별 예제 실행
python llm_router.py           # 예제 1: 인텐트 라우터
python analysis_pipeline.py    # 예제 2: 다단계 분석
python llm_approval.py         # 예제 3: Human-in-the-Loop
python parallel_analysis.py    # 예제 4: 병렬 LLM
python conversational.py       # 예제 5: 대화형 에이전트</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SpoonOS] GraphAgent, 도구, MCP 프로토콜 및 메모리 관리]]></title>
            <link>https://velog.io/@neo_blockchain/SpoonOS-GraphAgent-%EB%8F%84%EA%B5%AC-MCP-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C-%EB%B0%8F-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@neo_blockchain/SpoonOS-GraphAgent-%EB%8F%84%EA%B5%AC-MCP-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C-%EB%B0%8F-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Fri, 06 Feb 2026 03:57:19 GMT</pubDate>
            <description><![CDATA[<h1 id="통합-및-확장">통합 및 확장</h1>
<p>그래프 시스템(Graph System)은 광범위한 SpoonOS 에코시스템과 원활하게 통합됩니다. 이 가이드는 그래프를 에이전트, 도구, MCP 서버 및 메모리 시스템과 연결하는 방법을 다룹니다.</p>
<h2 id="graphagent-통합">GraphAgent 통합</h2>
<p><code>GraphAgent</code>는 그래프 실행을 SpoonOS 에이전트 라이프사이클, 영구 메모리 및 세션 관리와 함께 래핑합니다.</p>
<h3 id="기본적인-graphagent-사용법">기본적인 GraphAgent 사용법</h3>
<pre><code class="language-python">import asyncio
from typing import TypedDict

from spoon_ai.graph import END, StateGraph, GraphAgent


class AnalysisState(TypedDict, total=False):
    input: str
    output: str


async def analyze(state: AnalysisState) -&gt; dict:
    return {&quot;output&quot;: f&quot;분석 완료: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}


def build_analysis_graph() -&gt; StateGraph:
    graph = StateGraph(AnalysisState)
    graph.add_node(&quot;analyze&quot;, analyze)
    graph.set_entry_point(&quot;analyze&quot;)
    graph.add_edge(&quot;analyze&quot;, END)
    return graph  # 중요: 컴파일되지 않은 StateGraph를 반환합니다.


async def main():
    agent = GraphAgent(
        name=&quot;crypto_analyzer&quot;,
        graph=build_analysis_graph(),
        memory_path=&quot;./agent_memory&quot;,
        session_id=&quot;user_123_session&quot;,
        preserve_state=True,  # 실행 간 상태 보존
    )

    result = await agent.run(&quot;BTC 가격 동향 분석&quot;)
    print(result)


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="에이전트-설정">에이전트 설정</h3>
<pre><code class="language-python">from typing import TypedDict

from spoon_ai.graph import END, StateGraph, GraphAgent


class AnalysisState(TypedDict, total=False):
    input: str
    output: str


async def analyze(state: AnalysisState) -&gt; dict:
    return {&quot;output&quot;: f&quot;분석 완료: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}


def build_analysis_graph() -&gt; StateGraph:
    graph = StateGraph(AnalysisState)
    graph.add_node(&quot;analyze&quot;, analyze)
    graph.set_entry_point(&quot;analyze&quot;)
    graph.add_edge(&quot;analyze&quot;, END)
    return graph  # 중요: 컴파일되지 않은 StateGraph


agent = GraphAgent(
    name=&quot;trading_assistant&quot;,
    graph=build_analysis_graph(),  # StateGraph (컴파일되지 않음)
    preserve_state=True,  # 실행 간 상태 유지
    memory_path=&quot;./memory&quot;,  # 메모리 저장 디렉토리
    session_id=&quot;session_abc123&quot;,  # 고유 세션 식별자
    max_metadata_size=1024,  # 선택 사항: 저장되는 메타데이터 크기 제한
)

print(f&quot;설정된 에이전트 세션: {agent.memory.session_id}&quot;)</code></pre>
<h3 id="실행-메타데이터">실행 메타데이터</h3>
<pre><code class="language-python">import asyncio
from typing import TypedDict

from spoon_ai.graph import END, StateGraph, GraphAgent


async def main() -&gt; None:
    class AnalysisState(TypedDict, total=False):
        input: str
        output: str

    async def analyze(state: AnalysisState) -&gt; dict:
        return {&quot;output&quot;: f&quot;분석 완료: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}

    graph = StateGraph(AnalysisState)
    graph.add_node(&quot;analyze&quot;, analyze)
    graph.set_entry_point(&quot;analyze&quot;)
    graph.add_edge(&quot;analyze&quot;, END)

    agent = GraphAgent(
        name=&quot;trading_assistant&quot;,
        graph=graph,  # 컴파일되지 않은 StateGraph
        memory_path=&quot;./memory&quot;,
        session_id=&quot;session_abc123&quot;,
        preserve_state=True,
    )

    # 에이전트 실행
    result = await agent.run(&quot;BTC 전망은 어때?&quot;)
    print(result)

    # 실행 메타데이터 액세스
    metadata = agent.get_execution_metadata()

    print(f&quot;성공 여부: {metadata.get(&#39;execution_successful&#39;)}&quot;)
    print(f&quot;마지막 요청: {metadata.get(&#39;last_request&#39;)}&quot;)
    print(f&quot;타임스탬프: {metadata.get(&#39;execution_time&#39;)}&quot;)


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="세션-관리">세션 관리</h3>
<pre><code class="language-python">from typing import TypedDict

from spoon_ai.graph import END, StateGraph, GraphAgent


class AnalysisState(TypedDict, total=False):
    input: str
    output: str


async def analyze(state: AnalysisState) -&gt; dict:
    return {&quot;output&quot;: f&quot;분석 완료: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}


def build_analysis_graph() -&gt; StateGraph:
    graph = StateGraph(AnalysisState)
    graph.add_node(&quot;analyze&quot;, analyze)
    graph.set_entry_point(&quot;analyze&quot;)
    graph.add_edge(&quot;analyze&quot;, END)
    return graph


agent = GraphAgent(
    name=&quot;session_manager&quot;,
    graph=build_analysis_graph(),
    memory_path=&quot;./memory&quot;,
    session_id=&quot;user_123_session&quot;,
)

# 현재 세션
print(f&quot;현재 세션: {agent.memory.session_id}&quot;)

# 세션 전환
agent.load_session(&quot;user_456_session&quot;)
print(f&quot;현재 세션: {agent.memory.session_id}&quot;)</code></pre>
<hr>
<h2 id="도구-통합">도구 통합</h2>
<p>외부 기능을 사용하기 위해 그래프 노드 내에서 SpoonOS 도구를 사용하십시오.</p>
<h3 id="기본-제공-도구-사용">기본 제공 도구 사용</h3>
<pre><code class="language-python">from typing import Any, TypedDict

from spoon_toolkits.crypto.crypto_powerdata.tools import CryptoPowerDataCEXTool


class MarketState(TypedDict, total=False):
    symbol: str
    market_data: Any
    data_source: str
    tool_error: str


async def fetch_market_data(state: MarketState) -&gt; dict:
    &quot;&quot;&quot;CryptoPowerData 도구를 사용하는 노드.&quot;&quot;&quot;
    symbol = state.get(&quot;symbol&quot;, &quot;BTC&quot;)
    tool = CryptoPowerDataCEXTool()

    result = await tool.execute(
        exchange=&quot;binance&quot;,
        symbol=f&quot;{symbol}/USDT&quot;,
        timeframe=&quot;1h&quot;,
        limit=24,
    )

    if getattr(result, &quot;error&quot;, None):
        return {&quot;market_data&quot;: {}, &quot;data_source&quot;: &quot;binance&quot;, &quot;tool_error&quot;: result.error}

    return {&quot;market_data&quot;: result.output, &quot;data_source&quot;: &quot;binance&quot;}</code></pre>
<h3 id="도구-결과-처리">도구 결과 처리</h3>
<pre><code class="language-python">import asyncio
import os
from typing import Any, TypedDict


try:
    from spoon_toolkits.crypto.crypto_data_tools.price_data import GetTokenPriceTool
except Exception:  # pragma: no cover - 선택적 종속성
    GetTokenPriceTool = None


class ProcessState(TypedDict, total=False):
    symbol: str
    tool_status: str
    tool_error: str
    tool_output: Any


async def process_with_tool(state: ProcessState) -&gt; dict:
    &quot;&quot;&quot;오류 처리가 포함된 강력한 도구 사용.&quot;&quot;&quot;
    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot; or GetTokenPriceTool is None:
        return {&quot;tool_status&quot;: &quot;skipped&quot;, &quot;tool_output&quot;: {&quot;reason&quot;: &quot;DOC_SNIPPET_MODE 또는 툴킷 누락&quot;}}

    tool = GetTokenPriceTool()
    try:
        result = await tool.execute(symbol=state.get(&quot;symbol&quot;, &quot;ETH-USDC&quot;))
        if getattr(result, &quot;error&quot;, None):
            return {&quot;tool_status&quot;: &quot;error&quot;, &quot;tool_error&quot;: result.error}
        return {&quot;tool_status&quot;: &quot;success&quot;, &quot;tool_output&quot;: result.output}
    except Exception as e:
        return {&quot;tool_status&quot;: &quot;error&quot;, &quot;tool_error&quot;: str(e)}


async def main() -&gt; None:
    print(await process_with_tool({&quot;symbol&quot;: &quot;ETH-USDC&quot;}))


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="한-노드에서-여러-도구-사용">한 노드에서 여러 도구 사용</h3>
<pre><code class="language-python">import asyncio
import os
from typing import Any, Dict, TypedDict


try:
    from spoon_toolkits.crypto.crypto_powerdata.tools import CryptoPowerDataCEXTool
except Exception:  # pragma: no cover - 선택적 종속성
    CryptoPowerDataCEXTool = None

try:
    from spoon_toolkits.crypto.crypto_data_tools.price_data import GetTokenPriceTool
except Exception:  # pragma: no cover - 선택적 종속성
    GetTokenPriceTool = None


class AnalysisState(TypedDict, total=False):
    symbol: str
    price_data: Any
    dex_price: Any


async def comprehensive_analysis(state: AnalysisState) -&gt; dict:
    &quot;&quot;&quot;여러 도구를 조율하는 노드.&quot;&quot;&quot;
    symbol = state.get(&quot;symbol&quot;, &quot;BTC&quot;)

    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot; or CryptoPowerDataCEXTool is None or GetTokenPriceTool is None:
        return {
            &quot;price_data&quot;: {&quot;source&quot;: &quot;stub&quot;, &quot;symbol&quot;: symbol},
            &quot;dex_price&quot;: {&quot;source&quot;: &quot;stub&quot;, &quot;symbol&quot;: symbol},
        }

    results: Dict[str, Any] = {}

    # 도구 1: 가격 데이터
    price_tool = CryptoPowerDataCEXTool()
    price_data = await price_tool.execute(
        exchange=&quot;binance&quot;,
        symbol=f&quot;{symbol}/USDT&quot;,
        timeframe=&quot;1h&quot;,
        limit=24,
    )
    results[&quot;price_data&quot;] = price_data.output if not getattr(price_data, &quot;error&quot;, None) else {&quot;error&quot;: price_data.error}

    # 도구 2: DEX 스팟 가격 스냅샷
    dex_tool = GetTokenPriceTool()
    dex_price = await dex_tool.execute(symbol=f&quot;{symbol}-USDC&quot;, exchange=&quot;uniswap&quot;)
    results[&quot;dex_price&quot;] = dex_price.output if not getattr(dex_price, &quot;error&quot;, None) else {&quot;error&quot;: dex_price.error}

    return results


async def main() -&gt; None:
    result = await comprehensive_analysis({&quot;symbol&quot;: &quot;BTC&quot;})
    print(result)


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<hr>
<h2 id="mcp-프로토콜-통합">MCP 프로토콜 통합</h2>
<p>동적 도구 검색을 위해 MCP(Model Context Protocol) 서버에 연결하십시오.</p>
<h3 id="mcp-개요">MCP 개요</h3>
<pre><code class="language-mermaid">graph LR
    A[그래프 노드] --&gt; B[MCP 클라이언트]
    B --&gt; C[MCP 서버]
    C --&gt; D[외부 도구]
    C --&gt; E[데이터 소스]
    C --&gt; F[API]</code></pre>
<h3 id="그래프에서-mcp-도구-사용">그래프에서 MCP 도구 사용</h3>
<pre><code class="language-python">import os
from typing import Any, TypedDict

from spoon_ai.tools.mcp_tool import MCPTool


class SearchState(TypedDict, total=False):
    query: str
    search_results: Any


tavily_tool = MCPTool(
    name=&quot;tavily-search&quot;,
    description=&quot;Tavily를 통한 웹 검색&quot;,
    mcp_config={
        &quot;command&quot;: &quot;npx&quot;,
        &quot;args&quot;: [&quot;--yes&quot;, &quot;tavily-mcp&quot;],
        &quot;env&quot;: {&quot;TAVILY_API_KEY&quot;: os.getenv(&quot;TAVILY_API_KEY&quot;, &quot;&quot;)},
    },
)


async def mcp_search_node(state: SearchState) -&gt; dict:
    &quot;&quot;&quot;MCP 도구를 호출하는 노드.&quot;&quot;&quot;
    if not os.getenv(&quot;TAVILY_API_KEY&quot;):
        return {&quot;search_results&quot;: &quot;건너뜀: TAVILY_API_KEY가 설정되지 않음&quot;}

    result = await tavily_tool.execute(query=state.get(&quot;query&quot;, &quot;&quot;), max_results=5)
    return {&quot;search_results&quot;: result}

async def main():
    # MCP 도구 노드 테스트
    result = await mcp_search_node({&quot;query&quot;: &quot;Bitcoin price&quot;})
    print(f&quot;\n쿼리: Bitcoin price&quot;)
    print(f&quot;결과: {result.get(&#39;search_results&#39;, &#39;결과 없음&#39;)}&quot;)


if __name__ == &quot;__main__&quot;:
    import asyncio
    asyncio.run(main())</code></pre>
<h3 id="상위-수준의-mcp-통합">상위 수준의 MCP 통합</h3>
<pre><code class="language-python">import os
import asyncio
from typing import TypedDict

from spoon_ai.graph.builder import HighLevelGraphAPI
from spoon_ai.graph.mcp_integration import MCPToolSpec


class MyState(TypedDict, total=False):
    user_query: str


async def main():
    api = HighLevelGraphAPI(MyState)

    # TAVILY_API_KEY 설정 확인
    tavily_key = os.getenv(&quot;TAVILY_API_KEY&quot;, &quot;&quot;).strip()
    if not tavily_key or &quot;...&quot; in tavily_key:
        print(&quot;참고: TAVILY_API_KEY가 설정되지 않았습니다. MCP 도구 등록을 건너뜁니다.&quot;)
        print(&quot;이 예제를 사용하려면 TAVILY_API_KEY 환경 변수를 설정하십시오.&quot;)
        return

    # HighLevelGraphAPI를 사용하여 MCP 도구 등록
    api.register_mcp_tool(
        intent_category=&quot;research&quot;,
        spec=MCPToolSpec(name=&quot;tavily-search&quot;),
        config={
            &quot;command&quot;: &quot;npx&quot;,
            &quot;args&quot;: [&quot;--yes&quot;, &quot;tavily-mcp&quot;],
            &quot;env&quot;: {&quot;TAVILY_API_KEY&quot;: tavily_key},
        },
    )

    # 도구 인스턴스 생성
    tool = api.create_mcp_tool(&quot;tavily-search&quot;)

    if tool:
        result = await tool.execute(query=&quot;Bitcoin price&quot;, max_results=3)
        print(f&quot;검색 결과: {len(str(result))} 자 반환됨&quot;)
    else:
        print(&quot;MCP 도구 생성 실패. 설정을 확인하십시오.&quot;)


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="더-많은-mcp-정보">더 많은 MCP 정보</h3>
<p>다음을 위해 전용 <strong><a href="/docs/core-concepts/mcp-protocol">MCP 프로토콜 가이드</a></strong>를 참조하십시오.</p>
<ul>
<li>서버 유형 (stdio, HTTP, WebSocket)</li>
<li>인증 및 보안</li>
<li>사용자 정의 MCP 서버 개발</li>
<li>고급 도구 검색 패턴</li>
</ul>
<hr>
<h2 id="메모리-관리">메모리 관리</h2>
<p>세션 간에 상태와 대화 기록을 유지합니다.</p>
<h3 id="graphagent-메모리-작업">GraphAgent 메모리 작업</h3>
<pre><code class="language-python">from typing import TypedDict

from spoon_ai.graph import END, StateGraph, GraphAgent


class AnalysisState(TypedDict, total=False):
    input: str
    output: str


async def analyze(state: AnalysisState) -&gt; dict:
    return {&quot;output&quot;: f&quot;분석 완료: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}


def build_analysis_graph() -&gt; StateGraph:
    graph = StateGraph(AnalysisState)
    graph.add_node(&quot;analyze&quot;, analyze)
    graph.set_entry_point(&quot;analyze&quot;)
    graph.add_edge(&quot;analyze&quot;, END)
    return graph


# 메모리가 포함된 에이전트 생성
agent = GraphAgent(
    name=&quot;assistant&quot;,
    graph=build_analysis_graph(),
    memory_path=&quot;./memory&quot;,
    session_id=&quot;user_123&quot;,
)

# 메타데이터 설정
agent.set_memory_metadata(&quot;last_analysis_time&quot;, &quot;2024-01-15T10:30:00Z&quot;)
agent.set_memory_metadata(&quot;user_preferences&quot;, {
    &quot;risk_tolerance&quot;: &quot;medium&quot;,
    &quot;favorite_tokens&quot;: [&quot;BTC&quot;, &quot;ETH&quot;, &quot;SOL&quot;]
})

# 메타데이터 가져오기
last_time = agent.get_memory_metadata(&quot;last_analysis_time&quot;)
prefs = agent.get_memory_metadata(&quot;user_preferences&quot;)

# 통계 가져오기
stats = agent.get_memory_statistics()
print(f&quot;총 메시지 수: {stats[&#39;total_messages&#39;]}&quot;)
print(f&quot;세션 ID: {stats[&#39;session_id&#39;]}&quot;)
print(f&quot;저장 경로: {stats[&#39;storage_path&#39;]}&quot;)
print(f&quot;파일 크기: {stats[&#39;file_size&#39;]} 바이트&quot;)</code></pre>
<h3 id="메모리-검색">메모리 검색</h3>
<pre><code class="language-python">from typing import TypedDict

from spoon_ai.graph import END, StateGraph, GraphAgent


class AnalysisState(TypedDict, total=False):
    input: str
    output: str


async def analyze(state: AnalysisState) -&gt; dict:
    return {&quot;output&quot;: f&quot;분석 완료: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}


def build_analysis_graph() -&gt; StateGraph:
    graph = StateGraph(AnalysisState)
    graph.add_node(&quot;analyze&quot;, analyze)
    graph.set_entry_point(&quot;analyze&quot;)
    graph.add_edge(&quot;analyze&quot;, END)
    return graph


agent = GraphAgent(
    name=&quot;assistant&quot;,
    graph=build_analysis_graph(),
    memory_path=&quot;./memory&quot;,
    session_id=&quot;user_123&quot;,
)

# 예시 메시지 추가 후 검색.
agent.memory.add_message({&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: &quot;여기 짧은 비트코인 분석이 있습니다...&quot;})

matches = agent.search_memory(query=&quot;bitcoin analysis&quot;, limit=5)
for match in matches:
    print(f&quot;내용: {str(match.get(&#39;content&#39;, &#39;&#39;))[:100]}...&quot;)
    print(f&quot;타임스탬프: {match.get(&#39;timestamp&#39;)}&quot;)
    print(&quot;---&quot;)</code></pre>
<h3 id="상태-지속성">상태 지속성</h3>
<pre><code class="language-python">import asyncio
from typing import TypedDict

from spoon_ai.graph import END, StateGraph, GraphAgent


class AgentState(TypedDict, total=False):
    input: str
    favorite_token: str
    output: str


async def process(state: AgentState) -&gt; dict:
    text = (state.get(&quot;input&quot;) or &quot;&quot;).lower()
    if &quot;remember&quot; in text and &quot;favorite token&quot; in text and &quot; is &quot; in text:
        token = state[&quot;input&quot;].split(&quot; is &quot;, 1)[-1].strip().upper()
        return {&quot;favorite_token&quot;: token, &quot;output&quot;: f&quot;알겠습니다. 당신의 최애 토큰이 {token}임을 기억하겠습니다.&quot;}
    if &quot;favorite token&quot; in text:
        token = state.get(&quot;favorite_token&quot;) or &quot;아직 설정되지 않음&quot;
        return {&quot;output&quot;: f&quot;당신의 최애 토큰은 {token}입니다.&quot;}
    return {&quot;output&quot;: f&quot;에코: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}


graph = StateGraph(AgentState)
graph.add_node(&quot;process&quot;, process)
graph.set_entry_point(&quot;process&quot;)
graph.add_edge(&quot;process&quot;, END)

# 에이전트 레이어에서 상태 지속성 활성화.
agent = GraphAgent(
    name=&quot;persistent_agent&quot;,
    graph=graph,  # 컴파일되지 않은 StateGraph
    preserve_state=True,  # 핵심 설정
    memory_path=&quot;./memory&quot;,
)


async def main() -&gt; None:
    # 첫 번째 실행
    print(await agent.run(&quot;나의 최애 토큰이 SOL임을 기억해줘&quot;))

    # 나중 실행 (상태가 보존됨)
    print(await agent.run(&quot;나의 최애 토큰이 뭐야?&quot;))


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="체크포인팅이-포함된-메모리">체크포인팅이 포함된 메모리</h3>
<pre><code class="language-python">from spoon_ai.graph import InMemoryCheckpointer
from typing import TypedDict

from spoon_ai.graph import END, StateGraph, GraphAgent

# 그래프 체크포인팅과 에이전트 메모리 결합
checkpointer = InMemoryCheckpointer(max_checkpoints_per_thread=50)

class MyState(TypedDict, total=False):
    input: str
    output: str


async def process(state: MyState) -&gt; dict:
    return {&quot;output&quot;: f&quot;처리됨: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}


graph = StateGraph(MyState, checkpointer=checkpointer)
graph.add_node(&quot;process&quot;, process)
graph.set_entry_point(&quot;process&quot;)
graph.add_edge(&quot;process&quot;, END)

agent = GraphAgent(
    name=&quot;full_memory_agent&quot;,
    graph=graph,
    memory_path=&quot;./memory&quot;,
    preserve_state=True
)

# 이제 다음을 가집니다:
# 1. 그래프 수준의 체크포인트 (실행 내 복구용)
# 2. 에이전트 수준의 메모리 (실행 간 지속성용)
print(&quot;체크포인팅 활성화됨:&quot;, agent.graph.graph.checkpointer is checkpointer)</code></pre>
<hr>
<h2 id="모니터링-및-디버깅">모니터링 및 디버깅</h2>
<p>실행을 추적하고 문제를 진단합니다.</p>
<h3 id="모니터링-활성화">모니터링 활성화</h3>
<pre><code class="language-python">from typing import TypedDict

from spoon_ai.graph import END, StateGraph


class MonitorState(TypedDict, total=False):
    input: str
    output: str


async def process(state: MonitorState) -&gt; dict:
    return {&quot;output&quot;: f&quot;처리됨: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}


graph = StateGraph(MonitorState)
graph.add_node(&quot;process&quot;, process)
graph.set_entry_point(&quot;process&quot;)
graph.add_edge(&quot;process&quot;, END)

graph.enable_monitoring([&quot;execution_time&quot;, &quot;success_rate&quot;, &quot;routing_performance&quot;, &quot;node_stats&quot;])
app = graph.compile()

async def main():
    # 그래프 실행
    result = await app.invoke({&quot;input&quot;: &quot;test&quot;, &quot;output&quot;: &quot;&quot;})
    print(result)

if __name__ == &quot;__main__&quot;:
    import asyncio
    asyncio.run(main())</code></pre>
<h3 id="실행-지표">실행 지표</h3>
<pre><code class="language-python">import asyncio
from typing import TypedDict

from spoon_ai.graph import END, StateGraph


async def main() -&gt; None:
    class MonitorState(TypedDict, total=False):
        input: str
        output: str

    async def process(state: MonitorState) -&gt; dict:
        return {&quot;output&quot;: f&quot;처리됨: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}

    graph = StateGraph(MonitorState)
    graph.enable_monitoring([&quot;execution_time&quot;, &quot;success_rate&quot;, &quot;routing_performance&quot;, &quot;node_stats&quot;])
    graph.add_node(&quot;process&quot;, process)
    graph.set_entry_point(&quot;process&quot;)
    graph.add_edge(&quot;process&quot;, END)

    app = graph.compile()
    initial_state = {&quot;input&quot;: &quot;hello&quot;, &quot;output&quot;: &quot;&quot;}

    # 그래프 실행
    result = await app.invoke(initial_state)
    print(result)

    # 지표 가져오기
    metrics = app.get_execution_metrics()

    print(&quot;실행 요약:&quot;)
    print(f&quot;  총 실행 수: {metrics[&#39;total_executions&#39;]}&quot;)
    print(f&quot;  성공률: {metrics[&#39;success_rate&#39;]:.1%}&quot;)
    print(f&quot;  평균 실행 시간: {metrics[&#39;avg_execution_time&#39;]:.3f}s&quot;)

    # 노드별 통계
    print(&quot;노드별 통계:&quot;)
    for node, stats in metrics.get(&quot;node_stats&quot;, {}).items():
        print(f&quot;  {node}:&quot;)
        print(f&quot;    호출 수: {stats[&#39;count&#39;]}&quot;)
        print(f&quot;    평균 시간: {stats[&#39;avg_time&#39;]:.3f}s&quot;)
        print(f&quot;    오류율: {stats[&#39;error_rate&#39;]:.1%}&quot;)


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="실행-기록">실행 기록</h3>
<pre><code class="language-python">import asyncio
from typing import TypedDict

from spoon_ai.graph import END, StateGraph


class MonitorState(TypedDict, total=False):
    input: str
    output: str


async def process(state: MonitorState) -&gt; dict:
    return {&quot;output&quot;: f&quot;처리됨: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}


graph = StateGraph(MonitorState)
graph.add_node(&quot;process&quot;, process)
graph.set_entry_point(&quot;process&quot;)
graph.add_edge(&quot;process&quot;, END)
app = graph.compile()


async def main() -&gt; None:
    await app.invoke({&quot;input&quot;: &quot;hello&quot;})
    for step in app.execution_history:
        print(
            f&quot;노드: {step.get(&#39;node_name&#39;)}\n&quot;
            f&quot;  성공: {step.get(&#39;success&#39;)}\n&quot;
            f&quot;  실행 시간: {step.get(&#39;execution_time&#39;, 0.0):.3f}s\n&quot;
        )


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="체크포인트를-사용한-디버깅">체크포인트를 사용한 디버깅</h3>
<pre><code class="language-python"># 상세 로깅 활성화
import logging
logging.getLogger(&quot;spoon_ai.graph&quot;).setLevel(logging.DEBUG)

import asyncio
from typing import TypedDict

from spoon_ai.graph import END, StateGraph


async def main() -&gt; None:
    class DebugState(TypedDict, total=False):
        input: str
        output: str

    async def process(state: DebugState) -&gt; dict:
        return {&quot;output&quot;: f&quot;처리됨: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}

    graph = StateGraph(DebugState)
    graph.add_node(&quot;process&quot;, process)
    graph.set_entry_point(&quot;process&quot;)
    graph.add_edge(&quot;process&quot;, END)
    app = graph.compile()
    initial_state = {&quot;input&quot;: &quot;hello&quot;}

    # 체크포인팅과 함께 실행
    result = await app.invoke(
        initial_state,
        config={&quot;configurable&quot;: {&quot;thread_id&quot;: &quot;debug_session&quot;}},
    )
    print(result)

    # 체크포인트 기록 검사
    config = {&quot;configurable&quot;: {&quot;thread_id&quot;: &quot;debug_session&quot;}}
    for checkpoint in graph.get_state_history(config):
        print(f&quot;노드 실행 후: {checkpoint.metadata.get(&#39;node&#39;)}&quot;)
        print(f&quot;상태: {checkpoint.values}&quot;)
        print(&quot;---&quot;)


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="실시간-모니터링을-위한-스트리밍">실시간 모니터링을 위한 스트리밍</h3>
<pre><code class="language-python"># 실시간 업데이트를 위한 실행 스트리밍
import asyncio
from typing import TypedDict

from spoon_ai.graph import END, StateGraph


async def main() -&gt; None:
    class StreamState(TypedDict, total=False):
        input: str
        output: str

    async def process(state: StreamState) -&gt; dict:
        return {&quot;output&quot;: f&quot;처리됨: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}

    graph = StateGraph(StreamState)
    graph.add_node(&quot;process&quot;, process)
    graph.set_entry_point(&quot;process&quot;)
    graph.add_edge(&quot;process&quot;, END)
    app = graph.compile()
    initial_state = {&quot;input&quot;: &quot;hello&quot;}

    async for update in app.stream(initial_state):
        node = update.get(&quot;__node__&quot;, &quot;unknown&quot;)
        print(f&quot;[{node}] 상태 업데이트: {list(update.keys())}&quot;)

        # 특정 조건 확인
        if update.get(&quot;error&quot;):
            print(f&quot;  오류: {update[&#39;error&#39;]}&quot;)

        if update.get(&quot;confidence&quot;, 0) &lt; 0.5:
            print(f&quot;  낮은 신뢰도: {update.get(&#39;confidence&#39;)}&quot;)


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<hr>
<h2 id="통합-패턴">통합 패턴</h2>
<h3 id="패턴-1-풀스택-에이전트">패턴 1: 풀스택 에이전트</h3>
<pre><code class="language-python">import asyncio
from typing import Any, TypedDict

from spoon_ai.graph import END, StateGraph, GraphAgent, InMemoryCheckpointer

try:
    # 선택 사항: 툴킷 기반 마켓 데이터 도구
    from spoon_toolkits.crypto.crypto_powerdata.tools import CryptoPowerDataCEXTool
except Exception:  # pragma: no cover - 선택적 종속성
    CryptoPowerDataCEXTool = None


class FullStackState(TypedDict, total=False):
    input: str
    symbol: str
    price_data: Any
    output: str


async def extract_symbol(state: FullStackState) -&gt; dict:
    text = (state.get(&quot;input&quot;) or &quot;&quot;).upper()
    if &quot;ETH&quot; in text:
        symbol = &quot;ETH&quot;
    elif &quot;SOL&quot; in text:
        symbol = &quot;SOL&quot;
    else:
        symbol = &quot;BTC&quot;
    return {&quot;symbol&quot;: symbol}


async def fetch_price(state: FullStackState) -&gt; dict:
    if CryptoPowerDataCEXTool is None:
        return {&quot;output&quot;: &quot;건너뜀: spoon_toolkits가 설치되지 않음&quot;}

    tool = CryptoPowerDataCEXTool()
    result = await tool.execute(
        exchange=&quot;binance&quot;,
        symbol=f&quot;{state.get(&#39;symbol&#39;, &#39;BTC&#39;)}/USDT&quot;,
        timeframe=&quot;1h&quot;,
        limit=10,
    )
    if getattr(result, &quot;error&quot;, None):
        return {&quot;output&quot;: f&quot;도구 오류: {result.error}&quot;, &quot;price_data&quot;: {}}
    return {&quot;price_data&quot;: result.output}


async def format_output(state: FullStackState) -&gt; dict:
    if state.get(&quot;output&quot;):
        return {&quot;output&quot;: state[&quot;output&quot;]}
    preview = str(state.get(&quot;price_data&quot;, {}))[:200]
    return {&quot;output&quot;: f&quot;{state.get(&#39;symbol&#39;)}의 가격 데이터를 가져왔습니다: {preview}...&quot;}


def build_graph() -&gt; StateGraph:
    graph = StateGraph(FullStackState, checkpointer=InMemoryCheckpointer())
    graph.enable_monitoring([&quot;execution_time&quot;, &quot;node_stats&quot;])

    graph.add_node(&quot;extract_symbol&quot;, extract_symbol)
    graph.add_node(&quot;fetch_price&quot;, fetch_price)
    graph.add_node(&quot;format_output&quot;, format_output)

    graph.set_entry_point(&quot;extract_symbol&quot;)
    graph.add_edge(&quot;extract_symbol&quot;, &quot;fetch_price&quot;)
    graph.add_edge(&quot;fetch_price&quot;, &quot;format_output&quot;)
    graph.add_edge(&quot;format_output&quot;, END)
    return graph


async def main() -&gt; None:
    agent = GraphAgent(
        name=&quot;full_stack_agent&quot;,
        graph=build_graph(),  # 중요: 컴파일되지 않은 StateGraph를 전달
        memory_path=&quot;./memory&quot;,
        preserve_state=True,
    )

    result = await agent.run(&quot;BTC 분석해줘&quot;)
    print(result)
    print(agent.graph.get_execution_metrics())


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="패턴-2-멀티-에이전트-핸드오프">패턴 2: 멀티 에이전트 핸드오프</h3>
<pre><code class="language-python">import asyncio
from typing import TypedDict

from spoon_ai.graph import END, StateGraph, GraphAgent


class HandoffState(TypedDict, total=False):
    input: str
    output: str


def build_research_graph() -&gt; StateGraph:
    graph = StateGraph(HandoffState)

    async def research(state: HandoffState) -&gt; dict:
        return {&quot;output&quot;: f&quot;연구 결과: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}

    graph.add_node(&quot;research&quot;, research)
    graph.set_entry_point(&quot;research&quot;)
    graph.add_edge(&quot;research&quot;, END)
    return graph


def build_analysis_graph() -&gt; StateGraph:
    graph = StateGraph(HandoffState)

    async def analyze(state: HandoffState) -&gt; dict:
        return {&quot;output&quot;: f&quot;분석: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}

    graph.add_node(&quot;analyze&quot;, analyze)
    graph.set_entry_point(&quot;analyze&quot;)
    graph.add_edge(&quot;analyze&quot;, END)
    return graph


def build_execution_graph() -&gt; StateGraph:
    graph = StateGraph(HandoffState)

    async def execute(state: HandoffState) -&gt; dict:
        return {&quot;output&quot;: f&quot;실행 계획: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}

    graph.add_node(&quot;execute&quot;, execute)
    graph.set_entry_point(&quot;execute&quot;)
    graph.add_edge(&quot;execute&quot;, END)
    return graph


async def main() -&gt; None:
    # 에이전트는 격리되어 있습니다. 입력을 통해 명시적으로 결과를 전달합니다.
    session_id = &quot;handoff_session&quot;
    memory_path = &quot;./memory&quot;

    research_agent = GraphAgent(
        name=&quot;researcher&quot;,
        graph=build_research_graph(),
        session_id=session_id,
        memory_path=memory_path,
    )

    analysis_agent = GraphAgent(
        name=&quot;analyst&quot;,
        graph=build_analysis_graph(),
        session_id=session_id,
        memory_path=memory_path,
    )

    execution_agent = GraphAgent(
        name=&quot;executor&quot;,
        graph=build_execution_graph(),
        session_id=session_id,
        memory_path=memory_path,
    )

    research_result = await research_agent.run(&quot;BTC 시장 연구&quot;)
    analysis_result = await analysis_agent.run(f&quot;분석: {research_result}&quot;)
    execution_result = await execution_agent.run(f&quot;실행: {analysis_result}&quot;)

    print(research_result)
    print(analysis_result)
    print(execution_result)


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="패턴-3-이벤트-기반-그래프">패턴 3: 이벤트 기반 그래프</h3>
<pre><code class="language-python">import asyncio
import json
from typing import Any, Dict, TypedDict

from spoon_ai.graph import END, StateGraph, GraphAgent


class EventState(TypedDict, total=False):
    input: str
    event_type: str
    event_data: Dict[str, Any]
    output: str


def build_event_graph() -&gt; StateGraph:
    graph = StateGraph(EventState)

    async def parse_event(state: EventState) -&gt; dict:
        payload = json.loads(state.get(&quot;input&quot;, &quot;{}&quot;))
        return {&quot;event_type&quot;: payload.get(&quot;event_type&quot;, &quot;&quot;), &quot;event_data&quot;: payload.get(&quot;data&quot;, {})}

    async def process_event(state: EventState) -&gt; dict:
        return {&quot;output&quot;: f&quot;{state.get(&#39;event_type&#39;)} 처리됨: {state.get(&#39;event_data&#39;)}&quot;}

    graph.add_node(&quot;parse_event&quot;, parse_event)
    graph.add_node(&quot;process_event&quot;, process_event)
    graph.set_entry_point(&quot;parse_event&quot;)
    graph.add_edge(&quot;parse_event&quot;, &quot;process_event&quot;)
    graph.add_edge(&quot;process_event&quot;, END)
    return graph


async def handle_event(event_type: str, data: dict) -&gt; str:
    agent = GraphAgent(
        name=&quot;event_processor&quot;,
        graph=build_event_graph(),
        session_id=f&quot;event_{event_type}&quot;,
        memory_path=&quot;./memory&quot;,
    )
    payload = json.dumps({&quot;event_type&quot;: event_type, &quot;data&quot;: data})
    return await agent.run(payload)


async def main() -&gt; None:
    print(await handle_event(&quot;price_alert&quot;, {&quot;symbol&quot;: &quot;BTC&quot;, &quot;price&quot;: 50000}))
    print(await handle_event(&quot;trade_signal&quot;, {&quot;symbol&quot;: &quot;ETH&quot;, &quot;side&quot;: &quot;buy&quot;}))


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SpoonOS] 그래프 빌드하기]]></title>
            <link>https://velog.io/@neo_blockchain/SpoonOS-%EA%B7%B8%EB%9E%98%ED%94%84-%EB%B9%8C%EB%93%9C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@neo_blockchain/SpoonOS-%EA%B7%B8%EB%9E%98%ED%94%84-%EB%B9%8C%EB%93%9C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 30 Jan 2026 02:37:38 GMT</pubDate>
            <description><![CDATA[<p>SpoonOS는 서로 다른 상황에 맞는 세 가지 그래프 빌드 방식을 제공합니다. 워크플로 복잡도와 팀 요구에 맞게 선택하세요.</p>
<p><strong>이번 글에서 다룰 내용:</strong> 명령형, 선언형, 고수준 API와 각각의 사용 시점
<strong>대상 독자:</strong> 핵심 개념을 이해하고 API 스타일을 고르고 싶은 분
<strong>예상 소요 시간:</strong> 약 6–10분</p>
<h2 id="api-비교">API 비교</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>명령형(Imperative)</th>
<th>선언형(Declarative)</th>
<th>고수준(High-Level)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>복잡도</strong></td>
<td>단순</td>
<td>중간</td>
<td>고급</td>
</tr>
<tr>
<td><strong>사용 사례</strong></td>
<td>빠른 프로토타입, 단순 워크플로</td>
<td>대규모 워크플로, 팀 협업</td>
<td>LLM 기반, 동적 라우팅</td>
</tr>
<tr>
<td><strong>직렬화</strong></td>
<td>불가</td>
<td>가능</td>
<td>가능</td>
</tr>
<tr>
<td><strong>코드 스타일</strong></td>
<td>메서드 체이닝</td>
<td>템플릿 객체</td>
<td>자동 추론</td>
</tr>
<tr>
<td><strong>적합한 경우</strong></td>
<td>학습, 소규모 그래프</td>
<td>프로덕션 시스템</td>
<td>지능형 에이전트</td>
</tr>
</tbody></table>
<hr>
<h2 id="명령형-api">명령형 API</h2>
<p>가장 단순한 그래프 빌드 방식입니다. 메서드 호출로 노드와 엣지를 직접 추가합니다.</p>
<h3 id="사용-시점">사용 시점</h3>
<ul>
<li>✅ 빠른 프로토타입과 실험</li>
<li>✅ 단순한 직선형·분기형 워크플로</li>
<li>✅ 그래프 시스템 학습</li>
<li>❌ 대규모·복잡한 워크플로(유지보수 어려움)</li>
<li>❌ 팀 협업(직렬화 미지원)</li>
</ul>
<h3 id="기본-예제">기본 예제</h3>
<pre><code class="language-python">import asyncio
from typing import TypedDict
from spoon_ai.graph import StateGraph, END

class WorkflowState(TypedDict):
    input: str
    step1_result: str
    step2_result: str
    final_result: str

async def step1(state: WorkflowState) -&gt; dict:
    return {&quot;step1_result&quot;: f&quot;Step1 processed: {state[&#39;input&#39;]}&quot;}

async def step2(state: WorkflowState) -&gt; dict:
    return {&quot;step2_result&quot;: f&quot;Step2 processed: {state[&#39;step1_result&#39;]}&quot;}

async def finalize(state: WorkflowState) -&gt; dict:
    return {&quot;final_result&quot;: f&quot;Final: {state[&#39;step2_result&#39;]}&quot;}

# 명령형으로 그래프 구성
graph = StateGraph(WorkflowState)

# 노드 추가
graph.add_node(&quot;step1&quot;, step1)
graph.add_node(&quot;step2&quot;, step2)
graph.add_node(&quot;finalize&quot;, finalize)

# 엣지 추가 (직선 흐름)
graph.add_edge(&quot;step1&quot;, &quot;step2&quot;)
graph.add_edge(&quot;step2&quot;, &quot;finalize&quot;)
graph.add_edge(&quot;finalize&quot;, END)

# 진입점 설정
graph.set_entry_point(&quot;step1&quot;)

# 컴파일 및 실행
app = graph.compile()

async def main():
    result = await app.invoke({
        &quot;input&quot;: &quot;Hello&quot;,
        &quot;step1_result&quot;: &quot;&quot;,
        &quot;step2_result&quot;: &quot;&quot;,
        &quot;final_result&quot;: &quot;&quot;
    })
    print(result[&quot;final_result&quot;])
    # 출력: Final: Step2 processed: Step1 processed: Hello

if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="조건부-라우팅">조건부 라우팅</h3>
<pre><code class="language-python">from spoon_ai.graph import StateGraph, END
from typing import TypedDict

class RouterState(TypedDict):
    query: str
    category: str
    result: str

async def classify(state: RouterState) -&gt; dict:
    query = state[&quot;query&quot;].lower()
    if &quot;price&quot; in query:
        return {&quot;category&quot;: &quot;price&quot;}
    elif &quot;news&quot; in query:
        return {&quot;category&quot;: &quot;news&quot;}
    return {&quot;category&quot;: &quot;general&quot;}

async def handle_price(state: RouterState) -&gt; dict:
    return {&quot;result&quot;: f&quot;Price handler: {state[&#39;query&#39;]}&quot;}

async def handle_news(state: RouterState) -&gt; dict:
    return {&quot;result&quot;: f&quot;News handler: {state[&#39;query&#39;]}&quot;}

async def handle_general(state: RouterState) -&gt; dict:
    return {&quot;result&quot;: f&quot;General handler: {state[&#39;query&#39;]}&quot;}

def route_by_category(state: RouterState) -&gt; str:
    return state.get(&quot;category&quot;, &quot;general&quot;)

# 그래프 구성
graph = StateGraph(RouterState)

graph.add_node(&quot;classify&quot;, classify)
graph.add_node(&quot;price&quot;, handle_price)
graph.add_node(&quot;news&quot;, handle_news)
graph.add_node(&quot;general&quot;, handle_general)

graph.set_entry_point(&quot;classify&quot;)

# 조건부 라우팅
graph.add_conditional_edges(
    &quot;classify&quot;,
    route_by_category,
    {
        &quot;price&quot;: &quot;price&quot;,
        &quot;news&quot;: &quot;news&quot;,
        &quot;general&quot;: &quot;general&quot;
    }
)

# 모든 핸들러는 END로
graph.add_edge(&quot;price&quot;, END)
graph.add_edge(&quot;news&quot;, END)
graph.add_edge(&quot;general&quot;, END)

app = graph.compile()

async def main():
    # 다양한 쿼리로 테스트
    test_queries = [
        &quot;What is the price of Bitcoin?&quot;,
        &quot;Show me crypto news&quot;,
        &quot;Tell me about blockchain&quot;
    ]

    for query in test_queries:
        result = await app.invoke({
            &quot;query&quot;: query,
            &quot;category&quot;: &quot;&quot;,
            &quot;result&quot;: &quot;&quot;
        })
        print(f&quot;Query: {query}&quot;)
        print(f&quot;Category: {result[&#39;category&#39;]}&quot;)
        print(f&quot;Result: {result[&#39;result&#39;]}&quot;)

if __name__ == &quot;__main__&quot;:
    import asyncio
    asyncio.run(main())</code></pre>
<h3 id="api-레퍼런스">API 레퍼런스</h3>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>add_node(name, fn)</code></td>
<td>노드 추가</td>
<td><code>graph.add_node(&quot;process&quot;, my_fn)</code></td>
</tr>
<tr>
<td><code>add_edge(from, to)</code></td>
<td>정적 엣지 추가</td>
<td><code>graph.add_edge(&quot;a&quot;, &quot;b&quot;)</code></td>
</tr>
<tr>
<td><code>add_conditional_edges(source, condition, path_map)</code></td>
<td>조건부 라우팅 추가</td>
<td>위 예제 참고</td>
</tr>
<tr>
<td><code>set_entry_point(name)</code></td>
<td>시작 노드 설정</td>
<td><code>graph.set_entry_point(&quot;start&quot;)</code></td>
</tr>
<tr>
<td><code>compile()</code></td>
<td>실행 가능한 앱 생성</td>
<td><code>app = graph.compile()</code></td>
</tr>
</tbody></table>
<hr>
<h2 id="선언형-api">선언형 API</h2>
<p>템플릿 객체로 그래프를 정의합니다. 대규모 워크플로와 팀 협업에 적합합니다.</p>
<h3 id="사용-시점-1">사용 시점</h3>
<ul>
<li>✅ 대규모·복잡한 워크플로</li>
<li>✅ 팀 협업(직렬화 가능한 템플릿)</li>
<li>✅ 버전 관리 가능한 그래프 정의</li>
<li>✅ 병렬 실행 그룹</li>
<li>❌ 빠른 프로토타입(보일러플레이트가 많음)</li>
</ul>
<h3 id="템플릿-구성-요소">템플릿 구성 요소</h3>
<pre><code class="language-python">from spoon_ai.graph.builder import (
    DeclarativeGraphBuilder,
    GraphTemplate,
    NodeSpec,
    EdgeSpec,
    ParallelGroupSpec,
)
from spoon_ai.graph.config import GraphConfig, ParallelGroupConfig</code></pre>
<table>
<thead>
<tr>
<th>구성 요소</th>
<th>용도</th>
</tr>
</thead>
<tbody><tr>
<td><code>NodeSpec</code></td>
<td>이름, 함수, 선택적 그룹으로 노드 정의</td>
</tr>
<tr>
<td><code>EdgeSpec</code></td>
<td>노드 간 엣지 정의</td>
</tr>
<tr>
<td><code>ParallelGroupSpec</code></td>
<td>동시 실행할 노드 그룹 정의</td>
</tr>
<tr>
<td><code>GraphTemplate</code></td>
<td>모든 스펙을 담는 컨테이너</td>
</tr>
<tr>
<td><code>DeclarativeGraphBuilder</code></td>
<td>템플릿으로 StateGraph 빌드</td>
</tr>
</tbody></table>
<h3 id="기본-선언형-예제">기본 선언형 예제</h3>
<pre><code class="language-python">import asyncio
from typing import TypedDict, Dict, Any
from spoon_ai.graph import END
from spoon_ai.graph.builder import (
    DeclarativeGraphBuilder,
    GraphTemplate,
    NodeSpec,
    EdgeSpec,
)
from spoon_ai.graph.config import GraphConfig

class AnalysisState(TypedDict):
    query: str
    analysis: str
    summary: str

async def analyze(state: AnalysisState) -&gt; dict:
    return {&quot;analysis&quot;: f&quot;Analysis of: {state[&#39;query&#39;]}&quot;}

async def summarize(state: AnalysisState) -&gt; dict:
    return {&quot;summary&quot;: f&quot;Summary: {state[&#39;analysis&#39;]}&quot;}

# 노드 정의
nodes = [
    NodeSpec(&quot;analyze&quot;, analyze),
    NodeSpec(&quot;summarize&quot;, summarize),
]

# 엣지 정의
edges = [
    EdgeSpec(&quot;analyze&quot;, &quot;summarize&quot;),
    EdgeSpec(&quot;summarize&quot;, END),
]

# 템플릿 생성
template = GraphTemplate(
    entry_point=&quot;analyze&quot;,
    nodes=nodes,
    edges=edges,
    config=GraphConfig(max_iterations=50),
)

# 그래프 빌드
builder = DeclarativeGraphBuilder(AnalysisState)
graph = builder.build(template)
app = graph.compile()

async def main():
    result = await app.invoke({
        &quot;query&quot;: &quot;Bitcoin trend&quot;,
        &quot;analysis&quot;: &quot;&quot;,
        &quot;summary&quot;: &quot;&quot;
    })
    print(result[&quot;summary&quot;])

if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="병렬-그룹-사용">병렬 그룹 사용</h3>
<pre><code class="language-python">from typing import Any, Dict, TypedDict

from spoon_ai.graph import END
from spoon_ai.graph.builder import (
    DeclarativeGraphBuilder,
    GraphTemplate,
    NodeSpec,
    EdgeSpec,
    ParallelGroupSpec,
)
from spoon_ai.graph.config import GraphConfig, ParallelGroupConfig

class DataState(TypedDict):
    symbol: str
    binance_data: Dict[str, Any]
    coinbase_data: Dict[str, Any]
    kraken_data: Dict[str, Any]
    aggregated: Dict[str, Any]

async def fetch_binance(state: DataState) -&gt; dict:
    # 시뮬레이션 API 호출
    return {&quot;binance_data&quot;: {&quot;source&quot;: &quot;binance&quot;, &quot;price&quot;: 45000}}

async def fetch_coinbase(state: DataState) -&gt; dict:
    return {&quot;coinbase_data&quot;: {&quot;source&quot;: &quot;coinbase&quot;, &quot;price&quot;: 45050}}

async def fetch_kraken(state: DataState) -&gt; dict:
    return {&quot;kraken_data&quot;: {&quot;source&quot;: &quot;kraken&quot;, &quot;price&quot;: 44980}}

async def aggregate(state: DataState) -&gt; dict:
    prices = [
        state.get(&quot;binance_data&quot;, {}).get(&quot;price&quot;, 0),
        state.get(&quot;coinbase_data&quot;, {}).get(&quot;price&quot;, 0),
        state.get(&quot;kraken_data&quot;, {}).get(&quot;price&quot;, 0),
    ]
    avg_price = sum(prices) / len([p for p in prices if p &gt; 0])
    return {&quot;aggregated&quot;: {&quot;average_price&quot;: avg_price}}

# 병렬 그룹을 지정한 노드 정의
nodes = [
    NodeSpec(&quot;fetch_binance&quot;, fetch_binance, parallel_group=&quot;data_fetch&quot;),
    NodeSpec(&quot;fetch_coinbase&quot;, fetch_coinbase, parallel_group=&quot;data_fetch&quot;),
    NodeSpec(&quot;fetch_kraken&quot;, fetch_kraken, parallel_group=&quot;data_fetch&quot;),
    NodeSpec(&quot;aggregate&quot;, aggregate),
]

# 엣지 정의
edges = [
    EdgeSpec(&quot;fetch_binance&quot;, &quot;aggregate&quot;),
    EdgeSpec(&quot;fetch_coinbase&quot;, &quot;aggregate&quot;),
    EdgeSpec(&quot;fetch_kraken&quot;, &quot;aggregate&quot;),
    EdgeSpec(&quot;aggregate&quot;, END),
]

# 병렬 그룹 정의
parallel_groups = [
    ParallelGroupSpec(
        name=&quot;data_fetch&quot;,
        nodes=[&quot;fetch_binance&quot;, &quot;fetch_coinbase&quot;, &quot;fetch_kraken&quot;],
        config=ParallelGroupConfig(
            join_strategy=&quot;all&quot;,      # 모두 완료 대기
            timeout=30.0,             # 30초 타임아웃
            error_strategy=&quot;collect_errors&quot;,
        )
    )
]

# 템플릿 생성
template = GraphTemplate(
    entry_point=&quot;fetch_binance&quot;,  # 병렬 그룹 진입점
    nodes=nodes,
    edges=edges,
    parallel_groups=parallel_groups,
    config=GraphConfig(max_iterations=50),
)

# 빌드 및 컴파일
builder = DeclarativeGraphBuilder(DataState)
graph = builder.build(template)
app = graph.compile()


async def main():
    # 병렬 그룹 실행 테스트
    result = await app.invoke({
        &quot;symbol&quot;: &quot;BTC&quot;,
        &quot;binance_data&quot;: {},
        &quot;coinbase_data&quot;: {},
        &quot;kraken_data&quot;: {},
        &quot;aggregated&quot;: {}
    })

    print(&quot;Parallel Group Execution Results:&quot;)
    print(f&quot;Symbol: {result[&#39;symbol&#39;]}&quot;)
    print(f&quot;\nBinance Data: {result[&#39;binance_data&#39;]}&quot;)
    print(f&quot;Coinbase Data: {result[&#39;coinbase_data&#39;]}&quot;)
    print(f&quot;Kraken Data: {result[&#39;kraken_data&#39;]}&quot;)
    print(f&quot;\nAggregated Average Price: {result[&#39;aggregated&#39;][&#39;average_price&#39;]}&quot;)


if __name__ == &quot;__main__&quot;:
    import asyncio
    asyncio.run(main())</code></pre>
<h3 id="템플릿-직렬화">템플릿 직렬화</h3>
<p>선언형 템플릿의 장점 중 하나는 직렬화입니다.</p>
<pre><code class="language-python">import json
import tempfile
from pathlib import Path
from typing import TypedDict, List, Dict, Any

from spoon_ai.graph import END
from spoon_ai.graph.builder import GraphTemplate, NodeSpec, EdgeSpec


class DemoState(TypedDict, total=False):
    input: str
    output: str


async def process(state: DemoState) -&gt; dict:
    return {&quot;output&quot;: state.get(&quot;input&quot;, &quot;&quot;)}


template = GraphTemplate(
    entry_point=&quot;process&quot;,
    nodes=[NodeSpec(&quot;process&quot;, process)],
    edges=[EdgeSpec(&quot;process&quot;, END)],
)

# 템플릿 직렬화 (저장/버전 관리용)
template_dict = {
    &quot;entry_point&quot;: template.entry_point,
    &quot;nodes&quot;: [{&quot;name&quot;: n.name, &quot;parallel_group&quot;: n.parallel_group} for n in template.nodes],
    &quot;edges&quot;: [{&quot;start&quot;: e.start, &quot;end&quot;: e.end} for e in template.edges],
}

# 임시 파일로 저장 (반복 실행에 안전)
out_path = Path(tempfile.gettempdir()) / &quot;workflow_template.json&quot;
out_path.write_text(json.dumps(template_dict, indent=2), encoding=&quot;utf-8&quot;)
print(f&quot;Wrote template: {out_path}&quot;)</code></pre>
<hr>
<h2 id="고수준-api">고수준 API</h2>
<p>가장 고급 방식입니다. (선택적으로) LLM으로 의도/파라미터를 추론하고, 사용자 쿼리마다 그래프를 빌드·실행하는 데 도움을 줍니다.</p>
<h3 id="사용-시점-2">사용 시점</h3>
<ul>
<li>✅ 복잡하고 동적인 워크플로</li>
<li>✅ LLM 기반 의사결정</li>
<li>✅ 자연어 파라미터 추출</li>
<li>✅ 적응형 라우팅이 있는 지능형 에이전트</li>
<li>❌ 단순·결정론적 워크플로(과한 선택)</li>
</ul>
<h3 id="주요-구성-요소">주요 구성 요소</h3>
<pre><code class="language-python">from spoon_ai.graph.builder import HighLevelGraphAPI, Intent, GraphTemplate, NodeSpec, EdgeSpec
from spoon_ai.graph.mcp_integration import MCPToolSpec</code></pre>
<table>
<thead>
<tr>
<th>구성 요소</th>
<th>용도</th>
</tr>
</thead>
<tbody><tr>
<td><code>HighLevelGraphAPI</code></td>
<td>지능형 그래프용 메인 인터페이스</td>
</tr>
<tr>
<td><code>Intent</code></td>
<td>의도 분석 결과 (<code>category</code>, <code>confidence</code>, <code>metadata</code>)</td>
</tr>
<tr>
<td><code>GraphTemplate</code> / <code>NodeSpec</code> / <code>EdgeSpec</code></td>
<td><code>StateGraph</code> 빌드에 쓰는 선언형 그래프 정의</td>
</tr>
<tr>
<td><code>MCPToolSpec</code></td>
<td>특정 의도 카테고리에 MCP 도구 등록</td>
</tr>
</tbody></table>
<h3 id="최소-예제-추론된-의도로-템플릿-선택">최소 예제 (추론된 의도로 템플릿 선택)</h3>
<pre><code class="language-python">import asyncio
import json
from typing import Any, Dict, List, TypedDict

from spoon_ai.graph import END
from spoon_ai.graph.builder import HighLevelGraphAPI, Intent, GraphTemplate, NodeSpec, EdgeSpec
from spoon_ai.schema import Message


class AnalysisState(TypedDict, total=False):
    user_query: str
    query_intent: str
    result: str


async def price_handler(state: Dict[str, Any]) -&gt; dict:
    return {&quot;result&quot;: f&quot;Price handler (stub): {state[&#39;user_query&#39;]}&quot;}


async def general_handler(state: Dict[str, Any]) -&gt; dict:
    return {&quot;result&quot;: f&quot;General handler (stub): {state[&#39;user_query&#39;]}&quot;}


def intent_prompt_builder(query: str) -&gt; List[Message]:
    return [
        Message(
            role=&quot;system&quot;,
            content=&#39;Return JSON only: {&quot;category&quot;: &quot;price_query|general_qa&quot;, &quot;confidence&quot;: 0.0-1.0}&#39;,
        ),
        Message(role=&quot;user&quot;, content=query),
    ]


def intent_parser(text: str) -&gt; Dict[str, Any]:
    try:
        return json.loads(text)
    except Exception:
        return {}


def build_template(intent: Intent) -&gt; GraphTemplate:
    if intent.category == &quot;price_query&quot;:
        return GraphTemplate(
            entry_point=&quot;price_handler&quot;,
            nodes=[NodeSpec(&quot;price_handler&quot;, price_handler)],
            edges=[EdgeSpec(&quot;price_handler&quot;, END)],
        )
    return GraphTemplate(
        entry_point=&quot;general_handler&quot;,
        nodes=[NodeSpec(&quot;general_handler&quot;, general_handler)],
        edges=[EdgeSpec(&quot;general_handler&quot;, END)],
    )


async def main():
    api = HighLevelGraphAPI(
        AnalysisState,
        intent_prompt_builder=intent_prompt_builder,
        intent_parser=intent_parser,
    )

    intent, state = await api.build_initial_state(&quot;What is BTC price?&quot;)
    template = build_template(intent)
    graph = api.build_graph(template)
    app = graph.compile()

    result = await app.invoke(state)
    print(&quot;intent:&quot;, intent.category)
    print(&quot;result:&quot;, result[&quot;result&quot;])


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="자동-파라미터-추론-선택">자동 파라미터 추론 (선택)</h3>
<p>고수준 API는 <strong>다음 두 가지를 제공할 때만</strong> 추가 파라미터를 추론해 초기 상태에 병합할 수 있습니다.</p>
<ul>
<li><code>parameter_prompt_builder(query, intent) -&gt; List[Message]</code></li>
<li><code>parameter_parser(text, intent) -&gt; Dict[str, Any]</code></li>
</ul>
<pre><code class="language-python"># 예:
# 사용자 쿼리: &quot;Analyze ETH trend for the past week&quot;
# 파라미터 파서가 반환할 수 있는 값:
# {&quot;symbol&quot;: &quot;ETH&quot;, &quot;timeframe&quot;: &quot;1w&quot;}</code></pre>
<h3 id="mcp-도구-연동-선택">MCP 도구 연동 (선택)</h3>
<pre><code class="language-python">from typing import TypedDict

from spoon_ai.graph.builder import HighLevelGraphAPI
from spoon_ai.graph.mcp_integration import MCPToolSpec


class MyState(TypedDict, total=False):
    user_query: str


api = HighLevelGraphAPI(MyState)

api.register_mcp_tool(
    intent_category=&quot;research&quot;,
    spec=MCPToolSpec(name=&quot;tavily-search&quot;),
    config={
        &quot;command&quot;: &quot;npx&quot;,
        &quot;args&quot;: [&quot;--yes&quot;, &quot;tavily-mcp&quot;],
        &quot;env&quot;: {&quot;TAVILY_API_KEY&quot;: &quot;...&quot;},
    },
)

tool = api.create_mcp_tool(&quot;tavily-search&quot;)</code></pre>
<hr>
<h2 id="모범-사례">모범 사례</h2>
<h3 id="1-단순하게-시작하고-점진적으로-확장">1. 단순하게 시작하고 점진적으로 확장</h3>
<pre><code class="language-python"># 프로토타입은 명령형으로 시작
from typing import TypedDict

from spoon_ai.graph import StateGraph, END
from spoon_ai.graph.builder import GraphTemplate, NodeSpec, EdgeSpec
from spoon_ai.graph.builder import DeclarativeGraphBuilder

class MyState(TypedDict, total=False):
    input: str
    output: str


async def process_fn(state: MyState) -&gt; dict:
    return {&quot;output&quot;: f&quot;processed: {state.get(&#39;input&#39;, &#39;&#39;)}&quot;}


graph = StateGraph(MyState)
graph.add_node(&quot;process&quot;, process_fn)
graph.set_entry_point(&quot;process&quot;)

# 워크플로가 안정되면 선언형으로 전환
template = GraphTemplate(
    entry_point=&quot;process&quot;,
    nodes=[NodeSpec(&quot;process&quot;, process_fn)],
    edges=[EdgeSpec(&quot;process&quot;, END)],
)

# 선언형 그래프 빌드
builder = DeclarativeGraphBuilder(MyState)
declarative_graph = builder.build(template)
declarative_app = declarative_graph.compile()</code></pre>
<h3 id="2-의미-있는-노드-이름-사용">2. 의미 있는 노드 이름 사용</h3>
<pre><code class="language-python"># 좋은 예: 설명적인 이름
from typing import TypedDict

from spoon_ai.graph import StateGraph


class MyState(TypedDict, total=False):
    user_query: str


async def classify_fn(state: MyState) -&gt; dict:
    return {}


async def fetch_fn(state: MyState) -&gt; dict:
    return {}


async def recommend_fn(state: MyState) -&gt; dict:
    return {}


graph = StateGraph(MyState)

graph.add_node(&quot;classify_user_intent&quot;, classify_fn)
graph.add_node(&quot;fetch_market_data&quot;, fetch_fn)
graph.add_node(&quot;generate_recommendation&quot;, recommend_fn)</code></pre>
<h3 id="3-관련-기능-그룹화">3. 관련 기능 그룹화</h3>
<pre><code class="language-python"># 데이터 조회 노드 그룹화
from spoon_ai.graph.builder import ParallelGroupSpec
from spoon_ai.graph.config import ParallelGroupConfig

parallel_groups = [
    ParallelGroupSpec(
        name=&quot;market_data&quot;,
        nodes=[&quot;fetch_price&quot;, &quot;fetch_volume&quot;, &quot;fetch_sentiment&quot;],
        config=ParallelGroupConfig(join_strategy=&quot;all&quot;)
    )
]
print(parallel_groups)</code></pre>
<h3 id="4-모든-라우팅-케이스-처리">4. 모든 라우팅 케이스 처리</h3>
<pre><code class="language-python"># 항상 폴백을 두기
from typing import TypedDict

from spoon_ai.graph import StateGraph, END


class RoutingState(TypedDict, total=False):
    route: str
    output: str


async def classifier(state: RoutingState) -&gt; dict:
    # 실제 그래프에서는 사용자 입력에 따라 intent/category를 설정합니다.
    return {&quot;route&quot;: state.get(&quot;route&quot;, &quot;unknown&quot;)}


async def handler_1(state: RoutingState) -&gt; dict:
    return {&quot;output&quot;: &quot;handled by handler_1&quot;}


async def handler_2(state: RoutingState) -&gt; dict:
    return {&quot;output&quot;: &quot;handled by handler_2&quot;}


async def fallback_handler(state: RoutingState) -&gt; dict:
    return {&quot;output&quot;: &quot;handled by fallback_handler&quot;}


def route_function(state: RoutingState) -&gt; str:
    return state.get(&quot;route&quot;, &quot;unknown&quot;)


graph = StateGraph(RoutingState)
graph.add_node(&quot;classifier&quot;, classifier)
graph.add_node(&quot;handler_1&quot;, handler_1)
graph.add_node(&quot;handler_2&quot;, handler_2)
graph.add_node(&quot;fallback_handler&quot;, fallback_handler)
graph.set_entry_point(&quot;classifier&quot;)

graph.add_conditional_edges(
    &quot;classifier&quot;,
    route_function,
    {
        &quot;known_intent_1&quot;: &quot;handler_1&quot;,
        &quot;known_intent_2&quot;: &quot;handler_2&quot;,
        &quot;unknown&quot;: &quot;fallback_handler&quot;  # 이걸 빼먹지 마세요!
    }
)

graph.add_edge(&quot;handler_1&quot;, END)
graph.add_edge(&quot;handler_2&quot;, END)
graph.add_edge(&quot;fallback_handler&quot;, END)</code></pre>
<h3 id="5-템플릿-문서화">5. 템플릿 문서화</h3>
<pre><code class="language-python">from typing import TypedDict

from spoon_ai.graph import END
from spoon_ai.graph.builder import GraphTemplate, NodeSpec, EdgeSpec
from spoon_ai.graph.config import GraphConfig
from spoon_ai.graph.builder import DeclarativeGraphBuilder

class MyState(TypedDict, total=False):
    input: str
    output: str


async def start(state: MyState) -&gt; dict:
    return {&quot;output&quot;: &quot;ok&quot;}


nodes = [NodeSpec(&quot;start&quot;, start)]
edges = [EdgeSpec(&quot;start&quot;, END)]

template = GraphTemplate(
    entry_point=&quot;start&quot;,
    nodes=nodes,
    edges=edges,
    config=GraphConfig(
        max_iterations=100,
        # 목적 문서화
        # 이 그래프는 암호화폐 가격 및 시장 분석 관련
        # 사용자 쿼리를 LLM 기반 라우팅으로 처리합니다.
    ),
)
# 빌드 및 컴파일
builder = DeclarativeGraphBuilder(MyState)
graph = builder.build(template)
app = graph.compile()</code></pre>
<hr>
<h2 id="비교-요약">비교 요약</h2>
<p><img src="https://velog.velcdn.com/images/neo_blockchain/post/eafdee8d-0289-4aef-9e47-9fc5143fb010/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>상황</th>
<th>권장 API</th>
</tr>
</thead>
<tbody><tr>
<td>학습/프로토타입</td>
<td>명령형</td>
</tr>
<tr>
<td>프로덕션 워크플로</td>
<td>선언형</td>
</tr>
<tr>
<td>팀 협업</td>
<td>선언형</td>
</tr>
<tr>
<td>지능형 에이전트</td>
<td>고수준</td>
</tr>
<tr>
<td>단순 자동화</td>
<td>명령형</td>
</tr>
<tr>
<td>동적 라우팅</td>
<td>고수준</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[SpoonOS 그래프 시스템 핵심 개념 완벽 가이드]]></title>
            <link>https://velog.io/@neo_blockchain/SpoonOS-%EA%B7%B8%EB%9E%98%ED%94%84-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@neo_blockchain/SpoonOS-%EA%B7%B8%EB%9E%98%ED%94%84-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Fri, 23 Jan 2026 04:58:06 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 오늘은 SpoonOS 그래프 시스템의 핵심 개념들을 깊이 있게 살펴보겠습니다. 이 개념들만 제대로 이해하면 어떤 LLM 기반 워크플로우든 구축할 수 있습니다.</p>
<p><strong>이 글에서 배울 내용:</strong> State, Node, Edge, Checkpointing, 그리고 병합(Merge) 동작 방식<br><strong>대상 독자:</strong> Quick Start를 마친 입문자<br><strong>예상 소요 시간:</strong> 약 5~8분</p>
<hr>
<h2 id="📌-전체-구조-미리보기">📌 전체 구조 미리보기</h2>
<p><img src="https://velog.velcdn.com/images/neo_blockchain/post/e3df5968-40f9-4563-898f-77c4fef60cea/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>개념</th>
<th>설명</th>
<th>핵심 포인트</th>
</tr>
</thead>
<tbody><tr>
<td><strong>State</strong></td>
<td>모든 노드가 공유하는 타입이 지정된 딕셔너리</td>
<td>각 노드는 상태를 읽고, 업데이트를 반환</td>
</tr>
<tr>
<td><strong>Node</strong></td>
<td>비동기 함수 (주로 LLM 호출)</td>
<td>단일 책임, 부분 업데이트 반환</td>
</tr>
<tr>
<td><strong>Edge</strong></td>
<td>노드 간 연결</td>
<td>정적, 조건부, 또는 LLM 기반</td>
</tr>
<tr>
<td><strong>Checkpoint</strong></td>
<td>각 노드 실행 전 상태 스냅샷</td>
<td>복구 및 Human-in-the-loop 지원</td>
</tr>
</tbody></table>
<hr>
<h2 id="🗂️-state-상태">🗂️ State (상태)</h2>
<p><strong>State</strong>는 그래프 전체를 흐르는 <code>TypedDict</code>입니다. 모든 노드는 현재 상태를 받아서 다시 병합할 업데이트를 반환합니다.</p>
<h3 id="llm-워크플로우를-위한-state-정의하기">LLM 워크플로우를 위한 State 정의하기</h3>
<pre><code class="language-python">from typing import TypedDict, List, Dict, Any, Optional

class ConversationState(TypedDict):
    # 입력 필드
    user_query: str
    user_id: str

    # LLM 관련 필드
    messages: List[dict]           # 컨텍스트를 위한 대화 기록
    intent: str                    # 분류된 의도
    extracted_params: Dict[str, Any]  # LLM이 추출한 파라미터

    # 처리 필드
    llm_analysis: str              # LLM 분석 결과
    tool_results: Dict[str, Any]   # 도구 호출 결과

    # 출력 필드
    final_response: str            # 사용자에게 보낼 최종 응답
    confidence: float              # LLM의 신뢰도 점수

    # 시스템 필드
    execution_log: List[str]</code></pre>
<h3 id="state-병합-동작-방식">State 병합 동작 방식</h3>
<p>노드가 업데이트를 반환하면, SpoonOS는 이를 기존 상태에 <strong>병합</strong>합니다:</p>
<table>
<thead>
<tr>
<th>필드 타입</th>
<th>병합 전략</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><strong>스칼라</strong> (str, int, float, bool)</td>
<td>교체</td>
<td><code>&quot;old&quot; → &quot;new&quot;</code></td>
</tr>
<tr>
<td><strong>dict</strong></td>
<td>깊은 병합</td>
<td><code>{a: 1} + {b: 2} → {a: 1, b: 2}</code></td>
</tr>
<tr>
<td><strong>list</strong></td>
<td>추가 (최대 100개)</td>
<td><code>[1, 2] + [3] → [1, 2, 3]</code></td>
</tr>
<tr>
<td><strong>None</strong></td>
<td>변경 없음</td>
<td>이전 값 유지</td>
</tr>
</tbody></table>
<pre><code class="language-python">import asyncio
import os
from typing import Any, Dict, TypedDict

from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message


class ConversationState(TypedDict, total=False):
    user_query: str
    intent: str
    extracted_params: Dict[str, Any]
    confidence: float


llm = LLMManager()


async def analyze_with_llm(state: ConversationState) -&gt; dict:
    &quot;&quot;&quot;예시: LLM 노드가 부분 업데이트를 반환합니다.&quot;&quot;&quot;
    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        return {
            &quot;intent&quot;: &quot;price_query&quot;,
            &quot;extracted_params&quot;: {&quot;symbol&quot;: &quot;BTC&quot;},
            &quot;confidence&quot;: 0.92,
        }

    await llm.chat(
        [
            Message(role=&quot;system&quot;, content=&quot;사용자 의도를 분석하고 파라미터를 추출하세요.&quot;),
            Message(role=&quot;user&quot;, content=state[&quot;user_query&quot;]),
        ],
        max_tokens=80,
    )

    # 변경된 필드만 반환
    return {
        &quot;intent&quot;: &quot;price_query&quot;,
        &quot;extracted_params&quot;: {&quot;symbol&quot;: &quot;BTC&quot;},
        &quot;confidence&quot;: 0.92,
    }


async def main() -&gt; None:
    print(await analyze_with_llm({&quot;user_query&quot;: &quot;BTC 가격?&quot;}))


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="state-베스트-프랙티스">State 베스트 프랙티스</h3>
<blockquote>
<p>💡 <strong>가이드라인</strong></p>
<ol>
<li><strong>TypedDict 사용</strong> - IDE 자동완성과 타입 체킹 지원</li>
<li><strong>messages 필드 포함</strong> - 멀티턴 LLM 대화를 위해 필수</li>
<li><strong>신뢰도 추적</strong> - LLM 출력에는 항상 신뢰도 점수 포함</li>
<li><strong>JSON 직렬화 가능하게 유지</strong> - 체크포인팅에 필수</li>
<li><strong>모든 필드 초기화</strong> - invoke 시점에 기본값 제공</li>
</ol>
</blockquote>
<hr>
<h2 id="🔧-node-노드">🔧 Node (노드)</h2>
<p><strong>Node</strong>는 작업을 수행하는 비동기 함수입니다. 주로 LLM 호출, 도구 실행, 데이터 처리 등을 담당합니다.</p>
<h3 id="노드-계약contract">노드 계약(Contract)</h3>
<pre><code class="language-python">import asyncio
import os
from typing import Any, Dict, List, TypedDict

from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message


class MyState(TypedDict, total=False):
    user_query: str
    messages: List[Dict[str, Any]]
    llm_response: str


llm = LLMManager()


async def my_llm_node(state: MyState) -&gt; dict:
    &quot;&quot;&quot;
    노드 함수 시그니처:

    Args:
        state: 현재 그래프 상태 (읽기 전용 뷰)

    Returns:
        dict: 업데이트할 필드들 (상태에 병합됨)
    &quot;&quot;&quot;
    # 상태에서 읽기
    query = state.get(&quot;user_query&quot;, &quot;&quot;)
    messages = state.get(&quot;messages&quot;, [])  # 직렬화를 위한 딕셔너리 리스트

    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        response_text = f&quot;(스텁) 응답: {query}&quot;
    else:
        # 기록을 Message 객체로 변환하고 LLM 호출
        history = [Message(role=m[&quot;role&quot;], content=m[&quot;content&quot;]) for m in messages]
        response = await llm.chat(history + [Message(role=&quot;user&quot;, content=query)], max_tokens=120)
        response_text = response.content

    # 업데이트 반환 (부분만, 전체 상태 아님)
    # JSON 직렬화를 위해 메시지를 딕셔너리로 저장
    return {
        &quot;llm_response&quot;: response_text,
        &quot;messages&quot;: messages + [
            {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: query},
            {&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: response_text}
        ]
    }


async def main() -&gt; None:
    result = await my_llm_node({&quot;user_query&quot;: &quot;안녕하세요&quot;, &quot;messages&quot;: []})
    print(result[&quot;llm_response&quot;])


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="llm을-위한-노드-패턴">LLM을 위한 노드 패턴</h3>
<h4 id="패턴-1-의도-분류-intent-classification">패턴 1: 의도 분류 (Intent Classification)</h4>
<pre><code class="language-python">import asyncio
import os
from typing import Any, Dict, List, TypedDict

from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message


class ConversationState(TypedDict, total=False):
    user_query: str
    intent: str
    confidence: float
    messages: List[Dict[str, Any]]


llm = LLMManager()


async def classify_intent_node(state: ConversationState) -&gt; dict:
    &quot;&quot;&quot;LLM을 사용하여 사용자 의도를 분류합니다.&quot;&quot;&quot;
    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        return {&quot;intent&quot;: &quot;general_question&quot;, &quot;confidence&quot;: 0.9}

    response = await llm.chat(
        [
            Message(
                role=&quot;system&quot;,
                content=(
                    &#39;JSON만 응답하세요: {&quot;intent&quot;: &quot;price_query|analysis_request|trade_command|general_question&quot;, &#39;
                    &#39;&quot;confidence&quot;: 0.0-1.0}&#39;
                ),
            ),
            Message(role=&quot;user&quot;, content=state[&quot;user_query&quot;]),
        ],
        max_tokens=80,
    )

    import json

    result = json.loads(response.content)
    return {&quot;intent&quot;: result[&quot;intent&quot;], &quot;confidence&quot;: result[&quot;confidence&quot;]}


async def main() -&gt; None:
    print(await classify_intent_node({&quot;user_query&quot;: &quot;BTC 가격이 얼마야?&quot;}))


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h4 id="패턴-2-파라미터-추출-parameter-extraction">패턴 2: 파라미터 추출 (Parameter Extraction)</h4>
<pre><code class="language-python">import asyncio
import os
from typing import Any, Dict, TypedDict

from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message


class ConversationState(TypedDict, total=False):
    user_query: str
    extracted_params: Dict[str, Any]


llm = LLMManager()


async def extract_params_node(state: ConversationState) -&gt; dict:
    &quot;&quot;&quot;LLM을 사용하여 자연어에서 파라미터를 추출합니다.&quot;&quot;&quot;
    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        return {&quot;extracted_params&quot;: {&quot;symbol&quot;: &quot;BTC&quot;, &quot;action&quot;: &quot;buy&quot;, &quot;amount&quot;: 0.1, &quot;price_type&quot;: &quot;market&quot;}}

    response = await llm.chat(
        [
            Message(
                role=&quot;system&quot;,
                content=(
                    &#39;트레이딩 파라미터를 JSON으로만 추출하세요. 예시: &#39;
                    &#39;{&quot;symbol&quot;: &quot;BTC&quot;, &quot;action&quot;: &quot;buy&quot;, &quot;amount&quot;: 0.1, &quot;price_type&quot;: &quot;market&quot;}&#39;
                ),
            ),
            Message(role=&quot;user&quot;, content=state[&quot;user_query&quot;]),
        ],
        max_tokens=120,
    )

    import json

    params = json.loads(response.content)
    return {&quot;extracted_params&quot;: params}


async def main() -&gt; None:
    print(await extract_params_node({&quot;user_query&quot;: &quot;시장가로 BTC 0.1개 매수&quot;}))


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h4 id="패턴-3-컨텍스트-기반-분석-analysis-with-context">패턴 3: 컨텍스트 기반 분석 (Analysis with Context)</h4>
<pre><code class="language-python">import asyncio
import os
from typing import Any, Dict, List, TypedDict

from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message


class ConversationState(TypedDict, total=False):
    user_query: str
    intent: str
    tool_results: Dict[str, Any]
    messages: List[Dict[str, Any]]
    llm_analysis: str


llm = LLMManager()


async def analyze_with_context_node(state: ConversationState) -&gt; dict:
    &quot;&quot;&quot;축적된 컨텍스트를 활용한 LLM 분석.&quot;&quot;&quot;
    context = f&quot;&quot;&quot;
사용자 질문: {state.get(&#39;user_query&#39;)}
의도: {state.get(&#39;intent&#39;)}
시장 데이터: {state.get(&#39;tool_results&#39;, {}).get(&#39;market_data&#39;, &#39;N/A&#39;)}
최근 메시지: {state.get(&#39;messages&#39;, [])[-3:]}
&quot;&quot;&quot;

    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        return {&quot;llm_analysis&quot;: f&quot;(스텁) 분석: {state.get(&#39;user_query&#39;, &#39;&#39;)}&quot;}

    response = await llm.chat(
        [
            Message(role=&quot;system&quot;, content=&quot;당신은 전문 암호화폐 분석가입니다. 상세한 분석을 제공하세요.&quot;),
            Message(role=&quot;user&quot;, content=context),
        ],
        max_tokens=200,
    )

    return {&quot;llm_analysis&quot;: response.content}


async def main() -&gt; None:
    result = await analyze_with_context_node(
        {&quot;user_query&quot;: &quot;BTC 분석해줘&quot;, &quot;intent&quot;: &quot;analysis_request&quot;, &quot;tool_results&quot;: {}, &quot;messages&quot;: []}
    )
    print(result[&quot;llm_analysis&quot;])


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h4 id="패턴-4-응답-생성-response-generation">패턴 4: 응답 생성 (Response Generation)</h4>
<pre><code class="language-python">import asyncio
import os
from typing import Any, Dict, List, TypedDict

from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message


class ConversationState(TypedDict, total=False):
    user_query: str
    llm_analysis: str
    tool_results: Dict[str, Any]
    messages: List[Dict[str, Any]]
    final_response: str


llm = LLMManager()


async def generate_response_node(state: ConversationState) -&gt; dict:
    &quot;&quot;&quot;최종 사용자 응답을 생성합니다.&quot;&quot;&quot;
    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        response_text = f&quot;(스텁) 응답: {state.get(&#39;user_query&#39;, &#39;&#39;)}&quot;
    else:
        response = await llm.chat(
            [
                Message(
                    role=&quot;system&quot;,
                    content=&quot;유용하고 간결한 응답을 생성하세요. 명확하고 실행 가능하게.&quot;,
                ),
                Message(
                    role=&quot;user&quot;,
                    content=(
                        f&quot;원래 질문: {state.get(&#39;user_query&#39;)}\n&quot;
                        f&quot;분석: {state.get(&#39;llm_analysis&#39;)}\n&quot;
                        f&quot;데이터: {state.get(&#39;tool_results&#39;, {})}\n&quot;
                    ),
                ),
            ],
            max_tokens=200,
        )
        response_text = response.content

    return {
        &quot;final_response&quot;: response_text,
        &quot;messages&quot;: state.get(&quot;messages&quot;, []) + [{&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: response_text}],
    }


async def main() -&gt; None:
    result = await generate_response_node(
        {&quot;user_query&quot;: &quot;안녕&quot;, &quot;llm_analysis&quot;: &quot;&quot;, &quot;tool_results&quot;: {}, &quot;messages&quot;: []}
    )
    print(result[&quot;final_response&quot;])


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<hr>
<h2 id="🔀-edge-엣지">🔀 Edge (엣지)</h2>
<p><strong>Edge</strong>는 노드 간의 제어 흐름을 정의합니다. 그래프 시스템은 여러 유형의 엣지를 지원합니다.</p>
<h3 id="1-정적-엣지-static-edges">1. 정적 엣지 (Static Edges)</h3>
<p>항상 소스에서 타겟으로 전이합니다:</p>
<pre><code class="language-python">import asyncio
from typing import Any, Dict, List, TypedDict

from spoon_ai.graph import StateGraph, END


class ConversationState(TypedDict, total=False):
    user_query: str
    intent: str
    llm_analysis: str
    final_response: str
    messages: List[Dict[str, Any]]


async def classify_intent_node(state: ConversationState) -&gt; dict:
    return {&quot;intent&quot;: &quot;general_question&quot;}


async def analyze_with_context_node(state: ConversationState) -&gt; dict:
    return {&quot;llm_analysis&quot;: f&quot;(스텁) 분석: {state.get(&#39;user_query&#39;, &#39;&#39;)}&quot;}


async def generate_response_node(state: ConversationState) -&gt; dict:
    return {&quot;final_response&quot;: f&quot;(스텁) 응답: {state.get(&#39;llm_analysis&#39;, &#39;&#39;)}&quot;}


graph = StateGraph(ConversationState)
graph.add_node(&quot;classify&quot;, classify_intent_node)
graph.add_node(&quot;analyze&quot;, analyze_with_context_node)
graph.add_node(&quot;respond&quot;, generate_response_node)

graph.set_entry_point(&quot;classify&quot;)
graph.add_edge(&quot;classify&quot;, &quot;analyze&quot;)
graph.add_edge(&quot;analyze&quot;, &quot;respond&quot;)
graph.add_edge(&quot;respond&quot;, END)

app = graph.compile()


async def main() -&gt; None:
    result = await app.invoke({&quot;user_query&quot;: &quot;비트코인에 대해 설명해줘&quot;})
    print(result[&quot;final_response&quot;])


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="2-조건부-엣지-llm-기반-라우팅">2. 조건부 엣지 (LLM 기반 라우팅)</h3>
<p>LLM 분류에 따라 라우팅합니다:</p>
<pre><code class="language-python">import asyncio
from typing import TypedDict

from spoon_ai.graph import StateGraph, END


class ConversationState(TypedDict, total=False):
    user_query: str
    intent: str
    confidence: float
    output: str


def route_by_intent(state: ConversationState) -&gt; str:
    &quot;&quot;&quot;LLM이 분류한 의도에 따라 라우팅합니다.&quot;&quot;&quot;
    intent = state.get(&quot;intent&quot;, &quot;general&quot;)
    confidence = state.get(&quot;confidence&quot;, 0)

    # 신뢰도가 낮으면 → 명확화 요청
    if confidence &lt; 0.7:
        return &quot;clarify&quot;

    return intent


async def classify(state: ConversationState) -&gt; dict:
    q = (state.get(&quot;user_query&quot;) or &quot;&quot;).lower()
    if &quot;가격&quot; in q or &quot;price&quot; in q:
        return {&quot;intent&quot;: &quot;price_query&quot;, &quot;confidence&quot;: 0.95}
    if &quot;분석&quot; in q or &quot;analy&quot; in q:
        return {&quot;intent&quot;: &quot;analysis_request&quot;, &quot;confidence&quot;: 0.9}
    if &quot;매수&quot; in q or &quot;매도&quot; in q or &quot;buy&quot; in q or &quot;sell&quot; in q:
        return {&quot;intent&quot;: &quot;trade_command&quot;, &quot;confidence&quot;: 0.9}
    return {&quot;intent&quot;: &quot;general_question&quot;, &quot;confidence&quot;: 0.9}


async def fetch_price(state: ConversationState) -&gt; dict:
    return {&quot;output&quot;: &quot;가격 조회 핸들러&quot;}


async def deep_analysis(state: ConversationState) -&gt; dict:
    return {&quot;output&quot;: &quot;분석 핸들러&quot;}


async def confirm_trade(state: ConversationState) -&gt; dict:
    return {&quot;output&quot;: &quot;거래 확인 핸들러&quot;}


async def general_response(state: ConversationState) -&gt; dict:
    return {&quot;output&quot;: &quot;일반 응답 핸들러&quot;}


async def ask_clarification(state: ConversationState) -&gt; dict:
    return {&quot;output&quot;: &quot;명확화 요청 핸들러&quot;}


graph = StateGraph(ConversationState)
graph.add_node(&quot;classify&quot;, classify)
graph.add_node(&quot;fetch_price&quot;, fetch_price)
graph.add_node(&quot;deep_analysis&quot;, deep_analysis)
graph.add_node(&quot;confirm_trade&quot;, confirm_trade)
graph.add_node(&quot;general_response&quot;, general_response)
graph.add_node(&quot;ask_clarification&quot;, ask_clarification)
graph.set_entry_point(&quot;classify&quot;)

graph.add_conditional_edges(
    &quot;classify&quot;,
    route_by_intent,
    {
        &quot;price_query&quot;: &quot;fetch_price&quot;,
        &quot;analysis_request&quot;: &quot;deep_analysis&quot;,
        &quot;trade_command&quot;: &quot;confirm_trade&quot;,
        &quot;general_question&quot;: &quot;general_response&quot;,
        &quot;clarify&quot;: &quot;ask_clarification&quot;,
    },
)

for node in [&quot;fetch_price&quot;, &quot;deep_analysis&quot;, &quot;confirm_trade&quot;, &quot;general_response&quot;, &quot;ask_clarification&quot;]:
    graph.add_edge(node, END)

app = graph.compile()


async def main() -&gt; None:
    result = await app.invoke({&quot;user_query&quot;: &quot;BTC 가격이 얼마야?&quot;})
    print(result[&quot;output&quot;])


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<p><img src="https://velog.velcdn.com/images/neo_blockchain/post/8ecc32b7-1b46-40fe-8f9e-334575429412/image.png" alt=""></p>
<h3 id="3-llm-기반-동적-라우팅">3. LLM 기반 동적 라우팅</h3>
<p>LLM이 직접 다음 단계를 결정하도록 합니다. 가장 간단한 방법은 내장 LLM 라우터를 활성화하고 다음 노드 이름을 선택하게 하는 것입니다.</p>
<pre><code class="language-python">import asyncio
from typing import TypedDict

from spoon_ai.graph import StateGraph, END
from spoon_ai.graph.config import GraphConfig, RouterConfig


class ConversationState(TypedDict, total=False):
    user_query: str
    result: str


async def route(state: ConversationState) -&gt; dict:
    # No-op 진입 노드. LLM 라우팅은 이 노드 후에 실행됩니다.
    return state


async def web_search(state: ConversationState) -&gt; dict:
    return {&quot;result&quot;: f&quot;(스텁) 웹 검색: {state[&#39;user_query&#39;]}&quot;}


async def respond(state: ConversationState) -&gt; dict:
    return {&quot;result&quot;: f&quot;(스텁) 응답: {state[&#39;user_query&#39;]}&quot;}


graph = StateGraph(ConversationState)
graph.add_node(&quot;route&quot;, route)
graph.add_node(&quot;web_search&quot;, web_search)
graph.add_node(&quot;respond&quot;, respond)
graph.set_entry_point(&quot;route&quot;)

# 핸들러 실행 후 그래프 종료
graph.add_edge(&quot;web_search&quot;, END)
graph.add_edge(&quot;respond&quot;, END)

# LLM 라우팅 활성화 및 대상 제한
graph.config = GraphConfig(
    router=RouterConfig(
        allow_llm=True,
        allowed_targets=[&quot;web_search&quot;, &quot;respond&quot;],
        default_target=&quot;respond&quot;,
    )
)
graph.enable_llm_routing(config={&quot;model&quot;: &quot;gpt-4&quot;, &quot;temperature&quot;: 0.1, &quot;max_tokens&quot;: 50})

app = graph.compile()


async def main():
    result = await app.invoke({&quot;user_query&quot;: &quot;오늘 BTC 뉴스 있어?&quot;, &quot;result&quot;: &quot;&quot;})
    print(result[&quot;result&quot;])


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="완전한-llm-라우팅-예제">완전한 LLM 라우팅 예제</h3>
<pre><code class="language-python">import asyncio
from typing import TypedDict
from spoon_ai.graph import StateGraph, END
from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message

class RouterState(TypedDict):
    query: str
    intent: str
    confidence: float
    result: str

llm = LLMManager()

async def classify_intent(state: RouterState) -&gt; dict:
    &quot;&quot;&quot;LLM이 신뢰도와 함께 의도를 분류합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;분류 후 JSON으로 응답:
        {&quot;intent&quot;: &quot;price|news|analysis|general&quot;, &quot;confidence&quot;: 0.0-1.0}&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;query&quot;])
    ])

    import json
    result = json.loads(response.content)
    return {&quot;intent&quot;: result[&quot;intent&quot;], &quot;confidence&quot;: result[&quot;confidence&quot;]}

async def handle_price(state: RouterState) -&gt; dict:
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;암호화폐 가격 정보를 제공하세요.&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;query&quot;])
    ])
    return {&quot;result&quot;: response.content}

async def handle_news(state: RouterState) -&gt; dict:
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;관련 암호화폐 뉴스를 요약하세요.&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;query&quot;])
    ])
    return {&quot;result&quot;: response.content}

async def handle_analysis(state: RouterState) -&gt; dict:
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;상세한 시장 분석을 제공하세요.&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;query&quot;])
    ])
    return {&quot;result&quot;: response.content}

async def handle_general(state: RouterState) -&gt; dict:
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;당신은 유용한 암호화폐 어시스턴트입니다.&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;query&quot;])
    ])
    return {&quot;result&quot;: response.content}

def route_by_intent(state: RouterState) -&gt; str:
    return state.get(&quot;intent&quot;, &quot;general&quot;)

# 그래프 구축
graph = StateGraph(RouterState)

graph.add_node(&quot;classify&quot;, classify_intent)
graph.add_node(&quot;price_handler&quot;, handle_price)
graph.add_node(&quot;news_handler&quot;, handle_news)
graph.add_node(&quot;analysis_handler&quot;, handle_analysis)
graph.add_node(&quot;general_handler&quot;, handle_general)

graph.set_entry_point(&quot;classify&quot;)

graph.add_conditional_edges(
    &quot;classify&quot;,
    route_by_intent,
    {
        &quot;price&quot;: &quot;price_handler&quot;,
        &quot;news&quot;: &quot;news_handler&quot;,
        &quot;analysis&quot;: &quot;analysis_handler&quot;,
        &quot;general&quot;: &quot;general_handler&quot;
    }
)

graph.add_edge(&quot;price_handler&quot;, END)
graph.add_edge(&quot;news_handler&quot;, END)
graph.add_edge(&quot;analysis_handler&quot;, END)
graph.add_edge(&quot;general_handler&quot;, END)

app = graph.compile()


async def main():
    result = await app.invoke(
        {&quot;query&quot;: &quot;비트코인 현재 가격이 얼마야?&quot;, &quot;intent&quot;: &quot;&quot;, &quot;confidence&quot;: 0.0, &quot;result&quot;: &quot;&quot;}
    )
    print(result[&quot;result&quot;])


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<hr>
<h2 id="💾-checkpointing-체크포인팅">💾 Checkpointing (체크포인팅)</h2>
<p><strong>Checkpointing</strong>은 각 노드 실행 전에 상태 스냅샷을 자동으로 저장합니다. 이를 통해 다음이 가능합니다:</p>
<ul>
<li><strong>복구</strong>: 실패 후 마지막 성공 노드부터 재개</li>
<li><strong>멀티턴 대화</strong>: 세션 간 LLM 컨텍스트 유지</li>
<li><strong>Human-in-the-loop</strong>: 사용자 입력을 위해 일시 중지, 새 데이터로 재개</li>
</ul>
<h3 id="checkpointing-설정하기">Checkpointing 설정하기</h3>
<pre><code class="language-python">from typing import Any, Dict, List, TypedDict

from spoon_ai.graph import StateGraph, InMemoryCheckpointer

checkpointer = InMemoryCheckpointer(
    max_checkpoints_per_thread=100
)

class ConversationState(TypedDict, total=False):
    user_query: str
    messages: List[Dict[str, Any]]
    llm_analysis: str

graph = StateGraph(
    ConversationState,
    checkpointer=checkpointer
)
print(&quot;checkpointer 설정됨:&quot;, graph.checkpointer is checkpointer)</code></pre>
<h3 id="멀티턴-llm-대화">멀티턴 LLM 대화</h3>
<pre><code class="language-python">import asyncio

from typing import Any, Dict, List, TypedDict

from spoon_ai.graph import StateGraph, END, InMemoryCheckpointer


class ConversationState(TypedDict, total=False):
    user_query: str
    messages: List[Dict[str, Any]]
    llm_response: str


checkpointer = InMemoryCheckpointer(max_checkpoints_per_thread=100)


async def respond(state: ConversationState) -&gt; dict:
    # 문서용 최소한의 결정론적 &quot;LLM&quot;
    user_query = state.get(&quot;user_query&quot;, &quot;&quot;)
    response_text = f&quot;(스텁) 답변: {user_query}&quot;
    messages = state.get(&quot;messages&quot;, [])
    return {
        &quot;llm_response&quot;: response_text,
        &quot;messages&quot;: messages
        + [{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: user_query}, {&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: response_text}],
    }


graph = StateGraph(ConversationState, checkpointer=checkpointer)
graph.add_node(&quot;respond&quot;, respond)
graph.set_entry_point(&quot;respond&quot;)
graph.add_edge(&quot;respond&quot;, END)
app = graph.compile()


async def main() -&gt; None:
    # 첫 번째 턴
    result = await app.invoke(
        {&quot;user_query&quot;: &quot;비트코인이 뭐야?&quot;, &quot;messages&quot;: []},
        config={&quot;configurable&quot;: {&quot;thread_id&quot;: &quot;user_123_session&quot;}},
    )

    # 두 번째 턴 - LLM이 첫 번째 턴의 컨텍스트를 가지고 있음
    result = await app.invoke(
        {&quot;user_query&quot;: &quot;가격 추세는 어때?&quot;, &quot;messages&quot;: result[&quot;messages&quot;]},
        config={&quot;configurable&quot;: {&quot;thread_id&quot;: &quot;user_123_session&quot;}},
    )

    # LLM은 대화 기록을 통해 &quot;그것&quot;이 비트코인을 가리킨다는 것을 알 수 있음
    print(result)


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h3 id="실패로부터-복구">실패로부터 복구</h3>
<pre><code class="language-python">import asyncio
from typing import TypedDict

from spoon_ai.graph import StateGraph, END, InMemoryCheckpointer


class ConversationState(TypedDict, total=False):
    user_query: str
    llm_analysis: str
    should_fail: bool


checkpointer = InMemoryCheckpointer(max_checkpoints_per_thread=100)


async def maybe_fail(state: ConversationState) -&gt; dict:
    if state.get(&quot;should_fail&quot;):
        raise RuntimeError(&quot;시뮬레이션된 실패&quot;)
    return {&quot;llm_analysis&quot;: f&quot;(스텁) 분석: {state.get(&#39;user_query&#39;, &#39;&#39;)}&quot;}


graph = StateGraph(ConversationState, checkpointer=checkpointer)
graph.add_node(&quot;maybe_fail&quot;, maybe_fail)
graph.set_entry_point(&quot;maybe_fail&quot;)
graph.add_edge(&quot;maybe_fail&quot;, END)
app = graph.compile()


async def main() -&gt; None:
    config = {&quot;configurable&quot;: {&quot;thread_id&quot;: &quot;analysis_session&quot;}}
    try:
        initial_state = {&quot;user_query&quot;: &quot;BTC 분석해줘&quot;, &quot;llm_analysis&quot;: &quot;&quot;, &quot;should_fail&quot;: True}
        result = await app.invoke(initial_state, config=config)
        print(result)
    except Exception as e:
        print(f&quot;실패: {e}&quot;)

        # 마지막 성공 상태 가져오기
        last_state = graph.get_state(config)

        if last_state:
            print(f&quot;마지막 노드: {last_state.metadata.get(&#39;node&#39;)}&quot;)
            print(f&quot;체크포인트 값: {last_state.values}&quot;)


if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<hr>
<h2 id="🎯-핵심-정리">🎯 핵심 정리</h2>
<ol>
<li><strong>State는 LLM 컨텍스트를 전달</strong> - 메시지, 추출된 파라미터, 분석 결과가 흐름</li>
<li><strong>Node는 LLM 호출을 캡슐화</strong> - 각 노드는 하나의 LLM 작업을 잘 수행</li>
<li><strong>Edge는 LLM 출력에 따라 라우팅</strong> - 의도 분류가 워크플로우를 주도</li>
<li><strong>Checkpoint는 대화를 가능하게 함</strong> - 멀티턴 컨텍스트가 호출 간에 보존</li>
</ol>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[SpoonOS Graph System으로 시작하기: 2분 완성 가이드]]></title>
            <link>https://velog.io/@neo_blockchain/SpoonOS-Graph-System%EC%9C%BC%EB%A1%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-2%EB%B6%84-%EC%99%84%EC%84%B1-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@neo_blockchain/SpoonOS-Graph-System%EC%9C%BC%EB%A1%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-2%EB%B6%84-%EC%99%84%EC%84%B1-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Fri, 16 Jan 2026 03:47:20 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 오늘은 <strong>SpoonOS Graph System</strong>을 사용해서 LLM(대규모 언어 모델) 기반 그래프 워크플로우를 만드는 방법을 알아보겠습니다. 복잡해 보이지만 실제로는 정말 간단합니다. 2분이면 충분해요!</p>
<h2 id="이-가이드에서-배울-내용">이 가이드에서 배울 내용</h2>
<ul>
<li>✅ 상태(State) 정의하는 방법</li>
<li>✅ 노드(Node) 추가하기</li>
<li>✅ 그래프 실행하기</li>
<li>✅ 체크포인트 읽기</li>
</ul>
<p><strong>예상 소요 시간:</strong> 약 2분<br><strong>난이도:</strong> 초보자 친화적</p>
<h2 id="시작하기-전에">시작하기 전에</h2>
<p>먼저 다음 준비가 필요합니다:</p>
<ol>
<li><strong><a href="../getting-started/installation.md">설치 가이드</a></strong>를 따라 SpoonOS를 설치하세요</li>
<li><code>spoon_ai.graph</code>에서 필요한 API를 import 합니다 (예: <code>StateGraph</code>, <code>END</code>)</li>
</ol>
<p>준비되셨나요? 그럼 바로 시작해볼까요!</p>
<h2 id="첫-번째-그래프-hello-world-llm-없이">첫 번째 그래프: Hello World (LLM 없이)</h2>
<p>가장 간단한 그래프부터 만들어봅시다. 노드 하나, 엣지 하나, 그리고 체크포인트 읽기만 있으면 됩니다.</p>
<pre><code class="language-python">import asyncio
from typing import TypedDict
from spoon_ai.graph import StateGraph, END

class HelloState(TypedDict):
    name: str
    message: str

async def say_hello(state: HelloState) -&gt; dict:
    return {&quot;message&quot;: f&quot;Hello, {state[&#39;name&#39;]}!&quot;}

graph = StateGraph(HelloState)
graph.add_node(&quot;hello&quot;, say_hello)
graph.set_entry_point(&quot;hello&quot;)
graph.add_edge(&quot;hello&quot;, END)
app = graph.compile()

async def main():
    config = {&quot;configurable&quot;: {&quot;thread_id&quot;: &quot;hello-demo&quot;}}
    result = await app.invoke({&quot;name&quot;: &quot;Graph&quot;, &quot;message&quot;: &quot;&quot;}, config=config)
    print(result[&quot;message&quot;])  # Hello, Graph!

    # Checkpoint read: requires thread_id (captures state before node execution)
    snapshot = graph.get_state(config)
    print(&quot;checkpoint values:&quot;, snapshot.values)

if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<p><strong>실행 방법:</strong></p>
<pre><code class="language-bash">python my_first_graph.py</code></pre>
<p>이 코드는 정말 간단하죠? <code>say_hello</code> 함수가 이름을 받아서 인사 메시지를 만들어줍니다. 그래프는 이 함수를 노드로 등록하고, 실행하면 자동으로 상태가 전달되고 업데이트됩니다.</p>
<h2 id="첫-번째-llm-그래프-만들기">첫 번째 LLM 그래프 만들기</h2>
<p>이제 진짜 재미있는 부분입니다! LLM을 사용해서 사용자 쿼리를 분석하는 그래프를 만들어봅시다.</p>
<pre><code class="language-python">import asyncio
from typing import TypedDict, List
from spoon_ai.graph import StateGraph, END
from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message

class ChatState(TypedDict):
    messages: List[dict]
    user_query: str
    llm_response: str

# LLM 초기화
llm = LLMManager()

async def analyze_query(state: ChatState) -&gt; dict:
    &quot;&quot;&quot;LLM을 사용해서 사용자 쿼리를 분석합니다.&quot;&quot;&quot;
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;You are a helpful crypto assistant.&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;user_query&quot;])
    ])
    return {&quot;llm_response&quot;: response.content}

# 그래프 구성
graph = StateGraph(ChatState)
graph.add_node(&quot;analyze&quot;, analyze_query)
graph.set_entry_point(&quot;analyze&quot;)
graph.add_edge(&quot;analyze&quot;, END)
app = graph.compile()

async def main():
    result = await app.invoke({
        &quot;messages&quot;: [],
        &quot;user_query&quot;: &quot;What is Bitcoin?&quot;,
        &quot;llm_response&quot;: &quot;&quot;
    })
    print(result[&quot;llm_response&quot;])

if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<p><strong>실행 방법:</strong></p>
<pre><code class="language-bash">python my_first_graph.py</code></pre>
<p>이제 LLM이 사용자의 질문을 받아서 답변을 생성합니다! 정말 신기하지 않나요?</p>
<h2 id="코드-이해하기">코드 이해하기</h2>
<h3 id="1-상태-스키마-정의하기">1. 상태 스키마 정의하기</h3>
<p>그래프의 핵심은 <strong>상태(State)</strong>입니다. 상태는 그래프 전체를 흐르는 데이터 구조예요.</p>
<pre><code class="language-python">from typing import Any, Dict, List, TypedDict


class ChatState(TypedDict):
    messages: List[Dict[str, Any]]   # 대화 기록
    user_query: str                  # 사용자 입력
    llm_response: str                # LLM 출력</code></pre>
<p><strong>상태의 특징:</strong></p>
<ul>
<li>모든 노드가 상태를 읽고 쓸 수 있습니다</li>
<li>상태는 그래프 전체를 통해 흐릅니다</li>
<li>각 노드는 상태의 일부만 업데이트할 수 있습니다</li>
</ul>
<p>:::tip TypedDict의 장점</p>
<ul>
<li>IDE 자동완성으로 실수를 줄일 수 있어요</li>
<li>타입 체킹으로 버그를 미리 잡을 수 있습니다</li>
<li>코드 자체가 문서가 됩니다
:::</li>
</ul>
<h3 id="2-llm-기반-노드-만들기">2. LLM 기반 노드 만들기</h3>
<p>노드는 비동기 함수로, 상태를 받아서 처리하고 결과를 반환합니다.</p>
<pre><code class="language-python">import os
from typing import Any, Dict, List, TypedDict

from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message


class ChatState(TypedDict, total=False):
    messages: List[Dict[str, Any]]
    user_query: str
    llm_response: str


llm = LLMManager()


async def analyze_query(state: ChatState) -&gt; dict:
    &quot;&quot;&quot;LLM을 사용해서 사용자 쿼리를 분석합니다.&quot;&quot;&quot;
    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        return {&quot;llm_response&quot;: f&quot;(stub) analyzed: {state.get(&#39;user_query&#39;, &#39;&#39;)}&quot;}

    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;You are a helpful crypto assistant.&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;user_query&quot;])
    ], max_tokens=200)
    return {&quot;llm_response&quot;: response.content}</code></pre>
<p><strong>노드의 역할:</strong></p>
<ul>
<li><strong>입력</strong>: 전체 상태 딕셔너리를 받습니다</li>
<li><strong>처리</strong>: LLM 호출, 도구 사용, 외부 API 호출 등을 수행합니다</li>
<li><strong>출력</strong>: 변경된 필드만 부분 업데이트로 반환합니다</li>
</ul>
<h3 id="3-그래프-구성하고-실행하기">3. 그래프 구성하고 실행하기</h3>
<p>이제 그래프를 만들고 실행해봅시다!</p>
<pre><code class="language-python">from typing import Any, Dict, List, TypedDict

from spoon_ai.graph import StateGraph, END


class ChatState(TypedDict, total=False):
    messages: List[Dict[str, Any]]
    user_query: str
    llm_response: str


async def analyze_query(state: ChatState) -&gt; dict:
    # LLM 키 없이도 실행 가능하도록 스텁 버전
    return {&quot;llm_response&quot;: f&quot;(stub) analyzed: {state.get(&#39;user_query&#39;, &#39;&#39;)}&quot;}


graph = StateGraph(ChatState)
graph.add_node(&quot;analyze&quot;, analyze_query)
graph.set_entry_point(&quot;analyze&quot;)
graph.add_edge(&quot;analyze&quot;, END)
app = graph.compile()</code></pre>
<p><strong>핵심 3단계:</strong></p>
<ol>
<li><strong>StateGraph 생성</strong>: 상태 스키마로 그래프를 만듭니다</li>
<li><strong>노드 추가</strong>: <code>.add_node(name, function)</code>으로 노드를 추가합니다</li>
<li><strong>진입점 설정 및 컴파일</strong>: 시작 노드를 설정하고 컴파일합니다</li>
</ol>
<h2 id="멀티-스텝-llm-워크플로우">멀티 스텝 LLM 워크플로우</h2>
<p>실제로는 여러 단계를 거치는 워크플로우가 더 유용하죠. 여러 LLM 호출을 연결해서 더 복잡한 작업을 해봅시다!</p>
<pre><code class="language-python">import asyncio
import os
from typing import TypedDict, List
from spoon_ai.graph import StateGraph, END
from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message

class AnalysisState(TypedDict):
    user_query: str
    intent: str
    analysis: str
    final_response: str

llm = LLMManager()

async def classify_intent(state: AnalysisState) -&gt; dict:
    &quot;&quot;&quot;LLM이 사용자의 의도를 분류합니다.&quot;&quot;&quot;
    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        return {&quot;intent&quot;: &quot;analysis_request&quot;}
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;사용자 쿼리를 다음 중 하나로 분류하세요:
        - price_query: 가격에 대한 질문
        - analysis_request: 시장 분석 요청
        - general_question: 기타 질문
        카테고리 이름만 답변하세요.&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;user_query&quot;])
    ])
    return {&quot;intent&quot;: response.content.strip().lower()}

async def generate_analysis(state: AnalysisState) -&gt; dict:
    &quot;&quot;&quot;LLM이 상세한 분석을 생성합니다.&quot;&quot;&quot;
    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        return {&quot;analysis&quot;: f&quot;(stub) analysis for: {state[&#39;user_query&#39;]}&quot;}
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;You are a crypto analyst. Provide detailed analysis.&quot;),
        Message(role=&quot;user&quot;, content=f&quot;Analyze: {state[&#39;user_query&#39;]}&quot;)
    ])
    return {&quot;analysis&quot;: response.content}

async def format_response(state: AnalysisState) -&gt; dict:
    &quot;&quot;&quot;LLM이 최종 응답을 포맷팅합니다.&quot;&quot;&quot;
    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        return {&quot;final_response&quot;: f&quot;(stub) summary: {state.get(&#39;analysis&#39;, &#39;&#39;)[:80]}...&quot;}
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;Format this analysis into a concise, user-friendly response.&quot;),
        Message(role=&quot;user&quot;, content=f&quot;Intent: {state[&#39;intent&#39;]}\nAnalysis: {state[&#39;analysis&#39;]}&quot;)
    ])
    return {&quot;final_response&quot;: response.content}

# 그래프 구성: classify -&gt; analyze -&gt; format
graph = StateGraph(AnalysisState)
graph.add_node(&quot;classify&quot;, classify_intent)
graph.add_node(&quot;analyze&quot;, generate_analysis)
graph.add_node(&quot;format&quot;, format_response)

graph.set_entry_point(&quot;classify&quot;)
graph.add_edge(&quot;classify&quot;, &quot;analyze&quot;)
graph.add_edge(&quot;analyze&quot;, &quot;format&quot;)
graph.add_edge(&quot;format&quot;, END)

app = graph.compile()

async def main():
    result = await app.invoke({
        &quot;user_query&quot;: &quot;What do you think about Bitcoin&#39;s price trend?&quot;,
        &quot;intent&quot;: &quot;&quot;,
        &quot;analysis&quot;: &quot;&quot;,
        &quot;final_response&quot;: &quot;&quot;
    })
    print(f&quot;Intent: {result[&#39;intent&#39;]}&quot;)
    print(f&quot;Response: {result[&#39;final_response&#39;]}&quot;)

if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h2 id="무슨-일이-일어났나요">무슨 일이 일어났나요?</h2>
<pre><code class="language-mermaid">graph LR
    A[User Query] --&gt; B[Classify Intent&lt;br/&gt;LLM Call 1]
    B --&gt; C[Generate Analysis&lt;br/&gt;LLM Call 2]
    C --&gt; D[Format Response&lt;br/&gt;LLM Call 3]
    D --&gt; E[Final Output]

    style A fill:#e1f5fe
    style E fill:#c8e6c9
    style B fill:#fff3e0
    style C fill:#fff3e0
    style D fill:#fff3e0</code></pre>
<p>이 워크플로우는 다음과 같이 동작합니다:</p>
<ol>
<li><strong>사용자 쿼리 입력</strong>: 사용자가 질문을 제공합니다</li>
<li><strong>의도 분류 (첫 번째 LLM 호출)</strong>: LLM이 질문의 의도를 분류합니다</li>
<li><strong>분석 생성 (두 번째 LLM 호출)</strong>: 분류된 의도에 따라 상세한 분석을 생성합니다</li>
<li><strong>응답 포맷팅 (세 번째 LLM 호출)</strong>: 최종 사용자 친화적인 형태로 포맷팅합니다</li>
<li><strong>최종 결과</strong>: 모든 중간 결과와 최종 결과가 상태에 포함됩니다</li>
</ol>
<p>각 단계가 순차적으로 실행되면서 상태가 점진적으로 업데이트되는 모습을 볼 수 있어요!</p>
<h2 id="빠른-참고-가이드">빠른 참고 가이드</h2>
<table>
<thead>
<tr>
<th>구성 요소</th>
<th>목적</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>StateGraph(schema)</code></td>
<td>새 그래프 생성</td>
<td><code>graph = StateGraph(MyState)</code></td>
</tr>
<tr>
<td><code>.add_node(name, fn)</code></td>
<td>LLM 기반 단계 추가</td>
<td><code>graph.add_node(&quot;analyze&quot;, llm_fn)</code></td>
</tr>
<tr>
<td><code>.add_edge(from, to)</code></td>
<td>노드 연결</td>
<td><code>graph.add_edge(&quot;a&quot;, &quot;b&quot;)</code></td>
</tr>
<tr>
<td><code>.set_entry_point(name)</code></td>
<td>시작 노드 설정</td>
<td><code>graph.set_entry_point(&quot;start&quot;)</code></td>
</tr>
<tr>
<td><code>.compile()</code></td>
<td>실행 준비</td>
<td><code>app = graph.compile()</code></td>
</tr>
<tr>
<td><code>.invoke(state)</code></td>
<td>그래프 실행</td>
<td><code>result = await app.invoke({...})</code></td>
</tr>
</tbody></table>
<hr>
<p><strong>마무리</strong></p>
<p>SpoonOS Graph System은 복잡한 LLM 워크플로우를 간단하게 만들 수 있게 해주는 강력한 도구입니다. 처음에는 어려워 보일 수 있지만, 실제로 사용해보면 정말 직관적이고 편리하다는 걸 느끼실 거예요.</p>
<p>궁금한 점이 있으시면 언제든 댓글로 남겨주세요! 😊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SpoonOS] 2분 만에 LLM 기반 그래프 워크플로우 만들기]]></title>
            <link>https://velog.io/@neo_blockchain/SpoonOS-2%EB%B6%84-%EB%A7%8C%EC%97%90-LLM-%EA%B8%B0%EB%B0%98-%EA%B7%B8%EB%9E%98%ED%94%84-%EC%9B%8C%ED%81%AC%ED%94%8C%EB%A1%9C%EC%9A%B0-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@neo_blockchain/SpoonOS-2%EB%B6%84-%EB%A7%8C%EC%97%90-LLM-%EA%B8%B0%EB%B0%98-%EA%B7%B8%EB%9E%98%ED%94%84-%EC%9B%8C%ED%81%AC%ED%94%8C%EB%A1%9C%EC%9A%B0-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Tue, 06 Jan 2026 04:41:05 GMT</pubDate>
            <description><![CDATA[<h1 id="spoonos-graph-system-빠른-시작-가이드">SpoonOS Graph System 빠른 시작 가이드</h1>
<p>SpoonOS Graph System을 사용해서 2분 만에 LLM 기반 그래프 워크플로우를 만들어보세요. 이 가이드에서는 단계별로 그래프를 구성하고 실행하는 방법을 자세히 설명합니다.</p>
<p><strong>이번 가이드에서 배울 내용:</strong></p>
<ul>
<li>상태(State) 정의하기</li>
<li>노드(Node) 추가하기</li>
<li>그래프 실행하기</li>
<li>체크포인트(Checkpoint) 읽기</li>
</ul>
<p><strong>대상 독자:</strong> 처음 사용하는 분들
<strong>예상 소요 시간:</strong> 약 2분</p>
<h2 id="시작하기-전에">시작하기 전에</h2>
<p>이 가이드를 따라하기 전에 다음 사항을 확인해주세요:</p>
<ul>
<li><strong><a href="../getting-started/installation.md">시작하기 / 설치</a></strong> 섹션의 단계를 완료했는지 확인</li>
<li><code>spoon_ai.graph</code>에서 공개 API를 import 할 수 있는지 확인 (예: <code>from spoon_ai.graph import StateGraph, END</code>)</li>
</ul>
<h2 id="2분-만에-만드는-hello-world-llm-없이">2분 만에 만드는 Hello World (LLM 없이)</h2>
<p>가장 간단한 실행 가능한 그래프부터 시작해봅시다. 하나의 노드, 하나의 엣지, 그리고 <code>thread_id</code>를 사용한 체크포인트 읽기로 구성되어 있습니다.</p>
<p>이 예제는 그래프의 기본 구조를 이해하는 데 도움이 됩니다. LLM 없이도 그래프가 어떻게 동작하는지 확인할 수 있어요.</p>
<pre><code class="language-python">import asyncio
from typing import TypedDict
from spoon_ai.graph import StateGraph, END

# 상태 스키마 정의: 그래프 전체에서 사용할 데이터 구조
class HelloState(TypedDict):
    name: str      # 사용자 이름
    message: str   # 인사 메시지

# 노드 함수: 상태를 받아서 처리하고 결과를 반환
async def say_hello(state: HelloState) -&gt; dict:
    return {&quot;message&quot;: f&quot;Hello, {state[&#39;name&#39;]}!&quot;}

# 그래프 생성 및 구성
graph = StateGraph(HelloState)           # 상태 스키마로 그래프 생성
graph.add_node(&quot;hello&quot;, say_hello)       # &quot;hello&quot;라는 이름의 노드 추가
graph.set_entry_point(&quot;hello&quot;)           # 진입점 설정
graph.add_edge(&quot;hello&quot;, END)             # hello 노드에서 종료로 연결
app = graph.compile()                    # 그래프 컴파일

async def main():
    # thread_id를 사용한 설정: 같은 thread_id로 실행하면 상태가 유지됨
    config = {&quot;configurable&quot;: {&quot;thread_id&quot;: &quot;hello-demo&quot;}}

    # 그래프 실행: 초기 상태를 전달하고 결과를 받음
    result = await app.invoke({&quot;name&quot;: &quot;Graph&quot;, &quot;message&quot;: &quot;&quot;}, config=config)
    print(result[&quot;message&quot;])  # 출력: Hello, Graph!

    # 체크포인트 읽기: thread_id가 필요함 (노드 실행 전 상태를 캡처)
    snapshot = graph.get_state(config)
    print(&quot;checkpoint values:&quot;, snapshot.values)

if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<p><strong>실행 방법:</strong></p>
<p>터미널에서 다음 명령어를 실행하세요:</p>
<pre><code class="language-bash">python my_first_graph.py</code></pre>
<p>이 코드를 실행하면 &quot;Hello, Graph!&quot;라는 메시지가 출력됩니다. 간단하지만 그래프의 핵심 개념인 상태, 노드, 엣지를 모두 포함하고 있어요.</p>
<h2 id="첫-번째-llm-그래프-만들기">첫 번째 LLM 그래프 만들기</h2>
<p>이제 실제로 LLM을 사용하는 그래프를 만들어봅시다. 사용자 쿼리를 분석하는 완전한 예제입니다.</p>
<p>LLM을 활용하면 단순한 인사말을 넘어서 복잡한 질문에 답변할 수 있는 지능형 시스템을 만들 수 있습니다.</p>
<pre><code class="language-python">import asyncio
from typing import TypedDict, List
from spoon_ai.graph import StateGraph, END
from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message

# 채팅 상태 스키마: 대화에 필요한 모든 정보를 담음
class ChatState(TypedDict):
    messages: List[dict]    # 대화 기록 (이전 메시지들)
    user_query: str         # 사용자 질문
    llm_response: str       # LLM의 응답

# LLM 매니저 초기화: LLM과 통신하기 위한 객체
llm = LLMManager()

# 쿼리 분석 노드: LLM을 사용해서 사용자 질문을 분석
async def analyze_query(state: ChatState) -&gt; dict:
    &quot;&quot;&quot;LLM을 사용하여 사용자 쿼리를 분석합니다.&quot;&quot;&quot;
    # LLM에게 시스템 프롬프트와 사용자 질문을 전달
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;You are a helpful crypto assistant.&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;user_query&quot;])
    ])
    # LLM의 응답을 상태에 저장
    return {&quot;llm_response&quot;: response.content}

# 그래프 구성
graph = StateGraph(ChatState)              # 채팅 상태로 그래프 생성
graph.add_node(&quot;analyze&quot;, analyze_query)   # 분석 노드 추가
graph.set_entry_point(&quot;analyze&quot;)           # 진입점을 analyze로 설정
graph.add_edge(&quot;analyze&quot;, END)             # analyze에서 종료로 연결
app = graph.compile()                       # 컴파일

async def main():
    # 초기 상태로 그래프 실행
    result = await app.invoke({
        &quot;messages&quot;: [],
        &quot;user_query&quot;: &quot;What is Bitcoin?&quot;,
        &quot;llm_response&quot;: &quot;&quot;
    })
    # LLM의 응답 출력
    print(result[&quot;llm_response&quot;])

if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<p><strong>실행 방법:</strong></p>
<pre><code class="language-bash">python my_first_graph.py</code></pre>
<p>이 코드를 실행하면 &quot;What is Bitcoin?&quot;이라는 질문에 대해 LLM이 생성한 답변이 출력됩니다. LLM이 암호화폐에 대한 설명을 제공할 거예요.</p>
<h2 id="코드-이해하기">코드 이해하기</h2>
<p>이제 각 부분이 어떻게 동작하는지 자세히 살펴봅시다.</p>
<h3 id="1-상태-스키마-정의하기">1. 상태 스키마 정의하기</h3>
<p>상태(State)는 그래프 전체를 흐르는 데이터입니다. 모든 노드가 이 상태를 읽고 쓸 수 있어요.</p>
<pre><code class="language-python">from typing import Any, Dict, List, TypedDict

# TypedDict를 사용하면 타입 안정성과 IDE 자동완성을 얻을 수 있습니다
class ChatState(TypedDict):
    messages: List[Dict[str, Any]]   # 대화 기록
    user_query: str                  # 사용자 입력
    llm_response: str                # LLM 출력</code></pre>
<p><strong>상태(State)란?</strong></p>
<ul>
<li>그래프 전체에서 공유되는 데이터 저장소입니다</li>
<li>각 노드는 상태를 읽어서 처리하고, 결과를 상태에 다시 쓸 수 있습니다</li>
<li>상태는 그래프가 실행되는 동안 계속 유지되고 업데이트됩니다</li>
</ul>
<p>:::tip TypedDict를 사용하는 이유</p>
<ul>
<li><strong>IDE 자동완성</strong>: 코드를 작성할 때 상태 필드 이름을 자동으로 제안해줍니다</li>
<li><strong>타입 체크</strong>: 잘못된 필드 이름이나 타입을 사용하면 미리 오류를 발견할 수 있습니다</li>
<li><strong>자기 문서화</strong>: 코드만 봐도 어떤 데이터가 필요한지 바로 알 수 있습니다</li>
</ul>
<p>:::</p>
<h3 id="2-llm-기반-노드-만들기">2. LLM 기반 노드 만들기</h3>
<p>노드(Node)는 그래프의 작업 단위입니다. 각 노드는 비동기 함수로 구현되며, 상태를 받아서 처리하고 결과를 반환합니다.</p>
<pre><code class="language-python">import os
from typing import Any, Dict, List, TypedDict

from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message

# total=False를 사용하면 모든 필드가 필수가 아닙니다
class ChatState(TypedDict, total=False):
    messages: List[Dict[str, Any]]
    user_query: str
    llm_response: str

# LLM 매니저 초기화
llm = LLMManager()

async def analyze_query(state: ChatState) -&gt; dict:
    &quot;&quot;&quot;LLM을 사용하여 사용자 쿼리를 분석합니다.&quot;&quot;&quot;
    # 문서 스니펫 모드에서는 실제 LLM 호출 없이 스텁 응답 반환
    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        return {&quot;llm_response&quot;: f&quot;(stub) analyzed: {state.get(&#39;user_query&#39;, &#39;&#39;)}&quot;}

    # 실제 LLM 호출: 시스템 프롬프트와 사용자 메시지 전달
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;You are a helpful crypto assistant.&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;user_query&quot;])
    ], max_tokens=200)  # 최대 토큰 수 제한

    # LLM 응답을 상태에 저장
    return {&quot;llm_response&quot;: response.content}</code></pre>
<p><strong>노드의 특징:</strong></p>
<ul>
<li><strong>입력</strong>: 전체 상태 딕셔너리를 받습니다</li>
<li><strong>처리</strong>: LLM 호출, 도구 사용, 외부 API 호출 등 다양한 작업을 수행할 수 있습니다</li>
<li><strong>출력</strong>: 변경된 필드만 딕셔너리로 반환합니다 (부분 업데이트)</li>
</ul>
<p><strong>왜 부분 업데이트를 사용할까요?</strong>
전체 상태를 반환하지 않고 변경된 부분만 반환하면, 코드가 더 간결해지고 실수로 다른 필드를 덮어쓸 위험이 줄어듭니다.</p>
<h3 id="3-그래프-구성-및-실행">3. 그래프 구성 및 실행</h3>
<p>그래프를 만들고 실행하는 과정은 세 단계로 나뉩니다.</p>
<pre><code class="language-python">from typing import Any, Dict, List, TypedDict

from spoon_ai.graph import StateGraph, END

class ChatState(TypedDict, total=False):
    messages: List[Dict[str, Any]]
    user_query: str
    llm_response: str

async def analyze_query(state: ChatState) -&gt; dict:
    # LLM 키 없이도 실행 가능하도록 스텁 응답 반환
    return {&quot;llm_response&quot;: f&quot;(stub) analyzed: {state.get(&#39;user_query&#39;, &#39;&#39;)}&quot;}

# 1단계: 상태 스키마로 그래프 생성
graph = StateGraph(ChatState)

# 2단계: 노드 추가 및 연결
graph.add_node(&quot;analyze&quot;, analyze_query)   # 노드 추가
graph.set_entry_point(&quot;analyze&quot;)            # 진입점 설정
graph.add_edge(&quot;analyze&quot;, END)              # 엣지 추가

# 3단계: 그래프 컴파일 (실행 준비)
app = graph.compile()</code></pre>
<p><strong>세 가지 필수 단계:</strong></p>
<ol>
<li><strong><code>StateGraph(schema)</code></strong>: 상태 스키마를 사용해서 새로운 그래프를 생성합니다</li>
<li><strong><code>.add_node(name, function)</code></strong>: 노드를 추가합니다. 이름과 함수를 지정해주세요</li>
<li><strong><code>.set_entry_point()</code> 및 <code>.compile()</code></strong>: 진입점을 설정하고 그래프를 컴파일합니다</li>
</ol>
<p>컴파일된 그래프는 <code>app.invoke()</code> 메서드를 사용해서 실행할 수 있습니다.</p>
<h2 id="다단계-llm-워크플로우">다단계 LLM 워크플로우</h2>
<p>실제로는 여러 단계를 거쳐서 복잡한 작업을 처리하는 경우가 많습니다. 다음 예제는 여러 번의 LLM 호출을 순차적으로 실행하는 워크플로우입니다.</p>
<p>이 예제에서는 사용자 질문을 받아서:</p>
<ol>
<li>의도 분류</li>
<li>상세 분석 생성</li>
<li>최종 응답 포맷팅</li>
</ol>
<p>이렇게 세 단계로 처리합니다.</p>
<pre><code class="language-python">import asyncio
import os
from typing import TypedDict, List
from spoon_ai.graph import StateGraph, END
from spoon_ai.llm import LLMManager
from spoon_ai.schema import Message

# 분석 상태 스키마: 각 단계의 결과를 저장
class AnalysisState(TypedDict):
    user_query: str        # 사용자 질문
    intent: str            # 분류된 의도
    analysis: str          # 상세 분석
    final_response: str    # 최종 응답

llm = LLMManager()

# 1단계: 의도 분류 노드
async def classify_intent(state: AnalysisState) -&gt; dict:
    &quot;&quot;&quot;LLM이 사용자의 의도를 분류합니다.&quot;&quot;&quot;
    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        return {&quot;intent&quot;: &quot;analysis_request&quot;}

    # LLM에게 의도 분류 작업을 요청
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;&quot;&quot;사용자 쿼리를 다음 중 하나로 분류하세요:
        - price_query: 가격에 대한 질문
        - analysis_request: 시장 분석 요청
        - general_question: 기타 질문
        카테고리 이름만 답변하세요.&quot;&quot;&quot;),
        Message(role=&quot;user&quot;, content=state[&quot;user_query&quot;])
    ])
    return {&quot;intent&quot;: response.content.strip().lower()}

# 2단계: 상세 분석 생성 노드
async def generate_analysis(state: AnalysisState) -&gt; dict:
    &quot;&quot;&quot;LLM이 상세한 분석을 생성합니다.&quot;&quot;&quot;
    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        return {&quot;analysis&quot;: f&quot;(stub) analysis for: {state[&#39;user_query&#39;]}&quot;}

    # 암호화폐 분석가 역할로 상세 분석 생성
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;You are a crypto analyst. Provide detailed analysis.&quot;),
        Message(role=&quot;user&quot;, content=f&quot;Analyze: {state[&#39;user_query&#39;]}&quot;)
    ])
    return {&quot;analysis&quot;: response.content}

# 3단계: 최종 응답 포맷팅 노드
async def format_response(state: AnalysisState) -&gt; dict:
    &quot;&quot;&quot;LLM이 최종 응답을 사용자 친화적으로 포맷팅합니다.&quot;&quot;&quot;
    if os.getenv(&quot;DOC_SNIPPET_MODE&quot;) == &quot;1&quot;:
        return {&quot;final_response&quot;: f&quot;(stub) summary: {state.get(&#39;analysis&#39;, &#39;&#39;)[:80]}...&quot;}

    # 분석 결과를 간결하고 읽기 쉬운 형식으로 변환
    response = await llm.chat([
        Message(role=&quot;system&quot;, content=&quot;Format this analysis into a concise, user-friendly response.&quot;),
        Message(role=&quot;user&quot;, content=f&quot;Intent: {state[&#39;intent&#39;]}\nAnalysis: {state[&#39;analysis&#39;]}&quot;)
    ])
    return {&quot;final_response&quot;: response.content}

# 그래프 구성: classify -&gt; analyze -&gt; format 순서로 실행
graph = StateGraph(AnalysisState)
graph.add_node(&quot;classify&quot;, classify_intent)      # 의도 분류 노드
graph.add_node(&quot;analyze&quot;, generate_analysis)     # 분석 생성 노드
graph.add_node(&quot;format&quot;, format_response)        # 포맷팅 노드

# 노드 간 연결 설정
graph.set_entry_point(&quot;classify&quot;)                # 시작점: classify
graph.add_edge(&quot;classify&quot;, &quot;analyze&quot;)            # classify -&gt; analyze
graph.add_edge(&quot;analyze&quot;, &quot;format&quot;)              # analyze -&gt; format
graph.add_edge(&quot;format&quot;, END)                    # format -&gt; 종료

app = graph.compile()

async def main():
    # 초기 상태로 그래프 실행
    result = await app.invoke({
        &quot;user_query&quot;: &quot;What do you think about Bitcoin&#39;s price trend?&quot;,
        &quot;intent&quot;: &quot;&quot;,
        &quot;analysis&quot;: &quot;&quot;,
        &quot;final_response&quot;: &quot;&quot;
    })

    # 각 단계의 결과 출력
    print(f&quot;Intent: {result[&#39;intent&#39;]}&quot;)
    print(f&quot;Response: {result[&#39;final_response&#39;]}&quot;)

if __name__ == &quot;__main__&quot;:
    asyncio.run(main())</code></pre>
<h2 id="무슨-일이-일어났나요">무슨 일이 일어났나요?</h2>
<p>이 워크플로우의 실행 흐름을 다이어그램으로 보면 다음과 같습니다:</p>
<pre><code class="language-mermaid">graph LR
    A[사용자 질문] --&gt; B[의도 분류&lt;br/&gt;LLM 호출 1]
    B --&gt; C[분석 생성&lt;br/&gt;LLM 호출 2]
    C --&gt; D[응답 포맷팅&lt;br/&gt;LLM 호출 3]
    D --&gt; E[최종 출력]

    style A fill:#e1f5fe
    style E fill:#c8e6c9
    style B fill:#fff3e0
    style C fill:#fff3e0
    style D fill:#fff3e0</code></pre>
<p><strong>실행 순서:</strong></p>
<ol>
<li><strong>사용자 질문 입력</strong>: &quot;비트코인 가격 추세에 대해 어떻게 생각하나요?&quot;</li>
<li><strong>1차 LLM 호출</strong>: 질문의 의도를 분류합니다 (예: &quot;analysis_request&quot;)</li>
<li><strong>2차 LLM 호출</strong>: 분류된 의도에 맞춰 상세한 분석을 생성합니다</li>
<li><strong>3차 LLM 호출</strong>: 생성된 분석을 사용자 친화적인 형식으로 포맷팅합니다</li>
<li><strong>최종 상태</strong>: 모든 중간 결과와 최종 응답이 상태에 저장됩니다</li>
</ol>
<p><strong>왜 여러 단계로 나눌까요?</strong></p>
<ul>
<li>각 단계가 명확한 책임을 가져서 코드가 더 이해하기 쉬워집니다</li>
<li>단계별로 결과를 확인하고 디버깅하기 쉽습니다</li>
<li>필요에 따라 특정 단계만 수정하거나 교체할 수 있습니다</li>
</ul>
<h2 id="빠른-참조-가이드">빠른 참조 가이드</h2>
<p>자주 사용하는 API를 한눈에 확인할 수 있는 치트시트입니다:</p>
<table>
<thead>
<tr>
<th>구성 요소</th>
<th>용도</th>
<th>예제</th>
</tr>
</thead>
<tbody><tr>
<td><code>StateGraph(schema)</code></td>
<td>새로운 그래프 생성</td>
<td><code>graph = StateGraph(MyState)</code></td>
</tr>
<tr>
<td><code>.add_node(name, fn)</code></td>
<td>LLM 기반 단계 추가</td>
<td><code>graph.add_node(&quot;analyze&quot;, llm_fn)</code></td>
</tr>
<tr>
<td><code>.add_edge(from, to)</code></td>
<td>노드 연결</td>
<td><code>graph.add_edge(&quot;a&quot;, &quot;b&quot;)</code></td>
</tr>
<tr>
<td><code>.set_entry_point(name)</code></td>
<td>시작 노드 설정</td>
<td><code>graph.set_entry_point(&quot;start&quot;)</code></td>
</tr>
<tr>
<td><code>.compile()</code></td>
<td>실행 준비</td>
<td><code>app = graph.compile()</code></td>
</tr>
<tr>
<td><code>.invoke(state)</code></td>
<td>그래프 실행</td>
<td><code>result = await app.invoke({...})</code></td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SpoonOS] 장기 기억 메모리]]></title>
            <link>https://velog.io/@neo_blockchain/SpoonOS-%EC%9E%A5%EA%B8%B0-%EA%B8%B0%EC%96%B5-%EB%A9%94%EB%AA%A8%EB%A6%AC</link>
            <guid>https://velog.io/@neo_blockchain/SpoonOS-%EC%9E%A5%EA%B8%B0-%EA%B8%B0%EC%96%B5-%EB%A9%94%EB%AA%A8%EB%A6%AC</guid>
            <pubDate>Wed, 17 Dec 2025 09:06:01 GMT</pubDate>
            <description><![CDATA[<h1 id="장기-메모리-세션-간-기억하기">장기 메모리: 세션 간 기억하기</h1>
<p>장기 메모리를 사용하면 에이전트가 <strong>세션 간에 기억</strong>할 수 있습니다. 단기 메모리(각 대화마다 재설정됨)와 달리, 장기 메모리는 무기한 지속되어 개인화된 경험, 과거 상호작용으로부터 학습, 시간에 따른 지식 구축을 가능하게 합니다.</p>
<h2 id="왜-장기-메모리가-필요할까요">왜 장기 메모리가 필요할까요?</h2>
<p>장기 메모리 없이는 모든 대화가 처음부터 시작됩니다:</p>
<pre><code class="language-text">세션 1: 사용자: &quot;다크 모드를 선호합니다&quot;    에이전트: &quot;알겠습니다!&quot;
세션 2: 사용자: &quot;설정을 변경해주세요&quot;       에이전트: &quot;어떤 설정인가요?&quot; ← 모든 것을 잊어버림</code></pre>
<p>장기 메모리와 함께:</p>
<pre><code class="language-text">세션 1: 사용자: &quot;다크 모드를 선호합니다&quot;    에이전트: &quot;알겠습니다!&quot; → 메모리에 저장
세션 2: 사용자: &quot;설정을 변경해주세요&quot;       에이전트: &quot;다크 모드를 활성화해드리겠습니다&quot; ← 기억함</code></pre>
<h2 id="작동-원리">작동 원리</h2>
<p>SpoonOS는 저장, 인덱싱, 의미 검색을 처리하는 관리형 메모리 서비스인 <a href="https://mem0.ai">Mem0</a>과 통합됩니다:</p>
<p><img src="https://velog.velcdn.com/images/neo_blockchain/post/b515cefe-e8ba-4450-9a72-99d91f81525e/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>기능</th>
<th>도움이 되는 방법</th>
</tr>
</thead>
<tbody><tr>
<td><strong>의미 검색</strong></td>
<td>의미로 메모리 찾기: &quot;사용자 선호도&quot;가 &quot;다크 모드를 좋아합니다&quot;를 찾음</td>
</tr>
<tr>
<td><strong>자동 스코핑</strong></td>
<td>메모리가 사용자/에이전트별로 자동으로 격리됨</td>
</tr>
<tr>
<td><strong>우아한 폴백</strong></td>
<td>Mem0이 다운되어도 작업이 빈 결과를 반환 (크래시 없음)</td>
</tr>
</tbody></table>
<h2 id="무엇을-저장할-수-있나요">무엇을 저장할 수 있나요?</h2>
<table>
<thead>
<tr>
<th>메모리 유형</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><strong>선호도</strong></td>
<td>&quot;사용자는 간결한 응답을 선호합니다&quot;</td>
</tr>
<tr>
<td><strong>사실</strong></td>
<td>&quot;사용자의 포트폴리오에 BTC와 ETH가 포함됩니다&quot;</td>
</tr>
<tr>
<td><strong>컨텍스트</strong></td>
<td>&quot;사용자는 밈 코인에 집중하는 일일 트레이더입니다&quot;</td>
</tr>
<tr>
<td><strong>히스토리</strong></td>
<td>&quot;사용자가 지난주에 Solana DeFi 프로토콜에 대해 물었습니다&quot;</td>
</tr>
</tbody></table>
<hr>
<h2 id="빠른-시작">빠른 시작</h2>
<pre><code class="language-bash">pip install spoon-ai mem0ai
export MEM0_API_KEY=&quot;your-mem0-key&quot;</code></pre>
<pre><code class="language-python">from spoon_ai.memory.mem0_client import SpoonMem0

mem0 = SpoonMem0({&quot;user_id&quot;: &quot;user_123&quot;})

# 저장 및 검색
mem0.add_text(&quot;사용자는 다크 모드를 선호합니다&quot;)
results = mem0.search(&quot;UI 선호도&quot;)
print(results)</code></pre>
<hr>
<p><strong>핵심 클래스:</strong> <code>spoon_ai.memory.mem0_client.SpoonMem0</code></p>
<h3 id="초기화">초기화</h3>
<pre><code class="language-python">from spoon_ai.memory.mem0_client import SpoonMem0

mem0 = SpoonMem0({
    &quot;api_key&quot;: &quot;YOUR_MEM0_API_KEY&quot;,   # 또는 MEM0_API_KEY env 변수
    &quot;user_id&quot;: &quot;user_123&quot;,            # 모든 작업을 이 사용자로 스코핑
    &quot;collection&quot;: &quot;my_namespace&quot;,     # 선택적 네임스페이스 격리
    &quot;metadata&quot;: {&quot;project&quot;: &quot;demo&quot;},  # 쓰기에 자동 첨부
    &quot;filters&quot;: {&quot;project&quot;: &quot;demo&quot;},   # 쿼리에 자동 적용
    &quot;async_mode&quot;: False,              # 동기 쓰기 (기본값)
})

if not mem0.is_ready():
    print(&quot;Mem0 서비스를 사용할 수 없습니다&quot;)</code></pre>
<h3 id="메모리-추가">메모리 추가</h3>
<p>대화 히스토리 또는 개별 텍스트를 저장합니다:</p>
<pre><code class="language-python"># 대화 메시지 추가
mem0.add_memory([
    {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Solana 밈 코인을 좋아합니다&quot;},
    {&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: &quot;알겠습니다, Solana에 집중하겠습니다&quot;},
], user_id=&quot;user_123&quot;)

# 단일 텍스트 추가 (간단한 방법)
mem0.add_text(&quot;사용자는 낮은 가스 수수료를 선호합니다&quot;)</code></pre>
<p>비동기 변형: <code>await mem0.aadd_memory(messages, user_id=...)</code></p>
<h3 id="메모리-검색">메모리 검색</h3>
<pre><code class="language-python">results = mem0.search_memory(
    &quot;Solana 밈 코인 고위험&quot;,
    user_id=&quot;user_123&quot;,
    limit=5,
)
for r in results:  # results는 Mem0 응답에서 추출된 문자열 목록입니다
    print(&quot;-&quot;, r)</code></pre>
<p>비동기 변형: <code>await mem0.asearch_memory(query, user_id=...)</code></p>
<h3 id="모든-메모리-가져오기">모든 메모리 가져오기</h3>
<pre><code class="language-python">all_memories = mem0.get_all_memory(user_id=&quot;user_123&quot;, limit=20)  # 클라이언트가 준비되지 않았거나 호출이 실패하면 [] 반환</code></pre>
<h2 id="데모-지능형-web3-포트폴리오-어시스턴트">데모: 지능형 Web3 포트폴리오 어시스턴트</h2>
<p>경로: <code>examples/mem0_agent_demo.py</code></p>
<p>핵심 아이디어: 에이전트(ChatBot)가 Mem0으로 구성되어 재시작 후에도 사용자 선호도를 기억할 수 있습니다.</p>
<pre><code class="language-python">from spoon_ai.chat import ChatBot

USER_ID = &quot;crypto_whale_001&quot;
SYSTEM_PROMPT = &quot;...포트폴리오 어시스턴트...&quot;

mem0_config = {
    &quot;user_id&quot;: USER_ID,
    &quot;metadata&quot;: {&quot;project&quot;: &quot;web3-portfolio-assistant&quot;},
    &quot;async_mode&quot;: False,  # 다음 쿼리가 데이터를 볼 수 있도록 동기 쓰기
}

# 장기 메모리가 활성화된 LLM 생성
llm = ChatBot(
    llm_provider=&quot;openrouter&quot;,
    model_name=&quot;openai/gpt-5.1&quot;,
    enable_long_term_memory=True,
    mem0_config=mem0_config,
)</code></pre>
<p>흐름:</p>
<ol>
<li><strong>세션 1</strong> – 선호도 캡처: 사용자가 고위험 Solana 밈 트레이더라고 말함; 모델이 응답; Mem0이 상호작용을 저장합니다.</li>
<li><strong>세션 2</strong> – 동일한 <code>mem0_config</code>로 새로운 ChatBot 다시 로드; 에이전트가 답변하기 전에 과거 선호도를 기억합니다 (Mem0 검색을 통해).</li>
<li><strong>세션 3</strong> – 사용자가 안전한 Arbitrum 수익으로 전환; 새 정보가 저장됨; 후속 쿼리가 업데이트된 선호도를 반영합니다.</li>
</ol>
<p>데모 실행:</p>
<pre><code class="language-bash">python examples/mem0_agent_demo.py</code></pre>
<h2 id="참고사항-및-모범-사례">참고사항 및 모범 사례</h2>
<ul>
<li>항상 <code>MEM0_API_KEY</code>를 설정하거나 <code>mem0_config</code>에 <code>api_key</code>를 전달하세요.</li>
<li>메모리가 스코핑되도록 안정적인 <code>user_id</code> (또는 <code>agent_id</code>)를 사용하세요. 더 엄격한 격리가 필요한 경우 <code>collection</code>/<code>filters</code>를 포함하세요. 래퍼는 누락된 경우 <code>user_id</code>를 필터와 메타데이터에 자동으로 주입합니다.</li>
<li>데모/테스트 중에는 읽기 후 쓰기 지연을 피하기 위해 <code>async_mode=False</code>를 유지하세요. 래퍼는 항상 추가에 대해 <code>mem0_config.get(&quot;async_mode&quot;, False)</code>를 사용합니다 (호출당 오버라이드 없음).</li>
<li>부재를 우아하게 처리하세요: <code>SpoonMem0.is_ready()</code>를 사용하면 Mem0이 설치되지 않았거나 구성되지 않은 경우 LTM을 비활성화할 수 있습니다. 그렇지 않으면 클라이언트를 사용할 수 없을 때 헬퍼가 빈 결과를 반환합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SpoonOS] 단기 기억 메모리]]></title>
            <link>https://velog.io/@neo_blockchain/SpoonOS-%EB%8B%A8%EA%B8%B0-%EA%B8%B0%EC%96%B5-%EB%A9%94%EB%AA%A8%EB%A6%AC</link>
            <guid>https://velog.io/@neo_blockchain/SpoonOS-%EB%8B%A8%EA%B8%B0-%EA%B8%B0%EC%96%B5-%EB%A9%94%EB%AA%A8%EB%A6%AC</guid>
            <pubDate>Wed, 17 Dec 2025 09:05:32 GMT</pubDate>
            <description><![CDATA[<h1 id="단기-메모리-대화-컨텍스트-관리하기">단기 메모리: 대화 컨텍스트 관리하기</h1>
<p>단기 메모리는 <strong>현재 대화</strong>를 추적합니다. 에이전트가 세 메시지 전에 &quot;내 이름은 Alice입니다&quot;를 기억하게 하고, 긴 대화에서 컨텍스트 창이 넘치지 않도록 방지합니다.</p>
<h2 id="문제점">문제점</h2>
<p>LLM은 제한된 컨텍스트 창을 가지고 있습니다. 대화가 길어질수록:</p>
<pre><code class="language-text">턴 1:  사용자: &quot;내 이름은 Alice입니다&quot;          ← 10 토큰
턴 50: 사용자: &quot;내 이름이 뭐였지?&quot;             ← 총 50,000 토큰
         에이전트: &quot;모르겠어요&quot; ← 컨텍스트 오버플로우, 이전 메시지 손실</code></pre>
<p>단순한 해결책은 트레이드오프가 있습니다:</p>
<table>
<thead>
<tr>
<th>접근 방식</th>
<th>문제점</th>
</tr>
</thead>
<tbody><tr>
<td><strong>모든 것 유지</strong></td>
<td>컨텍스트 창 초과, 비용 폭증</td>
</tr>
<tr>
<td><strong>오래된 메시지 삭제</strong></td>
<td>중요한 컨텍스트 손실 (&quot;내 이름은 Alice입니다&quot;)</td>
</tr>
<tr>
<td><strong>고정 슬라이딩 윈도우</strong></td>
<td>임의의 잘림, 중요한 정보 손실 가능</td>
</tr>
</tbody></table>
<h2 id="spoonos-솔루션">SpoonOS 솔루션</h2>
<p><code>ShortTermMemoryManager</code>가 컨텍스트를 지능적으로 관리합니다:</p>
<p><img src="https://velog.velcdn.com/images/neo_blockchain/post/36918916-9c61-418e-93df-1431c0abbe8d/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>기능</th>
<th>하는 일</th>
</tr>
</thead>
<tbody><tr>
<td><strong>토큰 인식</strong></td>
<td>메시지 수가 아닌 실제 토큰 수 추적</td>
</tr>
<tr>
<td><strong>스마트 트리밍</strong></td>
<td>여러 전략: 가장 오래된 것부터, 시작부터, 끝부터</td>
</tr>
<tr>
<td><strong>요약</strong></td>
<td>필요할 때 오래된 메시지를 요약으로 압축</td>
</tr>
<tr>
<td><strong>내장</strong></td>
<td><code>ChatBot</code>이 자동으로 처리—추가 코드 불필요</td>
</tr>
</tbody></table>
<h2 id="언제-사용해야-할까요">언제 사용해야 할까요?</h2>
<ul>
<li>여러 턴 대화가 있는 <strong>챗봇</strong></li>
<li>이전 컨텍스트를 기억해야 하는 <strong>에이전트</strong></li>
<li>컨텍스트 제한을 초과할 <strong>긴 세션</strong></li>
<li>토큰 사용량을 줄이기 위한 <strong>비용 최적화</strong></li>
</ul>
<hr>
<h2 id="빠른-시작">빠른 시작</h2>
<pre><code class="language-bash">pip install spoon-ai
export OPENAI_API_KEY=&quot;your-key&quot;</code></pre>
<pre><code class="language-python">import asyncio
from spoon_ai.chat import ChatBot

# ChatBot은 자동 트리밍 기능이 있는 내장 단기 메모리를 포함합니다
llm = ChatBot(model_name=&quot;gpt-5.1-chat-latest&quot;, llm_provider=&quot;openai&quot;)

async def main():
    await llm.ask([{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;내 이름은 Alice입니다&quot;}])
    await llm.ask([{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;프랑스의 수도는 어디인가요?&quot;}])
    response = await llm.ask([{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;내 이름이 뭐였지?&quot;}])  # &quot;Alice&quot;를 기억함
    print(response)

asyncio.run(main())</code></pre>
<hr>
<h2 id="shorttermmemorymanager">ShortTermMemoryManager</h2>
<p><code>ChatBot</code>의 자동 처리 이상의 세밀한 제어가 필요한 경우, <code>ShortTermMemoryManager</code>를 직접 사용하세요:</p>
<pre><code class="language-python">import asyncio
from spoon_ai.memory.short_term_manager import ShortTermMemoryManager, TrimStrategy
from spoon_ai.schema import Message

manager = ShortTermMemoryManager()
history = [
    Message(id=&quot;u1&quot;, role=&quot;user&quot;, content=&quot;안녕하세요!&quot;),
    Message(id=&quot;a1&quot;, role=&quot;assistant&quot;, content=&quot;안녕하세요 — 무엇을 도와드릴까요?&quot;),
    Message(id=&quot;u2&quot;, role=&quot;user&quot;, content=&quot;DeFi가 뭐예요?&quot;),
]


# 토큰 예산에 따라 메시지 목록 트리밍
trimmed = asyncio.run(
    manager.trim_messages(
        messages=history,
        max_tokens=48,
        strategy=TrimStrategy.FROM_END,
        keep_system=True,
    )
)

# 모델 호출 전 히스토리 요약
llm_ready, removals, summary = asyncio.run(
    manager.summarize_messages(
        messages=history,
        max_tokens_before_summary=48,
        messages_to_keep=2,
        summary_model=&quot;anthropic/claude-3.5-sonnet&quot;,
        llm_manager=chatbot.llm_manager,
        llm_provider=chatbot.llm_provider,
        existing_summary=chatbot.latest_summary() or &quot;&quot;,
    )
)</code></pre>
<p><code>llm_ready</code> — LLM에 전달할 수 있는 압축된 히스토리
<code>removals</code> — RemoveMessage 지시문 목록: <code>spoon_ai.graph.reducers.add_messages</code>를 사용하여 지속된 히스토리에 제거 사항을 적용하세요.</p>
<p>참고: <code>summarize_messages()</code>와 <code>ChatBot.ask()</code> 모두 구성된 LLM을 호출합니다. 이러한 예제가 끝까지 실행될 수 있도록 <code>chatbot.llm_manager</code>/<code>chatbot.llm_provider</code> (및 필요한 API 키나 env 변수)가 설정되어 있는지 확인하세요.</p>
<pre><code class="language-python">from spoon_ai.chat import ChatBot
from spoon_ai.graph.reducers import add_messages

chatbot = ChatBot(enable_short_term_memory=True)
history = [
    Message(id=&quot;u1&quot;, role=&quot;user&quot;, content=&quot;안녕하세요!&quot;),
    Message(id=&quot;a1&quot;, role=&quot;assistant&quot;, content=&quot;안녕하세요 — 무엇을 도와드릴까요?&quot;),
    Message(id=&quot;u2&quot;, role=&quot;user&quot;, content=&quot;DeFi가 뭐예요?&quot;),
]
# 최신 어시스턴트 메시지 제거
assistant_ids = [msg.id for msg in history if msg.role == &quot;assistant&quot;]
remove_last = chatbot.remove_message(assistant_ids[-1])

# 또는 전체 히스토리 지우기
remove_all = chatbot.remove_all_messages()

# 지속된 히스토리에 지시문 적용
updated_history = add_messages(history, [remove_last])
cleared_history = add_messages(history, [remove_all])</code></pre>
<p><code>add_messages()</code>는 제거 지시문을 기존 히스토리에 병합하여 대상 항목(또는 전체 전사본)을 삭제합니다.
이는 단기 메모리 관리자가 요약이 오래된 턴을 트리밍할 때 <code>RemoveMessage</code> 항목을 내보내는 방식과 동일합니다.</p>
<hr>
<h2 id="스레드-상태-및-체크포인트-검사">스레드 상태 및 체크포인트 검사</h2>
<p>그래프가 실행될 때마다 최신 스냅샷(메시지 및 메타데이터)을 검색하고, 전체 체크포인트 히스토리를 반복하거나, 외부 소비자를 위한 <code>CheckpointTuple</code>을 읽을 수 있습니다. 이를 통해 메모리 동작을 디버깅하거나, 모든 체크포인트에서 재생하거나, 상태를 지속적 저장소에 동기화하기가 쉬워집니다. 아래 예제는 최신 요약을 가져오고, 모든 체크포인트를 나열하며, 튜플 스타일 페이로드를 보는 방법을 보여줍니다.</p>
<pre><code class="language-python">config = {&quot;configurable&quot;: {&quot;thread_id&quot;: &quot;memory_demo_thread&quot;}}

snapshot = graph.get_state(config)
print(&quot;최신 체크포인트:&quot;, snapshot.metadata.get(&quot;checkpoint_id&quot;))

for snap in graph.get_state_history(config):
    print(&quot;히스토리 id:&quot;, snap.metadata.get(&quot;checkpoint_id&quot;))

checkpoint_tuple = graph.checkpointer.get_checkpoint_tuple(config)
print(&quot;체크포인트 튜플:&quot;, checkpoint_tuple)

for entry in graph.checkpointer.iter_checkpoint_history(config):
    print(&quot;튜플 히스토리 항목:&quot;, entry)</code></pre>
<p>컴파일된 그래프(<code>CompiledGraph</code>)를 사용하는 경우, 대신 <code>graph.graph.get_state(config)</code>와 <code>graph.graph.get_state_history(config)</code>를 호출하세요. 위 스니펫은 <code>graph</code>가 <code>StateGraph</code>라고 가정합니다.</p>
]]></description>
        </item>
    </channel>
</rss>