ガイド: 分析(発言量・話者ごとの統計)
あなたは会議や配信の「誰がどれだけ話したか」「いつ盛り上がったか」を集計できます。
どのデータを使うか
| 知りたいこと | データ | 注意 |
|---|---|---|
| 話者ごとの発言数・文字数 | 書き出しの JSON の utterances(final / refined だけが入っている) |
セッションが終わった後の書き出しで集計すると確実 |
| 時間帯ごとの発言の多さ | startedAt(エポックミリ秒) |
startedAt は、文字おこしくんが最初の音声を受け取った時刻を表す。startedAt は、話者をまたいで比べてよい |
| 話している時間 | WebSocket の speech_started / speech_ended(トラック単位) |
リアルタイムに受け取って自分で数える。書き出しには入らない |
| 音声認識にかけた音声の秒数(利用量) | GET /v1/usage(usage:read の API キー) |
利用量は UTC の日ごとに出る。文字おこしくんは、利用量をトラックごとに数えて足す(2 人が同時に話せば 2 倍になる) |
partialは集計に使わないでください。同じ発話が何度も届くので、数えると何倍にもなります。- 話者分離が推定した話者(
inferred)の発話は、後の改訂で別のspeakerIdの話者に付け替わることがあります。リアルタイムに集計するなら、utteranceIdごとに最新の改訂だけを数え直してください。 - 話者が分からない発話(書き出しでは
speakerIdが無い)は、トラック単位でまとめるか「不明」として別に数えます。
例: 書き出しから話者ごとの発言量を出す(Node)
// node stats.mjs transcript.json
import { readFileSync } from "node:fs";
const transcript = JSON.parse(readFileSync(process.argv[2], "utf8"));
const stats = new Map(); // 話者 → { name, count, chars, first, last }
for (const u of transcript.utterances) {
const key = u.speakerId ?? `track:${u.trackId}`;
const s = stats.get(key) ?? { name: u.speakerName, count: 0, chars: 0, first: u.startedAt, last: u.startedAt };
s.count += 1;
s.chars += [...u.text].length; // コードポイント数で数える
s.last = Math.max(s.last, u.startedAt);
stats.set(key, s);
}
const total = [...stats.values()].reduce((sum, s) => sum + s.chars, 0) || 1;
for (const s of [...stats.values()].sort((a, b) => b.chars - a.chars)) {
console.log(`${s.name}\t${s.count} 回\t${s.chars} 文字\t${((s.chars / total) * 100).toFixed(1)}%`);
}
例: 5 分ごとの発言数(Python)
import json, sys
from collections import Counter
from datetime import datetime, timezone, timedelta
transcript = json.load(open(sys.argv[1], encoding="utf-8"))
jst = timezone(timedelta(hours=9))
buckets = Counter()
for u in transcript["utterances"]:
t = datetime.fromtimestamp(u["startedAt"] / 1000, jst)
buckets[t.replace(minute=t.minute - t.minute % 5, second=0, microsecond=0)] += 1
for t, n in sorted(buckets.items()):
print(t.strftime("%H:%M"), "#" * n)
例: 話している時間をリアルタイムに数える
const speakingSince = new Map(); // trackId → 話し始めた時刻(受信時刻)
const speakingMs = new Map(); // trackId → 合計
function onMessage(m) {
if (m.type === "speech_started" && !speakingSince.has(m.trackId)) speakingSince.set(m.trackId, Date.now());
if (m.type === "speech_ended" || m.type === "track_closed") {
const since = speakingSince.get(m.trackId);
if (since !== undefined) speakingMs.set(m.trackId, (speakingMs.get(m.trackId) ?? 0) + Date.now() - since);
speakingSince.delete(m.trackId);
}
if (m.type === "welcome") {
// 再接続: 切断中の分は分からないので、今話しているトラックから数え直す
speakingSince.clear();
for (const trackId of m.speakingTrackIds) speakingSince.set(trackId, Date.now());
}
}
あなたは welcome.tracks と track_opened で届くトラックのうち、mode: "single" のトラックの speakerId から、トラックと話者の対応を取れます(mixed のトラックには複数人の声が入っています)。
プライバシー
発言量の集計も、参加者の発言に関する情報です。集計は参加者に伝えた目的の範囲で使い、集計を公開する場合は本人の了承を得てください(プライバシーポリシー)。
コーディングエージェント向けのプロンプト
文字おこしくんの書き出し JSON(GET /v1/sessions/:id/transcript)から、話者ごとの発言回数・文字数・割合と、5 分ごとの発言数を集計して CSV に出すスクリプトを書いてください。
仕様: https://moji.kaitoranma.com/docs/guides/analytics.md
- utterances には final / refined だけが入っている。speakerId が無い発話は track:<trackId> として数える。
- 文字数はコードポイント数で数える。時刻は startedAt(エポックミリ秒)を Asia/Tokyo に変換する。
- リアルタイム版を作る場合は partial を数えず、utteranceId ごとに最新の改訂だけを数え直す(inferred の話者は付け替わる)。
- 入力 JSON の小さな fixture でテストする。