Neander: 에이전트 우선 프로그래밍 언어 (Agent-First Programming Language)
요약
에이전트 중심의 새로운 프로그래밍 언어인 Neander를 소개합니다. 기존의 도구 카탈로그 방식에서 벗어나, 에이전트가 런타임에 필요한 API를 직접 탐색(discover)하고 호출(call)하는 역전된 모델을 제안합니다.
핵심 포인트
- 에이전트가 컨텍스트에 모든 도구를 로드하는 대신, 런타임에 필요한 기능을 탐색함
- 컨텍스트 창의 혼잡을 줄여 비용과 지연 시간을 효과적으로 절감
- discover와 call이라는 두 가지 핵심 동사를 기반으로 한 언어 구조
- 호스트 내부에서 실행되는 Neander 런타임을 통해 동적인 API 발견 지원
지난번 Source Code as the Seam Between Systems에서, 저는 시스템 사이의 이음새(seam)를 위한 프로그래밍 언어를 구축했다고 말하며 글을 마쳤고, 다시 돌아오겠다고 약속했습니다.
이제 그 약속을 지키러 왔습니다.
이 언어의 이름은 Neander(Neanderthal에서 명명됨)이며, 현재 사양(specification)을 확인할 수 있습니다.
지난 포스트가 어디에서 끝났는지 빠르게 요약해 보겠습니다. 한 시스템이 다른 시스템에 무언가를 요청해야 할 때, 그 사이의 이음새는 구조화된 데이터(structured data)를 전달하는 와이어(wire)였으며, 그 중간에는 통합 코드(integration code)를 작성하는 인간이 있었습니다. 여기서 인간을 제외하고, 호출하는 시스템이 런타임(runtime)에 무엇이 필요한지 결정하는 에이전트(agent)가 되도록 하면, 이음새는 더 이상 와이어가 아니게 됩니다. 그것은 언어가 됩니다. 호출되는 시스템은 실행 환경(execution environment)을 노출하고, 호출자는 작은 프로그램들을 보냄으로써 이를 구동합니다.
Neander가 바로 그 언어입니다.
역전 (The inversion)
모두가 시작했던 모델은 도구 카탈로그(tool catalog) 방식입니다. 호스트가 노출하는 모든 함수를 에이전트의 컨텍스트(context)에 로드한 다음, 에이전트가 선택하게 하는 방식이죠. 이는 확장성(scale)이 없습니다. 수백 개의 도구 정의가 컨텍스트를 어지럽히고, 모든 중간 결과가 그 위에 쌓이며, 그에 따라 비용과 지연 시간(latency)이 상승합니다.
Neander는 이를 뒤집습니다. 에이전트의 컨텍스트는 호스트가 할 수 있는 모든 것의 카탈로그를 갖는 대신, 하나의 압축된 요소인 Neander Reference 자체를 보유합니다. 무언가를 수행하기 위해 에이전트는 짧은 프로그램을 작성합니다. 프로그램은 런타임에 어떤 API를 사용할 수 있는지 묻고, 필요한 것을 호출하며, 결과를 조합하여 단 하나의 답변을 돌려줍니다. 발견(Discovery)은 컨텍스트 창(context window)에서 미리 이루어지는 것이 아니라, 프로그램 내부의 런타임에서 발생합니다.
이로 인해 언어에는 대부분의 비중을 차지하는 두 개의 동사가 생깁니다. discover는 런타임에 어떤 네임스페이스(namespaces), 함수(functions), 문서(documents)가 존재하는지 묻고, call은 해당 함수 중 하나를 호출합니다. 그 외의 모든 것 — 분기(branching)와 유한 루프(bounded loops), 구조적 타입 시스템(structural type system), 명시적 에러 처리(explicit error handling) — 은 이 두 가지를 결합하기 위해 존재합니다.
먼저 에이전트가 주변을 탐색합니다:
neander 1 {
types {}
main -> [Function] {
...
에이전트는 반환된 설명(descriptions)을 읽은 다음, 찾아낸 것을 호출하는 두 번째 프로그램을 작성합니다:
neander 1 {
types {}
main -> decimal(2, half_away) {
...
이것이 본질적으로 전체적인 형태입니다. 한 번의 호출, 결과에 대한 루프(loop), 약간의 조건부 로직(conditional logic), 그리고 또 다른 호출. 에이전트는 단일 작업을 위해 이를 작성하여 런타임(runtime)으로 보내고, 작업이 끝나면 버립니다.
흥미로운 점은 코드가 실행되는 위치입니다. Neander 런타임은 호스트(host)
_내부_에 존재합니다. 임베딩 애플리케이션(embedding application)이 자체 API를 런타임에 등록하므로, 실행은 이러한 API 바로 옆인 서버 측(server-side)에서 이루어집니다. 위에서 설명한 바와 같이, 에이전트는 외부에 머물며 신뢰할 수 없는 상태로 유지되고, 임베딩 애플리케이션과 통신하기 위해 이 언어를 사용합니다.
물론, 이러한 설정은 몇 가지 의구심을 자아낼 수 있습니다.
Neander가 의도적으로 할 수 없는 것들
안심할 수 있는 점은 구조적인 측면에서 오는 것이지, 행동을 약속하는 것이 아닙니다. 타인의 코드를 실행하는 것을 위험하게 만드는 요소들이 Neander에는 존재하지 않습니다. 격리할 대상 자체가 없기 때문입니다.
- 영원히 실행될 수 없습니다. 의도적으로 튜링 완전성(Turing-complete)을 갖추지 않았습니다. 재귀(recursion)는 없으며, 모든 루프(loop)는 정적으로 경계가 정해져 있습니다. 프로그램이 실행되기 전에 종료(termination)가 증명되므로, 밤잠을 설칠 정도의 정지 문제(halting question)는 걱정할 필요가 없습니다.
- 외부로 뻗어나갈 수 없습니다. 파일 I/O, 소켓(sockets), 시스템 접근이 불가능합니다. 프로그램이 건드릴 수 있는 유일한 것은 호스트가 등록하기로 선택한 API뿐입니다. 격리할 대상이 없으므로 샌드박스(sandbox)도 필요하지 않습니다. 언어 자체가 곧 샌드박스입니다.
- 비용을 폭증시킬 수 없습니다. 모든 실행은 연산, 메모리, 시간에 대한 엄격한 상한선(예산 시스템, budget system) 하에서 실행됩니다. 이를 초과하면 실행 중인 호스트가 아니라 _해당 프로그램_이 중단됩니다.
- 호스트의 API를 오용할 수 없습니다. 검증(validation)에 실패한 프로그램은 절대 실행되지 않습니다. 검증을 통과한 프로그램은 존재하는 함수만을 올바른 타입의 인자(arguments)와 함께 호출하며, 존재하지 않을 수도 있는 값을 존재하는 것처럼 취급할 수 없습니다.
그 이름은 철학에 부합합니다. 컴퓨팅 초기 시절의 여분의 제한적인 언어들처럼, 이 언어는 아주 적은 일만 할 수 있습니다. 그리고 언어가 할 수 있는 일이 적을수록, 잘못될 가능성도 적어집니다.
대상 (The audience)
Neander는 특이한 대상을 위해 작성되었습니다. 오직 에이전트(agents)만이 이를 작성하며, 인간은 이를 호스팅(host)합니다. 따라서 시작 가이드나 튜토리얼 같은 것은 없습니다. 수동 코더(manual coder)를 위한 것은 아무것도 없습니다.
규범적인 명세서 (specification) 외에도, 예시 중심의 Neander 레퍼런스(Reference)는 프로그램을 작성할 에이전트들을 정조준하고 있으며, 런타임(runtime)은 이를 인밴드(in-band) 방식으로 그들에게 전달합니다.
newadventuresinit.github.io/neander 웹사이트는 Neander를 평가하고 이를 시스템에 내장할지 결정하려는 인간들을 위한 것입니다. 하지만 인간들이 이를 실무에 투입하기 전에 아직 발표되지 않은 한 가지가 있습니다. 바로 레퍼런스 런타임 구현체(reference runtime implementation)입니다.
그것은 이미 존재합니다. 이에 대해서는 다음 시간에 더 자세히 다루겠습니다.
그동안 Neander 명세서(specification)는 공개되어 있으며, 라이선스는 허용적(permissive)이고, 모든 논의의 장은 열려 있습니다. 한 번 둘러보시고 여러분의 생각을 알려주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기