- delta๋ LLM ์คํธ๋ฆฌ๋ฐ API์์ ์ด์ ์ฒญํฌ ์ดํ ์๋ก ์์ฑ๋ ํ ์คํธ ์กฐ๊ฐ(์ฆ๋ถ ๋ณ๊ฒฝ๋)
- SSE (Server-Sent Events) ๋ฐฉ์์ผ๋ก ์ ๋ฌ๋๋ ๊ฐ ์ด๋ฒคํธ ์ฒญํฌ ์์ ๋ณ๊ฒฝ ๋ด์ฉ ๊ฐ์ฒด
- ์ํ์ ฮ(๋ธํ) = โ๋ณํ๋โ์์ ์ ๋ํ โ์ ์ฒด ์ค ์ด๋ฒ์ ์ถ๊ฐ๋ ๋ถ๋ถโ ์๋ฏธ
ํด๋น ๊ฐ๋ ์ด ํ์ํ ์ด์
- LLM์ด ์ ์ฒด ์๋ต์ ์์ฑ ํ ํ ๋ฒ์ ๋ฐํํ๋ฉด ์ฌ์ฉ์๊ฐ ์ ์ด~์์ญ ์ด ๋๊ธฐํด์ผ ํจ
- delta๋ฅผ ์ด์ฉํ ์คํธ๋ฆฌ๋ฐ์ผ๋ก ์ฒซ ๊ธ์๋ถํฐ ์ค์๊ฐ ํ์ โ UX ๋ํญ ๊ฐ์
- ๊ธด ์๋ต์ด๋ Tool Use์์ HTTP ํ์์์ ๋ฐฉ์ง
AS-IS (์คํธ๋ฆฌ๋ฐ ์์)
sequenceDiagram autonumber Client->>LLM API: POST /messages (stream: false) Note over LLM API: ์ ์ฒด ์๋ต ์์ฑ ์ค...<br/>(5~30์ด ๋๊ธฐ) LLM API-->>Client: ์์ฑ๋ ์ ์ฒด ์๋ต ๋ฐํ Note over Client: ์ฌ์ฉ์๋ ๊ฒฐ๊ณผ๋ฅผ<br/>ํ ๋ฒ์ ๋ฐ์
TO-BE (delta ์คํธ๋ฆฌ๋ฐ)
sequenceDiagram autonumber Client->>LLM API: POST /messages (stream: true) LLM API-->>Client: delta: "์" LLM API-->>Client: delta: "๋ " LLM API-->>Client: delta: "ํ์ธ์" LLM API-->>Client: message_stop Note over Client: ๊ธ์๊ฐ ์์ฑ๋ ๋๋ง๋ค<br/>์ค์๊ฐ ํ์
Anthropic API์ delta ์ด๋ฒคํธ ๊ตฌ์กฐ
delta๊ฐ ์ ๋ฌ๋๋ ์ ์ฒด ์ด๋ฒคํธ ํ๋ฆ:
event: message_start โ 1. ๋ฉ์์ง ์ด๊ธฐํ
event: content_block_start โ 2. ์ฝํ
์ธ ๋ธ๋ก ์์
event: content_block_delta โ 3. delta ๋ฐ๋ณต ์ ๋ฌ (ํต์ฌ!)
event: content_block_delta
event: content_block_delta
event: content_block_stop โ 4. ๋ธ๋ก ์ข
๋ฃ
event: message_delta โ 5. ๋ฉ์์ง ๋ ๋ฒจ ๋ณ๊ฒฝ (stop_reason ๋ฑ)
event: message_stop โ 6. ์คํธ๋ฆผ ์ข
๋ฃ
์ค์ delta ์ฒญํฌ ์์:
{
"type": "content_block_delta",
"index": 0,
"delta": {
"type": "text_delta",
"text": "์๋
ํ์ธ์"
}
}delta์ 3๊ฐ์ง ํ์ (Anthropic ๊ธฐ์ค)
| delta ํ์ | ์ค๋ช | ํฌํจ ํ๋ |
|---|---|---|
text_delta | ์ผ๋ฐ ํ ์คํธ ์๋ต ์กฐ๊ฐ | text |
input_json_delta | Tool Use ํ๋ผ๋ฏธํฐ (partial JSON) | partial_json |
thinking_delta | Extended Thinking ๋ด์ฉ | thinking |
# Python SDK์์ delta ํ์
๋ณ ์ฒ๋ฆฌ
with client.messages.stream(...) as stream:
for event in stream:
if event.type == "content_block_delta":
if event.delta.type == "text_delta":
print(event.delta.text, end="", flush=True)
elif event.delta.type == "thinking_delta":
print(event.delta.thinking, end="", flush=True)OpenAI vs Anthropic delta ๊ตฌ์กฐ ๋น๊ต
๋ API ๋ชจ๋ delta ๊ฐ๋ ์ ์ฌ์ฉํ์ง๋ง ๊ตฌ์กฐ๊ฐ ๋ค๋ฆ:
OpenAI (chat.completion.chunk):
{
"id": "chatcmpl-abc123",
"object": "chat.completion.chunk",
"choices": [{
"index": 0,
"delta": {
"role": "assistant", // ์ฒซ ์ฒญํฌ์๋ง ํฌํจ
"content": "์๋
" // ํ
์คํธ ์กฐ๊ฐ
},
"finish_reason": null
}]
}Anthropic (content_block_delta):
{
"type": "content_block_delta",
"index": 0,
"delta": {
"type": "text_delta", // delta ํ์
๋ช
์
"text": "์๋
" // ํ
์คํธ ์กฐ๊ฐ
}
}| ๋น๊ต ํญ๋ชฉ | OpenAI | Anthropic |
|---|---|---|
| delta ์์น | choices[0].delta.content | delta.text |
| delta ํ์ ๊ตฌ๋ถ | ์์ (content/tool_calls) | type ํ๋๋ก ๋ช
์ |
| ์ด๋ฒคํธ ๋ํ | ๋จ์ JSON chunk | SSE event + data |
| Tool Use delta | delta.tool_calls | input_json_delta |
delta ๋์ ์ผ๋ก ์ ์ฒด ํ ์คํธ ์กฐํฉํ๊ธฐ
delta๋ ์กฐ๊ฐ์ด๋ฏ๋ก, ์ ์ฒด ์๋ต์ ์ป์ผ๋ ค๋ฉด ์ง์ ๋์ ํด์ผ ํจ:
full_text = ""
with client.messages.stream(...) as stream:
for text in stream.text_stream:
full_text += text # delta ๋์
print(text, end="", flush=True) # ์ค์๊ฐ ์ถ๋ ฅ
print(f"\n์ต์ข
์ ์ฒด ์๋ต: {full_text}")