diff --git a/deploy/hel913-unified/README.md b/deploy/hel913-unified/README.md new file mode 100644 index 0000000..c5c27e5 --- /dev/null +++ b/deploy/hel913-unified/README.md @@ -0,0 +1,393 @@ +# HEL-913 · 统一体验运行包(固定版本,未部署、cap=0) + +> 本目录是把已通过 HEL-912 审核的统一候选装成的**可交接运行包**:精确源码 +> 版本、产物与哈希、脱敏配置模板、唯一启动方式、独立空资料空间、停止/ +> 备份/保留资料回退说明、一次零真实调用的运行准备验证记录、真实记忆的 +> 完整可审安装清单,以及语音窗口的唯一布防接线。**本卡未部署任何现网 +> 服务、未监听 836x/8082/9880/8090、未改任何现有实例;实际启用与新用量 +> 由总管统一向白栖知说明并获批准后再执行。** + +## 0. 精确版本:产品基线 vs 运行包提交(不要混同) + +- **已审产品基线**:`agent/2/8e99cc2cc592` @ `4a38106eef21c43ba24ab7d07992b1bfe539137d` + (HEL-911 接缝修复;HEL-912 审核「通过,无必须修复项」)。父线: + `407fc81`(HEL-910 窗口记录)← `958c696`(HEL-908 R1/R2 返工)。 + **产品代码(src/web/config/tools/tests)以这个提交为准。** +- **运行包提交**:同一分支上、`4a38106` 之后的追加提交——只新增/修改 + `deploy/hel913-unified/` 与 `docs/` 下文件,**不改任何产品代码**。交付 + 评论给出运行包精确 head;本文件自身就在该提交里,所以不能在文件内 + 写死自己的哈希。部署**照抄入口是运行包提交**(本目录随它一起走), + 只 checkout `4a38106` 会没有本目录——这是收口修正点,别再踩。 +- **部署前自校验**(两条都必须通过): + ```bash + git merge-base --is-ancestor 4a38106eef21c43ba24ab7d07992b1bfe539137d HEAD && echo ok + git diff --stat 4a38106eef21c43ba24ab7d07992b1bfe539137d HEAD -- src web config tools tests # 必须为空 + ``` +- 需求基准 v1.12(`docs/requirements/`),需求检查见 `handoff-HEL-913.json`。 + +## 1. 交付物清单(本目录) + +| 文件 | 作用 | +| --- | --- | +| `README.md` | 本运行说明(唯一照抄入口;不跳旧 HEL-895 通用步骤) | +| `manifest.json` | 机器可读清单:产品基线、dist 哈希、环境引用、边界 | +| `dist-manifest.json` | web dist 8 文件 SHA256(复用 HEL-910 实测构建) | +| `models.template.yaml` | 脱敏模型/预算配置(**一切预算=0**;占位符标注待批写入位置) | +| `voice.template.yaml` | 脱敏语音配置(livekit_url 必须是 wss 桥地址,见 §5) | +| `worker.env.template` | 语音 worker 环境模板(含 ASR 窗口闸三项,默认未布防=拒绝一切 ASR) | +| `voice-window-arm.template.env` | 语音窗口一次性布防参数模板(默认全 0/未布防) | +| `api_ctl.sh` / `worker_ctl.sh` | 统一实例 API(单进程 HTTP+HTTPS)与语音 worker 启停(身份核验版) | +| `bridge_ctl.template.sh` | wss TLS 桥启停(获批语音窗口用;浏览器侧语音的前提) | +| `tts_ctl.template.sh` | 共享 TTS 9880 窗口内临时恢复/停回(只停自己起的 PID) | +| `force_end.template.sh` | 窗口到期强制收尾(`now` 立即 / `watch ` 到点;封窗停服) | +| `verify_offline.py` | 零真实调用运行准备验证(34 项;TestClient 进程内,不监听端口) | +| `test_ctl_scripts.sh` | 启停脚本离线替身检查(42 项;stub,零真实服务) | +| `mem0-cp311-wheels.txt` | 真实 Mem0 记忆栈 cp311 缺项轮清单(40 项,待批一次性下载) | + +运行记录:`docs/rework/hel913/verify-offline-run.txt`(.12 产品 3.11 venv +实跑 34/34)、`docs/rework/hel913/ctl-checks-run.txt`(收口轮启停检查 42/42)。 + +## 2. 版本固定与产物复用 + +- **源码**:产品基线 `4a38106`(含 `requirements.lock` 19 项固定版本, + Python 3.11 实测)+ 运行包提交(仅本目录与 docs)。 +- **web dist 复用**:`git diff 958c696 4a38106 -- web/` 为空(网页源码零变动), + 直接复用 HEL-910 已实测部署的 dist(.12 `/opt/cybergril-hel910/current/web/dist`, + REVISION 标记 `958c6962…`),只读拷贝,不安装 node、不重建、不跳任何会 + `npm install` 或创建旧端口实例的旧发布步骤(如 `deploy/hel895-release-candidate.md` + 的通用总装流程——那不是本包的入口)。8 文件哈希见 `dist-manifest.json` + (2026-10-29 于 .12 逐文件实测),部署时核对一致即可。 +- **解释器(既有环境引用,不新建、不增改)**: + - 产品 API:`.12 /opt/cybergril-hel879/current/.venv311`(Python 3.11.16, + requirements.lock 全量,fastapi 0.159.1 / uvicorn 0.54.0 / httpx 0.28.1 / + pydantic 2.14.1)。**只借用运行,绝不向它安装任何东西**(记忆栈装进 + 本实例自己的隔离 venv,见 §7)。 + - 语音 worker:`.12 /opt/voice-agent/agent/venv`(livekit-agents 1.8.8, + 含 silero VAD)。 + - 证书:`.12 /opt/cybergril-voice-certs/cybergril-voice.{pem,key}`(既有 + 自签,覆盖 192.168.200.12;8361 与 wss 桥 8363 共用)。 + - 本机 LiveKit `ws://127.0.0.1:7880`(常驻);TTS 9880 / 面板 8090 / + wss 桥 8363 只在获批窗口内启动(§5),未获批保持停机。 + +## 3. 目标实例布局(唯一布局;用户实例 data 必须为空) + +``` +/opt/cybergril-hel913/ # 实例根(名字可换;以下结构固定) + current/ # git checkout <运行包提交>(见 §0,不是 4a38106) + web/dist/ # 按 dist-manifest.json 核哈希后放入(只读复用件) + etc/ + models.yaml # 由 models.template.yaml 复制,填入获批网关与预算 + voice.yaml # 由 voice.template.yaml 复制,填入服务器本地值 + semantic.yaml # (仅批准真实记忆时)由 config/semantic.example.yaml 复制,见 §7 + data/ # 独立空资料空间:新库从零初始化 + worker/.env # 由 worker.env.template 复制并填好(占位全清) + logs/ +``` + +**不打包进她的正式档案**:任何历史测试库(8360 团队库、HEL-907/910 窗口库)、 +聊天记录、LiveKit/TTS 凭据、自签证书私钥、agent venv 内实物。用户实例在用户 +亲自创建前 `companion` 表必须为空(离线验证第 3 项复现该守门)。 + +## 4. 首次准备与启停(唯一照抄入口) + +### 4.1 首次准备(获批部署窗口内执行一次;不真启动服务) + +```bash +# 0) 取运行包提交(精确 head 见交付评论;下述校验两条都要过,见 §0) +git clone -b agent/2/8e99cc2cc592 ssh://git@192.168.200.36:222/leefer/CyberGril.git \ + /opt/cybergril-hel913/current +cd /opt/cybergril-hel913/current +git checkout --detach <本轮交付评论的运行包完整提交号> +git merge-base --is-ancestor 4a38106eef21c43ba24ab7d07992b1bfe539137d HEAD +git diff --stat 4a38106eef21c43ba24ab7d07992b1bfe539137d HEAD -- src web config tools tests # 空 + +# 1) 目录骨架(固定布局) +mkdir -p /opt/cybergril-hel913/{etc,data,logs,worker} + +# 2) 配置落位(占位符必须全部替换;预算值未经批准一律保持 0) +cp deploy/hel913-unified/models.template.yaml /opt/cybergril-hel913/etc/models.yaml +cp deploy/hel913-unified/voice.template.yaml /opt/cybergril-hel913/etc/voice.yaml +cp deploy/hel913-unified/worker.env.template /opt/cybergril-hel913/worker/.env +vi /opt/cybergril-hel913/etc/models.yaml /opt/cybergril-hel913/etc/voice.yaml /opt/cybergril-hel913/worker/.env + +# 3) web dist 只读复用(核哈希后拷入;不装 node、不重建) +rsync -a /opt/cybergril-hel910/current/web/dist/ /opt/cybergril-hel913/current/web/dist/ +python3 - <<'EOF' +import hashlib, json, pathlib +m = json.loads(pathlib.Path("deploy/hel913-unified/dist-manifest.json").read_text()) +bad = [k for k, want in m.items() + if hashlib.sha256((pathlib.Path("web/dist")/k).read_bytes()).hexdigest() != want] +raise SystemExit(f"dist hash mismatch: {bad}" if bad else 0) +EOF + +# 4) 未创建档案守门复验(进程内 TestClient,零监听零出站;34 项全过才继续) +PYTHONPATH=src /opt/cybergril-hel879/current/.venv311/bin/python \ + deploy/hel913-unified/verify_offline.py --web web/dist +``` + +### 4.2 启动与停止(唯一方式;子命令只认 start|stop|status) + +```bash +# API:单进程双入口(HTTP 8360 + HTTPS 8361 共用同一 app 状态;lifespan 只进一次) +cd /opt/cybergril-hel913/current +HEL913_BASE=/opt/cybergril-hel913 bash deploy/hel913-unified/api_ctl.sh start +# 健康检查:curl -s http://127.0.0.1:8360/api/health(version=R4-assembly, +# semantic_backend=stub;llm.ok=false 是诚实状态——cap=0 且未配网关) + +# 语音 worker(语音窗口获批后;接线与前置见 §5) +HEL913_BASE=/opt/cybergril-hel913 bash deploy/hel913-unified/worker_ctl.sh start + +# 停止顺序:先 worker 后 API(语音会话先释放,再关统一进程) +HEL913_BASE=/opt/cybergril-hel913 bash deploy/hel913-unified/worker_ctl.sh stop +HEL913_BASE=/opt/cybergril-hel913 bash deploy/hel913-unified/api_ctl.sh stop +``` + +启停脚本安全口径(收口版,`test_ctl_scripts.sh` 42 项离线替身验证): +非法子命令拒绝退出 2,绝不静默当 start;重复 start 拒绝(不丢既有 pid +句柄);stop/失败收尾只杀**身份核验通过**(cmdline 含本启动器/模块名且 +cwd 是本实例目录)的进程,PID 被复用时拒杀并报告;启动健康/注册检查 +不过时清掉自己刚起的进程、保留日志退出 1;`worker_ctl.sh` 启动前拒绝 +含未填 `<...>` 占位或解析失败的 `.env`。 + +同一数据目录**绝不允许**两个写入实例并存(双入口由 `deploy/voice/serve_dual_8360_8361.py` +在同一进程内保证;另起独立 uvicorn 会触发 lifespan 重入 fail-closed)。 + +## 5. 语音窗口唯一接线(获批窗口才动;默认全 0/未布防) + +浏览器从 HTTPS(8361) 页面连 LiveKit **必须走 wss**(混合内容策略), +`voice.yaml` 的 `livekit_url` 因此必须是 wss 桥地址(默认拟 `wss://192.168.200.12:8363`, +`bridge_ctl.template.sh` 启停,与 8361 同证书、回环转发 7880)——**不是 +可选件**;服务器内部(开房/删房/worker 注册)仍走 `livekit_internal` 的 +本机明文 7880。窗口内各上限的唯一接线: + +| 上限 | 接线(唯一) | 性质 | +| --- | --- | --- | +| ASR 音频总量 / 每通重连 | `worker/.env` 三项 `CYBERGRIL_ASR_WINDOW_ID`、`CYBERGRIL_ASR_WINDOW_MAX_AUDIO_SECONDS`、`CYBERGRIL_ASR_MAX_CONNECT_RETRIES_PER_CALL`(空/0=拒绝一切 ASR;布防值见 `voice-window-arm.template.env`) | 程序硬闸,发送前拒绝(`src/cybergril/voice/asr_usage.py`,逐通落账) | +| 每通墙钟/房间寿命 | `voice.yaml` `session_ttl_seconds: 600` + token TTL 300;挂断即删房(失败留账重试) | 程序硬闸 + 到期失效 | +| TTS 段数 | `tts_ctl.template.sh` start 时记录 tts.log 起始字节,`grep -c "first chunk"` 对照窗口段数上限 | 监控口径(到顶即人工 force_end;程序不硬闸) | +| 窗口总时限 | `force_end.template.sh watch ` 到点强制收尾(DELETE 全部会话→worker→api→bridge→tts,各按专属句柄);更可靠的等价物是获批后装 systemd timer 调 `now` | 可执行停服,非口头承诺 | + +窗口启动顺序:tts_ctl snapshot→start → bridge_ctl start → 改 `worker/.env` +布防三项并 worker_ctl start → api_ctl start(见 §4.2)→ force_end watch +挂上。窗口结束(到期/触线/人工):force_end now → 按 `seal_budget_hel879.py` +封账归档(**封账不解除**,旧三窗口余额也不结转进新窗)。 + +## 6. 备份与保留资料回退 + +- **发布/阶段归档**(本包口径,沿用 HEL-896 收口工具,一致性快照): + ```bash + # 停服口径(推荐;目录内 SQLite 一并 checkpoint+逐表核对) + bash deploy/hel913-unified/api_ctl.sh stop + /opt/cybergril-hel913/venv311/bin/python tools/release_snapshot_hel896.py snapshot \ + --name <阶段名> --db /opt/cybergril-hel913/data/companion.db \ + --file /opt/cybergril-hel913/data --out /opt/cybergril-hel913/archives/ + ``` + 产物含逐文件 SHA256、代码精确提交、逐表行数与回退演练;旧归档不删。 +- **回退**:停服 → 解包快照到新 data 目录(按 rollback_plan 源路径映射)→ + `current` 切回上一运行包提交(`git checkout <旧head>`)→ 起服务 → `verify` 通过。 +- **整实例撤销但保留资料**:`api_ctl.sh stop`,data/ 原样保留,重新启用 = + 同一 data 目录再 start(epoch 递增、聊天记忆保留,离线验证第 11 组复现)。 +- **CG-009 每日自动备份/用户手动完整导出(R6)尚未实现**,本节只是发布归档, + 不得冒充 R6 完成。 + +## 7. 零真实调用运行准备验证(已执行,34/34;口径如实) + +在 .12 产品 3.11 venv 对产品基线树实跑 `verify_offline.py`(进程内 +TestClient,零监听、零出站——网关地址重写为 127.0.0.1:1 + provider.stream +出站哨兵双保险): + +- 包完整性:Python 3.11.16、候选树 import、**4 个关键包版本打印** + (fastapi/uvicorn/httpx/pydantic——不是 19 项锁依赖的逐项核对)、记忆栈 + 四项缺置如实。 +- 入口/创建守门:health 诚实(llm.ok=false)、空库 404 无伪造档案、预览 + 本地计算、创建 201、二次创建 409、底色落库与预览一致、persona 在 cap=0 + 下诚实 failed(无编造诠释)。 +- 传输:SSE 对话在预算 0 下诚实 `chat.error`,零 provider 出站;失败轮如实 + 落库;同轮重试幂等拒绝。 +- 日子与回忆:**空库上的**未分享生活服务端裁剪(told=1;未做正反种子对照, + 不夸成实质权限全覆盖)、分页形状、未知回执/详情 404。 +- 语音:status 诚实 voice=false;TTS 不可达时开房 503 拒绝(绝不建房); + 未知会话 DELETE 404。 +- 重开:epoch=2、身份/关系/聊天保留、无伪造回复。 +- dist:GET / 出 index.html;8 文件哈希与 manifest 全符。 +- worker 接线:agent venv(livekit-agents 1.8.8)下 `import cybergril.voice.worker` + 通过(只读 import,未启动注册)。 + +**预算 0 下只能诚实拒绝的**:一切真实创建诠释/习惯、对话生成、生活事件 +生成、判定/成长/gist、主动措辞、嵌入、语音开房。`/api/health` 的 200 不代表 +模型已运行——`llm.ok=false` 才是诚实状态。 + +## 8. 真实记忆(Mem0)最少安装与接线 —— 完整可审清单(零安装零下载,待批) + +### 8.1 现状(2026-10-29 只读实测,收口轮复核沿用) + +- 服务器 .12 产品 `.venv311`(3.11.16)内 `mem0 / qdrant_client / + fastembed / onnxruntime` 四项均未安装;服务器全盘无 mem0 安装、无固定 + tarball、无已建轮、无 fastembed 缓存、uv 缓存仅 28K。**没有另一个已 + 准备好的 3.11 兼容环境**;旧 3.10 `hel857-venv`(工作站)不能混入 3.11 + 进程。 +- 工作站本地实物(未上服务器):固定源 tarball `mem0-2c81af3.tar.gz` + (sha256 `9dcef9303ebcec116eec6739f01b8b35bd24459de6a569686b4d19b9412dda23`, + 与 `deploy/mem0-stack.lock` 一致)、固定源已建轮 + `mem0ai-2.2.1-py3-none-any.whl`(sha256 + `698284b6083ab8b7761454d65876a6d1bb983d278ad21a7bee38d760cb20e9db`)、 + fastembed 模型缓存 92MB(Qdrant/bge-small-zh-v1.5 + bm25)、uv 缓存可 + 复用 3 轮(greenlet/pillow/truststore cp311)。 + +### 8.2 安装目标环境:本实例自己的隔离 venv(不是产品 .venv311) + +**绝不向借用的 `/opt/cybergril-hel879/current/.venv311` 增改任何依赖。** +目标环境是 `/opt/cybergril-hel913/venv311`(服务器既有 python3.11 基座 +新建,3.11.16),装两套东西:产品锁 19 项 + 记忆栈 41 项(40 缺轮 + 固定 +源 mem0ai 轮)。装好后本实例 API 用它跑(`api_ctl.sh` 里 +`HEL913_VENV=/opt/cybergril-hel913/venv311/bin/python`),与 hel879 实例 +彻底隔离;`.venv311` 保持只读借用前的原样(部署后可直接改回借用口径, +两者兼容——同一 requirements.lock)。 + +### 8.3 一次性批准窗口内的完整命令(逐项哈希留证;任一步失败即停) + +```bash +# W0 · 固定源身份核对(零下载;哈希不符立即停,禁用索引同名轮替代) +sha256sum /mem0-2c81af3.tar.gz # 必须等于 9dcef930…2dda23 +sha256sum /mem0ai-2.2.1-py3-none-any.whl # 必须等于 698284b6…20e9db + +# W1 · 获批下载窗口(工作站联网执行;cp311/manylinux2014_x86_64 定版) +mkdir -p wheels-product wheels-mem0 +python3 -m pip download --only-binary :all: --implementation cp \ + --python-version 3.11 --platform manylinux2014_x86_64 --abi cp311 \ + -r requirements.lock -d wheels-product/ # 产品锁 19 项 +python3 -m pip download --only-binary :all: --implementation cp \ + --python-version 3.11 --platform manylinux2014_x86_64 --abi cp311 \ + -c requirements.lock -r deploy/hel913-unified/mem0-cp311-wheels.txt -d wheels-mem0/ # 记忆栈 40 项 +# 其中 greenlet/pillow/truststore 3 项可从工作站 uv 缓存离线复用 +# (release-artifacts.json 记录),命中即从 wheels-mem0/ 删除同版本再放缓存件 +cp /mem0ai-2.2.1-py3-none-any.whl wheels-mem0/ +( cd wheels-product && sha256sum * > ../wheels-product.sha256 ) +( cd wheels-mem0 && sha256sum * > ../wheels-mem0.sha256 ) # 逐项留证入库 + +# W2 · 传输与落位(scp 到 .12;约 wheels 250–350MB + 模型缓存 92MB) +scp -r wheels-product wheels-mem0 wheels-product.sha256 wheels-mem0.sha256 /fastembed-cache root@192.168.200.12:/opt/cybergril-hel913/ +ssh root@192.168.200.12 'cd /opt/cybergril-hel913/wheels-product && sha256sum -c ../wheels-product.sha256 && \ + cd ../wheels-mem0 && sha256sum -c ../wheels-mem0.sha256' # 传输后复核 + +# W3 · 隔离 venv 创建 + 仅本地轮安装(--no-index 全程,失败即停) +ssh root@192.168.200.12 +/opt/cybergril-hel879/runtime/cpython-3.11.16/bin/python3.11 -m venv /opt/cybergril-hel913/venv311 +/opt/cybergril-hel913/venv311/bin/python -m pip install --no-index \ + --find-links /opt/cybergril-hel913/wheels-product -r /opt/cybergril-hel913/current/requirements.lock +/opt/cybergril-hel913/venv311/bin/python -m pip install --no-index \ + --find-links /opt/cybergril-hel913/wheels-mem0 \ + --find-links /opt/cybergril-hel913/wheels-product \ + -c /opt/cybergril-hel913/current/requirements.lock \ + mem0ai==2.2.1 qdrant-client==1.20.0 fastembed==0.9.1 +# mem0ai 的安装来源必须是 W0 的固定源码轮(同版本号不同来源不互认) + +# W4 · co-import 冒烟(同进程;失败即停,不降级) +cd /opt/cybergril-hel913/current +MEM0_TELEMETRY=False HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1 PYTHONPATH=src /opt/cybergril-hel913/venv311/bin/python -c \ + "import cybergril.api, mem0, qdrant_client, fastembed; print('co-import ok')" + +# W5 · 模型缓存落位(真实本地嵌入零下载) +mkdir -p /opt/cybergril-hel913/data/sem +cp -a /opt/cybergril-hel913/fastembed-cache /opt/cybergril-hel913/data/sem/fastembed-cache +# api_ctl.sh 通过 FASTEMBED_CACHE_PATH 指向它,模型资源禁止联网获取。 + +# W6 · 清理暂存(轮文件传输件用毕即删;哈希清单留档) +rm -rf /opt/cybergril-hel913/wheels-product /opt/cybergril-hel913/wheels-mem0 /opt/cybergril-hel913/fastembed-cache +``` + +**空间/影响(.12 实测口径估)**:`venv311` 成品约 450–550MB(产品 19 项 +约 120–150MB + 记忆栈约 330–400MB,参照 3.10 同栈实测 366MB)+ 传输暂存 +约 350MB(W6 删除)+ 模型缓存 92MB(常驻 data/sem)。首次本地嵌入零下载 +(缓存就位)。**当前不触发任何嵌入**:装完不批 B 预算时 +`CYBERGRIL_SEMANTIC=stub`,长期检索走词面 FTS,不以 stub 冒充真实 Mem0。 + +### 8.4 记忆账本与配置接线(新空用户库续账,不搬测试聊天) + +- `CYBERGRIL_SEMANTIC`(api_ctl.sh 的 `HEL913_SEMANTIC`):`stub`(默认, + 全链可跑、非语义)→ 批准后 `mem0`(真实)。 +- 配置:`cp config/semantic.example.yaml /opt/cybergril-hel913/etc/semantic.yaml`, + 启动器显式传 `CYBERGRIL_SEMANTIC_CONFIG=$BASE/etc/semantic.yaml`(不再误读仓库默认路径)。 + `MEM0_TELEMETRY=False`、`FASTEMBED_CACHE_PATH=$BASE/data/sem/fastembed-cache`、 + `HF_HUB_OFFLINE=1`、`TRANSFORMERS_OFFLINE=1` 由启动器在 import 前设置。 + 填 `ledger_path=/opt/cybergril-hel913/data/embed_ledger.sqlite3`、 + `vector_root/history_root=/opt/cybergril-hel913/data/sem/{vec,hist}`、 + 网关三键(与 models.yaml 同源)、`authority` 块。 +- **权威旧账续账(498 已用 / 原批 500 / 冻结 2 不动)**:把 HEL-857 系列 + 唯一权威嵌入账文件复制为实例 `data/embed_ledger.sqlite3`(只复制账本, + **不搬任何测试聊天/向量**——聊天在旧测试库,向量在新库从零生成); + `authority` 按 `config/semantic.example.yaml` 算式填 + `prefix_rows: 181`、`prefix_sha256: <权威账前 181 行逐行摘要,总管按账现值填>`、 + `min_used: 498`。批准 B 嵌入 180 条后写累计口径 + `embed_cap: 680`、`embed_reserve: 2`(可用 = 680−498−2 = 180;**禁止** + 把 180 直接填成 cap、禁止动冻结 2 条;缺/错 authority 在构造前拒绝, + 指向新空文件重开余额同样被拒——`src/cybergril/memory/semantic.py` + `verify_ledger_authority`)。 +- **不把旧实验余额当新用量**:39/60(HEL-857 真实请求旧账)与旧三窗口 + 余额一律不结转;用户实例的 LLM/请求账从 0 起记(自己的 SQLite 日账)。 +- **副作用如实分开**:真实 embed(写入向量库+权威账)、SDK 内部提取 LLM + (`llm_ops` 独立 cap + 统一请求闸 "semantic" 类)、co-import 冒烟(零 + 调用,只 import)——三者互不冒充;当前阶段三者全部 0。 + +## 9. 本包不做的事 + +不部署、不监听现网端口、不恢复 9880/8090/836x/8082、不触 4080、不下载任何 +依赖/模型/浏览器、不改模型/认证/运行时绑定、不合并 PR、不替换正式界面、 +8300/8310/8320/8350 与既有资料不动、8340/ComfyUI 保持停机、不向 +`/opt/cybergril-hel879/current/.venv311` 增改依赖。旧三次测试窗口余额不 +结转;一切提额、安装、部署都是待批准建议。 + + +## 10. 总管独立取件勘误与最终执行约束(2026-10-29) + +本节优先于上文笼统模板描述,当前仍全部待批,零真实调用。 + +- 运行包基线8ea5fdc之后仅机械收口:补语义配置路径/缓存/禁遥测离线变量; + force_end按真实持久化sessions列表取session_id;补桥/TTS非法命令拒绝和失败收尾; + 传输hash清单与目标工作目录、既有3.11解释器路径纠正;每通TTL改600秒与10分钟一致。 +- 40项记忆缺轮与产品19项锁是现有版本清单。下载使用两份requirements合并约束, + 不得让第二次下载/安装解析器静默换掉产品锁版本:W1增加`-c requirements.lock`, + W3记忆安装同时传`--find-links .../wheels-product -c .../requirements.lock`。 + 解析冲突/缺cp311轮即停并留首错,不自动换版本、不引入新模型。 +- B记忆日顶明确为embed_daily=60、llm_daily=40(SDK提取另受累计llm_cap=60), + authority/累计embed_cap=680、reserve=2原样。语义日账按UTC(北京时间08:00) + 切日,产品对话/生活日账显式TZ=Asia/Shanghai按北京时间00:00切日,不能混说。 +- 不运行测试脚本里的失管sleep替身作为长期任务。未来实际截止保护用获批后的 + systemd一次性定时服务(服务/日志/停机句柄可查);不能把本轮本地watch留到结束后。 + 部署卡执行`systemd-run --unit=cybergril-hel913-expiry --on-calendar="<明确UTC截止时间>" \ + /usr/bin/env HEL913_BASE=/opt/cybergril-hel913 /usr/bin/bash \ + /opt/cybergril-hel913/current/deploy/hel913-unified/force_end.template.sh now`, + 放行前必须核`systemctl list-timers cybergril-hel913-expiry.timer`及临时实例停止句柄。 + 这是未来待批动作,本轮没有创建服务/定时器。 +- 语音为团队陪同的3次预约短用,每次600秒,总ASR1800秒,建房尝试最多6次, + 每通ASR重连最多2次,TTS60段及声音登记+试听合计10次由当班执行者监督计数。 + 这些软上限不冒充程序硬闸;不在无人值守时开启语音窗口。 + TTS段顶/语音配额到顶只结束语音,B文字生活仍可用;到期/用户叫停才停全实例。 + 自定义声音需要既有8090面板的实例专属启动句柄与前后快照, + 复用HEL907已有面板启动方法,不动8081;句柄或快照未备齐不放行上传/试听, + 默认已有工程声音仍能通话。共享voices列表需恢复前快照,自定义参考材料在独立实例保留。 +- 到期脚本负责停本实例入口与自己启动的语音服务。封账归档、LiveKit只读核空、 + voices快照恢复由当班执行者另行执行并核实,不能把日志里的done当全部清理完成。 + 清理失败保留原始错误/账/日志,不解除封账、不重启服务来“证明已停”。 + + +自定义声音细分额度(同一次待批方案):最多登记2段参考音频,每段由陪同人员先核≤20秒; +自动转写最多2次/合计40秒,单列监督账,不冒充通话ASR的1800秒硬闸。 +最多试听8次;登记2次+试听8次共10次。试听合成并入60段TTS总上限。 +两类ASR合计最大1840秒(通话1800+参考40);失败/重试同样记账,不返余额。 +本轮未执行登记、转写或试听。 + + +### 最终统一预算勘误:团队先验真实记忆,再交用户(总管) + +此条取代上文A“0嵌入”的旧建议,仅修改待批方案,当前真实额度仍0。 +团队先用独立测试资料核真实Mem0接入,不把首次故障留给用户: +A的55请求/50万tokens内,最多10次用于SDK提取;先用最多20条新嵌入。 +这10次/20条从同一批新申请的60次提取/180条嵌入中先扣, +不是另加额度;余量(至少50次提取/160条嵌入)留给用户期,使用实账计算。 +两阶段共用唯一活动记账文件、串行写入,测试向量/档案与用户向量/档案分开, +只共用计数账,不拷贝测试聊天或测试记忆给用户。旧权威embed_ops前缀不改, +旧39/60实验的授权和账原封保存,不挪用其余额;新提取批建立独立可审计操作记录。 +如旧llm_ops表已有用量,新60次必须按现值加增量并保存旧行,不可清表/重置; +操作前核旧基数并留证,实际新增始终≤60。团队检查最多2小时,从首个真实调用计时; +检查不通过则停止并报告首个错误,用户期不自动开始。 diff --git a/deploy/hel913-unified/api_ctl.sh b/deploy/hel913-unified/api_ctl.sh new file mode 100644 index 0000000..edee4d1 --- /dev/null +++ b/deploy/hel913-unified/api_ctl.sh @@ -0,0 +1,146 @@ +#!/usr/bin/env bash +# HEL-913 统一体验运行包 · 统一实例 API 启停(单进程 HTTP+HTTPS 双入口) +# +# 启动器 deploy/voice/serve_dual_8360_8361.py 保证 HTTP 与 HTTPS 共用同一 +# API 进程/同一份状态(lifespan 只进一次)。本脚本只做本实例常规启停安全: +# - 子命令只认 start|stop|status,其他一律拒绝退出 2(不会静默当 start); +# - start 前核对 pid 句柄:本实例已在跑 → 拒绝重复 start(不覆盖句柄); +# 句柄 pid 活着但不属于本实例(PID 复用)→ 拒绝动手,提示人工处理; +# - stop/start 失败收尾只杀经身份核验的进程(/proc/ 的 cmdline 含 +# 本启动器且 cwd 是本实例代码目录),身份不符一律不杀; +# - start 健康检查不过 → 清掉自己刚起的进程、保留日志、退出 1。 +# +# 用法: [ENV=..] bash deploy/hel913-unified/api_ctl.sh start|stop|status +# 环境变量(默认值按部署说明的实例布局): +# HEL913_BASE 实例根目录(含 current/ etc/ data/ logs/) +# HEL913_HTTP HTTP 端口(默认 8360) +# HEL913_HTTPS HTTPS 端口(默认 8361;语音页需要 secure context) +# HEL913_VENV 产品 3.11 venv python(默认 /opt/cybergril-hel879/current/.venv311/bin/python) +# HEL913_CERT / HEL913_KEY 证书路径(默认 /opt/cybergril-voice-certs/ 下既有证书) +set -u +BASE=${HEL913_BASE:-/opt/cybergril-hel913} +CODE=${HEL913_CODE:-$BASE/current} +HTTP=${HEL913_HTTP:-8360} +HTTPS=${HEL913_HTTPS:-8361} +VENV=${HEL913_VENV:-/opt/cybergril-hel879/current/.venv311/bin/python} +CERT=${HEL913_CERT:-/opt/cybergril-voice-certs/cybergril-voice.pem} +KEY=${HEL913_KEY:-/opt/cybergril-voice-certs/cybergril-voice.key} +LOG=$BASE/logs/api.log +PIDF=$BASE/logs/api.pid +MARKER="serve_dual_8360_8361.py" + +usage() { echo "usage: [ENV=..] bash $0 start|stop|status" >&2; exit 2; } + +# 进程身份核验:pid 存活、cmdline 含本启动器、cwd 是本实例代码目录。 +# 任一不满足即视为“不是我们的进程”(含 PID 被系统复用),一律不动它。 +ours() { + local p=$1 cmd cw + case "$p" in ''|*[!0-9]*) return 1 ;; esac + [ "$p" != "$$" ] || return 1 + kill -0 "$p" 2>/dev/null || return 1 + cmd=$(tr '\0' ' ' < "/proc/$p/cmdline" 2>/dev/null) || return 1 + cw=$(readlink "/proc/$p/cwd" 2>/dev/null) || return 1 + case "$cmd" in *"$MARKER"*) ;; *) return 1 ;; esac + [ "$cw" = "$CODE" ] || return 1 + return 0 +} + +pidfile_pid() { [ -f "$PIDF" ] && cat "$PIDF" 2>/dev/null; } + +stop() { + local p + p=$(pidfile_pid) + if [ -z "$p" ]; then + echo "api not running (no pid file)" + return 0 + fi + if kill -0 "$p" 2>/dev/null && ! ours "$p"; then + echo "REFUSED: pid $p is alive but is NOT this instance's api (pid reuse?) — not killing; resolve $PIDF manually" >&2 + return 1 + fi + if ours "$p"; then + kill "$p" + sleep 3 + if ours "$p"; then kill -9 "$p"; sleep 1; fi + fi + rm -f "$PIDF" + echo "api stopped" + return 0 +} + +start() { + local p + p=$(pidfile_pid) + if [ -n "$p" ] && kill -0 "$p" 2>/dev/null; then + if ours "$p"; then + echo "REFUSED: api already running (pid $p) — stop first" >&2 + else + echo "REFUSED: pid file has live pid $p that is not this instance's api (pid reuse?) — resolve $PIDF manually" >&2 + fi + exit 1 + fi + rm -f "$PIDF" # 死句柄:清掉再起 + [ -d "$CODE" ] || { echo "REFUSED: code dir missing: $CODE" >&2; exit 1; } + [ -x "$VENV" ] || { echo "REFUSED: venv python not executable: $VENV" >&2; exit 1; } + [ -r "$CERT" ] && [ -r "$KEY" ] || { echo "REFUSED: cert/key not readable ($CERT / $KEY)" >&2; exit 1; } + mkdir -p "$BASE/logs" "$BASE/data" + cd "$CODE" || exit 1 + set -a + export CYBERGRIL_DB=$BASE/data/companion.db + export CYBERGRIL_SEMANTIC=${HEL913_SEMANTIC:-stub} + export CYBERGRIL_R2_ENABLED=${HEL913_R2:-1} + export TZ=Asia/Shanghai + export CYBERGRIL_SEMANTIC_CONFIG=$BASE/etc/semantic.yaml + export MEM0_TELEMETRY=False + export FASTEMBED_CACHE_PATH=$BASE/data/sem/fastembed-cache + export HF_HUB_OFFLINE=1 + export TRANSFORMERS_OFFLINE=1 + export CYBERGRIL_MODELS=$BASE/etc/models.yaml + export CYBERGRIL_VOICE_CONFIG=$BASE/etc/voice.yaml + export CYBERGRIL_WEB=$CODE/web/dist + export PYTHONPATH=$CODE/src + set +a + nohup "$VENV" deploy/voice/serve_dual_8360_8361.py \ + --http-port "$HTTP" --https-port "$HTTPS" --cert "$CERT" --key "$KEY" \ + > "$LOG" 2>&1 & + p=$! + echo "$p" > "$PIDF" + echo "api starting (pid $p) http=$HTTP https=$HTTPS" + sleep 6 + if curl -s --max-time 3 "http://127.0.0.1:$HTTP/api/health" >/dev/null \ + && curl -sk --max-time 3 "https://127.0.0.1:$HTTPS/api/health" >/dev/null; then + echo "OK: $HTTP + $HTTPS healthy" + return 0 + fi + echo "FAIL: health check did not pass — full log kept at $LOG; tail:" >&2 + tail -20 "$LOG" >&2 + # 失败收尾:只清自己刚起的这个进程(身份核验),日志保留 + if ours "$p"; then + kill "$p"; sleep 2 + ours "$p" && kill -9 "$p" + fi + rm -f "$PIDF" + echo "cleaned up failed start (log kept: $LOG)" >&2 + exit 1 +} + +status() { + local p + p=$(pidfile_pid) + if [ -n "$p" ]; then + if ours "$p"; then echo "running pid $p (identity verified)" + elif kill -0 "$p" 2>/dev/null; then echo "pid $p alive but NOT ours (stale pid file?)" + else echo "pid $p not alive (stale pid file)"; fi + else + echo "no pid file" + fi + curl -s --max-time 3 "http://127.0.0.1:$HTTP/api/health" && echo " <- health" \ + || echo "health not answering" +} + +case "${1:-}" in + start) start ;; + stop) stop ;; + status) status ;; + *) usage ;; +esac diff --git a/deploy/hel913-unified/bridge_ctl.template.sh b/deploy/hel913-unified/bridge_ctl.template.sh new file mode 100644 index 0000000..7f264cb --- /dev/null +++ b/deploy/hel913-unified/bridge_ctl.template.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# HEL-913 运行包 · wss TLS 桥启停模板(8363 → 127.0.0.1:7880;脱敏待批件) +# +# 浏览器从 HTTPS(8361) 页面连 LiveKit 必须走 wss(混合内容策略),本桥在新 +# 端口终结 TLS 并只向回环 7880 转发(字节级,协议无关;WebRTC 媒体面不经 +# 本桥)。与 8361 同一张自签证书。改编自 HEL-910 已实测 bridge_ctl.sh; +# 本文件默认不运行,获批语音窗口才使用。子命令只认 start|stop|status。 +# +# 用法: [ENV=..] bash bridge_ctl.template.sh start|stop|status +# 环境变量: HEL913_BASE / HEL913_BRIDGE_PORT(8363) / HEL913_CERT / HEL913_KEY +set -u +case "${1:-}" in start|stop|status) ;; *) echo "invalid subcommand" >&2; exit 2 ;; esac +BASE=${HEL913_BASE:-/opt/cybergril-hel913} +PORT=${HEL913_BRIDGE_PORT:-8363} +CERT=${HEL913_CERT:-/opt/cybergril-voice-certs/cybergril-voice.pem} +KEY=${HEL913_KEY:-/opt/cybergril-voice-certs/cybergril-voice.key} +LOG=$BASE/logs/bridge.log +PIDF=$BASE/logs/bridge.pid +MARKER="wss_bridge.example.py" + +usage() { echo "usage: [ENV=..] bash $0 start|stop|status" >&2; exit 2; } + +ours() { + local p=$1 cmd cw + case "$p" in ''|*[!0-9]*) return 1 ;; esac + [ "$p" != "$$" ] || return 1 + kill -0 "$p" 2>/dev/null || return 1 + cmd=$(tr '\0' ' ' < "/proc/$p/cmdline" 2>/dev/null) || return 1 + cw=$(readlink "/proc/$p/cwd" 2>/dev/null) || return 1 + case "$cmd" in *"$MARKER"*) ;; *) return 1 ;; esac + [ "$cw" = "$BASE/current/deploy/voice" ] || return 1 + return 0 +} + +pidfile_pid() { [ -f "$PIDF" ] && cat "$PIDF" 2>/dev/null; } + +stop() { + local p + p=$(pidfile_pid) + if [ -z "$p" ]; then echo "bridge not running (no pid file)"; return 0; fi + if kill -0 "$p" 2>/dev/null && ! ours "$p"; then + echo "REFUSED: pid $p alive but not this bridge (pid reuse?) — not killing; resolve $PIDF manually" >&2 + return 1 + fi + if ours "$p"; then kill "$p"; sleep 2; ours "$p" && kill -9 "$p"; fi + rm -f "$PIDF" + echo "bridge stopped" +} + +case "${1:-}" in + stop) stop; exit $? ;; + status) + p=$(pidfile_pid) + if [ -n "$p" ]; then + ours "$p" && echo "running pid $p (identity verified)" \ + || { kill -0 "$p" 2>/dev/null && echo "pid $p alive but NOT ours" || echo "pid $p not alive (stale)"; } + else + echo "no pid file" + fi + exit 0 ;; +esac + +p=$(pidfile_pid) +if [ -n "$p" ] && kill -0 "$p" 2>/dev/null; then + echo "REFUSED: bridge already running (pid $p)" >&2; exit 1 +fi +rm -f "$PIDF" +[ -r "$CERT" ] && [ -r "$KEY" ] || { echo "REFUSED: cert/key not readable" >&2; exit 1; } +cd "$BASE/current/deploy/voice" || exit 1 +nohup /usr/bin/python3 wss_bridge.example.py \ + --listen "0.0.0.0:$PORT" --upstream 127.0.0.1:7880 \ + --cert "$CERT" --key "$KEY" > "$LOG" 2>&1 & +echo $! > "$PIDF" +echo "bridge starting (pid $(cat "$PIDF")) on :$PORT" +sleep 3 +timeout 8 openssl s_client -connect "127.0.0.1:$PORT" -servername 192.168.200.12 /dev/null \ + | grep -q "TLSv1.3" && echo "OK: bridge TLS up" \ + || { echo "FAIL: TLS probe failed — check $LOG"; tail -5 "$LOG"; stop; exit 1; } diff --git a/deploy/hel913-unified/dist-manifest.json b/deploy/hel913-unified/dist-manifest.json new file mode 100644 index 0000000..91c842c --- /dev/null +++ b/deploy/hel913-unified/dist-manifest.json @@ -0,0 +1,10 @@ +{ + "assets/her-room.jpg": "51ca450a454c108d304feee2745070fbbbd3232863096126badf279ee53a925a", + "assets/her-talk.jpg": "0b9e91b58d75afb836efdad3d8e8f0aab0c44df64b2c4ddd59d011fd499aeb33", + "assets/icons.svg": "8630691c7349dd33c1accf2331cb4c844ffd9b70465021b1c18bf8f5aea0588e", + "assets/index-B8TdHoQm.css": "a0af3163f564f2ff9cfe7d995378b7ac436bfe2f9fabda9f370ca0aa30c2412f", + "assets/index-Vm-EFPLb.js": "81f55e20c5c2afe4673ccc9020264a2bf99b3c84998c275f03fa48fe4f940bb0", + "demo-material.json": "78fd67e8e4a680e8b3277db4a922360a83fa0be1ceee0cb257ba1c74bc6ede37", + "index.html": "8d84f131b6922fe92ac0c562f5fec73aacd9c6e4db2cfa28f6190b4246f9c8d0", + "vendor/livekit-client-2.22.4.umd.min.js": "040db4c0db72e7f7599cd0e06b0f58f95e0082a395cdde410ca26224a79a0626" +} \ No newline at end of file diff --git a/deploy/hel913-unified/force_end.template.sh b/deploy/hel913-unified/force_end.template.sh new file mode 100644 index 0000000..eb88ddb --- /dev/null +++ b/deploy/hel913-unified/force_end.template.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# HEL-913 运行包 · 窗口到期强制收尾模板(脱敏待批件;默认不布防) +# +# 用途:获批体验/语音窗口到截止时点的**可执行**停服与封窗:先经统一实例 +# API 逐个 DELETE 本实例房间(欠清理的房间会留在登记账重试),再按专属 +# pid 句柄停 worker → api → wss 桥 → 共享 TTS(都不是我们的就如实报告, +# 绝不按进程名/端口扫杀,绝不触碰 8300/8310/8320/8350、7880/7881、10095、 +# 8081 等共享服务)。改编自 HEL-910 已实测 force_end.sh/watchdog.sh。 +# +# 用法: +# bash force_end.template.sh now # 立即收尾(幂等,可重复) +# bash force_end.template.sh watch # 到点自动收尾(受监督后台任务; +# # 更可靠的等价物是获批后装 +# # systemd timer 调本脚本 now) +# 环境变量: HEL913_BASE / HEL913_HTTP(8360) +set -u +set -o pipefail +BASE=${HEL913_BASE:-/opt/cybergril-hel913} +HTTP=${HEL913_HTTP:-8360} +PKG=$BASE/current/deploy/hel913-unified +LOG=$BASE/logs/force_end.log + +usage() { echo "usage: [ENV=..] bash $0 now|watch " >&2; exit 2; } + +force_end() { + mkdir -p "$BASE/logs" + ts() { date -u +%FT%TZ; } + { + echo "[$(ts)] force_end begin" + # 1) 结清本实例房间(DELETE 走统一实例所有者路径;失败房间留账重试) + for sid in $(python3 - "$BASE/data" <<'EOF' 2>/dev/null +import json, sys, pathlib +p = pathlib.Path(sys.argv[1]) / "companion-voice-sessions.json" +try: + d = json.loads(p.read_text()) +except Exception: + raise SystemExit +# VoiceSessionService._persist_state stores sessions as a LIST, not a dict. +# Debts already follow bounded SDK cleanup; do not reset their retry budgets here. +for entry in d.get("sessions", []): + sid = entry.get("session_id", "") + if sid and all(c.isalnum() or c in "-_" for c in sid): + print(sid) +EOF +); do + code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 8 -X DELETE "http://127.0.0.1:$HTTP/api/voice/session/$sid" || echo err) + echo "[$(ts)] DELETE session $sid -> $code" + done + # 2) 按专属句柄逐个停(每步幂等;不是我们的如实报告) + bash "$PKG/worker_ctl.sh" stop 2>&1 | sed 's/^/ worker: /' + bash "$PKG/api_ctl.sh" stop 2>&1 | sed 's/^/ api: /' + bash "$PKG/bridge_ctl.template.sh" stop 2>&1 | sed 's/^/ bridge: /' + bash "$PKG/tts_ctl.template.sh" stop 2>&1 | sed 's/^/ tts: /' + echo "[$(ts)] force_end done(封账归档另由 seal_budget_hel879.py 按账执行,封账不解除)" + } >> "$LOG" 2>&1 +} + +case "${1:-}" in + now) force_end ;; + watch) + DL=${2:?deadline epoch seconds required} + case "$DL" in *[!0-9]*|'') usage ;; esac + mkdir -p "$BASE/logs" + echo "[$(date -u +%FT%TZ)] watch start, deadline $(date -u -d "@$DL" +%FT%TZ) (pid $$)" >> "$LOG" + while [ "$(date +%s)" -lt "$DL" ]; do sleep 30; done + echo "[$(date -u +%FT%TZ)] DEADLINE REACHED — force end" >> "$LOG" + force_end + ;; + *) usage ;; +esac diff --git a/deploy/hel913-unified/manifest.json b/deploy/hel913-unified/manifest.json new file mode 100644 index 0000000..c448f82 --- /dev/null +++ b/deploy/hel913-unified/manifest.json @@ -0,0 +1,86 @@ +{ + "task": "HEL-913", + "product_baseline_head": "4a38106eef21c43ba24ab7d07992b1bfe539137d", + "package_head": "运行包提交 = 本文件所在分支 tip(交付评论给出精确 hash;本文件无法写死自身提交)。部署照抄入口是运行包提交,只 checkout 4a38106 会没有 deploy/hel913-unified/", + "package_only_change_check": "git diff --stat 4a38106 -- src web config tools tests 必须为空(运行包只动 deploy/hel913-unified 与 docs)", + "branch": "agent/2/8e99cc2cc592", + "review": "HEL-912 01a187e8-1058-7a2d-84e7-bd1896f212cb passed, no must-fix items (product baseline)", + "requirements_lock": "requirements.lock (19 pinned, Python 3.11.16 verified; offline verify printed 4 key package versions only — not a per-item lock audit)", + "web_dist_reuse": { + "source": "/opt/cybergril-hel910/current/web/dist (REVISION 958c6962fb1f2a4462a6df794d893aee1fcb0667)", + "justification": "git diff 958c696 4a38106 -- web/ is empty", + "manifest": "deploy/hel913-unified/dist-manifest.json (8 files, sha256 measured on .12 2026-10-29)" + }, + "environments": { + "api_python_default": "/opt/cybergril-hel879/current/.venv311 (Python 3.11.16) — borrow-run only, NEVER install into it", + "api_python_after_mem0": "/opt/cybergril-hel913/venv311 — isolated instance venv created only inside the approved one-shot window (README §8)", + "worker_python": "/opt/voice-agent/agent/venv (livekit-agents 1.8.8)", + "certs": "/opt/cybergril-voice-certs/cybergril-voice.{pem,key} (shared by 8361 and wss bridge 8363)", + "livekit": "ws://127.0.0.1:7880 internal (standing); browser MUST use wss bridge (8363) — not optional from HTTPS pages", + "tts_panel_bridge": "9880/8090/8363 only inside approved voice windows (README §5); stopped otherwise" + }, + "package_files": [ + "deploy/hel913-unified/README.md", + "deploy/hel913-unified/manifest.json", + "deploy/hel913-unified/dist-manifest.json", + "deploy/hel913-unified/models.template.yaml", + "deploy/hel913-unified/voice.template.yaml", + "deploy/hel913-unified/worker.env.template", + "deploy/hel913-unified/voice-window-arm.template.env", + "deploy/hel913-unified/api_ctl.sh", + "deploy/hel913-unified/worker_ctl.sh", + "deploy/hel913-unified/bridge_ctl.template.sh", + "deploy/hel913-unified/tts_ctl.template.sh", + "deploy/hel913-unified/force_end.template.sh", + "deploy/hel913-unified/verify_offline.py", + "deploy/hel913-unified/test_ctl_scripts.sh", + "deploy/hel913-unified/mem0-cp311-wheels.txt", + "docs/rework/hel913/verify-offline-run.txt", + "docs/rework/hel913/ctl-checks-run.txt", + "docs/hel913-capability-and-enablement.md" + ], + "offline_verification": { + "where": ".12 product .venv311, candidate tree at /tmp/hel913-verify (cleaned after run); re-run by manager: 34/34 passed, dist 8-file hashes match", + "result": "34/34 checks passed; worker import ok under livekit-agents 1.8.8", + "zero_real_calls": "models base_url rewritten to 127.0.0.1:1 + provider.stream outbound sentinel; no listening sockets; no rooms; no gateway contact", + "scope_limits": "deps check printed 4 key versions only; unshared-life trimming verified on an empty DB (told=1) without positive/negative seed contrast — not claimed as full dependency or permission coverage", + "ctl_scripts": "test_ctl_scripts.sh 42/42 offline stub checks (invalid subcommand, duplicate start, pid-reuse refusal, failed-start cleanup, .env gating, templates default 0)" + }, + "voice_window_wiring": { + "asr_hard_gate": "worker/.env CYBERGRIL_ASR_WINDOW_ID/_MAX_AUDIO_SECONDS/_MAX_CONNECT_RETRIES_PER_CALL (empty/0 = deny all ASR)", + "room_lifetime": "voice.yaml session_ttl_seconds=900 + token_ttl 300 + delete-on-hangup (bounded cleanup ledger)", + "tts_segments": "monitored via tts.log first-chunk count since window start (tts_ctl.template.sh records start byte); hard stop = window deadline", + "deadline": "force_end.template.sh now|watch (DELETE sessions -> worker -> api -> bridge -> tts, pid-handle stops only)" + }, + "memory_gap": { + "server_venv311": "mem0/qdrant_client/fastembed/onnxruntime all absent (verified 2026-10-29; never install into this borrowed venv)", + "no_other_311_env_on_server": true, + "local_artifacts": { + "mem0-2c81af3.tar.gz": "9dcef9303ebcec116eec6739f01b8b35bd24459de6a569686b4d19b9412dda23", + "mem0ai-2.2.1-py3-none-any.whl": "698284b6083ab8b7761454d65876a6d1bb983d278ad21a7bee38d760cb20e9db", + "fastembed_cache": "92MB (Qdrant/bge-small-zh-v1.5 + bm25)", + "hel857_venv": "Python 3.10.12 — must not mix into 3.11 process" + }, + "install_target": "isolated /opt/cybergril-hel913/venv311 (python3.11 base + product lock 19 wheels + mem0 stack 40 wheels + fixed-source mem0ai wheel; all --no-index; complete auditable steps in README §8.3 with per-file sha256 evidence and fail-stop)", + "ledger_continuation": "authoritative embed ledger copied to instance data/embed_ledger.sqlite3 (ledger only — no test chats/vectors moved); authority prefix_rows=181, prefix_sha256 by manager, min_used=498, embed_cap=680 cumulative formula, frozen 2 untouched; 39/60 and old window balances never carried over" + }, + "enablement_doc": "docs/hel913-capability-and-enablement.md §2 — single consistent pending-approval proposal: A/B/C separate accounts, all awaiting approval (manager does not self-approve A), B voice = 3 calls x <=10min = <=1800s ASR hard gate with per-cap consequences and executable window end, B/C boundary fixed", + "boundaries": "not deployed; nothing listening; caps all 0; no PR; existing instances and data untouched; no downloads this card; protected venvs untouched", + "coordinator_close_verification": "docs/rework/hel913/coordinator-close-checks.txt: 17/17 targeted offline checks; product baseline unchanged", + "file_sha256": { + "deploy/hel913-unified/README.md": "580e3100b207c8c0e3da9cddc644b00d7b3d66ae0d195ac058dba279f63e242e", + "deploy/hel913-unified/api_ctl.sh": "3eeff54fcdc1becb79d9fe63767eceee9209ed6239663ff7d0a3e90d27379c55", + "deploy/hel913-unified/bridge_ctl.template.sh": "43e951f01ceed07b9c1606cfc046aa1d34eefc8fad3a40c66db2de57f16c1d14", + "deploy/hel913-unified/dist-manifest.json": "c2ebc6e515096b535f5e08cce18dc9c4f104c1b2cabe3f3afb5fdd88f12d63db", + "deploy/hel913-unified/force_end.template.sh": "3f250942ff68e26e4a71750e17fd804fa57be921097d83ee76f1b5b0024fe893", + "deploy/hel913-unified/mem0-cp311-wheels.txt": "2a6fd3d287fe0713a2ac683584ee7f3723c83c236696f39551015ca2db358b94", + "deploy/hel913-unified/models.template.yaml": "0b8f051cedfcf0218929a652fed91b00762c33dd17fbb8f7eadcc50c65d4e837", + "deploy/hel913-unified/test_ctl_scripts.sh": "702f7b911598fc0433f63f250f2f1e58dec9d727dd7eb6ae8a773d521fcad5cd", + "deploy/hel913-unified/tts_ctl.template.sh": "f638f24a41f07cfef64e66eee996b4280fdd7aac80a5fc20be6d47a41b72cabf", + "deploy/hel913-unified/verify_offline.py": "86ea3da6f2c682ad8ee9a932f91d713e0778c61be5d4c15f916e38d15e3e66e4", + "deploy/hel913-unified/voice-window-arm.template.env": "4002b99d63fa556a5f1e8cb86bf74846e7b944571dd76ed40bfb549cf64f3307", + "deploy/hel913-unified/voice.template.yaml": "55e556d5df149ab4fa257640b4d87dc40bb43107a5e90279826516a7ec423f4c", + "deploy/hel913-unified/worker.env.template": "be80fc87cc04a379a776732ad94f392e08af4ecb9eaad4190a090f4071f40383", + "deploy/hel913-unified/worker_ctl.sh": "57fd028e3b0d932cdbde483925cc7e39a5b994fa53742f9350cc5fc22381077f" + } +} diff --git a/deploy/hel913-unified/mem0-cp311-wheels.txt b/deploy/hel913-unified/mem0-cp311-wheels.txt new file mode 100644 index 0000000..85a4be4 --- /dev/null +++ b/deploy/hel913-unified/mem0-cp311-wheels.txt @@ -0,0 +1,45 @@ +# HEL-913 记忆栈 cp311 缺项轮清单(40 项;来源 docs/rework/hel879/sdk-audit/sdk-audit.json 闭包 55-产品已有) +# mem0ai 不在本表:只认固定源码轮 sha256 698284b6…(deploy/hel895-release-candidate.md §4 唯一入口)。 +# 获批下载命令:pip download --only-binary :all: --implementation cp --python-version 3.11 \ +# --platform manylinux2014_x86_64 --abi cp311 -r 本文件 -d wheels/ 逐项 sha256 留证。 +# 其中 greenlet/pillow/truststore 三项已在工作站 uv 缓存命中(hel-879 release-artifacts 记录),可离线复用。 +backoff==2.2.1 +charset-normalizer==3.5.2 +coloredlogs==15.0.1 +distro==1.9.0 +fastembed==0.9.1 +filelock==4.1.0 +flatbuffers==25.12.19 +fsspec==2026.9.0 +greenlet==3.5.6 +grpcio==1.84.1 +h2==4.4.1 +hf-xet==1.7.0 +hpack==4.2.0 +httpcore2==2.14.0 +httpx2==2.14.0 +huggingface-hub==1.33.0 +humanfriendly==10.0 +hyperframe==6.1.0 +jiter==0.18.0 +loguru==0.7.3 +mmh3==5.3.1 +mpmath==1.3.0 +numpy==2.2.6 +onnxruntime==1.23.2 +openai==3.37.0 +pillow==12.3.0 +portalocker==4.4.1 +posthog==7.75.11 +protobuf==6.33.6 +py-rust-stemmers==0.1.8 +pytz==2026.5 +qdrant-client==1.20.0 +requests==2.34.2 +sniffio==1.3.1 +sqlalchemy==2.0.55 +sympy==1.14.0 +tokenizers==0.23.3 +tqdm==4.70.1 +truststore==0.10.5 +urllib3==2.8.0 diff --git a/deploy/hel913-unified/models.template.yaml b/deploy/hel913-unified/models.template.yaml new file mode 100644 index 0000000..e4af16a --- /dev/null +++ b/deploy/hel913-unified/models.template.yaml @@ -0,0 +1,89 @@ +# HEL-913 统一体验运行包 · models 配置模板(脱敏;本文件一切预算=0) +# +# 用法:复制到 <实例>/etc/models.yaml,按部署说明只改 base_url 指向获批网关、 +# 按批准包写入预算值。未经批准一律保持 0(发送前拒绝,fail-closed)。 +# 候选分支 config/models.yaml 的 tiers/routing 与本模板一致;本模板把 +# base_url 换成占位符并保留 HEL-908 reasoning: buffer_turn 与 HEL-911/912 +# 审核通过的调度修正(turn_detection=None 在代码侧,见 src/cybergril/voice/worker.py)。 +tiers: + high: + provider: openai_compat + model: <获批模型名> + base_url: + api_key_env: null + timeout: 60.0 + temperature: 0.85 + reasoning: buffer_turn + standard: + provider: openai_compat + model: <获批模型名> + base_url: + api_key_env: null + timeout: 40.0 + temperature: 0.7 + reasoning: buffer_turn + low: + provider: openai_compat + model: <获批模型名> + base_url: + api_key_env: null + timeout: 20.0 + temperature: 0.4 + reasoning: buffer_turn +routing: + dialogue_main: high + dialogue_followup: standard + proactive_message: high + crisis: high + life_event: low + memory_gist: low + fact_extract: low + output_guard: low + pattern_detect: low + filler: none + tick: none + reach_out: none + avatar_drive: none +budget: + daily_tokens: 0 + warn_ratio: 0.7 + hard_stop_ratio: 0.95 + daily_requests: + dialogue: 0 + life: 0 + background: 0 + semantic: 0 + # 用户实例永不配置一次性总量(total_requests/total_tokens 保持缺省): + # 一次性总量只属于独立团队测试实例,见 docs/budget-package-hel895.md §6。 +asr: + provider: scripted + model: qwen3-asr-1.7b + language: zh +paralinguistic: + provider: scripted + model: sensevoice-small + window_ms: 1500 + enabled: true +tts: + provider: silence + model: fun-cosyvoice3-0.5b + voice_ref: null + sample_rate: 16000 +vad: + provider: energy + model: silero-v6 + sample_rate: 16000 +turn: + provider: heuristic + silence_min_ms: 150 + silence_hard_end_ms: 800 +embedding: + provider: openai_compat + model: qwen3-embedding-0.6b + base_url: null + api_key_env: null + dim: 1024 +avatar: + route: vrm + renderer: three.js + lipsync: envelope diff --git a/deploy/hel913-unified/test_ctl_scripts.sh b/deploy/hel913-unified/test_ctl_scripts.sh new file mode 100644 index 0000000..b7445ec --- /dev/null +++ b/deploy/hel913-unified/test_ctl_scripts.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash +# HEL-913 运行包 · 启停脚本离线替身检查(零真实服务、零真实端口、零出站) +# +# 用离线替身(stub 解释器 = 只休眠的脚本)与静态检查验证收口后的常规启停 +# 安全:非法子命令拒绝、重复 start 拒绝、stop 的 PID 复用防护、启动失败 +# 收尾清理、.env 占位/解析守门。不启动真实 API/worker/共享服务,不监听 +# 任何端口(curl 只连本机高位死端口,必然失败,正是失败路径所需的输入)。 +# +# 用法: bash deploy/hel913-unified/test_ctl_scripts.sh (在检出根目录) +set -u +PKG=$(cd "$(dirname "$0")" && pwd) +ROOT=$(cd "$PKG/../.." && pwd) +TMP=$(mktemp -d /tmp/hel913-ctl-test.XXXXXX) +PASS=0; FAIL=0 +cleanup() { rm -rf "$TMP"; } +trap cleanup EXIT + +ok() { PASS=$((PASS+1)); echo "PASS $1"; } +bad() { FAIL=$((FAIL+1)); echo "FAIL $1"; } +expect_rc() { # expect_rc