tianye1999/callpilot

GitHub: tianye1999/callpilot

CallPilot 是一个运行在 Quectel 4G 蜂窝模组上的开源 AI 电话代理,支持自动接听、外呼、收发短信及 IVR 导航,通话数据完全自托管。

Stars: 2 | Forks: 1

# CallPilot ### 你的电话——由 AI 接听和拨打,跑在*你自己的* SIM 卡和硬件上。 [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) [![Latest release](https://img.shields.io/github/v/release/tianye1999/callpilot?color=orange&label=release)](https://github.com/tianye1999/callpilot/releases/latest) [![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](#contributing) [![中文 README](https://img.shields.io/badge/docs-中文-red.svg)](#中文) CallPilot 把一个 **~$20 的 4G 模组变成实时 AI 电话 agent**。它接听你的来电,并使用实时语音模型与来电者*对话*,支持外呼、收发短信、操作 IVR 菜单(DTMF),并能对每通电话进行录音和总结。 与云端的来电筛选应用不同,**一切都在你自有的硬件上运行**——你的 SIM 卡、你的 API key、你的录音和消息都保留在你的机器上。

CallPilot — start an AI-handled call
Start an AI-handled call: pick a preset task, type a number, describe the goal — the AI dials and talks for you.

**CallPilot 有何不同** - ☎️ **真实的蜂窝通话,而非 VoIP** —— 由 Quectel EC20/EG25 模组上的硬件 AT 事件(`RING → ATA`)驱动,而非屏幕自动化或 SIP 中继。 - 🔒 **隐私优先与自托管** —— 通话/短信内容保留在你的机器上(即 *Edge*);可选的云端仅作传输中转,**不存储任何数据**。 - 🧠 **自带大脑** —— 通过 **Qwen Omni / OpenAI Realtime / 豆包**进行实时语音到语音对话,或者采用完全在**设备本地**运行的 VAD→STT→LLM→TTS 流水线,音频永远不会离开你的 Mac。 - 🖥️📱 **完整的全栈,而非脚本** —— 包含已签名的 macOS 桌面应用、原生的 **iOS & Android** 远程终端,以及可从任何地方进行配对和拨号的 Cloudflare 控制面。 | 平台 | 状态 | |---|---| | macOS 桌面版(已签名且经过公证的 DMG) | ✅ **Beta — [下载 v0.6.0](https://github.com/tianye1999/callpilot/releases/latest)** | | 云端控制面(远程配对 + 拨号) | ✅ Beta | | Windows 桌面版 | 🧪 代码已完成,**等待硬件测试反馈** | | iOS 远程终端 | 🚧 TestFlight 内测中(0.7.0 开发中) | | Android 远程终端 | 🚧 发布签名已完成,真机验证进行中(0.7.0 开发中) | **[▶ 快速开始](#developer-path-macos-from-source)** · **[🛒 硬件获取(约 $20)](#get-the-hardware)** · **[🗺 路线图](docs/roadmap.md)** · **[🇨🇳 中文](#中文)** ## English ### 这是什么 CallPilot 将蜂窝模组与云端实时语音模型桥接起来,使 AI “助手”能够代你接听和拨打电话: ``` Phone call → EC20 modem ──(AT: RING/ATA/CLCC)── CallPilot │ 8kHz PCM │ Audio bridge ────── VoiceAgent (Qwen Omni / Doubao / OpenAI realtime) │ EventHub → web dashboard (served as a desktop app or browser) ``` - **AI 大脑:** 云端实时语音转语音(默认为阿里巴巴的 **Qwen Omni**,可选字节跳动的 **豆包** 或 **OpenAI Realtime**)。无需安装本地 ML 模型。 - **电话功能:** 来自 EC20/EG25 模组的硬件 AT 事件 —— 纯粹的 `RING → ATA`,非屏幕自动化。 - **特性:** 自动接听、外呼拨号(支持单个及带白名单的批量外呼)、短信收发(中文 UCS2)、AI 工具调用(发送短信 / 挂断 / 读取 OTP / **DTMF 键盘**)、单次通话录音 + 延迟指标 + LLM 总结、实时转录、本地扬声器监听、双语(英文/中文)桌面 UI。 **本地三段式 provider**(`AGENT_PROVIDER=local`,于 v0.5.0 引入):设备本地 VAD → STT → 文本 LLM → 设备本地 TTS。音频永远不会离开你的机器;只有转录文本会发送给文本大脑(默认为 `qwen-plus`,使用相同的 DashScope key,比实时音频便宜一个数量级)。设置:`pip install 'callpilot[local]'` 然后运行 `python -m agentcall.local_models`(一次性下载约 300 MB 模型)。工具、转录、摘要和预设均可正常使用。 v0.4.0 增加了多项针对外呼工作质量的通话控制: - **任务预设库:** 首次启动会从 [`data/number_profiles.example.json`](data/number_profiles.example.json) 初始化本地库。使用 **Presets** 页面来创建、编辑、复制、禁用或删除条目;高级用户仍可直接编辑 `data/number_profiles.json`。每个 `label` / `task` / `scenario` / `opening` 字段均支持字符串或 `{zh,en}` 对象。完整的 schema 和编写指南见 [`docs/number-profiles.md`](docs/number-profiles.md)(中文)。 - **带子主题的预设拨号:** 选择预设会自动填入号码和主题,而主题输入框仍可编辑,以便输入本次通话的具体子主题,同时不会丢失预设匹配。 - **动态场景提示词:** 当没有匹配的预设时,轻量级文本模型可以在接通前起草通话场景和开场白(`PROMPT_GEN_*`)。 - **更多 IVR 控制:** DTMF 默认采用带内传输(`DTMF_MODE=inband`),因此按键音会被合成到通话音频路径中。实验性的手动响应控制(默认为 `MANUAL_RESPONSE_CONTROL=false`)可以在 AI 单次回复前合并冗长的 IVR 菜单语音。 - **语音设置:** 设置中包含 Qwen/OpenAI 的语音选择器及官方预览链接,此外还有 `VOICE_STYLE`,支持自由文本格式的说话风格提示。 - **OpenAI 模型选择:** OpenAI Realtime provider 默认使用 `gpt-realtime-2.1-mini` 以降低通话延迟,在 `.env` / 设置中仍可选择 `gpt-realtime-2.1`、`gpt-realtime-2`、`gpt-realtime` 和 `gpt-realtime-mini`。 ### 硬件与平台支持 | 项目 | 状态 | |------|--------| | Quectel EC20(此版本针对 `EC20CEFAGR08A03M4G` 测试) | ✅ 已验证 | | macOS(通过 Rosetta 支持 Apple Silicon 与 Intel) | ✅ 已验证 | | Windows 10/11(官方 Quectel 驱动,原生 COM 端口) | 🧪 已实现全面支持,**等待硬件测试反馈** | | Linux(原生串口) | ⚠️ 代码路径存在,**未经验证** | | 音频:`uac_ffmpeg`(通过 UAC 声卡的 ffmpeg) | ✅ 已验证 —— **仅限 macOS** | | 音频:`uac`(PortAudio/WASAPI) | 🧪 Windows 路径,等待验证(在 macOS 上不可用) | | 音频:`nmea`(USB 串口 PCM) | ❌ 在 macOS 上会导致 USB 崩溃 —— 请勿使用 | | SIM | 需要语音 + 短信服务;VoLTE/CS 语音取决于运营商 | macOS 对于 Quectel 厂商接口**没有原生串口**,因此需要使用 USB→PTY 桥(`scripts/ec20_usb_pty.py`)来暴露 `/tmp/ec20-at`。 ### 硬件获取 你需要一个 **Quectel EC20 或 EG25** 4G 模组(此版本针对 `EC20CEFAGR08A03M4G` 进行了验证)。常见的 mini-PCIe 模组还需要: - 一块**带 SIM 卡槽的 USB 转接板**(将 mini-PCIe 模组转换为 USB 设备), - 一根 **4G 天线**, - 一张**开通了语音 + 短信服务的 SIM 卡**(语音 + 短信已确认可用;VoLTE / CS 语音取决于你的运营商)。 一套完整的 EC20 模组 + 转接板套装大约 **¥100–200 / $15–30** —— 可在 AliExpress 或淘宝搜索“EC20 USB 转接板”。 ### 前置要求 - DMG 安装路径:配备已激活 SIM 卡的 EC20/EG25 模组。该应用内置了 Python runtime、CallPilot 代码、`ffmpeg` 和 `libusb`。 - 无需手动进行 AT 设置即可使用音频:服务在启动时会自动启用 UAC 语音(`AT+QCFG="USBCFG"` + `AT+QPCMV=1,2`)。如果该模组之前从未启用过 UAC,请重新插拔一次 USB 以使新的 USB 配置生效。 - 开发者路径:Python 3.12+,PATH 中需包含可用的 `ffmpeg`,并且在 macOS 上需要运行 `brew install libusb` 以支持 USB→PTY 桥。 - 一个 **DashScope API key**(用于 Qwen)。可在 获取。国际用户需通过阿里巴巴云的 **Model Studio**(这是一个不同的 endpoint —— 高级用户可以通过 `.env` 中的 `DASHSCOPE_REALTIME_URL` 环境变量来指向它)。 (豆包处于实验性阶段;外呼通话可能没有声音。OpenAI 凭证是可选的。) ### 普通用户安装(macOS DMG) 从 [最新的 GitHub Release](https://github.com/tianye1999/callpilot/releases/latest) 下载 `CallPilot.dmg`,打开并将 `CallPilot.app` 拖入 `/Applications`。官方发布的 DMG 已经使用 Developer ID 签名、公证并装订,因此 Gatekeeper 应允许正常的打开流程,无需右键点击。DMG 由 [`packaging/build_installer.sh`](packaging/build_installer.sh) 构建,当设置了发布签名变量时,它还会验证已签名/公证的构建产物。 首次启动时,从菜单栏应用打开 。设置向导会引导你完成硬件状态检查、provider 凭证、机主/角色设置以及可选的测试短信,因此在正常安装过程中你无需手动编辑 `.env`。 ### 开发者路径(macOS 源码运行) ``` git clone https://github.com/tianye1999/callpilot.git callpilot && cd callpilot bash scripts/setup.sh # one command: checks Python 3.12+/ffmpeg, creates .venv + .env # terminal 1 — USB→PTY 桥接(暴露 /tmp/ec20-at) .venv/bin/python scripts/ec20_usb_pty.py --map 2:/tmp/ec20-at # terminal 2 — 服务(打开 http://127.0.0.1:47100) .venv/bin/python app.py ```
手动设置(setup.sh 做的事情) ``` python3 -m venv .venv .venv/bin/pip install -e ".[dev]" cp .env.example .env # then edit .env (see below) ```
最小化的 `.env`: ``` DASHSCOPE_API_KEY=sk-your-key MODEM_PORT=/tmp/ec20-at MODEM_AUDIO_MODE=uac_ffmpeg MODEM_AUDIO_KEYWORD=Interface OWNER_NAME=Your Name # shown to callers; blank = neutral "the owner" AGENT_LANGUAGE=en # language the AI speaks on calls & summaries (zh|en); default zh ``` 然后打开 并按照首次运行向导操作,或者如果你愿意,可以手动编辑 `.env`。拨打模组的 SIM 卡号码 —— AI 应该会自动接听。所有设置都可以在 UI 的 **Settings** 面板中进行实时编辑。 有关完整的配置列表,请以 [`.env.example`](.env.example) 为准;v0.4.0 在其中的选项包括 `NUMBER_PROFILES_ENABLED`、 `NUMBER_PROFILES_FILE`、`PROMPT_GEN_ENABLED`、`PROMPT_GEN_MODEL`、 `PROMPT_GEN_TIMEOUT`、`PROMPT_GEN_WAIT_SECONDS`、`DTMF_MODE`、 `MANUAL_RESPONSE_CONTROL`、`MANUAL_RESPONSE_SILENCE_MS`、 `MANUAL_RESPONSE_MAX_WAIT_MS`、`QWEN_VOICE`、`OPENAI_VOICE` 和 `VOICE_STYLE`。 ### 将收到的短信转发至邮箱 此选配功能**默认关闭**。在 **Settings → Dialing & SMS** 中,输入一个收件人地址以及发件人账号的 SMTP 主机、端口、TLS 模式、用户名、应用密码和发件人地址,然后启用 **Forward received SMS to email**。新的模组短信会被排入队列,而不会阻塞模组监听器。当可靠检测到验证码时,邮件主题将以 `【验证码 】` 开头;不相关的数字不会被提升为验证码。 对于 Google Workspace/Gmail,通常的值为 `smtp.gmail.com`,端口 `587`,`starttls`,完整的邮箱地址作为用户名/发件人,以及 Google **App Password**(而非普通的账号密码)。CallPilot 仅将该机密存储在本地被 git 忽略的 `.env` 中,并且绝不会通过设置 API 返回。公开的 DMG/EXE 构建特意不包含共享的发件人凭证。启用转发会将短信内容发送到配置的邮箱中。 ### 远程 Web 拨号器 Issue #31 和 #31.1 添加了一个**默认关闭**的远程听筒:从本地拨号面板配对一次手机,然后重复使用一个固定的 HTTPS 页面通过 Dongle SIM 拨打电话。每次通话仍会获得一个新的短期 LiveKit 凭证。持久的设备凭证是一个 HttpOnly、Secure、SameSite 的 cookie;Edge 仅持久化其哈希值,并允许本地 dashboard 立即撤销已配对的手机。 公共隧道必须指向专用的 loopback 网关127.0.0.1:47445`,而不是特权管理端口 `WEB_PORT` (47100)。该网关仅提供拨号器/PWA 以及配对/会话的 endpoint;SMS、设置、录音和任意模组 API 在此处均不存在。填写 [`.env.example`](.env.example) 中的 `REMOTE_*` / `LIVEKIT_*` 设置,启用后重启,并将固定的 HTTPS 域名路由到 `REMOTE_GATEWAY_PORT`。原始的一次性移动端链接将作为备用保留。参见 [ADR-001](docs/decisions/001-remote-web-dialer-livekit.md)。 移动端的后台/锁屏拨打电话以及接管来电仍需要后续的原生应用支持。 Issue #42 添加了公司托管的 Beta 模式。在 `REMOTE_CLOUD_ENABLED=true` 的设置下,Edge 会向 `REMOTE_CLOUD_URL` 发起出站 WSS 连接;用户无需运行 Cloudflare Tunnel,且 Edge 不会接收 LiveKit API Secret。注册使用一次性的 Beta 代码,而最终生成的 Edge 凭证和 Ed25519 设备密钥会保存在 Keychain/Credential Manager 中。现有的 loopback 网关仍作为明确的诊断备用方案保留。 ### 快速开始(Windows)—— 等待硬件测试反馈 Windows **不需要 USB 桥**:安装官方的 Quectel EC20 Windows 驱动程序后,模组将显示为原生的 COM 端口。`MODEM_PORT=auto`(Windows 默认值)会通过 USB VID 扫描 Quectel AT 端口;音频使用 `MODEM_AUDIO_MODE=uac`(PortAudio/WASAPI —— `uac_ffmpeg` 仅限 macOS)。 ``` git clone https://github.com/tianye1999/callpilot.git callpilot; cd callpilot powershell -ExecutionPolicy Bypass -File scripts\windows\setup.ps1 # checks Python/ffmpeg, creates .venv + .env .venv\Scripts\python app.py # 登录时自动启动(Task Scheduler): powershell -ExecutionPolicy Bypass -File scripts\windows\install.ps1 install ```
手动设置(setup.ps1 做的事情) ``` python -m venv .venv .venv\Scripts\pip install -e ".[dev]" copy .env.example .env # then edit .env ```
详情见 [`scripts/windows/README.md`](scripts/windows/README.md)。此路径代码已完成并通过了 CI 测试,但**尚未在真实硬件上验证** —— 如果你在 Windows 上使用 EC20,请[向我们反馈](.github/ISSUE_TEMPLATE)! ### 桌面应用与安装包对比 ``` # macOS .venv/bin/pip install pyinstaller # pywebview is already a core dependency bash scripts/build_app.sh # → dist/CallPilot.app # 独立安装程序 bash packaging/build_installer.sh # → dist/CallPilot.app + dist/CallPilot.dmg # Windows powershell -ExecutionPolicy Bypass -File scripts\windows\build_app.ps1 # → dist\CallPilot\CallPilot.exe ``` 在 macOS 上,`CallPilot.app` 是一个**菜单栏应用**:菜单栏中会显示一个电话图标(绿色 = 服务运行中,灰色 = 已停止),带有 *Open dashboard / Restart service / Quit* 选项。`scripts/build_app.sh` 会基于你的本地代码检出构建一个用于开发的轻量级应用。`packaging/build_installer.sh` 构建独立的 DMG,其中捆绑了 runtime 和原生依赖;官方发布版本会设置签名和公证变量,以便应用 + DMG 经过 Developer ID 签名、公证、装订和自我验证。 ### 无需真人接听即可验证其工作 - **拨打你自己的手机**:最简单的检查方式 —— 接听后你会听到 AI 说话。 - **拨打运营商的客服热线**(一个会进行语音回复的 IVR):如果 AI 能与语音菜单进行连贯的多轮对话,说明双向音频均工作正常。 - **向运营商的服务号码发送余额查询短信**:你应该会收到回复短信 —— 这证明了发送和接收功能均正常,包括非 ASCII (UCS2) 解码。 - **运行硬件回归脚本**:`.venv/bin/python scripts/regression_call.py --task "check plan usage"` 会通过本地 Web API 拨打一通测试电话,等待录音生成,并以 PASS/FAIL 退出;添加 `--no-dial` 可直接回放最近一次的录音。 ### 故障排除 新用户安装与首次运行的 Q&A:[`docs/faq.md`](docs/faq.md)(中文)。 | 症状 | 可能的原因 / 修复方法 | |---------|-------------------| | App 无法打开 `/tmp/ec20-at` | USB 桥未运行,或模组重新插拔(桥会自动重连;服务也会重新打开串口) | | 模组反复从 USB 掉线 | 首要原因:**系统休眠**会重新枚举 USB 并导致模组的 endpoint 停滞。launchd plists 使用 `caffeinate -s` 包裹了这两个进程;如果你手动运行,请使用 `caffeinate -s .venv/bin/python ...` 或设置 `pmset -a sleep 0`。桥在重连时也会执行 `dev.reset()` 并在 1→30 秒之间进行退避;在连续 6 次快速失败后,它会退出以便 launchd 进行冷重启 | | macOS 上完全没有声音 | `MODEM_AUDIO_MODE` 必须设置为 `uac_ffmpeg`;`PortAudio`/`nmea` 在此无法工作 | | PortAudio `-9986 / -66740` | `coreaudiod` 卡死:`sudo killall coreaudiod` | | 在房间内听不到 AI 的声音 | 在设置中启用 **Monitor on this Mac**;调高 `MONITOR_UPLINK_GAIN` 以改善对方侧的声音 | | 来电者(非 AI)的声音太轻 | 调高 `MONITOR_UPLINK_GAIN`(默认 8,我们在真实硬件上曾设置为 15) | | 第二通电话没有声音 | 已修复 —— 语音通道会在每次通话时重新激活 | ### 安全、隐私与法律 **在真实话机上使用前请务必阅读。** - **不适用于紧急呼叫。** 请勿依赖 CallPilot 进行任何生命安全通信。 - **录音法律因司法管辖区而异** —— 通话录音**默认关闭**,仅在启用时存储在本地。请在首次运行设置期间明确选择,稍后可在设置中或通过 `RECORDING_ENABLED=false` 进行更改。你有责任获得法律要求的任何同意。 - **防骚扰 / 电话营销规则适用**于外呼和批量拨号。请使用拨号白名单并拨打你自己的号码进行测试。 - **你需承担所有运营商费用和 API 成本。** - **你的 API key 保留在本地 `.env` 中**(已被 git 忽略)。切勿提交它们。 - **短信转邮件转发是一项数据导出功能。** 它默认禁用;启用后,短信发件人、时间戳和正文将通过你配置的受 TLS 保护的 SMTP 账号离开本应用。请使用应用密码,切勿提交。 - **按“原样”提供,不提供任何担保**(Apache-2.0)。 ## 中文 ### 你的电话——由 AI 接听和拨打,跑在*你自己*的 SIM 卡和硬件上。 CallPilot 把一个 **~¥150 的 4G 模组变成实时 AI 电话 agent**:它接听来电并*用实时语音模型和对方对话*,外呼、收发短信、按 IVR 菜单键(DTMF),每通电话录音 + 摘要。 和云端挡电话服务不同,**一切都跑在你拥有的硬件上**——你的 SIM、你的 API Key,通话录音和短信都留在你自己机器上。

CallPilot 拨号界面
发起一通 AI 代打电话:选预设任务、填号码、描述目标——AI 替你拨打并对话。

**CallPilot 有何不同** - ☎️ **真实蜂窝通话,不是 VoIP** —— 由 Quectel EC20/EG25 模组的硬件 AT 事件(`RING → ATA`)驱动,不是屏幕自动化或 SIP 中继。 - 🔒 **隐私优先、可自托管** —— 通话/短信内容留在你的机器(*Edge*);可选云端只做传输中转,**不存储任何内容**。 - 🧠 **自带大脑** —— 实时端到端语音走 **Qwen Omni / OpenAI Realtime / 豆包**,或完全**本地**的 VAD→STT→LLM→TTS 流水线,音频不出本机。 - 🖥️📱 **完整栈,不是脚本** —— 签名公证的 macOS 桌面 App、原生 **iOS & Android** 远程手柄、可远程配对拨号的 Cloudflare 控制面。 | 平台 | 状态 | |---|---| | macOS 桌面(签名公证 DMG) | ✅ **Beta —— [下载 v0.6.0](https://github.com/tianye1999/callpilot/releases/latest)** | | 云控制面(远程配对 + 拨号) | ✅ Beta | | Windows 桌面 | 🧪 代码完备,**待硬件反馈** | | iOS 远程手柄 | 🚧 TestFlight 内测(0.7.0 开发中) | | Android 远程手柄 | 🚧 已 release 签名,真机验收进行中(0.7.0 开发中) | **[▶ 快速开始](#开发者路径macos-源码运行)** · **[🛒 准备硬件(~¥150)](#硬件准备)** · **[🗺 路线图](docs/roadmap.md)** · **[🇬🇧 English](#english)** ### 这是什么 CallPilot 把 4G 模组接到云端实时语音大模型,让 AI「助理」替你接打电话:插上 Quectel EC20/EG25,来电自动接听并与对方对话,可外呼、收发短信、按 IVR 菜单键、 每通电话录音+延迟打点+AI 摘要——全部跑在你自己的硬件和 API Key 上。 - **AI 大脑**:云端端到端实时语音(默认阿里 **Qwen Omni**,可选字节 **Doubao** 或 **OpenAI Realtime**),无需安装本地模型。 - **电话通道**:EC20/EG25 模组的硬件 AT 事件(`RING → ATA`),非屏幕自动化。 - **能力**:自动接听、外呼(单个+批量带白名单)、中文短信收发、AI 工具调用 (发短信/挂断/查验证码/**DTMF 按键**)、通话录音+摘要、实时转写、本机监听、 中英双语桌面界面。 **本地三段式 provider**(`AGENT_PROVIDER=local`,v0.5.0 引入):本地 VAD → 本地转写 → 云端文本模型 → 本地合成。音频不出本机,只有转写文本上云(默认 `qwen-plus`,同一个 DashScope key,比 realtime 音频便宜一个量级)。启用: `pip install 'callpilot[local]'` 后运行 `python -m agentcall.local_models` 一次性下载 ~300MB 模型。工具调用/转写/摘要/预设库全部照常。 v0.4.0 增加了几项面向外呼质量的控制: - **预调教任务库**:首次启动会从 [`data/number_profiles.example.json`](data/number_profiles.example.json) 初始化本地任务库;可在「任务库」页面新建、编辑、复制、停用或删除预设,高级用户仍可直接编辑 `data/number_profiles.json`。`label` / `task` / `scenario` / `opening` 字段均支持普通字符串或 `{zh,en}` 双语对象。完整结构与编写指南见 [`docs/number-profiles.md`](docs/number-profiles.md)。 - **拨号下拉 + 子主题**:选择预设会自动填号码和事项,事项框仍可改成本通的具体子主题, 同时保留预设命中。 - **动态场景提示词**:预设未命中时,可在接通前用轻量文本模型生成本通场景与开场白 (`PROMPT_GEN_*` 配置)。 - **更稳的 IVR 控制**:DTMF 默认走带内音频(`DTMF_MODE=inband`),按键音直接合成进通话音频流; 实验性的手动应答控制默认关闭(`MANUAL_RESPONSE_CONTROL=false`),可把连续 IVR 菜单合并后再让 AI 回复一次。 - **音色设置**:设置面板提供 Qwen/OpenAI 音色下拉和官网试听链接,`VOICE_STYLE` 可补充自由文本说话风格。 - **OpenAI 模型选择**:OpenAI Realtime provider 默认使用 `gpt-realtime-2.1-mini` 以优先降低电话链路延迟;仍可在 `.env` / 设置面板切换到 `gpt-realtime-2.1`、 `gpt-realtime-2`、`gpt-realtime` 或 `gpt-realtime-mini`。 ### 硬件与平台支持 | 项 | 状态 | |----|------| | Quectel EC20(本版本对 `EC20CEFAGR08A03M4G` 验证) | ✅ 已验证 | | macOS(Apple Silicon 与 Intel/Rosetta) | ✅ 已验证 | | Windows 10/11(Quectel 官方驱动,原生 COM 口) | 🧪 已完整支持,**待硬件复现反馈** | | Linux(原生串口) | ⚠️ 代码路径存在,**未验证** | | 音频 `uac_ffmpeg`(ffmpeg 走 UAC 声卡) | ✅ 已验证——**仅 macOS** | | 音频 `uac`(PortAudio/WASAPI) | 🧪 Windows 主路径,待验证(macOS 上不可用) | | 音频 `nmea`(USB 串口 PCM) | ❌ macOS 会崩 USB,勿用 | | SIM 卡 |需语音+短信服务;VoLTE/CS 语音取决于运营商 | macOS 没有 Quectel 厂商串口的原生设备,需先跑 USB→PTY 桥(`scripts/ec20_usb_pty.py`) 暴露出 `/tmp/ec20-at`。 ### 硬件准备 需要一个 **Quectel EC20 或 EG25** 4G 模组(本版本对 `EC20CEFAGR08A03M4G` 验证)。 常见的 mini-PCIe 模组还需要:**带 SIM 卡座的 USB 转接板**(把模组变成 USB 设备)、 **4G 天线**、一张**开通语音+短信的 SIM**(已在真机验证;VoLTE 取决于运营商)。 模组+转接板全套约 **¥100–200**,淘宝搜「EC20 USB 转接板」。 ### 前置 - 普通用户 DMG 路径:一张有效 SIM 的 EC20/EG25 模组;App 已内置 Python runtime、 CallPilot 代码、`ffmpeg` 与 `libusb`。 - 音频无需手动发 AT:服务启动时自动启用 UAC 语音(`AT+QCFG="USBCFG"` + `AT+QPCMV=1,2`); 模组此前从未启用过 UAC 的话,重插一次 USB 让新配置生效。 - 开发者源码路径:Python 3.12+、PATH 里有 `ffmpeg`;macOS 还需 `brew install libusb`(USB→PTY 桥的 pyusb 依赖系统库)。 - **DashScope API Key**(Qwen 用),申请:。 豆包 provider 仍为 experimental,外呼可能不出声;OpenAI 凭证可选。 ### 普通用户安装(macOS DMG) 从 [最新 GitHub Release](https://github.com/tianye1999/callpilot/releases/latest) 下载 `CallPilot.dmg`,打开后把 `CallPilot.app` 拖到 `/Applications`。官方发布 DMG 已用 Developer ID 签名、完成公证并 staple,Gatekeeper 应可直接按正常方式打开,无需右键。 这个 DMG 由 [`packaging/build_installer.sh`](packaging/build_installer.sh) 构建;发布签名变量 存在时脚本也会自检签名、公证与 staple 状态。 首次启动后,从菜单栏 App 打开 。首启向导会引导检查硬件、 填写 provider 凭证、设置机主/人设,并可发送一条测试短信;普通安装无需手改 `.env`。 ### 开发者路径(macOS 源码运行) ``` git clone https://github.com/tianye1999/callpilot.git callpilot && cd callpilot bash scripts/setup.sh # 一条命令:检查 Python 3.12+/ffmpeg,创建 .venv + .env # 终端 1 — USB→PTY 桥 .venv/bin/python scripts/ec20_usb_pty.py --map 2:/tmp/ec20-at # 终端 2 — 服务(打开 http://127.0.0.1:47100) .venv/bin/python app.py ```
手动步骤(即 setup.sh 做的事) ``` python3 -m venv .venv .venv/bin/pip install -e ".[dev]" cp .env.example .env # 编辑 .env ```
打开 跟随首启向导,或按上方英文段手动写最小 `.env`。 之后拨打模组 SIM 卡号码即可,AI 应自动接听;所有配置都能在界面「设置」面板里实时修改。 完整配置以 [`.env.example`](.env.example) 为准;v0.4.0 新增/相关项包括 `NUMBER_PROFILES_ENABLED`、`NUMBER_PROFILES_FILE`、`PROMPT_GEN_ENABLED`、 `PROMPT_GEN_MODEL`、`PROMPT_GEN_TIMEOUT`、`PROMPT_GEN_WAIT_SECONDS`、`DTMF_MODE`、 `MANUAL_RESPONSE_CONTROL`、`MANUAL_RESPONSE_SILENCE_MS`、`MANUAL_RESPONSE_MAX_WAIT_MS`、 `QWEN_VOICE`、`OPENAI_VOICE`、`VOICE_STYLE`。 ### 收到短信后转发到邮箱 该功能**默认关闭**。在「设置 → 外呼与短信」中填写一个收件邮箱,以及发件账号的 SMTP 主机、端口、加密方式、用户名、应用密码和发件地址,再打开「收到短信后转发到 邮箱」。新短信只做非阻塞入队,不会卡住模组监听;可靠识别到验证码时,邮件标题以 `【验证码 】` 开头,普通数字不会被误当验证码。 Google Workspace/Gmail 通常填写 `smtp.gmail.com`、端口 `587`、`starttls`,用户名和 发件地址填写完整邮箱,密码填写 Google **应用专用密码**而不是账号登录密码。密钥只存 在本机且被 git 忽略的 `.env`,设置 API 不会回显。公开 DMG/EXE 不内置任何共享发件 凭证。开启此功能即表示短信发件号码、时间和正文会通过 TLS SMTP 发往所填收件邮箱。 ### 远程网页拨号 issue #31 与 #31.1 新增一个**默认关闭**的远程手柄:先从本机拨号面板把手机配对 一次,之后反复打开固定 HTTPS 页面,即可通过 Dongle SIM 外呼。每通仍签发新的短期 LiveKit 凭证;长期手机凭证只存在 HttpOnly、Secure、SameSite Cookie 中,Edge 本地 只保存哈希,并可从本机面板立即撤销设备。 公网隧道必须指向独立的最小权限网关 `127.0.0.1:47445`,绝不能指向管理端口 `WEB_PORT`(47100)。该网关只有拨号页/PWA、配对和单通会话接口,不存在短信、设置、 录音或任意模组 API。按 [`.env.example`](.env.example) 填写 `REMOTE_*` / `LIVEKIT_*`, 启用后重启,再把固定 HTTPS 域名转发到 `REMOTE_GATEWAY_PORT`。原来的一次性手机链接 继续作为故障排查后备入口。安全边界见 [ADR-001](docs/decisions/001-remote-web-dialer-livekit.md)。锁屏后台与入站接管仍需后续 原生 App。 ### 快速开始(Windows)—— 待硬件复现反馈 Windows **不需要 USB 桥**:装 Quectel 官方 EC20 Windows 驱动后模组直接暴露原生 COM 口。`MODEM_PORT=auto`(Windows 默认)按 USB VID 自动扫描 AT 口;音频用 `MODEM_AUDIO_MODE=uac`(PortAudio/WASAPI,`uac_ffmpeg` 仅 macOS)。 ``` git clone https://github.com/tianye1999/callpilot.git callpilot; cd callpilot powershell -ExecutionPolicy Bypass -File scripts\windows\setup.ps1 # 检查 Python/ffmpeg,创建 .venv + .env .venv\Scripts\python app.py # 开机常驻(计划任务): powershell -ExecutionPolicy Bypass -File scripts\windows\install.ps1 install ```
手动步骤(即 setup.ps1 做的事) ``` python -m venv .venv .venv\Scripts\pip install -e ".[dev]" copy .env.example .env # 编辑 .env ```
详见 [`scripts/windows/README.md`](scripts/windows/README.md)。该路径代码完备且 过 CI,但**尚未真机验证**——如果你有 EC20 + Windows,欢迎提 issue 反馈! ### 桌面 App 与安装包 ``` # macOS .venv/bin/pip install pyinstaller # pywebview 已是核心依赖 bash scripts/build_app.sh # → dist/CallPilot.app # 独立安装包 bash packaging/build_installer.sh # → dist/CallPilot.app + dist/CallPilot.dmg # Windows powershell -ExecutionPolicy Bypass -File scripts\windows\build_app.ps1 # → dist\CallPilot\CallPilot.exe ``` macOS 上 `CallPilot.app` 是**菜单栏 App**:顶栏一个电话图标(绿=服务运行中, 灰=已停止),菜单含「打开控制台 / 重启服务 / 退出」。它只是本地代码仓库的薄壳 控制面板——接电话的服务在后台常驻(launchd),关掉面板窗口不影响接打电话。 `scripts/build_app.sh` 适合开发调试;`packaging/build_installer.sh` 会把 runtime 和原生依赖 打进独立 DMG;官方发布构建会设置签名与公证变量,使 App + DMG 完成 Developer ID 签名、公证、staple 和自检。 ### 无需真人也能自测 - **拨你的运营商客服/IVR 热线**:若 AI 能与语音菜单连贯多轮对话,说明双向语音都通。 - **向运营商服务号发送余额查询短信**:会收到回复短信,验证发+收+中文编解码全链路。 - **跑真机回归脚本**:`.venv/bin/python scripts/regression_call.py --task "查询套餐使用情况"` 会通过本地 Web API 发起一通测试外呼、等待录音并以 PASS/FAIL 退出;加 `--no-dial` 可直接回放最近一通录音。 ### 排障 新手安装与首启常见问题见 [`docs/faq.md`](docs/faq.md)。 | 现象 | 可能原因 / 解决 | |------|----------------| | 打不开 `/tmp/ec20-at` | 桥没跑或模组重插(桥会自动重连,服务也会重开串口) | | 模组反复从 USB 掉线 | 首要诱因是**系统睡眠**导致 USB 重枚举、端点 stall。launchd plist 已用 `caffeinate -s` 包裹进程;手动运行请加 `caffeinate -s` 前缀或 `pmset -a sleep 0`。桥重连时会先 `dev.reset()` 并指数退避(1→30s),连续快速失败达阈值后退出交给 launchd 冷重启 | | macOS 完全没声音 | `MODEM_AUDIO_MODE` 必须是 `uac_ffmpeg` | | PortAudio 报 `-9986 / -66740` | coreaudiod 卡死:`sudo killall coreaudiod` | | 电脑上听不到 AI | 设置里开「本机监听」;对方声音小就调大 `MONITOR_UPLINK_GAIN` | | 第二通电话没声音 | 已修复(每通电话重新启用语音通道) | ### 安全、隐私与合规 **上真机前务必阅读。** 不用于紧急电话;通话录音**默认关闭**,首启时需明确选择,开启后 仅存储在本地,可在设置面板或 `RECORDING_ENABLED=false` 关闭——是否录音、是否需征得对方同意由你按 当地法律负责;外呼/批量呼叫须遵守反骚扰与营销合规;运营商资费与 API 费用由你自行 承担;API Key 和 SMTP 应用密码只存于本地 `.env`(已 git 忽略),切勿提交。短信邮件 转发默认关闭;开启后,短信发件号码、接收时间和正文会离开 App 并发往配置邮箱。本软件按「原样」提供, 不作任何担保(Apache-2.0)。 ### 贡献 Mac Beta 阶段仍最需要同型号硬件的复现反馈。有 EC20 的话,欢迎带上模组固件、 macOS 版本、以及哪里成功/失败开 issue。测试:`.venv/bin/pytest`(无需硬件)。 架构与「想改 X 去哪」见 [`docs/architecture.md`](docs/architecture.md);想单独学习/验证某个 模组原子能力(原始 AT、拨号、短信、DTMF),见 [`examples/modem/`](examples/modem/)。 许可证:[Apache-2.0](LICENSE)。
标签:4G通信, 人工智能, 物联网硬件, 用户模式Hook绕过, 移动开发, 网络调试, 自动化, 自托管, 语音助手, 逆向工具