Cómo añadir una herramienta de transcripción a un agente de IA
Agrupa las dos llamadas a la API de KolWrite dentro de una sola herramienta llamada transcribe_media. A continuación encontrarás el esquema para Anthropic (tool-use) y OpenAI (function-calling), la función en Python que hace el trabajo, un bucle básico de ejecución y una nota breve para AGENTS.md o CLAUDE.md con la que tus agentes de código sabrán cómo llamar a la API.
¿Realmente hace falta definir una herramienta?
Si tu agente puede ejecutar comandos de shell (Claude Code, Codex CLI, Cursor, Windsurf), no necesita un esquema: ejecuta curl o Python y las instrucciones de agent-instructions.md le dicen cómo. Una definición de herramienta es para agentes que construyes con una API y un bucle de herramientas, o para marcos que llaman a funciones.
Definición de la herramienta para Anthropic (tool-use)
{
"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"]
}
}
Definición de la herramienta para OpenAI (function-calling)
{
"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"]
}
}
}
Ambos formatos usan exactamente el mismo JSON Schema para los parámetros. La lista enum recoge los modelos actuales (core y edge son heredados).
La función que ejecuta la herramienta
Esta función se encarga de subir el archivo local, iniciar la transcripción, esperar a que termine y devolver un resumen manejable. Cuando se pide formato JSON, guarda la respuesta completa en disco y le devuelve al modelo solo las líneas numeradas; en grabaciones largas, pasar la lista entera de palabras llenaría el contexto sin necesidad.
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)
Ejemplo mínimo de bucle de herramientas (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})
Cambia YOUR_MODEL por el identificador del modelo que estés usando. El bucle seguirá iterando hasta que el modelo deje de solicitar herramientas. Puedes usar esta misma función ejecutora desde cualquier librería o entorno que permita llamar a funciones de Python.
Instrucciones para AGENTS.md y CLAUDE.md
Copia este bloque en el archivo de instrucciones que lee tu agente de código. Así te aseguras de que en cada sesión utilice el endpoint, las claves y los formatos adecuados:
## 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.
Cómo controlar el gasto de los agentes
- La transcripción se cobra por segundo de audio, a partir de 0,48 US$ la hora con el modelo
base. Con la regla anterior, el agente pedirá permiso antes de procesar archivos largos. - Guarda los resultados en caché dejando el JSON junto al archivo multimedia. Así podrás reutilizarlo en vez de transcribirlo otra vez.
- Elige el modelo más económico que te resuelva la tarea:
basesi el audio es claro,oraclepara calidad de producción yoracle_minisi además necesitas traducir. - Un agente no puede registrarse por su cuenta; tiene que ser una persona quien cree la cuenta y la clave de API. Pásale la clave mediante una variable de entorno.
Preguntas y respuestas
¿Existe algún SDK o servidor MCP para KolWrite?
En el momento de escribir esto no hay publicados un paquete SDK ni un servidor MCP. La API es HTTPS y JSON; usa la definición de herramienta anterior, curl o Python directamente, o genera un cliente a partir de openapi.json.
¿Cómo hago para que Claude Code transcriba audio?
Pon la clave en KOLWRITE_API_KEY, añade la regla anterior a CLAUDE.md y pide la transcripción. Claude Code llama a la API REST con curl o Python siguiendo agent-instructions.md.
¿Qué modelo debería usar un agente por defecto?
oracle para audio de producción. Para audio claro, base es la opción más barata, y oracle_mini es el modelo más económico con traducción. Pasa siempre el parámetro model, ya que el valor por defecto apunta al modelo antiguo core.
¿El agente puede crear su propia clave de API?
No. Hace falta que una persona se registre en console.kolwrite.com, compre créditos y genere la clave. El agente solo la lee de las variables de entorno.