# 구조

> 플랫폼도 두뇌도 갈아 끼우는데 코어는 하나입니다. 그 사이에 사람이 지키는 선이 있습니다.

- 수치: 어댑터 1
- 사람이 보는 화면: https://aitryed.com/ko/built/loki/architecture/
- 반대 언어: https://aitryed.com/en/built/loki/architecture/index.md
- 이 사이트 안내(AI용): https://aitryed.com/ko/llms.txt

---

**메신저도 갈아 끼우고 AI도 갈아 끼우는데, 가운데 몸통은 하나입니다. 그 사이에 «사람이 지켜야 하는 선»이 하나 있습니다.**

> **[도판]** 플랫폼은 손잡이일 뿐이고, 어댑터 뒤는 전부 공용이다. 디스코드를 붙일 때 새로 쓴 건 어댑터 하나였다.
>
> 슬랙과 디스코드에서 온 메시지가 어댑터를 지나 신뢰 경계를 넘고, 잡 큐와 세션을 거쳐 두뇌 CLI로 가서 내 PC에서 셸과 파일과 스킬을 실행한다.

## 「갈아 끼운다」가 무슨 뜻인가

이 물건은 **양쪽 끝이 바뀔 수 있게** 만들어져 있습니다.

| 끝 | 바뀔 수 있는 것 |
|---|---|
| **앞쪽** | 슬랙 / 디스코드 / 그 밖의 메신저 |
| **뒤쪽** | 어떤 AI를 쓸 것인가 |

그리고 **가운데는 하나**입니다. 그래서 디스코드를 붙일 때 **새로 쓴 건 연결 부품 하나**였습니다. 나머지는 그대로 썼습니다.

## 연결 부품이 지킬 약속은 넷뿐입니다

메신저를 새로 붙이려면 **네 가지만** 하면 됩니다.

| 약속 | 무엇 |
|---|---|
| **말을 표준 형태로 바꾼다** | 메신저마다 메시지 생김새가 다름 |
| **권한을 판정한다** | 이 사람이 이걸 시켜도 되나 |
| **일감으로 넘긴다** | 실제 작업 쪽으로 |
| **답을 돌려준다** | 원래 자리로 |

**넷뿐이라는 게 중요합니다.** 약속이 많으면 새 메신저를 붙일 때마다 고생하고, 결국 안 붙이게 됩니다.

## AI를 갈아 끼우는 방식이 특이합니다

보통은 **AI 회사의 열쇠(API 키)를 발급받아** 넣습니다. 그러면 쓴 만큼 돈이 나갑니다.

이 물건은 다릅니다. **이 PC에 이미 로그인돼 있는 프로그램을 «대신 실행»합니다.**

제가 평소에 쓰는 프로그램이 이미 깔려 있고 로그인돼 있으니, **그걸 그대로 부르는 것**입니다.

| | 열쇠 방식 | 이 방식 |
|---|---|---|
| 로그인 | 열쇠를 따로 발급 | **이미 돼 있는 걸 씀** |
| 요금 | 쓴 만큼 | **월정액 안에서** |
| 조건 | 어디서나 됨 | **그 PC에서만 됨** |

마지막 줄이 대가입니다. **이 방식은 제 PC에 묶입니다.** 그래도 「제 PC에게 시키는 물건」이라 그 제약이 문제가 안 됐습니다.

## 그리고 여기서 값을 치른 것 — 「제공자 하나가 갑자기 닫혔습니다」

쓰고 있던 AI 제공처 하나가 **개인 계정에 문을 닫았습니다.** 무료뿐 아니라 **돈 내고 쓰던 구독까지 같이** 막혔습니다.

이게 무서운 이유는 **제 잘못이 아니고, 제가 막을 수도 없었다**는 점입니다. 어느 날 갑자기 안 됩니다.

그래서 배운 게 이겁니다.

> **갈아 끼울 수 있게 만든 건 편하려고가 아니라, 한쪽이 죽어도 살아남으려고입니다.**

만약 그 제공자에 단단히 묶여 있었으면 **그날 이 물건 전체가 죽었습니다.** 갈아 끼우는 구조라서 **연결 부품 하나 바꾸고 계속** 갔습니다.

이런 건 **평소엔 낭비처럼 보입니다.** 안 갈아 끼우면 필요 없는 구조니까요. **한 번 필요해질 때 값을 다 합니다.**

## 여기부터는 자세한 기록입니다

## 어댑터의 계약은 네 개다

플랫폼 어댑터가 지켜야 할 것은 넷뿐이다 — **들어온 걸 정규화하고, 권한을 판정하고, 잡으로 넘기고, 답을 돌려준다.**

그 뒤로는 코어가 슬랙인지 디스코드인지 모른다. 그래서 디스코드를 붙일 때 새로 쓴 게 어댑터 하나였다.

계약이 네 개라는 게 중요하다. 셋이면 권한 판정이 코어로 새고, 다섯이면 플랫폼마다 다른 게 코어에 스며든다. **경계는 넓지도 좁지도 않아야 하고, 그 폭은 "플랫폼마다 정말로 다른 것"이 몇 개인지로 정해진다.**

디스코드를 붙이면서 이식 버그가 하나 나왔다. 조직 시스템이 사용자 식별자를 **슬랙 형식으로 하드코딩한 정규식**으로 검사하고 있었고, 디스코드의 숫자 식별자를 전부 거부했다. 플랫폼 중립이라고 믿었던 코어에 플랫폼이 스며 있던 자리다.

## 두뇌도 갈아 끼운다

응답을 만드는 CLI를 바꿀 수 있다. 방식이 특이한데, **이 PC에 이미 로그인돼 있는 CLI를 스폰한다.**

그래서 어느 걸 골라도 **요금제가 정액 그대로**다. API 키를 꽂는 방식이었으면 제공자를 바꿀 때마다 과금 모델이 바뀌었을 텐데, 로그인된 CLI를 부르는 방식이라 그 사람의 구독이 그대로 적용된다.

세션은 **제공자별로 따로 파일링**된다. 세션 식별자는 그 에이전트 저장소의 영수증이라 섞으면 안 되기 때문이다. 전환해도 양쪽 대화가 다 보존된다.

다만 예외가 있다. **손님 요청과 오너의 DM 밖 요청은 무엇을 골랐든 항상 기본 두뇌로 간다.** 권한 규칙이 그 CLI 형식의 파일이라, 다른 CLI가 들고 가지 못한다. 안전장치가 못 따라가면 전환도 안 시키는 게 맞다.

## 죽은 제공자를 배운 것

제공자 하나가 개인 계정에 닫혔다. 무료뿐 아니라 유료 구독도 함께.

증상이 고약했다. **로그인은 성공하고, 첫 요청에서 죽는다.** 그래서 "설정은 됐는데 왜 안 되지"가 된다.

그 자리를 후속 CLI로 메우면서 함정을 셋 더 밟았다.

- **stdin 관용구처럼 보이는 인자가 stdin을 버렸다.** `-p -` 형태를 넘겼더니 `-`를 프롬프트 문자열로 받고 표준입력을 무시했다. 인사말이 돌아왔다.
- **죽은 대화 식별자가 에러가 아니었다.** 경고만 하고 새 대화로 자가 복구해 새 식별자를 준다. 그래서 재시도 로직이 아니라 **돌아온 식별자를 저장하는 코드**가 필요했다.
- **설치가 PATH를 레지스트리에만 넣었다.** 이미 돌고 있던 워커는 그걸 못 본다. 폴백 경로가 없으면 "설치했는데 아무 일도 안 일어남"이 된다.

셋 다 [조용한 고장](/ko/built/loki/silent-failures/index.md) 계열이다. 어느 것도 예외를 던지지 않는다.
