Novita AI Sandbox를 사용하여 Chrome 플러그인 구축하는 방법

Novita AI Sandbox를 사용하여 Chrome 플러그인 구축하는 방법

문서 사이트나 코딩 튜토리얼을 탐색할 때 혼자가 아니라고 상상해보세요. 모든 것을 혼자 탐색하는 대신, 페이지마다 따라오는 AI 어시스턴트가 있습니다. 특정 웹페이지에 묶여 있지 않습니다. 항상 바로 옆에서 도움을 줄 준비가 되어 있습니다. 온라인에서 만난 코드를 안전하게 실행하고, 설명을 제공하며, 필요할 때마다 통찰력을 제공할 수 있습니다.

그렇다면 이런 경험을 어떻게 구현할 수 있을까요? 브라우저 확장 프로그램을 구축하면 됩니다. 이 확장 프로그램은 사용자가 대화할 수 있는 AI 에이전트를 포함하며, 에이전트는 코드를 실행하고 다른 작업을 안전하게 수행할 수 있는 보안 샌드박스 머신에 접근할 수 있습니다.

이 시스템을 만들기 위해 Chrome 확장 프로그램을 구축하고, 도구 사용이 가능한 Novita 모델을 사용하며, Novita Sandbox를 에이전트의 보안 런타임으로 통합할 것입니다. 이 글에서는 전체 구축 과정을 단계별로 살펴보겠습니다.

코드 어시스턴트 스크린샷

이 튜토리얼을 마치면 다음 내용을 배울 수 있습니다:

  • AI 에이전트와 통합되는 Chrome 확장 프로그램을 구축하는 방법
  • Novita의 Agentic LLM을 활용하는 방법
  • 브라우저 에이전트를 위한 안전한 환경으로 Novita Sandbox를 설정하는 방법
  • 확장 프로그램이 에이전트와 실시간으로 통신하는 방법

샌드박스: 필요한 유일한 도구

우리가 만들 Chrome 확장 프로그램은 사용자를 돕기 위해 AI 에이전트에 의존합니다. 이 확장 프로그램은 코딩 어시스턴트 역할을 하므로, 에이전트는 코드 실행, 파일 생성, 출력 검사 등 개발자가 일반적으로 수행하는 모든 작업을 할 수 있어야 합니다. 이를 위해 많은 도구 목록이 필요할 것이라고 생각할 수 있지만, 실제로는 단 하나의 도구만 있으면 됩니다: 샌드박스입니다.

샌드박스는 에이전트에게 Linux 환경에 대한 액세스 권한을 부여하여, 명령어 실행, 파일 생성 및 수정, 터미널에서 수행하는 모든 작업을 가능하게 합니다. 이 프로젝트에서는 Novita Sandbox를 사용하겠습니다.

설정하려면 먼저 Novita Sandbox 패키지를 설치합니다:

pip install novita-sandbox

그런 다음 NOVITA_API_KEY 환경 변수를 API 키로 설정합니다. 완료되면 다음과 같이 샌드박스를 생성하고 사용할 수 있습니다:

from novita_sandbox.code_interpreter import Sandbox

sandbox = Sandbox.create()

result = sandbox.commands.run('ls -l')
print(result)

sandbox.kill()

이 코드 조각은 샌드박스를 생성하고, ls -l 명령어를 실행한 후 출력을 출력하고, 샌드박스를 종료합니다. 이 간단한 워크플로우가 브라우저 어시스턴트가 샌드박스를 활용하여 사용자를 돕는 방법의 기초입니다.

이제 이 원칙을 전체 확장 프로그램에 적용해 보겠습니다.

브라우저 어시스턴트 에이전트의 아키텍처

이 프로젝트의 아키텍처는 클라이언트-서버 모델을 따릅니다. Chrome 확장 프로그램은 클라이언트 역할을 하며, 전용 백엔드 서버는 AI 에이전트와 샌드박스 환경을 모두 호스팅합니다.

확장 프로그램은 WebSocket 연결을 통해 서버와 통신합니다. 이를 통해 사용자의 요청과 에이전트의 응답이 지연 없이 즉시 흐르는 양방향 실시간 메시징이 가능합니다. 서버는 다시 모델 엔드포인트와 샌드박스 서비스를 포함하는 Novita의 API와 통신합니다.

함께, 확장 프로그램과 백엔드는 사용자의 브라우징 경험 내에서 직접 코드를 안전하게 실행하고, 정보를 빠르게 처리하며, 유용한 설명을 제공할 수 있는 지능형 브라우저 어시스턴트를 형성합니다.

확장 프로그램 구축

이제 전체 아키텍처를 이해했으므로 확장 프로그램 자체를 구현해 보겠습니다. 플러그인 서버부터 시작합니다.

확장 프로그램 서버

확장 프로그램 서버는 단일 엔드포인트 /ws를 가진 간단한 WebSocket 서비스입니다. 이 엔드포인트는 사용자로부터 메시지를 수신하고 LLM의 응답을 실시간으로 반환합니다. 또한 에이전트가 코드를 실행하거나 작업을 수행해야 할 때마다 샌드박스를 호출하여 도구 호출을 처리합니다.

종속성

서버는 세 가지 핵심 라이브러리에 의존합니다:

  • FastAPI: WebSocket 구현을 제공하는 HTTP 프레임워크
  • OpenAI: Novita 모델과 통신하는 데 사용되는 SDK
  • Novita Sandbox: 코드가 안전하게 실행되는 보안 환경

다음 명령어로 설치합니다:

pip install novita-sandbox "fastapi[standard]" openai

Novita API 키를 환경 변수로 설정합니다:

export NOVITA_API_KEY = sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

서버 코드 작성

필요한 모듈을 임포트하는 것으로 시작합니다:

import os
import json
import uvicorn
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.middleware.cors import CORSMiddleware
from novita_sandbox.code_interpreter import Sandbox
from openai import OpenAI

LLM 클라이언트 초기화

다음으로, Novita의 API를 가리키는 OpenAI 클라이언트를 만듭니다. 이 예제에서는 llama-3.3-70b-instruct 모델을 사용하지만, 도구 호출을 지원하는 모든 Novita 모델이 작동합니다.

client = OpenAI(
   base_url="https://api.novita.ai/openai",
   api_key=os.environ["NOVITA_API_KEY"],
)

model = "meta-llama/llama-3.3-70b-instruct"

도구 스키마 정의

에이전트는 각각 샌드박스와 상호 작용하는 네 가지 도구를 사용합니다:

  • read_file: 파일 내용 읽기
  • write_file: 단일 파일 생성 및 쓰기
  • write_files: 여러 파일 생성 및 쓰기
  • run_commands: 샌드박스 내에서 셸 명령어 실행

다음은 전체 도구 스키마입니다:

tools = [
   {
       "type": "function",
       "function": {
           "name": "read_file",
           "description": "샌드박스 내 파일 내용 읽기",
           "parameters": {
               "type": "object",
               "properties": {"path": {"type": "string"}},
               "required": ["path"],
           },
       },
   },
   {
       "type": "function",
       "function": {
           "name": "write_file",
           "description": "샌드박스 내 단일 파일 쓰기",
           "parameters": {
               "type": "object",
               "properties": {
                   "path": {"type": "string"},
                   "data": {"type": "string"},
               },
               "required": ["path", "data"],
           },
       },
   },
   {
       "type": "function",
       "function": {
           "name": "write_files",
           "description": "샌드박스 내 여러 파일 쓰기",
           "parameters": {
               "type": "object",
               "properties": {
                   "files": {
                       "type": "array",
                       "items": {
                           "type": "object",
                           "properties": {
                               "path": {"type": "string"},
                               "data": {"type": "string"},
                           },
                           "required": ["path", "data"],
                       },
                   }
               },
               "required": ["files"],
           },
       },
   },
   {
       "type": "function",
       "function": {
           "name": "run_commands",
           "description": "샌드박스 작업 디렉토리에서 셸 명령어 실행",
           "parameters": {
               "type": "object",
               "properties": {
                   "command": {"type": "string"},
               },
               "required": ["command"],
           },
       },
   },
]

HTTP 서버 구성

FastAPI를 설정하고 CORS를 활성화하여 Chrome 확장 프로그램이 서버에 요청할 수 있도록 합니다.

app = FastAPI()

app.add_middleware(
   CORSMiddleware,
   allow_origins=["*"],
   allow_methods=["*"],
   allow_headers=["*"],
)

도구 구현

각 도구를 구현하고 에이전트의 도구 호출을 샌드박스로 라우팅하는 핸들러 함수를 정의합니다:

def make_tool_handlers(sandbox):
   def read_file(path: str):
       print(f"[LOG] read_file 호출됨, 경로: {path}")
       try:
           content = sandbox.files.read(path)
           print(f"[LOG] read_file 결과: {content}")
           return content
       except Exception as e:
           return f"파일 읽기 오류: {e}"

   def write_file(path: str, data: str):
       print(f"[LOG] write_file 호출됨, 경로: {path}")
       try:
           sandbox.files.write(path, data)
           return f"파일이 {path}에 성공적으로 생성되었습니다"
       except Exception as e:
           return f"파일 쓰기 오류: {e}"

   def write_files(files: list):
       print(f"[LOG] write_files 호출됨, 파일 {len(files)}개")
       try:
           sandbox.files.write_files(files)
           return f"{len(files)}개 파일이 성공적으로 생성되었습니다"
       except Exception as e:
           return f"여러 파일 쓰기 오류: {e}"

   def run_commands(command: str):
       print(f"[LOG] run_commands 호출됨, 명령어: {command}")
       try:
           result = sandbox.commands.run(command)
           return result.stdout
       except Exception as e:
           return f"명령어 실행 오류: {e}"

   return {
       "read_file": read_file,
       "write_file": write_file,
       "write_files": write_files,
       "run_commands": run_commands,
   }

WebSocket 엔드포인트 생성

이제 WebSocket 엔드포인트를 구현해 보겠습니다. 사용자가 엔드포인트에 연결하면 새 샌드박스 인스턴스가 생성됩니다.

사용자와 에이전트 간의 모든 통신은 이 연결을 통해 흐릅니다. 사용자가 에이전트에게 도구를 사용하도록 요청하면 에이전트는 핸들러 함수를 통해 적절한 도구를 선택하고 실행합니다.

연결이 종료되면 샌드박스가 종료됩니다.

@app.websocket("/ws")
async def websocket_endpoint(ws: WebSocket):
   await ws.accept()
   print("\
[WS] 클라이언트 연결됨")

   # 연결당 샌드박스 생성
   sandbox = Sandbox.create(timeout=1200)
   print("[WS] 샌드박스 생성됨")

   tools_exec = make_tool_handlers(sandbox)
   messages = []  # 이 웹소켓 내에서 유지

   try:
       while True:
           data = await ws.receive_text()
           print(f"[WS] 메시지 수신: {data}")

           # 사용자 메시지 추가
           messages.append({"role": "user", "content": data})

           # LLM 호출
           response = client.chat.completions.create(
               model=model,
               messages=messages,
               tools=tools,
           )

           assistant_msg = response.choices[0].message
           messages.append(assistant_msg)

           # LLM이 도구를 호출하려는 경우
           if assistant_msg.tool_calls:
               print(f"[WS] 어시스턴트가 {len(assistant_msg.tool_calls)}개의 도구 호출을 요청했습니다.")

               results = []

               for tool_call in assistant_msg.tool_calls:
                   fn_name = tool_call.function.name
                   fn_args = json.loads(tool_call.function.arguments)

                   print(f"[WS] 도구 호출: {fn_name}, 인자={fn_args}")

                   if fn_name in tools_exec:
                       result = tools_exec[fn_name](**fn_args)
                   else:
                       result = f"오류: 알 수 없는 도구 {fn_name}"

                   results.append(result)

                   messages.append({
                       "tool_call_id": tool_call.id,
                       "role": "tool",
                       "content": str(result),
                   })

               # 후속 모델 호출
               follow_up = client.chat.completions.create(
                   model=model,
                   messages=messages,
               )
               final_answer = follow_up.choices[0].message
               messages.append(final_answer)

               await ws.send_json({
                   "reply": final_answer.content,
                   "tool_output": results,
               })

           else:
               # 단순 모델 텍스트 출력
               await ws.send_json({"reply": assistant_msg.content})

   except WebSocketDisconnect:
       print("[WS] 클라이언트 연결 해제됨")

   finally:
       sandbox.kill()
       print("[WS] 샌드박스 종료됨")

서버 실행

마지막으로 Uvicorn을 사용하여 서비스를 시작합니다:

if __name__ == "__main__":
   uvicorn.run(app, host="0.0.0.0", port=8000)

이것으로 서버 구성 요소가 완료되었습니다.

확장 프로그램

확장 프로그램은 사용자가 상호 작용하는 인터페이스입니다. 함께 작동하여 모든 웹페이지에서 실행되는 작은 파일 모음으로 구성됩니다. 활성화되면 사용자는 현재 탐색 중인 페이지에서 직접 확장 프로그램 서버와 실시간으로 통신할 수 있습니다.

확장 프로그램에는 다음 파일이 포함됩니다:

  • manifest.json: 확장 프로그램의 구성 및 권한 정의
  • background.js: 서비스 워커 로직과 컨텍스트 메뉴 작업 처리
  • content.js: 페이지 내 상호 작용 관리 및 어시스턴트 대화 상자 표시
  • styles.css: 페이지 내 어시스턴트 창 스타일 지정

각 파일은 고유한 역할을 가지며, 함께 완전하고 기능적인 확장 프로그램을 형성합니다.

확장 프로그램 작동 방식

파일을 구현하기 전에 사용자 관점에서 확장 프로그램이 어떻게 작동하는지 간단히 살펴보겠습니다:

에이전트 샌드박스 플러그인 스크린샷

  1. 사용자가 웹페이지에서 마우스 오른쪽 버튼을 클릭하면 컨텍스트 메뉴가 나타납니다.
  2. 메뉴에서 **“Agent Sandbox”**를 선택합니다.
  3. 확장 프로그램이 페이지에 어시스턴트 대화 상자를 엽니다.
  4. 사용자가 Connect를 클릭하여 확장 프로그램 서버와 연결을 설정합니다.
  5. 연결이 설정되면 사용자가 메시지 상자에 직접 입력할 수 있습니다.
  6. 메시지를 입력한 후 Send를 클릭하여 서버로 전송합니다.
  7. 에이전트에 페이지 컨텍스트를 제공하려면 Extract를 클릭하여 웹페이지의 모든 표시 콘텍스트를 캡처할 수 있습니다.
  8. 추가 컨텍스트를 수동으로 추가한 후 다시 Send를 클릭할 수도 있습니다.

코드 어시스턴트 스크린샷

이제 워크플로우를 이해했으므로 확장 프로그램 파일을 구현해 보겠습니다.

manifest.json

먼저 manifest.json 파일을 만듭니다. 이 파일은 확장 프로그램의 권한, 백그라운드 로직 및 콘텐츠 스크립트를 구성합니다.

{
 "manifest_version": 3,
 "name": "Agent Sandbox",
 "version": "1.0",
 "description": "WebSocket을 통해 Novita AI 샌드박스 에이전트와 채팅합니다.",
 "permissions": [
   "contextMenus",
   "activeTab",
   "scripting"
 ],
 "host_permissions": [
   "ws://localhost:8000/*",
   "http://localhost:8000/*",
   "https://localhost:8000/*"
 ],
 "background": {
   "service_worker": "background.js"
 },
 "action": {
   "default_icon": "icon.png"
 },
 "content_scripts": [
   {
     "matches": ["<all_urls>"],
     "js": ["content.js"],
     "css": ["styles.css"]
   }
 ]
}

이 파일은 Chrome에 어떤 스크립트를 로드할지, 필요한 권한, 서비스 워커 역할을 할 파일을 알려줍니다.

background.js

백그라운드 스크립트는 백그라운드에서 실행되는 서비스 워커입니다. 이 스크립트는 확장 프로그램을 Chrome 컨텍스트 메뉴에 추가하고 사용자 상호 작용을 수신하는 역할을 합니다. 사용자가 메뉴 옵션을 선택하면 백그라운드 스크립트가 콘텐츠 스크립트에 메시지를 보내어 확장 프로그램 대화 상자를 활성화합니다.

chrome.runtime.onInstalled.addListener(() => {
 chrome.contextMenus.create({
   id: "ask-assistant",
   title: "Agent Sandbox",
   contexts: ["all"]
 });
});

chrome.contextMenus.onClicked.addListener((info, tab) => {
 chrome.scripting.executeScript(
   {
     target: { tabId: tab.id },
     files: ["content.js"]
   },
   () => {
     chrome.tabs.sendMessage(tab.id, {
       type: "OPEN_PANEL"
     });
   }
 );
});

content.js

콘텐츠 스크립트는 웹페이지 내에 확장 프로그램 대화 상자를 표시하는 역할을 합니다. 백그라운드 스크립트로부터 메시지를 수신하면 대화 상자를 엽니다. 이 스크립트는 일반 JavaScript입니다. 표준 DOM 연산을 통해 UI를 관리하고 WebSocket API를 사용하여 확장 프로그램 서버와 통신합니다.

let socket = null;

chrome.runtime.onMessage.addListener((msg) => {
 if (msg.type === "OPEN_PANEL") {
   openPanel();
 }
});

// -------------------------------------------------
// 웹페이지 텍스트 추출기
// -------------------------------------------------
function extractWebpageContent() {
 const cloned = document.cloneNode(true);
 cloned.querySelectorAll("script, style, iframe, noscript").forEach(e => e.remove());

 let main =
   cloned.querySelector("article") ||
   cloned.querySelector("main") ||
   cloned.querySelector("#content") ||
   cloned.body;

 const text = Array.from(main.querySelectorAll("h1, h2, h3, p"))
   .map(el => el.innerText.trim())
   .filter(Boolean)
   .join("\
\
");

 return text;
}

// -------------------------------------------------
// 패널 UI
// -------------------------------------------------
function openPanel() {
 const old = document.getElementById("assistant-box");
 if (old) old.remove();

 const box = document.createElement("div");
 box.id = "assistant-box";

 box.innerHTML = `
   <div id="assistant-container">
     <div id="assistant-header">
       <h3>코드 어시스턴트</h3>
       <button id="assistant-close">×</button>
     </div>

     <textarea id="assistant-input" placeholder="에이전트에게 메시지 보내기..."></textarea>

     <div class="btn-row">
       <button id="connect-btn">연결</button>
       <button id="disconnect-btn">연결 해제</button>
       <button id="send-btn">보내기</button>
       <button id="extract-btn">추출</button>
     </div>

     <div id="assistant-result">연결되지 않음.</div>
   </div>
 `;

 document.body.appendChild(box);

 document.getElementById("assistant-close").onclick = () => box.remove();

 const resultBox = document.getElementById("assistant-result");
 const inputBox = document.getElementById("assistant-input");

 // -------------------------------------------------
 // 텍스트 선택 리스너
 // -------------------------------------------------
 document.addEventListener("mouseup", () => {
   const selection = window.getSelection().toString().trim();
   if (!selection) return;

   // 선택한 텍스트를 메시지 상자에 추가
   inputBox.value += (inputBox.value ? "\
\
" : "") + selection;

   // 텍스트 영역을 아래로 스크롤
   inputBox.scrollTop = inputBox.scrollHeight;
 });

 // -------------------------------------------------
 // 연결
 // -------------------------------------------------
 document.getElementById("connect-btn").onclick = () => {
   if (socket && socket.readyState === WebSocket.OPEN) {
     resultBox.innerText = "이미 연결됨.";
     return;
   }

   socket = new WebSocket("ws://localhost:8000/ws");

   socket.onopen = () => {
     resultBox.innerText = "WebSocket에 연결됨.";
   };

   socket.onmessage = (event) => {
     const data = JSON.parse(event.data);
     resultBox.innerText += "\
\
어시스턴트:\
" + data.reply;
     resultBox.scrollTop = resultBox.scrollHeight;
   };

   socket.onerror = () => {
     resultBox.innerText = "WebSocket 오류.";
   };

   socket.onclose = () => {
     resultBox.innerText = "연결 해제됨.";
   };
 };

 // -------------------------------------------------
 // 연결 해제
 // -------------------------------------------------
 document.getElementById("disconnect-btn").onclick = () => {
   if (socket) socket.close();
 };

 // -------------------------------------------------
 // 메시지 보내기
 // -------------------------------------------------
 document.getElementById("send-btn").onclick = () => {
   const context = inputBox.value;

   if (!socket || socket.readyState !== WebSocket.OPEN) {
     resultBox.innerText = "연결되지 않음.";
     return;
   }

   socket.send(JSON.stringify({ message: context }));

   inputBox.value = "";

   resultBox.innerText += "\
\
나:\
" + context;
 };

 // -------------------------------------------------
 // 페이지 추출 → 메시지 상자에 추가
 // -------------------------------------------------
 document.getElementById("extract-btn").onclick = () => {
   const extracted = extractWebpageContent();

   if (!extracted || extracted.length < 10) {
     resultBox.innerText = "유용한 콘텐츠를 추출할 수 없습니다.";
     return;
   }

   // 추출된 텍스트를 입력 상자에 추가 (자동으로 보내지 않음)
   inputBox.value += (inputBox.value ? "\
\
" : "") + extracted;

   // 텍스트 영역 스크롤
   inputBox.scrollTop = inputBox.scrollHeight;

   resultBox.innerText = "📄 추출된 콘텐츠가 메시지 상자에 추가되었습니다.";
 };
}

styles.css

그런 다음 styles.css 파일을 사용하여 인터페이스 스타일을 지정하고 확장 프로그램이 페이지에 표시되는 방식을 제어합니다.

#assistant-box {
  position: fixed;
  top: 10%;
  right: 10%;
  width: 350px;
  background: #000;
  border: 1px solid #00ff7f;
  border-radius: 8px;
  box-shadow: 0 0 10px rgba(0,255,127,0.5);
  z-index: 999999;
  font-family: monospace;
  color: #00ff7f;
}

#assistant-container {
  padding: 12px;
}

#assistant-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

#assistant-close {
  background: transparent;
  border: none;
  font-size: 20px;
  cursor: pointer;
  padding: 0 5px;
  font-weight: bold;
  color: #00ff7f;
}

#assistant-container textarea {
  width: 100%;
  height: 60px;
  margin-top: 8px;
  background: #0d0d0d;
  color: #00ff7f;
  border: 1px solid #00ff7f;
  border-radius: 4px;
  padding: 6px;
  resize: vertical;
  outline: none;
}

#send-btn {
  width: 100%;
  margin-top: 10px;
  background: #00ff7f;
  border: none;
  padding: 10px;
  color: #000;
  font-weight: bold;
  cursor: pointer;
  border-radius: 4px;
  transition: 0.2s;
}

#send-btn:hover {
  background: #00e66a;
}

#assistant-box pre {
  background: #0a0a0a;
  padding: 8px;
  border-radius: 4px;
  max-height: 120px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  margin-top: 5px;
}

#assistant-result {
  margin-top: 12px;
  background: #0d0d0d;
  padding: 8px;
  border-radius: 5px;
  white-space: pre-wrap;
  max-height: 150px;
  overflow: auto;
}

Chrome 확장 프로그램 설정 방법

이제 모든 필수 파일이 준비되었으므로 다음 단계는 확장 프로그램을 Chrome에 로드하는 것입니다. 다음 단계를 따르세요:

  1. 확장 프로그램 서버 코드를 Python 파일로 저장합니다.
  2. 모든 종속성을 설치하고 서버에 필요한 환경 변수를 설정합니다.
  3. 컴퓨터에 code-assistant-extension이라는 새 폴더를 만들어 확장 프로그램 클라이언트를 저장합니다.
  4. 폴더에 다음 파일을 추가합니다:
    • manifest.json
    • background.js
    • content.js
    • styles.css
  5. Chrome을 열고 chrome://extensions/로 이동합니다.
  6. 오른쪽 상단의 개발자 모드를 활성화합니다.
  7. 압축 해제된 확장 프로그램 로드를 클릭합니다.
  8. 확장 프로그램 파일이 포함된 폴더를 선택합니다.
  9. 확장 프로그램이 도구 모음에 나타납니다.
  10. 웹페이지에서 텍스트를 마우스 오른쪽 버튼으로 클릭하면 컨텍스트 메뉴에 Agent Sandbox가 표시됩니다.

이제 확장 프로그램을 사용할 준비가 되었습니다.

리포지토리 - AI Code Assistant Browser Extension

결론

이 글에서는 Novita Sandbox 기반 백엔드에 연결되는 Chrome 확장 프로그램을 구축하여 AI 에이전트가 사용자가 탐색하는 동안 안전하게 코드를 실행하고 도움을 줄 수 있도록 했습니다. 이 패턴은 코딩 도움을 넘어 대화형 학습 도구, 디버깅 어시스턴트, 문서 개선 도구 등에 활용될 수 있습니다.

이 아키텍처는 브라우저에 구애받지 않으므로, 동일한 접근 방식을 최소한의 변경으로 모든 최신 브라우저에 적용할 수 있습니다. 여기서부터 어시스턴트의 기능을 확장하거나 UI를 개선하거나 새로운 샌드박스 도구를 추가할 수 있습니다. 이 기반은 강력하고 지능적인 브라우저 동반자를 만드는 문을 엽니다.

Novita AI는 개발자에게 사용하기 쉬운 API와 저렴하고 안정적인 GPU 인프라를 제공하여 AI 애플리케이션을 구축하고 확장할 수 있도록 지원하는 선도적인 AI 클라우드 플랫폼입니다.