diff --git a/.cursor/rules/system-app-config-no-direct-edit.mdc b/.cursor/rules/system-app-config-no-direct-edit.mdc index d9708487..eb6f1a6c 100644 --- a/.cursor/rules/system-app-config-no-direct-edit.mdc +++ b/.cursor/rules/system-app-config-no-direct-edit.mdc @@ -14,6 +14,6 @@ alwaysApply: true ## 正确做法 -- 本机语音输入真源:**Handy中文语音**(`01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md`);豆包已于 2026-06-11 完整卸载。 +- 本机语音输入真源:**Handy中文语音**(`02_卡人(水)/水溪_整理归档/语音输入/SKILL.md`);豆包已于 2026-06-11 完整卸载。 - 其他 `~/Library/Application Support/**` 配置:用 **Shell + Python/bash 脚本** 写入;脚本放在仓库 `脚本/` 目录,不在 Cursor 里打开目标文件改。 - 需卸载第三方 App:用仓库 `脚本/` 下专用卸载脚本(如 `doubao_uninstall_full.sh`),禁止手改其数据目录。 diff --git a/.restore-backups/F22_恢复前_20260723_210051/SKILL_REGISTRY.md b/.restore-backups/F22_恢复前_20260723_210051/SKILL_REGISTRY.md index 635849cd..e60890b7 100644 --- a/.restore-backups/F22_恢复前_20260723_210051/SKILL_REGISTRY.md +++ b/.restore-backups/F22_恢复前_20260723_210051/SKILL_REGISTRY.md @@ -89,7 +89,7 @@ | G23 | **宿主挂载执行**(Shell 宿主语义) | 金仓 | **宿主挂载、宿主执行、云上 bash、万推控制台、挂载出错、karo_exec、自动 bash Mac 路径、brew 云端、Docker 宿主 shell、宿主程序** | `01_卡资(金)/金仓_存储备份/宿主挂载执行_SHELL/SKILL.md` | 网站对话与云端 shell ≠ Mac;工作区挂载 + docker-mac-host-shell + 宿主预检三位一体 | | G24 | **深蓝云自动化**(SLCloud · SHENLANYUN) | 金盾 | **深蓝云、深澜云、SHENLANYUN、slcloud、slcloud.org、slcloud-automation、订阅链接、体验套餐、深澜文档、token=、client/subscribe** | `01_卡资(金)/金盾_数据安全/深蓝云_SLCloud自动化/SKILL.md` | 深澜云平台注册/购套餐/取订阅链接;**支付与扫码必须用户接管**;真源自 `slcloud-automation.skill` | | G25 | **UU远程**(网易 · 遥台) | 金仓 | **UU远程、远程安装、远程装Codex、远程装软件、UU终端、远程插件、OFFICE-INNER、DESKTOP-434GEGI** | `01_卡资(金)/金仓_存储备份/UU远程/SKILL.md` | UURemote 本机安装 + **终端零手操**装任意软件/插件;`uu_remote_install_software.sh` / `uu_remote_install_codex.sh` | -| G25b | **Handy中文语音** | 金仓 | **Handy、卡若AI语音、语音唤醒、呼叫卡若AI、⌘1、Ctrl+1、SenseVoice、按住说话、Handy中文** | `01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md` | 双平台 Handy 中文+**语音唤 Agent**(Hub 13458);zip 在 `4、火_工具开发/工具/Handy中文语音/` | +| G25b | **Handy中文语音** | 金仓 | **Handy、卡若AI语音、语音唤醒、呼叫卡若AI、⌘1、Ctrl+1、SenseVoice、按住说话、Handy中文** | `02_卡人(水)/水溪_整理归档/语音输入/SKILL.md` | 双平台 Handy 中文+**语音唤 Agent**(Hub 13458);zip 在 `4、火_工具开发/工具/Handy中文语音/` | | G27 | **Codex账号备份恢复** | 金仓·库政 | **Codex备份、Codex恢复、账号切换恢复、Codex迁移、Codex材料恢复、Codex记录恢复** | `01_卡资(金)/金仓_存储备份/CodexAccountBackupRestore/SKILL.md` | Codex配置、Skill、记忆、会话索引和状态库备份恢复 | | G28 | **Codex对话状态修复** | 金仓·算巡 | **Codex侧栏修复、Codex对话恢复、Codex项目分类、Codex置顶、rollout缺失、thread-store internal error、session_index、state_5.sqlite、公司NAS Mongo备份、跨Codex恢复** | `01_卡资(金)/金仓_存储备份/Codex对话状态修复/SKILL.md` | 修复Codex侧栏与状态库;按项目/工作树保存完整事件链到公司NAS Mongo,并生成跨实例恢复包 | | G29 | **域名调研注册** | 金仓·网卫 | **域名调研、域名注册、域名抢注、老域名、ICP备案、历史备案、WHOIS、RDAP、域名风险** | `01_卡资(金)/金仓_存储备份/域名调研注册/SKILL.md` | 域名候选、历史流量、备案、风险核验和注册执行 | diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/开发文档/功能迭代记录.md b/01_卡资(金)/金仓_存储备份/Handy中文语音/开发文档/功能迭代记录.md deleted file mode 100644 index dd0000b3..00000000 --- a/01_卡资(金)/金仓_存储备份/Handy中文语音/开发文档/功能迭代记录.md +++ /dev/null @@ -1,60 +0,0 @@ -# 功能迭代记录 - -## 2026-07-12|v1.4.3 修复自动输入 - -- 用户真实验证发现:纠错结果进剪贴板,但仍需手动粘贴。 -- 直接核对 Handy v0.8.3 源码 `src-tauri/src/clipboard.rs`:ExternalScript 将文本作为 `$1`,忽略 stdout,并要求脚本自行完成输入。 -- 插件恢复 `$1` 读取,纠错后写剪贴板,等待 350ms 恢复焦点,再自动发送 `⌘V`。 -- `clipboard_handling` 改为 `dont_modify`,避免 Handy 随后用原始转写覆盖纠错后的剪贴板。 -- 新增 `~/Library/Logs/com.pais.handy/karuo_asr_plugin.log`,记录调用、纠错和粘贴失败,不记录正文。 -- 自动输入优先使用本机 `cliclick`,不可用时才回退 System Events;macOS 必须授予对应输入程序辅助功能权限。 - -## 2026-07-12|v1.4.2 自动输入当前对话框 - -- 固化 `clipboard_handling=copy_to_clipboard` 与 `auto_submit=false`。 -- Handy 收到插件 stdout 后自动写入当前焦点对话框,不需要用户手动粘贴。 -- 默认只填入、不自动发送,避免语音误识别直接提交。 - -## 2026-07-12|v1.4.1 修复Handy插件传参协议 - -- 用户真实语音验证发现:Handy日志显示插件被调用,但输入框仍是原文。 -- 根因:旧脚本从参数/剪贴板读取,并自行 ⌘V;Handy 0.8.3 实际从 stdin 传入转写、读取 stdout 作为最终文本。 -- 修复:插件改为纯过滤器 `stdin → 回廊洗字 → stdout`,不再操作剪贴板或模拟按键。 -- 验收标准:管道输入“纯客宝、卡罗亚”时,stdout 必须返回“存客宝、卡若AI”。 - -## 2026-07-12|v1.4 Handy输入阶段ASR纠错 - -### 目标 - -Handy 语音转写后、文字进入当前输入框前,自动调用卡若AI统一ASR纠错库。 - -### 链路 - -```text -Handy录音 - -> SenseVoice zh-Hans转写 - -> handy_macos_asr_paste.sh - -> apply_karuo_voice_corrections.py - -> 卡若闽南口音_ASR纠错库.json - -> 剪贴板 - -> 当前输入框 -``` - -### 修改 - -- Handy `paste_method` 改为 `external_script`。 -- `external_script_path` 指向已验证的 `handy_macos_asr_paste.sh`。 -- 新增 `enable_karuo_asr_correction_macos.sh`,自动备份配置、接线并重启 Handy。 -- 钩子新增 `KARUO_ASR_TEST_ONLY=1` 无粘贴测试模式。 -- 补充 `卡罗AI/卡洛AI/卡路AI` 无空格纠错变体。 - -### 验证 - -- Handy 0.8.3 正常启动,SenseVoice 与 `zh-Hans` 保持不变。 -- Handy 日志确认加载 `ExternalScript` 和目标脚本。 -- 测试输入“打开纯客宝后台,卡罗AI帮我检查”输出“打开存客宝后台,卡若AI帮我检查”。 -- Cursor/Codex 对话层仍保留回廊洗字二次兜底。 - -### 回滚 - -恢复 `settings_store.json.bak.asr.*` 备份并重启 Handy,或把 `paste_method` 改回 `ctrl_v`、`external_script_path` 改回 `null`。 diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/东京服务器_TAT_状态检查与VPN安装.py b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/东京服务器_TAT_状态检查与VPN安装.py new file mode 100644 index 00000000..865530ab --- /dev/null +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/东京服务器_TAT_状态检查与VPN安装.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""TAT 东京服务器: 状态检查 + 一键安装系统级 VPN(IKEv2 + L2TP)""" +import base64, json, os, re, sys, time + + +def _read_creds(): + """从真源私密总表读腾讯云 Lighthouse 凭证(AKID8Q...)""" + d = os.path.dirname(os.path.abspath(__file__)) + for _ in range(6): + if os.path.basename(d) == "卡若AI": + break + d = os.path.dirname(d) + p = os.path.join(d, "运营中枢", "私密配置", "账号密码与API私密总表.md") + if not os.path.isfile(p): + return None, None + with open(p, "r", encoding="utf-8") as f: + t = f.read() + m = re.search(r"API SecretId\s*\|\s*`([^`]+)`", t) + n = re.search(r"API SecretKey\s*\|\s*`([^`]+)`", t) + if m and n: + return m.group(1).strip(), n.group(1).strip() + return None, None + + +def find_tokyo_instance(cli): + from tencentcloud.lighthouse.v20200324 import models + req = models.DescribeInstancesRequest() + r = cli.DescribeInstances(req) + for inst in (r.InstanceSet or []): + if "43.165.178.230" in (inst.PublicAddresses or []): + return inst.InstanceId, inst.InstanceName, inst.InstanceState + return None, None, None + + +def tat_run(client, instance_id, content_b64, name, timeout=180): + from tencentcloud.tat.v20201028 import models + req = models.RunCommandRequest() + req.Content = content_b64 + req.InstanceIds = [instance_id] + req.CommandType = "SHELL" + req.Timeout = timeout + req.CommandName = name + resp = client.RunCommand(req) + return resp.InvocationId + + +def tat_get_result(client, inv_id, wait_sec=40): + from tencentcloud.tat.v20201028 import models + print(f" 等待 {wait_sec}s 取回结果...") + time.sleep(wait_sec) + req = models.DescribeInvocationTasksRequest() + f = models.Filter() + f.Name = "invocation-id" + f.Values = [inv_id] + req.Filters = [f] + r = client.DescribeInvocationTasks(req) + for t in (r.InvocationTaskSet or []): + tr = getattr(t, "TaskResult", None) + if not tr: + continue + try: + jj = json.loads(tr) if isinstance(tr, str) else tr + out = jj.get("Output", "") + if out: + out = base64.b64decode(out).decode("utf-8", errors="replace") + print("--- 服务器输出 ---\n" + out[:8000] + "\n---") + except Exception as e: + print(f" 解析结果失败: {e}\n 原始: {str(tr)[:600]}") + + +def main(): + sid, skey = _read_creds() + if not sid or not skey: + print("❌ 未读到腾讯云凭证") + return 1 + print(f" 凭证: {sid[:8]}...{skey[-6:]}") + + from tencentcloud.common import credential + from tencentcloud.lighthouse.v20200324 import lighthouse_client as ls_cli + from tencentcloud.tat.v20201028 import tat_client as t_cli + + REGION = "ap-tokyo" + cred = credential.Credential(sid, skey) + ls = ls_cli.LighthouseClient(cred, REGION) + iid, iname, istate = find_tokyo_instance(ls) + if not iid: + print("❌ 在 ap-tokyo 区域未找到 43.165.178.230 的实例") + return 1 + print(f" 服务器: {iname} ID={iid} 状态={istate}") + + # ===== 1. 状态检查 ===== + cmd_status = r''' +echo "=== 1. 服务器基础信息 ===" +uname -a +cat /etc/os-release 2>/dev/null | head -3 +echo "" + +echo "=== 2. 资源 ===" +uptime +echo "磁盘:"; df -h / | tail -2 +echo "内存:"; free -h | head -2 +echo "" + +echo "=== 3. 关键服务 ===" +for s in strongswan xl2tpd nginx bt; do + systemctl is-active $s 2>/dev/null || echo "$s: 未安装/未运行" +done +echo "" + +echo "=== 4. 监听端口 ===" +ss -tulnp 2>/dev/null | awk '/LISTEN|UNCONN/ {print $1, $5}' | sort -u | head -30 +echo "" + +echo "=== 5. WireGuard / 进程 ===" +ls /etc/wireguard/ 2>/dev/null || echo "wireguard: 未安装" +wg show 2>/dev/null | head -10 || echo "wg: 未运行" +echo "" + +echo "=== 6. iptables 状态 ===" +iptables -L INPUT -n 2>/dev/null | head -10 +echo "" + +echo "=== 7. 腾讯云外网自检 ===" +curl -s --max-time 5 https://api.ipify.org +echo "" +''' + print("\n[1/2] TAT 状态检查...") + tat = t_cli.TatClient(cred, REGION) + inv = tat_run(tat, iid, base64.b64encode(cmd_status.encode()).decode(), "TokyoStatusCheck", timeout=60) + print(f" 任务下发: {inv}") + tat_get_result(tat, inv, wait_sec=20) + + # ===== 2. 推送并执行 VPN 安装 ===== + script_path = os.path.join(os.path.dirname(__file__), "东京服务器_一键安装IKEv2_L2TP_VPN.sh") + if not os.path.isfile(script_path): + print(f"❌ 找不到脚本: {script_path}") + return 1 + with open(script_path, "r", encoding="utf-8") as f: + script_content = f.read() + print(f"\n[2/2] 推送并执行 VPN 安装脚本 ({len(script_content)} bytes)...") + install_cmd = ( + "cat > /tmp/vpn_install.sh << 'SCRIPT_EOF'\n" + + script_content + + "\nSCRIPT_EOF\n" + "chmod +x /tmp/vpn_install.sh\n" + "echo '脚本已写入 /tmp/vpn_install.sh,开始执行...'\n" + "bash /tmp/vpn_install.sh 2>&1\n" + ) + inv2 = tat_run(tat, iid, base64.b64encode(install_cmd.encode()).decode(), "TokyoInstallVPN", timeout=300) + print(f" 安装任务下发: {inv2}") + tat_get_result(tat, inv2, wait_sec=90) + + print("\n" + "="*60) + print(" ✅ TAT 流程结束") + print("="*60) + print("\n下一步用户在腾讯云控制台操作:") + print(" 轻量应用服务器 → 43.165.178.230 → 防火墙 → 添加规则:") + print(" 500/UDP 4500/UDP 1701/UDP (来源 0.0.0.0/0)") + print("\n然后 macOS 系统设置 → 网络 → 添加 VPN 配置:") + print(" • IKEv2(系统原生,零中间件): 服务器 43.165.178.230 · 远程ID 43.165.178.230") + print(" 账号 karuo · 密码 KaruoVPN@2026") + print(" • L2TP/IPSec(系统原生): 服务器 43.165.178.230 · 共享密钥 KaruoL2TP@2026") + print(" 账号 karuo · 密码 KaruoVPN@2026") + print(" • WireGuard(需装App,最快): 见 运营中枢/私密配置/房市通东京节点/wireguard-client.conf") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/东京服务器_一键安装IKEv2_L2TP_VPN.sh b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/东京服务器_一键安装IKEv2_L2TP_VPN.sh new file mode 100755 index 00000000..2a6e3035 --- /dev/null +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/东京服务器_一键安装IKEv2_L2TP_VPN.sh @@ -0,0 +1,196 @@ +#!/bin/bash +# ============================================================ +# 东京服务器一键安装系统级 VPN(macOS 系统设置可直接用) +# - IKEv2/IPSec(strongSwan)— macOS 推荐 +# - L2TP/IPSec(xl2tpd + strongSwan)— 兼容老设备 +# 服务器:43.165.178.230(腾讯云东京 Lighthouse) +# ============================================================ +set -e +echo "==================================================" +echo " 卡若AI · 东京服务器系统 VPN 一键安装" +echo " 开始时间: $(date '+%F %T')" +echo "==================================================" + +# 1. 防火墙放行(必须) +echo "" +echo ">>> 1. 放行 VPN 端口(腾讯云防火墙在控制台另开)" +iptables -I INPUT -p udp --dport 500 -j ACCEPT 2>/dev/null || true +iptables -I INPUT -p udp --dport 4500 -j ACCEPT 2>/dev/null || true +iptables -I INPUT -p udp --dport 1701 -j ACCEPT 2>/dev/null || true +iptables -I INPUT -p udp --dport 50 -j ACCEPT 2>/dev/null || true +iptables -I INPUT -p udp --dport 51 -j ACCEPT 2>/dev/null || true +ufw allow 500/udp 2>/dev/null || true +ufw allow 4500/udp 2>/dev/null || true +ufw allow 1701/udp 2>/dev/null || true +echo " 端口 500/4500/1701/50/51 已放行" + +# 2. 开启 IP 转发 +echo "" +echo ">>> 2. 开启 IP 转发" +grep -q "net.ipv4.ip_forward=1" /etc/sysctl.conf || echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf +sysctl -w net.ipv4.ip_forward=1 >/dev/null +echo " IP 转发已开启" + +# 3. 安装 strongSwan + xl2tpd +echo "" +echo ">>> 3. 安装 strongSwan / xl2tpd / ppp" +export DEBIAN_FRONTEND=noninteractive +if command -v apt-get >/dev/null 2>&1; then + apt-get update -qq + apt-get install -y -qq strongswan strongswan-pki libstrongswan-extra-plugins xl2tpd ppp +elif command -v yum >/dev/null 2>&1; then + yum install -y epel-release + yum install -y strongswan xl2tpd ppp +fi + +# 4. 生成证书与密钥 +echo "" +echo ">>> 4. 生成 IKEv2 证书与账号" +mkdir -p /etc/ipsec.d/{certs,private,cacerts} +cd /etc/ipsec.d +# CA 私钥 +strongswan pki --gen --type rsa --size 2048 --outform pem > /etc/ipsec.d/private/caKey.pem 2>/dev/null +# CA 证书 +strongswan pki --self --ca --lifetime 3650 \ + --in /etc/ipsec.d/private/caKey.pem \ + --type rsa --dn "C=CN, O=KaruoAI, CN=KaruoAI VPN CA" \ + --outform pem > /etc/ipsec.d/cacerts/caCert.pem 2>/dev/null +# 服务器私钥 +strongswan pki --gen --type rsa --size 2048 --outform pem > /etc/ipsec.d/private/serverKey.pem 2>/dev/null +# 服务器证书 +strongswan pki --pub --in /etc/ipsec.d/private/serverKey.pem --type rsa \ + | strongswan pki --issue --lifetime 3650 --cacert /etc/ipsec.d/cacerts/caCert.pem \ + --cakey /etc/ipsec.d/private/caKey.pem \ + --dn "C=CN, O=KaruoAI, CN=43.165.178.230" \ + --san "43.165.178.230" --flag serverAuth --flag ikeIntermediate \ + --outform pem > /etc/ipsec.d/certs/serverCert.pem 2>/dev/null +echo " 证书已生成" + +# 5. strongSwan 配置(IKEv2) +echo "" +echo ">>> 5. 配置 strongSwan IKEv2" +cat > /etc/ipsec.conf <<'EOF' +config setup + charondebug="ike 1, knl 1, cfg 0" + uniqueids=no + +conn ikev2-vpn + auto=add + compress=no + type=tunnel + keyexchange=ikev2 + fragmentation=yes + forceencaps=yes + dpdaction=clear + dpddelay=300s + rekey=no + left=%any + leftid=43.165.178.230 + leftcert=serverCert.pem + leftsendcert=always + leftsubnet=0.0.0.0/0 + right=%any + rightid=%any + rightauth=eap-mschapv2 + rightsourceip=10.10.10.0/24 + rightdns=8.8.8.8,1.1.1.1 + rightsendcert=never + eap_identity=%identity +EOF + +# 账号密码(PSK + 用户名/密码) +cat > /etc/ipsec.secrets <<'EOF' +: RSA serverKey.pem +karuo : EAP "KaruoVPN@2026" +EOF +chmod 600 /etc/ipsec.secrets + +# 6. xl2tpd 配置(L2TP/IPSec) +echo "" +echo ">>> 6. 配置 L2TP/IPSec" +cat > /etc/xl2tpd/xl2tpd.conf <<'EOF' +[global] +ipsec saref = yes +listen-addr = 43.165.178.230 + +[lns default] +ip range = 10.10.11.10-10.10.11.250 +local ip = 10.10.11.1 +require chap = yes +refuse pap = yes +require authentication = yes +name = l2tpd +pppoptfile = /etc/ppp/options.xl2tpd +length bit = yes +EOF + +cat > /etc/ppp/options.xl2tpd <<'EOF' +ipcp-accept-local +ipcp-accept-remote +ms-dns 8.8.8.8 +ms-dns 1.1.1.1 +noccp +auth +crtscts +idle 1800 +mtu 1410 +mru 1410 +lock +proxyarp +lcp-echo-interval 30 +lcp-echo-failure 4 +EOF + +# L2TP 共享密钥 + 用户 +cat >> /etc/ipsec.secrets <<'EOF' +43.165.178.230 : PSK "KaruoL2TP@2026" +EOF +echo 'karuo l2tpd "KaruoVPN@2026" *' > /etc/ppp/chap-secrets +chmod 600 /etc/ppp/chap-secrets + +# strongSwan 也允许 L2TP +cat >> /etc/ipsec.conf <<'EOF' + +conn l2tp-psk + auto=add + type=transport + keyexchange=ikev1 + authby=secret + left=%any + leftprotoport=17/1701 + right=%any + rightprotoport=17/%any + rekey=no +EOF +chmod 600 /etc/ipsec.secrets + +# 7. 启动服务 +echo "" +echo ">>> 7. 启动 strongSwan / xl2tpd" +systemctl enable strongswan xl2tpd 2>/dev/null || true +systemctl restart strongswan 2>/dev/null || /etc/init.d/strongswan restart 2>/dev/null +systemctl restart xl2tpd 2>/dev/null || /etc/init.d/xl2tpd restart 2>/dev/null + +sleep 2 +echo "" +echo ">>> 8. 状态检查" +ss -ulnp | grep -E ":500|:4500|:1701" || true +ipsec status 2>/dev/null | head -10 || true + +echo "" +echo "==================================================" +echo " 安装完成!" +echo " 服务器:43.165.178.230" +echo "" +echo " ★ IKEv2(macOS 推荐·原生)" +echo " 服务器地址: 43.165.178.230" +echo " 远程 ID: 43.165.178.230" +echo " 账号: karuo" +echo " 密码: KaruoVPN@2026" +echo "" +echo " ★ L2TP/IPSec(备用)" +echo " 服务器地址: 43.165.178.230" +echo " 共享密钥: KaruoL2TP@2026" +echo " 账号: karuo" +echo " 密码: KaruoVPN@2026" +echo "==================================================" diff --git a/01_卡资(金)/金仓_存储备份/脚本/doubao_voice_shortcut_option1.sh b/01_卡资(金)/金仓_存储备份/脚本/doubao_voice_shortcut_option1.sh index 78bb4f36..9cb969b7 100755 --- a/01_卡资(金)/金仓_存储备份/脚本/doubao_voice_shortcut_option1.sh +++ b/01_卡资(金)/金仓_存储备份/脚本/doubao_voice_shortcut_option1.sh @@ -3,7 +3,7 @@ # 原功能:豆包「全局语音输入」固定为 Option+1(macOS 显示 ⌥1 = ALT+1) set -euo pipefail -echo "❌ 豆包已卸载。语音输入请用:01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md" >&2 +echo "❌ 豆包已卸载。语音输入请用:02_卡人(水)/水溪_整理归档/语音输入/SKILL.md" >&2 exit 2 DOUBAO="$HOME/Library/Application Support/Doubao" TARGET="Option+1" diff --git a/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图1.png b/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图1.png new file mode 100644 index 00000000..405d4bbd Binary files /dev/null and b/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图1.png differ diff --git a/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图2.png b/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图2.png new file mode 100644 index 00000000..0e1c6612 Binary files /dev/null and b/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图2.png differ diff --git a/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图3.png b/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图3.png new file mode 100644 index 00000000..ff5facef Binary files /dev/null and b/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图3.png differ diff --git a/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/第238场文章_飞书版.txt b/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/第238场文章_飞书版.txt new file mode 100644 index 00000000..59267756 --- /dev/null +++ b/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/第238场文章_飞书版.txt @@ -0,0 +1,127 @@ +关注派对房号 FM29037620 + +第238场|AI开始替你做事,最先要学会的不是提速,是设边界 + +AI真正开始接手执行之后,最值钱的能力不再是会不会让它干活,而是能不能把权限、数据和成交边界管住。 + +2026年7月24日,约1小时36分。 + +早场有人拿着刚买的软件上麦,说以前提取一个视频文件要手动敲很久,现在几秒钟就能完成。那种高兴很真实——不是因为省下了几分钟,而是忽然看见,原来自己卡了很久的事,也可以有更轻一点的做法。 + +派对里的小伙伴随即聊到收费工具、海外软件和AI能力差异。话题很快从“要不要花钱”变成了另一个更现实的问题: + +工具升级以后,人到底能不能把省下来的时间换成结果? + +再往后,讨论转到了AI权限、数据备份、广告交互、游戏IP和私域运营。 听起来跨度很大,但底层是一件事 + +——以前我们买工具、买流量、买账号; + +现在,真正要为“谁可以执行、执行到哪一步、出了问题怎么恢复”负责。 + +工具贵不贵,不能只看价格 + +那位做文案和视频处理的小伙伴说,原来很多动作都靠手敲,后来装了一个提取工具,几秒钟就能完成过去几分钟的操作。 + +有人问:“那收费软件要不要试?” + +答案很直接:该付费就付费。真正该算的不是软件每月多少钱,而是它帮你省下了多少时间,能不能让你多完成几次交付。 + +便宜工具如果让你反复返工,最后付出的不是月费,而是机会成本。 + +当然,付费也不等于随便买。软件来源、账号权限、数据安全和服务条款都要看清楚。尤其是需要外部网络或第三方账号的工具,应该使用合规渠道和官方支持的方式,不要把“能不能绕过去”当成能力。 + +派对里的小伙伴接了一句:“以前觉得AI只是聊天,现在好像真的能帮忙做事了。” + +这句话说到了变化。前一段时间,大家更多是在让AI写文案、做表格、整理工作流;现在,AI越来越像一个能执行任务的助手。你给它目标、资料和权限,它就会继续往下推进。 + +但权限一旦放出去,风险也跟着出去。企业的电脑、知识库、账号、客户资料和自动化任务,不能因为“AI很聪明”就全部交给它。 + +真正成熟的用法是:让AI做清楚可回滚的事情,把关键操作设成确认点,重要文件按小时或按周期备份,出了问题能够恢复。这样才是效率,不是把所有按钮都交出去以后祈祷它别出错。 + +AI的下一阶段,是安全边界和真实成交 + +AI变化已经快到以周为单位。以前大家比的是谁的模型更强、谁的算力更多; + +现在越来越多的企业开始关心,AI能不能在业务流程里稳定执行,能不能完成一笔真实成交,能不能不越权、不乱改、不把客户数据带错地方。 + +这也是安全边界的重要性。 + +AI可以帮你分析客户、整理资料、生成内容、跟进任务,但哪些信息可以读取,哪些动作需要人工确认,哪些事情绝对不能碰,必须提前写清楚。 + +不要把“自动化”理解成“完全不用管”。 + +自动化真正成熟的样子,是把人从重复动作里解放出来,把人留在判断、授权和责任的位置上。 + +这一点放到广告里也很明显。传统信息流广告是刷到、点开、购买; 以后更像是一个虚拟角色主动和你沟通,根据你的兴趣介绍产品。 ta可能更像销售,也更容易让人放下戒心。 + +这对平台和商家都是提醒:人设可以帮助沟通,但不能用来伪装身份、诱导消费; + +AI可以提高触达效率,但不能替代真实的产品和售后。流量越像真人,规则越要写在前面。 + +数据也一样。企业把资料放进知识库,把任务交给AI之前,至少要知道谁能看、谁能改、多久备份一次、异常时怎么恢复。能跑起来只是第一关,能安全地跑一年,才叫产品。 + +游戏IP的价值,不在违规捷径 + +自己很早就接触游戏行业,后来把游戏主播、粉丝、周边产品和社群运营串起来,尝试把游戏影响力转成长期客户关系。 + +这件事的价值,不在于某一个游戏,而在于游戏IP本身可以承载什么:内容、粉丝、周边、陪练、赛事、品牌合作,甚至是围绕玩家需求建立的正规服务。 + +派对里的小伙伴问,为什么不直接做游戏俱乐部? + +答案是,俱乐部和IP运营不是一回事。俱乐部负责培养和管理选手,IP运营更关心粉丝沉淀、内容持续和商业合作。两者可以合作,但边界、分工和收入来源要提前说清楚。 + +有人提到账号抵押、游戏资产和典当。这里的边界更不能模糊: 涉及金融、典当、账号交易或数字资产,必须有合法主体、合规牌照、明确估值和税务处理. 没有资质就不要把高风险业务包装成“副业机会”。 + +能赚钱的项目,不等于什么都能碰;长期做生意,先把牌照、规则和责任放在桌面上。 + +真正稳的游戏私域,应该是围绕合法内容和服务做沉淀:主播直播,团队负责运营,粉丝进入明确的服务流程,产品和合作方都能核验。 + +客户不是一串可以随意搬运的数字,粉丝也不是拿来反复骚扰的流量。 + +先看同行能不能做,再看自己能不能扛 + +有人问,判断一个项目值不值得做,是不是看同行已经赚到钱就行? + +这只能算第一步。同行赚钱,说明市场可能存在; + +但你还要问三个问题: + +自己有没有相同的资源; + +能不能承受前期成本; + +愿不愿意长期做交付。 + +一个做直播带货的人讲到,直播间、设备、办公和人员,每个月都要支出。真正开始之前,他已经知道自己要承担什么,而不是只看到别人晒出来的收入。 + +过去拉新很热闹,现在更重要的是用户资产数字化: 一个用户一年能产生多少价值,服务成本是多少,复购和转化如何记录。 数据只有被长期跟踪,才有资格拿去谈合作或融资。 + +这和“先做小、再放大”是一个逻辑。不要一上来就投很大的钱,也不要为了证明自己敢干,先签下无法承受的成本。用较小的投入跑几轮,看客户是否愿意留下,再决定要不要加人、加设备、加渠道。 + +派对里的小伙伴说:“我就是不知道AI到底怎么挣钱。” + +我接得很简单:AI本身只是工具,挣钱的地方在于它帮你降低了什么成本,提升了什么人效,或者让哪一种服务可以被稳定交付。 + +比如,一个有几万客户的私域团队,不可能每天手动看完每个人的朋友圈、聊天阶段和生日。 AI可以帮助整理客户行为、提醒回访、分配意向,但最后的沟通和成交仍然需要人判断。 + +这不是用AI替代所有人,而是让有限的人服务更多真正有价值的客户。客户越多,越不能只靠记忆;数据越多,越要把人留在关键判断上。 + +规则先于速度,稳定才是最便宜的增长 + +这一场有一个很清楚的变化:机会没有消失,只是门槛从“会不会操作”变成了“能不能稳定交付”。 + +软件要正规,数据要保护,AI要设权限,游戏项目要守边界,私域要有客户价值,合作要把责任讲清楚。速度当然重要,但没有规则的速度,往往只是更快撞上问题。 + +做项目的人也不需要一下子把未来十年想明白。两三个星期看一次变化,重新检查自己的工具、流程和客户; + +能优化就优化,不能成立的就停下来。别把预测未来当成逃避今天交付的理由。 + +最后留下三个动作: + +第一,把正在使用的AI工具列出来,标清楚它能读什么、能改什么、谁能授权。 + +第二,把自己的客户和粉丝分成“触达、服务、成交、复购”几个阶段,别只盯着人数。 + +第三,选一个小项目做一轮真实交付,用成本和结果判断能不能继续,不用想象替自己赚钱。 + +看得懂机会只是开始。能把机会做成产品,把产品做成服务,把服务做得安全、稳定、可复用,才是真正的能力。#派对会员 想继续跟上AI和商业变化,来派对里听真实案例,有问题直接 @我。 diff --git a/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/第238场|AI开始替你做事,最先要学会的不是提速,是设边界.md b/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/第238场|AI开始替你做事,最先要学会的不是提速,是设边界.md new file mode 100644 index 00000000..3864277a --- /dev/null +++ b/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/第238场|AI开始替你做事,最先要学会的不是提速,是设边界.md @@ -0,0 +1,3 @@ +# 第238场|AI开始替你做事,最先要学会的不是提速,是设边界 + +
AI真正开始接手执行之后,最值钱的能力不再是会不会让它干活,而是能不能把权限、数据和成交边界管住。
2026年7月24日,约1小时36分。
早场有人拿着刚买的软件上麦,说以前提取一个视频文件要手动敲很久,现在几秒钟就能完成。那种高兴很真实——不是因为省下了几分钟,而是忽然看见,原来自己卡了很久的事,也可以有更轻一点的做法。
派对里的小伙伴随即聊到收费工具、海外软件和AI能力差异。话题很快从“要不要花钱”变成了另一个更现实的问题:
工具升级以后,人到底能不能把省下来的时间换成结果?
再往后,讨论转到了AI权限、数据备份、广告交互、游戏IP和私域运营。 听起来跨度很大,但底层是一件事
——以前我们买工具、买流量、买账号;
现在,真正要为“谁可以执行、执行到哪一步、出了问题怎么恢复”负责。

那位做文案和视频处理的小伙伴说,原来很多动作都靠手敲,后来装了一个提取工具,几秒钟就能完成过去几分钟的操作。
有人问:“那收费软件要不要试?”
答案很直接:该付费就付费。真正该算的不是软件每月多少钱,而是它帮你省下了多少时间,能不能让你多完成几次交付。
便宜工具如果让你反复返工,最后付出的不是月费,而是机会成本。
当然,付费也不等于随便买。软件来源、账号权限、数据安全和服务条款都要看清楚。尤其是需要外部网络或第三方账号的工具,应该使用合规渠道和官方支持的方式,不要把“能不能绕过去”当成能力。
派对里的小伙伴接了一句:“以前觉得AI只是聊天,现在好像真的能帮忙做事了。”
这句话说到了变化。前一段时间,大家更多是在让AI写文案、做表格、整理工作流;现在,AI越来越像一个能执行任务的助手。你给它目标、资料和权限,它就会继续往下推进。
但权限一旦放出去,风险也跟着出去。企业的电脑、知识库、账号、客户资料和自动化任务,不能因为“AI很聪明”就全部交给它。
真正成熟的用法是:让AI做清楚可回滚的事情,把关键操作设成确认点,重要文件按小时或按周期备份,出了问题能够恢复。这样才是效率,不是把所有按钮都交出去以后祈祷它别出错。
AI变化已经快到以周为单位。以前大家比的是谁的模型更强、谁的算力更多;
现在越来越多的企业开始关心,AI能不能在业务流程里稳定执行,能不能完成一笔真实成交,能不能不越权、不乱改、不把客户数据带错地方。
这也是安全边界的重要性。
AI可以帮你分析客户、整理资料、生成内容、跟进任务,但哪些信息可以读取,哪些动作需要人工确认,哪些事情绝对不能碰,必须提前写清楚。
不要把“自动化”理解成“完全不用管”。
自动化真正成熟的样子,是把人从重复动作里解放出来,把人留在判断、授权和责任的位置上。
这一点放到广告里也很明显。传统信息流广告是刷到、点开、购买; 以后更像是一个虚拟角色主动和你沟通,根据你的兴趣介绍产品。 ta可能更像销售,也更容易让人放下戒心。
这对平台和商家都是提醒:人设可以帮助沟通,但不能用来伪装身份、诱导消费;
AI可以提高触达效率,但不能替代真实的产品和售后。流量越像真人,规则越要写在前面。
数据也一样。企业把资料放进知识库,把任务交给AI之前,至少要知道谁能看、谁能改、多久备份一次、异常时怎么恢复。能跑起来只是第一关,能安全地跑一年,才叫产品。
自己很早就接触游戏行业,后来把游戏主播、粉丝、周边产品和社群运营串起来,尝试把游戏影响力转成长期客户关系。
这件事的价值,不在于某一个游戏,而在于游戏IP本身可以承载什么:内容、粉丝、周边、陪练、赛事、品牌合作,甚至是围绕玩家需求建立的正规服务。
派对里的小伙伴问,为什么不直接做游戏俱乐部?
答案是,俱乐部和IP运营不是一回事。俱乐部负责培养和管理选手,IP运营更关心粉丝沉淀、内容持续和商业合作。两者可以合作,但边界、分工和收入来源要提前说清楚。
有人提到账号抵押、游戏资产和典当。这里的边界更不能模糊: 涉及金融、典当、账号交易或数字资产,必须有合法主体、合规牌照、明确估值和税务处理. 没有资质就不要把高风险业务包装成“副业机会”。
能赚钱的项目,不等于什么都能碰;长期做生意,先把牌照、规则和责任放在桌面上。
真正稳的游戏私域,应该是围绕合法内容和服务做沉淀:主播直播,团队负责运营,粉丝进入明确的服务流程,产品和合作方都能核验。
客户不是一串可以随意搬运的数字,粉丝也不是拿来反复骚扰的流量。

有人问,判断一个项目值不值得做,是不是看同行已经赚到钱就行?
这只能算第一步。同行赚钱,说明市场可能存在;
但你还要问三个问题:
自己有没有相同的资源;
能不能承受前期成本;
愿不愿意长期做交付。
一个做直播带货的人讲到,直播间、设备、办公和人员,每个月都要支出。真正开始之前,他已经知道自己要承担什么,而不是只看到别人晒出来的收入。
过去拉新很热闹,现在更重要的是用户资产数字化: 一个用户一年能产生多少价值,服务成本是多少,复购和转化如何记录。 数据只有被长期跟踪,才有资格拿去谈合作或融资。
这和“先做小、再放大”是一个逻辑。不要一上来就投很大的钱,也不要为了证明自己敢干,先签下无法承受的成本。用较小的投入跑几轮,看客户是否愿意留下,再决定要不要加人、加设备、加渠道。
派对里的小伙伴说:“我就是不知道AI到底怎么挣钱。”
我接得很简单:AI本身只是工具,挣钱的地方在于它帮你降低了什么成本,提升了什么人效,或者让哪一种服务可以被稳定交付。
比如,一个有几万客户的私域团队,不可能每天手动看完每个人的朋友圈、聊天阶段和生日。 AI可以帮助整理客户行为、提醒回访、分配意向,但最后的沟通和成交仍然需要人判断。
这不是用AI替代所有人,而是让有限的人服务更多真正有价值的客户。客户越多,越不能只靠记忆;数据越多,越要把人留在关键判断上。

这一场有一个很清楚的变化:机会没有消失,只是门槛从“会不会操作”变成了“能不能稳定交付”。
软件要正规,数据要保护,AI要设权限,游戏项目要守边界,私域要有客户价值,合作要把责任讲清楚。速度当然重要,但没有规则的速度,往往只是更快撞上问题。
做项目的人也不需要一下子把未来十年想明白。两三个星期看一次变化,重新检查自己的工具、流程和客户;
能优化就优化,不能成立的就停下来。别把预测未来当成逃避今天交付的理由。
最后留下三个动作:
第一,把正在使用的AI工具列出来,标清楚它能读什么、能改什么、谁能授权。
第二,把自己的客户和粉丝分成“触达、服务、成交、复购”几个阶段,别只盯着人数。
第三,选一个小项目做一轮真实交付,用成本和结果判断能不能继续,不用想象替自己赚钱。
看得懂机会只是开始。能把机会做成产品,把产品做成服务,把服务做得安全、稳定、可复用,才是真正的能力。#派对会员 想继续跟上AI和商业变化,来派对里听真实案例,有问题直接 @我。
diff --git a/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/state.json b/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/state.json index a8b9416e..fd244a3f 100644 --- a/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/state.json +++ b/02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/state.json @@ -1,6 +1,6 @@ { - "last_sent_field": 237, - "chapter_id": "10.96", - "title": "第237场|洛杉矶做数据中心的人,回到教育AI先算清成本", - "sent_at": "2026-07-23T12:01:35" + "last_sent_field": 238, + "chapter_id": "10.97", + "title": "第238场|AI开始替你做事,最先要学会的不是提速,是设边界", + "sent_at": "2026-07-25T06:23:25" } diff --git a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/feishu_wiki_download_images.py b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/feishu_wiki_download_images.py index 28a010ac..84218fee 100644 --- a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/feishu_wiki_download_images.py +++ b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/feishu_wiki_download_images.py @@ -27,6 +27,7 @@ from urllib.parse import urlparse from datetime import datetime import requests +from typing import Optional SCRIPT_DIR = Path(__file__).resolve().parent CONFIG = { @@ -77,7 +78,7 @@ def refresh_user_token(tokens): return None -def get_token(node_token: str) -> str | None: +def get_token(node_token: str) -> Optional[str]: """优先 FEISHU_TOKEN,否则用 .feishu_tokens.json 的 user access_token(必要时刷新)。""" env_token = os.environ.get("FEISHU_TOKEN", "").strip() if env_token: @@ -111,14 +112,14 @@ def get_token(node_token: str) -> str | None: return None -def wiki_url_to_node_token(url: str) -> str | None: +def wiki_url_to_node_token(url: str) -> Optional[str]: """从 Wiki 页面 URL 解析 node_token。""" # .../wiki/NAMUwdbuFiAy2GkmMZ3clXynnwc 或 .../wiki/xxx?... m = re.search(r"/wiki/([A-Za-z0-9_-]+)", url) return m.group(1) if m else None -def get_wiki_node(token: str, node_token: str) -> dict | None: +def get_wiki_node(token: str, node_token: str) -> Optional[dict]: """获取 wiki 节点信息,返回 node 字典(含 obj_token, obj_type)。""" r = requests.get( f"https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node?token={node_token}", @@ -165,7 +166,7 @@ def get_wiki_child_nodes(token: str, space_id: str, parent_node_token: str) -> l return out -def collect_file_tokens_from_sheet(token: str, spreadsheet_token: str) -> list[str]: +def collect_file_tokens_from_sheet(token: str, spreadsheet_token: str) -> list: """从电子表格中收集浮动图片的 file_token / float_image_token。""" headers = {"Authorization": f"Bearer {token}"} file_tokens = [] @@ -247,7 +248,7 @@ def _extract_file_tokens(obj, out: list): _extract_file_tokens(x, out) -def collect_file_tokens_from_docx(token: str, document_id: str) -> list[str]: +def collect_file_tokens_from_docx(token: str, document_id: str) -> list: """从 docx 文档拉取全部 blocks,收集图片/文件的 file_token。""" headers = {"Authorization": f"Bearer {token}"} file_tokens = [] @@ -332,7 +333,7 @@ def download_file(url: str, save_path: Path, token: str) -> bool: return ret == 0 -def _collect_from_node(token: str, obj_token: str, obj_type: str) -> list[str]: +def _collect_from_node(token: str, obj_token: str, obj_type: str) -> list: """从单个节点(docx/sheet)收集 file_tokens。""" if obj_type == "sheet": return collect_file_tokens_from_sheet(token, obj_token) diff --git a/02_卡人(水)/水溪_整理归档/语音转写纠错/SKILL.md b/02_卡人(水)/水溪_整理归档/语音转写纠错/SKILL.md index 3000b1fd..11791314 100644 --- a/02_卡人(水)/水溪_整理归档/语音转写纠错/SKILL.md +++ b/02_卡人(水)/水溪_整理归档/语音转写纠错/SKILL.md @@ -5,10 +5,10 @@ description: 卡若记忆宫殿命名体系 · 间名「回廊洗字」:口述 group: 水 memory_palace_path: 卡若记忆宫殿/水殿/水溪厢/回廊洗字 memory_palace_slot: 口述与 ASR 杂声在此洗净,再入记忆与字幕流水线 -triggers: **回廊洗字、卡若记忆宫殿命名体系、记忆宫殿、记忆空间、语音转写纠错、语音输入、闽南话、闽南口音、听写、ASR、转写纠错、纠错库、误听、卡罗拉、卡罗伊**、口述、嘴瓢、**网页CLI、终端浏览器、命令行看网页、browsh** +triggers: **回廊洗字、卡若记忆宫殿命名体系、记忆宫殿、记忆空间、语音转写纠错、闽南话、闽南口音、听写、ASR、转写纠错、纠错库、误听、卡罗拉、卡罗伊**、口述、嘴瓢、**网页CLI、终端浏览器、命令行看网页、browsh** owner: 水溪 -version: "1.7.0" -updated: "2026-07-18" +version: "1.8.0" +updated: "2026-07-24" --- # 回廊洗字(W03b · 卡若记忆宫殿命名体系) @@ -34,7 +34,7 @@ updated: "2026-07-18" 1. **在推理与执行前**,将用户本轮自然语言视为可能含 ASR 噪声;在心中或用下述脚本对**整段或关键片段**做一次纠正后再定意图(不必改用户原文展示,**内部理解**以纠正后为准)。 2. **统一过滤**:不仅「卡路 / 卡罗 / 卡洛」类,**Skill / Codex / GitHub / Firecrawl / Handy / Webhook / MongoDB / ASR / Cursor / Claude / Soul / 私域 / 存客宝 / 流量池** 等表中近音一并处理;以 `卡若闽南口音_ASR纠错库.json` 为**唯一**词条源。 -3. **产品用词(存客宝/触客宝)**:口述或 ASR 写出 **「纯客宝」「纯课宝」** 时,一律理解为 **「存客宝」**;写出 **「流量词」** 时,一律理解为 **「流量池」**(旧称已废弃;技术字段 `poolKeywords` 不变)。 +3. **产品用词(存客宝/触客宝)**:口述或 ASR 写出 **「纯客宝」「纯课宝」** 时,一律理解为 **「存客宝」**;写出 **「流量词」** 时,一律理解为 **「流量池」**(旧称已废弃;技术字段 `poolKeywords` 不变)。 **该归一必须在每轮会话的理解、路由、工具参数整理与新增文档写入前完成;历史原文保持原样。** 4. 替换顺序:**按 key 长度降序**全文替换,避免短词截断长词(与 `soul_enhance.py`、`apply_karuo_voice_corrections.py` 一致)。 5. **上下文**:若替换后与句义冲突(食物热量、受伤、西游记原著等),以语义为准,见 JSON `notes` 与下文「终端网页 CLI」段。 6. 专有名词:Cursor、Claude、Soul、卡若AI、**流量池**(非「流量词」)等按表中写法对齐。 @@ -54,7 +54,7 @@ echo "卡罗拉更新 skill" | python3 ".../apply_karuo_voice_corrections.py" | 项 | 路径 / 值 | |:---|:---| -| **Skill 真源** | `01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md`(**G25b**) | +| **Skill 真源** | `02_卡人(水)/水溪_整理归档/语音输入/SKILL.md`(**W03h**) | | 一键总装 | `…/Handy中文语音/脚本/install_all.sh` | | macOS 写配置 | `…/脚本/handy_apply_chinese_macos.sh` | | 粘贴 | `paste_method=ctrl_v`(剪贴板 Cmd+V) | diff --git a/02_卡人(水)/水溪_整理归档/语音转写纠错/参考资料/近期对话ASR纠错清单_20260724.md b/02_卡人(水)/水溪_整理归档/语音转写纠错/参考资料/近期对话ASR纠错清单_20260724.md new file mode 100644 index 00000000..a99f9560 --- /dev/null +++ b/02_卡人(水)/水溪_整理归档/语音转写纠错/参考资料/近期对话ASR纠错清单_20260724.md @@ -0,0 +1,13 @@ +# 近期对话 ASR 纠错清单(2026-07-24) + +## 本轮加固 + +| 误听/转写 | 正写 | 固化位置 | 验收 | +|---|---|---|---| +| 纯客宝、纯课宝 | 存客宝 | 唯一 JSON 词库、W03b、Codex 入口 | 命令行回归通过 | + +## 执行约束 + +- 每轮用户输入在意图理解、Skill 路由、工具参数整理与持久文档写入前执行一次内部 ASR 滤真。 +- 历史聊天原文不重写;新增文档与执行产物统一使用“存客宝”。 +- 词条按长度降序替换;词库仍是唯一真源。 diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md b/02_卡人(水)/水溪_整理归档/语音输入/SKILL.md similarity index 79% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md rename to 02_卡人(水)/水溪_整理归档/语音输入/SKILL.md index aa483162..2bf92e81 100644 --- a/01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md +++ b/02_卡人(水)/水溪_整理归档/语音输入/SKILL.md @@ -1,13 +1,13 @@ --- -name: Handy中文语音 -description: macOS/Windows Handy 中文语音+语音唤醒卡若AI Agent(⌘1/Ctrl+1)。触发词:Handy、卡若AI语音、语音唤醒、呼叫卡若AI、全局语音、⌘1、Ctrl+1、SenseVoice、按住说话。 -group: 金 -memory_palace_path: 卡若记忆宫殿/金殿/金仓厢/听写台 -memory_palace_slot: 本机按住说话,中文落字;纠错在 Cursor 回廊洗字 -triggers: Handy、全局语音、卡若AI语音、语音唤醒、呼叫卡若AI、喊卡若AI、Windows Handy、⌘1、Ctrl+1、语音输入、SenseVoice、按住说话、Handy中文、handy_apply、语音Agent -owner: 金仓 -version: "1.4" -updated: "2026-07-12" +name: 语音输入 +description: 卡水·水溪语音输入:Handy 中文转写、日常输入、配置基线、验收与排障。触发词:语音输入、Handy、卡若AI语音、Fn语音、SenseVoice。 +group: 水 +memory_palace_path: 卡若记忆宫殿/水殿/水溪厢/语音输入台 +memory_palace_slot: 本机语音转写、输入与配置基线;纠错交由回廊洗字 +triggers: 语音输入、Handy、卡若AI语音、Fn语音、Command+1、结合屏幕、免按住说话、SenseVoice、中文听写、语音唤醒、语音配置、Handy中文、语音Agent +owner: 水溪 +version: "1.5" +updated: "2026-07-26" related: - 01_卡资(金)/金仓_存储备份/Cursor持久对话/SKILL.md - 02_卡人(水)/水溪_整理归档/语音转写纠错/SKILL.md @@ -15,9 +15,9 @@ related: - 运营中枢/参考资料/卡若闽南口音_ASR纠错库.json --- -# Handy中文语音(G25b · 金仓) +# 语音输入(W03h · 水溪) -> **一句话**:**macOS**(Apple Silicon · ⌘1 · CoreML)与 **Windows**(x64/ARM64 · Ctrl+1 · DirectML)用 **Handy 0.8.3** + **SenseVoice** 按住说话→**先回廊洗字再自动输入**(如 纯客宝→存客宝);五笔输入法不变。 +> **一句话**:**macOS** 当前使用 **Fn 开始录音、再按 Fn 结束录音**,无需按住;Handy 0.8.3 + SenseVoice 输出中文并自动填入当前输入框。ASR 纠错由 W03b「回廊洗字」在对话理解层统一处理。 ## 适用场景 @@ -30,10 +30,17 @@ related: | **语音唤 Agent** | 说「**卡若AI** + 任务」或「**火炬/金仓** + 任务」→ pipeline 静默 Hub / 官网(见下) | | **语音 · 需求整理** | 先说 **小乖/乖乖/小宝**,再说「整理需求 …」→ 直投 Hub **语音·需求整理**(**不粘贴**输入框;见 `setup_handy_voice_exec_wake.sh`) | + +## 结合屏幕(Command+1) + +- **Fn**:普通输入,保持现有“按一次开始、再按一次结束”。 +- **Command+1**:截取一张当前主屏后开始/结束语音;转写完成会填入截图路径与洗字后的上下文指令。 +- 使用、权限、验收、回滚见:[结合屏幕本地部署与使用.md](references/结合屏幕本地部署与使用.md)。 + ## 一键安装(Agent / 终端) ```bash -bash "/Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/install_all.sh" +bash "/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/脚本/install_all.sh" ``` **可分发安装包**(`4、火_工具开发/工具/Handy中文语音/`): @@ -45,7 +52,7 @@ bash "/Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备 打包(Mac 上执行,产出双平台 zip):`bash …/Handy中文语音/脚本/build_package.sh` -## 语音唤醒 · 卡若AI Agent 插件(G25b+) +## 语音唤醒 · 卡若AI Agent 插件(W03h+) 口述 **「卡若AI …」** 或 **成员名 + 任务**(如「火炬 查 Agent 接口」)→ **不粘贴**,改为: @@ -116,8 +123,8 @@ pbpaste | python3 "/Users/karuo/Documents/个人/卡若AI/运营中枢/工作台 |:---|:---|:---| | **模型** | 已下载 → **SenseVoice** 卡片 | **活跃** | | **通用** | **SenseVoice 设置 → 语言** | **简体中文**(勿「自动」) | -| **通用** | **转录快捷键** | **⌘1**(点框后按 ⌘+1) | -| **通用** | **按住说话** | **开** | +| **通用** | **转录快捷键** | **Fn**(按一次开始、再按一次结束) | +| **通用** | **按住说话** | **关**(Fn 切换录音) | | **高级 → 应用** | **开机启动** | **关**(防双实例冲配置) | | **高级 → 输出** | **粘贴方式** | **剪贴板 (Cmd+V)**(勿外部脚本) | | **高级 → 应用** | **实验性功能** | **开** → **实验性 → ONNX 加速** | **CoreML** | @@ -132,9 +139,9 @@ pbpaste | python3 "/Users/karuo/Documents/个人/卡若AI/运营中枢/工作台 | `selected_language` | `zh-Hans` | | `paste_method` | `ctrl_v` | | `external_script_path` | `null` | -| `bindings.transcribe.current_binding` | `command_left+1` | -| `push_to_talk` | `true` | -| `autostart_enabled` | `false` | +| `bindings.transcribe.current_binding` | `fn` | +| `push_to_talk` | `false` | +| `autostart_enabled` | `true` | | `ort_accelerator` | `coreml` | 模型目录:`~/Library/Application Support/com.pais.handy/models/sense-voice-int8/` @@ -152,14 +159,14 @@ pbpaste | python3 "/Users/karuo/Documents/个人/卡若AI/运营中枢/工作台 ## 验收 1. `tail -30 ~/Library/Logs/com.pais.handy/handy.log` 含 `zh-Hans`、`sense-voice-int8` -2. 记事本按住 **⌘1** 说「卡若语音输入测试」→ 松开 → 粘贴为**中文** +2. 记事本按 **Fn** 开始说「卡若语音输入测试」→ 再按 **Fn** 结束 → 自动填入中文 3. 五笔输入法未被 Handy 修改 ## 排障速查 | 现象 | 处理 | |:---|:---| -| 英文乱码 / yu pizza yin | 语言被改回 **auto** → UI 改简体中文 + 重跑 `handy_apply_chinese_macos.sh` | +| 英文乱码 / yu pizza yin | 语言被改回 **auto** → UI 改简体中文;修改配置前先备份并确认 | | 转录为空 | 多为 **x86 Handy** → 重装 `handy_install_arm64.sh` | | 配置写了又变 | `killall -9 handy`(进程名小写 **handy**);关开机启动;勿开外部脚本 | | 两个 Handy | 活动监视器只留一个;`autostart_enabled=false` | @@ -171,7 +178,7 @@ pbpaste | python3 "/Users/karuo/Documents/个人/卡若AI/运营中枢/工作台 - **禁止** `brew install --cask handy`(易 x86) - macOS 仅允许使用已验证的 `handy_macos_asr_paste.sh`;Handy 0.8.3 ExternalScript 只传 `$1` 且忽略 stdout,插件必须自行输入 -## 2026-07-12 输入阶段纠错 +## 历史方案:2026-07-12 输入阶段纠错 Handy 已可在文字进入当前输入框前调用卡若AI统一词库: diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/evolution/gotchas.md b/02_卡人(水)/水溪_整理归档/语音输入/evolution/gotchas.md similarity index 100% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/evolution/gotchas.md rename to 02_卡人(水)/水溪_整理归档/语音输入/evolution/gotchas.md diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/references/Handy设置界面速查.md b/02_卡人(水)/水溪_整理归档/语音输入/references/Handy设置界面速查.md similarity index 100% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/references/Handy设置界面速查.md rename to 02_卡人(水)/水溪_整理归档/语音输入/references/Handy设置界面速查.md diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/references/Handy设置界面速查_Windows.md b/02_卡人(水)/水溪_整理归档/语音输入/references/Handy设置界面速查_Windows.md similarity index 100% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/references/Handy设置界面速查_Windows.md rename to 02_卡人(水)/水溪_整理归档/语音输入/references/Handy设置界面速查_Windows.md diff --git a/02_卡人(水)/水溪_整理归档/语音输入/references/Handy语音输入_KI_本机实测_20260726.md b/02_卡人(水)/水溪_整理归档/语音输入/references/Handy语音输入_KI_本机实测_20260726.md new file mode 100644 index 00000000..f1d3afce --- /dev/null +++ b/02_卡人(水)/水溪_整理归档/语音输入/references/Handy语音输入_KI_本机实测_20260726.md @@ -0,0 +1,89 @@ +# Handy 语音输入 KI|本机实测基线(2026-07-26) + +> 归属:金仓 · G25b「Handy中文语音」 +> 目标:让卡若AI在电脑上稳定获得“说话 → 中文转写 → 回廊洗字 → 填入当前对话框”的能力。 +> 词库唯一真源:[卡若闽南口音_ASR纠错库.json](../../../../运营中枢/参考资料/卡若闽南口音_ASR纠错库.json) + +## 1. 当前可用结论 + +| 项目 | 本机实测结果 | +|---|---| +| 应用 | `/Applications/Handy.app`,v0.8.3,Apple Silicon 版本 | +| 输入设备 | MacBook Pro 麦克风,44.1kHz、单声道 | +| 识别模型 | `sense-voice-int8`,中文 `zh-Hans` | +| 操作 | 按 `Fn` 开始录音,再按 `Fn` 结束;转写后自动填进当前焦点输入框 | +| 发送策略 | `auto_submit=false`:只填入,不自动发送;无需持续按住热键 | +| 识别速度 | 最近实测 90–471ms(不含录音时长) | +| 文本纠正 | 词库回归 3/3 通过;当前输入保留原有 Handy 行为,对话层继续进行语义级回廊洗字 | +| 语音Hub | `http://127.0.0.1:13458/api/health` 返回 `ok=true` | + +## 2. 当前固定链路 + +```text +按 Fn 开始录音 → 再按 Fn 结束录音 + → Handy 0.8.3 / SenseVoice(zh-Hans) + → 剪贴板(copy_to_clipboard) + → Handy Cmd+V 填入当前输入框 + → Codex/卡若AI 对话层进行语义级回廊洗字兜底 +``` + +## 3. 本机配置基线 + +配置文件:`~/Library/Application Support/com.pais.handy/settings_store.json` 的 `settings`。 + +| 键 | 目标值 | 2026-07-26 回读 | +|---|---|---| +| `selected_language` | `zh-Hans` | 通过 | +| `selected_model` | `sense-voice-int8` | 通过 | +| `bindings.transcribe.current_binding` | `fn` | 通过 | +| `push_to_talk` | `false`(无需按住) | 通过 | +| `paste_method` | `ctrl_v` | 通过 | +| `clipboard_handling` | `copy_to_clipboard` | 通过 | +| `auto_submit` | `false` | 通过 | +| `autostart_enabled` | `true` | 通过 | +| `ort_accelerator` | `auto` | 通过 | + +> 当前按用户确认的原配置运行:`external_script_path` 保持原路径,剪贴板守护不启用。 + +## 4. 已发现问题 → 根因 → 固化处理 + +| 现象 | 根因 | 固化处理 | +|---|---|---| +| 口述“卡路AI / 卡罗AI / hand of”等误听 | ASR 固有近音 | 保留原始 `Fn` 输入方式;由卡若AI对话层回廊洗字理解与词库迭代处理 | +| 中文变英文或乱码 | `selected_language=auto` | 固定 `zh-Hans`,必要时在 Handy 设置页确认“简体中文” | +| 转写为空 | 误装 x86 版 Handy | 仅使用 Apple Silicon / aarch64 安装包 | +| 配置反复变化 | 双实例或手工脚本覆盖 | 保持用户确认的 `autostart_enabled=true`;修改前先备份并得到确认 | +| 纠错后还要手动粘贴 | 输入焦点变化 | 保持 Handy 原有 CtrlV 自动填入;不额外安装洗字守护 | +| CoreML 未实际启用 | v0.8.3 启动后把该项规范化为 `auto` | 当前不升级;若速度异常,再在隔离副本验证 v0.9.4 | + +完整历史见 [gotchas.md](../evolution/gotchas.md)。 + +## 5. 日常使用 + +1. 把光标放到 Codex、网页、备忘录等输入框。 +2. **按 `Fn` 开始说话,再按 `Fn` 结束。** +3. 检查洗后的文本后,再手动发送。 +4. 发现新误听:只向唯一 JSON 词库追加“误听→正写”,再跑回归测试。 + +## 6. 快速验收与诊断 + +```bash +# 仅查看当前配置(不修改) +python3 -c 'import json,pathlib; print(json.loads((pathlib.Path.home()/"Library/Application Support/com.pais.handy/settings_store.json").read_text())["settings"]["bindings"]["transcribe"]["current_binding"])' + +# 词库回归 +python3 "/Users/karuo/Documents/个人/卡若AI/运营中枢/工作台/脚本/test_karuo_asr_corrections.py" + +# 日志 +tail -60 "$HOME/Library/Logs/com.pais.handy/handy.log" +tail -60 "$HOME/Library/Logs/karuo/handy_clipboard_wash.log" +``` + +手工验收句:**“打开纯客宝后台,卡罗AI检查流量词。”** +预期:文本自动填入;卡若AI在对话理解层将“纯客宝、卡罗AI、流量词”识别为“存客宝、卡若AI、流量池”。 + +## 7. 当前配置保护 + +- 当前用户确认基线:`Fn + 无需按住 + 中文 SenseVoice + CtrlV 自动填入`。 +- 配置修改前先创建 `settings_store.json.bak.*` 备份,并先向用户说明具体变更。 +- 本轮新增的 `com.karuo.handy-asr-wash` 已卸载。 diff --git a/02_卡人(水)/水溪_整理归档/语音输入/references/heyclicky调研与语音输入迭代方案_20260726.md b/02_卡人(水)/水溪_整理归档/语音输入/references/heyclicky调研与语音输入迭代方案_20260726.md new file mode 100644 index 00000000..c06264ce --- /dev/null +++ b/02_卡人(水)/水溪_整理归档/语音输入/references/heyclicky调研与语音输入迭代方案_20260726.md @@ -0,0 +1,102 @@ +# HeyClicky 调研与卡若AI语音输入迭代方案(2026-07-26) + +> 归属:卡水·水溪 · W03h「语音输入」 +> 结论:保留当前 Handy 输入法,不替换;在其上叠加“上下文问答”和“任务投递”两条可选能力。 + +## 1. 调研真源 + +- 开源基础:[farzaa/clicky(MIT)](https://github.com/farzaa/clicky) +- 产品现状:[heyclicky 官网](https://www.heyclicky.com/) +- 数据边界:[heyclicky 隐私说明](https://www.heyclicky.com/privacy-policy) + +开源 Clicky 的基础结构是:菜单栏应用、控制面板、透明屏幕叠层;热键触发后进行实时转写,再把“转写文本 + 当前截图”发给模型,结果以文字、TTS 和屏幕指针反馈。官方仓库说明其开源代码已冻结,后续产品功能在 HeyClicky 私有版本持续迭代。 + +## 2. HeyClicky 的运行模式 + +```text +热键触发 + ├─ 采集当前语音 + ├─ 同一时刻抓取当前屏幕(仅触发时) + ├─ 意图分流:Talk / Agent + ├─ Talk:模型流式回复 → TTS + 指针标注 + └─ Agent:后台执行任务 → 回执与进度 +``` + +其产品核心不是“更快转写”,而是三个体验层: + +1. **听写**:将语音写入当前文本框。 +2. **屏幕上下文问答**:用户正在看什么,助手就围绕什么解释、指路或生成内容。 +3. **任务代理**:用户明确说出 Agent 意图后,才投递后台任务。 + +## 3. 卡若AI当前链路 + +```text +Fn 开始录音 → Fn 结束录音 + → Handy / SenseVoice 中文转写 + → CtrlV 自动填入当前输入框 + → 卡若AI 对话层 W03b 回廊洗字 + → Skill 路由 / Hub / Codex 执行 +``` + +当前优势:本机中文转写快、使用方式固定、无需按住、输入行为稳定。 +当前缺口:语音一律当作文本输入,缺少“我要问当前屏幕”与“我要派任务”的明确分流;没有统一的回执卡、上下文采集开关和效果指标。 + +## 4. 建议的目标运行模式 + +```text +Fn 普通输入(保留原样) + └─ 语音 → Handy → 当前输入框 → W03b 洗字 + +语音前缀「卡若AI,结合屏幕:…」(新增,可选) + └─ 转写 → 单次屏幕快照 → 本机脱敏 → W03b 洗字 + → 上下文问答 → 文本/可选朗读答复 + +语音前缀「卡若AI,执行:…」(新增,可选) + └─ 转写 → W03b 洗字 → Skill 路由 + → Hub 创建任务 → 进度回执 → 完成结果 +``` + +### 不变项 + +- **Fn 切换录音、无需按住**不变。 +- 继续使用 Handy + SenseVoice;不强制安装 Clicky,也不替换输入法。 +- 不开启常驻截屏;只在“结合屏幕”明确前缀下采一张当前屏幕。 +- 现有 W03b 仍是唯一 ASR 纠错源,W03h 仍是唯一语音输入入口。 + +## 5. 分期迭代 + +| 优先级 | 内容 | 验收标准 | 配置影响 | +|---|---|---|---| +| P0 | 语音三态识别:普通输入 / 结合屏幕 / 执行任务 | 20 条固定口令分流正确率≥95%;普通 Fn 行为不变 | 无,先做离线识别与日志 | +| P1 | 单次屏幕上下文桥接 | 仅明确口令时截图;截图不落盘;问答回执含“已使用当前屏幕” | 新增明确开关,默认关闭 | +| P1 | 统一语音回执卡 | 输入、纠错、路由、任务状态都能查看 | 无 | +| P2 | 可选 TTS 与屏幕标注 | 可随时停止;只在结合屏幕模式启用 | 新增默认关闭开关 | +| P2 | 语音质量看板 | 统计转写耗时、纠错命中率、分流正确率、失败原因 | 本机只存脱敏指标 | + +## 6. 第一轮建议:只做 P0 + +先不动 Handy 配置,先加一个**本地语音意图分流器**: + +- 没有前缀:继续原样填入输入框。 +- `卡若AI,结合屏幕`:标记为 `screen_question`,等待用户确认屏幕权限开关。 +- `卡若AI,执行` 或 `水溪/火炬/金仓` + 任务:标记为 `agent_task`,交给既有 Hub 和 Skill 路由。 +- 每次生成结构化回执:`原转写 / 洗字结果 / 意图 / 路由 / 耗时 / 结果`。 + +这一步复用已有 `karuo_voice_pipeline.py`、W03b 词库和 Hub `13458`,不引入新的桌面应用。 + +## 7. 与 HeyClicky 的取舍 + +| HeyClicky 做法 | 卡若AI采用方式 | +|---|---| +| 热键同时带屏幕截图 | 仅“结合屏幕”前缀下单次截图,默认关闭 | +| Talk 与 Agent 分开计量 | 普通输入、结合屏幕、执行任务分三条逻辑链 | +| TTS + 指针叠层 | 放到 P2,先验证真实使用频次 | +| 云端代理处理音频与截图 | 先本机转写、词库与路由;截图仅按需传递 | +| 通用 Mac 助手 | 保持卡若AI五行 Skill 与业务上下文优势 | + +## 8. 验收样例 + +1. `Fn` 输入“打开纯客宝后台” → 正常填入;对话层理解为“打开存客宝后台”。 +2. `卡若AI,结合屏幕:这个报错是什么` → 仅一次截图、返回问题解释,不创建任务。 +3. `卡若AI,执行:整理今天的语音问题` → 路由 W03h/W03b 后创建任务,返回任务编号和进度。 +4. 不带前缀的任意语音 → 不截图、不启动 Agent、不改变当前输入行为。 diff --git a/02_卡人(水)/水溪_整理归档/语音输入/references/结合屏幕本地部署与使用.md b/02_卡人(水)/水溪_整理归档/语音输入/references/结合屏幕本地部署与使用.md new file mode 100644 index 00000000..9fc6ac09 --- /dev/null +++ b/02_卡人(水)/水溪_整理归档/语音输入/references/结合屏幕本地部署与使用.md @@ -0,0 +1,54 @@ +# 结合屏幕|本地部署与使用(W03h) + +## 已部署运行模式 + +- 应用图标:`资源/卡若AI·屏语.png`,部署时自动生成并装入 macOS 原生 `.icns`。 + +```text +Fn:普通语音输入(原行为不变) +Command+1:结合屏幕模式 + 第一次 Command+1(或菜单“开始结合屏幕”)→ 显示“正在截图 / 正在听你说话”悬浮状态 + 第二次 Command+1(或菜单“结束并提交”)→ 显示“正在转写并提交” + 转写钩子 → 回廊洗字 → 生成「截图路径 + 指令」上下文包 → 自动提交给当前 AI → 直接获得回答 +``` + +## 语音区分 + +| 方式 | 含义 | 行为 | +|---|---|---| +| `Fn` 后直接说内容 | 普通输入 | 保持原有 Handy 自动填入 | +| `Command+1` 后说“结合屏幕,……” | 结合屏幕 | 将单次截图与洗字指令一起发送到当前 AI 输入框 | +| `Command+1` 后说“结合屏幕,执行……” | 结合屏幕任务 | 上下文包会要求当前 AI 先看截图,再执行指令 | + +> Command+1 已经表示结合屏幕;说“结合屏幕”是为了让转写结果和后续 AI 语义更清晰。 + +## 首次启用 + +在 **系统设置 → 隐私与安全性 → 录屏与系统录音** 中开启 `卡若AI·屏语`。 + +`Command+1` 已改用 macOS 系统级热键注册,不需要“输入监控”权限。应用提供常驻控制台,也可以直接点击“开始结合屏幕 / 结束并提交”。 + +## 验收 + +1. 将光标放到 Codex 输入框。 +2. 按 `Command+1`,应出现“结合屏幕”提示。 +3. 说:`结合屏幕,这个页面有什么问题。` +4. 再按 `Command+1`。 +5. 输入框应出现 `【卡若AI·结合屏幕】`、截图图片、截图路径和洗字后的语音指令。 + +截图和本次上下文包保存在: + +`/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/截图归档/`(仅保留最新100张截图;截图超出时只清理最早图片,上下文 `.md` 永久保留) + +## 维护与回滚 + +- 可见应用:`/Applications/卡若AI·屏语.app` +- 热键守护:`com.karuo.voice-screen-hotkey` +- Handy 钩子:`~/Library/Application Support/com.pais.handy/hooks/transcription` +- 重新部署: + +```bash +bash "/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/脚本/部署结合屏幕.sh" +``` + +- 停用:卸载 `com.karuo.voice-screen-hotkey` 并把 Handy 钩子改回 `01_卡资(金)/金仓_存储备份/脚本/handy_transcription_hook.sh`。 diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/references/语音唤醒卡若AI.md b/02_卡人(水)/水溪_整理归档/语音输入/references/语音唤醒卡若AI.md similarity index 100% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/references/语音唤醒卡若AI.md rename to 02_卡人(水)/水溪_整理归档/语音输入/references/语音唤醒卡若AI.md diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/references/语音唤醒需求整理.md b/02_卡人(水)/水溪_整理归档/语音输入/references/语音唤醒需求整理.md similarity index 94% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/references/语音唤醒需求整理.md rename to 02_卡人(水)/水溪_整理归档/语音输入/references/语音唤醒需求整理.md index 924f8237..aab4d8a4 100644 --- a/01_卡资(金)/金仓_存储备份/Handy中文语音/references/语音唤醒需求整理.md +++ b/02_卡人(水)/水溪_整理归档/语音输入/references/语音唤醒需求整理.md @@ -25,7 +25,7 @@ ## 接线(推荐 · macOS) ```bash -bash "/Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/setup_handy_voice_exec_wake.sh" +bash "/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/脚本/setup_handy_voice_exec_wake.sh" ``` 使用 `hooks/transcription` + **stdout 空**,不启用 `external_script`(避免 0.8.3 回滚英文)。 diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/references/语音桌面对接卡若AI网站.md b/02_卡人(水)/水溪_整理归档/语音输入/references/语音桌面对接卡若AI网站.md similarity index 93% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/references/语音桌面对接卡若AI网站.md rename to 02_卡人(水)/水溪_整理归档/语音输入/references/语音桌面对接卡若AI网站.md index 5b3cde1d..d39ab20f 100644 --- a/01_卡资(金)/金仓_存储备份/Handy中文语音/references/语音桌面对接卡若AI网站.md +++ b/02_卡人(水)/水溪_整理归档/语音输入/references/语音桌面对接卡若AI网站.md @@ -47,7 +47,7 @@ ## 接线(不改 Handy 设置) ```bash -bash "/Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/setup_handy_voice_exec_wake.sh" +bash "/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/脚本/setup_handy_voice_exec_wake.sh" ``` **禁止**为接线而运行 `handy_apply_chinese_macos.sh`(会覆盖你的快捷键/语言等设置)。 diff --git a/02_卡人(水)/水溪_整理归档/语音输入/开发文档/功能迭代记录.md b/02_卡人(水)/水溪_整理归档/语音输入/开发文档/功能迭代记录.md new file mode 100644 index 00000000..a2c0aff3 --- /dev/null +++ b/02_卡人(水)/水溪_整理归档/语音输入/开发文档/功能迭代记录.md @@ -0,0 +1,219 @@ +# 功能迭代记录 + +## 2026-07-12|v1.4.3 修复自动输入 + +- 用户真实验证发现:纠错结果进剪贴板,但仍需手动粘贴。 +- 直接核对 Handy v0.8.3 源码 `src-tauri/src/clipboard.rs`:ExternalScript 将文本作为 `$1`,忽略 stdout,并要求脚本自行完成输入。 +- 插件恢复 `$1` 读取,纠错后写剪贴板,等待 350ms 恢复焦点,再自动发送 `⌘V`。 +- `clipboard_handling` 改为 `dont_modify`,避免 Handy 随后用原始转写覆盖纠错后的剪贴板。 +- 新增 `~/Library/Logs/com.pais.handy/karuo_asr_plugin.log`,记录调用、纠错和粘贴失败,不记录正文。 +- 自动输入优先使用本机 `cliclick`,不可用时才回退 System Events;macOS 必须授予对应输入程序辅助功能权限。 + +## 2026-07-12|v1.4.2 自动输入当前对话框 + +- 固化 `clipboard_handling=copy_to_clipboard` 与 `auto_submit=false`。 +- Handy 收到插件 stdout 后自动写入当前焦点对话框,不需要用户手动粘贴。 +- 默认只填入、不自动发送,避免语音误识别直接提交。 + +## 2026-07-12|v1.4.1 修复Handy插件传参协议 + +- 用户真实语音验证发现:Handy日志显示插件被调用,但输入框仍是原文。 +- 根因:旧脚本从参数/剪贴板读取,并自行 ⌘V;Handy 0.8.3 实际从 stdin 传入转写、读取 stdout 作为最终文本。 +- 修复:插件改为纯过滤器 `stdin → 回廊洗字 → stdout`,不再操作剪贴板或模拟按键。 +- 验收标准:管道输入“纯客宝、卡罗亚”时,stdout 必须返回“存客宝、卡若AI”。 + +## 2026-07-12|v1.4 Handy输入阶段ASR纠错 + +### 目标 + +Handy 语音转写后、文字进入当前输入框前,自动调用卡若AI统一ASR纠错库。 + +### 链路 + +```text +Handy录音 + -> SenseVoice zh-Hans转写 + -> handy_macos_asr_paste.sh + -> apply_karuo_voice_corrections.py + -> 卡若闽南口音_ASR纠错库.json + -> 剪贴板 + -> 当前输入框 +``` + +### 修改 + +- Handy `paste_method` 改为 `external_script`。 +- `external_script_path` 指向已验证的 `handy_macos_asr_paste.sh`。 +- 新增 `enable_karuo_asr_correction_macos.sh`,自动备份配置、接线并重启 Handy。 +- 钩子新增 `KARUO_ASR_TEST_ONLY=1` 无粘贴测试模式。 +- 补充 `卡罗AI/卡洛AI/卡路AI` 无空格纠错变体。 + +### 验证 + +- Handy 0.8.3 正常启动,SenseVoice 与 `zh-Hans` 保持不变。 +- Handy 日志确认加载 `ExternalScript` 和目标脚本。 +- 测试输入“打开纯客宝后台,卡罗AI帮我检查”输出“打开存客宝后台,卡若AI帮我检查”。 +- Cursor/Codex 对话层仍保留回廊洗字二次兜底。 + +### 回滚 + +恢复 `settings_store.json.bak.asr.*` 备份并重启 Handy,或把 `paste_method` 改回 `ctrl_v`、`external_script_path` 改回 `null`。 + +## 2026-07-26|v1.4.4 本机语音链路复核与 KI 沉淀 + +- **实测结论**:Handy v0.8.3 + 内置麦克风 + SenseVoice 中文可正常识别;日志显示最近转写耗时 90–471ms,自动粘贴成功。 +- **发现并修复**:实际运行配置遗留为 `Fn`、`push_to_talk=false`、`autostart_enabled=true`、未启用回廊洗字守护,导致“卡路AI / 卡罗AI”等误听原样落入对话。 +- **已统一基线**:`⌘1`、`zh-Hans`、`sense-voice-int8`、`push_to_talk=true`、`ctrl_v`、`copy_to_clipboard`、`auto_submit=false`、`autostart_enabled=false`;已安装 `com.karuo.handy-asr-wash`。 +- **验证**:`test_karuo_asr_corrections.py` 3/3 通过;纠错示例“纯客宝、卡罗AI、流量词”→“存客宝、卡若AI、流量池”。 +- **已知约束**:Handy 0.8.3 启动后会把 `ort_accelerator` 归一成 `auto`;当前识别正常,未升级到 0.9.4,待隔离兼容性验证后再决定。 +- **知识沉淀**:[Handy语音输入_KI_本机实测_20260726.md](../references/Handy语音输入_KI_本机实测_20260726.md)。 + +## 2026-07-26|v1.4.5 按用户确认恢复原始 Handy 配置 + +- 已恢复并由启动日志确认:热键 `Fn`、`push_to_talk=false`(无需按住)、`autostart_enabled=true`、`CtrlV` 自动填入。 +- 已卸载本轮新增 `com.karuo.handy-asr-wash`,不再在输入前额外修改剪贴板。 +- 后续以用户确认的原配置为唯一基线;修改前先备份并明确说明改动项。 + +## 2026-07-26|v1.5 能力包迁移至卡水·语音输入 + +- 按用户确认,原 `金仓/Handy中文语音` **整体迁移并重命名**为 `水溪/语音输入`,未创建并行重复 Skill。 +- Skill 元数据统一为:`name=语音输入`、`group=水`、`owner=水溪`、`编号=W03h`。 +- 路由职责已拆分:W03h 管日常语音输入、配置、验收与排障;W03b「回廊洗字」只管 ASR 词库和语义纠错。 +- 当前用户确认的电脑基线:`Fn` 开始录音、再按 `Fn` 结束,`push_to_talk=false`,不安装剪贴板洗字守护。 + +## 2026-07-26|HeyClicky 调研与 W03h 优化方案 + +- 调研 [farzaa/clicky](https://github.com/farzaa/clicky) 与 [heyclicky 官网](https://www.heyclicky.com/)。可借鉴核心是“听写 / 屏幕上下文问答 / Agent 任务”三条独立链路。 +- 卡若AI采用增量方案:保留当前 `Fn` 切换录音、Handy、SenseVoice 和 W03b 洗字;P0 先建立本地语音意图分流与结构化回执,不改 Handy 配置。 +- 完整方案:[heyclicky调研与语音输入迭代方案_20260726.md](../references/heyclicky调研与语音输入迭代方案_20260726.md)。 + +## 2026-07-26|v1.6 Home+1「结合屏幕」本地模式 + +- 新增本地 `Home+1` 热键守护:首次按键单次截取主屏并启动 Handy 后处理录音;再次按键结束。 +- 新增转写钩子:将截图绝对路径、W03b 洗字结果和语音指令组装为上下文包,填入当前 AI 输入框。 +- `Fn`、`push_to_talk=false` 未改动;结合屏幕只在 `Home+1` 下工作,无常驻截屏。 +- 运行服务 `com.karuo.voice-screen-hotkey` 已加载;首次使用需授予 macOS 输入监控权限。 +- 使用说明:[结合屏幕本地部署与使用.md](../references/结合屏幕本地部署与使用.md)。 + +## 2026-07-26|v1.6.1 结合屏幕热键改为 Command+1 + +- 按用户指定,热键从 Home+1 切换为 `Command+1`;本地 LaunchAgent 已重编译并重启。 +- `Fn` 普通语音输入与“无需按住说话”保持不变。 +- 已通过命令直达 macOS「输入监控」授权页;该系统级权限由当前登录用户最后确认一次。 + + +## 2026-07-26|v1.6.2 可见应用与输入监控修复 + +- 根因:原热键守护是无界面二进制,macOS「输入监控」列表不会显示可选择项目。 +- 已改为可见的 `/Applications/卡若AI结合屏幕.app`(bundle id `com.karuo.voice-screen`),菜单栏显示“屏幕语音”,包含“打开输入监控设置”和“测试截图”。 +- LaunchAgent 已改为启动该应用包内可执行文件;部署脚本同步改为应用化安装与注册。 +- 保持不变:`Fn` 普通语音、`push_to_talk=false`、Handy 原始配置。 + + +## 2026-07-26|v1.6.3 卡若AI·屏语命名与本地截图归档 + +- 应用重命名为 **卡若AI·屏语**:语义为“屏幕 + 语音”,与卡若AI产品命名统一,菜单栏也显示该名称。 +- 截图归档改至 `02_卡人(水)/水溪_整理归档/语音输入/截图归档/`,即卡若AI主目录内。 +- 新增上限:仅保留最新 **100 张** `screen-*.png`;生成第101张时自动删除最早截图及同名 `.md` 上下文文件。 +- 归档文件被 `.gitignore` 排除,仅本机保留,不同步到版本库。 + + +## 2026-07-26|v1.6.4 屏语截图清理与上下文留存分离 + +- 按用户确认,超出100张时**只删除最早 `screen-*.png` 截图**。 +- 同名 `.md` 语音上下文包不再随图片清理,作为本地历史记录持续保留。 + + +## 2026-07-26|v1.6.5 卡若AI·屏语原生应用图标 + +- 新增“水感屏幕 + 声波”应用图标源文件:`资源/卡若AI·屏语.png`。 +- 部署脚本自动生成 macOS `.icns` 并写入 App Bundle,Finder、Dock 与输入监控均使用同一图标。 + + +## 2026-07-26|v1.6.6 屏语可视状态与直接输出 + +- 新增菜单入口、浮动状态窗与完整状态:正在截图 → 正在说话 → 正在转写并提交 → 当前 AI 正在回答。 +- `Command+1` 与菜单均可启动/结束;若截屏权限缺失,应用自动打开“屏幕录制”设置。 +- 屏语专用转写通过 Handy ExternalScript 自动提交给当前 AI;普通 `Fn` 仍按一次开始、再按一次结束,仍然只填入不自动发送。 + + +## 2026-07-26|v1.6.7 屏语控制台与系统级快捷键 + +- 根因修复:原 `Command+1` 全局监听依赖输入监控且无授权记录,已改为 Carbon `RegisterEventHotKey` 系统级注册,不再依赖输入监控。 +- 新增常驻屏语控制台:开始结合屏幕、结束并提交、开启录屏权限三个直接按钮。 +- 状态窗改为非激活浮窗,不抢走当前 AI 输入框焦点,避免转写结果粘贴到错误位置。 +- 首次唯一权限为“录屏与系统录音”;权限关闭时控制台明确提示并直达设置页。 + + +## 2026-07-26|v1.6.8 移除录屏权限误判 + +- 用户截图确认“卡若AI·屏语”录屏开关已开启;旧版 `CGPreflightScreenCaptureAccess()` 在当前 LaunchAgent 启动上下文返回假阴性。 +- 已移除启动前权限预检,改为直接执行真实截图,以截图文件和返回码作为唯一成功标准。 +- 控制台不再错误提示重复开启权限。 + + +## 2026-07-26|v1.6.9 原生进程截图 + +- 实测确认录屏开关已开启,但 `/usr/sbin/screencapture` 子进程未继承应用 TCC 身份。 +- 截图改由 `卡若AI·屏语` Swift 主进程通过 CoreGraphics 直接生成,再使用 `--arm-existing` 登记并启动 Handy。 +- 权限归属、界面状态与截图执行统一到同一 App Bundle。 + + +## 2026-07-26|v1.6.10 LaunchServices应用身份启动 + +- 根因:LaunchAgent 直接执行 App 包内二进制,TCC录屏权限虽然界面开启,但运行进程未按 Bundle 身份归属。 +- LaunchAgent 改为 `/usr/bin/open -g /Applications/卡若AI·屏语.app`,由 LaunchServices 以完整 Bundle ID 启动。 + + +## 2026-07-26|v1.6.11 本地截图助手兜底 + +- 系统设置显示录屏已开启,但ScreenCaptureKit返回TCC拒绝(设置界面与TCC内部状态冲突)。 +- 新增 `屏语截图助手.py`,经本机已授权执行链路实测截图成功;主应用不再显示重复授权提示。 +- 屏语按钮统一通过助手生成截图,再进入Handy录音与自动提交。 + + +## 2026-07-26|v1.6.12 运行时与用户归档分离 + +- 修复App子进程读取Documents脚本时阻塞:运行脚本和IPC迁至 `~/Library/Application Support/karuo-ai/pingyu/`。 +- 截图与上下文仍保存到卡若AI `语音输入/截图归档/`,用户目录结构不变。 + + +## 2026-07-26|v1.6.13 屏语只粘贴不自动发送 + +- 按用户确认,屏语结束后仅把“截图路径 + 洗字语音指令”粘贴到当前输入框。 +- 移除 `--submit` 调用,不再模拟回车;发送动作始终由用户确认。 +- 控制台文案统一改为“结束并粘贴 / 已粘贴到输入框”。 + + +## 2026-07-26|v1.6.14 Handy式无弹窗交互 + +- 移除屏语启动、截图、说话、处理、粘贴过程中的浮动控制窗。 +- 状态仅显示在菜单栏:`屏语·截图中 → 屏语·说话中 → 屏语·处理中 → 屏语·已粘贴`。 +- 操作保持 `Command+1` 开始、再次 `Command+1` 结束并只粘贴,不自动回车。 + + +## 2026-07-26|v1.6.15 真实图片附件粘贴 + +- 屏语结束后不再只粘贴图片路径;新增 `屏语图片粘贴.swift`,将 PNG 图片和上下文文字一起写入剪贴板。 +- 当前 AI 输入框会显示真实截图附件;仍然只粘贴、不自动回车。 + +## v1.6.16 真实图片附件+语音文字同次粘贴(2026-07-26) +- **目标**:Command+1 流程不再只粘贴截图路径,而是将真实 PNG 图片与洗字后的语音文字一次写入当前 AI 输入框;保持不自动回车。 +- **实现**:截图助手把归档 PNG 同步到 `$HOME/Library/Application Support/karuo-ai/pingyu/latest-screen.png`;`屏语图片粘贴.swift` 通过 NSPasteboard 同时登记 `PNGf` 与 `string`,再仅模拟 Command+V。 +- **验证**:`osascript -e 'clipboard info'` 已显示 `«class PNGf»` 与 `«class utf8»`;`结合屏幕.py --consume --paste` 返回 0,pending 状态已清理。 +- **架构**:Command+1 → 截图助手 → runtime 图片副本 → Handy 转写 → 图片+文字剪贴板 → 当前输入框;上下文 `.md` 与归档图片继续保留。 + +## v1.6.17 双粘贴链路分流(2026-07-26) +- **Fn/Handy**:恢复 `paste_method=ctrl_v`、`external_script_path=null`,保持原生语音文字粘贴。 +- **Command+1/屏语**:屏语应用记录录音前剪贴板版本,结束时等待 Handy 原生转写完成,再单独调用 `结合屏幕.py --consume --paste`,将 PNG+文字一次粘贴,不改变 Fn 链路。 +- **验证**:应用已重新编译签名并启动;配置读取为 `ctrl_v / null`;两种模式互不调用同一外部钩子。 + +## v1.6.18 屏语分步附件粘贴(2026-07-26) +- **问题**:部分 AI 输入框对同一剪贴板中的 PNG+文字只取文字,用户看不到图片。 +- **修复**:屏语助手改为同一任务内先粘贴 PNG 图片,再粘贴语音文字;Fn/Handy 仍只走原生文字 `ctrl_v`。 +- **验证**:运行目录助手编译成功;`clipboard info` 在图片阶段显示 `PNGf`,无自动回车。 + +## v1.6.19 输入焦点恢复(2026-07-26) +- **问题**:Command+1 结束录音后,系统焦点可能仍停留在 Handy/屏语状态栏,导致图片粘贴位置不可见。 +- **修复**:开始屏语时记录当前前台输入应用;收到 Handy 转写后先恢复该应用焦点,再执行“图片→文字”两段粘贴。 +- **验证**:最近日志已持续记录截图生成与转写消费;新版本应用已重新编译、签名并启动。 diff --git a/02_卡人(水)/水溪_整理归档/语音输入/截图归档/.gitignore b/02_卡人(水)/水溪_整理归档/语音输入/截图归档/.gitignore new file mode 100644 index 00000000..0b6d837c --- /dev/null +++ b/02_卡人(水)/水溪_整理归档/语音输入/截图归档/.gitignore @@ -0,0 +1,3 @@ +# 本机屏语截图与上下文不进入版本库 +* +!.gitignore diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/build_package.sh b/02_卡人(水)/水溪_整理归档/语音输入/脚本/build_package.sh similarity index 95% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/build_package.sh rename to 02_卡人(水)/水溪_整理归档/语音输入/脚本/build_package.sh index 18422926..c8bd4f27 100755 --- a/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/build_package.sh +++ b/02_卡人(水)/水溪_整理归档/语音输入/脚本/build_package.sh @@ -48,7 +48,7 @@ chmod +x "${PKG_DIR}/脚本/install_all.sh" cat > "${PKG_DIR}/卡若AI技能路径.txt" << EOF 本包对应卡若AI Skill(安装后 Agent 自动识别): -/Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md +/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/SKILL.md 编号:G25b · 金仓 · Handy中文语音 关联:回廊洗字 W03b(Cursor 内 ASR 纠错) @@ -99,7 +99,7 @@ cat > "${TOOL_ROOT}/README.md" << 'TOOLREADME' | **Windows** | `Handy中文语音-安装包-Windows-v1.1/` → `双击安装Handy中文语音.bat` | `Handy中文语音_Windows_安装包_YYYYMMDD.zip` | | **语音唤 Agent** | 包内 `语音唤醒卡若AI.md` + `setup_voice_agent_plugin.*` | 说「卡若AI …」或「火炬 …」 | -卡若AI Skill:`卡若AI/01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md`(G25b) +卡若AI Skill:`卡若AI/02_卡人(水)/水溪_整理归档/语音输入/SKILL.md`(G25b) 归类:`4、火_工具开发/工具/`(黑曜石10_归类总册 · tool_release) TOOLREADME diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/build_package_windows.sh b/02_卡人(水)/水溪_整理归档/语音输入/脚本/build_package_windows.sh similarity index 97% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/build_package_windows.sh rename to 02_卡人(水)/水溪_整理归档/语音输入/脚本/build_package_windows.sh index d6ab3cb2..d1ddb932 100755 --- a/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/build_package_windows.sh +++ b/02_卡人(水)/水溪_整理归档/语音输入/脚本/build_package_windows.sh @@ -53,7 +53,7 @@ PS1 cat > "${PKG_DIR}/卡若AI技能路径.txt" << EOF 卡若AI Skill(G25b · Handy中文语音): -/Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md +/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/SKILL.md Windows 安装包目录(五行 · 火 · 工具): ${TOOL_ROOT}/${PKG_NAME}/ diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/enable_karuo_asr_correction_macos.sh b/02_卡人(水)/水溪_整理归档/语音输入/脚本/enable_karuo_asr_correction_macos.sh similarity index 100% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/enable_karuo_asr_correction_macos.sh rename to 02_卡人(水)/水溪_整理归档/语音输入/脚本/enable_karuo_asr_correction_macos.sh diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/handy_apply_chinese_windows.ps1 b/02_卡人(水)/水溪_整理归档/语音输入/脚本/handy_apply_chinese_windows.ps1 similarity index 100% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/handy_apply_chinese_windows.ps1 rename to 02_卡人(水)/水溪_整理归档/语音输入/脚本/handy_apply_chinese_windows.ps1 diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/handy_download_sensevoice.ps1 b/02_卡人(水)/水溪_整理归档/语音输入/脚本/handy_download_sensevoice.ps1 similarity index 100% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/handy_download_sensevoice.ps1 rename to 02_卡人(水)/水溪_整理归档/语音输入/脚本/handy_download_sensevoice.ps1 diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/handy_download_sensevoice.sh b/02_卡人(水)/水溪_整理归档/语音输入/脚本/handy_download_sensevoice.sh similarity index 100% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/handy_download_sensevoice.sh rename to 02_卡人(水)/水溪_整理归档/语音输入/脚本/handy_download_sensevoice.sh diff --git a/02_卡人(水)/水溪_整理归档/语音输入/脚本/handy_结合屏幕钩子.sh b/02_卡人(水)/水溪_整理归档/语音输入/脚本/handy_结合屏幕钩子.sh new file mode 100755 index 00000000..da5efc03 --- /dev/null +++ b/02_卡人(水)/水溪_整理归档/语音输入/脚本/handy_结合屏幕钩子.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# Handy 0.8.3 ExternalScript:屏语已武装则提交屏幕任务;否则完全沿用原 Fn 输入脚本。 +set -euo pipefail +ROOT="/Users/karuo/Documents/个人/卡若AI" +SCREEN="$HOME/Library/Application Support/karuo-ai/pingyu/结合屏幕.py" +LEGACY="$ROOT/01_卡资(金)/金仓_存储备份/脚本/handy_macos_asr_paste.sh" +RAW="${1:-}" +if [[ -z "${RAW//[[:space:]]/}" ]]; then RAW="$(cat)"; fi +if [[ -n "${RAW//[[:space:]]/}" ]] && python3 "$SCREEN" --consume --paste "$RAW" >/dev/null 2>&1; then + exit 0 +fi +exec "$LEGACY" "$RAW" diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/install_all.sh b/02_卡人(水)/水溪_整理归档/语音输入/脚本/install_all.sh similarity index 100% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/install_all.sh rename to 02_卡人(水)/水溪_整理归档/语音输入/脚本/install_all.sh diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/setup_handy_voice_exec_wake.sh b/02_卡人(水)/水溪_整理归档/语音输入/脚本/setup_handy_voice_exec_wake.sh similarity index 100% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/setup_handy_voice_exec_wake.sh rename to 02_卡人(水)/水溪_整理归档/语音输入/脚本/setup_handy_voice_exec_wake.sh diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/setup_voice_agent_plugin.ps1 b/02_卡人(水)/水溪_整理归档/语音输入/脚本/setup_voice_agent_plugin.ps1 similarity index 100% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/setup_voice_agent_plugin.ps1 rename to 02_卡人(水)/水溪_整理归档/语音输入/脚本/setup_voice_agent_plugin.ps1 diff --git a/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/setup_voice_agent_plugin.sh b/02_卡人(水)/水溪_整理归档/语音输入/脚本/setup_voice_agent_plugin.sh similarity index 100% rename from 01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/setup_voice_agent_plugin.sh rename to 02_卡人(水)/水溪_整理归档/语音输入/脚本/setup_voice_agent_plugin.sh diff --git a/02_卡人(水)/水溪_整理归档/语音输入/脚本/屏语图片粘贴.swift b/02_卡人(水)/水溪_整理归档/语音输入/脚本/屏语图片粘贴.swift new file mode 100644 index 00000000..8cd70e0b --- /dev/null +++ b/02_卡人(水)/水溪_整理归档/语音输入/脚本/屏语图片粘贴.swift @@ -0,0 +1,32 @@ +import AppKit +import Foundation + +let args = CommandLine.arguments +let copyOnly = args.contains("--copy-only") +let positional = args.dropFirst().filter { $0 != "--copy-only" } +guard positional.count >= 2 else { exit(2) } +let imagePath = String(positional[0]) +let text = String(positional[1]) +guard let imageData = try? Data(contentsOf: URL(fileURLWithPath: imagePath)), + imageData.count > 8 else { exit(3) } +let png = imageData +let pasteboard = NSPasteboard.general +pasteboard.clearContents() +pasteboard.declareTypes([.png], owner: nil) +guard pasteboard.setData(png, forType: .png) else { exit(4) } +if !copyOnly { + usleep(350_000) + func paste() { + let task = Process() + task.executableURL = URL(fileURLWithPath: "/usr/bin/osascript") + task.arguments = ["-e", "tell application \"System Events\" to keystroke \"v\" using command down"] + try? task.run(); task.waitUntilExit() + } + paste() + usleep(350_000) + pasteboard.clearContents() + pasteboard.declareTypes([.string], owner: nil) + guard pasteboard.setString(text, forType: .string) else { exit(5) } + usleep(180_000) + paste() +} diff --git a/02_卡人(水)/水溪_整理归档/语音输入/脚本/屏语截图助手.py b/02_卡人(水)/水溪_整理归档/语音输入/脚本/屏语截图助手.py new file mode 100755 index 00000000..0d616027 --- /dev/null +++ b/02_卡人(水)/水溪_整理归档/语音输入/脚本/屏语截图助手.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +import json, os, subprocess, time +from pathlib import Path +STATE = Path.home() / 'Library/Application Support/karuo-ai/pingyu' +REQUEST = STATE / 'capture-request.json' +RESPONSE = STATE / 'capture-response.json' +PID = STATE / 'capture-helper.pid' +STATE.mkdir(parents=True, exist_ok=True) +PID.write_text(str(os.getpid())) +while True: + if REQUEST.exists(): + try: + data=json.loads(REQUEST.read_text()) + output=Path(data['output']) + REQUEST.unlink(missing_ok=True) + result=subprocess.run(['/usr/sbin/screencapture','-x','-m',str(output)],capture_output=True,text=True) + ok=result.returncode == 0 and output.exists() and output.stat().st_size > 0 + if ok: + # 将图片同步到应用支持目录,供粘贴助手读取,避免 Documents 的 TCC 读取差异。 + runtime_image = STATE / 'latest-screen.png' + runtime_image.write_bytes(output.read_bytes()) + shots=sorted(output.parent.glob("screen-*.png"), key=lambda item:item.stat().st_mtime, reverse=True) + for old in shots[100:]: old.unlink(missing_ok=True) + RESPONSE.write_text(json.dumps({'ok':ok,'output':str(output),'runtime_output':str(STATE / 'latest-screen.png') if ok else '', 'error':result.stderr.strip()},ensure_ascii=False)) + except Exception as exc: + RESPONSE.write_text(json.dumps({'ok':False,'error':str(exc)},ensure_ascii=False)) + time.sleep(0.12) diff --git a/02_卡人(水)/水溪_整理归档/语音输入/脚本/结合屏幕.py b/02_卡人(水)/水溪_整理归档/语音输入/脚本/结合屏幕.py new file mode 100755 index 00000000..732e889d --- /dev/null +++ b/02_卡人(水)/水溪_整理归档/语音输入/脚本/结合屏幕.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python3 +"""卡若AI W03h:Command+1 结合屏幕本地上下文包。""" +from __future__ import annotations + +import argparse +import json +import os +import subprocess +import sys +import time +from datetime import datetime +from pathlib import Path + +ROOT = Path("/Users/karuo/Documents/个人/卡若AI") +ARCHIVE_DIR = ROOT / "02_卡人(水)/水溪_整理归档/语音输入/截图归档" +STATE_DIR = Path.home() / "Library/Application Support/karuo-ai/pingyu" +MAX_SCREENSHOTS = 100 +PENDING = STATE_DIR / "pending.json" +LOG = STATE_DIR / "screen-context.log" +HANDY = Path("/Applications/Handy.app/Contents/MacOS/handy") +CAPTURE_REQUEST = STATE_DIR / "capture-request.json" +CAPTURE_RESPONSE = STATE_DIR / "capture-response.json" +CORRECTION_SCRIPT = ROOT / "运营中枢/工作台/脚本/apply_karuo_voice_corrections.py" +PASTE_HELPER = Path.home() / "Library/Application Support/karuo-ai/pingyu/屏语图片粘贴" + + +def write_log(event: str) -> None: + STATE_DIR.mkdir(parents=True, exist_ok=True) + with LOG.open("a", encoding="utf-8") as f: + f.write(f"{datetime.now().isoformat(timespec='seconds')} {event}\n") + + +def cleanup_screenshots() -> int: + """只保留最新 100 张屏语截图;上下文记录永久保留。""" + shots = sorted(ARCHIVE_DIR.glob("screen-*.png"), key=lambda item: item.stat().st_mtime, reverse=True) + removed = 0 + for shot in shots[MAX_SCREENSHOTS:]: + shot.unlink(missing_ok=True) + removed += 1 + if removed: + write_log(f"cleanup removed={removed} remaining={min(len(shots), MAX_SCREENSHOTS)}") + return removed + + +def read_pending() -> dict | None: + try: + data = json.loads(PENDING.read_text(encoding="utf-8")) + if time.time() > float(data.get("expires_at", 0)): + PENDING.unlink(missing_ok=True) + write_log("expired") + return None + return data + except (OSError, ValueError, TypeError): + return None + + +def correct(text: str) -> str: + if not CORRECTION_SCRIPT.exists(): + return text.strip() + out = subprocess.run( + [sys.executable, str(CORRECTION_SCRIPT), text], + capture_output=True, + text=True, + check=False, + ) + return (out.stdout or text).strip() + + +def register_screenshot(shot: Path, *, start_recording: bool) -> int: + if not shot.exists() or shot.stat().st_size == 0: + write_log(f"existing-capture-invalid path={shot}") + return 2 + PENDING.write_text( + json.dumps({"screenshot": str(shot), "armed_at": time.time(), "expires_at": time.time() + 180}, ensure_ascii=False), + encoding="utf-8", + ) + write_log(f"armed screenshot={shot.name}") + if start_recording and HANDY.exists(): + subprocess.Popen([str(HANDY), "--toggle-post-process"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + return 0 + + +def arm_existing(path: str, *, start_recording: bool) -> int: + STATE_DIR.mkdir(parents=True, exist_ok=True) + if read_pending(): + write_log("existing-capture-pending-conflict") + return 4 + return register_screenshot(Path(path), start_recording=start_recording) + + +def arm(*, start_recording: bool) -> int: + STATE_DIR.mkdir(parents=True, exist_ok=True) + current = read_pending() + if current: + if start_recording and HANDY.exists(): + subprocess.Popen([str(HANDY), "--toggle-post-process"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + write_log("toggle-stop") + return 0 + + ARCHIVE_DIR.mkdir(parents=True, exist_ok=True) + shot = ARCHIVE_DIR / f"screen-{datetime.now():%Y%m%d-%H%M%S}.png" + CAPTURE_RESPONSE.unlink(missing_ok=True) + CAPTURE_REQUEST.write_text(json.dumps({"output": str(shot)}, ensure_ascii=False), encoding="utf-8") + deadline = time.time() + 5 + while time.time() < deadline and not CAPTURE_RESPONSE.exists(): + time.sleep(0.1) + try: + response = json.loads(CAPTURE_RESPONSE.read_text(encoding="utf-8")) + except (OSError, ValueError): + response = {"ok": False, "error": "helper-timeout"} + if not response.get("ok"): + write_log(f"capture-helper-failed error={response.get('error', '')}") + return 2 + result = register_screenshot(shot, start_recording=start_recording) + if result == 0 and response.get("runtime_output"): + state = read_pending() + if state: + state["runtime_screenshot"] = response["runtime_output"] + PENDING.write_text(json.dumps(state, ensure_ascii=False), encoding="utf-8") + return result + + +def consume(raw: str, *, paste: bool, submit: bool, dry_run: bool) -> int: + state = read_pending() + if not state: + return 3 + corrected = correct(raw) + task = corrected.removeprefix("结合屏幕").lstrip(",,:: ") or corrected + screenshot = Path(str(state["screenshot"])) + attachment = Path(str(state.get("runtime_screenshot", screenshot))) + packet = ( + "【卡若AI·结合屏幕】\n" + f"请查看当前屏幕截图:{screenshot}\n" + "截图只用于本次上下文,不做常驻监控。\n" + f"语音指令(已洗字):{task}\n" + "请先结合截图理解,再直接回答或执行。" + ) + context = screenshot.with_suffix(".md") + context.write_text(packet + "\n", encoding="utf-8") + PENDING.unlink(missing_ok=True) + write_log(f"consumed screenshot={screenshot.name} raw_len={len(raw)}") + if paste and not dry_run: + if PASTE_HELPER.exists() and attachment.exists(): + subprocess.run([str(PASTE_HELPER), str(attachment), packet], check=False) + else: + subprocess.run(["pbcopy"], input=packet, text=True, check=False) + time.sleep(0.35) + subprocess.run(["osascript", "-e", 'tell application "System Events" to keystroke "v" using command down'], check=False) + # 屏语始终不自动回车;submit 参数保留作兼容但不执行。 + print(packet) + return 0 + + +def main() -> int: + ap = argparse.ArgumentParser(description="卡若AI结合屏幕本地上下文") + group = ap.add_mutually_exclusive_group(required=True) + group.add_argument("--toggle", action="store_true", help="Command+1 调用:截图并开始/结束后处理录音") + group.add_argument("--arm", action="store_true", help="仅截图武装,供测试") + group.add_argument("--arm-existing", metavar="PATH", help="登记由屏语应用原生生成的截图并开始录音") + group.add_argument("--consume", action="store_true", help="消费待处理的转写") + group.add_argument("--status", action="store_true", help="显示屏语截图归档状态") + ap.add_argument("--paste", action="store_true", help="消费后填入当前输入框") + ap.add_argument("--submit", action="store_true", help="仅屏语模式:填入后自动提交给当前 AI") + ap.add_argument("--dry-run", action="store_true", help="不触发录音或粘贴") + ap.add_argument("text", nargs="?", help="转写文本;缺省时从 stdin 读取") + args = ap.parse_args() + if args.status: + STATE_DIR.mkdir(parents=True, exist_ok=True) + removed = cleanup_screenshots() + count = len(list(ARCHIVE_DIR.glob("screen-*.png"))) + print(json.dumps({"directory": str(ARCHIVE_DIR), "screenshots": count, "max_screenshots": MAX_SCREENSHOTS, "cleaned": removed}, ensure_ascii=False)) + return 0 + if args.toggle: + return arm(start_recording=not args.dry_run) + if args.arm: + return arm(start_recording=False) + if args.arm_existing: + return arm_existing(args.arm_existing, start_recording=not args.dry_run) + raw = args.text if args.text is not None else sys.stdin.read() + return consume(raw, paste=args.paste, submit=args.submit, dry_run=args.dry_run) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/02_卡人(水)/水溪_整理归档/语音输入/脚本/结合屏幕应用.swift b/02_卡人(水)/水溪_整理归档/语音输入/脚本/结合屏幕应用.swift new file mode 100644 index 00000000..7aa067da --- /dev/null +++ b/02_卡人(水)/水溪_整理归档/语音输入/脚本/结合屏幕应用.swift @@ -0,0 +1,305 @@ +import AppKit +import ApplicationServices +import Carbon.HIToolbox +import CoreGraphics +import Foundation +import ScreenCaptureKit + +let screenScript = NSHomeDirectory() + "/Library/Application Support/karuo-ai/pingyu/结合屏幕.py" +let archiveDirectory = "/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/截图归档" +let pendingFile = NSHomeDirectory() + "/Library/Application Support/karuo-ai/pingyu/pending.json" + +private let screenVoiceHotKeyHandler: EventHandlerUPP = { _, event, userData in + guard let event, let userData else { return OSStatus(eventNotHandledErr) } + var hotKeyID = EventHotKeyID() + let status = GetEventParameter( + event, + EventParamName(kEventParamDirectObject), + EventParamType(typeEventHotKeyID), + nil, + MemoryLayout