Спикеры и роли
Как разделить аудио по говорящим и разметить их роли.
Диаризация разделяет аудио по говорящим: API определяет, какой фрагмент речи кому принадлежит, и присваивает каждому сегменту метку говорящего (speaker_0, speaker_1) — или понятную роль (Клиент, Агент). Концептуально режим описан на странице диаризация.
Разделение по говорящим
Передайте task=diarize в запросе на POST /v1/audio/transcriptions. Замените nx-XXXXXXXXXXXXXXXXXXXXXXXX на свой API-ключ, а meeting.mp3 — на путь к вашему файлу.
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@meeting.mp3" \
-F "model=whisper-1" \
-F "task=diarize"import requests
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",
},
)
print(response.json())Node.js 18+:
import fs from "fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("meeting.mp3")]), "meeting.mp3");
form.append("model", "whisper-1");
form.append("task", "diarize");
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
console.log(await response.json());package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
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.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"
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"],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts response.bodyВ ответе каждый сегмент содержит метку говорящего в поле speaker:
{
"task": "diarize",
"language": "ru",
"duration": 12.4,
"text": "Здравствуйте! Чем могу помочь? Хочу оформить возврат.",
"segments": [
{
"start": 0.0,
"end": 2.1,
"text": "Здравствуйте! Чем могу помочь?",
"speaker": "speaker_0"
},
{
"start": 2.5,
"end": 5.0,
"text": "Хочу оформить возврат.",
"speaker": "speaker_1"
}
]
}Метки говорящих сохраняются во всех форматах ответа. Например, при response_format=text:
Speaker 0: Здравствуйте! Чем могу помочь?
Speaker 1: Хочу оформить возврат.Минимальная длительность аудио для диаризации — 3 секунды. Более короткие
записи выдают ошибку 400. Если вы заранее знаете число говорящих, передайте
num_speakers — это повысит качество разделения.
Разметка ролей
По умолчанию говорящие обозначаются безликими метками speaker_0, speaker_1. Разметка ролей заменяет их на понятные роли — например, Клиент и Агент. Она включается параметром roles и доступна только вместе с диаризацией (task=diarize). Поддерживаются три режима:
Автоматический
roles=auto — модель сама определяет и назначает подходящие роли на основе содержания разговора.
Список ролей
JSON-массив строк, например ["Клиент", "Агент"] — вы отправляете набор ролей, а модель распределяет их между говорящими.
Роли с описаниями
JSON-объект {"Клиент": "Клиент, который обратился в поддержку", "Агент": "Оператор поддержки"} — фиксированные роли с описаниями, которые помогают модели точнее их распознать.
Достаточно добавить поле roles к запросу на диаризацию. Значение — либо auto, либо JSON-строка со списком или объектом ролей.
curl https://api.nexara.ru/v1/audio/transcriptions \
-H "Authorization: Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@call.mp3" \
-F "model=whisper-1" \
-F "task=diarize" \
-F 'roles=["Клиент", "Агент"]'import requests
with open("call.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",
"roles": '["Клиент", "Агент"]',
},
)
print(response.json())import fs from "fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("call.mp3")]), "call.mp3");
form.append("model", "whisper-1");
form.append("task", "diarize");
form.append("roles", JSON.stringify(["Клиент", "Агент"]));
const response = await fetch(
"https://api.nexara.ru/v1/audio/transcriptions",
{
method: "POST",
headers: { Authorization: "Bearer nx-XXXXXXXXXXXXXXXXXXXXXXXX" },
body: form,
},
);
console.log(await response.json());package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
file, _ := os.Open("call.mp3")
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, _ := writer.CreateFormFile("file", "call.mp3")
io.Copy(part, file)
writer.WriteField("model", "whisper-1")
writer.WriteField("task", "diarize")
writer.WriteField("roles", `["Клиент", "Агент"]`)
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"
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("call.mp3")],
["model", "whisper-1"],
["task", "diarize"],
["roles", ["Клиент", "Агент"].to_json],
],
"multipart/form-data",
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts response.bodyВ результате метки speaker_N заменяются на назначенные роли:
{
"task": "diarize",
"segments": [
{
"start": 0.0,
"end": 2.1,
"text": "Здравствуйте! Чем могу помочь?",
"speaker": "Агент"
},
{
"start": 2.5,
"end": 5.0,
"text": "Хочу оформить возврат.",
"speaker": "Клиент"
}
]
}Если модель определила больше говорящих, чем было передано в списке, остальные
спикеры будут помечены как неизвестные. Например, если в записи говорило 4
человека, а в списке передано только 2, остальные будут помечены как
unknown_1 и unknown_2.
Ограничения
- В одном запросе можно задать не более 10 ролей.
- Каждое название роли — непустая строка длиной не более 64 символов, роли не должны повторяться.
- Описание роли (в режиме с описаниями) не должно превышать 500 символов.
Разметка ролей тарифицируется дополнительно к стоимости диаризации. Если
разметку не удалось применить (например, в записи не оказалось говорящих),
надбавка не списывается, а в ответе остаются исходные метки speaker_N.
Стоимость услуги смотрите на странице тарифов.
Что учитывать
- Хранение ключа. Не вставляйте API-ключ прямо в код. Храните его в переменной окружения (например,
export NEXARA_API_KEY="nx-...") и ссылайтесь на неё. Подробнее — на странице аутентификации.