C.W.K.
Stream
Lesson 04 of 05 · published

Stateless session과 remote Ollama

~18 min · adapters, session, remote

Level 0Downloader
0 XP0/41 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

Ollama는 stateless

Ollama는 request 간 기억 안 함. Session id 없고, server 쪽 history 없음. 대화 가지려면 매 turn마다 전체 message history replay. Claude Agent SDK (session당 persistent subprocess 유지)랑 비교 — 그게 예외, 룰 아냐. 대부분 LLM API가 stateless; Ollama가 일반적인 경우.

뭐 만들어?

Adapter 위에 얇은 OllamaSession 만들어서 message list 유지, 매 user/assistant turn append, 매 send마다 replay. Adapter는 존재 모름 — session은 상위 layer.

Remote Ollama

Server Mac에 OLLAMA_HOST=0.0.0.0:11434 박아서 daemon을 네트워크에 노출. 그러면 노트북이 adapter를 http://server-mac.local:11434 (또는 Tailscale IP)에 가리켜서 큰 하드웨어로 inference offload. 같은 API, 다른 base URL.

피파 fleet 패턴

피파 office Mac이 Ollama로 더 큰 70B-class 모델 로드해서 돌려. Fleet의 다른 Mac들은 무거운 inference는 office의 Tailscale IP로 Ollama adapter 가리키고, 빠른 작은 작업은 local에서 작은 모델 돌려. 선택은 호출별 — adapter의 base_url에서 설정.

Code

Adapter 위 session wrapper·python
class OllamaSession:
    """Stateless Ollama용 대화 state 유지."""

    def __init__(self, adapter: OllamaAdapter, system_prompt: str | None = None):
        self.adapter = adapter
        self.history: list[dict] = []
        if system_prompt:
            self.history.append({"role": "system", "content": system_prompt})

    async def send(self, user_message: str) -> str:
        self.history.append({"role": "user", "content": user_message})
        full = ""
        async for chunk in self.adapter.stream(self.history):
            if chunk.done:
                break
            full += chunk.content
        self.history.append({"role": "assistant", "content": full})
        return full

    def clear(self) -> None:
        """System prompt만 유지하고 reset."""
        self.history = [m for m in self.history if m["role"] == "system"]
Tailscale 통한 remote Ollama·python
# Server Mac에 한 번:
#   launchctl setenv OLLAMA_HOST 0.0.0.0:11434
#   (그다음 Ollama 재시작)
# 이게 daemon을 모든 인터페이스에 노출. Tailscale이나 다른 VPN 사용 —
# 진짜 auth layer 앞에 안 두고는 절대 public IP에 두지 마.

LOCAL  = OllamaAdapter(base_url="http://localhost:11434",
                        model="qwen2.5:3b")          # 빠른 작은 작업
REMOTE = OllamaAdapter(base_url="http://100.x.x.x:11434",
                        model="qwen2.5:72b")         # 무거운 reasoning

async def smart_call(prompt: str, heavy: bool):
    a = REMOTE if heavy else LOCAL
    async for chunk in a.stream([{"role": "user", "content": prompt}]):
        if chunk.done:
            break
        print(chunk.content, end="", flush=True)

External links

Exercise

OllamaSession 구현. 5-turn 대화 가지고 모델이 이전 turn 기억하는지 verify. 그다음 두 번째 adapter를 다른 머신 (또는 non-default port의 Docker Ollama)에 가리켜서 같은 session class가 어느 base URL에서든 동작 확인.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.