Cluelessly 会议纪要助手源码解析:从音频到结构化纪要的工程实践

简介:Cluelessly 是一套面向开发者与团队协作场景的 AI 会议纪要助手源码,聚焦实时转录、对话分析与可执行洞察生成,适合希望自建会议记录工具、研究 AI 语音应用或进行二次开发的中高级开发者。项目采用 Laravel 12 与 PHP 8.2+ 构建后端,前端基于 Vue 3.5 与 TypeScript、Inertia.js,桌面端借助 NativePHP/Electron 实现本地集成,AI 能力由 OpenAI Realtime API 驱动,配合 Tailwind CSS 4.1 与 Vite 6 完成样式与构建,SQLite 负责本地会话存储。压缩包共 347 个文件,约 65.05MB,以 155 个 vue 组件、77 个 php 后端逻辑、41 个 ts 脚本为主,另含 md 文档、sh 脚本、json 配置及少量图片与音频采集相关文件,结构完整。已有 115 人学习关注。读者可获得涵盖实时转录、动作项提取、上下文保护与见解生成的完整工程实现,便于快速部署验证与功能扩展。

1. 从两小时录音到一页纸:Cluelessly 到底替谁省了事

上周三晚上十点,某公司做后端的老张给我发来一段 1 小时 47 分的会议录音,说“你帮我看看这个 AI 会议纪要助手到底靠不靠谱,我明天晨会要用”。我打开这份 Cluelessly 的源码包,从音频解码一路翻到纪要生成,跑完一遍发现它真正解决的问题不是“转文字”,而是把转写、说话人切分、要点抽取、待办归并串成了一条能落地的流水线。市面上多数 AI 会议记录工具要么只给逐字稿,要么把摘要做得像散文,Cluelessly 的定位更接近“会议后处理中间件”:输入音频或实时流,输出结构化的议题、决议、行动项。它适合三类人——需要批量处理会议录音的团队工具开发者、想二次开发会议机器人的全栈工程师、以及被逐字稿淹没又不想手动整理的产品经理。源码是完整的,前后端加推理服务都在,不是那种只给个 demo 页面的半成品。

2. 拆开 Cluelessly 的流水线:音频进、结构化纪要出

2.1 四个核心模块与数据流转

Cluelessly 的架构不复杂,但每个环节都有取舍。整条链路是:音频预处理 → 语音转写(ASR)→ 说话人分离(Diarization)→ 纪要生成(LLM 摘要 + 结构化抽取)。源码目录里audio_pipeline/负责重采样和静音切分,asr_engine/封装了转写模型调用,diarization/做说话人聚类,summarizer/是纪要生成的核心。数据流不是串行阻塞的,转写和说话人分离可以并行跑,最后按时间戳对齐合并。这个设计的好处是:如果转写模型换了,说话人模块不用动;如果只想出逐字稿,把 summarizer 关掉就行。

我一般会先看config/pipeline.yaml,里面定义了各阶段的开关和模型路径。默认配置下,音频先被切成 30 秒片段做 VAD(语音活动检测),静音段直接跳过,这一步能省掉 20% 到 40% 的推理时间。转写输出带词级时间戳,说话人分离输出的是“谁在什么时间段说话”,两者按时间窗口做交集对齐。对齐逻辑在utils/align.py里,用的是重叠区间匹配,不是简单的最近邻,这点比很多开源实现讲究。

2.2 环境搭建与依赖安装

源码包解压后先看requirements.txt和README里的环境说明。Python 版本要求 3.9 以上,推荐 3.10。核心依赖包括音频处理库、推理运行时和 Web 框架。我习惯用虚拟环境隔离,避免和系统里的包打架。

# 创建虚拟环境并激活 python3.10 -m venv venv_cluelessly source venv_cluelessly/bin/activate # 安装核心依赖,国内网络建议加镜像源 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 如果要用 GPU 推理,额外装对应版本的运行时 pip install torch==2.1.0+cu118 -f https://download.pytorch.org/whl/torch_stable.html

这里有个参数要注意:requirements.txt里对推理运行时的版本卡得比较死,不要随意升级。我试过把 torch 升到 2.2,结果 diarization 模块的某个算子不兼容,报了一堆看不懂的错。如果只是跑 CPU 推理,把 GPU 相关的包注释掉就行,速度慢但功能完整。安装完成后跑python -m pytest tests/做自检,通过的用例数应该在 40 个以上,少于这个数说明依赖没装全。

2.3 配置文件的关键参数怎么调

config/pipeline.yaml是整条流水线的控制中心,改错一个参数可能让纪要质量断崖式下跌。下面这张表是我实际调过的几个关键项:

参数名默认值作用调整建议
asr.chunk_size30音频切片长度(秒)网络差或音频质量低时降到 15
asr.languageauto转写语言中英混会时强制设 zh,否则英文词会被吞
diarization.min_speakers2最少说话人数单人录音设为 1,否则会强行切出两个人
diarization.cluster_threshold0.7聚类阈值调高到 0.8 说话人更少,调低到 0.6 更细
summarizer.max_tokens2048纪要生成长度上限长会议调到 4096,但注意模型上下文限制
summarizer.temperature0.3生成随机性要稳定输出别超过 0.5,要发散可到 0.7

改完配置后不用重启服务,Cluelessly 支持热加载,但只对下一个任务生效。正在跑的任务还是用旧配置。这个设计在批量处理时很有用,可以边跑边调。

2.4 跑通第一个会议纪要任务

环境好了、配置调了,接下来用一段测试音频跑通全流程。源码包的samples/目录下有一段 3 分钟的模拟会议录音,两个人对话,带一点背景噪声。命令行入口是main.py,支持文件和实时流两种模式。

# 用示例音频跑一次完整流水线 python main.py \ --input samples/meeting_demo.wav \ --output outputs/demo_result.json \ --config config/pipeline.yaml \ --enable-diarization \ --enable-summary # 查看输出结构 cat outputs/demo_result.json | python -m json.tool | head -50

输出是一个 JSON,包含transcript(逐字稿)、speakers(说话人时间段)、summary(纪要正文)、action_items(行动项列表)。action_items是单独抽出来的,每条带负责人和截止时间(如果原文提到了)。我跑下来发现,3 分钟音频在 CPU 上大约 40 秒出结果,GPU 上 8 秒左右。如果输出里action_items为空,先检查summarizer的 prompt 模板是不是被改过,默认模板在prompts/summary_zh.txt里,对中文会议做了优化。

3. 把 Cluelessly 接进自己的系统:API 封装与批量处理

3.1 启动 HTTP 服务并调用接口

命令行适合调试,真正要用还是得走 API。Cluelessly 自带一个基于 FastAPI 的服务端,启动命令在server/目录下。默认监听 8000 端口,支持上传音频文件和提交转写任务两种模式。

# 启动 API 服务 cd server uvicorn app:app --host 0.0.0.0 --port 8000 --workers 2 # 另开终端,提交一个音频文件 curl -X POST "http://localhost:8000/api/v1/transcribe" \ -H "Content-Type: multipart/form-data" \ -F "file=@samples/meeting_demo.wav" \ -F "enable_summary=true" \ -F "language=zh"

接口返回一个task_id,后续用/api/v1/task/{task_id}查状态和结果。--workers参数控制并发进程数,CPU 推理建议设为核数的一半,GPU 推理设为 1 到 2 就行,多了会抢显存。我试过 4 个 worker 同时跑 GPU 推理,结果第三个任务开始就 OOM 了,血泪经验是 GPU 场景下 worker 数别超过 GPU 数量。

3.2 批量处理会议录音的脚本写法

实际场景里往往是一堆录音要处理,一个个调 API 太慢。我写了个批量脚本,核心是控制并发数和失败重试。Cluelessly 的任务队列是内存级的,重启就丢,所以批量处理时要把结果落盘。

import requests import os import time from concurrent.futures import ThreadPoolExecutor, as_completed API_BASE = "http://localhost:8000/api/v1" AUDIO_DIR = "recordings/" OUTPUT_DIR = "results/" MAX_WORKERS = 2 # GPU 场景别超过 2 def process_one(audio_file): """提交单个音频并轮询结果""" filepath = os.path.join(AUDIO_DIR, audio_file) with open(filepath, "rb") as f: resp = requests.post( f"{API_BASE}/transcribe", files={"file": f}, data={"enable_summary": "true", "language": "zh"}, timeout=30 ) task_id = resp.json()["task_id"] # 轮询直到完成,最多等 10 分钟 for _ in range(120): time.sleep(5) status = requests.get(f"{API_BASE}/task/{task_id}").json() if status["state"] == "done": return audio_file, status["result"] elif status["state"] == "failed": return audio_file, {"error": status.get("message", "unknown")} return audio_file, {"error": "timeout"} # 批量提交,控制并发 audio_files = [f for f in os.listdir(AUDIO_DIR) if f.endswith((".wav", ".mp3"))] with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor: futures = {executor.submit(process_one, f): f for f in audio_files} for future in as_completed(futures): name, result = future.result() out_path = os.path.join(OUTPUT_DIR, name + ".json") with open(out_path, "w", encoding="utf-8") as f: json.dump(result, f, ensure_ascii=False, indent=2) print(f"done: {name}")

这段脚本的关键点是MAX_WORKERS和轮询间隔。MAX_WORKERS设大了会把服务打挂,设小了处理慢。我的经验是 CPU 推理设 4,GPU 推理设 2。轮询间隔 5 秒是折中值,太短浪费请求,太长结果延迟。另外注意timeout=30是上传超时,大文件要调大,不然传一半就断了。

3.3 输出格式定制与下游对接

Cluelessly 默认输出 JSON,但很多团队需要 Markdown 或直接推到协作工具。源码里exporters/目录提供了几个导出器,包括 Markdown、纯文本和一种简化的表格格式。如果都不满足,可以自己写一个,只要实现export(result) -> str接口就行。

# 自定义导出器示例:输出带行动项的 Markdown from exporters.base import BaseExporter class MyMarkdownExporter(BaseExporter): def export(self, result): lines = ["# 会议纪要\n"] lines.append(f"## 摘要\n{result['summary']}\n") lines.append("## 行动项\n") for item in result.get("action_items", []): owner = item.get("owner", "待定") deadline = item.get("deadline", "未定") lines.append(f"- [ ] {item['content']} (负责人:{owner},截止:{deadline})") return "\n".join(lines)

把这个文件放到exporters/下,然后在配置里把export.format改成my_markdown就能生效。注意行动项里的owner和deadline是 LLM 从原文抽的,不一定准,下游用之前最好人工过一眼。我一般会在导出后加一个校验步骤,把没有负责人的行动项标红。

4. 避坑与排查:那些让我加班到凌晨的坑

4.1 转写结果全是乱码或空文本

现象:提交音频后返回的transcript字段是空字符串,或者出现大量重复字符。原因:最常见的是音频采样率不匹配。Cluelessly 的 ASR 模块期望 16kHz 单声道,如果输入是 44.1kHz 立体声,重采样环节可能静默失败。另一个原因是音频编码格式不被支持,比如某些 AMR 或 OPUS 变体。解决:先用ffprobe检查音频参数,不对就先转码。命令是ffmpeg -i input.mp3 -ar 16000 -ac 1 output.wav。如果转码后还是空,检查asr.chunk_size是不是设得太小,小于 5 秒的片段可能被 VAD 直接过滤掉。

4.2 说话人分离把一个人切成好几个

现象:speakers字段里出现 5 个以上说话人,但实际会议只有 3 个人。原因:cluster_threshold设得太低,聚类过细。背景噪声、咳嗽、笑声都可能被当成独立说话人。解决:把diarization.cluster_threshold从默认 0.7 调到 0.8 或 0.85,同时把min_speakers设成实际人数。如果还是不行,检查音频里有没有明显的信道差异(比如不同麦克风录的),这种情况需要先做音量归一化。

4.3 纪要生成跑到一半卡住不动

现象:任务状态一直是running,日志里最后一行停在summarizer: generating...。原因:LLM 推理超时或显存不足。长会议(超过 1 小时)的转写文本可能超过模型上下文窗口,导致生成过程被截断或挂起。解决:先看summarizer.max_tokens是不是设得太大,超过模型上限会直接失败。然后把长文本做分段摘要,Cluelessly 支持summarizer.chunked=true,会自动把转写切成多段分别摘要再合并。如果显存不够,把summarizer.device改成cpu,慢但能跑完。

4.4 API 并发一高就返回 500

现象:批量提交时,前几个任务正常,后面全部返回 500 错误。原因:服务端的任务队列是内存级的,没有持久化,并发超过 worker 数时新任务直接拒绝。另外 GPU 推理时多个 worker 抢显存会导致 OOM。解决:把--workers降到 1 到 2,然后在客户端做限流,用队列控制提交速度。如果任务量大,建议在 Cluelessly 前面加一层消息队列,比如用 Redis 做任务缓冲,客户端只负责投递,不直接调 API。

4.5 中文会议里英文术语被转写错

现象:转写结果里“API”变成“A批”,“Kubernetes”变成“库伯耐提斯”。原因:ASR 模型的语言设置是auto,在中英混说时倾向于用中文音译英文词。解决:把asr.language强制设为zh,同时在asr.hotwords里加入常用英文术语列表。Cluelessly 支持热词增强,格式是每行一个词。我一般会把团队常用的技术栈词汇都加进去,转写准确率能提升不少。

5. 进阶:用热词和后处理把纪要质量再拉一档

跑通基础流程后,真正拉开差距的是细节调优。Cluelessly 的asr.hotwords支持动态加载,我习惯在每次会议前根据议程生成一个热词文件。比如这次会议要讨论“推荐系统召回率”,就把“召回率”“协同过滤”“Embedding”加进去。热词文件放在config/hotwords/下,按会议类型分文件,配置里引用对应文件就行。

另一个提质量的手段是后处理。Cluelessly 输出的action_items有时候会把“我们下周看看”这种模糊表述也抽进来,我加了一个过滤规则:只保留包含明确动词(完成、提交、确认、上线)和明确时间(本周、下周一、月底)的条目。规则写在postprocess/filter.py里,用正则匹配,简单但有效。

验证纪要质量我一般用两个指标:行动项召回率和摘要压缩比。召回率靠人工标注几段会议做基准,压缩比是摘要字数除以转写字数,中文会议一般在 5% 到 10% 之间比较合理。低于 5% 说明摘要太简略,高于 15% 说明没抓住重点。我每次调完参数都会跑一遍基准集,对比这两个指标的变化。

从那以后我每次部署 Cluelessly 之前,都会先用一段带背景噪声的双人对话做冒烟测试,确认转写、分离、摘要三个环节都正常再上批量任务。这个习惯帮我省掉了至少三次半夜爬起来排查的麻烦。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

PAT乙级1087 有多少不同的值:从集合去重到容斥公式

PAT乙级1087 有多少不同的值:从集合去重到容斥公式

PAT 乙级 1087——有多少不同的值。题面很短:给一个正整数 N,把 1 到 N 每个整数 i 都算一遍 ⌊i/2⌋ ⌊i/3⌋ ⌊i/5⌋,最后输出这些结果里不同值的个数。第一次看到这道题,多数人第一反应是“这不就是模拟吗”,可真…

2026/10/20 10:32:13 阅读更多 →
OpenArm 新手指南:开源 7 自由度人形机械臂,从 0 到动起来只需 4 步

OpenArm 新手指南:开源 7 自由度人形机械臂,从 0 到动起来只需 4 步

OpenArm 新手指南:开源 7 自由度人形机械臂,从 0 到动起来只需 4 步 【免费下载链接】openarm A fully open-source humanoid arm for physical AI research and deployment in contact-rich environments. 项目地址: https://gitcode.com/GitHub_Tren…

2026/10/20 10:32:13 阅读更多 →
4 步装好 KernelSU meta-overlayfs 元模块:让 /system 修改真正生效

4 步装好 KernelSU meta-overlayfs 元模块:让 /system 修改真正生效

4 步装好 KernelSU meta-overlayfs 元模块:让 /system 修改真正生效 【免费下载链接】KernelSU A Kernel based root solution for Android 项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU 改了 /system 下的文件,重启后全变回原样&…

2026/10/20 10:32:13 阅读更多 →

最新新闻

Terminal-Bench完整指南:4步跑通你的第一个AI终端评测

Terminal-Bench完整指南:4步跑通你的第一个AI终端评测

Terminal-Bench完整指南:4步跑通你的第一个AI终端评测 【免费下载链接】terminal-bench-1 A benchmark for LLMs on complicated tasks in the terminal 项目地址: https://gitcode.com/GitHub_Trending/tb/terminal-bench-1 你的Agent演示很精彩&#xff0c…

2026/10/21 2:02:39 阅读更多 →
免 root 三步跑通 WorkTool:你的企业微信自动回复机器人

免 root 三步跑通 WorkTool:你的企业微信自动回复机器人

免 root 三步跑通 WorkTool:你的企业微信自动回复机器人 【免费下载链接】worktool 一款安全稳定的Android无障碍服务工具,支持控制企微/微信来运行的无人值守群管理企业微信机器人 项目地址: https://gitcode.com/GitHub_Trending/wo/worktool W…

2026/10/21 2:02:39 阅读更多 →
STM32驱动W25Q64 SPI Flash实战:从硬件连接到代码实现

STM32驱动W25Q64 SPI Flash实战:从硬件连接到代码实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/21 2:02:39 阅读更多 →
如何用 Jasna 自动修复 VR180 视频:从分眼处理到投影选择的完整新手教程

如何用 Jasna 自动修复 VR180 视频:从分眼处理到投影选择的完整新手教程

如何用 Jasna 自动修复 VR180 视频:从分眼处理到投影选择的完整新手教程 【免费下载链接】jasna Video restoration tool 项目地址: https://gitcode.com/GitHub_Trending/ja/jasna Jasna 是一款面向 GPU 的视频修复工具,内置 VR180 自动修复能力…

2026/10/21 2:02:39 阅读更多 →
gamdl:Apple Music 无损下载工具完全指南,一键收藏歌曲与 4K 音乐视频

gamdl:Apple Music 无损下载工具完全指南,一键收藏歌曲与 4K 音乐视频

gamdl:Apple Music 无损下载工具完全指南,一键收藏歌曲与 4K 音乐视频 【免费下载链接】gamdl A command-line app for downloading Apple Music songs, music videos and post videos. 项目地址: https://gitcode.com/GitHub_Trending/ga/gamdl …

2026/10/21 2:02:39 阅读更多 →
Django数据可视化实践:超市销售系统全流程开发与热词分析

Django数据可视化实践:超市销售系统全流程开发与热词分析

毕业设计做到一半才意识到"选对题"比“写代码”重要得多——这是我在带几个应届生做完整套毕设流程后最深的体会。今天这篇想好好拆解一个很有代表性的题目:基于PythonDjango的超市销售系统,以及其中数据可视化模块的具体实现。这个选题好在哪…

2026/10/21 2:01:38 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事:AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里,按宣传口径,本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”,而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起:用户到底在追求什么“封号重开”这四个字,放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流,背后是大量的时间和精力成本。正常情况下,没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”,而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时,就意识到:单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →