kinstra-neko 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kinstra Neko contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ NOTE: This project integrates with third-party QQ protocol gateways
24
+ (NapCat and any OneBot v11 implementation) and with
25
+ DeepSeek Harness (MIT), each governed by its own license and terms.
26
+ This project does not redistribute those gateways and does not endorse
27
+ circumventing any platform's terms of service. Use a dedicated
28
+ secondary QQ account at your own risk.
package/README.md CHANGED
@@ -1,3 +1,233 @@
1
- # Temporary Holding Version
1
+ <img src="./assets/banner.svg" alt="Kinstra Neko 动画横幅:消息星座凝成猫娘,@ 信标点亮耳尖" width="100%"/>
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <h3 align="center">住在你 QQ 群里的猫娘</h3>
4
+
5
+ <p align="center">
6
+ 一个自部署的社交陪伴机器人 —— 被 @ 会回,没人叫时按话题相关度自然插话,记得群友的小事,有自己的性格。
7
+ <br/>
8
+ 基于 <a href="https://github.com/deepseek-ai/deepseek-harness">DeepSeek Harness</a> 二次开发,通过 OneBot v11 接入 QQ。
9
+ </p>
10
+
11
+ <div align="center">
12
+
13
+ ![License: MIT](https://img.shields.io/badge/license-MIT-67e8f9?style=flat-square)
14
+ ![Platform: Linux | Windows](https://img.shields.io/badge/platform-Linux%20%7C%20Windows-ff7eb3?style=flat-square)
15
+ ![Runtime: Node 22.19+](https://img.shields.io/badge/runtime-Node%2022.19%2B-a78bfa?style=flat-square)
16
+ ![Protocol: OneBot v11](https://img.shields.io/badge/protocol-OneBot%20v11-cbb8ff?style=flat-square)
17
+ ![Based on DeepSeek Harness](https://img.shields.io/badge/based%20on-DeepSeek%20Harness-ff9ecb?style=flat-square)
18
+ ![TypeScript 6.0](https://img.shields.io/badge/TypeScript-6.0-3178c6?style=flat-square)
19
+ ![56 tests passing](https://img.shields.io/badge/tests-56%20passing-3fb96e?style=flat-square)
20
+
21
+ </div>
22
+
23
+ > [!WARNING]
24
+ > **账号风险须知**:QQ 非官方协议接入存在账号被限制的可能。**请务必使用专门注册的小号**,本项目作者不对任何账号后果负责。建议先拉小号进测试群体验,确认稳定再长期使用。
25
+
26
+ ---
27
+
28
+ ## 特性
29
+
30
+ <table>
31
+ <tr>
32
+ <td width="50%" valign="top">
33
+ <h4>💬 低打扰的群聊存在感</h4>
34
+ 被 @、被引用、叫名字必回;没被叫到时,只在话题真的和她有关、群里正热闹、过了冷却期时以小概率插话;加入话题后顺着聊几句,话题走远就安静。冷却、每小时上限、静默时段、防刷屏全部可配。
35
+ </td>
36
+ <td width="50%" valign="top">
37
+ <h4>🔒 出厂即安全默认</h4>
38
+ 总开关关、试运行(observe)开、白名单空 —— 不配置一句话都不会发。试运行期间她只在面板记录「我本来会说什么」,确认对味再开麦。主人私聊永远放行,陌生人会话零本地工具。
39
+ </td>
40
+ </tr>
41
+ <tr>
42
+ <td width="50%" valign="top">
43
+ <h4>🧠 零依赖长期记忆</h4>
44
+ 自带中文二元切词 BM25 记忆库:JSONL 磁盘事实库 + 热群 LRU 内存倒排索引(冷群零驻留)+ 遗忘曲线与人物档案。无需 Redis / 数据库,2G 内存服务器也跑得动。
45
+ </td>
46
+ <td width="50%" valign="top">
47
+ <h4>🐾 可微调的原创人格</h4>
48
+ 面板直接编辑 Markdown 人格卡,下一轮即时生效;系统提示词变量、记忆上下文、remember / recall 工具自动挂载。回复支持换行多气泡、引用、真 @ 群成员、拍一拍,带拟人打字延迟。
49
+ </td>
50
+ </tr>
51
+ <tr>
52
+ <td width="50%" valign="top">
53
+ <h4>🖥️ 图形化首启向导</h4>
54
+ 浏览器内三步向导 + 七个配置页面(概览 / 连接 / 群聊与权限 / 人格 / 试运行 / 日志 / 高级),品牌化界面,零 UI 库依赖。主人还能直接在 DSH 面板里翻看每个群的长会话、随时插话。
55
+ </td>
56
+ <td width="50%" valign="top">
57
+ <h4>📦 一条命令装完,无 Docker</h4>
58
+ Linux 一键脚本:Node 22、内存不足自动加 2G swap、NapCat 自动托管、systemd 守护(开机自启、崩溃重启、700MB 内存上限);Windows 自动下载运行 NapCat 官方一键包(连 QQ 一起装好)+ 桌面快捷方式。用户只需扫码登录小号、粘贴模型 API Key。
59
+ </td>
60
+ </tr>
61
+ </table>
62
+
63
+ ### 它有意不做
64
+
65
+ - **不内置 TTS 语音、翻唱、视频等重模块** —— 保持 2G 可跑、行为可预期;后续作为可选模块提供。
66
+ - **不 fork、不镜像、不分发 NapCat** —— 安装器只在你的机器上从官方 release 获取并运行 NapCat 官方一键包(自动部署 QQ 与 NapCat,无需预装 QQ),不把它打进本项目产物;NapCat 自身遵循其许可证与条款。
67
+ - **不 fork DSH 内核** —— 本项目是一个 DSH bundle 插件,内核随 npm 官方源独立更新。
68
+
69
+ ---
70
+
71
+ ## 快速开始
72
+
73
+ ### 🐧 Linux 云服务器(Ubuntu / Debian,2H2G 友好)
74
+
75
+ ```bash
76
+ curl -fsSL https://raw.githubusercontent.com/EROQIN/kinstra-neko/main/installer/linux/install.sh | sudo bash
77
+ # 国内网络慢可: sudo CN_MIRROR=1 bash install.sh
78
+ ```
79
+
80
+ 脚本会:装 Node 22(内存不足自动加 2G swap,swappiness=10)→ 全局装 DSH → 建独立 `kinstra-neko` profile 并装本插件 → 引导装 NapCat(无桌面环境配 Xvfb 扫码)→ 注册 `kinstra-neko` systemd 服务(开机自启、`--max-old-space-size=700`)。
81
+
82
+ ### 🪟 Windows
83
+
84
+ ```powershell
85
+ irm https://raw.githubusercontent.com/EROQIN/kinstra-neko/main/installer/windows/install.ps1 | iex
86
+ ```
87
+
88
+ 脚本会自动下载运行 NapCat 官方一键包(`NapCat.Shell.Windows.OneKey`,连 QQNT 一起装好,无需预装 QQ),并把 NapCat 目录、`run-napcat.bat` 薄壳与 OneBot 配置都准备好;也支持在「连接」页改接任意 OneBot v11 实现。
89
+
90
+ ### 🧑‍💻 已有 Node 环境,只想装插件
91
+
92
+ ```bash
93
+ npx kinstra-neko@latest init # 建 profile、装 bundle、引导写凭据
94
+ dsh --profile kinstra-neko web --port 3090
95
+ ```
96
+
97
+ ### 装完之后 · 3 分钟向导
98
+
99
+ 1. 浏览器打开 `http://127.0.0.1:3090`(云服务器用 `http://<IP>:3090`,注意安全组放行)。
100
+ 2. 按向导填:**猫娘小号 QQ**、**你的 QQ(主人)**。
101
+ 3. 在「连接」页点「生成 OneBot 配置」再点「启动 NapCat」,用猫娘小号扫弹出窗口里的二维码登录;插件会自动核对登录号与配置是否一致。
102
+ 4. 在 DSH 设置里配置模型服务商与 API Key(DeepSeek 官方或任意 OpenAI 兼容端点)。
103
+ 5. 在「群聊与权限」里加白名单群。
104
+ 6. **保持试运行**,在群里 @ 她,到面板「试运行」页看她本来会说什么;确认对味,再关试运行开麦。
105
+
106
+ ---
107
+
108
+ ## 模型配置
109
+
110
+ DSH 支持 DeepSeek 官方与任意 OpenAI 兼容网关。密钥写 `~/.dsh/.credentials.yaml`(权限 0600,永不明文回传面板),provider / 模型表写 profile 的 cordis 配置(DSH 设置页图形化完成,安装器也会引导)。
111
+
112
+ 最小 OpenAI 兼容配置示例(`dsh-llm-pi-ai` 风格):
113
+
114
+ ```yaml
115
+ - name: '@deepseek-ai/dsh-llm-pi-ai'
116
+ config:
117
+ providers:
118
+ deepseek:
119
+ api: openai-completions
120
+ baseURL: https://api.deepseek.com/v1
121
+ apiKeyEnv: DEEPSEEK_API_KEY
122
+ models:
123
+ - id: deepseek-chat
124
+ contextWindow: 131072
125
+ ```
126
+
127
+ 2G 服务器建议选上下文适中、单价低的对话模型;群会话 preset 已把 compaction 阈值调小(6144 字符触发、工具结果裁剪),控制长会话内存。
128
+
129
+ ---
130
+
131
+ ## 架构
132
+
133
+ <img src="./assets/architecture.svg" alt="Kinstra Neko 架构图" width="860"/>
134
+
135
+ ```
136
+ QQ 小号
137
+ ▲ OneBot v11(NapCat 安装器自动托管 / 任意兼容实现;WS 收事件 + HTTP 调接口)
138
+ src/onebot, src/gateway
139
+ ▼
140
+ chat hub(白名单 → 群聊决策 → 停顿分段 → 引用/@/拍一拍)
141
+ ▼
142
+ DSH Session(一群一会话,kinstra-social / kinstra-private 两个 preset)
143
+ + systemPrompt.variable 注入人格、context 注入记忆、会话级 remember/recall 工具
144
+ ▼ turn/end + assistant/message
145
+ 回复拆条 → 气泡延迟 → OneBot send_msg(试运行则只记 observe)
146
+ ```
147
+
148
+ | 模块 | 职责 |
149
+ | --- | --- |
150
+ | `src/config` | zod 配置 schema、深合并默认、面板密钥脱敏 |
151
+ | `src/onebot` | WS 指数退避重连、HTTP/WS 双通道、消息段解析(@/引用/图片/表情/语音/合并转发/卡片) |
152
+ | `src/chat/group-policy.ts` | 纯函数发言决策,单测覆盖(必回 / 插话 / engaged / 冷却 / 静默 / burst) |
153
+ | `src/chat/hub.ts` | 消息中枢:停顿聚合、编号引用、回复拆气泡与打字延迟 |
154
+ | `src/memory/store.ts` | JSONL + 热群 LRU 倒排索引、BM25、遗忘曲线、人物档案 |
155
+ | `src/persona` | Markdown 人格卡 CRUD、社交/私聊输出规则 |
156
+ | `src/sessions` | DSH 会话生命周期与记忆工具(`kinstra_remember` / `kinstra_recall`) |
157
+ | `src/gateway` | NapCat 安装目录探测、OneBot 配置生成、进程启停与健康检查(loopback + 令牌) |
158
+ | `src/client` | 品牌插槽、配置面板、首启向导 |
159
+
160
+ ---
161
+
162
+ ## 配置在哪
163
+
164
+ | 内容 | 位置 |
165
+ | --- | --- |
166
+ | 插件全部配置 | `~/.dsh/plugins-data/kinstra-neko/config.json`(一般只通过面板改) |
167
+ | 人格卡 | `~/.dsh/plugins-data/kinstra-neko/personas/*.md` |
168
+ | 长期记忆 | `~/.dsh/plugins-data/kinstra-neko/memory/facts.jsonl` |
169
+ | 试运行记录 | `~/.dsh/plugins-data/kinstra-neko/observe.jsonl` |
170
+ | 聊天 → DSH 会话映射 | `~/.dsh/plugins-data/kinstra-neko/sessions.json` |
171
+ | 模型 API Key | `~/.dsh/.credentials.yaml`(0600) |
172
+ | profile / bundle | `~/.dsh/profiles/kinstra-neko/` |
173
+
174
+ ---
175
+
176
+ ## 调整猫娘的性格
177
+
178
+ 面板「群聊与权限」:
179
+
180
+ - **插话概率 / 相关度门槛 / 每小时上限 / 冷却 / 静默时段**:控制她多爱说话。出厂偏安静。
181
+ - **叫醒词、兴趣词**:叫她名字必回;话题命中兴趣词更容易插话。
182
+ - **按群覆盖**:`config.group.overrides[群号]` 可为单个群单独调一套。
183
+
184
+ 面板「人格」:直接编辑 Markdown 人格卡。输出约定写在卡里:
185
+
186
+ | 写法 | 含义 |
187
+ | --- | --- |
188
+ | 换行 | 拆成多条气泡 |
189
+ | `[沉默]` | 本轮不说话 |
190
+ | `<reply:编号>` | 引用回复对应编号的群消息 |
191
+ | `<at:QQ号>` | 真 @ 群成员 |
192
+ | `<poke>` | 拍一拍 |
193
+
194
+ 完整规则见 [`personas/neko.md`](./personas/neko.md) 与 `src/persona/rules.ts`。
195
+
196
+ ---
197
+
198
+ ## 常见运维(Linux)
199
+
200
+ ```bash
201
+ sudo systemctl status kinstra-neko # 状态
202
+ sudo systemctl restart kinstra-neko # 重启
203
+ journalctl -u kinstra-neko -f # 实时日志
204
+ free -h # 确认 swap 已生效
205
+ ```
206
+
207
+ 面板「日志」标签也能看到插件侧最近 300 条分级日志;连接页提供一键测试与重连。
208
+
209
+ ---
210
+
211
+ ## 开发
212
+
213
+ ```bash
214
+ npm install --legacy-peer-deps # DSH 是 pnpm 生态,npm 需此开关
215
+ npm run typecheck # tsc --noEmit
216
+ npm test # vitest,56 个用例
217
+ npm run build # tsdown 双入口:lib/index.js + lib/client.js
218
+
219
+ # 装进本地 DSH 调试:
220
+ dsh kinstra-neko --from-default-profile web --dump-config >/dev/null # 仅首次
221
+ dsh plugin --profile kinstra-neko add "link:$PWD"
222
+ dsh --profile kinstra-neko web --port 3090
223
+ ```
224
+
225
+ 无真实 QQ 环境时,决策、消息解析、记忆、回复拆条均为纯函数,直接跑单测即可;
226
+ 端到端链路有内存假 DSH host + 假 OneBot 的 e2e 测试覆盖。
227
+
228
+ ---
229
+
230
+ ## 许可证
231
+
232
+ [MIT](./LICENSE)。本项目代码不包含任何第三方 QQ 美术素材;品牌图标与横幅均为原创 SVG。
233
+ 接入的各协议端、DeepSeek Harness 各自遵循其许可证与条款。
@@ -0,0 +1,76 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 452" width="960" height="452"
2
+ role="img" aria-label="Kinstra Neko 架构:QQ 群消息经 OneBot 协议端进入插件,由群聊决策、消息中枢、记忆与人格处理,进入一群一会话的 DSH Session,回复再送回 QQ">
3
+ <defs>
4
+ <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
5
+ <stop offset="0" stop-color="#100719"/>
6
+ <stop offset="1" stop-color="#0b0d1c"/>
7
+ </linearGradient>
8
+ <filter id="gl" x="-60%" y="-60%" width="220%" height="220%">
9
+ <feGaussianBlur stdDeviation="3" result="b"/>
10
+ <feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
11
+ </filter>
12
+ <marker id="ahPink" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">
13
+ <path d="M0,0 L8,4.5 L0,9 Z" fill="#ff7eb3"/>
14
+ </marker>
15
+ <marker id="ahCyan" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">
16
+ <path d="M0,0 L8,4.5 L0,9 Z" fill="#67e8f9"/>
17
+ </marker>
18
+ <marker id="ahViolet" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">
19
+ <path d="M0,0 L8,4.5 L0,9 Z" fill="#a78bfa"/>
20
+ </marker>
21
+ </defs>
22
+
23
+ <rect width="960" height="452" rx="16" fill="url(#bg)"/>
24
+
25
+ <g font-family="-apple-system,'PingFang SC','Microsoft YaHei',sans-serif">
26
+ <!-- ① QQ -->
27
+ <rect x="180" y="24" width="600" height="56" rx="12" fill="#16202c" stroke="#67e8f9" stroke-width="1.5"/>
28
+ <text x="208" y="50" font-size="17" font-weight="700" fill="#cdf3ff">QQ 群聊 · 小号(User 账号)</text>
29
+ <text x="208" y="70" font-size="12.5" fill="#7f93ad">群消息 / @ / 引用 / 拍一拍</text>
30
+ <text x="752" y="58" text-anchor="end" font-size="12" fill="#67e8f9">①</text>
31
+
32
+ <line x1="480" y1="82" x2="480" y2="102" stroke="#67e8f9" stroke-width="2" marker-end="url(#ahCyan)"/>
33
+
34
+ <!-- ② 协议端 -->
35
+ <rect x="180" y="104" width="600" height="56" rx="12" fill="#1a1430" stroke="#a78bfa" stroke-width="1.5"/>
36
+ <text x="208" y="130" font-size="17" font-weight="700" fill="#ddd2ff">OneBot v11 协议端</text>
37
+ <text x="208" y="150" font-size="12.5" fill="#9486b8">NapCat(安装器自动托管) · 任意 OneBot v11 兼容实现</text>
38
+ <text x="752" y="138" text-anchor="end" font-size="12" fill="#a78bfa">②</text>
39
+
40
+ <line x1="480" y1="162" x2="480" y2="182" stroke="#a78bfa" stroke-width="2" marker-end="url(#ahViolet)"/>
41
+
42
+ <!-- ③ 插件主体 -->
43
+ <rect x="180" y="184" width="600" height="148" rx="14" fill="#1d1028" stroke="#ff7eb3" stroke-width="2" filter="url(#gl)"/>
44
+ <text x="208" y="212" font-size="17" font-weight="800" fill="#ffc2dd">Kinstra Neko 插件(DSH bundle)</text>
45
+
46
+ <rect x="208" y="226" width="264" height="42" rx="9" fill="#241430" stroke="#ff7eb3" stroke-opacity="0.55"/>
47
+ <text x="224" y="245" font-size="13.5" font-weight="700" fill="#ffd0e4">群聊决策</text>
48
+ <text x="224" y="261" font-size="11" fill="#a486a6">@必回 · 相关度插话 · 冷却/静默/防刷屏</text>
49
+
50
+ <rect x="488" y="226" width="264" height="42" rx="9" fill="#241430" stroke="#a78bfa" stroke-opacity="0.55"/>
51
+ <text x="504" y="245" font-size="13.5" font-weight="700" fill="#e4d8ff">消息中枢</text>
52
+ <text x="504" y="261" font-size="11" fill="#a486a6">停顿分段 · 拆气泡 · 真@展开 · 拍一拍</text>
53
+
54
+ <rect x="208" y="276" width="264" height="42" rx="9" fill="#241430" stroke="#67e8f9" stroke-opacity="0.55"/>
55
+ <text x="224" y="295" font-size="13.5" font-weight="700" fill="#cdf3ff">长期记忆</text>
56
+ <text x="224" y="311" font-size="11" fill="#8fa6bc">中文二元 BM25 · 遗忘曲线 · 热群 LRU</text>
57
+
58
+ <rect x="488" y="276" width="264" height="42" rx="9" fill="#241430" stroke="#ff7eb3" stroke-opacity="0.55"/>
59
+ <text x="504" y="295" font-size="13.5" font-weight="700" fill="#ffd0e4">人格与权限</text>
60
+ <text x="504" y="311" font-size="11" fill="#a486a6">Markdown 人格卡 · 双 preset 工具隔离</text>
61
+
62
+ <line x1="480" y1="334" x2="480" y2="354" stroke="#ff7eb3" stroke-width="2" marker-end="url(#ahPink)"/>
63
+
64
+ <!-- ④ DSH Session -->
65
+ <rect x="180" y="356" width="600" height="56" rx="12" fill="#1a1430" stroke="#cbb8ff" stroke-width="1.5"/>
66
+ <text x="208" y="382" font-size="17" font-weight="700" fill="#e6dcff">DeepSeek Harness · 一群一会话</text>
67
+ <text x="208" y="402" font-size="12.5" fill="#9486b8">kinstra-social(纯聊天) / kinstra-private(主人完整工具) · 模型与密钥随 DSH 配置</text>
68
+ <text x="752" y="390" text-anchor="end" font-size="12" fill="#cbb8ff">④</text>
69
+
70
+ <!-- ⑤ 回流:回复送回 QQ;试运行只记录 -->
71
+ <path d="M 780 384 C 900 384 906 52 782 52" fill="none" stroke="#ff7eb3" stroke-width="2"
72
+ stroke-dasharray="6 5" marker-end="url(#ahPink)"/>
73
+ <text x="893" y="232" text-anchor="middle" font-size="12" fill="#ff9ecb"
74
+ transform="rotate(90 893 232)">回复拆条 → send_msg(试运行只写 observe)</text>
75
+ </g>
76
+ </svg>
@@ -0,0 +1,233 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 380" width="1280" height="380"
2
+ role="img" aria-label="Kinstra Neko:消息星座凝成猫娘,@ 信标点亮耳尖">
3
+ <title>Kinstra Neko</title>
4
+ <defs>
5
+ <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
6
+ <stop offset="0" stop-color="#100719"/>
7
+ <stop offset="0.42" stop-color="#1d0e2b"/>
8
+ <stop offset="0.75" stop-color="#150b24"/>
9
+ <stop offset="1" stop-color="#0a0f1e"/>
10
+ </linearGradient>
11
+ <radialGradient id="blobPink" cx="0.5" cy="0.5" r="0.5">
12
+ <stop offset="0" stop-color="#ff7eb3" stop-opacity="0.20"/>
13
+ <stop offset="1" stop-color="#ff7eb3" stop-opacity="0"/>
14
+ </radialGradient>
15
+ <radialGradient id="blobViolet" cx="0.5" cy="0.5" r="0.5">
16
+ <stop offset="0" stop-color="#8b6fe8" stop-opacity="0.16"/>
17
+ <stop offset="1" stop-color="#8b6fe8" stop-opacity="0"/>
18
+ </radialGradient>
19
+ <linearGradient id="tGrad" x1="0" y1="0" x2="1" y2="0.3">
20
+ <stop offset="0" stop-color="#ff9ecb"/>
21
+ <stop offset="0.55" stop-color="#cbb8ff"/>
22
+ <stop offset="1" stop-color="#8fe9ff"/>
23
+ </linearGradient>
24
+ <linearGradient id="accGrad" x1="0" y1="0" x2="1" y2="0">
25
+ <stop offset="0" stop-color="#ff7eb3"/>
26
+ <stop offset="1" stop-color="#67e8f9"/>
27
+ </linearGradient>
28
+ <radialGradient id="tg" cx="0.5" cy="0.5" r="0.5">
29
+ <stop offset="0" stop-color="#ff7eb3" stop-opacity="0.22"/>
30
+ <stop offset="1" stop-color="#ff7eb3" stop-opacity="0"/>
31
+ </radialGradient>
32
+ <linearGradient id="beamGrad" x1="0" y1="0" x2="1" y2="0">
33
+ <stop offset="0" stop-color="#ffffff" stop-opacity="0"/>
34
+ <stop offset="0.5" stop-color="#ffd9ec" stop-opacity="0.55"/>
35
+ <stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
36
+ </linearGradient>
37
+ <clipPath id="clip"><rect width="1280" height="380"/></clipPath>
38
+ <filter id="glow" x="-80%" y="-80%" width="260%" height="260%">
39
+ <feGaussianBlur stdDeviation="6" result="b"/>
40
+ <feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
41
+ </filter>
42
+ <filter id="glow2" x="-120%" y="-120%" width="340%" height="340%">
43
+ <feGaussianBlur stdDeviation="2.6" result="b"/>
44
+ <feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
45
+ </filter>
46
+ </defs>
47
+
48
+ <!-- 深空底 + 双色辉光 -->
49
+ <rect width="1280" height="380" fill="url(#bg)"/>
50
+ <ellipse cx="1010" cy="180" rx="360" ry="210" fill="url(#blobPink)"/>
51
+ <ellipse cx="240" cy="330" rx="330" ry="170" fill="url(#blobViolet)"/>
52
+ <g stroke="#ffffff" stroke-opacity="0.045" stroke-width="1">
53
+ <line x1="0" y1="96" x2="1280" y2="96"/>
54
+ <line x1="0" y1="196" x2="1280" y2="196"/>
55
+ <line x1="0" y1="296" x2="1280" y2="296"/>
56
+ </g>
57
+
58
+ <!-- 全幅粒子场(环境层) -->
59
+ <g>
60
+ <circle cx="195" cy="24" r="1.0" fill="#a7f0ff" opacity="0.2"><animate attributeName="opacity" values="0.2;0.81;0.2" dur="4.7s" begin="0.9s" repeatCount="indefinite"/></circle>
61
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;-3 -4;0 0" dur="8.9s" begin="4.4s" repeatCount="indefinite"/><circle cx="677" cy="339" r="1.8" fill="#a7f0ff" opacity="0.15"><animate attributeName="opacity" values="0.15;0.83;0.15" dur="4.0s" begin="4.4s" repeatCount="indefinite"/></circle></g>
62
+ <circle cx="1082" cy="257" r="1.3" fill="#ffe9f5" opacity="0.16"><animate attributeName="opacity" values="0.16;0.54;0.16" dur="3.1s" begin="3.9s" repeatCount="indefinite"/></circle>
63
+ <circle cx="549" cy="33" r="1.9" fill="#a7f0ff" opacity="0.13"><animate attributeName="opacity" values="0.13;0.84;0.13" dur="3.6s" begin="2.4s" repeatCount="indefinite"/></circle>
64
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;-7 -6;0 0" dur="12.6s" begin="3.5s" repeatCount="indefinite"/><circle cx="1225" cy="263" r="1.2" fill="#ffc7e0" opacity="0.26"><animate attributeName="opacity" values="0.26;0.56;0.26" dur="4.7s" begin="3.5s" repeatCount="indefinite"/></circle></g>
65
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;-5 -8;0 0" dur="12.2s" begin="4.9s" repeatCount="indefinite"/><circle cx="269" cy="100" r="2.4" fill="#ffc7e0" opacity="0.13"><animate attributeName="opacity" values="0.13;0.92;0.13" dur="4.4s" begin="4.9s" repeatCount="indefinite"/></circle></g>
66
+ <circle cx="927" cy="263" r="1.9" fill="#ffe9f5" opacity="0.26"><animate attributeName="opacity" values="0.26;0.72;0.26" dur="6.1s" begin="3.2s" repeatCount="indefinite"/></circle>
67
+ <circle cx="261" cy="284" r="2.3" fill="#ffc7e0" opacity="0.29"><animate attributeName="opacity" values="0.29;0.94;0.29" dur="4.1s" begin="2.8s" repeatCount="indefinite"/></circle>
68
+ <circle cx="1148" cy="180" r="2.5" fill="#ffc7e0" opacity="0.14"><animate attributeName="opacity" values="0.14;0.62;0.14" dur="6.7s" begin="1.7s" repeatCount="indefinite"/></circle>
69
+ <circle cx="1101" cy="187" r="2.3" fill="#cbb8ff" opacity="0.14"><animate attributeName="opacity" values="0.14;0.58;0.14" dur="6.0s" begin="2.0s" repeatCount="indefinite"/></circle>
70
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;-7 -7;0 0" dur="12.9s" begin="4.0s" repeatCount="indefinite"/><circle cx="404" cy="10" r="1.6" fill="#ffe9f5" opacity="0.29"><animate attributeName="opacity" values="0.29;0.84;0.29" dur="5.2s" begin="4.0s" repeatCount="indefinite"/></circle></g>
71
+ <circle cx="810" cy="206" r="2.4" fill="#ffc7e0" opacity="0.21"><animate attributeName="opacity" values="0.21;0.75;0.21" dur="5.3s" begin="0.8s" repeatCount="indefinite"/></circle>
72
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;7 -8;0 0" dur="10.4s" begin="2.4s" repeatCount="indefinite"/><circle cx="145" cy="273" r="2.5" fill="#ffe9f5" opacity="0.11"><animate attributeName="opacity" values="0.11;0.74;0.11" dur="4.3s" begin="2.4s" repeatCount="indefinite"/></circle></g>
73
+ <circle cx="523" cy="39" r="2.5" fill="#ffc7e0" opacity="0.08"><animate attributeName="opacity" values="0.08;0.53;0.08" dur="4.5s" begin="3.9s" repeatCount="indefinite"/></circle>
74
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;6 -4;0 0" dur="10.1s" begin="0.4s" repeatCount="indefinite"/><circle cx="249" cy="319" r="1.4" fill="#cbb8ff" opacity="0.3"><animate attributeName="opacity" values="0.3;0.66;0.3" dur="4.0s" begin="0.4s" repeatCount="indefinite"/></circle></g>
75
+ <circle cx="49" cy="338" r="2.3" fill="#cbb8ff" opacity="0.16"><animate attributeName="opacity" values="0.16;0.84;0.16" dur="6.6s" begin="2.4s" repeatCount="indefinite"/></circle>
76
+ <circle cx="1064" cy="52" r="1.7" fill="#a7f0ff" opacity="0.12"><animate attributeName="opacity" values="0.12;0.92;0.12" dur="5.8s" begin="2.2s" repeatCount="indefinite"/></circle>
77
+ <circle cx="1166" cy="122" r="1.6" fill="#ffc7e0" opacity="0.25"><animate attributeName="opacity" values="0.25;0.66;0.25" dur="4.5s" begin="4.4s" repeatCount="indefinite"/></circle>
78
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;-6 -8;0 0" dur="12.9s" begin="4.4s" repeatCount="indefinite"/><circle cx="362" cy="299" r="2.2" fill="#ffc7e0" opacity="0.22"><animate attributeName="opacity" values="0.22;0.75;0.22" dur="5.5s" begin="4.4s" repeatCount="indefinite"/></circle></g>
79
+ <circle cx="1183" cy="303" r="1.3" fill="#a7f0ff" opacity="0.2"><animate attributeName="opacity" values="0.2;0.92;0.2" dur="4.1s" begin="0.2s" repeatCount="indefinite"/></circle>
80
+ <circle cx="1204" cy="276" r="2.0" fill="#cbb8ff" opacity="0.28"><animate attributeName="opacity" values="0.28;0.6;0.28" dur="6.0s" begin="1.9s" repeatCount="indefinite"/></circle>
81
+ <circle cx="1099" cy="366" r="2.2" fill="#a7f0ff" opacity="0.29"><animate attributeName="opacity" values="0.29;0.58;0.29" dur="5.6s" begin="3.4s" repeatCount="indefinite"/></circle>
82
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;7 -4;0 0" dur="7.4s" begin="4.0s" repeatCount="indefinite"/><circle cx="354" cy="156" r="1.0" fill="#ffe9f5" opacity="0.29"><animate attributeName="opacity" values="0.29;0.87;0.29" dur="2.6s" begin="4.0s" repeatCount="indefinite"/></circle></g>
83
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;-7 -7;0 0" dur="12.5s" begin="2.3s" repeatCount="indefinite"/><circle cx="60" cy="199" r="2.3" fill="#a7f0ff" opacity="0.21"><animate attributeName="opacity" values="0.21;0.54;0.21" dur="3.3s" begin="2.3s" repeatCount="indefinite"/></circle></g>
84
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;-6 -8;0 0" dur="8.7s" begin="3.2s" repeatCount="indefinite"/><circle cx="161" cy="323" r="1.8" fill="#ffe9f5" opacity="0.09"><animate attributeName="opacity" values="0.09;0.81;0.09" dur="5.0s" begin="3.2s" repeatCount="indefinite"/></circle></g>
85
+ <circle cx="665" cy="339" r="2.5" fill="#a7f0ff" opacity="0.12"><animate attributeName="opacity" values="0.12;0.61;0.12" dur="4.8s" begin="1.8s" repeatCount="indefinite"/></circle>
86
+ <circle cx="1111" cy="339" r="2.5" fill="#ffc7e0" opacity="0.22"><animate attributeName="opacity" values="0.22;0.56;0.22" dur="5.3s" begin="4.8s" repeatCount="indefinite"/></circle>
87
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;-4 -9;0 0" dur="12.0s" begin="0.3s" repeatCount="indefinite"/><circle cx="1127" cy="293" r="2.4" fill="#cbb8ff" opacity="0.24"><animate attributeName="opacity" values="0.24;0.73;0.24" dur="4.0s" begin="0.3s" repeatCount="indefinite"/></circle></g>
88
+ <circle cx="949" cy="193" r="0.9" fill="#a7f0ff" opacity="0.15"><animate attributeName="opacity" values="0.15;0.84;0.15" dur="2.7s" begin="4.7s" repeatCount="indefinite"/></circle>
89
+ <circle cx="752" cy="359" r="2.1" fill="#cbb8ff" opacity="0.12"><animate attributeName="opacity" values="0.12;0.92;0.12" dur="3.6s" begin="4.7s" repeatCount="indefinite"/></circle>
90
+ <circle cx="711" cy="244" r="2.5" fill="#ffe9f5" opacity="0.11"><animate attributeName="opacity" values="0.11;0.84;0.11" dur="4.1s" begin="3.8s" repeatCount="indefinite"/></circle>
91
+ <circle cx="1061" cy="277" r="2.0" fill="#cbb8ff" opacity="0.17"><animate attributeName="opacity" values="0.17;0.87;0.17" dur="3.0s" begin="0.8s" repeatCount="indefinite"/></circle>
92
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;5 -8;0 0" dur="9.5s" begin="2.9s" repeatCount="indefinite"/><circle cx="219" cy="353" r="1.2" fill="#cbb8ff" opacity="0.08"><animate attributeName="opacity" values="0.08;0.52;0.08" dur="4.9s" begin="2.9s" repeatCount="indefinite"/></circle></g>
93
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;4 -7;0 0" dur="11.5s" begin="2.9s" repeatCount="indefinite"/><circle cx="452" cy="62" r="1.6" fill="#cbb8ff" opacity="0.12"><animate attributeName="opacity" values="0.12;0.69;0.12" dur="3.3s" begin="2.9s" repeatCount="indefinite"/></circle></g>
94
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;4 -8;0 0" dur="8.4s" begin="3.0s" repeatCount="indefinite"/><circle cx="588" cy="23" r="2.4" fill="#a7f0ff" opacity="0.22"><animate attributeName="opacity" values="0.22;0.61;0.22" dur="4.3s" begin="3.0s" repeatCount="indefinite"/></circle></g>
95
+ <circle cx="841" cy="177" r="1.5" fill="#ffe9f5" opacity="0.08"><animate attributeName="opacity" values="0.08;0.55;0.08" dur="6.4s" begin="2.6s" repeatCount="indefinite"/></circle>
96
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;6 -8;0 0" dur="8.1s" begin="0.9s" repeatCount="indefinite"/><circle cx="139" cy="351" r="1.9" fill="#cbb8ff" opacity="0.25"><animate attributeName="opacity" values="0.25;0.66;0.25" dur="2.9s" begin="0.9s" repeatCount="indefinite"/></circle></g>
97
+ <circle cx="1254" cy="363" r="1.7" fill="#ffc7e0" opacity="0.22"><animate attributeName="opacity" values="0.22;0.91;0.22" dur="6.7s" begin="2.7s" repeatCount="indefinite"/></circle>
98
+ <circle cx="488" cy="308" r="2.6" fill="#a7f0ff" opacity="0.14"><animate attributeName="opacity" values="0.14;0.51;0.14" dur="4.9s" begin="4.2s" repeatCount="indefinite"/></circle>
99
+ <circle cx="1259" cy="122" r="1.4" fill="#ffc7e0" opacity="0.28"><animate attributeName="opacity" values="0.28;0.53;0.28" dur="4.0s" begin="3.5s" repeatCount="indefinite"/></circle>
100
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;-5 -5;0 0" dur="12.4s" begin="3.6s" repeatCount="indefinite"/><circle cx="182" cy="319" r="1.9" fill="#a7f0ff" opacity="0.22"><animate attributeName="opacity" values="0.22;0.62;0.22" dur="4.1s" begin="3.6s" repeatCount="indefinite"/></circle></g>
101
+ <circle cx="869" cy="290" r="1.2" fill="#cbb8ff" opacity="0.22"><animate attributeName="opacity" values="0.22;0.78;0.22" dur="5.1s" begin="0.5s" repeatCount="indefinite"/></circle>
102
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;6 -6;0 0" dur="10.4s" begin="3.3s" repeatCount="indefinite"/><circle cx="470" cy="64" r="1.8" fill="#cbb8ff" opacity="0.08"><animate attributeName="opacity" values="0.08;0.75;0.08" dur="2.7s" begin="3.3s" repeatCount="indefinite"/></circle></g>
103
+ <circle cx="668" cy="282" r="1.7" fill="#ffc7e0" opacity="0.21"><animate attributeName="opacity" values="0.21;0.61;0.21" dur="5.1s" begin="3.3s" repeatCount="indefinite"/></circle>
104
+ <circle cx="1075" cy="114" r="2.4" fill="#ffe9f5" opacity="0.09"><animate attributeName="opacity" values="0.09;0.71;0.09" dur="4.0s" begin="1.9s" repeatCount="indefinite"/></circle>
105
+ <circle cx="949" cy="83" r="1.2" fill="#ffe9f5" opacity="0.24"><animate attributeName="opacity" values="0.24;0.58;0.24" dur="5.3s" begin="4.1s" repeatCount="indefinite"/></circle>
106
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;5 -7;0 0" dur="7.8s" begin="3.8s" repeatCount="indefinite"/><circle cx="1205" cy="284" r="1.7" fill="#cbb8ff" opacity="0.21"><animate attributeName="opacity" values="0.21;0.52;0.21" dur="2.8s" begin="3.8s" repeatCount="indefinite"/></circle></g>
107
+ <circle cx="501" cy="321" r="1.1" fill="#cbb8ff" opacity="0.29"><animate attributeName="opacity" values="0.29;0.73;0.29" dur="3.6s" begin="2.3s" repeatCount="indefinite"/></circle>
108
+ <circle cx="834" cy="342" r="1.4" fill="#ffc7e0" opacity="0.11"><animate attributeName="opacity" values="0.11;0.82;0.11" dur="5.0s" begin="2.5s" repeatCount="indefinite"/></circle>
109
+ <circle cx="706" cy="331" r="1.7" fill="#cbb8ff" opacity="0.26"><animate attributeName="opacity" values="0.26;0.61;0.26" dur="6.7s" begin="2.1s" repeatCount="indefinite"/></circle>
110
+ <circle cx="89" cy="231" r="2.2" fill="#a7f0ff" opacity="0.23"><animate attributeName="opacity" values="0.23;0.65;0.23" dur="5.5s" begin="1.1s" repeatCount="indefinite"/></circle>
111
+ <circle cx="808" cy="51" r="1.1" fill="#ffc7e0" opacity="0.19"><animate attributeName="opacity" values="0.19;0.79;0.19" dur="3.5s" begin="2.4s" repeatCount="indefinite"/></circle>
112
+ <circle cx="1087" cy="363" r="2.2" fill="#ffe9f5" opacity="0.13"><animate attributeName="opacity" values="0.13;0.55;0.13" dur="4.9s" begin="4.8s" repeatCount="indefinite"/></circle>
113
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;3 -4;0 0" dur="11.3s" begin="4.7s" repeatCount="indefinite"/><circle cx="756" cy="103" r="1.0" fill="#ffc7e0" opacity="0.08"><animate attributeName="opacity" values="0.08;0.53;0.08" dur="4.3s" begin="4.7s" repeatCount="indefinite"/></circle></g>
114
+ <circle cx="82" cy="68" r="1.5" fill="#ffc7e0" opacity="0.23"><animate attributeName="opacity" values="0.23;0.81;0.23" dur="5.5s" begin="2.0s" repeatCount="indefinite"/></circle>
115
+ <circle cx="539" cy="323" r="2.1" fill="#ffc7e0" opacity="0.22"><animate attributeName="opacity" values="0.22;0.88;0.22" dur="2.7s" begin="2.8s" repeatCount="indefinite"/></circle>
116
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;7 -8;0 0" dur="11.4s" begin="3.8s" repeatCount="indefinite"/><circle cx="560" cy="24" r="1.3" fill="#cbb8ff" opacity="0.3"><animate attributeName="opacity" values="0.3;0.6;0.3" dur="6.3s" begin="3.8s" repeatCount="indefinite"/></circle></g>
117
+ <circle cx="672" cy="41" r="1.7" fill="#ffe9f5" opacity="0.28"><animate attributeName="opacity" values="0.28;0.79;0.28" dur="4.8s" begin="1.1s" repeatCount="indefinite"/></circle>
118
+ <circle cx="1224" cy="16" r="2.3" fill="#ffe9f5" opacity="0.09"><animate attributeName="opacity" values="0.09;0.67;0.09" dur="2.9s" begin="2.9s" repeatCount="indefinite"/></circle>
119
+ <g><animateTransform attributeName="transform" type="translate" values="0 0;-5 -6;0 0" dur="7.2s" begin="4.7s" repeatCount="indefinite"/><circle cx="421" cy="204" r="1.8" fill="#ffe9f5" opacity="0.13"><animate attributeName="opacity" values="0.13;0.63;0.13" dur="6.7s" begin="4.7s" repeatCount="indefinite"/></circle></g>
120
+ <circle cx="685" cy="215" r="1.1" fill="#ffc7e0" opacity="0.13"><animate attributeName="opacity" values="0.13;0.63;0.13" dur="4.5s" begin="2.5s" repeatCount="indefinite"/></circle>
121
+ <circle cx="1166" cy="164" r="1.0" fill="#ffc7e0" opacity="0.21"><animate attributeName="opacity" values="0.21;0.89;0.21" dur="5.5s" begin="2.6s" repeatCount="indefinite"/></circle>
122
+ </g>
123
+
124
+ <!-- 必制动效层 A:横跨整幅的扫光(行程 1680px) -->
125
+ <g clip-path="url(#clip)">
126
+ <rect x="-260" y="-140" width="230" height="660" transform="skewX(-20)"
127
+ fill="url(#beamGrad)" opacity="0.10">
128
+ <animate attributeName="x" values="-260;1420" dur="8s" begin="2s" repeatCount="indefinite"/>
129
+ <animate attributeName="opacity" values="0;0.12;0.12;0" keyTimes="0;0.12;0.85;1"
130
+ dur="8s" begin="2s" repeatCount="indefinite"/>
131
+ </rect>
132
+ </g>
133
+
134
+ <!-- 左栏:标题区(标题为文档内第一个 text,决定 Rule 7 标题窗口) -->
135
+ <ellipse cx="300" cy="146" rx="330" ry="78" fill="url(#tg)">
136
+ <animate attributeName="opacity" values="0.55;1;0.55" dur="4.6s" repeatCount="indefinite"/>
137
+ </ellipse>
138
+ <text x="72" y="150" font-family="-apple-system,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif"
139
+ font-size="72" font-weight="800" letter-spacing="1" fill="url(#tGrad)">Kinstra Neko</text>
140
+ <line x1="72" y1="180" x2="300" y2="180" stroke="url(#accGrad)" stroke-width="4"
141
+ stroke-linecap="round" filter="url(#glow2)" stroke-dasharray="228" stroke-dashoffset="228">
142
+ <animate attributeName="stroke-dashoffset" values="228;0" dur="1.8s"
143
+ begin="0.6s" fill="freeze"/>
144
+ </line>
145
+ <text x="72" y="214" font-family="-apple-system,'PingFang SC','Microsoft YaHei',sans-serif"
146
+ font-size="23" fill="#d3c0ec">住在 QQ 群里的猫娘</text>
147
+ <text x="72" y="244" font-family="-apple-system,'Segoe UI','PingFang SC',sans-serif"
148
+ font-size="15.5" letter-spacing="0.6" fill="#9586b3">OneBot v11 · DeepSeek Harness · 自部署 · 2H2G 友好</text>
149
+ <g font-family="-apple-system,'PingFang SC','Microsoft YaHei',sans-serif" font-size="14" font-weight="600">
150
+ <rect x="72" y="269" width="104" height="30" rx="15" fill="#1a1027" stroke="#ff7eb3" stroke-opacity="0.75"/>
151
+ <text x="124" y="289" text-anchor="middle" fill="#ffb3d4">低打扰社交</text>
152
+ <rect x="192" y="269" width="96" height="30" rx="15" fill="#1a1027" stroke="#a78bfa" stroke-opacity="0.75"/>
153
+ <text x="240" y="289" text-anchor="middle" fill="#cbb8ff">长期记忆</text>
154
+ <rect x="304" y="269" width="96" height="30" rx="15" fill="#1a1027" stroke="#67e8f9" stroke-opacity="0.75"/>
155
+ <text x="352" y="289" text-anchor="middle" fill="#9fe7ff">一键部署</text>
156
+ </g>
157
+
158
+ <!-- 右栏主视觉:消息星座凝成的猫娘(锚点 1000,190;生命动效全部嵌在内层 g) -->
159
+ <g transform="translate(1000,190)">
160
+ <g>
161
+ <animateTransform attributeName="transform" type="translate"
162
+ values="0 0;0 -10;0 0" keyTimes="0;0.5;1" dur="6s" repeatCount="indefinite"/>
163
+
164
+ <!-- 星座节点(消息事件) -->
165
+ <circle cx="-110" cy="-118" r="5" fill="#67e8f9" filter="url(#glow2)" opacity="0.9"><animate attributeName="r" values="5;5;9.5;5" keyTimes="0;0.55;0.62;1" dur="6s" repeatCount="indefinite"/><animate attributeName="opacity" values="0.85;0.85;1;0.85" keyTimes="0;0.55;0.62;1" dur="6s" repeatCount="indefinite"/></circle>
166
+ <circle cx="-52" cy="-56" r="4" fill="#ff7eb3" filter="url(#glow2)" opacity="0.9"><animate attributeName="opacity" values="0.35;1;0.35" dur="3.8s" begin="0.4s" repeatCount="indefinite"/></circle>
167
+ <circle cx="52" cy="-56" r="4" fill="#ff7eb3" filter="url(#glow2)" opacity="0.9"><animate attributeName="opacity" values="0.35;1;0.35" dur="3.1s" begin="0.9s" repeatCount="indefinite"/></circle>
168
+ <circle cx="110" cy="-118" r="5" fill="#67e8f9" filter="url(#glow2)" opacity="0.9"><animate attributeName="opacity" values="0.35;1;0.35" dur="3.8s" begin="1.4s" repeatCount="indefinite"/></circle>
169
+ <circle cx="84" cy="-30" r="3.5" fill="#a78bfa" filter="url(#glow2)" opacity="0.9"><animate attributeName="opacity" values="0.35;1;0.35" dur="2.4s" begin="0.2s" repeatCount="indefinite"/></circle>
170
+ <circle cx="108" cy="30" r="4" fill="#ff7eb3" filter="url(#glow2)" opacity="0.9"><animate attributeName="opacity" values="0.35;1;0.35" dur="2.4s" begin="1.1s" repeatCount="indefinite"/></circle>
171
+ <circle cx="62" cy="98" r="4" fill="#a78bfa" filter="url(#glow2)" opacity="0.9"><animate attributeName="opacity" values="0.35;1;0.35" dur="3.8s" begin="0.7s" repeatCount="indefinite"/></circle>
172
+ <circle cx="0" cy="114" r="4.5" fill="#ff7eb3" filter="url(#glow2)" opacity="0.9"><animate attributeName="opacity" values="0.35;1;0.35" dur="2.4s" begin="1.6s" repeatCount="indefinite"/></circle>
173
+ <circle cx="-62" cy="98" r="4" fill="#a78bfa" filter="url(#glow2)" opacity="0.9"><animate attributeName="opacity" values="0.35;1;0.35" dur="3.1s" begin="0.5s" repeatCount="indefinite"/></circle>
174
+ <circle cx="-108" cy="30" r="4" fill="#ff7eb3" filter="url(#glow2)" opacity="0.9"><animate attributeName="opacity" values="0.35;1;0.35" dur="2.4s" begin="1.2s" repeatCount="indefinite"/></circle>
175
+ <circle cx="-84" cy="-30" r="3.5" fill="#a78bfa" filter="url(#glow2)" opacity="0.9"><animate attributeName="opacity" values="0.35;1;0.35" dur="2.4s" begin="0.9s" repeatCount="indefinite"/></circle>
176
+ <circle cx="-195" cy="-30" r="3" fill="#67e8f9" filter="url(#glow2)" opacity="0.9"><animate attributeName="opacity" values="0.35;1;0.35" dur="2.4s" begin="2.0s" repeatCount="indefinite"/></circle>
177
+ <circle cx="185" cy="70" r="3" fill="#ff7eb3" filter="url(#glow2)" opacity="0.9"><animate attributeName="opacity" values="0.35;1;0.35" dur="3.8s" begin="2.6s" repeatCount="indefinite"/></circle>
178
+ <circle cx="-165" cy="140" r="2.5" fill="#cbb8ff" filter="url(#glow2)" opacity="0.9"><animate attributeName="opacity" values="0.35;1;0.35" dur="2.4s" begin="3.1s" repeatCount="indefinite"/></circle>
179
+
180
+ <!-- 粗霓虹猫耳轮廓,开场描线一次后常驻 -->
181
+ <path d="M -110 -118 L -52 -56 C -30 -65 30 -65 52 -56 L 110 -118
182
+ L 80 -40 C 116 -4 112 66 62 98 C 30 118 -30 118 -62 98
183
+ C -112 66 -116 -4 -80 -40 Z"
184
+ fill="none" stroke="#ff7eb3" stroke-width="7" stroke-linejoin="round"
185
+ stroke-linecap="round" filter="url(#glow)" pathLength="100"
186
+ stroke-dasharray="100" stroke-dashoffset="100">
187
+ <animate attributeName="stroke-dashoffset" values="100;0" dur="2.4s"
188
+ begin="0.3s" fill="freeze"/>
189
+ </path>
190
+
191
+ <!-- 内耳 -->
192
+ <path d="M -92 -96 L -66 -60" stroke="#cbb8ff" stroke-width="3.5" stroke-linecap="round" opacity="0.85"/>
193
+ <path d="M 92 -96 L 66 -60" stroke="#cbb8ff" stroke-width="3.5" stroke-linecap="round" opacity="0.85"/>
194
+
195
+ <!-- 眨眼(scale 放在第三层 g,远离 translate 基座) -->
196
+ <g transform="translate(-50,16)"><g>
197
+ <animateTransform attributeName="transform" type="scale"
198
+ values="1 1;1 1;1 0.12;1 1" keyTimes="0;0.86;0.93;1" dur="5s" repeatCount="indefinite"/>
199
+ <path d="M -20 0 Q 0 16 20 0" fill="none" stroke="#67e8f9" stroke-width="5" stroke-linecap="round"/>
200
+ </g></g>
201
+ <g transform="translate(50,16)"><g>
202
+ <animateTransform attributeName="transform" type="scale"
203
+ values="1 1;1 1;1 0.12;1 1" keyTimes="0;0.86;0.93;1" dur="5s" begin="0.12s" repeatCount="indefinite"/>
204
+ <path d="M -20 0 Q 0 16 20 0" fill="none" stroke="#67e8f9" stroke-width="5" stroke-linecap="round"/>
205
+ </g></g>
206
+
207
+ <!-- 鼻与 ω 嘴 -->
208
+ <polygon points="-6,34 6,34 0,41" fill="#ff9ecb"/>
209
+ <path d="M -13 46 Q -6 58 0 50 Q 6 58 13 46" fill="none" stroke="#ff9ecb"
210
+ stroke-width="4" stroke-linecap="round"/>
211
+
212
+ <!-- 胡须 -->
213
+ <g stroke="#d9c6ff" stroke-width="2.5" stroke-linecap="round" stroke-opacity="0.8">
214
+ <line x1="-64" y1="44" x2="-108" y2="36"/>
215
+ <line x1="-64" y1="60" x2="-106" y2="60"/>
216
+ <line x1="64" y1="44" x2="108" y2="36"/>
217
+ <line x1="64" y1="60" x2="106" y2="60"/>
218
+ </g>
219
+
220
+ <!-- @ 信标:自群聊深处升起,点亮左耳尖后隐去(6s 循环) -->
221
+ <g opacity="0">
222
+ <animateMotion dur="6s" repeatCount="indefinite" calcMode="linear"
223
+ keyPoints="0;0;1;1;0" keyTimes="0;0.45;0.6;0.82;1"
224
+ path="M -235 95 Q -252 -18 -112 -116"/>
225
+ <animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.45;0.5;0.82;1"
226
+ dur="6s" repeatCount="indefinite"/>
227
+ <circle r="15" fill="#100719" stroke="#67e8f9" stroke-width="4" filter="url(#glow)"/>
228
+ <text x="0" y="6.5" text-anchor="middle" font-size="18" font-weight="800"
229
+ font-family="-apple-system,'Segoe UI',sans-serif" fill="#a7f0ff">@</text>
230
+ </g>
231
+ </g>
232
+ </g>
233
+ </svg>