시리즈: AI와 함께 코딩 배우기

엔진이 여러분이 가져온 어떤 모델과도 대화하는 방법

AI 레이어는 인터페이스다: 엔진을 다시 링크하지 않고 어떤 모델이든 가져오라.

의존성이 아니라 인터페이스로서의 AI 호출자는 앞에. 모델은 뒤에. 작고, 안정적이고, 일부러 단순한 계약. 비헤이비어 트리 내비게이션 군중 시뮬 감각 시스템 의사결정 트리 모델 관리 인터페이스 — 핸들 & 텐서 온디바이스 SLM 클라우드 LLM 학습된 정책 휴리스틱 모델을 바꾼다. 벤더를 바꾼다. 선 위의 아무것도 바뀌지 않는다.
모든 인터페이스 경계는 호출자를 깨뜨리지 않고 미래의 변화가 일어날 수 있는 자리다.

원하는 어떤 모델이든 가져와라. RakuAI의 AI 레이어는 임포트가 아니라 인터페이스다 — 그래서 모델 시장이 분기마다 누가 최고인지를 두고 소란을 피워도, 여러분의 엔진은 신경 쓸 필요가 없다.

이번 주말 누군가 나에게 날카로운 질문을 던졌다. “AI가 여러분 엔진 안의 런타임 프리미티브라면, 엔진은 여러분이 연결해 둔 어떤 모델에든 갇히는 셈이다. 다음에 모델 벤더가 움직이면, 엔진을 다시 작성해야 한다.”

타당한 우려다. 그리고 틀렸지만, 타당하다.

런타임 안의 AI 프리미티브는 특정 모델이 아니다. 그것은 인터페이스다. 각각 자신만의 C API를 갖는 다섯 개의 별도 서브시스템으로, 어느 모델, 어느 가중치, 어느 추론 경로가 답을 만들어내는지 누구도 알 필요 없이 엔진의 나머지 부분에서 주소 지정이 가능하다. 모델은 인터페이스 뒤에 산다. 호출자는 그 앞에 산다. 둘 사이의 계약은 일부러 작고, 안정적이고, 단순하다.

이 글은 그 경계선이 어떻게 그어졌는지, 그리고 그것을 그어 둔 것에서 왜 계속 더 많은 가치를 얻고 있는지에 관한 것이다.

“AI 프리미티브”가 실제로 가리키는 것

우리 런타임의 AI 레이어는 하나가 아니다. 다섯이다.

  • 비헤이비어 트리. 결정론적 실행 레이어. 의도가 파악된 후 에이전트에게 무엇을 할지 알려준다.
  • 내비게이션 메시와 경로 탐색. “이 공간을 어떻게 이동하는가” 레이어.
  • 군중 시뮬레이션. “많은 에이전트가 어떻게 서로를 피하고 함께 일관되게 행동하는가” 레이어.
  • 감각 시스템과 지각. “에이전트가 무엇을 관찰하는가” 레이어.
  • 의사결정 트리와 복잡한 AI 로직. “내가 아는 모든 것이 주어졌을 때, 나는 무엇을 하고 싶은가” 레이어.

각각은 자체 서브시스템이다. 각각은 자체 DLL로 출시된다. 각각은 공개 C API를 가진다. 이들 중 어느 것도 특정 모델을 임포트하지 않는다. 이들 모두는 각자의 인터페이스를 통해 엔진의 나머지와 대화하며, 서로에게도 같은 방식으로 대화한다.

.raku 경험 파일이 "ai_behavior": "strafe"라고 말할 때, 그것은 모델을 지칭하는 것이 아니다. 등록된 비헤이비어를 지칭하는 것이다. 런타임이 문자열을 해석한다. 문자열은 구현체에 매핑된다. 구현체는 비헤이비어 트리, 의사결정 트리, 학습된 정책, 혹은 손으로 작성한 휴리스틱일 수 있다. 호출자는 알지 못한다. 파일도 알지 못한다. 구현체는 둘 중 어느 쪽도 건드리지 않고 교체될 수 있다.

그것이 전체 트릭이다.

모델 관리 레이어는 그 자체로 서브시스템이다

런타임 안에 실제 머신러닝 추론이 필요했을 때, 우리는 그것을 위 다섯 서브시스템 중 하나에 억지로 끼워넣지 않았다. 자체 API 표면을 가진 여섯 번째 관심사를 추가했다. 모델 관리다.

// Roughly what the surface looks like, simplified for the post.
RakuModelHandle raku_ai_load_model(const char* model_id, RakuModelOptions opts);
RakuInferenceResult raku_ai_infer(RakuModelHandle h, const RakuTensor* input);
void raku_ai_unload_model(RakuModelHandle h);

모델을 호출하고 싶은 비헤이비어 트리 노드는 이 API를 거친다. 벤더 SDK를 임포트해서가 아니다. 특정 런타임에 링크해서가 아니다. 모델 관리 서브시스템에 핸들을 요청하고 그것을 사용해서다.

즉 모델 관리 서브시스템이 코드베이스 안에서 특정 모델 포맷, 벤더, 혹은 추론 프레임워크를 아는 유일한 곳이라는 뜻이다. 엔진 안의 다른 모든 곳은 핸들과 텐서를 본다. 모델을 바꾼다. 런타임을 바꾼다. 벤더를 바꾼다. 그 외에는 아무것도 바뀔 필요가 없다.

이것은 화려하지 않은 인프라 작업이다. 또한 AI 생태계가 분기마다 어느 모델이 새로운 최고인지를 두고 소란을 피울 때 우리가 패닉하지 않게 해 주는 것이기도 하다.

이 경계선은 파일 포맷도 보호한다

모든 .raku 경험 파일 맨 위에 있는 ai 블록을 보라. 다음과 같은 키를 가진다.

"ai": {
  "dda_enabled": true,
  "target_flow_state": 0.7,
  "profiler_mode": "active"
}

이 중 어느 것도 모델을 지칭하지 않는다. 이들은 능력을 지칭한다. “동적 난이도 조절이 켜져 있다. 런타임은 0.7의 플로우 상태를 목표해야 한다. 프로파일러가 활성 상태다.” 런타임은 이 능력들을 전달하기 위해 어느 서브시스템이 관여할지 결정한다. 이번 분기 정답이 비헤이비어 트리라면, 그것이 실행된다. 다음 분기 그것이 작은 온디바이스 모델이 되더라도, 파일은 바뀌지 않는다.

시스템 안에 긋는 모든 인터페이스 경계는 호출자를 깨뜨리지 않고 미래의 변화가 일어날 수 있는 자리다. 우리는 처음부터 그것들을 공격적으로 그었다. 지금 우리는 그 절약분을 쓰고 있다.

이것이 왜 점점 덜 중요해지지 않고 더 중요해지는가

세 가지 이유다.

하나. 모델 시장은 계속 움직인다. 작년의 최고 모델은 올해의 비싼 모델이다. 올해의 최고는 내년의 낡은 것이 된다. 엔진에 모델 벤더를 하드코딩한 팀들은 지금쯤 그 통합 작업을 두세 번은 했을 것이다. 그 경로에 모델 관리 인터페이스를 둔 팀들은 그것을 한 번만 했다.

둘. 온디바이스가 예전보다 더 중요해졌다. 인터페이스는 같은 ai_behavior: "strafe"를 개발 환경에서는 서버사이드 모델에 대해, 프로덕션에서는 온디바이스 모델에 대해 실행할 수 있게 해준다. 호출자는 알지 못한다. 그 유연성이야말로 경험 레이어를 다시 작성하지 않고도 온디바이스 추론이 실현 가능한 유일한 이유다.

셋. 개발 루프 안의 AI 어시스턴트는 깔끔한 경계에서 이득을 본다. 어시스턴트에게 새 비헤이비어를 추가해 달라고 요청할 때, 그것이 존중해야 하는 계약은 등록된-비헤이비어 인터페이스다. 벤더 SDK들의 뒤엉킴이 아니다. 인터페이스가 깔끔할수록, 어시스턴트는 더 빨리 올바른 코드를 만들어내고, 내 쪽의 리뷰 비용은 더 작아진다.

이것의 어려운 점

정직한 비용들이다.

인터페이스 설계는 구현보다 시간이 더 걸린다. 인터페이스 단계를 건너뛰고 그냥 작동하는 버전을 작성하고 싶은 유혹은 진짜다. 저항하라. 여기서 취하는 모든 지름길은 나중에 구현체를 교체해야 할 때 값을 치르게 된다.

인터페이스에 무엇을 넣을지에 대해 규율이 있어야 한다. 모든 파라미터는 쉽게 깨뜨릴 수 없는 계약이다. 필요하다고 생각하는 것보다 적게 추가하라. 두 번째 사용 사례가 실제로 무엇이 일반적인지 보여줄 때까지 기다려라. AI 인터페이스의 첫 버전은 거기에 속하지 않는 것으로 밝혀진 다섯 개의 파라미터를 가지고 있었다. 나중에 그것들을 제거하는 것은 고통스러웠다.

등록된 비헤이비어는 버전 관리가 필요하다. "strafe"가 런타임의 한 빌드에서는 한 가지를 의미하고 다음 빌드에서는 약간 다른 것을 의미할 때, 호출자는 게임플레이 회귀를 통해 그것을 알게 된다. 우리는 비헤이비어에 버전을 매기고 .raku 파일을 런타임 버전에 고정한다. 성가시다. 필요하다.

때로는 정말로 의존성을 원하게 된다. 이것이 이단적인 부분이다. 어떤 일반적인 인터페이스도 표현할 수 없는 특정 능력을 특정 모델이 갖는 경우가 있다. 정직한 대응은 그 능력이 일반화되도록 인터페이스를 확장하는 것이지, 그 모델을 호출자로 새어 들어가게 놔두는 것이 아니다. 우리는 한 번 이상 지름길을 택하고 싶은 자신을 붙잡은 적이 있다.

아키텍처적 논지는 단순하다. AI는 런타임 프리미티브다. 그것을 구동하는 파일 포맷은 모델이 아니라 능력을 지칭한다. 런타임은 능력을 현재 그것을 전달하는 어느 서브시스템에든 해석해 낸다. 세 레이어 사이의 배선이 인터페이스다. 인터페이스는 일부러 작고, 안정적이고, 단순하다.

그것이 이 글의 전부다.

다시 만드는 일로 돌아간다.

여러분의 모델을 가져오라. 인터페이스가 기다리고 있다.

RakuAI는 핸들과 텐서를 통해 어떤 모델과도 대화한다 — 개발 환경에서는 서버사이드, 프로덕션에서는 온디바이스, 호출자는 결코 그 차이를 알지 못한다. 여러분의 가중치가 모델 시장보다 오래가도록 만들어진 공간 런타임에 어떻게 꽂히는지 보라.

← 전체 글