본문으로 건너뛰기

API 연동 가이드

외부 시스템에서 PlantPulse AI의 MCP 및 API를 직접 호출하는 방법입니다.

없어진 주소로 찌르지 마십시오

옛 독립 서비스 MCP Server(:50000) · Ontology(:8888) 는 아카이브됐고, LightRAG 를 직접 열던 :7114 도 없습니다. 도구는 MCP 게이트웨이 한 주소에 모입니다 → MCP 도구 지도


MCP 클라이언트는 게이트웨이 한 주소로​

외부 MCP 클라이언트(다른 에이전트 · 데스크톱 앱 등)는 https://<호스트>/mcp/gateway/ 하나로 붙습니다(Streamable HTTP). 플랫폼 통합 MCP · 시계열 인사이트 · RAG · 문서 이해 · 비전 · 공학 계산 · 디지털 스레드 · 운영자가 붙인 외부 서버의 도구가 이 주소에 모입니다.

할 일자리
키 받기AI 관리 콘솔 › MCP 게이트웨이 › 클라이언트 API 키 에서 키 발급 — 키(ppgw_…)는 발급 직후 한 번만 보입니다. 키에 범위(설비 · 사이트 · 프로젝트 · 고객 PO · 협력사)와 등급을 줍니다 — 범위가 없는 키는 외부 서버 도구만 부릅니다
볼 도구 정하기같은 메뉴의 권한 — 새 키는 아무것도 허용되지 않은 채 시작합니다. 서버마다 «모든 도구» 또는 일부 도구를 허용합니다
부르기X-API-Key: ppgw_… 또는 Authorization: Bearer ppgw_…
호출 보기모니터링 › 호출 기록·지표 — 키마다 호출이 남습니다
  • 키가 없거나 틀리면 401, 1분에 20번 넘게 틀린 클라이언트는 그 분 동안 429 입니다.
  • 이 경로는 게이트웨이의 MCP(/mcp/)만 넘깁니다. 게이트웨이 관리 REST(/api/v1)는 바깥에 열리지 않습니다.
  • 끝 슬래시 없는 /mcp/gateway 는 /mcp/gateway/ 로 넘겨 줍니다(308).
  • 화면 · 권한 · 키 폐기는 → AI 관리 콘솔 — MCP 게이트웨이

그 밖의 엔드포인트​

서비스URL프로토콜인증
MCP 게이트웨이https://<호스트>/mcp/gateway/MCP (Streamable HTTP)게이트웨이 키 ppgw_… — 위
코파일럿 공개 APIhttps://<호스트>/api/v1/…RESTX-API-Key → 아래
LiteLLM (모델)http://<서버>:4000OpenAI 호환마스터 키
통합 MCP (플랫폼)<플랫폼>/api/v5/mcpMCP (JSON-RPC 2.0)api_key — Authorization: Bearer 또는 X-API-Key

서비스 직결 — 운영자 · 시스템 연동용​

사내 MCP 서버는 게이트웨이를 거치지 않고 직접도 열려 있습니다. 다만 인증이 설치 공용 인바운드 키(PI_INBOUND_API_KEY) 하나라 그 키를 든 쪽은 전권이고, 호출자마다의 도구 권한도 호출 기록도 없습니다. 운영자 스크립트 · 플랫폼 CEP 같은 시스템 연동에만 쓰고, 고객의 MCP 클라이언트에는 게이트웨이를 주십시오.

서비스직결 주소인증
시계열 인사이트http://<서버>:8970/mcp/ · https://<호스트>/mcp/timeseries/인바운드 키
RAG 관문http://<서버>:7115/mcp/ · https://<호스트>/mcp/rag/인바운드 키
문서 이해https://<호스트>/mcp/parser/(호스트 포트 7120 은 미발행)인바운드 키
비전https://<호스트>/mcp/vision/(호스트 포트 7130 은 미발행)인바운드 키

디지털 스레드 · 공학 계산 · 플랫폼 통합 MCP 를 한 주소로 받는 길은 게이트웨이뿐입니다.

기본 호스트의 경로로도 열려 있습니다​

서브도메인이나 포트를 따로 열지 않아도, 프록시가 기본 호스트(채팅과 같은 주소)의 경로로 다음을 넘겨 줍니다.

경로넘어가는 곳
https://<호스트>/mcp/gateway/MCP 게이트웨이의 MCP — 외부 MCP 클라이언트는 이것
https://<호스트>/llm/LiteLLM — OpenAI SDK 의 base_url 은 https://<호스트>/llm/v1
https://<호스트>/mcp/rag/RAG 관문의 MCP
https://<호스트>/mcp/timeseries/시계열 인사이트의 MCP
https://<호스트>/mcp/parser/문서 이해의 MCP
https://<호스트>/mcp/vision/비전의 MCP
이 경로는 프록시가 아무것도 열어 주지 않습니다

인증은 뒷단이 합니다 — /mcp/gateway/ 는 게이트웨이 키 ppgw_…, 직결 MCP 넷은 X-API-Key: <PI_INBOUND_API_KEY>, /llm/ 은 LiteLLM 마스터 키가 없으면 401 입니다. 기본 호스트는 대개 바깥 DNS 가 붙은 주소이므로, 설치 뒤 CHANGE_ME_… 로 남은 키가 없는지 반드시 확인하십시오 → 자격증명 관리

LiteLLM 관리 화면(/llm/ui)은 이 경로로 열리지 않습니다 — :4000 으로 여십시오.

뒷단의 인바운드 키​

시계열 인사이트 · RAG · 문서 이해 · 비전 · 디지털 스레드는 같은 인바운드 키(설치 env 의 PI_INBOUND_API_KEY)를 씁니다 → 자격증명 관리. X-API-Key: <키> 또는 Authorization: Bearer <키> 로 보냅니다.

  • 키 없이 열리는 것은 /ping · /health 둘뿐입니다. /metrics 도 키가 필요합니다.

  • 키가 없거나 틀리면 401 입니다(RAG 는 missing or invalid API key).

  • 🔴 키가 비어 있으면 서비스가 뜨지 않습니다(fail-closed). 설치 점검(bin/lint-env.sh)이 먼저 [fail] PI_INBOUND_API_KEY is empty and PI_INBOUND_AUTH_DISABLED is not true 로 멈추고, 서비스 로그에는 FATAL: … is empty — refusing to start … 이 남습니다. 빈 키로 «조용히 열린 뒷단» 은 생기지 않습니다.

  • 인증을 일부러 끄려면(격리된 시험 환경 등) 설치 env 에 PI_INBOUND_AUTH_DISABLED=true 를 적습니다(기본 false). compose 가 이 값을 서비스마다의 이름으로 넘깁니다.

    서비스컨테이너가 읽는 이름
    RAGRAG_INBOUND_AUTH_DISABLED
    시계열 인사이트TS_INBOUND_AUTH_DISABLED
    디지털 스레드DT_INBOUND_AUTH_DISABLED
    문서 이해 · 비전INBOUND_AUTH_DISABLED

    키가 적혀 있으면 키가 이깁니다(lint-env 가 [note] … is ignored — PI_INBOUND_API_KEY is set and the key wins 로 알립니다). true · false 밖의 값은 [fail] 입니다. 끈 채로 뜬 서비스는 /health 에 그렇다고 적습니다(디지털 스레드 auth.inbound=disabled, 시계열 인사이트 inbound_auth: "disabled") — 관리 콘솔의 API 키 화면에서 점검 실행 하면 열림 으로 잡힙니다 → AI 관리 콘솔 — API 키

문서 이해·비전은 호스트 포트(7120 · 7130)를 발행하지 않습니다 — 바깥에서는 위의 /mcp/parser/ · /mcp/vision/ 경로로만 닿습니다. 디지털 스레드(7140)도 호스트 포트를 열지 않습니다.

MCP 경로는 끝의 슬래시까지입니다

/mcp 가 아니라 /mcp/ 입니다. 슬래시를 빼면 리다이렉트나 404 가 납니다.


MCP 프로토콜 호출​

다섯 서버 모두 MCP(Model Context Protocol) 표준을 따릅니다. 공장 데이터·온톨로지 도구는 플랫폼 통합 MCP(/api/v5/mcp), 파형 분석은 시계열 인사이트, 문서·사진은 나머지 셋입니다.

도구 이름에 접두어가 붙는 자리는 채팅입니다

서버를 직접 부를 때는 접두어 없는 이름(analyze_asset)을 씁니다. insight_ 같은 접두어는 채팅의 도구 등록기가 붙이는 것이라, 채팅 화면에서 보는 이름과 여기서 쓰는 이름이 다릅니다.

MCP 기본 구조​

MCP는 JSON-RPC 2.0 기반의 HTTP POST 통신입니다. 통합 MCP는 api_key 인증(Authorization: Bearer <token>)이 필요합니다.

POST /api/v5/mcp HTTP/1.1
Content-Type: application/json
Authorization: Bearer <platform.api.token>

{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "<도구 이름>",
"arguments": {
"<파라미터1>": "<값1>",
"<파라미터2>": "<값2>"
}
}
}

도구 목록 조회​

curl -X POST <플랫폼>/api/v5/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <platform.api.token>" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}'

도구 호출 예시​

거의 항상 search_domains 로 시작합니다

사람은 이름으로 말하고 도구는 ID 를 받습니다. search_domains 가 14개 도메인을 부분일치로 검색해 «도메인 + ID» 를 확정하고, 그 ID 를 상세 도구에 넘기는 것이 기본 순서입니다.

① 이름으로 찾아 ID 를 얻는다​

curl -X POST <플랫폼>/api/v5/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <platform.api.token>" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_domains",
"arguments": {
"query": "압축기",
"domain": "asset",
"limit": 20
}
}
}'

domain 을 알면 지정해 정밀 검색하고, 모르면 생략하면 도메인별로 갈라 돌려줍니다.

② 자산 계층을 펼친다​

curl -X POST <플랫폼>/api/v5/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <platform.api.token>" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_asset_tree",
"arguments": { "asset_id": "<①에서 얻은 ID>" }
}
}'
tags 키가 없는 것은 «태그 없음» 이 아닙니다

기본적으로 태그는 개수(tag_count)만 옵니다. 이름·tag_id 가 실제로 필요할 때만 include_tags: true 를 주십시오 — 응답이 몇 배로 커집니다. 태그 유무의 정본은 tag_count 이고, tag_count: 0 만이 진짜 태그 없음입니다.

③ 태그 현재값을 한 번에 읽는다​

curl -X POST <플랫폼>/api/v5/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <platform.api.token>" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_tag_values",
"arguments": { "tag_ids": ["<tag_id>", "<tag_id>"] }
}
}'

tag_ids 는 배열이고 한 번에 100개까지 넣습니다. 응답의 found: false 는 미등록 태그, error 는 조회 실패(재시도할 가치가 있음)를 뜻합니다. 기간 이력은 get_tag_history 입니다.


통합 MCP 인증​

통합 MCP는 api_key 인증이 필수입니다. 채팅이 쓰는 값은 코파일럿의 platform.api.url · platform.api.token(환경변수 CO_PLATFORM_API_URL · CO_PLATFORM_API_TOKEN — compose 가 호스트 env 의 PP_API_URL · PP_API_TOKEN 을 옮겨 넣습니다)입니다. 2026.09 까지의 접두사 CH_ 는 2026.10 에 CO_ 로 바뀌었고 폴백이 없습니다.

⚠ 옛 이름 mcp.api.url · mcp.api.token 은 없습니다. 기동할 때 옛 설정 행이 새 이름으로 옮겨지지만, 환경변수를 옛 이름으로 주면 아무 일도 일어나지 않습니다.

인증 헤더​

curl -X POST <플랫폼>/api/v5/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <platform.api.token>" \
-d '{ ... }'

온톨로지(지식 그래프) 호출​

온톨로지 도구는 통합 MCP 에 흡수되었습니다. 🔴 ontology_* 라는 이름의 도구는 없습니다 — 옛 독립 서비스(:8888) 시절의 이름입니다. 지금은 셋입니다.

도구무엇을 답하나
get_ontology한 사이트의 구성 개요 — 구역/라인/설비 구조 + 설비별 태그 개수
query_ontology방향성 관계 탐색 — 「X 가 무엇에 영향을 주나」, 상류·하류
query_sparql다중 조건 교차 질의 (SELECT / ASK 전용)

이름이 비슷하지만 반환하는 축이 다릅니다. 순수 계층 구조는 get_asset_tree, 사이트 구성 개요는 get_ontology, 관계 전파는 query_ontology 입니다.

사이트 구성 개요​

curl -X POST <플랫폼>/api/v5/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <platform.api.token>" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_ontology",
"arguments": { "site_id": "<site_id>" }
}
}'

site_id 는 필수입니다 — search_domains 에 domain: "site" 로 먼저 해소하십시오.

태그 총수는 total_tag_count 하나가 정본입니다

노드별 tag_count 를 합산하지 마십시오 — 펼치지 않은 가지가 빠집니다. total_tag_count_unknown: true 는 「셀 수 없었다」는 뜻이므로 추정하지 마십시오.

관계 탐색 — 「이게 무엇에 영향을 주나」​

curl -X POST <플랫폼>/api/v5/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <platform.api.token>" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "query_ontology",
"arguments": {
"entity_id": "<설비 ID>",
"direction": "DOWN",
"depth": 2
}
}
}'
인자값뜻
entity_id(필수)탐색을 시작할 엔티티
relation_typeFEEDS · DEPENDS_ON · PRODUCES생략하면 전체
directionUP · DOWN · BOTH기본 DOWN(하류 영향)
depth1 ~ 5기본 2

응답은 root + nodes[] + edges[] + truncated 입니다. truncated: true 면 깊이나 폭에서 잘렸다는 뜻이니 범위를 좁혀 다시 묻습니다.


시계열 인사이트 MCP 호출​

설비 이상 탐지​

curl -X POST http://127.0.0.1:8970/mcp/ \
-H "Content-Type: application/json" \
-H "X-API-Key: <PI_INBOUND_API_KEY>" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "detect_asset_anomaly",
"arguments": {
"asset_id": "ASSET_DJ_M_01_1",
"lookback_minutes": 1440,
"top_k": 5
}
}
}'

threshold 를 생략하면 기본 5.0 입니다. 센서 이상 점수는 0~1 이 아니라 보정 점수 (기준 구간 대비 로버스트 z)라서, 0 을 주면 기준 중앙값보다 조금만 높아도 전부 이상으로 셉니다 → AI 모델

설비 센서 예측​

curl -X POST http://127.0.0.1:8970/mcp/ \
-H "Content-Type: application/json" \
-H "X-API-Key: <PI_INBOUND_API_KEY>" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "forecast_asset",
"arguments": {
"asset_id": "ASSET_DJ_M_01_1",
"prediction_length": 96,
"enable_bounds": true
}
}
}'

설비 건강 점수​

curl -X POST http://127.0.0.1:8970/mcp/ \
-H "Content-Type: application/json" \
-H "X-API-Key: <PI_INBOUND_API_KEY>" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_asset_health",
"arguments": {
"asset_id": "ASSET_DJ_M_01_1"
}
}
}'

RAG (LightRAG) 호출​

RAG 엔진은 LightRAG 1.5.7(2026.10 부터 우리가 감싼 이미지 pi/plantpulse-lightrag)이지만, 🔴 직접 부르지 않습니다. LightRAG 는 컨테이너 안에만 있고 바깥에서 닿는 자리는 rag-api(7115) 관문 하나입니다.

문서 업로드·목록·삭제는 관리 콘솔에서 합니다(옛 LightRAG WebUI 는 콘솔로 흡수됐습니다). 검색은 관문의 REST 로 부르고 X-API-Key 로 인증합니다.

정보

코파일럿 채팅의 문서 검색은 이 관문의 MCP 도구 rag_search 를 MCP 게이트웨이를 거쳐 부릅니다(코파일럿에 내장 문서 검색은 없습니다). REST /api/v1/query 와 MCP rag_search 는 같은 봉투를 냅니다 — LightRAG 에 직접 붙지 않습니다. 아래 응답 모양은 LightRAG 의 것이 아니라 관문이 정한 봉투입니다.

텍스트 검색​

curl -X POST http://127.0.0.1:7115/api/v1/query \
-H "Content-Type: application/json" \
-H "X-API-Key: <PI_INBOUND_API_KEY>" \
-d '{
"query": "펌프 정비 절차",
"mode": "hybrid"
}'
인자값
query검색어 — 3글자 이상(짧으면 400)
modehybrid(기본) · naive · local · global — 그 밖의 값은 422
응답뜻
{"retrieved": true, "answer": …, "sources": [{"file_name": …, "reference_id": …}]}찾았습니다. reference_id 는 답 본문의 출처 표식 [n] 의 n 입니다(엔진이 번호를 주지 않은 출처에는 없습니다)
{"retrieved": false, "code": …, "reason": …, "sources": []}못 찾았습니다. answer 키 자체가 없습니다
HTTP 502엔진 쪽 실패
retrieved: false 는 오류가 아니라 «없다» 입니다

HTTP 는 200 입니다. code 가 NO_CONTEXT 면 색인된 문서에 관련 내용이 없는 것이고, EMPTY_ANSWER 면 엔진이 빈손으로 돌아온 것입니다. 다시 불러도 같은 답이 옵니다 — answer 가 없는 응답을 결과처럼 보여 주지 마십시오.


도면 비교 — 두 판의 차이​

도면 수집(DRAWING_INGEST_ENABLED=true)을 켠 설치에서 쓸 수 있습니다 → 환경 변수 설정. 입구는 둘이고, 비교는 문서 이해가 합니다. rag-api 는 색인된 두 판의 원본을 꺼내 문서 이해에 넘기고 그 결과를 그대로 돌려줍니다. 둘 다 인바운드 키를 받습니다.

입구언제
rag-api GET /api/v1/drawings/{asset_id}/{doc_name}/diff이미 색인된 도면 — 앞 판을 찾아서 비교합니다
문서 이해 POST /api/v1/drawings/diff손에 든 파일 두 개를 바로 비교합니다(색인과 무관)

색인된 도면의 앞 판과 비교 (rag-api)​

curl -G "http://127.0.0.1:7115/api/v1/drawings/<asset_id>/<doc_name>/diff" \
-H "X-API-Key: <PI_INBOUND_API_KEY>" \
--data-urlencode "lang=ko"
인자뜻
against비교할 앞 판의 문서 이름. 비우면 같은 도면 번호 가운데 판 표시가 바로 앞인 것(A < B < … < Z < AA, 1 < 2 < 10)을 고릅니다
against_asset앞 판이 다른 설비에 올라가 있을 때 그 설비 ID (기본 같은 설비)
langsummary 줄의 언어 ko · en. 비우면 설치 언어(PP_LANG). 숫자는 언어와 상관없이 도면에 적힌 그대로입니다
상태뜻
404어느 쪽이 색인돼 있지 않거나, 원본이 보관돼 있지 않거나, 고를 앞 판이 없음
422비교기가 파일을 읽지 못함
502비교기나 객체 저장소에 닿지 못함

같은 라우터에 쪽 미리보기(…/page/{n} — PDF 는 PNG, DXF 는 SVG)와 판 상태(…/revision-status — current · outdated · change_open · unknown)가 있습니다.

파일 두 개를 바로 비교 (문서 이해)​

문서 이해는 호스트 포트를 발행하지 않으므로 바깥에서는 parser.* 호스트로 부릅니다.

curl -X POST "https://parser.<도메인>/api/v1/drawings/diff" \
-H "X-API-Key: <PI_INBOUND_API_KEY>" \
-F "old=@A-100_rev-A.dxf" -F "new=@A-100_rev-B.dxf" -F "lang=ko"
  • 파일은 .dxf 나 텍스트 층이 있는 벡터 .pdf, 하나에 50 MB 까지입니다. DWG 는 받지 않습니다.
  • lang 의 기본값은 en 입니다(rag-api 입구와 다릅니다).
  • 거절은 error_kind: input 과 함께 확장자 415 · 크기 413 · 읽을 수 없음 400 입니다.

결과의 모양​

키담는 것
title바뀐 표제란 칸
dimensionsadded · removed · changed(값 · 단위 · 개수 · 공차 · 글자 · 위치 중 무엇이 바뀌었나) · unchanged
bom · notes바뀐 BOM 행 · 주기
regions바뀐 영역 — 뒤 판의 bbox 와 앞 판의 bbox_old. 맞추기에 실패하면 status: unavailable 과 이유이고, 영역을 지어내지 않습니다
summary사람이 읽는 요약 줄 — 숫자는 도면의 글자 그대로
confidence · limitshigh · medium · low 와, 비교하지 못한 것(스캔 쪽 · 표제란 없음 · 한쪽에만 BOM 등)
warningsdifferent_drawing_numbers(도면 번호가 다름) · revision_label_unchanged(판 표시가 같음) 등

영상 · 음성 — rag-api 의 /api/v1/media​

영상 · 음성 수집을 켠 설치(설치 env PI_MEDIA_ENABLED=true)에서만 붙는 경로입니다. 꺼진 설치에는 이 경로가 아예 없어 404 이고, 영상 · 음성 업로드는 400 으로 거절됩니다. 켜졌는지는 GET /api/v1/config 의 media_ingest_enabled 로 봅니다 → 환경 변수 설정 · AI 관리 콘솔 — 동영상

메서드 · 경로하는 일
GET /api/v1/media/jobs파일마다 처리 상태 · 길이 · 구간 수 · 장면 그림 수(thumb_count)와 처리 시간대(window — open · next_start · spec)
GET /api/v1/media/jobs/{asset_id}/{doc_name}파일 하나의 상세
GET /api/v1/media/integrity색인됐는데 검색 색인에 없는 구간
POST /api/v1/media/jobs/{asset_id}/{doc_name}/retry다시 대기열에(실패 · 건너뜀). ?force=true 면 처음부터 다시 녹취
GET /api/v1/media/{asset_id}/{doc_name}/transcript보관된 녹취록
GET /api/v1/media/{asset_id}/{doc_name}/thumb?t=<ms>그 시각 이전의 가장 가까운 장면 그림
GET /api/v1/media/{asset_id}/{doc_name}/stream재생 — Range 요청에 206 으로 답합니다
  • 올리기는 문서와 같은 POST /api/v1/docs/upload 이고, 지우기도 같은 DELETE /api/v1/docs/{asset_id}/{doc_name} 입니다(플랫폼 원장에서 온 것은 409).
  • 장면 그림(…/thumb) 은 ETag 와 Cache-Control: private, max-age=86400 을 붙입니다. 같은 ETag 를 If-None-Match 로 보내면 그림을 다시 읽지 않고 304 로 답합니다. 상태 DB 가 잠시 바쁘면 500 대신 503 state database busy, retry shortly 와 Retry-After: 1 입니다 — 그 시간 뒤 다시 부르십시오.
  • 코파일럿 화면이 쓰는 중계(코파일럿의 /media/thumb · /media/stream, 로그인 세션 인증)도 ETag · 304 를 그대로 넘기고, 뒷단의 503 은 Retry-After 와 함께 503 으로 넘깁니다(전에는 502 로 감쌌습니다).

요청 id 와 지표 — X-Request-ID · /metrics​

2026.10 재릴리즈부터 파이썬 뒷단 다섯(RAG · 시계열 인사이트 API · 문서 이해 · 비전 · 디지털 스레드)이 같은 규칙을 씁니다.

  • 요청 id — 요청에 X-Request-ID(1~128자, 영문 · 숫자 · . _ -)를 실어 보내면 그 값을, 없거나 모양이 틀리면 새로 만든 값을 응답 헤더 X-Request-ID 로 돌려줍니다. 그 요청이 도는 동안 남는 로그 줄에 같은 값이 request_id 로 붙습니다. RAG 는 오류 응답 본문에도 request_id 를 더합니다.
  • 지표 — GET /metrics 가 Prometheus 텍스트 형식을 냅니다. 인바운드 키가 필요합니다(헬스 경로와 달리 면제가 아닙니다). 포트는 바뀌지 않았습니다 → 로그 및 모니터링
curl -s -D - -o /dev/null -H "X-Request-ID: my-trace-001" \
-H "X-API-Key: <PI_INBOUND_API_KEY>" http://127.0.0.1:7115/api/v1/config | grep -i x-request-id

LLM API 호출 (OpenAI 호환)​

LiteLLM 프록시는 OpenAI 호환 API를 제공합니다. 기존 OpenAI SDK를 그대로 사용할 수 있습니다. LAN 에서는 http://<서버>:4000/v1, 기본 호스트를 거치면 https://<호스트>/llm/v1 입니다.

채팅 완성​

curl http://127.0.0.1:4000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer 설치-시-변경" \
-d '{
"model": "aura-lite",
"messages": [
{"role": "user", "content": "주입기 베어링 교체 주기를 알려줘."}
],
"temperature": 0.7
}'

임베딩 생성​

curl http://127.0.0.1:4000/v1/embeddings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer 설치-시-변경" \
-d '{
"model": "aura-embedding-4b",
"input": "주입기 베어링 교체 절차"
}'

모델 목록 확인​

curl http://127.0.0.1:4000/v1/models \
-H "Authorization: Bearer 설치-시-변경"

사용 가능한 모델​

모델 이름용도실제 모델
aura-lite대화 · 분석 (이미지 입력 겸용)Aura — 기본 Aura-Lite-NVFP4
aura-vision위와 같은 엔진의 비전용 별칭위와 같은 모델
aura-embedding-4b임베딩Aura-Embedding-4B
aura-reranker-0.6b리랭킹Aura-Reranker-0.6B
옛 이름 gpt-4o · text-embedding-3-large 는 이제 오류입니다

예전 판은 OpenAI 호환 별칭(gpt-4o · gpt-4o-mini · text-embedding-3-large · rerank-multilingual-v3.0)을 받았습니다. 지금은 받지 않습니다 — 연동 코드에 옛 이름이 남아 있으면 호출이 거절됩니다. 위 표의 이름으로 바꾸십시오. 지금 떠 있는 이름은 /v1/models 로 확인할 수 있습니다.

어느 이름이든 사내에서 도는 Aura 모델로 갑니다 — 폐쇄망 설치에서도 같습니다.


Python SDK 예시​

OpenAI SDK로 LLM 호출​

from openai import OpenAI

client = OpenAI(
base_url="http://127.0.0.1:4000/v1",
api_key="설치-시-변경"
)

response = client.chat.completions.create(
model="aura-lite",
messages=[
{"role": "user", "content": "주입기 상태를 분석해줘."}
]
)

print(response.choices[0].message.content)

MCP 도구 호출​

import requests

def call_mcp_tool(url, tool_name, arguments=None, headers=None):
response = requests.post(
url,
headers=headers or {},
json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": tool_name,
"arguments": arguments or {}
}
}
)
return response.json()

# 이름으로 설비를 찾는다 (통합 MCP — api_key 필요)
result = call_mcp_tool(
"<플랫폼>/api/v5/mcp",
"search_domains",
{"query": "압축기", "domain": "asset"},
headers={"Authorization": "Bearer <platform.api.token>"}
)
print(result)

# 설비 이상 탐지 (TimeSeries-Insight — 인바운드 키 필요)
result = call_mcp_tool(
"http://127.0.0.1:8970/mcp/",
"detect_asset_anomaly",
{"asset_id": "ASSET_DJ_M_01_1", "lookback_minutes": 1440},
headers={"X-API-Key": "<PI_INBOUND_API_KEY>"}
)
print(result)

코파일럿 공개 API (/api/v1)​

외부 시스템이 채팅과 아침 카드를 그대로 쓰는 입구입니다. 채팅과 같은 주소(기본 호스트)에 있습니다.

메서드 · 경로하는 일
POST /api/v1/chat질문하고 답을 받습니다. 필요한 도구는 서버가 고릅니다
GET /api/v1/tasks/daily그날의 데일리 카드(매일 05:00 생성)를 읽습니다. ?date=YYYY-MM-DD — 공장 시간대 기준, 비우면 오늘
GET /api/v1/tools에이전트가 지금 쓸 수 있는 도구 목록(이름 · 설명 · 조회전용 여부)
GET /api/v1/ai-feedback사람이 AI 결과에 단 표시(헛경보 · 제안 수락/거절/효과)와 서버가 규칙으로 단 표시(맞음 · 헛경보 · 놓침)를 읽습니다 — 2026.10 (아래)
curl -X POST https://<호스트>/api/v1/chat \
-H "Content-Type: application/json" \
-H "X-API-Key: <발급받은 키>" \
-d '{ "input_value": "지금 끊어진 엣지 장비가 몇 대야?" }'

응답은 answer(마크다운) · session_id · duration_ms · tools(이 답을 만들며 부른 도구) 입니다. session_id 를 빼거나 비우면 새 대화를 열고(화면의 「새 대화」와 같습니다 — 기본 앱, 부른 사람 소유) 그 id 를 응답의 session_id 로 돌려줍니다. 대화를 이어 가려면 다음 요청에 그 값을 넣습니다. 동기 호출이라 도구를 여러 번 부르는 질문은 수십 초가 걸릴 수 있습니다.

2026.09 판에서는 session_id 없는 호출이 500 이었습니다

문서는 「비우면 새 대화」라고 했지만 서버가 500 을 냈고, X-API-Key 로 부른 호출도 500 으로 끝났습니다. 2026.10 에서 둘 다 고쳐졌습니다.

답이 비면 502 입니다

2026.10 재릴리즈 3 부터 모델이 다시 시도한 뒤에도 답을 만들지 못하면 HTTP 502 로 돌려줍니다. 본문의 모양은 같고 answer 가 비어 있습니다. 빈 답을 200 으로 받아 «답 없음» 을 결과처럼 보여 주지 않게 한 것입니다 — 같은 질문을 다시 보내면 됩니다.

AI 결과에 사람이 단 표시 — GET /api/v1/ai-feedback​

코파일럿의 제안에서 사람이 AI 결과에 단 표시(무시 사유 · 수락/거절 · 효과)를 읽기만 하는 입구입니다(쓰는 길은 화면뿐입니다) → AI 발견과 정확도 — 사람이 남기는 판단. 인증은 다른 /api/v1 과 같습니다(X-API-Key 만 — 2026.10 재릴리즈 6 부터 로그인 세션은 403).

curl "https://<호스트>/api/v1/ai-feedback?since=2026-10-01T00:00:00Z&limit=500" \
-H "X-API-Key: <발급받은 키>"
인자뜻
sincelabeled_at 하한(RFC 3339 또는 epoch ms). 비우면 처음부터
asset_id설비 id 로 거릅니다
target_kindANOMALY(이상 한 건) · FINDING(AI 발견) · PROPOSAL(발견에서 난 제안 카드) · MISSED(놓침 신고) · DAILY_TASK
limit최대 줄 수 1~1000, 기본 500

응답은 count · items · next_since 입니다. 오래된 것부터 오므로 다음 쪽은 next_since 로 다시 부릅니다 — 같은 시각의 줄이 겹쳐 올 수 있으니 feedback_id 로 거르십시오. 같은 대상에 사람이 다시 누르면 그 줄의 value · labeled_at 이 바뀝니다(마지막 표시가 이깁니다).

label_keyvalue
verdicthit · false_alarm · unsure(판정 보류 — 정확도 계산에서 뺍니다)
reason헛경보 이유 setup · idle · maintenance · sensor · other / 제안 거절 이유 already_done · not_needed · wrong · other
cause실제 원인 breakdown · degradation · sensor · setup · maintenance · normal · other
proposal_decisionaccepted · rejected
outcomeeffective · not_effective · unknown (comment 에 메모)
missedmissed (comment 에 설명, occurred_at 에 놓친 사건 시각)

새 화면(메뉴 셋)에서 사람이 남기는 값은 verdict 의 false_alarm(확인할 이상의 무시)과 그 reason, proposal_decision, outcome 입니다. hit · missed 는 서버의 정지 · 고장 규칙 대조가 달고, unsure · cause 는 옛 화면에서 남긴 줄에 있습니다.

각 줄에는 occurred_at(표시가 가리키는 사건 시각) · labeled_by · labeled_at · config_ver(그 결과를 낸 판정 버전)이 함께 오고, 시각 옆의 …Stamp 는 공장 시간대(PP_TZ)로 쓴 값입니다.

키 — 두 가지 방법​

방법어디서성질
발급 키 (권장)코파일럿 관리 콘솔 → API 키 → 발급이름을 붙여 여러 개 발급하고 하나씩 폐기할 수 있습니다. 마지막 사용 시각이 남습니다
환경변수 키/etc/kopens/plantpulse-ai.env 의 CO_API_V1_KEY(2026.09 까지 CH_API_V1_KEY — 옛 이름은 조용히 무시됩니다)한 줄뿐입니다. 비워 두면 이 키로 하는 인증만 꺼집니다
발급 키는 «발급 직후 한 번» 만 보입니다

저장은 해시로만 하므로 목록에는 앞자리만 나옵니다. 잃어버렸으면 새로 발급하고 옛 것을 폐기하십시오.

이 키로 닿는 곳은 공개 API 뿐입니다

키가 주는 권한은 /api/v1 과 코파일럿 MCP(/mcp) 입니다. 2026.10 재릴리즈 6 부터 이 두 경로는 키로만 열리고, 로그인한 브라우저 세션으로 부르면 403 입니다. 키로 부른 호출의 권한 · 문서 등급은 «외부 API» 역할 묶음(기본 대외비)을 따르고 범위는 늘 전체입니다 → 권한. 관리자 경로와 API 문서 화면(/swagger-ui.html)은 관리자 로그인이 필요합니다. 도구를 «직접 부르는» 길은 열려 있지 않습니다 — 상태를 바꾸는 도구에는 사람의 승인 절차가 붙어 있기 때문입니다.

화면이 쓰는 API 중 바뀐 것 (2026.10 재릴리즈 3)​

아래는 공개 API 가 아니라 코파일럿 화면이 로그인 세션으로 부르는 경로입니다. 화면을 흉내 내 부르는 연동이 있다면 확인하십시오.

메서드 · 경로바뀐 것
PUT /api/employees/{아이디}/work-role🔴 관리자만 됩니다(전에는 본인도 됐습니다). 본문 {"workRole": "maintenance"}. 바꿀 때마다 감사 로그에 employee.work_role:<아이디> 로 남습니다 → 사용자 권한과 업무 역할
PUT /api/employees/{아이디}/home-role새 경로 — 본인의 시작 화면 선호. 본문 {"homeRole": "manager"}(업무 역할과 같은 다섯 값). 본인 또는 관리자가 부릅니다. 승인 권한은 바꾸지 않습니다. 메뉴 셋(묻기 · 제안 · 자동화)의 새 화면은 첫 화면이 누구에게나 묻기라 이 값을 쓰지 않습니다 — 경로는 남아 있습니다
GET /api/sop?query=…&assetId=…assetId 인자가 생겼습니다 — 주면 다른 설비의 출처를 뺍니다. 응답은 {answer, sources[], citations, truncated} 이고 답은 600자 안의 문장 경계에서 자릅니다(truncated). 검색어가 너무 짧으면 400, 문서 검색 서비스가 답하지 않으면 503 + Retry-After 입니다

응답 형식​

MCP 성공 응답​

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{ ... 도구 실행 결과 JSON ... }"
}
]
}
}

MCP 오류 응답​

{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32600,
"message": "Invalid request",
"data": "상세 오류 메시지"
}
}

양이 많을 때 — limit 과 has_more​

🔴 page · size 는 없습니다. 통합 MCP 도구는 limit 으로 상한을 주고, 응답의 has_more 로 「더 있다」를 알립니다.

{
"name": "search_domains",
"arguments": {
"query": "압축기",
"domain": "asset",
"limit": 50
}
}
도구limit 기본값최대값
search_domains50500
get_tag_history500500
get_tag_values(배열 tag_ids — 한 번에)100개
has_more: true 를 만나면

쪽 번호가 없으므로 조건을 좁히는 것이 정석입니다 — domain 을 지정하거나 기간을 자르거나 검색어를 구체화하십시오. 같은 질의를 계속 밀어 다음 쪽을 받는 형태가 아닙니다.


시간 형식​

통합 MCP 의 시간 파라미터는 서버 로컬 시각입니다. 이름은 from · to 이고, 형식은 YYYY-MM-DDTHH:MM:SS 입니다.

{
"name": "get_tag_history",
"arguments": {
"tag_id": "<tag_id>",
"from": "2026-03-29T00:00:00",
"to": "2026-03-29T23:59:59"
}
}
오프셋과 Z 를 붙이면 거부됩니다

2026-03-29T00:00:00+09:00 이나 ...Z 는 받지 않습니다. 오프셋 없는 로컬 시각만 받습니다 — 서버가 지금 몇 시로 알고 있는지는 get_current_time 으로 확인하십시오.

start_time · end_time 이라는 이름도 없습니다. from · to 입니다.

응답에서 _iso 접미사가 붙은 필드는 사람이 읽기 쉬운 KST 형식입니다:

{
"timestamp": 1711641600000,
"timestamp_iso": "2026-03-29 09:00:00"
}