Skip to main content

Robot Scripting Language (RSL) - 카미봇 미니 DSL: 파서/검증기/async 인터프리터

Project description

pykamrsl — Kamibot Pi 미니 DSL (RSL)

RSL (Robot Scripting Language) — 카미봇 파이(Kamibot Pi) 로봇을 제어하기 위한 작은 도메인 특화 언어와 그 실행 도구체인. parse → validate → interpret 3 단 파이프라인을 무의존성(dependencies = []) 으로 제공한다.

pykamrsl 자체는 시리얼 포트를 열지 않는다 — 인터프리터는 사용자가 주입한 비동기 도구 사전(dict[str, AsyncTool]) 을 디스패치한다. 실제 카미봇 조작은 LangChain BaseTool, MCP 서버, 또는 직접 만든 stub 어느 백엔드로도 갈아 끼울 수 있다.

설치

pip install pykamirsl
이름 용도
배포명 (pykamirsl) pip install 시 입력
임포트명 (pykamrsl) 코드에서 import pykamrsl

LangChain BaseTool 어댑터와 같이 쓸 거라면 선택 의존성을 함께 설치:

pip install "pykamirsl[langchain]"

5 분 시작 — 하드웨어 없이

FakeTool 만 있으면 카미봇이 없어도 전 파이프라인을 그대로 돌려볼 수 있다.

import asyncio
from pykamrsl import parse, validate, RSLInterpreter

# 1) RSL 스크립트
source = """
BEGIN MISSION "장애물 회피"
  SET threshold = 30
  SENSE DISTANCE AS d
  IF d > threshold:
    MOVE BACKWARD CM=10
    BEEP
    SIGNAL "장애물 감지 — 후진 d={d}"
  ELSE:
    MOVE FORWARD CM=20
  END IF
END MISSION
"""

# 2) parse: 텍스트 → Mission AST
mission = parse(source)

# 3) validate: 값 범위 + 변수 정의 정적 검증
errors = validate(mission)
print("validation errors:", errors)   # [] = 통과

# 4) 도구 stub (실제 환경에선 LangChain BaseTool / MCP 어댑터로 교체)
class FakeTool:
    def __init__(self, name, ret="ok"):
        self.name = name
        self._ret = ret
    async def ainvoke(self, args):
        print(f"  → {self.name}({args})")
        return self._ret

tools = {
    "move_forward_cm":          FakeTool("move_forward_cm"),
    "move_backward_cm":         FakeTool("move_backward_cm"),
    "play_beep":                FakeTool("play_beep"),
    # SENSE 결과는 dict 또는 JSON 문자열로 반환 — 인터프리터가 알아서 디코드.
    # DISTANCE 는 min(left, right) 가 변수에 들어감 → 여기선 d=50 → IF 분기.
    "read_object_distance_raw": FakeTool("read_object_distance_raw",
                                         {"left": 50, "right": 80}),
}

# 5) 인터프리터 실행
log = asyncio.run(RSLInterpreter(tools).arun(mission))
print("\n".join(log))

출력 예:

validation errors: []
[CMD] read_object_distance_raw()
[CMD] move_backward_cm(distance_cm=10)
[CMD] play_beep()

=== 미션 시작: 장애물 회피 ===
센서 DISTANCE → d=50
ok
ok
SIGNAL: 장애물 감지 — 후진 d=50
=== 미션 종료 ===
  • [CMD] 줄은 인터프리터가 콘솔로 echo 하는 호출 시그니처. 끄려면 RSLInterpreter(tools, echo_commands=False).
  • ok 두 줄은 FakeTool 의 반환값 — 실제 환경에서는 도구가 돌려준 한국어 안내 문자열이 그대로 로그에 들어간다.
  • SENSE DISTANCEmin(left, right) 를 변수에 저장한다 (d=50).

패키지 표면

from pykamrsl import (
    parse, ParseError,             # 텍스트 → Mission AST
    validate, ValidationError,     # AST 의미·범위 검증
    RSLInterpreter, RuntimeRSLError, MAX_LOOP_ITERS,
    SYSTEM_PROMPT, build_retry_message,
)

pykamrsl.ast_nodes 도 export 됨: Mission, Assign, Move, Turn, Sense, Signal, If, Loop, Condition, IntLit, StrLit, VarRef 등.

RSL 문법 한눈에 보기

mission     := BEGIN MISSION "title" ... END MISSION
statement   := assign | move | turn | stop | wait | led | sound
             | draw | sense | signal | if_block | loop_block | repeat_block
assign      := SET name = value
move        := MOVE FORWARD|BACKWARD  CM = v
            |  MOVE FORWARD|BACKWARD  SECONDS = v
            |  MOVE FORWARD|BACKWARD  SPEED = v        # 연속 이동 — STOP 전까지
turn        := TURN LEFT|RIGHT ANGLE = v
stop        := STOP
wait        := WAIT SECONDS = v
led         := LED RGB r, g, b
            |  LED PRESET n
sound       := BEEP | MELODY SCALE = s SECONDS = t
draw        := DRAW TRIANGLE|RECTANGLE|PENTAGON|HEXAGON|STAR|CIRCLE SIZE = v
sense       := SENSE DISTANCE|LINE_LEFT|LINE_CENTER|LINE_RIGHT|COLOR|BATTERY AS var
signal      := SIGNAL "message with {var} interpolation"
if_block    := IF cond:
                 ...
               (ELSE:
                 ...)?
               END IF
loop_block  := LOOP UNTIL|WHILE cond:
                 ...
               END LOOP
repeat_block := REPEAT count TIMES:
                 ...
               END REPEAT
cond        := value <|>|<=|>=|==|!= value
            |  var                           # 단일 변수 → truthy 검사
value       := 정수 | "문자열" | var
  • 키워드는 모두 영어 대문자. 변수명은 [a-z0-9_]+.
  • 코멘트는 # ... (라인 끝까지).
  • 블록 종료는 명시적 END IF / END LOOP / END MISSION — 들여쓰기 의존 없음.

수치 허용 범위 (검증기)

매개변수 범위
speed 0 ~ 100
cm 1 ~ 200
seconds (이동/대기) 1 ~ 30
degrees 1 ~ 360
RGB 채널 0 ~ 255
LED PRESET 0 ~ 8 (0=꺼짐, 1=빨강, 2=주황, 3=노랑, 4=초록, 5=파랑, 6=하늘, 7=보라, 8=흰색)
DRAW SIZE 5 ~ 50
MELODY SCALE / SECONDS 1 ~ 200 / 1 ~ 60
REPEAT count 1 ~ 100 (= MAX_LOOP_ITERS)

리터럴이 범위를 벗어나면 validate() 가 에러 메시지를 리스트로 반환한다. 변수에 담긴 값은 런타임에 인터프리터가 검사한다.

도구 인터페이스 (인터프리터 백엔드)

RSLInterpreter(tools_by_name)tools_by_name: dict[str, AsyncTool] 은 다음 구조적 인터페이스만 만족하면 된다. langchain_core.BaseTool 인스턴스가 자동으로 만족한다 — langchain-mcp-adapters 가 돌려주는 도구도 그대로 주입 가능.

from typing import Protocol, Any

class AsyncTool(Protocol):
    name: str
    async def ainvoke(self, args: dict) -> Any: ...

인터프리터가 호출하는 도구 이름

RSL 문장 도구 이름 인자 비고
MOVE FORWARD CM=v move_forward_cm distance_cm
MOVE BACKWARD CM=v move_backward_cm distance_cm
MOVE * SECONDS=v move_*_seconds seconds
MOVE * SPEED=v drive_*_continuous speed 연속 이동
TURN LEFT/RIGHT turn_left_degrees / turn_right_degrees degrees
STOP stop_now
WAIT SECONDS=v wait_seconds seconds
LED RGB r, g, b set_led_rgb r, g, b
LED PRESET n set_led_preset preset_index
BEEP play_beep
MELODY SCALE=s SECONDS=t play_melody_note scale, seconds
DRAW <shape> SIZE=v draw_<shape> size shape ∈ {tri,rect,penta,hexa,star,circle} 의 풀네임
SENSE DISTANCE read_object_distance_raw 반환 {"left": int, "right": int}
SENSE LINE_* read_line_sensors_raw 반환 {"left": int, "center": int, "right": int}
SENSE COLOR read_color_index_raw 반환 정수
SENSE BATTERY read_battery_level_raw 반환 정수

센서 도구의 dict / int 는 그대로 돌려줘도 되고 JSON 문자열로 직렬화해서 돌려줘도 된다 — 인터프리터의 _to_text / _coerce_dict / _coerce_int 가 양쪽을 모두 처리한다.

LLM 으로 RSL 자동 생성 (선택)

자연어 한국어 명령 → RSL 변환을 LLM 에 맡길 때 쓰는 시스템 프롬프트가 패키지에 포함돼 있다. few-shot 예시 3 개와 출력 규칙이 들어 있다.

from pykamrsl import SYSTEM_PROMPT, build_retry_message
from openai import OpenAI                                    # 예시 — 다른 모델 OK

client = OpenAI()
resp = client.chat.completions.create(
    model="gpt-4o-mini",
    temperature=0,
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user",   "content": "앞으로 20cm 가고 LED 를 파란색으로 켜줘"},
    ],
)
source = resp.choices[0].message.content
# 이후 parse → validate → RSLInterpreter.arun 로 흘리면 끝.
# validate 가 에러를 돌려주면 build_retry_message(errors) 로 LLM 에 재시도 요청 가능.

SIGNAL 의 {var} 보간

SIGNAL "장애물 감지 — d={d}, threshold={threshold}" 는 실행 시점의 변수 값으로 치환된다. 정의되지 않은 변수는 원본 그대로 남는다.

V1 알려진 제약

  • 산술/논리 연산자 없음 — + - * / AND OR NOT 미지원. 비교 6 종 < > <= >= == != 만.
  • 변수에 상수만 대입 가능 (SET x = 30 ✓, SET x = y + 1 ✗).
  • LOOP 본문은 최대 100 회 실행 후 자동 종료 — SIGNAL: loop_iteration_cap_reached 가 로그에 남음.
  • 단순 N 회 반복은 REPEAT N TIMES: … END REPEAT 를 사용한다. 본문은 카운터 변수를 노출하지 않으며 N 은 1~100. 변수 N 이 100 을 넘으면 런타임에 100 으로 잘리고 SIGNAL: repeat_count_clamped 가 로그에 남음.

더 큰 예제

저장소의 examples/ 디렉토리에는 다음을 포함한 end-to-end LangGraph 에이전트 예제가 들어 있다:

  • LLM → RSL → MCP 서버 → 카미봇 통합 흐름
  • MCP 서버 (kamibot_mcp_server.py) — 카미봇 도구 24 종을 stdio 로 노출
  • KAMIBOT_MOCK=true 로 하드웨어 없이 통합 테스트
pip install "pykamirsl[langchain]" langchain-openai langgraph langchain-mcp-adapters
python -m examples.main

라이선스

MIT. 학습용으로 자유롭게 수정·재배포 가능.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pykamirsl-0.1.2.tar.gz (22.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pykamirsl-0.1.2-py3-none-any.whl (22.0 kB view details)

Uploaded Python 3

File details

Details for the file pykamirsl-0.1.2.tar.gz.

File metadata

  • Download URL: pykamirsl-0.1.2.tar.gz
  • Upload date:
  • Size: 22.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.4

File hashes

Hashes for pykamirsl-0.1.2.tar.gz
Algorithm Hash digest
SHA256 e4b8e4ece4def7657ea3ab74f634ff511e90c4bdf1eb740abc135fe83798895b
MD5 a8da4188ca4f596d4589aae53cbaeb25
BLAKE2b-256 5d875241fece90e1700283c9bc94decb697e091e561e8fb22b9c15e06b659dde

See more details on using hashes here.

File details

Details for the file pykamirsl-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: pykamirsl-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 22.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.4

File hashes

Hashes for pykamirsl-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 521ddb9039fd4417319474658221877fae911ff5086535f9831235da01a5f042
MD5 d5ba7ca40ccdd3ab2de1edc2e27e6d61
BLAKE2b-256 1103db6c4d36971713cca7e0e51a9cf8a6896eee37d55f707ae2501bca50dd46

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page