이전 시리즈(구축편/불안정성 분리편/3모델 비교편)의 연속입니다.
DGX Spark 1대(GB10, 128GB 유니파이드 메모리)에서 TensorFold + LiteLLM + opencode를 운영했습니다.
이전 편 「[Qwen3.8-Flash-Next와 고군분투한 기록]」에서 Flash-Next의 루프에 패배하여, 상용 모델을 Qwen3.8-27B로 타협하게 되었습니다.
거기서 운영 기반・샘플링・고속화 부분을 다듬어, 결과적으로 '정답'에 도달한 기록입니다.
수치는 제가 가진 1대에서 실측한 것입니다. IP 주소, 호스트 이름, 인증 정보는 플레이스홀더로 대체했습니다.
- Flash-Next를 포기하고
**Qwen3.8-27B (MLX 4-bit + DFlash2)**를 상용 모델로 사용했습니다. 안정성・속도・순수함에서 정답이었습니다. -
샘플링은 공식 준수합니다(thinking/non-thinking에 따라 값이 다름. non-thinking은 presence_penalty=1.5)
)。-
고속화로
효과가 있었던 것은 --prefill-fp8
(
prefill +33%). --checkpoint-slots
--spill-gib
(prefix 유지) -
효과가 없었던 것은 --mtp-drafts
/--drafter-bits
(수용 개수가 완전히 동일 = 투기적인 튜닝은 무효). -
이미지는 Python 계열 v0.6.6이 정답입니다 (네이티브 Zig v1.0.x는 27B 미포팅 + Python보다 느림). - 운영의 함정은
(profile 하위 폴더는 docker compose
의 profile과 logs
logs
에 나오지 않음).
이전 편의 결론(Flash-Next는 빠르지만 루프함)을 받아, 상용 모델을 27B로 전환했습니다. 결과:
| 모델 | 능력 | 속도 | 안정성/사용 용이성 |
|---|
| Qwen3.8-27B (채택) | 중~고 | 빠름 | ◎(익숙함) |
| Qwen3.8-Flash-Next | 고 | 장문맥에서 선전 | △(fast 루프, think 전제) |
- 27B는 dense 27B입니다.
MLX 4-bit + DFlash2로 단일 기기에서도 실용적인 속도를 보여줍니다. -
단문맥에서 decode ~148 tok/s, 장문맥(150k 초과)에서 ~50 tok/s (dense는 문맥 길이에 따라 KV 읽기가 증가합니다). -
지시에 순순히 따릅니다 (루프하지 않음). 에이전트 용도에서는 '지능'보다 '순수함'이 효과적입니다.
'타협'이라고 썼지만,
상용 모델의 안정성 면에서는 27B가 최상의 기본값이었습니다.
GPU가 1개이므로 '동시에 1모델'. compose의 profile로 전환합니다.
services:
tensorfold: # 기본값 = Qwen3.8-27B (container: tensorfold)
tensorfold-flashnext: # profiles: [
| 항목 | 변경 | 의도 |
|---|---|---|
`compaction.buffer` | 8000 → 48000 | 출력 토큰(32768)을 포함해도 상한 초과가 없도록 여유 공간 확보 |
`limit.context` | 서버 창에 맞게 조정 (예: 240000) | 선언된 값이 실제 용량을 초과하면 'too long → 압축' 현상이 빈번함 |
`small_model` | 중지 상태의 호스트 → 가동 중 | title 생성 등 보조 작업 실패를 해소 |
`headerTimeout` /`chunkTimeout` | 기본값(5분) → 600000/900000 ms | 긴 prefill에서도 끊기지 않도록
- 증상 대부분은 **'컨텍스트 상한 초과로 인한 BadRequest'**와 **'중지된 호스트로의 연결 실패'**였습니다.
Qwen3.8 (27B / Flash-Next 공통)의 **모델 카드** 권장값으로 통일합니다.
- **Thinking**:`temperature=1.0, top_p=0.95, top_k=20, min_p=0.0, presence_penalty=0.0, repetition_penalty=1.0`
- **Instruct (non-thinking)**:`temperature=0.7, top_p=0.80, top_k=20, min_p=0.0, **presence_penalty=1.5**, repetition_penalty=1.0`
**litellm과 opencode 모두** 공식값으로 통일.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
litellm-config.yaml (non-thinking 루트 예시)
extra_body:
chat_template_kwargs:
...
교훈:
추론 파라미터는 체감에 의존하기보다 공식 카드를 기준으로 맞출 것. 특히 non-thinking의 `presence_penalty`는 놓치기 쉬움.
TensorFold의 27B로 각 플래그를 측정했습니다.
| 조치 | 결과 |
|---|---|
`--prefill-fp8` | prefill +33% (1,364 → 1,815 tok/s @30k cold). decode는 변화 없음 |
`--checkpoint-slots 16` +`--spill-gib 32` | prefix 유지 향상 (6개 대화를 거친 후에도 재전송은 99.9% cached・ttft 0.12s) |
`--mtp-drafts 6` | 효과 없음 (수용 개수가 완전히 동일) |
`--drafter-bits 0` | 효과 없음 (동일함)
- **효과가 있었던 것**: prefill-fp8(프롬프트 처리)와 prefix 유지(재prefill 감소). - **효과가 없었던 것**: 투기 디코드(DFlash2)의 추가 튜닝. 수용 개수가 **완전히 동일**했고, 플래그가 투기 경로를 변경하지 않았습니다. - 최종 구성:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
tensorfold serve TensorFold/Qwen3.8-27B-MLX-4bit --name Qwen3.8-27B --host 0.0.0.0
--parallel 8 --drafter z-lab/Qwen3.8-27B-DFlash2
--prefill-fp8 --checkpoint-slots 16 --spill-gib 32
장문 컨텍스트의 decode(~50 tok/s)를 직접 올리는 것보다 **컨텍스트 압축**이 핵심이라는 정리도 얻었습니다 (미착수).
- 현재 버전 =
**TensorFold v0.6.6 (Python 기반)** = Python 라인의 최신 릴리스.
- 최신 버전 =
**v1.0.7 (네이티브 Zig**이나, `tensorfold-native` **사용) CLI/지원 모델이 다름**.
- 공식 릴리스 노트(v1.0.0)의 결정적인 내용:
Qwen3.8-27B ...
**being ported for 1.0.x**. Qwen3.8-27B passes drafted/plain equality, but remains outside 1.0.0 because its **paired served decode and cold-prefill results are slower than Python 0.6.6**.
- →
**27B를 GB10에서 사용할 경우 v0.6.6이 정답**입니다. v1.0.x로 올려도 빨라지지 않고, 오히려 느리거나 미지원합니다.
| 모델 | 능력 | 속도 | 안정성 | 포지셔닝 |
|---|---|---|---|---|
| Qwen3.8-27B | 중~고 | 빠름 | ◎ |
안정성과 속도의 만능 기본값 |
| Qwen3.8-Flash-Next | 고 | 장문맥에서 준수함 | △ (fast 루프, think 전제) | 품질 중시 |
| Qwen3.6-35B-A3B | 중 | 가장 빠름 | ○ | 경량 모델 |
| GLM 5.3 Flash | 고 | 빠름 | 도입 필요 | 경쟁 모델 |
| 체크포인트 | 속도(GB10) | 품질 | 비고 |
|---|---|---|---|
| MLX 4-bit (채택) | ◎가장 빠름 | △~○ (난이도 높은 문제에서 하락 보고) | DFlash2 대응 |
| NVFP4 (`nvidia` / `RadixArk`) | ○(느림) | ○~◎ | 통제 연구에서 MLX보다 정답률 높음 (18/24 vs 14/24) |
| FP8 | △(무거움, ~30GB) | ◎ | 단일 기기에서는 불리 -
-
**속도 중시(opencode 에이전트)**라면 **MLX 4-bit**. **품질 중시**라면 **NVFP4**로 전환하여 A/B 테스트.
-
**일상 사용은 27B가 최적의 기본값**입니다 (Flash-Next는 think 전제라 일상 사용하기 어려움). -
**샘플링은 공식 준수**해야 합니다 (특히 non-thinking일 때 `presence_penalty=1.5`). -
**고속화는 prefill-fp8과 prefix 유지**가 효과적입니다 (투기적인 추가 튜닝은 효과 없음). -
**이미지는 v0.6.6 (Python)이 정답**입니다. -
**프로파일 운영 시** (기본 서비스를 일상 사용 모델로 고정할 때) `docker compose logs`에 주의해야 합니다.
'타협'에서 시작한 27B이지만,
솔직함・안정성・속도의 균형 면에서 일상 사용의 정답이었습니다.
전편과 함께, 단일 기기 DGX Spark로 로컬 LLM을 일상 사용하는 실천 기록입니다.
수치는 필자 환경에서의 실측값입니다. 드라이버・이미지・체크포인트・부하에 따라 변동됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50