JA ▾
API キーを取得

LLM API 会議メモ:Python でトランスクリプトから要約へ

LLM API の会議議事録を取得するには、短いシステムプロンプト付きでチャット完了エンドポイントに議事録を送信し、返信を読み取ります。品質を決定する要素は、プロンプト、固定された出力形式、および長時間の録音の処理方法です。

更新日:

等角の会議テーブルと、その上にチェックリストカードに凝縮される会話風吹き出し

主要ポイント

  • 1 回のチャット完了呼び出しで議事録がノートに変換され、JSON モードにより固定された形式が得られます。
  • 100,000 トークンのウィンドウは、チャンク化なしでほとんどの会議議事録を保持できます。
  • モデルに、所有者や日付が不明な場合は null を書き込ませ、それらを捏造しないようにしてください。
  • フルキーでは 8 件の同時リクエストが可能で、これは並列で 1 日分の会議を処理するのに十分な数です。

4つのステップによるパイプライン

  1. 録音して文字起こしします。 音声文字変換ツールを任意に使用してください。この API はテキストのみに対応しており、音声は受け付けません。
  2. テキストを準備します。 会議のタイトル、日付、出席者名を含むヘッダーを追加します。話者ラベルがあれば、非常に役立ちます。
  3. モデルを呼び出します。 必要なセクションを定義するシステムプロンプト付きで議事録を送信します。
  4. 結果を保存して共有します。 JSON を保存し、Markdown または HTML としてレンダリングし、チームが閲覧する場所に投稿してください。

エンドポイントは POST /v1/chat/completions on https://api.whisperapis.com/v1、モデルIDは uncensored です。Base URLとAPI キーを変更すれば、どのOpenAI SDKでも動作します。先に音声認識を設定する必要がある場合は、クイックスタートドキュメントがテキスト側の処理をカバーしており、Whisper互換のサーバーであればトランスクリプトを提供できます。

JSON モードで構造化された議事録を取得

自由形式の要約は読み取りには適していますが、パイプラインは保存およびフィルタリング可能なフィールドを必要とします。response_format を {"type": "json_object"} に設定して有効にする JSON モードは、モデルに単一の JSON オブジェクトを返させます。システムプロンプトでキーを説明し、プロンプトが形状を定義するため、そこで JSON の単語を使用してください。

import json
from openai import OpenAI

client = OpenAI(base_url="https://api.whisperapis.com/v1", api_key="YOUR_KEY")

SYSTEM = """You write meeting notes from raw transcripts.
Use only what the transcript says. If an owner or a date is not stated, write null.
Reply with a JSON object with the keys: summary (string), decisions (array of strings),
action_items (array of objects with task, owner, due), open_questions (array of strings)."""

transcript = open("standup.txt", encoding="utf-8").read()

resp = client.chat.completions.create(
    model="uncensored",
    messages=[
        {"role": "system", "content": SYSTEM},
        {"role": "user", "content": "Transcript:\n\n" + transcript},
    ],
    response_format={"type": "json_object"},
    temperature=0.2,
)

notes = json.loads(resp.choices[0].message.content)
for item in notes["action_items"]:
    print(item["owner"], "->", item["task"], item["due"])

そのプロンプトの2行が大半の役割を果たします。「トランスクリプトの内容のみを使用する」という指示は、モデルが妥当な詳細を捏造するのを防ぎます。「所有者や日付が明記されていない場合はnullを記載する」という指示は、モデルが「不明」と答えるための正当な方法を提供し、でたらめな名前を捏造するよりも優れています。0.2のような低いtemperatureは実行ごとのばらつきを減らし、同じトランスクリプトが2回処理される可能性がある場合に役立ちます。

常に防御的に解析してください。json.loads を try ブロックで囲み、失敗時は 1 回だけ再試行してください。失敗したリクエストには課金されず、1 回の再試行でほとんどの一時的なフォーマットエラーが修正されます。

待っている人に結果をストリーミング

誰かがボタンをクリックしてメモを待っている場合、到着したテキストを逐次表示します。streamをtrueに設定すると、APIはサーバー送信イベント(SSE)を送信し、ストリームの末尾に使用量が含まれます。

from openai import OpenAI

client = OpenAI(base_url="https://api.whisperapis.com/v1", api_key="YOUR_KEY")
transcript = open("standup.txt", encoding="utf-8").read()

stream = client.chat.completions.create(
    model="uncensored",
    messages=[
        {"role": "system", "content": "Write concise meeting notes in Markdown with headings: Summary, Decisions, Actions."},
        {"role": "user", "content": transcript},
    ],
    stream=True,
    temperature=0.3,
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

ストリーミングには運用上の注意点があります。ストリーミングリクエストはストリームが終了するまで同時実行スロットを保持します。フルキーでは同時に 8 件、トライアルキーでは 2 件までです。ブラウザタブが閉じてもサーバーが読み込みを続けていると、そのスロットは使用中のままになるため、クライアントが切断されたらアップストリームリクエストをキャンセルしてください。

誰も見ないバッチジョブではストリーミングをスキップしてください。通常のリクエストの方がシンプルで、回答が1つのオブジェクトとして取得できます。

長時間の会議と 100k ウィンドウ

コンテキストウィンドウは100,000トークンで、プロンププトと応答を合わせてカウントします。1時間の会議は約9,000語、または約12,000トークンに相当するため、終日ワークショップでも1つのリクエストに収まります。これにより、通常必要なmap-and-reduce設計(トランスクリプトを分割し、各部分を要約し、さらにそれらの要約をまとめる設計)が不要になります。この設計の各フェーズでは詳細が失われ、5分目で下された決定が50分目で修正された場合でも、誤って報告されやすくなります。

送信する前にサイズを必ず確認してください。英語テキストの場合、文字数を 4 で割る粗いチェックがあります。制限に近い場合、クリーンなオプションは 2 つあります。

  • アジェンダ項目などの自然な区切りで分割し、それぞれを要約してから、それらをマージする最終呼び出しに部分的な議事録を渡します。
  • 会議開始前の長い雑談など、重要ではないコンテンツを削除します。

リクエストボディの最大サイズは8 MBですが、テキストには実用的な制限ではありません。プロンプトは学習に使用されないため、録音に社内計画や顧客名が含まれる場合に重要です。

出力を変更するプロンプトの詳細

議事録の読者を明記する

"会議を欠席したエンジニア向けのメモ"と"エグゼクティブチームへの1段落の更新"では、得られる詳細が異なります。読者をシステムプロンプトに記載してください。

モデルに出席者リストを提供する

音声認識は名前をしばしば誤変換します。「出席者:Priya Nair(PM)、Tom Ortiz(バックエンド)、Lena Fischer(デザイン)」のような行があれば、モデルは「Tom Ortis」と書かれたトランスクリプトを修正し、タスクを正しい人物に割り当てることができます。

決定事項とアクションを別々に求める

決定はグループが合意した事項です。アクションは 1 人の人物が行う事項です。両方が 1 つのリストにある場合、読者はそれをスキップします。キーを分離することで区別を明確にし、モデルはそれを一貫して従います。

機密情報は保持する

実際の会議には率直な表現、特定の人物への不満、法的懸念、激しい論争が含まれます。ここで扱うモデルは、法的な成人向け利用に対してコンテンツフィルタなしで回答するようにチューニングされているため、却下するのではなく発言の要約を行います。出力の所有権はあなたにあるため、その場にいなかった人々に渡す前にメモを確認してください。

1 日分の会議全体を実行する

1 日 6 回の会議を録音するチームは、それらを並列で処理できます。8 件の同時リクエストで、一度に最大 8 件を送信できます。セマフォ付きの小さなワーカープールで対応できます。フルキーの 1 分あたりの制限は 300 リクエストで、会議パイプラインが到達することはありません。

Node は Python と同じ方法で動作します。以下はストリーミングなしのバージョンです。

import OpenAI from "openai";
import { readFileSync } from "node:fs";

const client = new OpenAI({
  baseURL: "https://api.whisperapis.com/v1",
  apiKey: process.env.API_KEY,
});

const transcript = readFileSync("standup.txt", "utf8");

const resp = await client.chat.completions.create({
  model: "uncensored",
  messages: [
    { role: "system", content: "Summarize the meeting in five bullets, then list action items with owners." },
    { role: "user", content: transcript },
  ],
  temperature: 0.2,
});

console.log(resp.choices[0].message.content);

HTTP 429 を受信した場合、エラーコードを読み取ってください。concurrency は一度にリクエストが多すぎること、rate_limited は 1 分あたりのリクエストが多すぎることを意味します。Retry-After: 5 ヘッダー付きの 429 は一時的な過負荷を示しており、5 秒待ってから再試行してください。

信頼する前に議事録を確認する

生成された議事録は、誤っていても自信を持って読めるため、人間が気づくことに依存するのではなく、パイプラインにいくつかの安価なチェックを組み込んでください。

  • 引用の確認。 各アクションアイテムについて短い引用を求め、その引用がトランスクリプト内にあるか検索してください。引用が見つからない場合は、その項目が捏造された可能性を示すサインです。
  • 所有者チェック。 各所有者を出席者リストと比較し、リストにない名前をフラグ付けしてください。
  • 空チェック。 決定のない会議はあり得ますが、40 分の文字起こしデータで結果のすべての配列が空の場合、入力が切り捨てられたか、間違ったファイルが使用されたことを意味します。
  • 長さの確認。 トランスクリプトの約5分の1より長いメモは、もはやメモではありません。プロンプトを絞り込むか、max_tokensを設定してください。

各保存された結果の横に、X-Request-Idレスポンスヘッダーをログに記録してください。誰かがアクションアイテムが間違っていると主張した場合、その識別子を使ってリクエストを検索し、トランスクリプトと比較できます。元のトランスクリプトをメモの隣に保持するのは安価で、ほとんどの紛争を迅速に解決します。

コストと最初のテスト

料金は入力トークン100万トークンあたり$0.15、出力トークン100万トークンあたり$0.50です。12,000トークンのトランスクリプトと800トークンのメモセットを持つ1時間の会議の場合、入力で$0.0018、出力で$0.0004、合計$0.0022かかります。このレートでは$1で約454回の会議がカバーできます。チャージは$20から開始され、カードまたは暗号資産で支払い可能です。詳細は料金ページをご覧ください。

試すには、サインアップして API キーページ を開いてください。1 人につき 1 回限り、カード不要で 7 日間有効な $0.50 のワンクリックトライアルが利用可能です。実際の議事録 1 つで上記の Python スクリプトを実行し、アクションアイテムが会議で起きたことと一致するまでプロンプトを調整してください。その後、残りをループしてください。

質問と回答

音声録音を直接送信できますか?

いいえ。API はテキストのみを受け取ります。録音を音声文字変換ツールで文字起こしし、その後に文字起こしデータを送信してください。

出力形式をどのようにして一貫させますか?

システムプロンプトで各キーを明示し、JSON モードを使用してください。温度を低く設定し、結果を try ブロックで解析し、失敗時は1回だけ再試行します。

1つのリクエストに収まるトランスクリプトの長さはどのくらいですか?

プロンプトと応答の合計でコンテキストウィンドウは100,000トークンです。これは通常、1日分のワークショップをカバーするのに十分です。

トランスクリプトはトレーニングに使用されますか?

いいえ、プロンプトはトレーニングには使用されません。

1回のミーティングあたりのコストはいくらですか?

12,000トークンのトランスクリプトと800トークンの要約を含む1時間のミーティングの場合、入力トークンと出力トークンがそれぞれ100万トークンあたり$0.15と$0.50の場合、コストは約$0.0022です。

キーはフォーム 1 つで手に入ります

アカウントを作成し、キーをコピーし、ベースURLを変更します。これでセットアップは完了です。

API キーを取得