# EasyLink EasyDoc-Extract 文档信息抽取

## 概述

EasyDoc-Extract 是一个智能文档信息抽取模型。上传文档（PDF/图片）后，模型会自动从中抽取结构化信息，返回 JSON 格式的抽取结果，适用于合同要素提取、发票解析、表单数据录入等场景。

本接口为异步模式：提交任务后获得任务 ID，通过轮询接口查询结果。

更多细节请参考 [EasyLink 官方文档](https://docs.easylink-ai.com/docs/capabilities/extraction/universal)。

**请求地址**：`https://api.modelverse.cn/v1/tasks/submit`

## 支持模型

- `easydoc-extract`

## 抽取模式说明

| 模式 | 触发条件 | 说明 |
| :--- | :--- | :--- |
| 封闭式 | `parameters.json_schema` 有效 JSON | 按指定 Schema 抽取字段，结果为结构化 JSON 对象 |
| 自定义 Prompt | `parameters.json_schema` 无效 + `parameters.prompt_cus` 非空 | 按自然语言描述抽取，结果为字符串 |
| 开放式 | 两者均为空 | 自动识别文档类型并抽取所有可识别字段 |

## 请求参数

### 提交任务

| 字段 | 类型 | 是否必需 | 说明 |
| :--- | :--- | :--- | :--- |
| model | string | 是 | 模型名称，固定为 `easydoc-extract` |
| input.img_url | string | 是 | 待抽取文件的 URL，支持 PDF / JPG / PNG / BMP / TIFF |
| parameters.json_schema | string | 否 | 封闭式模式：JSON Schema 格式字符串，指定需要抽取的字段 |
| parameters.prompt_cus | string | 否 | 自定义 Prompt 模式：自然语言描述抽取需求 |

`parameters.json_schema` 示例（封闭式模式，抽取合同关键字段）：

```json
{
  "type": "object",
  "properties": {
    "甲方名称": {"type": "string"},
    "乙方名称": {"type": "string"},
    "合同金额": {"type": "string"},
    "签订日期": {"type": "string"},
    "合同编号": {"type": "string"}
  }
}
```

### 查询任务状态

| 字段 | 类型 | 是否必需 | 说明 |
| :--- | :--- | :--- | :--- |
| task_id | string | 是 | 提交任务时返回的任务 ID |

## 响应字段

### 提交响应

| 字段 | 类型 | 说明 |
| :--- | :--- | :--- |
| output.task_id | string | 任务 ID，用于后续查询 |
| request_id | string | 请求 ID |

### 状态查询响应

| 字段 | 类型 | 说明 |
| :--- | :--- | :--- |
| output.task_status | string | 任务状态：`Pending` / `Running` / `Success` / `Failure` |
| output.result | array | 抽取结果列表（成功时返回），每个元素对应一个文件，见下方说明 |
| output.error_message | string | 错误信息（失败时返回） |
| usage.page_count | integer | 文件总页数，用于计费 |

`output.result` 每个元素的结构：

| 字段 | 类型 | 说明 |
| :--- | :--- | :--- |
| page_count | integer | 当前文件页数 |
| key_index | array | 抽取字段名列表（封闭式模式返回） |
| extracted_fields | object/string | 抽取结果：封闭式模式为 JSON 对象，自定义 Prompt 模式为字符串 |

## 示例

### Curl 示例

**提交任务（封闭式模式）：**

```bash
curl -X POST https://api.modelverse.cn/v1/tasks/submit \
  -H "Authorization: Bearer {api_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "easydoc-extract",
    "input": {
      "img_url": "https://example.com/contract.pdf"
    },
    "parameters": {
      "json_schema": "{\"type\":\"object\",\"properties\":{\"甲方名称\":{\"type\":\"string\"},\"乙方名称\":{\"type\":\"string\"},\"合同金额\":{\"type\":\"string\"},\"签订日期\":{\"type\":\"string\"}}}"
    }
  }'
```

**提交任务（开放式模式）：**

```bash
curl -X POST https://api.modelverse.cn/v1/tasks/submit \
  -H "Authorization: Bearer {api_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "easydoc-extract",
    "input": {
      "img_url": "https://example.com/document.pdf"
    }
  }'
```

**查询任务状态：**

```bash
curl "https://api.modelverse.cn/v1/tasks/status?task_id={task_id}" \
  -H "Authorization: Bearer {api_key}"
```

### Python 示例

```python
import json
import time
import requests

api_key = "******"  # 替换为你的 API Key
base_url = "https://api.modelverse.cn/v1"

headers = {
    "Authorization": f"Bearer {api_key}",
    "Content-Type": "application/json",
}

# 提交任务（封闭式模式）
json_schema = {
    "type": "object",
    "properties": {
        "甲方名称": {"type": "string"},
        "乙方名称": {"type": "string"},
        "合同金额": {"type": "string"},
        "签订日期": {"type": "string"},
    },
}

submit_data = {
    "model": "easydoc-extract",
    "input": {
        "img_url": "https://example.com/contract.pdf",
    },
    "parameters": {
        "json_schema": json.dumps(json_schema, ensure_ascii=False),
    },
}

resp = requests.post(f"{base_url}/tasks/submit", headers=headers, json=submit_data)
task_id = resp.json()["output"]["task_id"]
print(f"task_id: {task_id}")

# 轮询查询结果
while True:
    result = requests.get(
        f"{base_url}/tasks/status",
        headers=headers,
        params={"task_id": task_id},
    ).json()

    status = result["output"]["task_status"]
    print(f"status: {status}")

    if status == "Success":
        print("抽取结果:")
        for item in result["output"]["result"]:
            print(f"  页数: {item.get('page_count')}")
            print(f"  字段名: {item.get('key_index')}")
            print(f"  抽取内容: {item.get('extracted_fields')}")
        print(f"总页数: {result['usage']['page_count']}")
        break
    elif status == "Failure":
        print(f"失败: {result['output'].get('error_message')}")
        break

    time.sleep(5)
```

## 响应示例

**提交成功：**

```json
{
  "output": {
    "task_id": "b_extract_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  },
  "request_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```

**任务完成（封闭式模式）：**

```json
{
  "output": {
    "task_id": "b_extract_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "task_status": "Success",
    "result": [
      {
        "page_count": 5,
        "key_index": ["甲方名称", "乙方名称", "合同金额", "签订日期"],
        "extracted_fields": {
          "甲方名称": "北京示例科技有限公司",
          "乙方名称": "上海示例贸易有限公司",
          "合同金额": "人民币壹拾万元整",
          "签订日期": "2024年1月1日"
        }
      }
    ],
    "submit_time": 1700000000,
    "finish_time": 1700000060
  },
  "usage": {
    "duration": 60,
    "page_count": 5
  },
  "request_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```

## 使用说明

1. **文件格式**：支持 PDF、JPG、PNG、BMP、TIFF，通过 `input.img_url` 传入文件的公网访问地址。
2. **抽取模式选择**：
   - 字段明确时推荐使用**封闭式模式**（传入 `parameters.json_schema`），结果最为准确。
   - 需要灵活描述抽取需求时使用**自定义 Prompt 模式**（传入 `parameters.prompt_cus`）。
   - 无特定需求时可使用**开放式模式**（两者均不传），自动识别并抽取文档中的所有可识别字段。
3. **json_schema 格式**：传入的是 JSON Schema 的**序列化字符串**（即对 JSON 对象调用 `json.dumps()` 后的结果），而非 JSON 对象本身。
4. **结果格式**：抽取结果通过 `output.result` 字段返回，为 JSON 数组，每个元素对应一个输入文件。
5. **计费**：按文件总页数计费，页数通过 `usage.page_count` 返回。
