PhysiCar API¶
피지카 에이아이의 클라우드 AI 서비스를 내 코드에서 호출하는 방법입니다. 서비스는 둘입니다:
chat(텍스트, POST https://api.physicar.ai/chat)과 realtime(음성,
wss://api.physicar.ai/realtime). 둘 다 하나의 공통 규격으로 뒤에서 여러
프로바이더에 라우팅되고, 같은 프롬프트 형태 — 지시문 + 도구 정의 — 를 받으므로
하나의 "두뇌"로 텍스트 에이전트와 음성 에이전트를 함께 만들 수 있습니다.
완전한 동작 예제는 워크스페이스 노트북 examples/agent.ipynb(커널
Python 3 (PhysiCar AI))입니다: 두 에이전트를 브라우저에서 실행하고 tool call로
로봇을 움직이는 MyApp 페이지.
인증¶
- 세션 토큰으로 인증합니다. HTTP 요청은
Authorization: Bearer <token>헤더로, WebSocket은 서브프로토콜token.<토큰>으로 전달합니다(브라우저는 WS에 헤더를 실을 수 없습니다). - 모든
/myapp/페이지에는 nginx가physicarSession.token()을 자동 주입합니다 — 로그인한 사용자의 토큰을 반환하며, 설정 코드가 필요 없습니다. 반드시 요청마다 읽으세요: 공유 로봇에서 토큰 하나를 전역에 캐시하면 모든 사용자가 한 계정으로 묶입니다. /chat은 게스트 요청(토큰 없음)도 네트워크당 소량의 일일 한도 안에서 허용합니다. 로그인 사용량은 크레딧에서 차감됩니다.
Chat¶
POST https://api.physicar.ai/chat — 턴당 요청 하나. 프롬프트(모델 + 지시문 +
도구)는 매 요청에 인라인으로 보내고, 서버는 대화만 보관합니다 — chat_id/turn으로
지정하며 7일간 유지됩니다.
GET /chat/models가 사용 가능한 모델 목록을 1M 토큰당 가격과 함께 반환합니다
(교실 학생이라면 교사의 허용 여부도 함께).
요청 본문¶
{
"user_message": { "contents": [ { "type": "text", "text": "안녕!" } ] },
"prompt": { "model": "...", "instructions": "...", "tools": [ ... ] },
"stream": true,
"audio": false,
"chat_id": "…",
"turn": 0
}
user_message.contents—text및/또는image({ "type": "image", "mime": "image/jpeg|image/png", "base64": "…" }) 파트.user_message.tool_call_outputs— 직전 턴 tool call들의 실행 결과(아래 도구 루프 참고).prompt—model,instructions,tools,reasoning_effort,max_completion_tokens.max_completion_tokens는 서버가 잔액이 감당하는 만큼으로 상한을 겁니다. 프롬프트는 서버에 저장해 두고(/chat/promptCRUD)prompt_id로 참조할 수도 있습니다.chat_id+turn— 기존 대화 이어가기. 첫 턴에는 둘 다 생략하면done이벤트가 발급된chat_id를 돌려줍니다. 다음에는turn + 1을 보내거나turn을 생략하면 끝에서 이어집니다. 이전turn을 보내면 대화가 그 지점으로 되감깁니다.
도구 정의¶
{
"name": "drive",
"description": "Drive the robot.",
"properties": [
{ "name": "speed", "type": "number", "description": "m/s", "required": false }
]
}
스트림 이벤트¶
"stream": true면 응답은 Server-Sent Events입니다; 각 data: 라인은 type을
가진 JSON 객체입니다:
type |
담는 내용 |
|---|---|
text |
답변 조각(content) |
tool_call |
call_id, name, arguments |
audio |
data(base64 PCM) — "audio": true일 때만 |
done |
chat_id, turn, full_text, tool_calls, usage, finish_reason |
error |
message, code |
usage는 그 턴에 차감된 크레딧입니다. finish_reason은 프로바이더 중립 값입니다:
stop(정상 — tool call 포함), length(잘림), refusal(거절).
stream 없이 호출하면 같은 필드를 담은 JSON 객체 하나가 돌아옵니다(chat_id,
turn, text, tool_calls, usage, finish_reason, 그리고 요청했다면
{data, duration_ms} 형태의 audio).
도구 루프¶
done.tool_calls가 비어 있지 않으면 각 {call_id, name, arguments}를 직접 실행하고
결과를 다음 요청의 user_message로 보냅니다:
{
"chat_id": "…", "turn": 1,
"prompt": { "model": "...", "instructions": "...", "tools": [ ... ] },
"user_message": {
"contents": [],
"tool_call_outputs": [
{ "call_id": "…", "name": "drive",
"contents": [ { "type": "text", "text": "speed 0.5 m/s, drove 2 s" } ] }
]
},
"stream": true
}
도구 결과의 contents에는 이미지도 넣을 수 있습니다 — 카메라 도구가 찍은 사진을
대화에 넣어 모델이 묘사하게 만드는 방법이 바로 이것입니다.
브라우저에서¶
const res = await fetch("https://api.physicar.ai/chat", {
method: "POST",
headers: { "Content-Type": "application/json",
"Authorization": "Bearer " + physicarSession.token() },
body: JSON.stringify({
chat_id: chatId, // 첫 턴에는 undefined
turn, // 첫 턴에는 0
prompt: { model, instructions, tools },
user_message: { contents: [{ type: "text", text: "안녕!" }] },
stream: true,
}),
});
Realtime¶
wss://api.physicar.ai/realtime — speech-to-speech 음성 대화:
마이크 ──▶ realtime 클라우드 (speech-to-speech LLM) ──▶ 스피커
│ ▲
tool_call tool_result
▼ │
내 도구들 ──▶ 로봇 Web API (/speed, /camera, ...)
GET https://api.physicar.ai/realtime/models가 사용 가능한 realtime 모델 목록을
반환합니다. 브라우저는 token.<토큰> 서브프로토콜로 인증하고, 브라우저가 아닌
클라이언트는 ?token=도 쓸 수 있습니다(쿼리 문자열은 로그에 남을 수 있어
서브프로토콜 방식을 권장합니다).
프로토콜은 JSON 텍스트 프레임만 사용합니다 — 오디오는 base64 pcm16으로 JSON에
실려 오갑니다. 샘플레이트는 모델마다 달라 session.ready의 audio_config로
알려줍니다.
클라이언트 → 서버¶
| 이벤트 | 담는 내용 |
|---|---|
session.start |
prompt(model, instructions, tools, voice), 대화를 이어갈 때 선택적 chat_id |
audio |
data — audio_config.input_rate의 base64 pcm16 |
text |
text — 텍스트 입력, 턴을 트리거 |
image |
data, mime, turn_complete — 명시적 이미지 입력(예: 카메라 촬영) |
tool_result |
call_id, name, output |
session.end |
세션 종료 |
서버 → 클라이언트¶
| 이벤트 | 담는 내용 |
|---|---|
session.ready |
model, audio_config(format, input_rate, output_rate), chat_id |
audio.delta |
data — audio_config.output_rate의 base64 pcm16 |
transcript.delta |
role(user/assistant), text |
tool_call |
call_id, name, arguments — chat과 같은 형태 |
turn.complete |
턴 완료 |
interrupted |
끼어들기(barge-in) — 재생 중인 오디오를 버리세요 |
error |
code, message |
session.end |
code, reason, session_cost(크레딧) |
세션¶
- 세션은 최대 30분, 100 크레딧까지이고, 90초 동안 활동이 없으면
종료됩니다.
session.end이벤트가 사유 코드(CLIENT_END,TIME_LIMIT,CREDIT_LIMIT,IDLE_TIMEOUT등)와 세션이 소비한 크레딧을 알려줍니다. 사용자당 동시 세션은 최대 10개입니다. - 전사(transcript)는
/chat과 대화 저장소를 공유합니다: 세션의chat_id를 이후의session.start에 — 또는/chat요청에 — 넘기면 대화가 이어집니다.
브라우저에서¶
const ws = new WebSocket("wss://api.physicar.ai/realtime",
["token." + physicarSession.token()]);
ws.onopen = () => ws.send(JSON.stringify({
type: "session.start",
prompt: { instructions, tools },
}));
더 알아보기
- 음성 에이전트를 단계별로 → Agent
- Tool call이 동작하는 원리 → 에이전트와 Tool Call
- 도구가 호출할 로봇 엔드포인트 → PhysiCar ROS