
前言在构建一个完整的 AI 应用项目时,开发者往往把注意力集中在核心业务逻辑——比如 LangGraph 工作流编排、大模型提示词工程、语音交互等。但真正决定项目可维护性和扩展性的,往往是那些"看不见"的基础设施代码:配置如何管理?路径如何解析?工具函数如何复用?底层能力如何统一封装?本文将以一个基于 LangGraph 的面试语音分析系统为背景,深入拆解其公共模块(common)的设计思路与实现细节。公共模块承担的是"基础设施层"的角色,负责将环境配置、通用工具和底层 AI 能力全部封装就绪,让上层业务代码可以专注于流程与逻辑本身。如果你正在搭建类似的 Python AI 项目,或者希望了解如何优雅地组织项目的基础模块,本文的内容会对你有所帮助。目录一、公共模块总览1.1 模块定位与职责1.2 目录结构1.3 模块依赖关系二、配置管理:.env + config.py2.1 为什么需要配置解耦2.2 .env 文件详解2.3 config.py 配置类设计2.4 关键依赖:python-dotenv2.5 安全注意事项三、路径工具:path_utils.py3.1 为什么需要路径工具3.2 核心实现3.3 使用示例四、时间工具:time_utils.py4.1 设计思路4.2 四个核心方法4.3 使用示例五、大模型调用:llm.py5.1 LangChain + OpenAI 兼容接口5.2 核心实现5.3 调用示例与响应解析六、语音模型:voice_model.py6.1 FunASR 框架简介6.2 模型加载与设备适配6.3 语音转文本实现6.4 使用示例七、Markdown 生成工具:markdown_utils.py7.1 应用场景7.2 核心实现八、图输出工具:output_graph_utils.py8.1 LangGraph 可视化8.2 核心实现九、总结与设计启示一、公共模块总览1.1 模块定位与职责公共模块(common)在项目中的定位可以用一句话概括:不关心"做什么",只关心"怎么做"。它负责三件核心事情:职责说明对应文件配置统一管理将大模型、数据库、语音模型等配置从代码中剥离,集中管理.env、config.py基础能力封装提供路径处理、时间格式化、Markdown 生成等通用工具path_utils.py、time_utils.py、markdown_utils.py核心能力支撑封装大模型调用、语音转文本、流程图可视化等底层能力llm.py、voice_model.py、output_graph_utils.py1.2 目录结构project_root/ ├── .env # 环境变量配置(敏感信息) ├── common/ # 公共模块目录 │ ├── __init__.py # 包初始化文件 │ ├── config.py # 统一配置管理 │ ├── path_utils.py # 路径工具 │ ├── time_utils.py # 时间工具 │ ├── llm.py # 大模型调用封装 │ ├── voice_model.py # 语音模型封装 │ ├── markdown_utils.py # Markdown 文档生成 │ └── output_graph_utils.py # LangGraph 图输出 ├── __000__demo/ # 测试与演示文件 │ └── 语音文件/ │ └── guy.mp3 └── ... # 业务模块1.3 模块依赖关系公共模块内部各文件之间存在明确的依赖关系,理解这个关系有助于避免循环引用问题:.env ──────────────────┐ │ (load_dotenv 读取) ▼ config.py / | \ / | \ ▼ ▼ ▼ llm.py voice_model.py 其他业务模块 \ | \ | ▼ ▼ path_utils.py time_utils.py (独立,无内部依赖) markdown_utils.py (独立,无内部依赖) output_graph_utils.py(独立,仅依赖外部 langgraph 库)关键原则:config.py依赖path_utils.py,其余工具模块相互独立。上层业务模块通过导入common下的模块获取能力,公共模块不反向依赖业务代码。二、配置管理:.env + config.py2.1 为什么需要配置解耦在项目开发中,一个常见的反模式是把 API Key、数据库密码等配置直接硬编码在代码里:# ❌ 反面示例:硬编码配置 llm = ChatOpenAI( api_key="sk-ee14xxxxx", # 写死在代码中 base_url="https://dashscope.xxx", # 环境切换时需要改代码 model="qwen-plus" )这种做法带来的问题:1.安全风险:敏感信息随代码一起提交到版本控制系统(如 Git),一旦仓库泄露,密钥即暴露。2.环境切换困难:开发环境、测试环境、生产环境使用不同的数据库地址或模型时,需要逐个修改代码。3.团队协作摩擦:每个开发者本地的数据库密码不同,合并代码时频繁产生冲突。解耦的核心思路是:代码只负责"读取配置",配置的值存储在代码之外。2.2 .env 文件详解.env文件是一种广泛使用的环境变量存储格式,每行一个键值对,用=连接。本项目的.env文件按功能分为三个区域:# ============================================ # 大模型配置 # ============================================ # MODEL_API_KEY:阿里云通义千问的 API 密钥 MODEL_API_KEY=sk-ee14 # MODEL_BASE_URL:OpenAI 兼容接口地址(DashScope 提供) MODEL_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1 # MODEL_NAME:使用的模型名称 MODEL_NAME=qwen-plus # ============================================ # 语音模型路径 # ============================================ # VOICE_MODEL_PATH:FunASR SenseVoice 语音识别模型本地路径 VOICE_MODEL_PATH=D:/my_duyi_project_dir/my_model/iic/SenseVoiceSmall # VOICE_VAD_MODEL_PATH:语音活动检测(VAD)模型本地路径 VOICE_VAD_MODEL_PATH=D:/my_duyi_project_dir/my_model/iic/speech_fsmn_vad_zh-cn-16k-common-pytorch # ============================================ # MySQL 数据库配置 # ============================================ MYSQL_HOST=localhost MYSQL_USER=root MYSQL_PASSWORD=123456 MYSQL_DATABASE_NAME=interview_voice_class几点说明:.env文件的命名以.开头,在 Linux/macOS 系统中默认为隐藏文件,这是一种约定俗成的安全保护。键名通常使用全大写加下划线的命名风格(如MYSQL_HOST),这是环境变量的通用规范。值不需要加引号(除非值本身包含空格或特殊字符)。.env文件必须加入.gitignore,防止提交到版本库:# .gitignore .env *.env.local2.3 config.py 配置类设计config.py的作用是读取.env文件中的环境变量,并将其封装为一个配置类的属性,方便项目其他模块统一调用。import os from dotenv import load_dotenv from common.path_utils import get_file_path # 加载 .env 文件 # 使用 get_file_path 确保从项目根目录定位 .env,避免工作目录不同导致找不到文件 load_dotenv(get_file_path(".env")) class Config: """ 全局配置类 所有配置项从 .env 文件读取,通过 os.getenv() 获取。 如果 .env 中未设置某项,os.getenv() 返回 None。 """ def __init__(self): # ---- 大模型相关配置 ---- self.MODEL_API