NexaraNexara

Zoom встречи

Узнайте, как обрабатывать записи Zoom-встреч через Nexara API.

Записи Zoom-встреч и онлайн-переговоров — частый сценарий: из одной записи можно получить расшифровку, разделение по участникам и готовый протокол встречи. Ниже разберём блок за блоком, а в конце соберём всё в один запрос и поделимся лайфхаками.

Разделение на говорящих

Включите диаризацию (task=diarize) и укажите diarization_setting=telephonic. Этот режим хорошо работает, где участники часто перебивают друг друга и говорят одновременно, как это обычно и бывает на встречах.

curl https://api.nexara.ru/v1/audio/transcriptions \
  -H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
  -F "file=@meeting.mp3" \
  -F "model=whisper-1" \
  -F "task=diarize" \
  -F "diarization_setting=telephonic"

Модель whisper-1 — универсальная и хорошо подходит для встреч на разных языках. Число участников определяется автоматически.

Роли участников

Чтобы вместо безликих speaker_0/speaker_1 в расшифровке были понятные роли, добавьте roles=auto — модель сама подберёт подписи по содержанию разговора (например, Ведущий, Участник). Если роли известны заранее, можно передать их списком.

curl https://api.nexara.ru/v1/audio/transcriptions \
  -H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
  -F "file=@meeting.mp3" \
  -F "model=whisper-1" \
  -F "task=diarize" \
  -F "diarization_setting=telephonic" \
  -F "roles=auto"

Подробнее о режимах разметки — на странице спикеры и роли.

Аналитика встречи

Самое ценное — сразу получить из встречи структурированный результат. Добавьте prompt (и, для строгой структуры, json_schema). Модель получает расшифровку с ролями, поэтому учитывает, кто что говорил. Подробно про режимы — на странице LLM-анализ. Ниже — три частых сценария.

Протокол встречи

Готовый протокол, в котором будут указаны краткое резюме, принятые решения и задачи с ответственными.

{
  "type": "object",
  "properties": {
    "summary": { "type": "string", "description": "Краткое резюме встречи" },
    "decisions": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Принятые решения"
    },
    "action_items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "task": { "type": "string" },
          "owner": {
            "type": "string",
            "description": "Ответственный, если назван"
          }
        },
        "required": ["task"]
      }
    }
  },
  "required": ["summary", "decisions", "action_items"]
}

Prompt: Составь протокол встречи: резюме, решения и задачи с ответственными.

Краткое резюме

Самый простой вариант — без схемы, ответ придёт текстом.

Prompt: Сделай краткое резюме встречи в пяти предложениях.

Список задач

Только задачи, поставленные на встрече, — для трекера или таск-менеджера.

{
  "type": "object",
  "properties": {
    "action_items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "task": { "type": "string" },
          "owner": { "type": "string" },
          "deadline": { "type": "string", "description": "Срок, если назван" }
        },
        "required": ["task"]
      }
    }
  },
  "required": ["action_items"]
}

Prompt: Выпиши все задачи, поставленные на встрече, с ответственными и сроками.

Собираем всё вместе

Один запрос: распознавание whisper-1, диаризация в режиме telephonic, авто-роли и протокол встречи через prompt + json_schema.

curl https://api.nexara.ru/v1/audio/transcriptions \
  -H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
  -F "file=@meeting.mp3" \
  -F "model=whisper-1" \
  -F "task=diarize" \
  -F "diarization_setting=telephonic" \
  -F "roles=auto" \
  -F "prompt=Составь протокол встречи: резюме, решения и задачи с ответственными." \
  -F 'json_schema={"type":"object","properties":{"summary":{"type":"string"},"decisions":{"type":"array","items":{"type":"string"}},"action_items":{"type":"array","items":{"type":"object","properties":{"task":{"type":"string"},"owner":{"type":"string"}},"required":["task"]}}},"required":["summary","decisions","action_items"]}'
import json
import requests

schema = {
    "type": "object",
    "properties": {
        "summary": {"type": "string"},
        "decisions": {"type": "array", "items": {"type": "string"}},
        "action_items": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "task": {"type": "string"},
                    "owner": {"type": "string"},
                },
                "required": ["task"],
            },
        },
    },
    "required": ["summary", "decisions", "action_items"],
}

with open("meeting.mp3", "rb") as audio_file:
    response = requests.post(
        "https://api.nexara.ru/v1/audio/transcriptions",
        headers={"Authorization": "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"},
        files={"file": audio_file},
        data={
            "model": "whisper-1",
            "task": "diarize",
            "diarization_setting": "telephonic",
            "roles": "auto",
            "prompt": "Составь протокол встречи: резюме, решения и задачи с ответственными.",
            "json_schema": json.dumps(schema),
        },
    )

result = response.json()
print(result["llm_output"])

Node.js 18+:

import fs from "fs";

const schema = {
  type: "object",
  properties: {
    summary: { type: "string" },
    decisions: { type: "array", items: { type: "string" } },
    action_items: {
      type: "array",
      items: {
        type: "object",
        properties: {
          task: { type: "string" },
          owner: { type: "string" },
        },
        required: ["task"],
      },
    },
  },
  required: ["summary", "decisions", "action_items"],
};

const form = new FormData();
form.append("file", new Blob([fs.readFileSync("meeting.mp3")]), "meeting.mp3");
form.append("model", "whisper-1");
form.append("task", "diarize");
form.append("diarization_setting", "telephonic");
form.append("roles", "auto");
form.append("prompt", "Составь протокол встречи: резюме, решения и задачи с ответственными.");
form.append("json_schema", JSON.stringify(schema));

const response = await fetch(
  "https://api.nexara.ru/v1/audio/transcriptions",
  {
    method: "POST",
    headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
    body: form,
  },
);

const result = await response.json();
console.log(result.llm_output);
package main

import (
	"bytes"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

func main() {
	schema := `{"type":"object","properties":{"summary":{"type":"string"},"decisions":{"type":"array","items":{"type":"string"}},"action_items":{"type":"array","items":{"type":"object","properties":{"task":{"type":"string"},"owner":{"type":"string"}},"required":["task"]}}},"required":["summary","decisions","action_items"]}`

	file, _ := os.Open("meeting.mp3")
	defer file.Close()

	var buf bytes.Buffer
	writer := multipart.NewWriter(&buf)
	part, _ := writer.CreateFormFile("file", "meeting.mp3")
	io.Copy(part, file)
	writer.WriteField("model", "whisper-1")
	writer.WriteField("task", "diarize")
	writer.WriteField("diarization_setting", "telephonic")
	writer.WriteField("roles", "auto")
	writer.WriteField("prompt", "Составь протокол встречи: резюме, решения и задачи с ответственными.")
	writer.WriteField("json_schema", schema)
	writer.Close()

	req, _ := http.NewRequest("POST", "https://api.nexara.ru/v1/audio/transcriptions", &buf)
	req.Header.Set("Authorization", "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX")
	req.Header.Set("Content-Type", writer.FormDataContentType())

	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}
require "net/http"
require "uri"
require "json"

schema = {
  "type" => "object",
  "properties" => {
    "summary" => { "type" => "string" },
    "decisions" => { "type" => "array", "items" => { "type" => "string" } },
    "action_items" => {
      "type" => "array",
      "items" => {
        "type" => "object",
        "properties" => {
          "task" => { "type" => "string" },
          "owner" => { "type" => "string" },
        },
        "required" => ["task"],
      },
    },
  },
  "required" => ["summary", "decisions", "action_items"],
}

uri = URI("https://api.nexara.ru/v1/audio/transcriptions")

request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX"
request.set_form(
  [
    ["file", File.open("meeting.mp3")],
    ["model", "whisper-1"],
    ["task", "diarize"],
    ["diarization_setting", "telephonic"],
    ["roles", "auto"],
    ["prompt", "Составь протокол встречи: резюме, решения и задачи с ответственными."],
    ["json_schema", schema.to_json],
  ],
  "multipart/form-data",
)

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)["llm_output"]

В ответе придёт расшифровка с ролями и готовый протокол встречи:

{
  "transcription": {
    "task": "diarize",
    "segments": [
      {
        "start": 0.0,
        "end": 3.4,
        "text": "Давайте начнём. Сегодня обсудим релиз.",
        "speaker": "Ведущий"
      },
      {
        "start": 3.8,
        "end": 7.2,
        "text": "Я подготовлю тестовую сборку к пятнице.",
        "speaker": "Участник"
      }
    ]
  },
  "llm_output": {
    "summary": "Команда обсудила предстоящий релиз и распределила задачи.",
    "decisions": ["Выпустить релиз в конце недели"],
    "action_items": [
      { "task": "Подготовить тестовую сборку", "owner": "Участник" }
    ]
  }
}

Лайфхаки

  • Режим telephonic. На встречах участники часто перебивают друг друга — этот режим справляется с такими записями лучше стандартного.
  • Авто-роли. На встречах состав участников заранее не известен, поэтому roles=auto обычно удобнее фиксированного списка, но если у вас есть список, лучше его передать для лучшего качества.
  • Длинные встречи — асинхронный режим. Записи совещаний бывают долгими, не держите HTTP-соединение открытым всё это время.
  • Большие файлы — по ссылке. Запись встречи удобно отдавать по URL (например, pre-signed ссылке на хранилище), а не загружать файлом.
  • Вложенные схемы. Для задач с ответственными используйте массив объектов в json_schema — так каждую задачу можно сразу разложить на поля.

Диаризация, разметка ролей и LLM-анализ тарифицируются дополнительно к стоимости распознавания. Итоговую стоимость смотрите на странице тарифов.

On this page