SSkilltecabyclaudinhocode
Enviar skill
← Voltar para o catálogo

voice-debrief

Documentos

面试录音转译复盘助手,处理面试录音或转录文本,支持保存到飞书文档(完整对话、问答整理、总评)并自动归档到多维表格。触发场景:用户提到"面试复盘"、上传音频文件(.wav/.m4a/.mp3/.mp4)需要转录、上传 PDF 转录文件需要整理、或说"帮我整理这场面试"。

2estrelas
Ver no GitHub ↗Autor: realwooolf

面试复盘

根据传入的文件类型自动判断执行阶段:

  • 传入音频文件(.wav / .m4a / .mp3)或视频文件(.mp4)→ 执行阶段一:本地转录
  • 传入 PDF 文件(已有转录文本)→ 执行阶段二:整理并输出结果

注意: Claude Code 不支持直接上传音频/视频文件。请粘贴文件路径,或截图文件所在位置(如 Finder 窗口底部会显示完整路径),例如: 帮我整理这场面试 /Users/xxx/Desktop/某公司二面.mp3

多文件处理: 如果用户传入多个音频/视频文件,在阶段一完成后自动执行阶段零(多文件合并排序),再进入阶段二。单文件则跳过合并直接处理。


前置依赖

使用前确保以下工具已安装:

# 转录(二选一,按平台选)
pip install mlx-whisper          # Apple Silicon Mac 用,GPU 加速,推荐
pip install openai-whisper        # 其他平台 fallback

# 音视频处理(whisper 内部依赖 ffmpeg 解码音频)
# macOS
brew install ffmpeg
# Ubuntu / Debian
sudo apt install ffmpeg
# Windows
# 前往 https://ffmpeg.org/download.html 下载,或用 winget install ffmpeg

平台说明:

  • Apple Silicon Mac(M1/M2/M3/M4)→ 使用 mlx-whisper,速度快 5-10 倍
  • Intel Mac / Windows / Linux → 自动 fallback 到 openai-whisper(CPU 模式)

飞书配置

若选择保存到飞书,需要先完成以下一次性配置。选本地保存可跳过此章节。

第一步:创建飞书自建应用

  1. 打开 open.feishu.cn,登录你的飞书账号
  2. 点击右上角「开发者后台」→「创建应用」→ 选「自建应用」
  3. 填写应用名称(随意,如 interview-debrief)和描述,创建完成

第二步:开启权限

  1. 进入应用后,左侧菜单点「权限管理」
  2. 点击「批量导入/导出权限」→「导入」,将以下 JSON 粘贴进去,点「下一步,确认新增权限」:
    {
      "scopes": {
        "user": [
          "docx:document",
          "bitable:app",
          "docs:doc",
          "docs:doc:readonly",
          "docs:document:import",
          "drive:drive",
          "drive:file",
          "wiki:wiki"
        ]
      }
    }
    
  3. 确认后权限全部生效(测试企业环境下免审核)

第三步:添加回调地址

  1. 左侧菜单点「安全设置」
  2. 找到「重定向 URL」,点击添加,填入:
    http://localhost:9998/callback
    
  3. 保存

第四步:发布应用

  1. 左侧菜单点「版本管理与发布」→「创建版本」
  2. 版本号填 1.0.0,更新说明随意填
  3. 可用范围保持默认(部分成员,仅自己可见)
  4. 点「保存」,飞书个人版免审核,保存后立即生效

第五步:获取凭证并创建配置文件

  1. 左侧菜单点「凭证与基础信息」,复制 App IDApp Secret

  2. 在终端运行以下命令创建配置文件(替换为你的实际值):

    cat > ~/.claude/feishu_config.json << 'EOF'
    {
      "app_id": "你的 App ID",
      "app_secret": "你的 App Secret",
      "interview_bitable_app_token": "",
      "interview_bitable_table_id": ""
    }
    EOF
    
  3. 多维表格配置(二选一):

    • 没有表格:两个字段留空,第一次运行时 skill 会自动创建并写入
    • 已有表格:打开飞书多维表格,URL 格式为 https://xxx.feishu.cn/base/ClG9xxxxxxx?table=tblxxxxxxxClG9xxxxxxx 填入 interview_bitable_app_tokentblxxxxxxx 填入 interview_bitable_table_id

配置完成后,第一次使用时会自动打开浏览器完成飞书 OAuth 授权,之后 token 自动缓存,无需重复操作。


阶段一:音频/视频处理

触发条件: 文件扩展名为 .wav、.m4a、.mp3、.mp4

检测已有转录文件

收到音频/视频文件路径后,先检查同目录下是否存在同名 .txt 文件(如 interview.m4a → 检查 interview.txt):

  • 存在:直接读取 .txt 内容,跳过转录,进入阶段二信息收集
  • 不存在:启动转录(见下方)

转录

先检测平台,选择对应的转录方式。使用 run_in_background=True 运行转录脚本,转录在后台进行,Claude 立即在对话框中询问阶段二所需的信息(保存方式、日期、公司名等)。收到通知转录完成后,直接用已收集的信息进入阶段零(多文件)或阶段二(单文件)。

import platform, subprocess, os

def is_apple_silicon():
    return platform.system() == "Darwin" and platform.machine() == "arm64"

def transcribe(files):
    for f in files:
        out = f.rsplit('.', 1)[0] + '.txt'
        if is_apple_silicon():
            # mlx-whisper:Apple Silicon GPU 加速,41 分钟音频约 3-5 分钟
            import mlx_whisper
            result = mlx_whisper.transcribe(
                f, path_or_hf_repo="mlx-community/whisper-large-v3-turbo", language="zh"
            )
        else:
            # openai-whisper:有 NVIDIA GPU 自动用 CUDA,否则 CPU
            import whisper, torch
            model = whisper.load_model("small")
            fp16 = torch.cuda.is_available()
            result = model.transcribe(f, language="zh", fp16=fp16)
        open(out, 'w', encoding='utf-8').write('\n'.join(s['text'].strip() for s in result['segments']))
        print('完成:', out)

注意:

  • mlx-whisper 首次运行会自动下载模型(约 800MB),之后缓存本地
  • 转录脚本必须用 run_in_background=True 启动,否则 Claude 会阻塞无法收集信息

阶段零:多文件合并排序(自动触发)

触发条件: 阶段一产出 2 个及以上 .txt 转录文件时自动执行,无需用户操作。

执行步骤:

  1. 读取所有 .txt 文件内容
  2. 根据文件名时间戳 + 内容语义判断顺序:
    • 文件名含时间戳(如 1733、215639)→ 按时间升序排列
    • 内容含开场白(自我介绍、你好)→ 排最前
    • 内容含道别(拜拜、谢谢)→ 排最后
    • 内容有明显上下文衔接 → 按语义连贯性判断
  3. 将排序结果告知用户确认(列出文件顺序和判断依据),确认后进入阶段二
  4. 若顺序不确定,询问用户

阶段二:面试整理 → 输出结果

触发条件: 文件扩展名为 .pdf,或阶段一/阶段零完成后的转录文本

启动前分两步收集信息:

第一问(单独问):

  • 保存方式:本地文件 还是 飞书文档?

收到回答后,若选飞书,检查 ~/.claude/feishu_config.json 是否存在且 app_id / app_secret 均为非空非占位符(不等于「你的 App ID」)。若未配置或配置不完整,先引导完成飞书配置(见「飞书配置」章节),配置完成后再继续。

第二问(合并成一条消息):

  • 日期(格式:YYYY-MM-DD)
  • 公司名、岗位名、面试轮次(如一面、二面、终面)
  • 面试官姓名(选填)、面试时长(选填,如 45min)

默认按面试类型处理。若用户说明是「其他」类型(如聊天、咨询等),则改为询问对方身份和主题,且跳过多维表格更新。

根据类型生成对应的文档标题和信息头:

面试:

{公司名} 完整对话
{公司名} 问答整理
{公司名} 面试总评

信息头:公司 / 岗位 / 轮次 / 日期

其他:

{对方身份} 完整对话
{对方身份} 问答整理
{对方身份} 总评

信息头:对方身份 / 主题 / 日期

1. 本地保存(选本地时执行)

将整理结果写入录音文件同目录下的 .md 文件:

import os

def save_local(source_path, title, full_dialogue, qa, summary):
    # source_path 可以是音频文件或 PDF 文件路径
    base_dir = os.path.dirname(os.path.abspath(source_path))
    for suffix, content in [("完整对话", full_dialogue), ("问答整理", qa), ("总评", summary)]:
        out = os.path.join(base_dir, f"{title}-{suffix}.md")
        with open(out, "w", encoding="utf-8") as f:
            f.write(content)
        print(f"已保存:{out}")

保存完成后,将总评内容直接输出在对话框中。后续飞书相关步骤全部跳过。


2. 获取飞书 user_access_token(选飞书时执行)

从配置中读取 APP_ID / APP_SECRET,TOKEN_CACHE 路径做 ~ 展开:

import json, time, os, subprocess, tempfile
import http.server, threading, webbrowser, urllib.parse

# 从本地配置文件读取(~/.claude/feishu_config.json),不写入 skill 避免泄露
_cfg = json.load(open(os.path.expanduser("~/.claude/feishu_config.json")))
APP_ID = _cfg["app_id"]
APP_SECRET = _cfg["app_secret"]
TOKEN_CACHE = os.path.expanduser("~/.claude/feishu_token.json")
REDIRECT_URI = "http://localhost:9998/callback"

def load_token():
    try:
        with open(TOKEN_CACHE) as f:
            return json.load(f)
    except:
        return None

def save_token(data):
    os.makedirs(os.path.dirname(TOKEN_CACHE), exist_ok=True)
    with open(TOKEN_CACHE, "w") as f:
        json.dump(data, f)

def curl_post_auth(url, data, headers=None):
    payload = json.dumps(data, ensure_ascii=False)
    with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False, encoding='utf-8') as f:
        f.write(payload)
        fname = f.name
    cmd = ["curl", "-sk", "-X", "POST", url, "--noproxy", "*",
           "-H", "Content-Type: application/json"]
    if headers:
        for h in headers:
            cmd += ["-H", h]
    cmd += ["--data", f"@{fname}"]
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
    finally:
        os.unlink(fname)
    if not result.stdout.strip():
        return {}
    return json.loads(result.stdout)

def get_app_token():
    d = curl_post_auth(
        "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
        {"app_id": APP_ID, "app_secret": APP_SECRET})
    return d.get("tenant_access_token")

def oauth_flow():
    # 先释放端口,防止上次未完成进程占用
    subprocess.run("lsof -ti:9998 | xargs kill -9 2>/dev/null", shell=True)
    app_token = get_app_token()
    code_holder = {}
    class Handler(http.server.BaseHTTPRequestHandler):
        def do_GET(self):
            parsed = urllib.parse.urlparse(self.path)
            params = urllib.parse.parse_qs(parsed.query)
            code_holder["code"] = params.get("code", [None])[0]
            self.send_response(200)
            self.end_headers()
            self.wfile.write(b"<html><body>Done! You can close this tab.</body></html>")
        def log_message(self, *args): pass
    server = http.server.HTTPServer(("localhost", 9998), Handler)
    t = threading.Thread(target=server.handle_request)
    t.start()
    auth_url = (f"https://open.feishu.cn/open-apis/authen/v1/authorize"
                f"?app_id={APP_ID}&redirect_uri={urllib.parse.quote(REDIRECT_URI)}"
                f"&scope=docx%3Adocument%20bitable%3Aapp%20docs%3Adoc%20docs%3Adoc%3Areadonly"
                f"%20docs%3Adocument%3Aimport%20drive%3Adrive%20drive%3Afile%20wiki%3Awiki"
                f"&response_type=code")
    print("正在打开飞书授权页面...")
    webbrowser.open(auth_url)
    t.join(timeout=120)
    code = code_holder.get("code")
    if not code:
        raise Excepti

Como adicionar

/plugin marketplace add realwooolf/voice-debrief

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Comentários · Nenhum comentário

Entre para comentar. Entrar

  • Ainda não há comentários. Seja o primeiro.