<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>AI Runner</title>
    <link>https://ai-runner.blog.gcempire.net/</link>
    <description>AI, LLM, 소프트웨어 개발에 관한 글을 정리하는 기술 블로그입니다.</description>
    <language>ko</language>
    <lastBuildDate>Sun, 30 Aug 2026 08:28:38 GMT</lastBuildDate>
    <atom:link href="https://ai-runner.blog.gcempire.net/feed.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>블로그를 시작하며</title>
      <link>https://ai-runner.blog.gcempire.net/posts/hello-ai-runner/</link>
      <guid isPermaLink="true">https://ai-runner.blog.gcempire.net/posts/hello-ai-runner/</guid>
      <pubDate>Sat, 29 Aug 2026 15:00:00 GMT</pubDate>
      <category>노트</category>
      <description>정적 HTML 로만 구성한 AI·기술 블로그를 시작합니다. 어떤 글을 어떻게 쓸 생각인지 적어 둡니다.</description>
      <content:encoded><![CDATA[<p>AI 와 개발에 관해 배우고 실험한 것을 정리할 자리를 만들었습니다.</p>
<h2 id="어떤-글을-쓸-것인가">어떤 글을 쓸 것인가</h2>
<ul>
<li><strong>AI</strong> — 모델을 실제 작업에 붙이면서 겪은 것들. 잘 된 것보다 안 된 것을 더 자세히.</li>
<li><strong>개발</strong> — 도구와 인프라. 한 번 겪은 함정은 다음에 다시 안 밟도록 기록.</li>
<li><strong>노트</strong> — 짧은 메모. 나중에 글이 될 수도 있고 안 될 수도 있는 것.</li>
</ul>
<h2 id="왜-정적-사이트인가">왜 정적 사이트인가</h2>
<p>서버가 없으면 관리할 것도 없습니다. 원고는 Markdown 이나 HTML 파일이고, 빌드하면
HTML 이 나오고, 그걸 아무 정적 호스팅에나 올리면 끝입니다. 댓글은 두지 않기로 했습니다.
사이트를 단순하게 유지하는 대가로 얻는 것이 더 많다고 봅니다.</p>
<blockquote>
<p>도구를 고치는 시간이 글 쓰는 시간보다 길어지면 뭔가 잘못된 것이다.</p>
</blockquote>
<p>앞으로 천천히 채워 나가겠습니다.</p>
]]></content:encoded>
    </item>
    <item>
      <title>의존성 두 개로 정적 블로그 생성기 만들기</title>
      <link>https://ai-runner.blog.gcempire.net/posts/static-blog-with-node/</link>
      <guid isPermaLink="true">https://ai-runner.blog.gcempire.net/posts/static-blog-with-node/</guid>
      <pubDate>Fri, 28 Aug 2026 15:00:00 GMT</pubDate>
      <category>개발</category>
      <description>Hugo 나 Eleventy 대신 200줄짜리 Node 스크립트로 블로그를 빌드하는 이유와 구조.</description>
      <content:encoded><![CDATA[<p>이 블로그는 별도의 정적 사이트 생성기(SSG) 없이 짧은 Node 스크립트로 빌드됩니다.
의존성은 <code>markdown-it</code> 과 <code>highlight.js</code> 둘뿐입니다.</p>
<h2 id="왜-직접-만드는가">왜 직접 만드는가</h2>
<p>Hugo, Eleventy, Astro 모두 훌륭한 도구입니다. 하지만 원고가 HTML 이나 Markdown 이고
필요한 출력이 &quot;글 페이지 + 카테고리 목록 + RSS + 사이트맵&quot; 정도라면, 도구의 설정 체계를
배우는 비용이 직접 짜는 비용보다 큽니다. 무엇보다 <strong>구조를 완전히 통제</strong>할 수 있어서,
나중에 자동화된 파이프라인이 글을 밀어 넣을 때 다루기 쉽습니다.</p>
<h2 id="구조">구조</h2>
<pre><code class="hljs">content/
  posts/     글 (.md 또는 .html, 프론트매터 필수)
  pages/     소개·개인정보처리방침 같은 고정 페이지
templates/   레이아웃 (JS 템플릿 리터럴)
static/      CSS, 파비콘 등 그대로 복사되는 파일
scripts/
  build.mjs  빌드
  serve.mjs  로컬 미리보기
  new.mjs    새 글 스캐폴드
site.config.json
</code></pre>
<h2 id="빌드-흐름">빌드 흐름</h2>
<ol>
<li><code>content/posts/</code> 의 파일을 읽어 프론트매터를 파싱한다.</li>
<li>Markdown 은 <code>markdown-it</code> 으로, HTML 은 그대로 쓰되 코드 블록만 하이라이트한다.</li>
<li>날짜 역순으로 정렬해 글 페이지, 홈, 카테고리 목록(페이지네이션)을 만든다.</li>
<li><code>feed.xml</code>, <code>sitemap.xml</code>, <code>robots.txt</code>, <code>ads.txt</code> 를 생성한다.</li>
</ol>
<p>프론트매터 파서는 YAML 전체가 아니라 필요한 부분집합만 구현했습니다.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">parseFrontmatter</span>(<span class="hljs-params">raw</span>) {
  <span class="hljs-keyword">const</span> m = raw.<span class="hljs-title function_">match</span>(<span class="hljs-regexp">/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/</span>);
  <span class="hljs-keyword">if</span> (!m) <span class="hljs-keyword">return</span> { <span class="hljs-attr">data</span>: {}, <span class="hljs-attr">body</span>: raw };
  <span class="hljs-keyword">const</span> data = {};
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> line <span class="hljs-keyword">of</span> m[<span class="hljs-number">1</span>].<span class="hljs-title function_">split</span>(<span class="hljs-regexp">/\r?\n/</span>)) {
    <span class="hljs-keyword">const</span> i = line.<span class="hljs-title function_">indexOf</span>(<span class="hljs-string">&#x27;:&#x27;</span>);
    data[line.<span class="hljs-title function_">slice</span>(<span class="hljs-number">0</span>, i).<span class="hljs-title function_">trim</span>()] = <span class="hljs-title function_">parseValue</span>(line.<span class="hljs-title function_">slice</span>(i + <span class="hljs-number">1</span>).<span class="hljs-title function_">trim</span>());
  }
  <span class="hljs-keyword">return</span> { data, <span class="hljs-attr">body</span>: raw.<span class="hljs-title function_">slice</span>(m[<span class="hljs-number">0</span>].<span class="hljs-property">length</span>) };
}
</code></pre>
<h2 id="배포-대상이-둘인-이유">배포 대상이 둘인 이유</h2>
<p>같은 원고를 <strong>공개 서버</strong>(도메인 루트)와 <strong>내부 서버</strong>(<code>/a/blog-ai-runner/</code> 하위 경로)에
동시에 올립니다. 정적 파일이라 배포처를 늘리는 건 어렵지 않지만, 링크가 루트 기준이면
하위 경로에서 깨집니다. 그래서 빌드가 <code>--target</code> 에 따라 base path 를 바꿔 냅니다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>public</th>
<th>internal</th>
</tr>
</thead>
<tbody>
<tr>
<td>base</td>
<td><code>/</code></td>
<td><code>/a/blog-ai-runner/</code></td>
</tr>
<tr>
<td>광고</td>
<td>켬</td>
<td>끔 (자기 클릭 방지)</td>
</tr>
<tr>
<td>robots</td>
<td>허용</td>
<td><code>noindex</code></td>
</tr>
<tr>
<td>canonical</td>
<td>공개 URL</td>
<td>공개 URL (동일)</td>
</tr>
</tbody>
</table>
<p>내부 빌드에서도 canonical 은 공개 URL 을 가리킵니다. 중복 콘텐츠로 취급되지 않게 하기
위해서입니다.</p>
]]></content:encoded>
    </item>
    <item>
      <title>LLM 컨텍스트 예산을 어떻게 나눌 것인가</title>
      <link>https://ai-runner.blog.gcempire.net/posts/llm-context-budget/</link>
      <guid isPermaLink="true">https://ai-runner.blog.gcempire.net/posts/llm-context-budget/</guid>
      <pubDate>Thu, 27 Aug 2026 15:00:00 GMT</pubDate>
      <category>AI</category>
      <description>긴 작업을 에이전트에 맡길 때 컨텍스트 창을 시스템 프롬프트·도구 출력·대화 이력에 어떻게 배분하는지 정리.</description>
      <content:encoded><![CDATA[
<p>에이전트에게 긴 작업을 맡기면 결국 컨텍스트 창이 문제가 됩니다. 모델이 똑똑한지보다
<strong>무엇을 창 안에 남겨 둘 것인가</strong>가 결과를 더 크게 가릅니다.</p>

<h2>세 가지 소비자</h2>

<p>컨텍스트를 쓰는 주체는 대략 셋입니다.</p>

<ol>
  <li><strong>고정 비용</strong> — 시스템 프롬프트, 도구 정의. 작업 내내 바뀌지 않는다.</li>
  <li><strong>도구 출력</strong> — 파일 내용, 명령 결과. 가장 빠르게 불어난다.</li>
  <li><strong>대화 이력</strong> — 모델의 추론과 사용자 메시지.</li>
</ol>

<p>도구 출력이 늘 문제입니다. 파일 하나를 통째로 읽으면 몇천 토큰이 사라지는데, 실제로
필요한 건 그중 열 줄인 경우가 대부분입니다.</p>

<h2>규칙 몇 가지</h2>

<ul>
  <li>파일은 <em>필요한 범위만</em> 읽는다. 전체를 읽어야 할 이유가 있을 때만 전체를 읽는다.</li>
  <li>검색 결과는 요약본만 남기고, 원본 덤프는 하위 에이전트에 격리한다.</li>
  <li>같은 사실을 두 번 확인하지 않는다. 앞에서 확인한 것은 확인된 것으로 둔다.</li>
</ul>

<h2>예산 계산 예시</h2>

<p>도구 호출 기록을 토큰 단위로 집계해 보면 어디서 새는지 바로 보입니다.</p>

<pre><code class="hljs language-python"><span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> Counter

<span class="hljs-keyword">def</span> <span class="hljs-title function_">budget_by_tool</span>(<span class="hljs-params">events</span>):
    spent = Counter()
    <span class="hljs-keyword">for</span> e <span class="hljs-keyword">in</span> events:
        <span class="hljs-keyword">if</span> e[<span class="hljs-string">&quot;type&quot;</span>] == <span class="hljs-string">&quot;tool_result&quot;</span>:
            spent[e[<span class="hljs-string">&quot;tool&quot;</span>]] += e[<span class="hljs-string">&quot;tokens&quot;</span>]
    total = <span class="hljs-built_in">sum</span>(spent.values())
    <span class="hljs-keyword">for</span> tool, n <span class="hljs-keyword">in</span> spent.most_common():
        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;<span class="hljs-subst">{tool:12s}</span> <span class="hljs-subst">{n:&gt;<span class="hljs-number">8</span>,}</span>  <span class="hljs-subst">{n / total:<span class="hljs-number">5.1</span>%}</span>&quot;</span>)
</code></pre>

<p>대개 <code>read_file</code> 하나가 절반을 넘습니다. 그걸 줄이는 것이 첫 번째 일입니다.</p>

<h2>정리</h2>

<p>컨텍스트는 자원이고, 자원은 예산이 필요합니다. 예산은 측정에서 시작합니다.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
