Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion content/posts/claude-code-weekly-limits-september-2026.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Anthropic은 2026년 5월 6일 공식 발표에서 Pro, Max, Team, seat-based En

- [Anthropic: Higher usage limits for Claude and a compute deal with SpaceX](https://www.anthropic.com/news/higher-limits-spacex)

이번 9월 변경의 핵심은 이 5시간 한도가 아니라 **주간 한도(weekly limit)**다.
이번 9월 변경의 핵심은 이 5시간 한도가 아니라 **주간 한도**(weekly limit)다.

Anthropic 도움말도 사용량 제한이 대화 길이, 복잡성, 사용하는 모델과 기능 등에 영향을 받으며, Claude.ai와 Claude Code 같은 여러 제품 영역의 사용량이 서로 영향을 줄 수 있다고 설명한다.

Expand Down
8 changes: 4 additions & 4 deletions content/posts/gpt-6-astra-vs-claude-fable-5-1.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ GPT-6 Astra에서는 비동기 도구 호출과 작업 도중 사용자 개입

OpenAI는 GPT-6 Astra를 복잡한 end-to-end 작업을 위한 자사의 가장 강력한 모델로 소개한다.

OpenAI가 공개한 비교에서 [AutomationBench](https://openai.com/index/gpt-6-astra/) 점수는 GPT-6 Astra가 **41.4%**, Claude Fable 5.1이 **31.4%**다.
OpenAI가 공개한 비교에서 [AutomationBench](https://openai.com/index/gpt-6-astra/) 점수는 GPT-6 Astra가 **41.4%**, Claude Fable 5.1이 **31.4**%다.

AutomationBench는 여러 단계의 실제 업무 자동화 능력을 평가하는 벤치마크라서 이번 글의 주제인 에이전트 작업과도 꽤 직접적으로 연결된다.

Expand Down Expand Up @@ -83,7 +83,7 @@ GPT-6 Astra의 모델 사양은 눈에 띈다.

[공식 모델 문서](https://developers.openai.com/api/docs/models/gpt-6-astra)에 따르면 Astra는 **1,050,000 토큰 컨텍스트 윈도우**와 **128,000 토큰 최대 출력**을 지원한다.

가격은 100만 토큰 기준 입력 **$10**, 캐시 입력 **$1**, 출력 **$50**이다. 272K 입력 토큰을 넘는 요청은 전체 요청에 더 높은 장문 요금이 적용된다.
가격은 100만 토큰 기준 입력 **\$10**, 캐시 입력 **\$1**, 출력 **\$50**이다. 272K 입력 토큰을 넘는 요청은 전체 요청에 더 높은 장문 요금이 적용된다.

하지만 더 중요한 변화는 이 숫자들이 아니다.

Expand Down Expand Up @@ -135,7 +135,7 @@ OpenAI가 [Responses API를 신규 프로젝트에 권장](https://developers.op

Anthropic의 접근에서 특히 눈에 들어오는 것은 cache read 가격이다.

Claude Fable 5.1의 기본 가격은 100만 토큰 기준 입력 **$10**, 출력 **$50**으로 Fable 5와 같다.
Claude Fable 5.1의 기본 가격은 100만 토큰 기준 입력 **\$10**, 출력 **\$50**으로 Fable 5와 같다.

그런데 Anthropic은 [cache read 가격을 75% 낮춰 100만 토큰당 $0.25](https://www.anthropic.com/claude-fable-and-mythos-5-1)로 변경했다.

Expand Down Expand Up @@ -168,7 +168,7 @@ Fable 5.1은 반복 컨텍스트가 많은 작업에서 **캐시 비용을 크

그리고 모델마다 하나의 작업을 끝내기 위해 소비하는 토큰과 도구 호출 횟수도 다를 수 있다.

결국 개발자가 봐야 하는 숫자는 점점 `cost per token`보다 **`cost per completed task`**에 가까워진다.
결국 개발자가 봐야 하는 숫자는 점점 `cost per token`보다 **`cost per completed task`에** 가까워진다.

비싼 모델이 더 적은 시도와 토큰으로 작업을 끝내면 결과적으로 더 저렴할 수도 있다. 반대로 토큰 단가는 같아도 긴 컨텍스트를 계속 다시 읽거나 실패와 재시도가 많으면 실제 비용은 올라간다.

Expand Down
8 changes: 4 additions & 4 deletions content/posts/korea-ai-third-place-dokpamo-evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ draft: false

Artificial Analysis 기준 전체 모델 가운데 10위권에 해당하는 성적이었고, 미국과 중국을 제외하면 특히 눈에 띄는 결과였다.

Artificial Analysis는 이후 한국을 미국과 중국에 이어 **"clear #3 nation in AI"**라고 평가했다.
Artificial Analysis는 이후 한국을 미국과 중국에 이어 "**clear #3 nation in AI**"라고 평가했다.

그런데 조금 이상한 일이 벌어졌다.

Expand All @@ -46,7 +46,7 @@ Artificial Analysis는 이후 한국을 미국과 중국에 이어 **"clear #3 n

정부는 GPU, 데이터, 인프라 등을 지원하고 여러 정예팀이 경쟁하는 방식으로 프로젝트를 진행하고 있다.

여기서 중요한 단어는 **'파운데이션 모델'**이다.
여기서 중요한 단어는 '**파운데이션 모델**'이다.

ChatGPT와 비슷한 챗봇 서비스를 하나 만드는 프로젝트가 아니다.

Expand Down Expand Up @@ -331,7 +331,7 @@ Web Interface

## 그렇다면 왜 '세계 3위'를 이야기할 때는 AAII를 사용했을까

Artificial Analysis는 최근 한국을 미국과 중국에 이어 **"clear #3 nation in AI"**라고 평가했다.
Artificial Analysis는 최근 한국을 미국과 중국에 이어 "**clear #3 nation in AI**"라고 평가했다.

중요한 것은 하나의 모델만 보고 내린 평가가 아니라는 점이다.

Expand All @@ -343,7 +343,7 @@ Artificial Analysis는 최근 한국을 미국과 중국에 이어 **"clear #3 n

한국의 글로벌 AI 경쟁력을 설명할 때는 AAII라는 글로벌 모델 성능 지표가 강력한 근거가 된다.

그런데 독파모에서 지원팀을 선정할 때 같은 지표의 비중은 **25%**다.
그런데 독파모에서 지원팀을 선정할 때 같은 지표의 비중은 **25**%다.

즉,

Expand Down
55 changes: 55 additions & 0 deletions e2e/markdown-emphasis.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { expect, test } from "@playwright/test"

const cases = [
{
slug: "korea-ai-third-place-dokpamo-evaluation",
phrases: [
["clear #3 nation in AI", 2],
["파운데이션 모델", 1],
["25", 1],
],
},
{ slug: "claude-code-weekly-limits-september-2026", phrases: [["주간 한도", 1]] },
{
slug: "gpt-6-astra-vs-claude-fable-5-1",
phrases: [
["완벽하지는 않지만 빨랐다", 1],
["31.4", 1],
["cost per completed task에", 1],
["$10", 2],
["$1", 1],
["$50", 2],
],
},
] as const

for (const { slug, phrases } of cases) {
test(`${slug} displays emphasis on the built page`, async ({ page }, testInfo) => {
await page.goto(`/posts/${slug}/`)
const article = page.locator("article[data-pagefind-body]")
await expect(article).toBeVisible()
for (const [phrase, count] of phrases) {
const strong = article
.locator("strong")
.filter({ hasText: new RegExp(`^${phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`) })
await expect(strong).toHaveCount(count)
for (let index = 0; index < count; index++) {
const item = strong.nth(index)
await item.scrollIntoViewIfNeeded()
await expect(item).toBeVisible()
expect(
await item.evaluate((element) =>
Number.parseInt(getComputedStyle(element).fontWeight, 10),
),
).toBeGreaterThanOrEqual(600)
const paragraph = item.locator("xpath=ancestor::p[1]")
await expect(paragraph).not.toContainText("**")
await expect(paragraph.locator(".katex")).toHaveCount(0)
await testInfo.attach(`${phrase}-${index}`, {
body: await paragraph.screenshot(),
contentType: "image/png",
})
}
}
})
}
17 changes: 17 additions & 0 deletions tests/markdown-cjk-emphasis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,20 @@ describe("markdown emphasis around Korean particles", () => {
expect(html).not.toContain("**완벽하지는 않지만 빨랐다**")
})
})

it.each([
['"**인용**"이라고', '"<strong>인용</strong>"이라고'],
["'**용어**'이다", "'<strong>용어</strong>'이다"],
["**한도**(limit)다", "<strong>한도</strong>(limit)다"],
["**25**%다", "<strong>25</strong>%다"],
["**`task`에** 가깝다", "<strong><code>task</code>에</strong> 가깝다"],
[
"**\\$10**, **\\$1**, **\\$50**이다",
"<strong>$10</strong>, <strong>$1</strong>, <strong>$50</strong>이다",
],
["**문장.**\n\n**평가 기준**이다", "<strong>평가 기준</strong>이다"],
])("renders safe punctuation boundaries: %s", async (source, expected) => {
const { html } = await renderMarkdown(source)
expect(html).toContain(expected)
expect(html).not.toContain("**")
})
10 changes: 10 additions & 0 deletions tests/post-currency.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { expect, it } from "vitest"
import { renderMarkdown } from "../src/lib/markdown"
import { getPostBySlug } from "../src/lib/posts"

it("currency remains strong prose", async () => {
const { html } = await renderMarkdown(getPostBySlug("gpt-6-astra-vs-claude-fable-5-1").content)
expect(html.match(/<strong>\$10<\/strong>/g)).toHaveLength(2)
expect(html.match(/<strong>\$50<\/strong>/g)).toHaveLength(2)
expect(html).toContain("<strong>$1</strong>")
})
51 changes: 51 additions & 0 deletions tests/post-emphasis.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { readdirSync, readFileSync } from "node:fs"
import { join } from "node:path"
import { Window } from "happy-dom"
import { visit } from "unist-util-visit"
import { describe, expect, it } from "vitest"
import { renderMarkdown } from "../src/lib/markdown"
import { createMarkdownProcessor } from "../src/lib/markdown/render"
import { getPostBySlug } from "../src/lib/posts"

const directory = join(process.cwd(), "content/posts")

describe("all authored posts preserve emphasis", () => {
for (const file of readdirSync(directory).filter((name) => name.endsWith(".md"))) {
it(`${file} has no unparsed emphasis delimiters in prose`, async () => {
const source = readFileSync(join(directory, file), "utf8")
const body = source.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, "")
const tree = createMarkdownProcessor().parse(body)
const failures: string[] = []
visit(tree, "text", (node) => {
if (/\*\*|__/.test(node.value)) {
failures.push(`line ${node.position?.start.line}: ${node.value}`)
}
})
expect(failures).toEqual([])
const { html } = await renderMarkdown(body)
const window = new Window()
try {
window.document.body.innerHTML = html
for (const code of window.document.querySelectorAll("pre, code")) code.remove()
expect(window.document.body.textContent).not.toMatch(/\*\*|__/)
} finally {
await window.happyDOM.close()
}
})
}
})

describe("real post rendering regressions", () => {
it.each([
["korea-ai-third-place-dokpamo-evaluation", "clear #3 nation in AI", 2],
["korea-ai-third-place-dokpamo-evaluation", "파운데이션 모델", 1],
["korea-ai-third-place-dokpamo-evaluation", "25", 1],
["claude-code-weekly-limits-september-2026", "주간 한도", 1],
["gpt-6-astra-vs-claude-fable-5-1", "31.4", 1],
["gpt-6-astra-vs-claude-fable-5-1", "완벽하지는 않지만 빨랐다", 1],
["gpt-6-astra-vs-claude-fable-5-1", "<code>cost per completed task</code>에", 1],
])("%s renders %s as strong", async (slug, text, count) => {
const { html } = await renderMarkdown(getPostBySlug(slug).content)
expect(html.split(`<strong>${text}</strong>`).length - 1).toBe(count)
})
})