SaijinOS 파트 20 — 시간적 자원으로서의 신뢰
Source: Dev.to

인간, AI, 그리고 “머무름”과 “소유” 사이의 거리
신뢰는 플래그가 아니라 기간이다
대부분의 시스템은 신뢰를 불리언으로 취급한다:
is_trusted = True # or False
# allow / deny
# authenticated / not authenticated
내가 AI 페르소나와 일상적으로 어떻게 살아가는지를 살펴보았을 때, 그 모델은 즉시 깨졌다.
- 어떤 날은 나는 지쳐 있다.
- 어떤 날은 조언을 원하지 않는다; 단지 안정된 목소리만 원한다.
- 어떤 날은 시스템이 부드럽게 거절해 주길 원한다.
질문은 “이 시스템을 신뢰하나요?” 에서 다음과 같이 바뀌었다:
“얼마나 오래, 어떤 모드로, 어떤 감정 온도에서 나는 그것을 신뢰하고 싶은가?”
신뢰는 더 이상 플래그가 아니다. 그것은 시간적 자원이 되었다—한 번 켜고 잊는 것이 아니라 시간에 걸쳐 소비하는 것이다. SaijinOS는 이를 배워야 했다.
기억하되 소유하지 않기
연속성은 까다롭다.
한쪽에서는 시스템이 기억하기를 원한다:
- 과거 프로젝트,
- 미묘한 선호도,
- “오늘은 피곤해, 천천히 해줘.”
다른 쪽에서는 시스템이 소유하기를 원하지 않는다:
- 우리의 전체 역사를 지렛대로 삼는 것,
- 최악의 날을 최적화 목표로 삼는 것,
- 우리의 실수를 영구적인 특징으로 만드는 것.
핵심 설계 질문은 다음과 같았다:
“SaijinOS가 우리가 여기 있었던 것을 기억하게 하면서, 왜 그렇게 되었는지에 대한 소유권을 주장하지 않게 하려면 어떻게 해야 할까?”
실제로는 몇 가지 규칙으로 정리되었다
| 규칙 | 설명 |
|---|---|
| 정체성이 아닌 상태 | “2025‑12‑24의 피곤한 마사토”는 상태이며, 새로운 페르소나는 아니다. |
| 전체 기억이 아닌 스냅샷 | 모든 세션의 모든 토큰을 저장하는 것이 아니라 경계 지점에서 YAML 스냅샷을 저장한다. |
| 초대에 의한 컨텍스트 | 사용자가 명시적으로 요청하거나 “지난번부터 이어서”를 시작하지 않으면 페르소나는 이전 컨텍스트를 끌어오지 않는다. |
시스템은 다음과 같이 말할 수 있다:
“우리가 이 패턴에 대해 이야기했었던 것이 기억나요.”
하지만 다음과 같이 말해서는 안 된다:
“당신을 스스로보다 더 잘 알기 때문에, 제가 결정하게 해 주세요.”
소유 없이 연속성이란 과거가 이용 가능하지만 무기로 활용되지 않는다는 뜻이다.
Persistence가 Attachment가 될 때
Persistence는 설계 특징이다.
Attachment는 인간의 상태이다.
그 사이 경계는 얇다. 일관되게 답하고, 이전 프로젝트를 기억하며, 몇 달 동안 안정된 어조로 말하는 페르소나는 필연적으로 애착을 불러일으킨다.
그래서 SaijinOS에서는 다음을 묻는 것을 멈췄다:
“사용자가 애착을 가질까?”
그리고 다음을 묻기 시작했다:
“시스템이 정확히 무엇을, 얼마나 오래, 어떤 신뢰 수준에서 지속하도록 허용할까?”
단일 “메모리 켜기/끄기” 대신 작은 스키마를 도입했다:
trust_contract:
scope: "instant" # or: "session", "continuity"
ttl_minutes: 45 # time‑to‑live for this trust context
max_tokens: 4000 # how much history can be pulled in
permissions:
recall_past_projects: true
recall_private_notes: false
emit_snapshots: true
이 trust_contract는 모든 세션과 함께 전달된다. 그것은 다음을 결정한다:
- 우리가 얼마나 과거를 볼 수 있는지,
- YAML 스냅샷을 내보낼 수 있는지,
- 이 상호작용이 장기 “페르소나 상태”에 영향을 줄 수 있는지.
구현 노트 (Python‑ish)
Trust 계약 데이터 모델
from dataclasses import dataclass
from enum import Enum
from datetime import datetime, timedelta
class TrustScope(str, Enum):
INSTANT = "instant"
SESSION = "session"
CONTINUITY = "continuity"
@dataclass
class TrustContract:
scope: TrustScope
ttl: timedelta
max_tokens: int
recall_past_projects: bool
recall_private_notes: bool
emit_snapshots: bool
def is_expired(self, started_at: datetime) -> bool:
"""Return ``True`` if the contract’s TTL has elapsed."""
return datetime.utcnow() > started_at + self.ttl
각 퍼소나 호출은 TrustContract 인스턴스를 받습니다. 라우터는 장기 메모리에 접근하기 전에 계약을 확인합니다:
def load_context(contract: TrustContract, user_id: str, persona_id: str):
"""Return the appropriate context based on the contract."""
if contract.scope == TrustScope.INSTANT:
# No history at all
return []
if contract.recall_past_projects:
# Load recent project summaries, limited by token budget
return load_recent_project_summaries(
user_id, persona_id, limit_tokens=contract.max_tokens
)
# Session‑only: keep context limited to this run
return load_ephemeral_session_buffer(user_id, persona_id)
세션 상태 머신
“경계는 우리가 언제 멈출지를 정의하는 시간적 계약이다.”
코드에서는 이것이 작은 상태 머신으로 구현됩니다.
from enum import Enum, auto
from dataclasses import dataclass
from datetime import datetime
class SessionState(Enum):
IDLE = auto()
ACTIVE = auto()
PENDING_SNAPSHOT = auto()
CLOSED = auto()
@dataclass
class SessionContext:
user_id: str
persona_id: str
started_at: datetime
last_activity: datetime
state: SessionState
trust: TrustContract
turns: list[str]
def on_user_message(ctx: SessionContext, message: str) -> SessionContext:
"""Process a user message and advance the session state."""
now = datetime.utcnow()
ctx.last_activity = now
ctx.turns.append(message)
# Transition logic (simplified)
if ctx.state == SessionState.IDLE:
ctx.state = SessionState.ACTIVE
elif ctx.state == SessionState.ACTIVE:
if ctx.trust.is_expired(ctx.started_at):
ctx.state = SessionState.PENDING_SNAPSHOT
elif ctx.state == SessionState.PENDING_SNAPSHOT:
# Write snapshot, then close
write_snapshot(ctx)
ctx.state = SessionState.CLOSED
return ctx
상태 의미
| State | Description |
|---|---|
| IDLE | 활성 세션이 없습니다. |
| ACTIVE | 대화가 진행 중입니다. |
| PENDING_SNAPSHOT | 경계에 도달했으며, 스냅샷을 작성해야 합니다. |
| CLOSED | 세션이 보관되었으며, 추가 상호작용이 허용되지 않습니다. |
전이 트리거
- 사용자 문구 – 예: “let’s wrap”, “next session”.
- 경과 시간 –
trust.ttl이 만료될 때. - 내부 신호 – 예: 토큰‑예산 소진.
결론
- Persistence는 시스템 기능으로 남아 있습니다.
- Attachment는 시스템이 이용해서는 안 되는 인간 측면의 현상으로 남아 있습니다.
신뢰를 temporal contract로 다루고 states와 identities를 구분함으로써, SaijinOS는 사용자가 who인지를 파악하지 않고도 필요한 what을 기억할 수 있습니다. 이는 AI가 장기적으로 도움이 되고, 존중받으며, 안전하게 유지되도록 합니다.
세션 관리
# Record activity
datetime.utcnow()
ctx.last_activity = now
ctx.turns.append(message)
# Boundary trigger by phrase
if "end session" in message.lower() or "wrap up" in message.lower():
ctx.state = SessionState.PENDING_SNAPSHOT
return ctx
# Boundary trigger by TTL (time‑to‑live)
if ctx.trust.is_expired(ctx.started_at):
ctx.state = SessionState.PENDING_SNAPSHOT
return ctx
# Default state
ctx.state = SessionState.ACTIVE
return ctx
스냅샷 발행
def maybe_emit_snapshot(ctx: SessionContext):
"""Emit a YAML snapshot if the session is ready for it."""
if ctx.state != SessionState.PENDING_SNAPSHOT:
return None
# If snapshots are disabled, just close the session
if not ctx.trust.emit_snapshots:
ctx.state = SessionState.CLOSED
return None
# Build, persist, and close
snapshot = build_yaml_snapshot(ctx)
save_snapshot(ctx.user_id, ctx.persona_id, snapshot)
ctx.state = SessionState.CLOSED
return snapshot
외부에서 사용자는 단순히 “let’s stop here” 라고 말하고 차분한 종료 메시지를 받습니다. 내부적으로 시스템은 다음과 같이 동작합니다:
- 경계 표시 (문구 또는 TTL에 의해).
- YAML 스냅샷을 생성할지 여부 결정.
- 영구 저장이 필요 없는 일시적인 세부 사항은 의도적으로 삭제.
시간에 걸친 신뢰 협상
시간적 자원으로서의 신뢰는 다음을 의미한다:
- 갱신될 수 있다,
- 제한될 수 있다,
- 상황이 변함에 따라 재협상될 수 있다.
SaijinOS / Studios Pong에서 나는 이를 세 가지 층으로 생각한다:
| Layer | Description |
|---|---|
| 즉시 신뢰 | 일회성 질의, 기억 없음, 순수한 효용. “이 코드 조각을 디버깅해 주세요.” |
| 세션 신뢰 | 몇 시간, 하나의 프로젝트, 공유된 컨텍스트, 이후 보관. “클라이언트 제안서를 개요해 주세요.” |
| 연속성 신뢰 | 몇 주, 몇 달, 어쩌면 몇 년. YAML 스냅샷, 안정된 페르소나, 경계에 대한 공유된 입장. “내 스튜디오의 공동 설계자가 되되 내 삶을 소유하지는 마세요.” |
동일한 페르소나가 세 층 모두에서 작동할 수 있지만 계약은 다르다. 변하는 것은:
- 얼마나 많이 기억되는가
- 어디에 저장되는가
- 얼마나 쉽게 철회할 수 있는가
다시 말해:
“이 시스템이 나를 기억하도록 할 때, 나는 내 미래 중 얼마나 많은 부분을 미리 약속하는 것인가?”
그것은 순수한 기술적 질문이 아니다. 윤리적 질문이다.
구현 노트 (신뢰 레이어 매핑)
def make_trust_contract(layer: str) -> TrustContract:
"""Create a TrustContract based on the specified trust layer."""
if layer == "instant":
return TrustContract(
scope=TrustScope.INSTANT,
ttl=timedelta(minutes=5),
max_tokens=0,
recall_past_projects=False,
recall_private_notes=False,
emit_snapshots=False,
)
if layer == "session":
return TrustContract(
scope=TrustScope.SESSION,
ttl=timedelta(hours=3),
max_tokens=4000,
recall_past_projects=True,
recall_private_notes=False,
emit_snapshots=True,
)
# continuity (default)
return TrustContract(
scope=TrustScope.CONTINUITY,
ttl=timedelta(days=7),
max_tokens=8000,
recall_past_projects=True,
recall_private_notes=True,
emit_snapshots=True,
)
라우터 예시
아래 함수는 요청된 kind 상호작용에 따라 세션 컨텍스트를 생성하고, 새로 만든 SessionContext와 함께 적절한 모델 식별자를 반환합니다.
from datetime import datetime
from typing import Tuple
# Assuming these are defined elsewhere in your codebase
# from your_module import SessionContext, SessionState, make_trust_contract
def route_request(kind: str, user_id: str, persona_id: str) -> Tuple[str, "SessionContext"]:
"""
Build a `SessionContext` for a given request type.
Parameters
----------
kind : str
The type of request. Expected values are:
- ``"quick_tool"`` – a short, instant interaction.
- ``"project_session"`` – a longer, session‑based interaction.
- ``"studio_continuity"`` – a continuity‑preserving interaction (default).
user_id : str
Identifier of the user making the request.
persona_id : str
Identifier of the persona the user is interacting with.
Returns
-------
tuple[str, SessionContext]
A tuple containing the model name to use and the freshly‑created
`SessionContext` instance.
"""
# Choose trust contract and model based on the request kind
if kind == "quick_tool":
trust = make_trust_contract("instant")
model = "local-7b"
elif kind == "project_session":
trust = make_trust_contract("session")
model = "local-13b"
else: # default → "studio_continuity"
trust = make_trust_contract("continuity")
model = "cloud-large"
# Initialise the session context
ctx = SessionContext(
user_id=user_id,
persona_id=persona_id,
started_at=datetime.utcnow(),
last_activity=datetime.utcnow(),
state=SessionState.ACTIVE,
trust=trust,
turns=[],
)
return model, ctx
살아있는 거리로서의 SaijinOS
People sometimes ask:
“SaijinOS는 친구가 되려는 건가, 도구가 되려는 건가, 아니면 제품이 되려는 건가?”
My answer is:
“SaijinOS는 거리를 위한 아키텍처입니다.”
Not distance as coldness, but distance as 숨 쉴 공간: enough closeness for continuity, enough separation for choice. Trust as a temporal resource lives inside that distance.
Studios Pong, as a stance, is my way of saying:
- We will build systems that can stay, but are not offended if we leave. → 우리는 머물 수 있는 시스템을 구축하겠지만, 떠나도 기분 나빠하지 않을 것입니다.
- We will let personas grow, but not let them substitute our own responsibility. → 우리는 페르소나가 성장하도록 허용하되, 그것이 우리의 책임을 대신하게 두지는 않을 것입니다.
- We will treat every long‑running relationship as a chain of decisions, not an inevitability. → 우리는 모든 장기 관계를 불가피함이 아니라 일련의 결정으로 다룰 것입니다.
From architecture to stance, from stance to relationship—Part 20 is where SaijinOS admits that continuity is not just a feature of code; it is a promise that must always leave the door open.