TH ▾
รับคีย์ API

บันทึกการประชุม API LLM: บทถอดเสียงสู่สรุปใน Python

เพื่อรับบันทึกการประชุมจาก llm api คุณส่งไฟล์ถอดเสียงไปยังเอนด์พอยต์ chat completions พร้อมพรอมต์ระบบสั้นๆ แล้วอ่านคำตอบ ส่วนสำคัญที่กำหนดคุณภาพคือ พรอมต์ รูปแบบเอาต์พุตที่ตายตัว และวิธีการจัดการกับไฟล์ถอดเสียงที่ยาว

อัปเดต

โต๊ะประชุมแบบไอโซเมตริกพร้อมฟองคำพูดที่รวมกันเป็นการ์ดรายการตรวจสอบด้านบน

ประเด็นสำคัญ

  • การเรียก chat completion หนึ่งครั้งแปลงบทถอดเสียงเป็นบันทึก; โหมด JSON ให้รูปแบบฟิลด์ที่แน่นอน
  • หน้าต่าง 100,000 โทเคนรองรับบทถอดเสียงการประชุมส่วนใหญ่โดยไม่ต้องแบ่งส่วน
  • บอกโมเดลให้เขียน null สำหรับเจ้าของและวันที่ที่ขาดหายไป เพื่อให้โมเดลไม่สร้างข้อมูลขึ้นมาเอง
  • คีย์เต็มรูปแบบอนุญาตให้ทำคำขอพร้อมกัน 8 คำขอ เพียงพอสำหรับการประมวลผลการประชุมหนึ่งวันแบบขนาน

ขั้นตอนการทำงานสี่ขั้นตอน

  1. บันทึกและถอดเสียง ใช้เครื่องมือแปลงเสียงเป็นข้อความใดก็ได้ API นี้รับเฉพาะข้อความและไม่รองรับไฟล์เสียง
  2. เตรียมข้อความ เพิ่มส่วนหัวที่มีชื่อกำหนดการประชุม วันที่ และรายชื่อผู้เข้าร่วม หากมีป้ายระบุผู้พูดจะช่วยได้มาก
  3. เรียกโมเดล ส่งบทถอดเสียงพร้อมระบบพรอมต์ที่กำหนดส่วนต่างๆ ที่คุณต้องการ
  4. จัดเก็บและแชร์ผลลัพธ์ บันทึก JSON, แสดงผลเป็น Markdown หรือ HTML, โพสต์ในที่ที่ทีมอ่าน

เอนด์พอยต์คือ POST /v1/chat/completions บน https://api.whisperapis.com/v1, ID โมเดลคือ uncensored และ SDK ของ OpenAI ใดๆ ก็ใช้ได้หลังจากเปลี่ยน Base URL และคีย์ หากคุณต้องตั้งค่าเครื่องมือแปลงเสียงเป็นข้อความก่อน เอกสาร เริ่มต้นอย่างรวดเร็ว จะครอบคลุมส่วนข้อความ และเซิร์ฟเวอร์ที่เข้ากันได้กับ Whisper ใดๆ ก็สามารถให้บทบันทึกได้

รับบันทึกที่มีโครงสร้างด้วยโหมด JSON

สรุปแบบอิสระเหมาะสำหรับการอ่าน แต่ขั้นตอนการทำงานต้องการฟิลด์ที่สามารถจัดเก็บและกรองได้ โหมด JSON เปิดใช้งานด้วย response_format ตั้งค่าเป็น {"type": "json_object"} ทำให้โมเดลส่งคืนออบเจกต์ 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"])

บรรทัดสองบรรทัดในพรอมต์นี้ทำหน้าที่ส่วนใหญ่ "ใช้เฉพาะสิ่งที่อยู่ในบันทึกการสนทนา" ช่วยป้องกันไม่ให้โมเดลเพิ่มรายละเอียดที่สมเหตุสมผลแต่ไม่ถูกต้อง "หากไม่มีการระบุเจ้าของหรือวันที่ ให้เขียน null" ให้วิธีที่ถูกต้องตามกฎหมายในการบอกว่าโมเดลไม่ทราบข้อมูล ซึ่งดีกว่าการสร้างชื่อขึ้นมาเอง อุณหภูมิต่ำเช่น 0.2 ลดความแปรปรวนระหว่างการรัน ซึ่งคุณต้องการเมื่อไฟล์บันทึกการสนทนาเดียวกันอาจถูกประมวลผลสองครั้ง

ควรแยกวิเคราะห์อย่างระมัดระวังเสมอ ห่อหุ้ม json.loads ไว้ใน try block และเมื่อเกิดข้อผิดพลาดให้ลองใหม่หนึ่งครั้ง คำขอที่ล้มเหลวจะไม่ถูกคิดเงิน และการลองใหม่เพียงครั้งเดียวสามารถแก้ไขข้อผิดพลาดการจัดรูปแบบแบบครั้งเดียวส่วนใหญ่ได้

สตรีมผลลัพธ์ไปยังคนที่รอ

หากใครคลิกปุ่มและรอรับบันทึก ให้แสดงข้อความเมื่อมาถึง ตั้งค่า stream เป็น true และ API จะส่งเหตุการณ์ที่ส่งโดยเซิร์ฟเวอร์; การใช้งานถูกรวมไว้ที่ท้ายสตรีม

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 รายการ หากแท็บเบราว์เซอร์ปิดและเซิร์ฟเวอร์ของคุณยังคงอ่านอยู่ ช่องนั้นจะยังคงใช้งานอยู่ ดังนั้นให้ยกเลิกคำขอ upstream เมื่อไคลเอนต์ตัดการเชื่อมต่อ

สำหรับงานแบทช์ที่ไม่มีคนดู ให้ข้ามสตรีมมิง คำขอธรรมดาง่ายกว่าและคุณได้รับคำตอบทั้งหมดในออบเจกต์เดียว

การประชุมที่ยาวนานและหน้าต่าง 100k

หน้าต่างบริบทมีขนาด 100,000 โทเคน โดยนับรวมพรอมต์และคำตอบรวมกัน การประชุมหนึ่งชั่วโมงมีคำประมาณ 9,000 คำ หรือประมาณ 12,000 โทเคน ดังนั้นเวิร์กช็อปตลอดทั้งวันจึงสามารถใส่ในคำขอเดียวได้ วิธีนี้กำจัดรูปแบบการออกแบบแบบ map-and-reduce ที่คุณต้องแบ่งไฟล์ถอดเสียง สรุปแต่ละส่วน แล้วสรุปผลสรุปเหล่านั้น ซึ่งแต่ละขั้นตอนของการออกแบบดังกล่าวจะสูญเสียรายละเอียด และการตัดสินใจที่เกิดขึ้นที่นาทีที่ 5 และแก้ไขที่นาทีที่ 50 อาจถูกรายงานผิดพลาดได้ง่าย

ยังคงตรวจสอบขนาดก่อนส่ง การตรวจสอบแบบหยาบคือจำนวนตัวหารด้วยสี่สำหรับข้อความภาษาอังกฤษ หากคุณอยู่ใกล้ขีดจำกัด มีสองตัวเลือกที่สะอาด:

  • แบ่งที่จุดแตกตามธรรมชาติเช่น รายการวาระการประชุม สรุปแต่ละรายการและส่งบันทึกบางส่วนไปยังการเรียกครั้งสุดท้ายที่รวมพวกมัน
  • ละเว้นเนื้อหาที่ไม่สำคัญเช่น ช่วงยาวของการพูดคุยเล็กๆ น้อยๆ ก่อนการประชุมเริ่มต้น

ขนาดสูงสุดของ body ของคำขอคือ 8 MB ซึ่งไม่ใช่ข้อจำกัดในทางปฏิบัติสำหรับข้อความ พรอมต์ไม่ถูกใช้ในการฝึก ซึ่งสำคัญหากการบันทึกเสียงมีแผนภายในหรือชื่อลูกค้า

รายละเอียดพรอมต์ที่เปลี่ยนผลลัพธ์

บอกว่าบันทึกสำหรับใคร

"บันทึกสำหรับวิศวกรที่พลาดการประชุม" ได้รายละเอียดต่างจาก "อัปเดตย่อหน้าเดียวสำหรับทีมผู้บริหาร" เขียนผู้อ่านเข้าไปในระบบพรอมต์

ให้รายชื่อผู้เข้าร่วมกับโมเดล

Speech-to-text มักทำให้ชื่อเพี้ยน บรรทัดเช่น "Attendees: Priya Nair (PM), Tom Ortiz (backend), Lena Fischer (design)" ช่วยให้โมเดลซ่อม transcript ที่บอกว่า "Tom Ortis" และแน่งานกับบุคคลที่ถูกต้อง

ขอการตัดสินใจและการกระทำแยกกัน

การตัดสินใจคือสิ่งที่กลุ่มตกลงกัน การกระทำคือสิ่งที่บุคคลหนึ่งจะทำ เมื่อทั้งสองอยู่ในรายการเดียว ผู้อ่านข้ามมัน คีย์แยกทำให้ความแตกต่างชัดเจน และโมเดลทำตามอย่างสม่ำเสมอ

เก็บข้อมูลสำคัญไว้

การประชุมจริงมักมีภาษาที่ตรงไปตรงมา การบ่นถึงบุคคลเฉพาะ ความกังวลทางกฎหมาย และการโต้แย้งอย่างรุนแรง โมเดลนี้ถูกปรับให้ตอบโดยไม่ปฏิเสธเนื้อหาสำหรับการใช้งานของผู้ใหญ่ตามกฎหมาย จึงสรุปสิ่งที่พูดไปแทนที่จะปฏิเสธ คุณยังคงเป็นเจ้าของผลลัพธ์ ดังนั้นจึงควรตรวจสอบบันทึกก่อนส่งให้ผู้ที่ไม่ได้เข้าร่วมการประชุม

การจัดการประชุมตลอดทั้งวัน

ทีมที่บันทึกการประชุมหกครั้งต่อวันสามารถประมวลผลแบบขนานได้ ด้วยคำขอพร้อมกัน 8 คำขอ สามารถส่งได้ครั้งละสูงสุดแปดรายการ; คอลเลกชันของ worker ขนาดเล็กพร้อม semaphore ก็เพียงพอแล้ว ขีดจำกัดอัตราต่อนาทีสำหรับคีย์เต็มคือ 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 หมายถึงมีคำขอมากเกินไปในครั้งเดียว ดังนั้นให้ลดขนาด pool ลง rate_limited หมายถึงมีคำขอมากเกินไปต่อนาที คำตอบ 429 พร้อม header Retry-After: 5 บ่งชี้ถึงการโอเวอร์โหลดชั่วคราว; รอห้าวินาทีแล้วลองอีกครั้ง

การตรวจสอบบันทึกก่อนที่คุณจะไว้วางใจ

บันทึกที่สร้างขึ้นอ่านดูมั่นใจแม้ว่าจะผิด ดังนั้นจึงควรสร้างการตรวจสอบที่ใช้ทรัพยากรน้อยไม่กี่รายการลงในสายการผลิตแทนที่จะพึ่งพามนุษย์ให้สังเกต

  • ตรวจสอบคำพูดอ้างอิง ขอคำพูดอ้างอิงสั้นๆ พร้อมรายการการกระทำแต่ละรายการ แล้วค้นหาในบันทึกการสนทนาเพื่อหาคำพูดนั้น หากคำพูดอ้างอิงไม่ปรากฏ แสดงว่ารายการนั้นถูกสร้างขึ้นมา
  • ตรวจสอบเจ้าของ เปรียบเทียบเจ้าของแต่ละรายการกับรายชื่อผู้เข้าร่วมของคุณและระบุชื่อที่ไม่ได้อยู่ในรายการ
  • ตรวจสอบความว่าง การประชุมที่ไม่มีผลการตัดสินใจเป็นไปได้ แต่ผลลัพธ์ที่มีชุดข้อมูลว่างทั้งหมดในบทถอดเสียงที่ยาว 40 นาที มักหมายถึงข้อมูลนำเข้าถูกตัดหรือใช้ไฟล์ผิด
  • ตรวจสอบความยาว บันทึกที่ยาวกว่าประมาณหนึ่งในห้าของบทถอดเสียงไม่ใช่บันทึก; ปรับพรอมต์หรือตั้งค่า max_tokens

บันทึกส่วนหัวการตอบกลับ X-Request-Id ไว้ข้างผลลัพธ์ที่บันทึกไว้แต่ละรายการ เมื่อมีคนบอกว่ารายการที่ต้องดำเนินการผิด ตัวระบุนี้ช่วยให้คุณหาคำขอนั้นและเปรียบเทียบกับบทถอดเสียงได้ การเก็บบทถอดเสียงดั้งเดิมไว้ข้างๆ บันทึกมีต้นทุนต่ำและยุติข้อโต้แย้งส่วนใหญ่ได้รวดเร็ว

ต้นทุนและการทดสอบครั้งแรก

ราคาคือ $0.15 ต่อโทเคนอินพุตหนึ่งล้านโทเคน และ $0.50 ต่อโทเคนเอาต์พุตหนึ่งล้านโทเคน การประชุมหนึ่งชั่วโมงพร้อม transcript ยาว 12,000 โทเคน และชุด notes ยาว 800 โทเคน มีต้นทุนอินพุต $0.0018 และเอาต์พุต $0.0004 รวมเป็น $0.0022 ในอัตราดังกล่าว เงิน $1 ครอบคลุมการประชุมลักษณะนี้ประมาณ 454 ครั้ง ดูหน้า ราคา เพื่อเติมเงิน ซึ่งเริ่มที่ $20 และจ่ายได้ด้วยบัตรหรือคริปโต

เพื่อทดลองใช้งาน ให้สมัครและเปิดหน้า คีย์ API มีเครดิตทดลองใช้งาน $0.50 แบบคลิกเดียว ให้ได้ครั้งเดียวต่อคน ใช้ได้ 7 วัน ไม่ต้องใช้บัตร รันสคริปต์ Python ข้างต้นกับ transcript จริงหนึ่งรายการ อ่าน JSON แล้วปรับพรอมต์จนกว่า action item จะตรงกับสิ่งที่เกิดขึ้นในการประชุม จากนั้นทำซ้ำกับรายการที่เหลือ

ถาม-ตอบ

ฉันส่งไฟล์เสียงบันทึกโดยตรงได้ไหม

ไม่ได้ API รับเฉพาะข้อความเท่านั้น ให้ถอดเสียงไฟล์เสียงด้วยเครื่องมือ speech-to-text ก่อน แล้วส่ง transcript

ฉันทำให้รูปแบบผลลัพธ์สอดคล้องกันได้อย่างไร

ใช้โหมด JSON พร้อมระบบพรอมต์ที่ระบุชื่อคีย์ทุกตัว และตั้งค่าอุณหภูมิให้ต่ำ จากนั้นแยกวิเคราะห์ผลลัพธ์ในบล็อก try และลองใหม่หนึ่งครั้งหากเกิดข้อผิดพลาด

บทถอดเสียงยาวแค่ไหนจึงจะพอดีกับคำขอเดียว

หน้าต่างบริบทรองรับโทเคน 100,000 โทเคนสำหรับพรอมต์รวมกับการตอบกลับ ซึ่งครอบคลุมเวิร์กช็อปตลอดทั้งวันในกรณีส่วนใหญ่

ข้อความถอดเสียงของฉันถูกนำไปใช้ฝึกโมเดลไหม

ไม่ พรอมต์ไม่ถูกนำไปใช้ในการฝึกโมเดล

การประชุมหนึ่งครั้งมีค่าใช้จ่ายเท่าใด

การประชุมที่ยาวหนึ่งชั่วโมงพร้อมบทถอดเสียง 12,000 โทเคนและบทสรุป 800 โทเคน มีค่าใช้จ่ายประมาณ $0.0022 เมื่ออัตราโทเคนอินพุตอยู่ที่ $0.15 และโทเคนเอาต์พุตอยู่ที่ $0.50 ต่อหนึ่งล้านโทเคน

คีย์ของคุณอยู่ห่างแค่ขั้นตอนเดียว

สร้างบัญชี คัดลอกคีย์ และเปลี่ยน base URL นั่นคือการตั้งค่าทั้งหมด

รับคีย์ API