晋升失败后,我用代码重新定义了自己

TypeScript守夜人
2026-07-05 19:06
阅读 406

写在前面:大家好,我是你们的老朋友,一个在大厂搬砖三年、业余在B站做技术UP主的普通程序员。今天这篇文章,我想换个风格,不聊纯技术,聊聊一个真实的故事——一个关于失败、挣扎,最终用技术找回自己的故事。同时,我会把故事里用到的技术,手把手教给你。


一、那个让我失眠的夜晚

2023年底,公司年度晋升答辩。我准备了整整两个月,PPT改了十几版,自认为表现还不错。结果公布那天,名单上没有我的名字。

那一刻的感觉,怎么说呢?就像你精心准备了一场考试,交卷后信心满满,结果成绩出来不及格。

那天晚上我失眠了。脑子里反复在想:是技术不够好?是汇报能力不行?还是人际关系没处理好?

第二天上班,我打开B站后台,看到一条粉丝私信:

"UP主,我看了你的教程,成功转行做了程序员。谢谢你!"

就是这条私信,让我突然清醒过来。

晋升失败,不代表我的人生失败。 与其纠结于不可控的结果,不如把精力放在可控的事情上——提升自己,帮助更多人。

于是,我决定做一个项目:用AI技术,把那些优秀的技术播客音频,自动转成文字稿,做成 searchable 的知识库,免费分享给所有想学习的人。

这个项目,用到了三个核心技术:爬虫AI音频处理Embedding

今天,我就把这个项目的完整实现过程,写成一篇教程,分享给所有零基础的朋友。

因为我相信:代码,可以治愈一切。


二、项目概览:我们要做什么?

先来看看这个项目的整体流程:

┌─────────────┐    ┌──────────────┐    ┌───────────────┐    ┌──────────────┐
│  第一步      │    │  第二步       │    │  第三步        │    │  第四步      │
│  爬虫获取    │───>│  AI音频处理   │───>│  Embedding    │───>│  构建知识库  │
│  音频资源    │    │  语音转文字   │    │  文字向量化   │    │  智能搜索    │
└─────────────┘    └──────────────┘    └───────────────┘    └──────────────┘

简单来说:

  1. 爬虫:从网上获取音频资源
  2. AI音频处理:把音频转成文字(语音识别)
  3. Embedding:把文字变成向量,方便语义搜索
  4. 知识库:用户可以输入问题,系统找到最相关的内容

听起来很复杂?别怕,我们一步一步来。我当初学的时候,也是一脸懵,但只要跟着做,你一定能搞定。


三、环境准备

在开始写代码之前,我们需要先把开发环境搭好。

3.1 安装Python

我们的项目主要使用Python,因为它的生态最丰富,对AI和爬虫的支持最好。

Windows用户:

  1. 访问 https://www.python.org/downloads/
  2. 下载最新的Python 3.10+版本
  3. 安装时务必勾选 "Add Python to PATH"

Mac用户:

brew install python

验证安装:

python --version
# 应该输出 Python 3.10.x 或更高版本

3.2 创建虚拟环境

我当初学的时候,最大的教训就是:永远不要在系统环境里装包,一定要用虚拟环境。 否则各种版本冲突会让你怀疑人生。

# 创建项目文件夹
mkdir tech-knowledge-base
cd tech-knowledge-base

# 创建虚拟环境
python -m venv venv

# 激活虚拟环境
# Windows:
venv\Scripts\activate
# Mac/Linux:
source venv/bin/activate

激活后,你的命令行前面会出现 (venv) 标识。

3.3 安装依赖包

pip install requests beautifulsoup4 openai-whisper sentence-transformers chromadb numpy

各包的作用如下表:

包名 用途 说明
requests 网络请求 爬虫的核心库,用来下载网页和音频
beautifulsoup4 HTML解析 从网页中提取我们需要的信息
openai-whisper 语音识别 OpenAI开源的AI音频转文字模型
sentence-transformers Embedding 把文字转成向量的模型
chromadb 向量数据库 存储和检索向量数据
numpy 数值计算 处理向量运算的基础库

3.4 项目目录结构

tech-knowledge-base/
│
├── venv/                # 虚拟环境(不用管)
├── audio/               # 存放下载的音频文件
├── transcripts/         # 存放转换后的文字稿
├── scraper.py           # 爬虫模块
├── audio_processor.py   # AI音频处理模块
├── embedding_engine.py  # Embedding模块
├── knowledge_base.py    # 知识库搜索模块
├── main.py              # 主程序入口
└── requirements.txt     # 依赖清单

四、核心概念:用最简单的话解释

在动手写代码之前,我们先搞清楚几个核心概念。我用大白话来讲,保证你能听懂。

4.1 什么是爬虫?

一句话解释:爬虫就是帮你自动上网"抄作业"的程序。

你平时上网,看到一篇好文章,会复制粘贴保存下来。如果只有1篇,手动复制没问题。但如果有1000篇呢?手动复制就太累了。

爬虫就是帮你自动完成这个过程的程序。它模拟浏览器的行为,访问网页,提取你想要的内容。

爬虫的基本流程:

发送请求 → 获取网页 → 解析内容 → 提取数据 → 保存结果

4.2 什么是AI音频处理?

一句话解释:让AI帮你"听"音频,然后把听到的内容写成文字。

这个技术的专业名字叫 ASR(Automatic Speech Recognition,自动语音识别)

我们用的是OpenAI开源的 Whisper 模型。它非常强大,支持多语言识别,准确率很高。

AI音频处理的流程:

加载音频 → 预处理(降噪、采样率转换) → AI模型推理 → 输出文字

4.3 什么是Embedding?

一句话解释:把文字变成一组数字(向量),让计算机能"理解"文字的含义。

这是最难理解的概念,我尽量讲清楚。

计算机不认识"苹果"这个词,它只认识数字。Embedding的作用,就是把"苹果"变成一串数字,比如 [0.12, -0.34, 0.56, ...]

关键来了:意思相近的文字,变成的数字也相近。

举个例子:

  • "如何学习Python" → [0.8, 0.2, 0.5, ...]
  • "Python入门教程" → [0.78, 0.22, 0.48, ...]
  • "今天天气怎么样" → [-0.3, 0.9, -0.1, ...]

你会发现,前两个向量的数字很接近,因为它们的意思相近。而第三个向量就和前两个差很远。

这就是Embedding的魔力:它让计算机能理解语义。

Embedding的应用流程:

原始文字 → Embedding模型 → 向量 → 存入向量数据库 → 语义搜索

五、实战项目:手把手带你写代码

好,概念讲完了,我们开始写代码。别紧张,跟着我一步一步来。

5.1 第一步:爬虫获取音频资源

创建 scraper.py

import requests
from bs4 import BeautifulSoup
import os
import time

class AudioScraper:
    """音频爬虫:从目标网站获取音频资源"""
    
    def __init__(self, save_dir="audio"):
        self.save_dir = save_dir
        # 创建保存目录
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)
        
        # 设置请求头,模拟浏览器访问
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                          "AppleWebKit/537.36 (KHTML, like Gecko) "
                          "Chrome/120.0.0.0 Safari/537.36"
        }
    
    def get_page_content(self, url):
        """获取网页内容"""
        try:
            response = requests.get(url, headers=self.headers, timeout=10)
            response.raise_for_status()  # 检查是否请求成功
            response.encoding = response.apparent_encoding  # 自动识别编码
            return response.text
        except requests.RequestException as e:
            print(f"请求失败: {e}")
            return None
    
    def extract_audio_links(self, html_content):
        """从网页中提取音频链接"""
        soup = BeautifulSoup(html_content, "html.parser")
        audio_links = []
        
        # 查找所有包含音频文件的链接
        # 这里以.mp3和.wav为例,根据实际情况调整
        for link in soup.find_all("a", href=True):
            href = link["href"]
            if href.endswith((".mp3", ".wav", ".m4a")):
                audio_links.append({
                    "title": link.get_text(strip=True),
                    "url": href
                })
        
        # 也可以查找<audio>标签
        for audio_tag in soup.find_all("audio"):
            source = audio_tag.find("source")
            if source and source.get("src"):
                audio_links.append({
                    "title": audio_tag.get("title", "未知音频"),
                    "url": source["src"]
                })
        
        return audio_links
    
    def download_audio(self, audio_info):
        """下载单个音频文件"""
        url = audio_info["url"]
        title = audio_info["title"]
        
        # 生成文件名(去除非法字符)
        safe_title = "".join(c for c in title if c.isalnum() or c in "._- ")
        if not safe_title:
            safe_title = "untitled"
        
        # 获取文件扩展名
        ext = os.path.splitext(url)[-1] or ".mp3"
        filename = f"{safe_title}{ext}"
        filepath = os.path.join(self.save_dir, filename)
        
        # 如果文件已存在,跳过
        if os.path.exists(filepath):
            print(f"已存在,跳过: {filename}")
            return filepath
        
        try:
            print(f"正在下载: {title}")
            response = requests.get(url, headers=self.headers, 
                                    stream=True, timeout=30)
            response.raise_for_status()
            
            # 分块写入文件(适合大文件)
            with open(filepath, "wb") as f:
                for chunk in response.iter_content(chunk_size=8192):
                    f.write(chunk)
            
            print(f"下载完成: {filename}")
            return filepath
            
        except requests.RequestException as e:
            print(f"下载失败: {e}")
            return None
    
    def run(self, url):
        """运行爬虫的完整流程"""
        print(f"=" * 50)
        print(f"开始爬取: {url}")
        print(f"=" * 50)
        
        # 第一步:获取网页
        html = self.get_page_content(url)
        if not html:
            print("获取网页失败")
            return []
        
        # 第二步:提取音频链接
        audio_links = self.extract_audio_links(html)
        print(f"找到 {len(audio_links)} 个音频资源")
        
        # 第三步:逐个下载
        downloaded_files = []
        for i, info in enumerate(audio_links, 1):
            print(f"\n[{i}/{len(audio_links)}] 处理中...")
            filepath = self.download_audio(info)
            if filepath:
                downloaded_files.append(filepath)
            
            # 礼貌爬取:每次请求间隔2秒,避免给服务器造成压力
            time.sleep(2)
        
        print(f"\n{'=' * 50}")
        print(f"爬取完成!共下载 {len(downloaded_files)} 个文件")
        print(f"{'=' * 50}")
        
        return downloaded_files

# 测试代码
if __name__ == "__main__":
    scraper = AudioScraper()
    # 注意:这里用一个示例URL,实际使用时替换为目标网站
    # 请确保你有权限爬取目标网站的内容
    files = scraper.run("https://example.com/podcast")

新手常见问题:

问题 原因 解决方案
403 Forbidden 网站检测到爬虫 添加更真实的请求头,使用随机UA
乱码 编码不匹配 使用 response.apparent_encoding 自动检测
下载超时 网络不稳定 增加 timeout 参数,添加重试机制
被反爬 请求太频繁 增加 time.sleep() 间隔

5.2 第二步:AI音频处理(语音转文字)

创建 audio_processor.py

import whisper
import os
import json
import torch

class AudioProcessor:
    """AI音频处理器:使用Whisper模型将音频转为文字"""
    
    def __init__(self, model_size="base", save_dir="transcripts"):
        self.save_dir = save_dir
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)
        
        # 检查是否有GPU可用
        self.device = "cuda" if torch.cuda.is_available() else "cpu"
        print(f"使用设备: {self.device}")
        
        # 加载Whisper模型
        # 模型大小选择指南:
        # - tiny:   最快,准确率一般,适合测试
        # - base:   速度和准确率平衡,推荐入门使用
        # - small:  准确率较好,需要更多内存
        # - medium: 准确率高,需要较大显存
        # - large:  最高准确率,需要很大显存(10GB+)
        print(f"正在加载Whisper模型 ({model_size}),首次使用需要下载...")
        self.model = whisper.load_model(model_size, device=self.device)
        print("模型加载完成!")
    
    def transcribe_audio(self, audio_path):
        """将单个音频文件转为文字"""
        if not os.path.exists(audio_path):
            print(f"文件不存在: {audio_path}")
            return None
        
        print(f"\n正在处理: {os.path.basename(audio_path)}")
        print("这一步可能需要几分钟,请耐心等待...")
        
        try:
            # Whisper的核心方法:transcribe
            result = self.model.transcribe(
                audio_path,
                language="zh",     # 指定语言为中文,提高准确率
                task="transcribe", # transcribe=转录, translate=翻译成英文
                verbose=False      # 不显示进度条(在代码中)
            )
            
            # 提取文字内容
            full_text = result["text"].strip()
            segments = result["segments"]  # 包含时间戳的分段信息
            
            return {
                "full_text": full_text,
                "segments": segments,
                "language": result.get("language", "unknown")
            }
            
        except Exception as e:
            print(f"处理失败: {e}")
            return None
    
    def save_transcript(self, transcript_data, audio_path):
        """保存转录结果"""
        if not transcript_data:
            return None
        
        # 生成输出文件名
        base_name = os.path.splitext(os.path.basename(audio_path))[0]
        output_path = os.path.join(self.save_dir, f"{base_name}.txt")
        
        # 保存纯文本
        with open(output_path, "w", encoding="utf-8") as f:
            f.write(transcript_data["full_text"])
        
        # 同时保存带时间戳的JSON格式
        json_path = os.path.join(self.save_dir, f"{base_name}.json")
        with open(json_path, "w", encoding="utf-8") as f:
            json.dump({
                "full_text": transcript_data["full_text"],
                "segments": [
                    {
                        "start": seg["start"],
                        "end": seg["end"],
                        "text": seg["text"]
                    }
                    for seg in transcript_data["segments"]
                ]
            }, f, ensure_ascii=False, indent=2)
        
        print(f"文字稿已保存: {output_path}")
        return output_path
    
    def process_batch(self, audio_files):
        """批量处理音频文件"""
        results = []
        
        for i, audio_path in enumerate(audio_files, 1):
            print(f"\n{'=' * 50}")
            print(f"[{i}/{len(audio_files)}] 处理音频文件")
            print(f"{'=' * 50}")
            
            # 转录
            transcript = self.transcribe_audio(audio_path)
            
            # 保存
            if transcript:
                saved_path = self.save_transcript(transcript, audio_path)
                results.append({
                    "audio": audio_path,
                    "transcript": saved_path,
                    "text_length": len(transcript["full_text"])
                })
        
        # 打印汇总
        print(f"\n{'=' * 50}")
        print(f"批量处理完成!")
        print(f"成功: {len(results)}/{len(audio_files)}")
        print(f"{'=' * 50}")
        
        return results

# 测试代码
if __name__ == "__main__":
    processor = AudioProcessor(model_size="base")
    
    # 获取audio目录下所有音频文件
    audio_dir = "audio"
    audio_files = [
        os.path.join(audio_dir, f) 
        for f in os.listdir(audio_dir) 
        if f.endswith((".mp3", ".wav", ".m4a"))
    ]
    
    if audio_files:
        processor.process_batch(audio_files)
    else:
        print("audio目录下没有找到音频文件")

Whisper模型选择指南:

模型 参数量 显存需求 速度 准确率 推荐场景
tiny 39M ~1GB 最快 一般 快速测试
base 74M ~1GB 较好 入门推荐
small 244M ~2GB 中等 日常使用
medium 769M ~5GB 较慢 很好 追求质量
large 1550M ~10GB 最好 生产环境

5.3 第三步:Embedding向量化

创建 embedding_engine.py

from sentence_transformers import SentenceTransformer
import numpy as np

class EmbeddingEngine:
    """Embedding引擎:将文字转换为向量"""
    
    def __init__(self, model_name="shibing624/text2vec-base-chinese"):
        """
        初始化Embedding模型
        
        参数:
            model_name: 模型名称
            - "shibing624/text2vec-base-chinese": 中文专用,推荐
            - "sentence-transformers/all-MiniLM-L6-v2": 英文专用,轻量
            - "BAAI/bge-base-zh-v1.5": 中文,效果好
        """
        print(f"正在加载Embedding模型: {model_name}")
        self.model = SentenceTransformer(model_name)
        print("Embedding模型加载完成!")
    
    def encode_text(self, text):
        """将单段文字转为向量"""
        embedding = self.model.encode(text, normalize_embeddings=True)
        return embedding
    
    def encode_texts(self, texts):
        """批量将文字转为向量"""
        embeddings = self.model.encode(
            texts, 
            normalize_embeddings=True,  # 归一化,方便后续计算余弦相似度
            show_progress_bar=True,     # 显示进度条
            batch_size=32               # 批处理大小
        )
        return embeddings
    
    def split_text_into_chunks(self, text, chunk_size=200, overlap=50):
        """
        将长文本切分成小块
        
        为什么要切分?
        1. Embedding模型对输入长度有限制(通常512个token)
        2. 小块文本的语义更集中,搜索更精准
        3. 方便定位到原文的具体位置
        
        参数:
            text: 原始文本
            chunk_size: 每块的字符数
            overlap: 相邻块重叠的字符数(保证语义连贯)
        """
        chunks = []
        start = 0
        
        while start < len(text):
            end = start + chunk_size
            chunk = text[start:end]
            
            # 尝试在句号处断开,避免切断句子
            if end < len(text):
                last_period = chunk.rfind("。")
                if last_period > chunk_size // 2:
                    chunk = chunk[:last_period + 1]
                    end = start + last_period + 1
            
            chunks.append(chunk.strip())
            start = end - overlap  # 重叠部分
        
        return chunks
    
    def get_similarity(self, vec1, vec2):
        """计算两个向量的余弦相似度"""
        return np.dot(vec1, vec2)  # 因为已经归一化,点积就是余弦相似度

# 测试代码
if __name__ == "__main__":
    engine = EmbeddingEngine()
    
    # 测试文本切分
    long_text = "Python是一门优秀的编程语言。它语法简洁,易于学习。" * 20
    chunks = engine.split_text_into_chunks(long_text, chunk_size=100, overlap=20)
    print(f"切分成 {len(chunks)} 块")
    
    # 测试Embedding
    texts = ["如何学习Python", "Python入门教程", "今天天气真好"]
    embeddings = engine.encode_texts(texts)
    
    print(f"\n向量维度: {embeddings.shape}")  # 应该是 (3, 768)
    
    # 计算相似度
    sim_01 = engine.get_similarity(embeddings[0], embeddings[1])
    sim_02 = engine.get_similarity(embeddings[0], embeddings[2])
    
    print(f"\n'如何学习Python' vs 'Python入门教程': {sim_01:.4f}")
    print(f"'如何学习Python' vs '今天天气真好': {sim_02:.4f}")
    print("\n可以看到,语义相近的句子,相似度更高!")

5.4 第四步:构建知识库与智能搜索

创建 knowledge_base.py

import chromadb
from embedding_engine import EmbeddingEngine
import os
import json

class KnowledgeBase:
    """知识库:存储向量数据并提供语义搜索"""
    
    def __init__(self, db_path="./chroma_db"):
        # 初始化ChromaDB客户端
        self.client = chromadb.PersistentClient(path=db_path)
        
        # 创建或获取集合
        self.collection = self.client.get_or_create_collection(
            name="tech_knowledge",
            metadata={"description": "技术知识库"}
        )
        
        # 初始化Embedding引擎
        self.embedding_engine = EmbeddingEngine()
        
        print(f"知识库已初始化,当前有 {self.collection.count()} 条记录")
    
    def add_document(self, text, metadata=None):
        """添加单个文档到知识库"""
        # 切分文本
        chunks = self.embedding_engine.split_text_into_chunks(text)
        
        if not chunks:
            print("文本为空,跳过")
            return
        
        # 生成Embedding
        embeddings = self.embedding_engine.encode_texts(chunks)
        
        # 生成唯一ID
        ids = [f"doc_{self.collection.count()}_{i}" for i in range(len(chunks))]
        
        # 存入数据库
        self.collection.add(
            ids=ids,
            embeddings=embeddings.tolist(),
            documents=chunks,
            metadatas=[metadata or {}] * len(chunks)
        )
        
        print(f"已添加 {len(chunks)} 个文本块")
    
    def add_from_file(self, filepath):
        """从文件添加文档"""
        with open(filepath, "r", encoding="utf-8") as f:
            text = f.read()
        
        metadata = {
            "source": os.path.basename(filepath),
            "type": "transcript"
        }
        
        self.add_document(text, metadata)
    
    def add_batch_from_directory(self, directory):
        """从目录批量添加文档"""
        files = [f for f in os.listdir(directory) if f.endswith(".txt")]
        
        print(f"找到 {len(files)} 个文本文件")
        
        for i, filename in enumerate(files, 1):
            filepath = os.path.join(directory, filename)
            print(f"\n[{i}/{len(files)}] 添加: {filename}")
            self.add_from_file(filepath)
        
        print(f"\n批量添加完成!知识库共有 {self.collection.count()} 条记录")
    
    def search(self, query, top_k=5):
        """
        语义搜索
        
        参数:
            query: 搜索问题
            top_k: 返回最相关的k条结果
        """
        # 将查询转为向量
        query_embedding = self.embedding_engine.encode_text(query)
        
        # 在数据库中搜索
        results = self.collection.query(
            query_embeddings=[query_embedding.tolist()],
            n_results=top_k
        )
        
        # 格式化输出
        formatted_results = []
        if results["documents"] and results["documents"][0]:
            for i, (doc, meta, distance) in enumerate(zip(
                results["documents"][0],
                results["metadatas"][0],
                results["distances"][0]
            )):
                similarity = 1 - distance  # 转换为相似度
                formatted_results.append({
                    "rank": i + 1,
                    "text": doc,
                    "source": meta.get("source", "unknown"),
                    "similarity": similarity
                })
        
        return formatted_results
    
    def interactive_search(self):
        """交互式搜索"""
        print("\n" + "=" * 50)
        print("  技术知识库 - 智能搜索")
        print("  输入 'quit' 或 'q' 退出")
        print("=" * 50)
        
        while True:
            query = input("\n请输入你的问题: ").strip()
            
            if query.lower() in ("quit", "q", "exit"):
                print("再见!")
                break
            
            if not query:
                continue
            
            results = self.search(query, top_k=3)
            
            if not results:
                print("没有找到相关内容")
                continue
            
            print(f"\n找到 {len(results)} 条相关结果:")
            print("-" * 40)
            
            for r in results:
                print(f"\n【第{r['rank']}条】相似度: {r['similarity']:.2%}")
                print(f"来源: {r['source']}")
                print(f"内容: {r['text'][:200]}...")

# 测试代码
if __name__ == "__main__":
    kb = KnowledgeBase()
    
    # 从transcripts目录加载数据
    transcript_dir = "transcripts"
    if os.path.exists(transcript_dir):
        kb.add_batch_from_directory(transcript_dir)
    
    # 启动交互式搜索
    kb.interactive_search()

5.5 主程序:串联所有模块

创建 main.py

"""
技术知识库 - 主程序
功能:爬取音频 → AI转文字 → Embedding向量化 → 智能搜索
"""

from scraper import AudioScraper
from audio_processor import AudioProcessor
from knowledge_base import KnowledgeBase
import os

def print_banner(text):
    print(f"\n{'=' * 60}")
    print(f"  {text}")
    print(f"{'=' * 60}\n")

def main():
    print_banner("技术知识库构建系统")
    
    # 配置参数
    TARGET_URL = "https://example.com/podcast"  # 替换为目标URL
    AUDIO_DIR = "audio"
    TRANSCRIPT_DIR = "transcripts"
    
    # ===== 阶段一:爬取音频 =====
    print_banner("阶段一:爬取音频资源")
    scraper = AudioScraper(save_dir=AUDIO_DIR)
    audio_files = scraper.run(TARGET_URL)
    
    if not audio_files:
        print("没有获取到音频文件,程序结束")
        return
    
    # ===== 阶段二:AI音频转文字 =====
    print_banner("阶段二:AI音频处理(语音转文字)")
    processor = AudioProcessor(model_size="base", save_dir=TRANSCRIPT_DIR)
    processor.process_batch(audio_files)
    
    # ===== 阶段三:构建知识库 =====
    print_banner("阶段三:构建知识库(Embedding + 向量存储)")
    kb = KnowledgeBase()
    kb.add_batch_from_directory(TRANSCRIPT_DIR)
    
    # ===== 阶段四:智能搜索 =====
    print_banner("阶段四:智能搜索")
    kb.interactive_search()

if __name__ == "__main__":
    main()

六、运行项目

一切准备就绪,运行主程序:

python main.py

你会看到这样的输出流程:

============================================================
  技术知识库构建系统
============================================================

============================================================
  阶段一:爬取音频资源
============================================================
开始爬取: https://example.com/podcast
找到 3 个音频资源
正在下载: 技术播客第1期
下载完成: 技术播客第1期.mp3
...

============================================================
  阶段二:AI音频处理(语音转文字)
============================================================
使用设备: cpu
正在加载Whisper模型 (base),首次使用需要下载...
模型加载完成!
正在处理: 技术播客第1期.mp3
文字稿已保存: transcripts/技术播客第1期.txt
...

============================================================
  阶段三:构建知识库
============================================================
正在加载Embedding模型...
Embedding模型加载完成!
找到 3 个文本文件
已添加 15 个文本块
...

============================================================
  阶段四:智能搜索
============================================================
  技术知识库 - 智能搜索
  输入 'quit' 或 'q' 退出
============================================================

请输入你的问题: 如何学习Python?

找到 3 条相关结果:
----------------------------------------
【第1条】相似度: 89.23%
来源: 技术播客第1期.txt
内容: Python是一门非常适合初学者的编程语言,它的语法简洁明了...

【第2条】相似度: 85.67%
来源: 技术播客第2期.txt
内容: 学习Python最好的方式就是动手实践,从小项目开始...

七、常见问题解答

问题 原因 解决方案
ModuleNotFoundError 依赖没装好 确认虚拟环境已激活,重新 pip install -r requirements.txt
Whisper下载很慢 模型文件较大 使用国内镜像源,或提前手动下载模型
内存不足 (OOM) 模型太大 换用 tinybase 模型
中文识别不准确 语言参数没设置 确保 language="zh"
搜索结果不相关 文本切分不合理 调整 chunk_sizeoverlap 参数
爬虫被封IP 请求太频繁 增加请求间隔,使用代理池

八、回到那个失眠的夜晚

教程写到这里,技术部分就讲完了。但我想再多说几句。

那个晋升失败的夜晚之后,我没有选择躺平,也没有选择抱怨。我把所有的精力都投入到了这个项目中。

白天在大厂正常上班,晚上回家就写代码、录视频、写教程。周末的时候,我会把做好的知识库分享给社区里的朋友。

三个月后,我的B站粉丝从5000涨到了3万。很多粉丝私信我说,他们通过我的教程,成功入门了编程,甚至找到了工作。

那一刻我意识到:晋升只是一个标签,但帮助他人成长带来的成就感,是任何标签都比不了的。

后来,我把这个项目的经历写进了下一次晋升的PPT里。不是为了炫耀,而是想说明:一个工程师的价值,不仅仅是完成KPI,更在于他能创造多少额外的价值。

结果?我晋升成功了。

但说实话,那个时候我已经不太在意结果了。因为在这个过程中,我已经成长了太多。


九、学习建议与下一步

如果你跟着这篇教程做完了项目,恭喜你,你已经掌握了爬虫、AI音频处理和Embedding的基础。接下来,你可以往这些方向继续深入:

9.1 爬虫进阶

  • 学习 Scrapy 框架,处理更复杂的爬取场景
  • 了解 Selenium/Playwright,处理JavaScript渲染的页面
  • 学习 分布式爬虫,提高爬取效率

9.2 AI音频进阶

  • 尝试 fine-tune Whisper,针对特定领域优化识别效果
  • 学习 说话人分离(Speaker Diarization),区分不同说话人
  • 探索 实时语音识别,做直播字幕等应用

9.3 Embedding进阶

  • 学习 RAG(检索增强生成),结合大语言模型做智能问答
  • 了解 向量数据库 的高级用法(Milvus、Pinecone等)
  • 探索 多模态Embedding,同时处理文本、图片、音频

9.4 推荐学习资源

方向 资源 说明
爬虫 《Python爬虫实战》 系统学习爬虫技术
AI音频 Hugging Face Whisper文档 官方文档最权威
Embedding 《动手学深度学习》 李沐老师的经典教程
综合 B站搜索我的账号 我会持续更新相关教程

十、写在最后

如果你正在经历挫折,请记住:

失败不是终点,放弃才是。

代码不会骗你。你写的每一行代码,都在让你变得更强。你帮的每一个人,都在让这个世界变得更好。

晋升失败算什么?大不了从头再来。

用代码重新定义自己,用技术温暖更多人。

我们下期见。


作者信息

  • B站技术UP主,专注分享编程教程
  • 大厂3年开发经验,热爱技术,热爱分享
  • 如果这篇教程对你有帮助,欢迎点赞、投币、收藏、转发!
  • 有问题可以在评论区留言,我会一一回复

评论 0

最热最新
暂无评论
TypeScript守夜人Lv.1
0
影响力
0
文章
0
粉丝