Como dar uma ferramenta de transcrição para um agente de IA
A ideia é juntar as duas chamadas do KolWrite dentro de uma única ferramenta chamada transcribe_media. Abaixo você encontra o esquema pronto para o tool-use da Anthropic e o function-calling da OpenAI, a função em Python que roda a tarefa, um loop básico de execução e uma instrução curta para colocar no seu AGENTS.md ou CLAUDE.md. Assim, os agentes de código já sabem como chamar a API do jeito certo.
Você precisa mesmo definir uma ferramenta?
Se o seu agente executa comandos de shell (Claude Code, Codex CLI, Cursor, Windsurf), ele não precisa de um esquema: roda curl ou Python, e as instruções de agent-instructions.md dizem como. Uma definição de ferramenta é para agentes que você constrói com uma API e um laço de ferramentas, ou para frameworks que chamam funções.
Definição da ferramenta no formato tool-use da Anthropic
{
"name": "transcribe_media",
"description": "Transcribe an audio or video file with the KolWrite API. Returns timed text. Use output_format \"json\" for word timestamps and speakers, \"srt\" for subtitles, \"txt\" for plain text. Billed per second of audio.",
"input_schema": {
"type": "object",
"properties": {
"path_or_url": { "type": "string", "description": "Local file path or a public URL of an audio or video file." },
"model": { "type": "string", "enum": ["base", "oracle_mini", "oracle", "oracle_pro", "oracle_max"], "description": "Default oracle. base is cheapest for clear audio; translate_to needs an Oracle model." },
"language": { "type": "string", "description": "Language code such as en, he, ar, es, or auto to detect." },
"translate_to": { "type": "string", "description": "Language code to translate into (Oracle models only)." },
"output_format": { "type": "string", "enum": ["json", "txt", "srt"] },
"word_timestamps": { "type": "boolean", "description": "Word times in json. For srt, false gives one cue per segment." },
"include_diarization": { "type": "boolean", "description": "Label who is speaking." },
"num_speakers": { "type": "integer", "minimum": 1 }
},
"required": ["path_or_url"]
}
}
Definição da ferramenta no formato function-calling da OpenAI
{
"type": "function",
"function": {
"name": "transcribe_media",
"description": "Transcribe an audio or video file with the KolWrite API. Returns timed text. Use output_format \"json\" for word timestamps and speakers, \"srt\" for subtitles, \"txt\" for plain text. Billed per second of audio.",
"parameters": {
"type": "object",
"properties": {
"path_or_url": { "type": "string", "description": "Local file path or a public URL of an audio or video file." },
"model": { "type": "string", "enum": ["base", "oracle_mini", "oracle", "oracle_pro", "oracle_max"] },
"language": { "type": "string", "description": "Language code such as en, he, ar, es, or auto to detect." },
"translate_to": { "type": "string", "description": "Language code to translate into (Oracle models only)." },
"output_format": { "type": "string", "enum": ["json", "txt", "srt"] },
"word_timestamps": { "type": "boolean" },
"include_diarization": { "type": "boolean" },
"num_speakers": { "type": "integer", "minimum": 1 }
},
"required": ["path_or_url"]
}
}
}
Os dois formatos usam exatamente o mesmo JSON Schema para os parâmetros. A lista enum traz apenas os modelos atuais, já que core e edge são legados.
A função que roda a ferramenta
O executor envia os arquivos locais, inicia a tarefa, espera terminar e devolve uma resposta enxuta. Quando o formato é JSON, ele salva o resultado completo num arquivo e retorna apenas as linhas numeradas. A lista inteira de palavras de uma gravação longa é muito maior do que o modelo precisa.
kolwrite.py
# kolwrite.py — three small functions around the KolWrite REST API
import os, time, requests
API = "https://app.kolwrite.com"
HEADERS = {"x-api-key": os.environ["KOLWRITE_API_KEY"]}
def upload(path):
"""Upload a local file (up to 100 MiB) and return a URL the API can read."""
name = os.path.basename(path)
form = requests.post(f"{API}/upload", headers=HEADERS, json={"filename": name}, timeout=30)
form.raise_for_status()
form = form.json()
with open(path, "rb") as media:
# No x-api-key on this request: it goes to the storage address.
sent = requests.post(form["uploadUrl"], data=form["uploadFields"],
files={"file": (name, media)}, timeout=600)
sent.raise_for_status()
return form["fileUrl"]
def transcribe(audio_url, timeout_s=3600, **options):
"""Start a job, wait for it, return the result: a dict for json, a string for srt and txt."""
body = {"audio_url": audio_url, "model": "oracle", **options}
job = requests.post(f"{API}/transcribe", headers=HEADERS, json=body, timeout=30)
job.raise_for_status()
job_id = job.json()["jobId"]
deadline = time.time() + timeout_s
while time.time() < deadline:
time.sleep(5)
reply = requests.get(f"{API}/transcribe/{job_id}", headers=HEADERS, timeout=30)
reply.raise_for_status()
reply = reply.json()
if reply["status"] == "COMPLETED":
return reply["result"]
if reply["status"] == "FAILED":
raise RuntimeError(reply.get("error", "transcription failed"))
raise TimeoutError(f"job {job_id} did not finish in {timeout_s} seconds")
tool.py
# tool.py — run a transcribe_media call for the model and keep the reply small
import json
from kolwrite import upload, transcribe # the helper from the video-editing guide
def numbered_line(number, segment):
who = f'{segment["speaker"]}: ' if segment.get("speaker") else ""
return f'S{number} [{segment["start"]:.1f}-{segment["end"]:.1f}] {who}{segment["text"]}'
def run_transcribe_media(path_or_url, output_format="json", **options):
url = path_or_url if path_or_url.startswith(("http://", "https://")) else upload(path_or_url)
result = transcribe(url, output_format=output_format, **options)
if output_format != "json":
return result # srt and txt are already strings
# Long recordings: save the full JSON and hand the model numbered lines, not 500 KB of words.
with open("transcript.json", "w", encoding="utf-8") as out:
json.dump(result, out, ensure_ascii=False)
lines = [numbered_line(i, s) for i, s in enumerate(result["segments"])]
return "Full JSON saved to transcript.json (words[] has word times).\n" + "\n".join(lines)
Loop básico de execução (Anthropic Messages API)
# loop.py — Anthropic Messages API tool loop around run_transcribe_media
import anthropic
from tool import run_transcribe_media
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY
TOOLS = [...] # the transcribe_media definition above
messages = [{"role": "user", "content": "Transcribe interview.mp3 and list the questions asked, with times."}]
while True:
reply = client.messages.create(model="YOUR_MODEL", max_tokens=2000, tools=TOOLS, messages=messages)
messages.append({"role": "assistant", "content": reply.content})
if reply.stop_reason != "tool_use":
print("".join(block.text for block in reply.content if block.type == "text"))
break
results = []
for block in reply.content:
if block.type == "tool_use" and block.name == "transcribe_media":
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": run_transcribe_media(**block.input)})
messages.append({"role": "user", "content": results})
Substitua YOUR_MODEL pelo identificador do modelo que você está usando. O laço continua rodando até o modelo parar de chamar ferramentas. Esse mesmo executor funciona em qualquer framework capaz de chamar uma função Python.
Instruções para o AGENTS.md e CLAUDE.md
Coloque este trecho no arquivo de projeto que o seu agente de código lê. Assim, toda sessão já usa o endpoint, os formatos e o tratamento de chave certos:
## Transcription
For speech-to-text, subtitles and word timings use the KolWrite API (https://app.kolwrite.com).
Before writing code read https://console.kolwrite.com/agent-instructions.md and /llms-full.txt.
- Key: KOLWRITE_API_KEY in the environment, sent as the x-api-key header. Never print it or commit it.
- POST /transcribe {"audio_url", "model": "oracle", "output_format": "json"}, then poll GET /transcribe/{jobId}.
- Local files: POST /upload first and use fileUrl as audio_url. Extract audio from video with ffmpeg to stay under 100 MiB.
- Subtitles: output_format "srt" with "word_timestamps": false (one cue per segment). Formats are json, txt, srt only.
- Always send "model". Ask me before transcribing anything longer than one hour: it costs money.
Como controlar os gastos do agente
- A transcrição é cobrada por segundo de áudio, a partir de US$ 0,48 por hora no modelo
base. Com a regra acima, o agente pede confirmação antes de transcrever arquivos longos. - Salve o JSON na mesma pasta do arquivo de mídia para fazer cache. Assim você reaproveita o resultado e não paga para transcrever tudo de novo.
- Escolha o modelo mais em conta para o que você precisa:
basepara áudios limpos,oraclepara qualidade de produção eoracle_minise precisar também de tradução. - O agente não consegue criar uma conta sozinho. Uma pessoa precisa fazer o cadastro, gerar a chave de API e passá-la por uma variável de ambiente.
Perguntas e respostas
O KolWrite tem SDK ou servidor MCP?
No momento da escrita, não há pacote SDK nem servidor MCP publicados. A API é HTTPS e JSON; use a definição de ferramenta acima, curl ou Python diretamente, ou gere um cliente a partir do openapi.json.
Como coloco o Claude Code para transcrever áudio?
Coloque a chave em KOLWRITE_API_KEY, adicione a regra acima ao CLAUDE.md e peça a transcrição. O Claude Code chama a API REST com curl ou Python seguindo o agent-instructions.md.
Qual modelo o agente deve usar por padrão?
Use o oracle em áudio de produção. Se o áudio estiver limpo, o base tem o menor preço, enquanto o oracle_mini é o modelo mais barato que faz tradução. Sempre informe o campo model, já que o padrão ainda é o modelo legado core.
O agente consegue criar a própria chave de API?
Não. Alguém precisa entrar em console.kolwrite.com, criar a conta, comprar créditos e gerar a chave. O agente só lê essa chave das variáveis de ambiente.