서버 구성
이 문서는 서버의 시작과 구성 가이드로, 세 가지 질문에 답합니다:
- 어떻게 시작하는가(로컬 / 클라우드 / 데스크톱 세 형태)
- 공인 네트워크가 필요한가(결론: 본체는 불필요. REST 인터페이스가 필요할 때만)
- 클라우드 연결을 어떻게 구성하는가(Rust 게이트웨이에 연결해 이후 원격 관리 가능)
사전 준비
| 항목 | 요구 사항 |
|---|---|
| Node.js | ≥ 18.17(20+ 권장) |
| 모델 API Key | OpenAI 호환 엔드포인트(DeepSeek / OpenAI / Ollama / 사용자 지정) |
| (선택) 클라우드 계정 | 자체 Rust 게이트웨이 + Hub, 또는 관리형 서비스 |
| (선택) QQ 오픈 플랫폼 봇 | q.qq.com에서 등록 |
bash
# 코드 받고 의존성 설치
pnpm install
# 대화형 구성(권장): LLM은 필수, QQ/메일은 선택이며 건너뛸 수 있음
bash scripts/configure-server.sh
# 또는 수동: 템플릿을 복사해 작성
# cp .env.example .env환경 변수 개요
프로젝트 루트의 .env에 작성(또는 시작 전 export):
| 변수 | 설명 | 기본 |
|---|---|---|
DATA_DIR | 데이터/테넌트 루트 | ./.zqagent_data |
PORT | REST 대기 포트 | 8787 |
HOST | 대기 주소(공인 배포 시 0.0.0.0) | 127.0.0.1 |
WORKSPACE | Agent 작업 디렉터리 | 현재 디렉터리 |
APPROVAL_TIMEOUT_SECONDS | 승인/질문 보류 타임아웃 | 900 |
MAX_ITERATIONS | 턴당 최대 반복 | 800 |
OPENAI_API_KEY / OPENAI_BASE_URL / OPENAI_MODEL / OPENAI_REASONING_EFFORT | 모델 구성(OPENAI_MODEL 미설정 시 모델 미지정이며 콘솔에서 선택 필요) | — |
TENANT_ID / TENANT_API_KEY / MODEL | 첫 시작 시 자동 생성되는 데모 테넌트 | demo / 첫 시작 시 무작위 생성 / 모델 기본값 |
SECRET_KEY | 알림 자격 증명 등을 암호화하는 마스터 키 | 자동 생성 |
FLEET_TOKEN / FLEET_SECRET / FLEET_GATEWAY_URL | 클라우드 연결(보통 등록 코드 연결로 fleet.json에 자동 기록) | — |
QQ_BOT_APP_ID / QQ_BOT_APP_SECRET / QQ_BOT_SANDBOX | QQ 봇(설정하면 활성화) | — |
CONTEXT_WINDOW / COMPACTION_THRESHOLD_PCT / COMPACTION_CAP_TOKENS | 긴 대화 압축 매개변수 | 자동 |
SECRET_KEY는 한 번 설정하면 장기 고정을 권장합니다. 테넌트 서명 키와 알림 채널 자격 증명 암호화에 쓰이므로 변경하면 기존 테넌트의 키와 자격 증명이 무효가 됩니다.
세 가지 배포 형태
로컬 / 내부망 실행(가장 권장, 공인 네트워크 불필요)
bash
# 최소 실행(시작과 대화가 되는지 먼저 확인)
pnpm run server
# 로그:
# zqagent server 监听 http://127.0.0.1:8787
# 已创建演示租户: id=demo apiKey=zk_xxxx(随机生成) model=...
# 완전한 원격 제어(.env 구성 후):
# FLEET_GATEWAY_URL=wss://gw.example.com/ws
# QQ_BOT_APP_ID=... QQ_BOT_APP_SECRET=...
# OPENAI_API_KEY=sk-... OPENAI_MODEL=...
pnpm run server시작 후 서비스 사용 가능 여부 확인:
bash
# 데모 테넌트 apiKey는 첫 시작 시 무작위 생성되어 시작 로그에 출력됨. 꺼내서 TOKEN에 저장
TOKEN=<시작 로그의 apiKey> # zk_1f0c... 형식
curl -s http://localhost:8787/healthz # => {"status":"ok"}
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8787/api/v1/sessions클라우드 배포(REST 공인 접근이 필요할 때)
클라우드 서버를 빌려 HOST=0.0.0.0으로 포트를 노출합니다.
bash
# pm2로 상주시키는 것을 권장
npm install -g pm2
HOST=0.0.0.0 pm2 start "pnpm run server" --name zqagent-server
# 보안 권장: 필요한 사람에게만 개방. REST를 혼자만 쓴다면 공인에 직접 노출하지 말고
# 내부망 관통을 쓰거나 클라우드 서버 방화벽으로 출발지 IP를 제한하세요.데스크톱 앱(macOS / Windows)
데스크톱 앱은 로컬에서 서버를 자동으로 띄우므로 수동 시작이 필요 없습니다. 데스크톱 앱을 참고하세요.
REST 공인 접근이 필요할 때의 세 가지 방안
| 방안 | 방법 | 적합 |
|---|---|---|
| A. 내부망 관통(개인 권장) | Cloudflare Tunnel(무료) 또는 frp/ngrok으로 로컬 포트를 공인 HTTPS에 매핑 | 집/내부망 머신. 서버를 빌리고 싶지 않음 |
| B. 클라우드 서버 | VPS에 직접 배포하고 HOST=0.0.0.0 | 기존 서버가 있거나 장기 안정성이 필요 |
| C. REST 미사용 | 게이트웨이 전 구간(자격 증명은 제어 플레인이 발급)을 쓰고 REST는 내 PC에서만 | 포트를 전혀 노출하고 싶지 않음 |
클라우드 연결(Rust 게이트웨이)
연결 후 어떤 기기에서든 이 노드를 원격 관리할 수 있습니다. 노드는 게이트웨이로 아웃바운드 연결(wss://, 보통 443)만 하면 되며, 공인 인바운드도 포트 개방도 필요 없고 MQTT broker도 필요 없습니다.
방법 1: 등록 코드로 연결(권장)
사용자 센터(Hub)에서 일회용 등록 코드를 만들고 노드 머신에서 실행합니다:
bash
zqagent link --hub https://hub.example.com --code <등록 코드>- 이 기기에서 서비스가 실행 중 → 핫 연결(재시작 불필요)
- 아니면 자격 증명을
{DATA_DIR}/fleet.json에 쓰고 다음zqagent serve에서 자동 연결
방법 2: 수동 구성
bash
# .env
FLEET_TOKEN=<고엔트로피 랜덤> # 네임스페이스 식별자
FLEET_SECRET=<고엔트로피 랜덤> # 서명 키(게이트웨이 측과 일치해야 함)
FLEET_GATEWAY_URL=wss://gw.example.com/ws
SECRET_KEY=<64자리 hex> # 명시적 설정을 강력 권장시작 후 확인:
bash
curl -s -H "Authorization: Bearer $ADMIN_API_KEY" \
http://localhost:8787/api/v1/fleet/pairing
# => {"data":{"gatewayUrl":"wss://gw.example.com/ws","fleetToken":"...","fleetSecret":"...","nodeId":"..."}}시작 로그에 「云端接入: wss://…(节点 nodeId)」가 나오면 성공입니다.
클라우드에 연결하지 않기
FLEET_*를 전혀 설정하지 않으면 순수 로컬 노드입니다. 로컬 기능은 모두 그대로이며 클라우드에서 보이지 않을 뿐입니다. 언제든 zqagent link로 연결할 수 있습니다.
게이트웨이 측 배포(키, TLS, 다중 복제본)는 리포지토리의
docs/rust-gateway-plan.md와deploy/k8s/gateway/를 참고하세요. 프로토콜 세부 사항은docs/wire-protocol.md.
자주 발생하는 문제
| 현상 | 확인 |
|---|---|
| 노드가 게이트웨이에 연결되지 않음 | 주소가 wss://.../ws인지, 인증서가 CA 발급(자체 서명 아님)인지, 방화벽에서 443 허용 여부 |
| 로그에 「未能接入网关」 | FLEET_GATEWAY_URL 접근성과 FLEET_SECRET이 게이트웨이와 일치하는지 확인 |
| 서버가 외부를 대기하지 않음 | HOST 기본은 127.0.0.1. 공인 접근에는 HOST=0.0.0.0과 포트 개방 필요 |
| QQ 게이트웨이 연결 실패 | AppID/Secret 확인, intents 권한 신청 여부, 샌드박스는 QQ_BOT_SANDBOX=1 |
| 메일이 발송되지 않음 | QQ 메일은 인증 코드(로그인 비밀번호 아님). secure와 포트 일치(465→true, 587→false) |
SECRET_KEY 변경 후 자격 증명 무효 | SECRET_KEY는 고정 권장. 변경하면 기존 테넌트 자격 증명이 모두 무효가 됨 |
