Pango AIPango AI Team
Module 01: Chatbot Service (API)1.5 GraphQL API & Code Examples
Module 01 • API Gateway

1.5.3 Gửi Câu Hỏi Cho AI (SendAiMessage)

MUTATION Yêu cầu Bearer Token

Gửi câu hỏi của người dùng tới mô hình AI. Hệ thống sẽ tiếp nhận yêu cầu và phản hồi ngay lập tức các mã định danh (chatSessionId, assistantMessageId, idempotencyKey) để Client kết nối Firebase Firestore nhận stream câu trả lời.

Non-Blocking Architecture

API này không đợi AI sinh toàn bộ câu trả lời mới trả về HTTP response. Do đó, request hoàn tất rất nhanh (thường dưới 300ms), loại bỏ hoàn toàn nguy cơ HTTP Timeout khi người dùng đặt câu hỏi phức tạp.


💻 Chi Tiết Truy Vấn & Mã Nguồn Mẫu

GraphQL Mutation & Payload:

GraphQL Mutation

SendAiMessage.gql
mutation SendAiMessage($request: AiChatRequestDtoInput!, $images: [Upload!]) {
  sendAiMessage(request: $request, images: $images) {
    chatSessionId
    chatRequestId
    idempotencyKey
    userMessageId
    assistantMessageId
    channel
    requestStatus
  }
}

Variables (JSON)

Variables
{
  "request": {
    "question": "VUS có những khóa học nào dành cho trẻ em?",
    "channel": "WEB_APP",
    "chatSessionId": null,
    "agentCode": "buddy",
    "topic": "qa",
    "idempotencyKey": "idem-1740200010000-abc1234"
  },
  "images": []
}

Phản Hồi Thành Công (JSON Response)

Response 200 OK
{
  "data": {
    "sendAiMessage": {
      "chatSessionId": "6a7d45b5aa2c04f1151bee2b",
      "chatRequestId": "req-1740200010000-abc1234",
      "idempotencyKey": "idem-1740200010000-abc1234",
      "userMessageId": "6a8913448201e708713c6e47",
      "assistantMessageId": "6a8913448201e708713c6e48",
      "channel": "WEB_APP",
      "requestStatus": "PROCESSING"
    }
  }
}

TypeScript / Axios:

sendAiMessage.ts
import axios from 'axios';

export interface SendAiMessageResult {
  chatSessionId: string;
  chatRequestId: string;
  idempotencyKey: string;
  userMessageId: string;
  assistantMessageId: string;
  channel: string;
  requestStatus: 'PROCESSING' | 'COMPLETED' | 'FAILED';
}

/**
 * Gửi câu hỏi của người dùng tới Pango AI Chatbot
 * @param apiUrl Endpoint GraphQL Gateway
 * @param token JWT Access Token sau khi ExternalLogin
 * @param question Nội dung câu hỏi người dùng nhập
 * @param activeSessionId Session ID hiện tại (null nếu là câu hỏi mở đầu phiên mới)
 * @param agentCode Mã định danh agent (mặc định 'buddy')
 * @param channel Kênh gửi tin ('WEB_APP' | 'MOBILE_APP')
 */
export async function sendAiMessage(
  apiUrl: string = process.env.VITE_API_URL!,
  token: string,
  question: string,
  activeSessionId: string | null = null,
  agentCode: string = 'buddy',
  channel: string = 'WEB_APP'
): Promise<SendAiMessageResult> {
  const query = `
    mutation SendAiMessage($request: AiChatRequestDtoInput!, $images: [Upload!]) {
      sendAiMessage(request: $request, images: $images) {
        chatSessionId
        chatRequestId
        idempotencyKey
        userMessageId
        assistantMessageId
        channel
        requestStatus
      }
    }
  `;

  // Tạo khóa Idempotency duy nhất chống gửi trùng
  const idempotencyKey = `idem-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;

  const variables = {
    request: {
      question,
      channel,
      chatSessionId: activeSessionId || null,
      agentCode,
      uploads: null,
      imageUrls: null,
      webPageUrls: null,
      staffInfo: null,
      topic: 'qa',
      times: null,
      idempotencyKey,
    },
    images: [],
  };

  const response = await axios.post(
    apiUrl,
    {
      query,
      variables,
      operationName: 'SendAiMessage',
    },
    {
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`,
        'orgId': process.env.VITE_ORG_ID,
        'x-org-id': process.env.VITE_ORG_ID,
        'x-client-type': 'app',
        'x-external-private-key': process.env.VITE_EXTERNAL_PRIVATE_KEY,
      },
    }
  );

  return response.data?.data?.sendAiMessage;
}

📑 Bảng Tham Số (Schema Definition)

AiChatRequestDtoInput

Thuộc TínhKiểu Dữ LiệuBắt BuộcMô Tả
questionString!Nội dung câu hỏi người dùng nhập
channelString!Định danh kênh gửi ("WEB_APP", "MOBILE_APP")
chatSessionIdStringKhôngID phiên chat đang tiếp diễn. Nếu gửi null, hệ thống tự sinh phiên mới
agentCodeString!Mã bot (mặc định "buddy")
topicStringKhôngChủ đề hội thoại (mặc định "qa")
idempotencyKeyString!Khóa chống lặp (UUID hoặc chuỗi ngẫu nhiên có timestamp)

Dữ Liệu Trả Về (SendAiMessageResponse)

Thuộc TínhKiểu Dữ LiệuMô Tả
chatSessionIdString!ID phiên chat (Lưu lại để gửi kèm các tin nhắn sau)
assistantMessageIdString!Rất quan trọng: ID tin nhắn của bot dùng để lắng nghe Stream trên Firestore
userMessageIdString!ID tin nhắn của người dùng trong hệ cơ sở dữ liệu
requestStatusString!Trạng thái ban đầu ("PROCESSING")