# Match practice parts to learner content

Match one learner-provided keyword set or content excerpt to up to three relevant IELTS Buddy Parts. The query stays as raw content; explicitly requested Part number and question type can constrain results. Use subject=listening and requiresAudio=true for listening practice. Results include audioAvailable, a factual match mode, stable question_part contentRef, and an attributed browser practice entry. The server does not start a session or generate answers.

- Capability ID: `practice.match_parts`
- Operation: `ielts_practice_match_parts`
- Category: 题库与练习
- Endpoint: `POST /api/v1/agent/capabilities/ielts_practice_match_parts`
- Authentication: Bearer Token（通过 /api/v1/agent-bindings 绑定账号）
- Required scopes: question_bank:read
- Side effect: read
- Human documentation: https://ieltsbuddy.igopx.cn/developers/api/ielts_practice_match_parts

## Request parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `difficulty` | string | 否 | Optional practice difficulty filter. |
| `excludePartIds` | number[] | 否 | Part ids already ruled out during this selection. |
| `hardTagQueries` | unknown[] | 否 | Only literal taxonomy constraints the user explicitly requested. These filters are strict, not inferred from the query. |
| `limit` | number | 否 | Maximum matched Parts to return, 1-3. |
| `partNumber` | number | 否 | Exact IELTS Part or Section number when the learner specifies one. |
| `preferUnseen` | boolean | 否 | Prefer an unseen Part when relevance is otherwise comparable. |
| `questionType` | string | 否 | Optional Part format: blank when all slots are fill-in, choice when all slots are selected-response. |
| `query` | string | 是 | The learner's original keywords or content. Preserve it verbatim; do not convert it into taxonomy filters. |
| `requiresAudio` | boolean | 否 | Require a published listening Part with a configured audio URL. Use true for listening practice recommendations. |
| `subject` | string | 否 | Optional IELTS subject filter. |

## Request example

```bash
curl -X POST \
  'https://work.ieltsbuddy.igopx.cn/api/v1/agent/capabilities/ielts_practice_match_parts' \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $IELTS_BUDDY_TOKEN" \
  --data '{"difficulty":"","excludePartIds":[],"hardTagQueries":[],"limit":3,"preferUnseen":false,"questionType":"","query":"<query>","requiresAudio":false,"subject":""}'
```

## Request schema

```json
{
  "type": "object",
  "properties": {
    "difficulty": {
      "default": "",
      "description": "Optional practice difficulty filter.",
      "type": "string",
      "enum": [
        "",
        "基础",
        "核心",
        "冲刺"
      ]
    },
    "excludePartIds": {
      "default": [],
      "description": "Part ids already ruled out during this selection.",
      "maxItems": 30,
      "type": "array",
      "items": {
        "type": "integer",
        "exclusiveMinimum": 0,
        "maximum": 9007199254740991
      }
    },
    "hardTagQueries": {
      "default": [],
      "description": "Only literal taxonomy constraints the user explicitly requested. These filters are strict, not inferred from the query.",
      "maxItems": 6,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1,
        "maxLength": 80
      }
    },
    "limit": {
      "default": 3,
      "description": "Maximum matched Parts to return, 1-3.",
      "type": "integer",
      "minimum": 1,
      "maximum": 3
    },
    "partNumber": {
      "description": "Exact IELTS Part or Section number when the learner specifies one.",
      "type": "integer",
      "minimum": 1,
      "maximum": 4
    },
    "preferUnseen": {
      "default": false,
      "description": "Prefer an unseen Part when relevance is otherwise comparable.",
      "type": "boolean"
    },
    "questionType": {
      "default": "",
      "description": "Optional Part format: blank when all slots are fill-in, choice when all slots are selected-response.",
      "type": "string",
      "enum": [
        "",
        "blank",
        "choice"
      ]
    },
    "query": {
      "type": "string",
      "minLength": 2,
      "maxLength": 500,
      "description": "The learner's original keywords or content. Preserve it verbatim; do not convert it into taxonomy filters."
    },
    "requiresAudio": {
      "default": false,
      "description": "Require a published listening Part with a configured audio URL. Use true for listening practice recommendations.",
      "type": "boolean"
    },
    "subject": {
      "default": "",
      "description": "Optional IELTS subject filter.",
      "type": "string",
      "enum": [
        "",
        "listening",
        "reading",
        "speaking",
        "writing"
      ]
    }
  },
  "required": [
    "difficulty",
    "excludePartIds",
    "hardTagQueries",
    "limit",
    "preferUnseen",
    "questionType",
    "query",
    "requiresAudio",
    "subject"
  ],
  "additionalProperties": false
}
```

## Response envelope

接口结果位于 data；code 为 0 表示调用成功。


```json
{
  "code": 0,
  "data": {},
  "message": "ok"
}
```

## Errors

| HTTP | Code | Meaning |
| --- | --- | --- |
| 400 | `40001` | 请求参数不符合接口契约 |
| 401 | `40101` | 缺少或使用了无效的访问令牌 |
| 403 | `40301` | 访问令牌不包含接口所需权限 |
| 404 | `40401` | 接口或目标资源不存在 |
