PySide6와 Ollama를 사용하여 구축한 81가지 도구를 갖춘 완전 로컬 AI 데스크톱 어시스턴트 (아키텍처 공개)
요약
PySide6와 Ollama를 활용하여 81개의 도구를 갖춘 완전 로컬 AI 데스크톱 어시스턴트 'Sentience'의 아키텍처를 소개합니다. Pydantic 기반의 엄격한 JSON 스키마를 통해 ReAct 루프를 구현하며, 멀티 프로바이더 지원과 자기 수정 도구 기능을 갖추고 있습니다.
핵심 포인트
- Ollama를 통한 완전 로컬 및 오프라인 작동 지원
- Pydantic 모델을 활용한 엄격한 JSON 도구 스키마 적용
- ReAct 루프 기반의 추론-행동-관찰 프로세스 구현
- OpenAI, Anthropic 등 멀티 프로바이더 추상화 구조
- 가드레일이 적용된 자기 수정(Self-modify) 도구 포함
왜 또 다른 채팅 UI가 아닌 데스크톱 앱인가
VS Code는 에디터를 제공합니다. Cursor는 에디터에 채팅 기능을 결합하여 제공합니다. Sentience는 이들과는 다른 형태의 존재입니다. AI가 주요 인터페이스(Surface)가 되는 네이티브 데스크톱 창이며, 81개의 일급 도구(First-class tools)가 ReAct 루프에 연결되어 있고, 텔레메트리(Telemetry), 확장 프로그램 마켓플레이스, 월간 결제 없이 Ollama를 통해 오프라인으로 작동하는 전체 시스템입니다.
약 6,200줄의 Python 코드로 구성되어 있습니다. 라이선스는 MIT입니다. 콜드 스타트(Cold start) 시 1초 이내에 창이 열립니다.
구조: 창, 에이전트, 그리고 도구 레지스트리 (Tool Registry)
[Sentience window (PySide6)]
- 사이드바 (파일 트리)
- 에디터 (QScintilla, 구문 강조)
...
메인 창에는 세 가지 위젯이 있습니다: 파일 트리, 코드 에디터, 그리고 채팅 패널입니다. 채팅 패널은 에이전트(Agent)로 들어가는 진입점입니다. 그 아래에는 셸(Shell) 접속이 가능한 임베디드 터미널이 있습니다. 에이전트는 셸 범위의 도구(Shell-scoped tools)를 실행할 때 내부적으로 동일한 터미널을 사용합니다.
엄격한 도구 스키마를 적용한 ReAct 루프
에이전트의 핵심은 Reason(추론) -> Act(행동) -> Observe(관찰) 루프이지만, 한 가지 차이점이 있습니다. 모든 도구는 모델이 채워야 하는 JSON 스키마를 가진 Pydantic 모델입니다. 자유 형식의 문자열 파싱(Free-form string parsing)은 없습니다.
class ToolRegistry:
def __init__(self):
self.tools: Dict[str, Tool] = {}
...
모델은 단일 시스템 메시지를 통해 81개의 모든 도구에 대해 안내받습니다. 매 턴마다 모델은 {"thought": "...", "tool": "read_file", "args": {...}} 또는 {"thought": "...", "final_answer": "..."}와 같은 JSON 객체를 반환해야 합니다. 우리는 pydantic.ValidationError를 포착하여 다시 프롬프트를 요청(Re-prompt)하는 방식으로 파싱하므로, 잘못된 형식의 도구 호출이 루프를 중단시키지 않습니다.
재작성 없는 멀티 프로바이더 (Multi-provider)
동일한 루프가 Ollama, OpenAI, Anthropic 또는 Groq를 대상으로 작동합니다. 추상화는 단 하나의 메서드로 이루어집니다:
class Provider(Protocol):
def chat(self, messages: list, tools: list[dict]) -> ModelResponse: ...
...
동일한 Provider 프로토콜을 사용하되, 전송 형식(Wire format)만 다릅니다. 에이전트는 반대편에 어떤 모델이 있는지 상관하지 않습니다. JSON 도구 스키마가 계약(Contract)이기 때문입니다.
정직함을 유지하는 자기 수정 도구 (The self-modify tool, kept honest)
edit_own_source라고 불리는 도구가 실제로 존재합니다. 이는 위험하기 때문에 게이트(gated)가 설정되어 있습니다:
@registry.register("edit_own_source", EditOwnSourceTool)
def edit_own_source(file: str, old: str, new: str, confirm: bool) -> str:
if not confirm:
...
두 가지 가드레일(guard rails)이 있습니다. 첫 번째는 모델이 confirm=True로 바꾸기 전에 사용자에게 반드시 차이점(diff)을 보여주도록 강제합니다. 두 번째는 src/ 디렉토리 외부의 모든 경로를 거부합니다. 모든 쓰기 작업 전에는 백업이 작성됩니다. 이것은 조용히 처리될 것이 아니라, 명시적으로 드러나야 하는 종류의 작업입니다.
에이전트 내부에 터미널이 존재하는 이유
에이전트가 셸 명령(shell command)을 실행할 때, 숨겨진 서브프로세스(subprocess)를 생성하지 않습니다. 대신 명령을 내장된 터미널 위젯(terminal widget)으로 밀어 넣습니다. 사용자는 무엇이 실행되고 있는지 정확히 볼 수 있고, Ctrl-C로 중단할 수 있으며, 이전 기록을 스크롤하여 확인할 수 있습니다. 에이전트는 이 출력을 다른 도구와 마찬가지로 읽어 들입니다.
이 단 하나의 설계 선택이 "에이전트가 백그라운드에서 rm -rf를 실행했다"와 같은 범주의 버그들을 완전히 없애버렸습니다. 가시성(Visibility)이 우선이고, 자율성(Autonomy)은 그다음입니다.
메모리: SQLite + 단순하지만 빠른 벡터
지속성 메모리(Persistent memory)는 임베딩(embeddings)을 위한 vec0 가상 테이블을 포함하는 SQLite 테이블입니다. Pinecone도, 서버도 필요 없습니다. 임베딩은 Ollama를 통해 nomic-embed-text로 로컬에서 계산됩니다. remember 도구는 행(row)을 작성하고, recall 도구는 코사인 유사도 기반의 top-k를 수행하여 결합된 텍스트를 반환합니다.
스택 (Stack)
| 계층 (Layer) | 기술 (Tech) |
|---|---|
| Window | PySide6 (Qt 6.5+) |
| ... |
다음 단계
채팅 패널을 위한 적절한 스트리밍 모드(현재는 덩어리째로 출력됨), edit_own_source를 앱 내에서 검토할 수 있는 시각적 diff 도구, 그리고 "스킬(skills)" 레지스트리가 예정되어 있습니다. 스킬은 3~5개의 도구를 하나의 명명된 기능으로 묶은 작은 JSON 매니페스트(manifests)로, 모델이 선택하여 사용할 수 있습니다. Cursor와 Claude에 스킬이 있듯이, Sentience에도 스킬이 있어야 합니다.
Repo: github.com/AmSach/sentience
실행 방법: pip install pyside6 openai anthropic aiohttp requests && python sentience.py
에이전트가 가져야 할 도구를 발견하신다면, Pydantic 스키마와 한 줄 설명을 포함하여 이슈(Issue)를 생성해 주세요. PR(Pull Request)은 24시간 이내에 병합합니다.
#Python #PySide6 #LocalAI #Ollama #OpenSource #BuildInPublic
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기