> For the complete documentation index, see [llms.txt](https://docs.maiagent.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maiagent.ai/tech/api-integration/api_knowledge.md).

# Presigned 檔案上傳模式

以 Presigned URL 兩段式上傳檔案到 MaiAgent：先取得預簽網址把檔案直接送進儲存空間，再把結果註冊為知識庫檔案或對話附件。

目前在 MaiAgent 上需要上傳檔案的地方有兩處

1. 知識庫文件上傳
2. 訊息用到的附件上傳

## Presigned 上傳模式

MaiAgent 支援 **Presigned 上傳模式**，此模式允許客戶端直接將檔案上傳至雲端儲存（如 S3），而不需經過伺服器中繼。使用此模式時，伺服器僅負責生成具有時效性與安全性的 Presigned URL，客戶端利用該 URL 直接進行檔案上傳。

#### 與傳統上傳模式的差異：

1. **資料流向**
   * 傳統模式：檔案須經過伺服器，再由伺服器上傳到雲端儲存。
   * Presigned 模式：檔案由客戶端直接上傳至雲端，避免伺服器處理檔案的負擔。
2. **效能與成本**
   * 傳統模式可能導致伺服器資源消耗過大，並增加資料傳輸成本。
   * Presigned 模式減少伺服器負載，提高效能並降低傳輸成本。
3. **安全性**
   * Presigned URL 包含簽名與有效期，確保上傳請求僅限於授權用戶在指定時間內使用。

此模式特別適合大檔案或高頻率上傳的場景，提升效率的同時保持高度的安全性。

#### 預簽上傳流程圖

```mermaid
sequenceDiagram
    participant Client as Client
    participant Server as Sever
    participant S3 as Storage(S3)

    Note over Client,S3: 預簽上傳流程
    
    Client->>+Server: 1. 請求預簽 URL
    Note right of Client: POST /api/v1/upload-presigned-url/<br/>包含檔名、類型、大小
    
    Server->>Server: 2. 驗證請求參數
    Server->>Server: 3. 產生預簽參數
    Note right of Server: 設定有效期、權限等
    Server-->>-Client: 4. 回傳預簽 URL 和參數
    
    Client->>+S3: 5. 使用預簽 URL 直接上傳
    Note right of Client: POST 到預簽 URL<br/>帶上 fields 全部欄位 + 檔案 binary
    S3-->>-Client: 6. 上傳完成回應 (HTTP 204)
    
    opt 知識庫場景：註冊檔案到知識庫
        Client->>+Server: 7. 用 fields.key 註冊
        Note right of Client: POST /api/v1/knowledge-bases/{KB_ID}/files/<br/>body 帶 file = fields.key
        Server->>Server: 8. 建立 KnowledgeBaseFile 並排程解析
        Server-->>-Client: 9. 回傳 file record
    end
```

#### 傳統上傳流程圖

傳統上傳只提供給訊息附件：`POST /api/v1/attachments-upload/`，以 multipart 表單把檔案放在 `file` 欄位，回應是附件物件。知識庫檔案只能用 Presigned 模式上傳。

```mermaid
sequenceDiagram
    participant Client as Client
    participant Server as Server
    participant S3 as Storage(S3)

    Note over Client,S3: 傳統上傳流程
    
    Client->>+Server: 1. 上傳檔案請求
    Note right of Client: POST /api/v1/attachments-upload/<br/>multipart 表單，檔案放在 file 欄位
    
    Server->>Server: 2. 驗證檔案<br/>(大小、類型、格式)
    Server->>Server: 3. 儲存暫存檔
    
    Server->>+S3: 4. 上傳檔案至 S3
    Note right of Server: 使用 AWS SDK<br/>上傳暫存檔
    S3-->>-Server: 5. 上傳成功回應
    
    Server->>Server: 6. 清理暫存檔
    Server-->>-Client: 7. 回傳上傳結果
    Note left of Server: 回傳附件物件（id、file 等）
```

***

#### Presigned 上傳檔案

以下描述如何使用 MaiAgent 提供的 API 完成檔案的 Presigned 上傳過程。**知識庫場景**走 Step 1 → 2 → 3；**訊息附件場景**走 Step 1 → 2 之後，再依[建立訊息附件](#message-attachments)取得附件物件，才能帶進訊息。

#### 1. **取得 Presigned URL**

**Endpoint**

`POST https://api.maiagent.ai/api/v1/upload-presigned-url/`

**說明**

客戶端向伺服器發送請求，取得一個 Presigned URL，該 URL 用於直接上傳檔案至雲端儲存。

**請求參數**

| 參數名稱        | 型別        | 必填 | 說明                                                                                                     |
| ----------- | --------- | -- | ------------------------------------------------------------------------------------------------------ |
| `filename`  | `string`  | 是  | 上傳檔案的名稱                                                                                                |
| `modelName` | `string`  | 是  | <p>模組名稱，用於分類上傳的檔案用途<br></p><p>知識庫請填寫<code>chatbot-file</code></p><p>訊息附件請填寫<code>attachment</code></p> |
| `fieldName` | `string`  | 是  | 檔案欄位名稱。知識庫（`chatbot-file`）與訊息附件（`attachment`）都請填寫`file`                                                |
| `fileSize`  | `integer` | 是  | 檔案大小（以位元組為單位），必須與實際 binary 大小完全一致，否則 Step 2 會被 S3 拒絕                                                   |

**範例請求**

```bash
curl --location 'https://api.maiagent.ai/api/v1/upload-presigned-url/' \
--header 'Authorization: Api-Key YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
    "filename": "document.pdf",
    "modelName": "chatbot-file",
    "fieldName": "file",
    "fileSize": 178329
}'
```

**回應範例**

```json
{
    "url": "https://s3.ap-northeast-1.amazonaws.com/whizchat-media-prod-django.playma.app",
    "fields": {
        "key": "media/chatbots/chatbot-file/86572e41-16ba-45dd-b049-28c69f77ffb0.pdf",
        "x-amz-algorithm": "AWS4-HMAC-SHA256",
        "x-amz-credential": "ASIATIVCN4X5XXXXXXXX/20260522/ap-northeast-1/s3/aws4_request",
        "x-amz-date": "20260522T020727Z",
        "x-amz-security-token": "IQoJb3JpZ2luX2VjEEoa...（很長的 STS token）",
        "policy": "eyJleHBpcmF0aW9uIjog...（base64 policy）",
        "x-amz-signature": "617e10d1db034ab351d3735de5c56287ecc926dbfe5b5884d2ce67deed363004"
    }
}
```

{% hint style="warning" %}
**Production 環境使用 AWS STS 短期憑證**（`x-amz-credential` 以 `ASIA` 開頭），回應的 `fields` 物件\*\*會包含 \*\***`x-amz-security-token`** 欄位。Step 2 上傳時必須一字不漏帶上所有 `fields` 欄位（含 `x-amz-security-token`），少帶會被 S3 拒絕。

Presigned URL **有效期約 1 小時**，請在取得後盡快使用。
{% endhint %}

***

#### 2. **上傳檔案至雲端儲存**

**說明**

使用從上述步驟取得的 `url` 和 `fields`，直接將檔案上傳至雲端儲存。**`fields` 物件中每一個欄位都必須帶上**（包含 `x-amz-security-token`）；不要寫死欄位清單，若 AWS 後續加新欄位，動態組裝可避免漏帶。

**上傳成功時 S3 回應 HTTP 204 No Content（空 body）**。

{% hint style="info" %}
部署使用 Azure Blob 儲存時，Step 1 的回應會多帶 `method: "PUT"` 與 `headers`：請改用 PUT 把檔案本體直接送到 `url`，並帶上 `headers` 裡的每個標頭，成功時回 HTTP 201。這時 `fields` 只有 `key`，後續步驟一樣使用 `fields.key`。回應沒有 `method` 時，一律照下方的 POST 方式上傳。
{% endhint %}

**範例請求（curl，含 STS token）**

```bash
curl --location 'https://s3.ap-northeast-1.amazonaws.com/whizchat-media-prod-django.playma.app' \
--form 'key="media/chatbots/chatbot-file/86572e41-16ba-45dd-b049-28c69f77ffb0.pdf"' \
--form 'x-amz-algorithm="AWS4-HMAC-SHA256"' \
--form 'x-amz-credential="ASIATIVCN4X5XXXXXXXX/20260522/ap-northeast-1/s3/aws4_request"' \
--form 'x-amz-date="20260522T020727Z"' \
--form 'x-amz-security-token="IQoJb3JpZ2luX2VjEEoa..."' \
--form 'policy="eyJleHBpcmF0aW9uIjog..."' \
--form 'x-amz-signature="617e10d1db034ab351d3735de5c56287ecc926dbfe5b5884d2ce67deed363004"' \
--form 'file=@"/path/to/document.pdf"'
```

**範例請求（Python，動態組裝，推薦做法）**

```python
import requests

# Step 1: 取得 Presigned URL
presign = requests.post(
    'https://api.maiagent.ai/api/v1/upload-presigned-url/',
    headers={'Authorization': 'Api-Key YOUR_API_KEY'},
    json={
        'modelName': 'chatbot-file',
        'fieldName': 'file',
        'filename': 'document.pdf',
        'fileSize': 178329,
    },
).json()

# Step 2: 把 fields 整個塞，所有欄位（含 x-amz-security-token）自動帶上
with open('/path/to/document.pdf', 'rb') as f:
    s3_response = requests.post(
        presign['url'],
        data=presign['fields'],
        files={'file': f},
    )
assert s3_response.status_code == 204, s3_response.text

file_key = presign['fields']['key']  # Step 3 要用的值
```

***

#### 3. **註冊檔案到知識庫（知識庫場景才需要）**

**Endpoint**

`POST https://api.maiagent.ai/api/v1/knowledge-bases/{knowledgeBasePk}/files/`

**說明**

Step 2 完成後，檔案 binary 已在 S3，但**還沒進入知識庫**。需要呼叫此 API 把上傳結果註冊成 `KnowledgeBaseFile`，系統才會開始解析、切片、建索引。

{% hint style="warning" %}
**`file` 欄位必須填入 Step 1 回應的 `fields.key`（MaiAgent S3 內部相對路徑），不是任意外部 URL**。若你拿到的是外部 URL（例如夥伴系統的下載連結），請先用 `GET` 下載檔案，再走 Step 1 → 2 → 3。
{% endhint %}

**路徑參數**

| 參數名稱              | 型別       | 說明              |
| ----------------- | -------- | --------------- |
| `knowledgeBasePk` | `string` | 知識庫的唯一識別碼（UUID） |

**請求參數**

| 參數名稱                            | 型別       | 必填 | 說明                                                                                                                                                    |
| ------------------------------- | -------- | -- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `files`                         | `array`  | 是  | 要建立的檔案清單，可一次批次建立多個                                                                                                                                    |
| `files[].filename`              | `string` | 是  | 原始檔名                                                                                                                                                  |
| `files[].file`                  | `string` | 是  | Step 1 回應的 `fields.key`（相對路徑，**不是 URL**）                                                                                                              |
| `files[].parser`                | `string` | 否  | 指定 parser 的 UUID。不填時，PDF、Word（.docx）、PowerPoint（.pptx）會自動偵測解析器（見 [Parser 解析工具](/tech/quickstart/parser.md#auto-detect-and-billing)），其他格式使用該檔案類型的預設解析器 |
| `files[].labels`                | `array`  | 否  | 檔案標籤（{id, name} 物件陣列）                                                                                                                                 |
| `files[].rawUserDefineMetadata` | `object` | 否  | 用戶自定義 metadata                                                                                                                                        |

**範例請求**

```bash
curl --location 'https://api.maiagent.ai/api/v1/knowledge-bases/86401a64-ad89-4847-a709-f4ccfa0af7b9/files/' \
--header 'Authorization: Api-Key YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
    "files": [
        {
            "filename": "document.pdf",
            "file": "media/chatbots/chatbot-file/86572e41-16ba-45dd-b049-28c69f77ffb0.pdf"
        }
    ]
}'
```

**回應範例**

```json
[
    {
        "id": "86572e41-16ba-45dd-b049-28c69f77ffb0",
        "filename": "document.pdf",
        "file": "https://media.maiagent.ai/media/chatbots/chatbot-file/86572e41-16ba-45dd-b049-28c69f77ffb0.pdf",
        "fileType": "pdf",
        "knowledgeBase": {
            "id": "86401a64-ad89-4847-a709-f4ccfa0af7b9",
            "name": "我的知識庫"
        },
        "size": 178329,
        "status": "initial",
        "parser": {
            "id": "535c5b86-0534-4d0a-abfe-82f3d37e769a",
            "name": "MaiAgent Parser",
            "provider": "maiagent",
            "order": 0,
            "supportsDiarization": false
        },
        "labels": [],
        "rawUserDefineMetadata": {},
        "createdAt": "1779425272000"
    }
]
```

**檔案狀態（`status`）**

| 值            | 說明          |
| ------------ | ----------- |
| `initial`    | 剛建立、等待處理    |
| `processing` | 正在解析、切片、建索引 |
| `done`       | 處理完成、可被檢索   |
| `failed`     | 處理失敗        |

#### 建立訊息附件（訊息附件場景才需要） <a href="#message-attachments" id="message-attachments"></a>

訊息附件在 Step 1 填 `"modelName": "attachment"`、`"fieldName": "file"`。Step 2 上傳完成後，把檔案登記成對話的附件，取得附件物件。

**Endpoint**

`POST https://api.maiagent.ai/api/v1/conversations/{conversationId}/attachments/`

**請求參數**

| 參數名稱       | 型別       | 必填 | 說明                                         |
| ---------- | -------- | -- | ------------------------------------------ |
| `file`     | `string` | 是  | Step 1 回應的 `fields.key`                    |
| `filename` | `string` | 是  | 原始檔名                                       |
| `type`     | `string` | 否  | `image`（圖片）、`audio`（音訊）或 `other`（其他檔案，預設值） |

**範例請求**

```bash
curl --location 'https://api.maiagent.ai/api/v1/conversations/8e44604a-8278-4c96-96b5-3e5a0548d738/attachments/' \
--header 'Authorization: Api-Key YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
    "file": "media/chat/attachment/0b6f3c2e-6a51-4f43-9d3f-6e2f1c9a7b10.pdf",
    "filename": "document.pdf",
    "type": "other"
}'
```

**回應範例**（HTTP 201）

```json
{
    "id": "5d1f2a7c-3b8e-4c19-9a62-1e4b7f0c8d35",
    "type": "other",
    "filename": "document.pdf",
    "file": "https://…/document.pdf",
    "expiresAt": null
}
```

送訊息時，把回應的 `id`、`type`、`filename`、`file` 放進附件陣列：[建立對話與訊息](/tech/api-integration/conversations-and-messages.md) 的 `attachments`，或 [completions](/tech/api-integration/dui-hua-yu-xun-xi-hui-fu-chuan-liu-tong-bu.md) 的 `message.attachments`。還沒有對話 ID 時（例如用 completions 開新對話），可以改用上方的傳統上傳 `POST /api/v1/attachments-upload/` 取得附件物件。

#### 訊息附件怎麼被處理 <a href="#attachment-processing" id="attachment-processing"></a>

附件物件本身只是「上傳好的檔案」；它掛到哪一則訊息、`type` 填什麼，決定模型怎麼讀它。`POST /conversations/{conversationId}/attachments/` 與 `POST /attachments-upload/` 建立的是同一種物件，前者預先綁定對話、後者在送訊息時才綁定，之後的處理完全相同；一個附件只會屬於一則訊息。

| `type`                                                       | 模型怎麼讀                                                                                                                                                                                                                                                                                                              | 什麼時候可用                                                                                                                                               |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `image`（`.png`、`.jpg`、`.jpeg`、`.gif`、`.webp`、`.bmp`、`.tiff`） | 以圖片直接送進 Agent 的語言模型，不解析、不建索引；Agent 必須使用支援多模態的模型，否則回覆「您選的 LLM 不支援多模態」。單次請求送進模型的圖片數由平台設定（預設 20 張），超過的不會送                                                                                                                                                                                                             | 送出訊息的當下                                                                                                                                              |
| `other`（PDF、Word、PowerPoint、Excel／CSV、HTML、TXT／Markdown）     | 依 Agent 的回答模式而定。**對話模式等非 Agent 模式**：上傳當下就建立一份只屬於這段對話的檔案，用知識庫的解析器解析、切片、向量化，回答時和知識庫一起檢索（取 Agent 的檢索區塊最大數量），內容不會整份塞進 prompt。**Agent 模式**：不會自動解析；Agent 在回答時依問題用附件工具處理——`parse_attachment_content` 解析後分段讀取、`read_attachment_full_text` 讀純文字檔全文、`ingest_attachment` 建立索引後再檢索（這幾個工具可在 Agent 的進階設定個別關閉）。Excel／CSV 都會轉成表格文字 | 非 Agent 模式：解析完成後；`waitForAttachments`（completions，預設 `true`）會等解析完成再開始回答，設 `false` 時這次回答可能還讀不到附件。Agent 模式：送出訊息後由 Agent 當場處理，`waitForAttachments` 沒有作用 |
| `audio`（`.wav`、`.mp3`、`.m4a`、`.aac`）                         | 以語音轉文字解析器轉成逐字稿，之後與 `other` 相同                                                                                                                                                                                                                                                                                      | 同上                                                                                                                                                   |

與知識庫檔案的差別：

|              | 訊息附件                                                                                                                  | 知識庫檔案                |
| ------------ | --------------------------------------------------------------------------------------------------------------------- | -------------------- |
| 範圍           | 只有同一段對話的後續訊息能檢索到；其他對話看不到                                                                                              | 掛載該知識庫的所有 Agent、所有對話 |
| 重複使用         | 同一段對話不必再傳；開新對話要重新上傳                                                                                                   | 上傳一次永久可用             |
| 標籤、元資料、查詢元資料 | 無                                                                                                                     | 有，可依標籤與查詢元資料限制範圍     |
| 解析器          | 非 Agent 模式依副檔名用預設解析器（對話平台開啟 OCR 時改用可辨識圖片的解析器）；Agent 模式由 Agent 在 `parse_attachment_content` 選擇                         | 上傳時可指定               |
| 保留           | 依平台的附件清理設定到期自動刪除（含解析副本），見使用者手冊 [Credits 計費制度](https://docs.maiagent.ai/others/credits#attachment-and-derived-storage) | 直到刪除                 |
| 刪除           | 透過 [對話附件管理 API](/tech/api-integration/endpoint-behaviors/conversation-attachments.md)（需開通檔案庫），或等到期                    | 知識庫檔案 API            |
| 計費           | 解析與向量化依知識庫規則計費，儲存計入「對話附件儲存」                                                                                           | 解析、向量化、檔案與向量儲存       |

要讓多段對話重複問同一批文件，請上傳到知識庫而不是當附件；要處理一次性的檔案（例如使用者的報價單）才用附件。

***

## 常見錯誤與排查

**Q：S3 回 `The AWS Access Key Id you provided does not exist in our records`**

A：Step 2 漏帶 `x-amz-security-token`，或寫死了過期的 access key / S3 endpoint。請永遠用 Step 1 即時回傳的 `url` 跟 `fields`，把 `fields` 整個物件原封不動帶到 Step 2。

**Q：S3 回 `Policy Condition failed`**

A：通常是 Step 1 的 `fileSize` 跟實際 binary 大小不一致；重新計算實際檔案大小，重跑 Step 1。

**Q：Step 3 回 400，說 `file` 欄位有問題**

A：`file` 必須填 Step 1 回應的 `fields.key`（例如 `media/chatbots/chatbot-file/xxx.pdf`），不是 Step 3 回應的 full URL，也不是任意外部 URL。

**Q：我已經有一個外部 URL（例如夥伴系統的下載連結），可以直接給 Step 3 嗎？**

A：不行。Step 3 的 `file` 欄位是 MaiAgent 自家 S3 的相對 key。請先用 `GET` 把該外部 URL 的檔案抓下來，再走 Step 1 → 2 → 3。

**Q：Step 3 之後檔案一直 `status: processing`**

A：解析時間視檔案大小、類型而定，大檔案可能要數分鐘。可用 `GET /api/v1/knowledge-bases/{KB_ID}/files/{file_id}/` 輪詢狀態，等到 `done` 才能被 Agent 檢索。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.maiagent.ai/tech/api-integration/api_knowledge.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
