채팅 API 사용

보안

로그인

  • 채팅 API에 사용할 JWT (access token) 을 얻기 위해 로그인 합니다.

import requests

genos_url = 'GenOS 주소'

url = f"{genos_url}/app/api/chat"
body = {
    "user_id": "genos_user_01",
    "password": "<PASSWD>"
}
res = requests.post(f"{url}/auth/login",json=body)
print(res.json())
  • 정상 출력은 아래와 같습니다.

{
    "code": 0,
    "errMsg": "success",
    "data": {
        "access_token": "<JWT 1>",
        "refresh_token": "<JWT 2>",
        "user": {
            "id": 1,
            "user_id": "genos_user_01",
            "name": "GenOS 사용자  01",
            "email": "genos_user_01@mnc.ai",
            "image": "string",
            "group": null,
            "reset_required": null,
            "consent_date": null,
            "consent_required": false,
            "last_login_time": "2025-02-18T14:25:00",
            "is_active": true,
            "reg_date": "2025-01-03T14:24:58",
            "mod_date": "2025-02-18T14:25:00",
            "group_id": null,
            "group_name": null
        },
        "current_login_ip": "123.123.123.123",
        "last_login_ip": "192.168.74.172",
        "last_login_date": "2025-02-18 14:25:00"
    }
}
  • access_token, refresh token의 값을 저장합니다.

(Opt.) Access 토큰 재발급

  • Access 토큰 만료 시, 모든 요청에서 405 Unauthorized 에러가 발생합니다.

  • refresh token을 갖고 있다면, access token의 재발급을 요청할 수 있습니다.

import requests

genos_url = 'GenOS 주소'

url = f"{genos_url}/app/api/chat"
body = {
    "refresh_token": refresh_token
}
res = requests.post(f"{url}/auth/refresh",json=body)
print(res.json())
  • 정상 출력은 아래와 같습니다.

{
    "code": 0,
    "errMsg": "success",
    "data": {
        "access_token": "<JWT 3>"
    }
}

채팅

  • 사용할 채팅 어플리케이션의 endpoint (jlig_i3hi_i743 형식) 가 필요합니다.

정보 조회

  • 해당 채팅의 주요 정보를 조회합니다.

import requests

genos_url = 'GenOS 주소'
endpoint = 'jlig_i3hi_i743'

url = f"{genos_url}/app/api/chat"
headers = {
    "Authorization": "Bearer <JWT 1>"
}
res = requests.get(f"{url}/chat/info?chat_endpoint={endpoint}", headers=headers)
print(res.json())
  • 정상 출력은 아래와 같습니다.

{
    "code": 0,
    "errMsg": "success",
    "data": {
        "chat_id": 112,      // 채팅 서비스 ID
        "chat_rev_id": 187,  // 채팅 서비스 리비전ID
        "chat_pub_id": 172,  // 채팅 서비스 배포ID
        "approval_result": "AP0001",
        "memo": null,
        "service_rev_id": 187,
        "icon": null,        // 지정되었을 경우 base64
        "reg_user": {
            "id": 1,
            "user_id": "administrator",
            "name": "관리자",
            "email": "admin@mnc.ai",
            "image": "string",
            "group": null,
            "reset_required": null,
            "consent_date": null,
            "is_active": true,
            "reg_date": "2025-01-03T14:24:58",
            "mod_date": "2025-02-18T14:25:00",
            "group_id": null,
            "group_name": null
        },
        "service_status": "CS0003",
        "title": "검색 UI",       // 채팅 서비스 이름
        "chat_name": "검색 UI",
        "description": "마크다운테스트",
        "workflow_id": 226,       // 연결된 워크플로우 ID
        "ui_template_id": 3,      // UI 타입 (1: 채팅, 2: PDF Viewer, 3: 검색)
        "session_id": "fecaa1a2-bedd-4558-a3eb-8f69035fe059",  // deprecated
        "greeting": "안녕하세요. 무엇을 도와드릴까요?",             // 인사말
        "chat_samples": [],                                    // 샘플 질문
        "feedback_type": "FILL_IN",                            // 피드백 설정
        "feedback_questions": [],
        "allow_other_feedback": true,
        "workflow_plugins": []     // 파일 기반 대화 등 플러그인 정보
    }
}
  • chat_id, workflow_id의 값을 저장합니다.

기본 채팅요청

  • 채팅 응답을 요청합니다.

import requests

genos_url = 'GenOS 주소'
endpoint = 'jlig_i3hi_i743'

url = f"{genos_url}/app/api/chat"
headers = {
    "Authorization": "Bearer <JWT 1>"
}
body = {
    "question": "Hi, How are you?"
}
res = requests.post(f"{url}/chat/v2/query/{endpoint}", headers=headers, json=body)
print(res.json())
  • 정상 출력은 아래와 같습니다.

{
    "code": 0,
    "errMsg": "success",
    "data": {
        "text": "Im fine thank you, and you?",
        "question": "Hi, How are you?,
        "chatId": "b3429b0c-de84-4740-acbf-ce53a257c5f4",
        "chatMessageId": "83812818-fc9e-40cc-ade9-8666a3e3f81d",
        "sessionId": "b3429b0c-de84-4740-acbf-ce53a257c5f4"
    }
}

멀티 턴 대화

  • 대화 이력을 기반으로 대화하기 위해서는, 같은 세션에 있다는 정보를 알려주어야 합니다.

  • 따라서 새로고침 시나 세션 재시작 등 하나로 묶어줄 대화에서 유지할 무작위 uuid4 값을 생성하고 이를 이용하면 히스토리 기반 대화가 가능합니다.

import requests
from uuid import uuid4

genos_url = 'GenOS 주소'
endpoint = 'jlig_i3hi_i743'
url = f"{genos_url}/app/api/chat"

headers = {
    "Authorization": "Bearer <JWT 1>"
}

session_id = str(uuid4()) # ex) f7a30d14-f9ae-4055-904f-594c0573e7a1

첫번째 턴

  • body의 chatId는 Flowise에 전달하기 위해서 사용되며, headers의 x-genos-session-id 는 GenOS의로깅을 위해 사용합니다.

  • 원활한 로깅 및 피드백 기능을 위해 매 채팅 요청마다 uuid4를 생성하여 x-genos-trace-id 헤더에 전달합니다.

headers['x-genos-session-id'] = session_id
headers['x-genos-trace-id'] = str(uuid4())

body = {
    "question": "My Name is GenOS",
    "chatId": session_id
}
res = requests.post(f"{url}/chat/v2/query/{endpoint}", headers=headers, json=body)
print(res.json())
  • 정상 출력은 아래와 같습니다.

{
    "code": 0,
    "errMsg": "success",
    "data": {
        "text": "Hi GenOS, How can I help you?",
        "question": "My Name is GenOS,
        "chatId": "f7a30d14-f9ae-4055-904f-594c0573e7a1",
        "chatMessageId": "83812818-fc9e-40cc-ade9-8666a3e3f81d",
        "sessionId": "f7a30d14-f9ae-4055-904f-594c0573e7a1"
    }
}

두번째 턴

  • 이전 대화 기록을 바탕으로 질의합니다.

  • session_id는 유지하며, trace id는 새로 생성합니다.

headers['x-genos-session-id'] = session_id
headers['x-genos-trace-id'] = str(uuid4())

body = {
    "question": "What is my name?",
    "chatId": session_id
}
res = requests.post(f"{url}/chat/v2/query/{endpoint}", headers=headers, json=body)
print(res.json())
  • 정상 출력은 아래와 같습니다.

{
    "code": 0,
    "errMsg": "success",
    "data": {
        "text": "Your name is GenOS.", // 이전 대화 이력을 바탕으로 대답
        "question": "What is my name?,
        "chatId": "f7a30d14-f9ae-4055-904f-594c0573e7a1",
        "chatMessageId": "1c052c1e-52f2-49b3-ad75-b4532251899f",
        "sessionId": "f7a30d14-f9ae-4055-904f-594c0573e7a1"
    }
}

(Advanced) 토큰 별 출력

  • LLM 추론은 시간이 많이 걸리기 때문에, 한번에 응답을 받지 않고 토큰 별 응답을 받을 수 있습니다.

  • websocket 통신을 기반으로 하는 socket.io를 이용합니다.

워크플로우에 socket.io 연결

  • python-socketio 라이브러리가 필요합니다.

  • sid를 body에 포함하면, workflow에서 해당 sid를 가진 room에만 socket.io "token" 메시지를 emit하여 동시 요청에도 별개의 응답을 받을 수 있습니다.

  • 비동기를 지원하는 예시 코드로 작성되었습니다.

from socketio import AsyncClient
import aiohttp

genos_url = 'GenOS 주소'
endpoint = 'kbps_xddq_wer3'
workflow_id = 226  # 채팅 정보 조회 시 얻은 workflow ID
url = f"{genos_url}/app/api/chat/chat/v2/query/{endpoint}"
headers = 생략

async def genos_chat_query_v2(body):
    sio = AsyncClient()
    
    @sio.event
    async def connect():
        pass

    @sio.event
    async def disconnect():
        pass

    @sio.on('start') # 첫번째 토큰 응답 전에 발생
    async def start(data):
        pass

    @sio.on('end') # 마지막 토큰 응답 후에 발생
    async def end():
        pass

    @sio.on('token')
    async def token(data): # 토큰 별 응답
        print(data)

    @sio.on('sourceDocuments') # flowise vectordb의 참조 문서 정보
    async def sourceDocuments(data):
        print(data)

    await sio.connect(genos_url, socketio_path=f'/workflow/{workflow_id}/socket.io')
    
    sid = sio.get_sid() # 연결된 socketio.id 클라이언트의 sid를 body에 포함
    body['socketIOClientId'] = sid          # Flowise에 요청을 위해 사용
    headers['x-genos-workflow-sid'] = sid   # GenOS 로깅을 위해 사용
    
    async with aiohttp.ClientSession() as session:
        async with session.post(url, json=body, headers=headers) as response:
            data = await response.json()
    
    await sio.disconnect()
    return data
  • 정상 출력은 위와 동일합니다.

피드백

  • 채팅 요청 시 사용한 trace_id를 이용하여 피드백을 추가할 수 있습니다.

  • 부정 피드백의 경우, 채팅 정보 조회 시 얻은 피드백 타입에 따라 사양이 달라집니다.

긍정 피드백 · 삭제

import requests

genos_url = 'GenOS 주소'
chat_id = 177 # 채팅 정보 조회 시 얻은 채팅 서비스 ID

# 아래는 피드백을 추가할 응답을 특정하기 위해 채팅 전송 요청 헤더의 "x-genos-trace-id"와 "x-genos-session-id"로 각각 지정
trace_id = '4844f357-52e6-483c-8a00-b90f1156d9c4' 
session_id = 'a404552c-d6ac-434b-aad7-4ad65ebeeb57'

url = f"{genos_url}/app/api/chat"
headers = {
    "Authorization": "Bearer <JWT 1>"
}
body = {
    "chat_id": chat_id,
    "session_id": session_id,
    "trace_id": trace_id,
    "thumbs": "up" # 긍정 피드백, "none" 이면 피드백 삭제
}
res = requests.post(f"{url}/chat/feedback/v2", headers=headers, json=body)
print(res.json())
  • 정상 출력은 아래와 같습니다.

{"code":0,"errMsg":"success","data":null}

부정 피드백

주관식

  • 채팅 정보 조회 시 feedback_type이 FILL_IN 이면 주관식 피드백으로 지정된 채팅입니다.

import requests

genos_url = 'GenOS 주소'
chat_id = 177 # 채팅 정보 조회 시 얻은 채팅 서비스 ID

# 아래는 피드백을 추가할 응답을 특정하기 위해 채팅 전송 요청 헤더의 "x-genos-trace-id"와 "x-genos-session-id"로 각각 지정
trace_id = '4844f357-52e6-483c-8a00-b90f1156d9c4' 
session_id = 'a404552c-d6ac-434b-aad7-4ad65ebeeb57'

url = f"{genos_url}/app/api/chat"
headers = {
    "Authorization": "Bearer <JWT 1>"
}
body = {
    "chat_id": chat_id,
    "session_id": session_id,
    "trace_id": trace_id,
    "thumbs": "down" # 부정피드백, "none" 이면 피드백 삭제
    "messages": [{ "comment": "The answer is unnecessarily long." }]
}
res = requests.post(f"{url}/chat/feedback/v2", headers=headers, json=body)
print(res.json())
  • 정상 출력은 위와 동일합니다.

객관식

  • 채팅 정보 조회 시 feedback_type이 CHOICE 이면 객관식 피드백으로 지정된 채팅입니다.

  • 채팅 정보 조회 시 얻을 수 있는 기타 피드백 관련 정보는 아래와 같습니다.

    • feedback_questions: list[str], 객관식 피드백 종류

    • allow_other_feedback: bool, 정해진 객관식 피드백 외에 다른 의견으로 피드백을 추가할 수 있는지 여부

    "feedback_type": "CHOICE",
    "feedback_questions": [
        "길어요",
        "틀렸어요"
    ],
    "allow_other_feedback": true,
...
  • 요청 API

    • messages에서 question에는 얻은 채팅 정보 조회 시 얻은 feedback_questions의 값을 그대로 넣고, allow_other_feedback이 true이면 "기타" 를 추가합니다.

    • is_checked가 실제로 사용자가 해당 피드백을 체크했는지에 대한 여부입니다.

    • comment는 is_checked가 true일 때만 값이 유효하며, 사용자가 부가적으로 추가한 의견입니다.

import requests

genos_url = 'GenOS 주소'
chat_id = 177 # 채팅 정보 조회 시 얻은 채팅 서비스 ID

# 아래는 피드백을 추가할 응답을 특정하기 위해 채팅 전송 요청 헤더의 "x-genos-trace-id"와 "x-genos-session-id"로 각각 지정
trace_id = '4844f357-52e6-483c-8a00-b90f1156d9c4' 
session_id = 'a404552c-d6ac-434b-aad7-4ad65ebeeb57'

url = f"{genos_url}/app/api/chat"
headers = {
    "Authorization": "Bearer <JWT 1>"
}
body = {
    "chat_id": chat_id,
    "session_id": session_id,
    "trace_id": trace_id,
    "thumbs": "down" # 부정피드백, "none" 이면 피드백 삭제
    "messages": [
        { "question": "길어요", "comment": "불필요해요", "is_checked": True },
        { "question": "틀렸어요", "comment": "", "is_checked": False },
        { "question": "기타", "comment": "", "is_checked": False }
    ]
}
res = requests.post(f"{url}/chat/feedback/v2", headers=headers, json=body)
print(res.json())
  • 정상 출력은 위와 동일합니다.

Last updated

Was this helpful?