Dograh 开源语音 AI 平台本地开发环境搭建:以 Devcontainer 为正式贡献者工作流的完整指南
【免费下载链接】dograhOpen source voice AI platform. Self-hosted alternative to Vapi and Retell. On Prem, BYOK across Speech to Speech or LLM/STT/TTS, with a visual workflow builder, MCP native and telephony support.项目地址: https://gitcode.com/GitHub_Trending/do/dograh
本文围绕 scripts/setup_local.devcontainer.md 展开,讲清 Dograh(一个可自托管的开源语音 AI 平台,定位为 Vapi/Retell 的自部署替代方案)中两类“本地运行”脚本的分工:setup_local.sh/setup_local.ps1服务于 Docker 部署,而仓库内建的.devcontainer/才是日常代码贡献的官方开发环境。读完本文,你将掌握 devcontainer 的镜像构建原理、venv 预置与同步机制、Postgres/Redis/MinIO 三个基础服务的自动编排方式,以及容器内启动后端与 UI 的完整日常开发流程。
先分清边界:setup_local.sh 是部署脚本,不是开发环境
scripts/setup_local.devcontainer.md 开篇就明确了一个容易被误用的事实:
setup_local.sh和setup_local.ps1负责为本地部署准备 OSS(Open Source)Docker 栈。它们不是本仓库推荐的贡献者工作流。
这一点值得展开。查看 scripts/setup_local.sh 的实现可以看到,它做的事情与“写代码”关系不大:
- 拉取部署文件:从仓库
main分支下载docker-compose.yaml(可通过DOGRAH_SKIP_DOWNLOAD=1跳过,改用当前目录的 compose 文件); - 生成生产取向的
.env:用openssl rand -hex 32随机生成OSS_JWT_SECRET、POSTGRES_PASSWORD、REDIS_PASSWORD、MinIO 根凭据等,并写入容器镜像注册表地址(默认REGISTRY=ghcr.io/dograh-hq)与遥测开关ENABLE_TELEMETRY; - 可选启用 coturn:交互式询问是否开启 TURN 服务器用于 WebRTC NAT 穿透,需指定浏览器与 API 容器都能访问的
TURN_HOST和TURN_SECRET(脚本内注释特别解释了为什么 127.0.0.1 不可用——API 容器自身的回环地址并不是 coturn 所在网络接口); - 启动命令:
docker compose --profile tunnel up --pull always,通过 Cloudflare quick tunnel 暴露一个临时公网 URL 以便入站电话 webhook 能到达本地 API,最终应用访问地址为http://localhost:3010。
也就是说,setup_local.sh模拟的是部署者的视角:拉官方镜像、起完整栈、打通公网隧道。而贡献者需要的是能改代码、跑测试、断点调试的环境——这正是 devcontainer 要解决的问题。
Devcontainer 是官方贡献者工作流
scripts/setup_local.devcontainer.md 给出的推荐路径是:日常开发请使用仓库内建的.devcontainer/目录,完整贡献者说明位于 docs/contribution/setup.mdx。
前置条件(引自 docs/contribution/setup.mdx):
- Git;
- 本地 Docker 引擎(如 Docker Desktop);
- 安装了 Dev Containers 扩展的 VS Code。
标准流程共五步:
- Fork 仓库并克隆你自己的 fork(而不是上游仓库,避免
origin指向只读仓库); - 在 VS Code 中打开文件夹,执行Dev Containers: Reopen in Container。首次构建需要数分钟——它会启动 Postgres、Redis、MinIO,预置 Python venv,创建
.env文件并安装 UI 依赖。之后的打开速度很快; - 容器内终端启动后端:
bash scripts/start_services_dev.sh该脚本会等待健康检查通过后才退出,正常退出即代表后端已就绪; 4. 第二个终端启动 UI:
cd ui && npm run dev -- --hostname 0.0.0.0- 浏览器打开
http://localhost:3000。
如果误克隆了上游仓库而非自己的 fork,可运行bash scripts/setup_fork.sh,它会提示输入 fork 地址、把origin指向 fork,并添加upstream远端。没有 VS Code 的用户也可以用 Dev Container CLI 以无头方式运行 devcontainer,或者完全脱离容器在宿主机上管理环境(参见 docs/contribution/setup.mdx 中的折叠说明)。
容器是怎么建出来的:.devcontainer/Dockerfile 的两阶段构建
devcontainer 的核心是 .devcontainer/Dockerfile,它采用两阶段构建,注释写得非常清楚,值得逐段理解。
阶段一:venv-builder —— 唯一的任务是填充 venv
基础镜像是ubuntu:24.04,关键安装步骤:
- 通过
ppa:deadsnakes/ppa安装Python 3.13(python3.13、python3.13-venv、python3.13-dev),这就是文档中“pinned Python 3.13”的落地方式; - 从官方 uv 镜像
ghcr.io/astral-sh/uv:latest拷贝uv/uvx可执行文件; - 以
VIRTUAL_ENV=/workspaces/dograh/venv创建 venv。在最终镜像中 venv 真实存在的路径上构建,是为了保证 venv 内的 shebang 和 console-scripts 符号链接在 rsync 到命名卷后仍然有效; - 用 uv 分两层安装依赖以最大化构建缓存:
- Layer 1:
api/requirements.txt+api/requirements.dev.txt(仅当这两个文件变化时缓存失效); - Layer 2:pipecat 子模块及其一组 extras(
cartesia,deepgram,openai,elevenlabs,groq,google,azure,sarvam,soundfile,silero,webrtc,speechmatics,openrouter,camb,mcp,inworld,smallest)和 dev group。
- Layer 1:
pipecat 装完后有两处“加固”处理(与api/Dockerfile保持镜像同步):
- 把
pipecat[webrtc]拉进来的opencv-python换成opencv-python-headless——非 headless 构建链接 X11/Qt(libxcb*),镜像里缺这些共享库时import cv2会在运行时失败; - 预下载 NLTK 的
punkt_tab分词器到/workspaces/dograh/venv/nltk_data,避免 pipecat 文本处理在第一次 agent 运行时访问网络。NLTK 会自动从sys.prefix/nltk_data找到它,因此随 venv 一起被拷贝/同步。
阶段二:运行时 devcontainer 镜像
基于mcr.microsoft.com/devcontainers/base:ubuntu-24.04(自带vscode用户、sudo 等 devcontainer 基础设施),安装运行时工具链:ffmpeg、jq、libpq-dev、postgresql-client、redis-tools、rsync、procps等,同样从 deadsnakes 安装 Python 3.13,并保留 uv(供post-create.sh做 editable 安装及用户临时uv pip install)。
随后把阶段一填好的 venv 复制到/opt/venv-template,并写入时间戳文件.build-stamp:
COPY --from=venv-builder --chown=vscode:vscode /workspaces/dograh/venv /opt/venv-template RUN date -u +%s > /opt/venv-template/.build-stamp这一步是整个 devcontainer 设计的精妙之处:运行时命名卷会“遮蔽”/workspaces/dograh/venv(避免重建容器时丢失已装好的环境),而镜像里保留一份“模板”+ 构建时间戳,让初始化脚本可以在依赖真正变化时才重新播种——后文会看到。
服务编排:两个 compose 文件如何拼出完整开发栈
.devcontainer/devcontainer.json 声明了dockerComposeFile指向两个文件,按顺序合并:
- docker-compose-local.yaml:提供三个基础设施服务;
- .devcontainer/docker-compose.yml:提供
workspace容器本身。
基础设施层:postgres / redis / minio
docker-compose-local.yaml 中三个服务均定义了 healthcheck 并接入同一个 bridge 网络app-network:
| 服务 | 镜像 | 关键配置 |
|---|---|---|
| postgres | pgvector/pgvector:pg17 | 用户/密码/库均为postgres;pg_isready健康检查;数据卷postgres_data |
| redis | redis:7 | --requirepass redissecret;redis-cli -a redissecret ping健康检查 |
| minio | quay.io/minio/minio | server /data --console-address ":9001",根账号minioadmin/minioadmin;S3 API 与 Console 端口9000/9001仅绑定到127.0.0.1(显式 localhost 绑定,避免直接暴露到局域网) |
注意 Postgres 用的是pgvector 扩展版镜像,这与仓库的知识库/检索能力(embedding 存储)相呼应;Redis 密码redissecret与 api/.env.example 中REDIS_URL的默认值一一对应。
工作区层:workspace 容器
.devcontainer/docker-compose.yml 定义了workspace服务:
build指向.devcontainer/Dockerfile,命令为sleep infinity(长驻容器,进程由用户手动启动);depends_on要求三个基础服务先通过健康检查才启动 workspace;- 卷挂载:源码
.:/workspaces/dograh:cached(Bind 挂载 + 缓存驱动),以及三个命名卷——dograh-venv→/workspaces/dograh/venv、dograh-ui-node_modules→ui/node_modules、dograh-ts-validator-node_modules→api/mcp_server/ts_validator/node_modules; - 端口映射
0.0.0.0:3000:3000与0.0.0.0:8000:8000(UI 与 API); extra_hosts添加host.docker.internal:host-gateway,方便容器内回访问宿主机;security_opt: seccomp=unconfined / apparmor=unconfined与cap_add: SYS_ADMIN——这是为了让容器内可以调试、跑需要较宽系统调用的进程(如实时语音管道、浏览器自动化测试类场景),是 devcontainer 场景常见的放宽配置,但应意识到它只应存在于开发环境。
devcontainer.json 其余关键配置:
initializeCommand: git submodule update --init --recursive——因为pipecat/是 git 子模块(语音管道 STT → LLM → TTS 的载体),初始化阶段先同步子模块再开始构建;runServices同时拉起workspace, postgres, redis, minio;shutdownAction: stopCompose保证关闭容器时整个栈一起停;forwardPorts自动转发 5432/6379/9000/9001,方便宿主机的 DB 客户端直连;portsAttributes里 3000(Dograh UI)和 8000(Dograh API)设为onAutoForward: ignore,避免 VS Code 端口检测器在端口未绑定前反复轮询刷出 ECONNREFUSED 日志——post-start.sh 顶部的注释解释了这一取舍;features安装 Node.js 24;customizations.vscode预装 Python/Pylance/Debugpy/Docker/ESLint/Prettier 扩展,并把 Python 解释器固定到/workspaces/dograh/venv/bin/python;- 远程用户为
vscode(非 root),与 venv 模板的chown vscode:vscode保持一致。
首次进入容器:post-create.sh 自动完成的五步
devcontainer.json 中postCreateCommand指向 .devcontainer/scripts/post-create.sh,这是“安装后端与前端依赖、创建容器专属 API env 文件、自动启动 Postgres/Redis/MinIO”这一承诺的具体实现。脚本带进度打印([n/5])、每步计时和set -euo pipefail+trap ERR的失败上报。五个步骤:
1. 修正命名卷挂载点的所有权
命名卷由 Docker 以 root 创建,而 postCreate 以vscode用户运行,因此先sudo chown三个挂载点:venv、ui/node_modules、api/mcp_server/ts_validator/node_modules。
2. 从镜像模板播种 venv(基于 build-stamp 的增量同步)
seed_venv函数比较镜像模板的.build-stamp与卷内现有 stamp:相同则跳过(输出Venv already in sync with image template),不同则rsync -a --delete /opt/venv-template/ /workspaces/dograh/venv。效果是:只有当.devcontainer/、api/requirements*.txt或 pipecat 源变化导致镜像重建时才会重灌 venv,与 docs/contribution/setup.mdx 中“仅当.devcontainer/、api/requirements*.txt或pipecat/变化才需要重建容器”的说明严格对应。
3. 生成容器专属的 env 文件(主机名重写)
copy_env_with_docker_hostnames复制模板并把基础设施主机名从localhost改写为 compose 服务名:
sed -i \ -e 's|@localhost:5432|@postgres:5432|g' \ -e 's|@localhost:6379|@redis:6379|g' \ -e 's|^MINIO_ENDPOINT=localhost:9000|MINIO_ENDPOINT=minio:9000|' \ "$dst"涉及三个文件:api/.env.example→api/.env、api/.env.test.example→api/.env.test(测试环境供 pytest 使用)、ui/.env.example→ui/.env。已有文件一律保留(“Keeping existing …”),不覆盖用户的本地修改。
这里有一个刻意不重写的细节,脚本注释写得很明白:MINIO_PUBLIC_ENDPOINT保持localhost:9000——因为该 URL 会出现在 UI 的响应中,最终由宿主机上的浏览器经 VS Code 端口转发加载,而不是由容器内进程访问。对照 api/.env.example,被重写的变量正是其中的:
DATABASE_URL="postgresql+asyncpg://postgres:postgres@localhost:5432/postgres"→@postgres:5432;REDIS_URL="redis://:redissecret@localhost:6379"→@redis:6379;MINIO_ENDPOINT=localhost:9000→minio:9000(凭据minioadmin/minioadmin、bucketvoice-audio与 compose 中 MinIO 配置一致)。
模板还包含BACKEND_API_ENDPOINT、UI_APP_URL、LOG_LEVEL=DEBUG、ENABLE_SIGNUP、Langfuse 追踪凭据等条目,均可在生成后的api/.env中直接调整。
4. 把 pipecat 切换为可编辑安装
uv pip install -e "$ROOT_DIR/pipecat" --no-deps播种的 venv 里 pipecat 依赖是构建时的冻结快照;这一步重新以 editable 方式注册绑定挂载的工作区中的pipecat 源码,使得对pipecat/的源码编辑立即生效;--no-deps跳过重新解析传递依赖(已由种子镜像满足)。editable 安装也是后续调试能力的前提——docs/contribution/setup.mdx 说明,因为 pipecat 以 editable 方式安装,.vscode/launch.json中justMyCode: false时可以直接在pipecat/子模块内打断点。
5. 并行安装 npm 依赖
npm ci --prefix ui & npm ci --prefix api/mcp_server/ts_validator & wait || fail两处npm ci并行执行并分别等待,任一失败立即报错退出。注意这两处node_modules都是命名卷,跨容器重建保留。
脚本最后还会检查一个可选的私人钩子.devcontainer/install.local.sh(gitignore 的按开发者定制脚本,例如安装个人 AI 编程工具),存在则执行,不存在则安全跳过。
再次打开容器时:post-start.sh
postStartCommand指向 .devcontainer/scripts/post-start.sh,它不再做任何安装,只打印启动提示(刻意不打印http://localhost:PORT形式的 URL,避免 VS Code 终端 URL 检测器把端口加入自动转发列表后持续轮询产生 ECONNREFUSED 日志):
Start the backend: bash scripts/start_services_dev.sh Start the UI in another terminal: cd ui && npm run dev -- --hostname 0.0.0.0容器内的日常开发工作流
后端由 scripts/start_services_dev.sh 统一管理。从脚本头部配置可以读出其工作方式:加载api/.env(可用DOGRAH_ENV_FILE覆盖)、PID 文件存放于run/目录、日志按时间戳写入logs/<timestamp>/并维护logs/latest软链、对/api/v1/health做健康检查(默认最多 30 次、间隔 2 秒)通过后才算启动成功、Uvicorn 基于端口默认 8000 并对api/目录变更自动热重载。
docs/contribution/setup.mdx 汇总的日常操作速查表(完整继承):
| 场景 | 做法 |
|---|---|
| 重启后端 | 重跑bash scripts/start_services_dev.sh——它会先停掉旧进程 |
| 停止后端 | bash scripts/stop_services.sh |
| 查看后端日志 | tail -f logs/latest/*.log |
| 代码热重载 | api/下的编辑自动重载;ari_manager、campaign_orchestrator、arq需要重启后端 |
| 重建容器 | 仅当.devcontainer/、api/requirements*.txt或pipecat/变化时——普通源码编辑永远不需要 |
| 同步 pipecat 子模块 | 拉取到子模块版本提升后执行git submodule update --init --recursive |
调试
仓库在 .vscode/launch.json 中为每个后端服务和 pytest 都提供了调试配置。用调试器取代启动脚本的流程:
- 先停掉脚本托管的后端以释放端口:
bash scripts/stop_services.sh; - 在 VS Code 的Run and Debug面板选择配置并按 F5:
| 配置 | 运行内容 |
|---|---|
| API: Uvicorn (reload) | FastAPI 后端,带自动热重载(端口取api/.env中UVICORN_PORT,默认 8000) |
| API: Arq worker (watch)/API: Campaign orchestrator/API: ARI manager | 其余后端服务,按需与 Uvicorn 并行启动 |
| Tests: API (pytest, full suite / current file) | 调试器下的 pytest,针对api/.env.test |
| Tests: Pipecat (pytest, current file)/Python: Current file | 调试 pipecat 测试或任意独立脚本 |
所有配置均加载api/.env(测试配置加载api/.env.test)并设置justMyCode: false,因此可以单步进入 FastAPI 与 pipecat 内部代码。
与仓库结构、贡献流程的衔接
理解 devcontainer 在开发中的位置,离不开它服务的仓库布局(引自 docs/contribution/setup.mdx):
| 路径 | 内容 |
|---|---|
ui/ | Next.js 前端——工作流构建器、仪表盘与 agent 编辑器 |
api/ | FastAPI 后端——REST API、campaign 编排、电话接入、ARQ 后台 worker |
pipecat/ | 语音管道(STT → LLM → TTS)的 git 子模块 |
docs/ | 本文档站点,MDX 编写 |
sdk/ | 以编程方式驱动 Dograh 的 Python/TypeScript SDK |
scripts/ | 安装、部署与更新脚本(含本文的setup_local.sh与 devcontainer 脚本) |
deploy/ | 远程部署使用的 nginx 与 coturn 配置模板 |
devcontainer 中的三个命名卷与这些目录一一对应:venv服务api/与pipecat/的 Python 依赖,两个node_modules卷分别服务ui/与api/mcp_server/ts_validator/(MCP 服务器的 TypeScript 校验器)。
贡献流程方面:创建分支、提交改动、推送到 fork(origin)并向dograh-hq/dograh:main发起 PR,由维护者评审合并;bug 与功能想法通过 Issue 与 Ideas 讨论区提交,good first issue标签是入门起点。若你是想部署自己的构建而非向上游贡献,则应走 docs/deployment/introduction.mdx 的部署路径——而不是本文的 devcontainer 路径,也不是setup_local.sh的隧道模式。
小结
把 scripts/setup_local.devcontainer.md 的简短说明放回仓库上下文后,可以得到清晰的分工图景:
setup_local.sh/setup_local.ps1:面向部署验证与隧道联调,拉镜像、生成强随机凭据的.env、可选 coturn,起--profile tunnel栈,应用入口http://localhost:3010;.devcontainer/:面向日常贡献,两阶段 Dockerfile 钉住 Python 3.13 与 uv 工具链,compose 自动拉起 pgvector Postgres、Redis、MinIO 并通过健康检查门控 workspace 启动;post-create.sh完成 build-stamp 感知的 venv 播种、带主机名重写的api/.env/api/.env.test/ui/.env生成、pipecat editable 化与并行npm ci;- 启动与迭代:
bash scripts/start_services_dev.sh等健康检查通过后启动后端,cd ui && npm run dev -- --hostname 0.0.0.0启动 UI,localhost:3000访问界面;仅当.devcontainer/、api/requirements*.txt或pipecat/变化时才需要重建容器。
掌握这套结构后,无论是要在 pipecat 管道中打断点、调整基础设施连接串,还是为 MCP 校验器改 TypeScript,都能在容器内以最短路径完成“改代码 → 生效 → 验证”的闭环。
【免费下载链接】dograhOpen source voice AI platform. Self-hosted alternative to Vapi and Retell. On Prem, BYOK across Speech to Speech or LLM/STT/TTS, with a visual workflow builder, MCP native and telephony support.项目地址: https://gitcode.com/GitHub_Trending/do/dograh
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考