luckytri 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.
Files changed (235) hide show
  1. package/.github/SECURITY.md +71 -0
  2. package/LICENSE +21 -0
  3. package/README.en.md +250 -0
  4. package/README.md +250 -0
  5. package/bin/luckytri.js +107 -0
  6. package/docs/brand/banner.png +0 -0
  7. package/docs/brand/palette.png +0 -0
  8. package/docs/brand/portrait.png +0 -0
  9. package/docs/en/CHANGELOG.md +113 -0
  10. package/docs/en/architecture.md +152 -0
  11. package/docs/en/chat-style.md +62 -0
  12. package/docs/en/connect.md +63 -0
  13. package/docs/en/guide.md +216 -0
  14. package/docs/en/her-life.md +189 -0
  15. package/docs/en/npm-release.md +25 -0
  16. package/docs/en/plugins.md +82 -0
  17. package/docs/en/recovery.md +28 -0
  18. package/docs/en/time.md +71 -0
  19. package/docs/en/webui.md +73 -0
  20. package/docs/zh/CHANGELOG.md +113 -0
  21. package/docs/zh/architecture.md +152 -0
  22. package/docs/zh/chat-style.md +62 -0
  23. package/docs/zh/connect.md +63 -0
  24. package/docs/zh/guide.md +216 -0
  25. package/docs/zh/her-life.md +189 -0
  26. package/docs/zh/npm-release.md +33 -0
  27. package/docs/zh/plugins.md +82 -0
  28. package/docs/zh/recovery.md +28 -0
  29. package/docs/zh/time.md +71 -0
  30. package/docs/zh/webui.md +73 -0
  31. package/package.json +108 -0
  32. package/plugins/sdk/index.d.ts +155 -0
  33. package/plugins/sdk/testing.js +58 -0
  34. package/plugins/weather/index.js +82 -0
  35. package/plugins/weather/luckytri-plugin.json +29 -0
  36. package/public/app/assets/ChatsPage-C-FcRapR.js +3 -0
  37. package/public/app/assets/ChatsPage-DfyPCS9N.css +1 -0
  38. package/public/app/assets/Empty-CpP5kQHH.js +2 -0
  39. package/public/app/assets/Empty-DmUB2-Bk.css +1 -0
  40. package/public/app/assets/Field-D4gY-INU.js +1 -0
  41. package/public/app/assets/HeartPage-Bft9wZp5.css +1 -0
  42. package/public/app/assets/HeartPage-CPy_thQd.js +1 -0
  43. package/public/app/assets/LifePage-BUi5euBF.css +1 -0
  44. package/public/app/assets/LifePage-CxWvwmSf.js +1 -0
  45. package/public/app/assets/MemoryPage-DC4rhQ68.js +1 -0
  46. package/public/app/assets/MemoryPage-cZvXfkXb.css +1 -0
  47. package/public/app/assets/NaturePage-CTXHCE2D.css +1 -0
  48. package/public/app/assets/NaturePage-IvYw04Zq.js +3 -0
  49. package/public/app/assets/PeoplePage-BmUxkrOU.css +1 -0
  50. package/public/app/assets/PeoplePage-Cqefm9-q.js +1 -0
  51. package/public/app/assets/PluginsPage-DKSaKvXb.js +2 -0
  52. package/public/app/assets/PluginsPage-HyBe5D8w.css +1 -0
  53. package/public/app/assets/SystemPage-CAMPoYqM.css +1 -0
  54. package/public/app/assets/SystemPage-CJmtJkrK.js +1 -0
  55. package/public/app/assets/TimePage-BOms_n4I.css +1 -0
  56. package/public/app/assets/TimePage-Cw44inM4.js +3 -0
  57. package/public/app/assets/formatters-ZmW_xFHw.js +1 -0
  58. package/public/app/assets/index-BheRW-1t.js +4 -0
  59. package/public/app/assets/index-YDz1ree1.css +1 -0
  60. package/public/app/assets/knowledge-DG0e0uKw.js +1 -0
  61. package/public/app/assets/liquid-garden-IMoHBwzc.png +0 -0
  62. package/public/app/assets/luckytri-home-95QKeMtc.png +0 -0
  63. package/public/app/assets/media-BzPDBm7e.js +1 -0
  64. package/public/app/index.html +27 -0
  65. package/public/guide.en.html +1053 -0
  66. package/public/guide.html +893 -0
  67. package/public/plugin-kit/v1/bridge.js +30 -0
  68. package/public/plugin-kit/v1/kit.css +14 -0
  69. package/public/tutorial.en.md +214 -0
  70. package/public/tutorial.md +214 -0
  71. package/scripts/backup.js +311 -0
  72. package/scripts/browser.js +41 -0
  73. package/scripts/launch.js +94 -0
  74. package/scripts/plugin.js +150 -0
  75. package/scripts/proxy-config.js +167 -0
  76. package/scripts/recover.js +26 -0
  77. package/scripts/runtime.js +25 -0
  78. package/scripts/service.js +47 -0
  79. package/scripts/setup.js +50 -0
  80. package/scripts/start-server.js +41 -0
  81. package/scripts/stop.js +36 -0
  82. package/server/app.js +96 -0
  83. package/server/channels/adapters.js +27 -0
  84. package/server/channels/contract.js +57 -0
  85. package/server/channels/hub.js +165 -0
  86. package/server/channels/index.js +25 -0
  87. package/server/channels/onebot/adapter.js +288 -0
  88. package/server/channels/onebot/index.js +336 -0
  89. package/server/channels/onebot/token.js +3 -0
  90. package/server/channels/qqbot/adapter.js +253 -0
  91. package/server/channels/qqbot/api.js +199 -0
  92. package/server/channels/qqbot/credentials.js +11 -0
  93. package/server/channels/qqbot/gateway.js +274 -0
  94. package/server/channels/qqbot/index.js +389 -0
  95. package/server/channels/segments.js +40 -0
  96. package/server/channels/session-key.js +153 -0
  97. package/server/channels/state.js +42 -0
  98. package/server/core/api.js +564 -0
  99. package/server/core/capabilities.js +208 -0
  100. package/server/core/context-builder.js +288 -0
  101. package/server/core/context-compactor.js +454 -0
  102. package/server/core/conversation-cues.js +325 -0
  103. package/server/core/conversation-manager.js +87 -0
  104. package/server/core/events.js +60 -0
  105. package/server/core/feedback.js +8 -0
  106. package/server/core/group-style.js +72 -0
  107. package/server/core/image-preprocessor.js +208 -0
  108. package/server/core/initiative-context.js +66 -0
  109. package/server/core/input-budget.js +32 -0
  110. package/server/core/local-demo.js +98 -0
  111. package/server/core/message-manager.js +118 -0
  112. package/server/core/message-scheduler.js +78 -0
  113. package/server/core/model-json.js +190 -0
  114. package/server/core/model-manager.js +1213 -0
  115. package/server/core/model-presets.js +900 -0
  116. package/server/core/name-call.js +29 -0
  117. package/server/core/net-guard.js +49 -0
  118. package/server/core/network.js +157 -0
  119. package/server/core/orchestrator.js +569 -0
  120. package/server/core/perceivers.js +91 -0
  121. package/server/core/person-name.js +22 -0
  122. package/server/core/persona-manager.js +243 -0
  123. package/server/core/reply-delivery.js +393 -0
  124. package/server/core/reply-target-resolver.js +50 -0
  125. package/server/core/repository.js +293 -0
  126. package/server/core/response-generator.js +50 -0
  127. package/server/core/response-validator.js +508 -0
  128. package/server/core/retired-prompts.js +51 -0
  129. package/server/core/sessions.js +109 -0
  130. package/server/core/speaker-names.js +92 -0
  131. package/server/core/topic-tracker.js +65 -0
  132. package/server/core/turn-processor.js +630 -0
  133. package/server/core/turn.js +236 -0
  134. package/server/core/vision-manager.js +633 -0
  135. package/server/http.js +18 -0
  136. package/server/i18n/decisions.js +206 -0
  137. package/server/i18n/errors.js +497 -0
  138. package/server/i18n/index.js +144 -0
  139. package/server/i18n/labels.js +653 -0
  140. package/server/i18n/models.js +111 -0
  141. package/server/index.js +102 -0
  142. package/server/knowledge/api.js +342 -0
  143. package/server/knowledge/embedding-profile.js +114 -0
  144. package/server/knowledge/index.js +13 -0
  145. package/server/knowledge/manager.js +386 -0
  146. package/server/knowledge/retrieval.js +93 -0
  147. package/server/knowledge/schema.js +154 -0
  148. package/server/knowledge/vector-index.js +90 -0
  149. package/server/mind/affect.js +265 -0
  150. package/server/mind/anticipations.js +507 -0
  151. package/server/mind/api.js +703 -0
  152. package/server/mind/attention.js +200 -0
  153. package/server/mind/bonds.js +558 -0
  154. package/server/mind/budget.js +152 -0
  155. package/server/mind/clock.js +83 -0
  156. package/server/mind/context-selection.js +61 -0
  157. package/server/mind/continuity.js +562 -0
  158. package/server/mind/conversation-origin.js +175 -0
  159. package/server/mind/day-planner.js +324 -0
  160. package/server/mind/days.js +210 -0
  161. package/server/mind/evidence-read.js +45 -0
  162. package/server/mind/evidence.js +148 -0
  163. package/server/mind/faces.js +138 -0
  164. package/server/mind/guard.js +60 -0
  165. package/server/mind/index.js +445 -0
  166. package/server/mind/initiative.js +333 -0
  167. package/server/mind/life/activities.js +21 -0
  168. package/server/mind/life/context.js +309 -0
  169. package/server/mind/life/diary.js +786 -0
  170. package/server/mind/life/growth.js +252 -0
  171. package/server/mind/life/index.js +1049 -0
  172. package/server/mind/life/presence.js +305 -0
  173. package/server/mind/meetings.js +1069 -0
  174. package/server/mind/memory-policy.js +48 -0
  175. package/server/mind/memory.js +888 -0
  176. package/server/mind/nature.js +262 -0
  177. package/server/mind/novelty.js +19 -0
  178. package/server/mind/observations.js +84 -0
  179. package/server/mind/own-day.js +324 -0
  180. package/server/mind/own-voice.js +318 -0
  181. package/server/mind/periods.js +118 -0
  182. package/server/mind/reading.js +120 -0
  183. package/server/mind/salience.js +201 -0
  184. package/server/mind/schema.js +562 -0
  185. package/server/mind/self.js +484 -0
  186. package/server/mind/senses.js +53 -0
  187. package/server/mind/thoughts.js +274 -0
  188. package/server/mind/time/activity-clock.js +196 -0
  189. package/server/mind/time/agenda.js +242 -0
  190. package/server/mind/time/api.js +200 -0
  191. package/server/mind/time/attention.js +34 -0
  192. package/server/mind/time/availability.js +69 -0
  193. package/server/mind/time/commitment-review.js +198 -0
  194. package/server/mind/time/delivery-links.js +130 -0
  195. package/server/mind/time/executor.js +512 -0
  196. package/server/mind/time/experiences.js +128 -0
  197. package/server/mind/time/games.js +562 -0
  198. package/server/mind/time/index.js +739 -0
  199. package/server/mind/time/intent.js +166 -0
  200. package/server/mind/time/kinds.js +171 -0
  201. package/server/mind/time/model-material.js +77 -0
  202. package/server/mind/time/plugin-activity.js +207 -0
  203. package/server/mind/time/presentation.js +51 -0
  204. package/server/mind/time/schedule.js +83 -0
  205. package/server/mind/time/schema.js +84 -0
  206. package/server/mind/time/search.js +383 -0
  207. package/server/mind/time/sharing.js +472 -0
  208. package/server/mind/time/task-links.js +311 -0
  209. package/server/mind/time/tasks.js +587 -0
  210. package/server/mind/time/works.js +432 -0
  211. package/server/mind/traits.js +191 -0
  212. package/server/mind/util.js +110 -0
  213. package/server/mind/view.js +273 -0
  214. package/server/paths.js +102 -0
  215. package/server/plugins/api.js +204 -0
  216. package/server/plugins/host.js +967 -0
  217. package/server/plugins/install.js +295 -0
  218. package/server/plugins/manifest.js +201 -0
  219. package/server/plugins/permissions.js +42 -0
  220. package/server/plugins/runner/guard.mjs +36 -0
  221. package/server/plugins/runner/worker.mjs +250 -0
  222. package/server/plugins/schema.js +91 -0
  223. package/server/plugins/zip.js +112 -0
  224. package/server/startup-lock.js +118 -0
  225. package/server/storage/archive.js +241 -0
  226. package/server/storage/backup-cleanup-worker.js +8 -0
  227. package/server/storage/backup-cleanup.js +414 -0
  228. package/server/storage/backup-lock.js +76 -0
  229. package/server/storage/backup-scheduler.js +131 -0
  230. package/server/storage/store.js +199 -0
  231. package/server/studio/display-names.js +138 -0
  232. package/server/studio/management.js +102 -0
  233. package/server/studio/readiness.js +190 -0
  234. package/server/studio/settings.js +236 -0
  235. package/server/version.js +10 -0
@@ -0,0 +1,30 @@
1
+ // The only way a plugin page, sealed in its sandbox, may speak to LuckyTri.
2
+ // It cannot see the admin token. The studio page makes the request for it,
3
+ // and only to this plugin's own routes.
4
+ (function () {
5
+ const pending = new Map();
6
+ window.luckytri = {
7
+ request(method, path, body) {
8
+ const id = Math.random().toString(36).slice(2);
9
+ return new Promise((resolve, reject) => {
10
+ pending.set(id, { resolve, reject });
11
+ parent.postMessage(
12
+ { luckytri: 1, id, method, path, body },
13
+ location.origin,
14
+ );
15
+ });
16
+ },
17
+ };
18
+ window.addEventListener("message", (event) => {
19
+ if (event.origin !== location.origin) return;
20
+ const data = event.data || {};
21
+ if (data.luckytri === "theme" && data.css) {
22
+ document.documentElement.setAttribute("style", data.css);
23
+ return;
24
+ }
25
+ const wait = pending.get(data.id);
26
+ if (!wait) return;
27
+ pending.delete(data.id);
28
+ data.ok ? wait.resolve(data.result) : wait.reject(new Error(data.error || "failed"));
29
+ });
30
+ })();
@@ -0,0 +1,14 @@
1
+ :root {
2
+ color: var(--ink, #2a241f);
3
+ background: transparent;
4
+ font-family: var(--font-ui, "Segoe UI", sans-serif);
5
+ }
6
+ body {
7
+ margin: 0;
8
+ }
9
+ button,
10
+ input,
11
+ textarea,
12
+ select {
13
+ font: inherit;
14
+ }
@@ -0,0 +1,214 @@
1
+ # LuckyTri · Getting Started
2
+
3
+ LuckyTri runs on your own computer and meets people through QQ. QQ has two ways to connect, and you pick one: **OneBot 11** (you bring a connector, reverse WebSocket) or the **QQ official bot** (QQ Open Platform). With either, her memory, mood and behavior are exactly the same; only the way messages arrive differs. You configure the model API yourself.
4
+
5
+ The top-right corner of the interface switches between "中文" and "English". Chinese is the default and your choice is remembered in this browser. What she says, writes in her diary and keeps as her own thoughts is never translated; that is her own content.
6
+
7
+ ## 1. Start
8
+
9
+ You need Node.js 24.5 or newer. Install globally:
10
+
11
+ ```powershell
12
+ node --version
13
+ npm install -g luckytri
14
+ luckytri
15
+ ```
16
+
17
+ LuckyTri starts in the background and opens <http://127.0.0.1:3210>, creating local configuration on the first run. The package includes the built WebUI. The experimental-SQLite notice does not mean the start failed.
18
+
19
+ Use `luckytri paths` to find `.env`, open it in a local editor and enter `ADMIN_TOKEN` in the studio. `luckytri --no-browser` launches without opening a browser, `luckytri start` runs in the foreground, and `luckytri stop` stops the service. Update with `luckytri stop`, `npm install -g luckytri@latest`, then `luckytri`. Your configuration and data stay intact. Default directories are listed in the [README](../../README.en.md#configuration-data-and-privacy).
20
+
21
+ For source development, run `npm install`, `npm run setup`, `npm run build` and `npm start` in the checkout. Later examples using `npm run setup/stop/backup/recovery/plugin` apply to source checkouts; global users use the corresponding `luckytri setup/stop/backup/recovery/plugin` commands.
22
+
23
+ On Windows you can double-click `启动LuckyTri.cmd` for daily use. If the service is already running, it only opens LuckyTri. Closing the browser does not stop the service; to stop it double-click `停止LuckyTri.cmd` or run `npm run stop` in the project folder. That command stops this project only and leaves other Node processes alone. When running in the foreground you can also press `Ctrl+C` in that terminal.
24
+
25
+ LuckyTri is "TA's little world". The left navigation has three groups and ten entries:
26
+
27
+ | Group | Entry | What is inside |
28
+ | ---------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
29
+ | TA | Now | TA's mood right now, what she is doing, what she said lately, what is on her mind and what she is waiting for; today's timeline, token usage and the status bar |
30
+ | TA | Inner life | The star map (TA's threads of self), notes she keeps in mind, the mood ribbon |
31
+ | TA | People | The people galaxy, each person's detail page, how TA is in each group |
32
+ | TA | Lifetime | The diary and TA on that day, reviews, "my road so far" and chapters, promises and anticipation, the bookshelf |
33
+ | Daily life | Time | The record of TA's actual life: today's activity, to-dos, works library, ongoing projects, experiences |
34
+ | Daily life | Chat | Session list, live chat and "why she said this", feedback, session settings, simulated messages, back to that moment (replay) |
35
+ | Daily life | Memory | What TA remembers, the bookshelf |
36
+ | Settings | Nature | Name and character (the glow beside it previews live), daily rhythm, TA's days, daily tokens, advanced prompts, trial chat |
37
+ | Settings | System | Connect QQ, model library, run switches |
38
+ | Settings | Plugins | Enable, install and remove the ways she touches the world. See [Plugins](plugins.md) |
39
+
40
+ The colors, sky and motion follow TA's mood and rhythm: warm when she is happy, grey-blue when she is low, night once she is asleep. At the bottom left you can pin one theme or turn on "Reduce motion". The small TA at the bottom right answers a poke, a long press is a head-pat, and a double click or "Chat" opens the trial chat. These are interface animations only and never change her mind. On a phone the navigation sits at the bottom and the other entries are under "More".
41
+
42
+ ## 2. Local tokens
43
+
44
+ Configuration is created on first launch. You can also initialize in advance:
45
+
46
+ ```powershell
47
+ luckytri setup
48
+ luckytri paths
49
+ ```
50
+
51
+ When there is no `.env`, setup writes an admin token and an OneBot connection token. An existing `.env` is kept as it is.
52
+
53
+ ```dotenv
54
+ HOST=127.0.0.1
55
+ PORT=3210
56
+ ADMIN_TOKEN=
57
+ ONEBOT_TOKEN=
58
+ LLM_API_KEY=
59
+ ```
60
+
61
+ | Field | Purpose |
62
+ | ----------------------------------- | ------------------------------------------------------------------------------------------------------ |
63
+ | `ADMIN_TOKEN` | Opens LuckyTri and calls the HTTP API; the stop command uses it too |
64
+ | `ONEBOT_TOKEN` | The OneBot 11 connector uses it for `/onebot/v11/ws` (only needed for OneBot) |
65
+ | `LLM_API_KEY` | Optional; when set it takes precedence over the model key saved in LuckyTri |
66
+ | `LUCKYTRI_CHANNEL` | Optional; `onebot` or `qqbot`. When set, the connection method cannot be changed in the interface |
67
+ | `QQBOT_APP_ID` / `QQBOT_APP_SECRET` | Optional; credentials of the QQ official bot, they take precedence over what is saved in the interface |
68
+ | `HOST` / `PORT` | Listen address and port; keep the defaults for local use |
69
+
70
+ The first time the page loads data it asks for `ADMIN_TOKEN`. The token stays in the current tab only. Restart after changing `.env`. Never give `ADMIN_TOKEN` to the QQ connector, and do not treat `ONEBOT_TOKEN` as the studio password.
71
+
72
+ ## 3. Try it in simulation mode first
73
+
74
+ Simulation mode is on by default, so nothing is sent to QQ.
75
+
76
+ 1. Open "Chat", click "+ Add session" above the session list and enter a test group number and name.
77
+ 2. In the chat in the middle click "Simulated message", type an ordinary line as one of the group members and send it.
78
+ 3. Click "Why she said that" under TA's reply to see the reason. "TA glanced over it" and "TA looked but stayed quiet" show up as dividers in the chat.
79
+
80
+ Without a model key, simulated replies use local samples and call no model. Once there is a key, the same path runs the real speaking flow, still without sending to QQ. Simulated messages go into a simulated session and never change TA's mood, relationships or memory.
81
+
82
+ "Simulated message" is only available in simulation mode. The "Chat" of the small TA and the trial chat to the right of "Nature" are different: they go through the real pipeline and read TA's mind as it is now, and the Nature page can even include an unsaved draft, but nothing is written and nothing is sent to QQ.
83
+
84
+ ## 4. Connect a model
85
+
86
+ Open "System → Model library", click "+ Add model" and choose a preset.
87
+
88
+ - For the API address enter the root of the provider's compatible endpoint. The program appends `/chat/completions` itself; do not write the full path again.
89
+ - For the model name enter a model ID the provider accepts.
90
+ - Leaving the API key empty keeps the saved key.
91
+
92
+ A preset only fills in a common address and model name; the real models are whatever your provider's console shows. Speaking decisions and replies need the model to return a JSON object.
93
+
94
+ After saving, click "Test this model's connection". The test carries no group chat text. Test again after changing the address, model or key.
95
+
96
+ You can also stay in simulation mode and trial-chat with TA under "Nature" to see how she speaks before letting her join real QQ.
97
+
98
+ ### Reading images
99
+
100
+ Enable "Image understanding" in the model profile and use a model whose provider supports vision. Images can be read from QQ URLs, local cache files or the image-cache API. Originals up to 32 MiB and 80 million pixels are accepted by default, so ordinary photos and screenshots larger than 4 MiB are no longer rejected outright. Small images retain their bytes after decoding validation. Large screenshots first try lossless compression, then encoding compression or proportional resizing as needed. Photos are oriented correctly without cropping. Each image sent to the model defaults to at most 4 MiB, 8192 pixels on its longest side and 16 million output pixels. Originals stay unchanged and duplicate detection uses their original contents.
101
+
102
+ Downloads have a 30-second timeout by default. An interrupted or damaged image fails independently of other images in the batch. AVIF is converted to a common format; animated images contribute their first frame, which does not establish the full animation. Large Base64 images returned by QQ's image-cache API no longer fall under the ordinary 1 MiB message limit; ordinary messages keep that limit.
103
+
104
+ To adjust this, set `VISION_SOURCE_MAX_MB` (4–64, default 32), `VISION_MODEL_MAX_MB` (1–4, default 4) or `VISION_DOWNLOAD_TIMEOUT_SECONDS` (5–120, default 30) in `.env`, then restart. Interpretation still depends on the selected model's visual abilities and an available provider endpoint.
105
+
106
+ ## 5. Connect QQ
107
+
108
+ Open "System → Connect QQ". There are two options at the top: **OneBot 11** and **QQ official bot**, and only one is used at a time. Switching drops the current connection; sessions from the previous method stay readable but can no longer be sent to. See `docs/en/connect.md` for how the two compare; the steps are below.
109
+
110
+ ### Option one: OneBot 11
111
+
112
+ LuckyTri only implements the reverse-WebSocket side of OneBot 11. You prepare and manage the connector yourself.
113
+
114
+ 1. Set `ONEBOT_TOKEN` in the local `.env` and restart LuckyTri.
115
+ 2. In the connector's own network settings add an OneBot 11 reverse WebSocket client:
116
+
117
+ | Item | Value on this machine |
118
+ | -------------- | ----------------------------------- |
119
+ | Protocol | OneBot 11 |
120
+ | WebSocket URL | `ws://127.0.0.1:3210/onebot/v11/ws` |
121
+ | Token | `ONEBOT_TOKEN` from `.env` |
122
+ | Message format | array |
123
+ | Enabled | on, then save |
124
+
125
+ 3. Log in to QQ in the connector. The account must already be in the target group; LuckyTri does not store QQ passwords.
126
+
127
+ `3210` is LuckyTri's port. When the connector and LuckyTri are on different machines or in containers, `127.0.0.1` points at each own environment: replace it with LuckyTri's real address and configure `ADMIN_TOKEN` first.
128
+
129
+ ### Option two: QQ official bot
130
+
131
+ No QQ account login and no extra connector are needed; LuckyTri connects to the QQ Open Platform by itself.
132
+
133
+ 1. On the QQ Open Platform create a bot, note its **AppID** and **AppSecret**, enable group and one-to-one messages as the platform requires, and add the bot to the group you test with or as a contact. The platform's entry points and review rules change over time; follow the Open Platform's current instructions.
134
+ 2. In "System → Connect QQ" choose "QQ official bot", fill in the AppID and AppSecret and save. You can also set `QQBOT_APP_ID` and `QQBOT_APP_SECRET` in `.env`. The AppSecret is never shown again after saving.
135
+ 3. When the status reads "Connected", @ her in the group or message her privately. For her to see every group message, enable "receive all messages" on the Open Platform; without it she only receives messages that @ her, and the connection page says so.
136
+ 4. The official interface gives no group names, so new sessions are shown by number. You can rename a session under "Chat".
137
+
138
+ With both options, connected only means the transport works. After a message arrives from the target group, the session appears in the session list under "Chat"; newly discovered sessions are paused by default.
139
+
140
+ ## 6. Let TA join a session
141
+
142
+ 1. In "System → Run switches" confirm chatting is allowed, turn simulation mode off and save. The status bar under "Now" shows both.
143
+ 2. In "Chat" select the target group and click "Resume" under "This session" on the right, or flip the switch in the list.
144
+ 3. In QQ @ her, or just call TA by name.
145
+ 4. Go back to "Chat" to read the messages and "Why she said that".
146
+
147
+ Whether she speaks is TA's own decision; there is no participation probability and no cooldown. When she is addressed by name, in a private chat, quoted, or a crisis signal appears, TA always looks closely and then chooses to answer, react briefly, say she does not feel like talking, or stay silent; the reason is shown under "Chat" and "Now → TA today". Ordinary group messages get a close look only when TA cares: she was just talking, someone asks everyone, the topic touches her interests, someone familiar is speaking, a lot of messages piled up. Otherwise she only glances, calls no model, and reads them together at the next close look. If she seems too quiet, raise "Proactive" under "Nature".
148
+
149
+ TA sleeps at 02:00 and wakes at 08:00 by default. Drag the moon and the sun under "Nature → Daily rhythm" to change this, or turn it off. If she is called while asleep she looks once she wakes, and the whole interface turns to night. The per-minute limit on speaking turns stays, to prevent bursts of consecutive replies.
150
+
151
+ Once "Image understanding" is on for a model, images in private chats, @ messages or quotes are read locally first and then shown to the model. When the main model cannot see images, pick another vision-capable model with image understanding on under "Chat → This session → Session settings". A model without image understanding never receives the picture.
152
+
153
+ ## 7. TA, memory and knowledge
154
+
155
+ "Nature" is the only part you write: name, character, interests, boundaries, bottom lines and daily rhythm, saved as a new version each time. As you drag a character dial, the glow beside it follows: gentleness warms the color and lifts the corners of the mouth, liveliness makes it bounce faster.
156
+
157
+ Everything else grows from experience: TA's self is the stars of the "Inner life" star map, her feeling for each person is under "People", and how she is in each group is in the group faces below "People". Open any of them to see where each change came from and undo what feels wrong; what was undone is never written back. Threads that have not been touched for a long time slowly fade into the "far away" of the map; they are not deleted and come back when talked about. The record of her time alone, her diary, night sorting, reviews and autobiography rewrites, together with what she is waiting for (arrangements others mentioned, things she promised; they can be undone), are all under "Lifetime". Daily token cap, background share, night sorting and the review interval are set under "Nature → TA's days".
158
+
159
+ In "Memory" pick a session first; you can also filter by person.
160
+
161
+ - TA has one memory across all sessions: what was learned elsewhere is only recalled when relevant; what was learned in a private chat is marked "known privately" and not said in public; what was asked to be kept secret stays in the original session. You can adjust discretion, lock or undo each memory. Memories about a person also appear on that person's detail page under "People".
162
+ - When a group member says "remember, I…", it goes straight into what TA remembers (passwords, verification codes and the like are not remembered).
163
+ - Reference material placed in a collection on the "Bookshelf" is searched within what the current session may see. Group chats never load other private chats' collections.
164
+ - Clearing the context in "Chat" does not delete long-term memory.
165
+
166
+ ## 8. Backup and stop
167
+
168
+ ```powershell
169
+ npm run backup
170
+ npm run stop
171
+ npm start
172
+ ```
173
+
174
+ Backups go to `data/backups/`. The database holds chats, memories and any keys saved in it, so keep it like a private file. `.env` is not part of this backup.
175
+
176
+ Everything of TA lives in this one database, so the service backs it up automatically while running: by default every 24 hours, in another process, it writes a `luckytri-auto-*.db`, keeps only the latest 7 and checks integrity afterwards. An automatic backup holds chats, memories, self, diary and relationships, but not model call records older than a day (each can be hundreds of KB and would make the backup more than ten times bigger), so "Why she said that" can only be looked back on for the last day. `npm run backup` is still the full backup. Adjust in `.env`:
177
+
178
+ | Variable | Meaning | Default |
179
+ | ----------------------- | ------------------------------------------------------ | -------------- |
180
+ | `BACKUP_INTERVAL_HOURS` | Interval between automatic backups, `0` turns them off | `24` |
181
+ | `BACKUP_KEEP` | How many automatic backups to keep | `7` |
182
+ | `BACKUP_TRACE_DAYS` | Days of call records kept inside a backup | `1` |
183
+ | `BACKUP_DIR` | Backup folder | `data/backups` |
184
+
185
+ To take an automatic backup right now: `node scripts/backup.js --auto --force`. A failed automatic backup is noted in the launcher window or `data/launcher.log` and retried after longer and longer pauses. A backup on the same disk as the database cannot survive disk failure; copy important backups elsewhere yourself.
186
+
187
+ When `DB_PATH` points to a different database and `BACKUP_DIR` is unset, backups go into a `backups` folder beside that database, keeping separate instances apart.
188
+
189
+ Under System → Data and backups, inspect each historical copy, set a daily cleanup time, maximum age and maximum count for full backups, or select individual copies for removal. By default, a daily check at 04:30 server local time selects full backups older than 14 days or beyond the newest two. Set the count to 0 to retain no full backups. Full and migration snapshots are no longer permanently locked and can all be selected manually. Automatic backups rotate only identifiable copies belonging to this instance, with the newest two reserved as restore points. Unidentifiable older copies, migration, older-version and pre-upgrade snapshots are removed only through manual selection.
190
+
191
+ Cleanup verifies a retained automatic backup of this instance from the last 72 hours. If the newest copy is damaged, another one is tried. New manifests include a database identity so a different instance cannot provide the recovery guarantee. Older manifests can still be verified and restored, but a refreshed automatic backup is needed before they can guarantee cleanup. Backup and cleanup share a folder lock; the live database, hard links to it and copies with runtime sidecars cannot be removed. Partial failures list the remaining files and accurately report the space already freed. Copy historical restore points worth keeping to another disk together with their `.db.json` manifests first.
192
+
193
+ To restore, stop the service, move the current `data/friend.db` and its `-wal` and `-shm` files away, then copy the chosen backup to `data/friend.db`. When `DB_PATH` is set, work on that path. Never keep an old WAL file next to a restored database. The safer way is `npm run recovery`, which verifies a backup and restores it into a brand new file; see `docs/en/recovery.md`.
194
+
195
+ ## 9. Troubleshooting
196
+
197
+ | Symptom | Check first |
198
+ | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
199
+ | The page does not open | Is Node running, and is the address `127.0.0.1:3210` |
200
+ | Port already in use | Run `npm run stop` first; do not kill every node process |
201
+ | Wrong token | Use `ADMIN_TOKEN`; restart after editing `.env` |
202
+ | OneBot not connected | Is the connector logged in; do the URL, port, path and `ONEBOT_TOKEN` all match |
203
+ | Official bot not connected | Are the AppID and AppSecret right, are group and one-to-one messages enabled for the bot, can the network reach the QQ Open Platform |
204
+ | Official bot only gets @ messages | Enable "receive all messages" on the Open Platform; this is a platform permission, not a LuckyTri limit |
205
+ | Connected but no sessions | Send one more new message in QQ; with OneBot also check the message format is array |
206
+ | No reply in a group | Global switch, session switch, simulation mode, key; under "Chat", was it "TA glanced over it" or did TA look closely and choose silence |
207
+ | No reply even when @-ed | Is TA asleep (see "Now", the interface is night), the per-minute limit, today's tokens used up, a model error, or TA's own reason |
208
+ | Model 401 / 403 | Key and permissions; an environment-variable key may override the one saved in LuckyTri |
209
+ | Model 404 | Do not append `/chat/completions` twice to the API address; check the model ID |
210
+ | Model returns no JSON | Use a model that supports JSON object output and run the connection test first |
211
+ | Memory did not appear | Was it an explicit "remember, I…", was it undone, is its discretion "keep secret" and you are not in the original session |
212
+ | Too much motion | "Reduce motion" at the bottom left; when the system asks for reduced motion, particles and morphing switch off automatically |
213
+
214
+ Never post API keys, `.env` or private chat text anywhere public.
@@ -0,0 +1,214 @@
1
+ # LuckyTri · 从零上手教程
2
+
3
+ LuckyTri 在你的电脑上运行,通过 QQ 与人相遇。QQ 有两种连接方式,二选一:**OneBot 11**(自备接入端,反向 WebSocket)或 **QQ 官方机器人**(QQ 开放平台)。两种方式下她的记忆、心情和行为完全一致,区别只在消息怎么到达。模型 API 由你自行配置。
4
+
5
+ 界面右上角可以在「中文」与「English」之间切换,默认中文,选择会记在这个浏览器里。她自己说的话、写的日记和留下的念头不翻译,那是她自己的内容。
6
+
7
+ ## 1. 启动
8
+
9
+ 需要 Node.js 24.5 或更高版本。普通用户直接全局安装:
10
+
11
+ ```powershell
12
+ node --version
13
+ npm install -g luckytri
14
+ luckytri
15
+ ```
16
+
17
+ LuckyTri 在后台启动并打开 <http://127.0.0.1:3210>,首次运行自动生成配置。无需下载仓库或构建 WebUI。终端里的 SQLite 实验性提示不表示启动失败,以能否打开 LuckyTri 为准。
18
+
19
+ 运行 `luckytri paths` 查看本机 `.env`,在本机编辑器中取得 `ADMIN_TOKEN` 并填入管理台。`luckytri --no-browser` 只后台启动,`luckytri start` 前台运行,`luckytri stop` 停止服务。更新使用 `luckytri stop` → `npm install -g luckytri@latest` → `luckytri`,配置与数据会保留;默认目录见 [README](../../README.md#配置数据与隐私)。
20
+
21
+ 从源码开发时,在仓库中执行 `npm install`、`npm run setup`、`npm run build`、`npm start`。下文的 `npm run setup/stop/backup/recovery/plugin` 面向源码用户;全局用户使用对应的 `luckytri setup/stop/backup/recovery/plugin`。
22
+
23
+ Windows 日常使用可以双击 `启动LuckyTri.cmd`。服务已在运行时,它只打开 LuckyTri。关闭浏览器不会停止服务;停止时双击 `停止LuckyTri.cmd`,或在项目目录执行 `npm run stop`。这条命令只停止当前项目,不会结束其他 Node 进程。前台运行时也可以在该终端按 `Ctrl+C`。
24
+
25
+ LuckyTri 是「TA 的小世界」,左侧导航分三组、十个入口:
26
+
27
+ | 分组 | 入口 | 里面有什么 |
28
+ | ---- | ---- | ----------------------------------------------------------------------------------------------- |
29
+ | TA | 此刻 | TA 此刻的心情、正在做什么、最近说的话、放在心上和在等的事;今天的时间线、Token 用量和运行状态条 |
30
+ | TA | 内心 | 心灵星图(TA 的自我线索)、放在心上的便签、心情彩带 |
31
+ | TA | 人际 | 人物星系、每个人的详情、TA 在各群的样子 |
32
+ | TA | 一生 | 日记本与那天的 TA、回顾、我的来路与章节、约定与期待、书架 |
33
+ | 日常 | 时间 | TA 实际生活的记录:今日的活动、待办、作品库、持续项目、体验记录 |
34
+ | 日常 | 对话 | 会话列表、实时聊天与「为什么这么说」、反馈、会话设置、模拟消息、回到那一刻(回放) |
35
+ | 日常 | 记忆 | TA 记得的事、资料书架 |
36
+ | 设置 | 天性 | 名字与性格(旁边的光团实时预览)、作息、TA 的日子、每日 Token、高级指令、试聊 |
37
+ | 设置 | 系统 | 连接 QQ、模型库、运行开关 |
38
+ | 设置 | 插件 | 启用、安装和卸下她接触世界的方式。见 [插件](plugins.md) |
39
+
40
+ 界面的颜色、天空和动效跟着 TA 的心情与作息变化:开心时暖、低落时灰蓝、睡着后变成夜晚。左下角可以把主题固定成某一种,也可以「减少动画」。右下角的小 TA 戳一下会回应一句,按住是摸摸头,双击或点「聊聊」打开试聊;这些只是界面动画,不会改变 TA 的心智。手机上导航在底部,其余入口在「更多」里。
41
+
42
+ ## 2. 本机令牌
43
+
44
+ 首次启动已生成配置;也可以提前初始化:
45
+
46
+ ```powershell
47
+ luckytri setup
48
+ luckytri paths
49
+ ```
50
+
51
+ 没有 `.env` 时,setup 会写入管理令牌和 OneBot 连接令牌。已有 `.env` 会原样保留。
52
+
53
+ ```dotenv
54
+ HOST=127.0.0.1
55
+ PORT=3210
56
+ ADMIN_TOKEN=
57
+ ONEBOT_TOKEN=
58
+ LLM_API_KEY=
59
+ ```
60
+
61
+ | 字段 | 用途 |
62
+ | ----------------------------------- | --------------------------------------------------------------- |
63
+ | `ADMIN_TOKEN` | 打开 LuckyTri 和调用 HTTP API;停止命令也会使用 |
64
+ | `ONEBOT_TOKEN` | OneBot 11 接入端连接 `/onebot/v11/ws`(只用 OneBot 方式时需要) |
65
+ | `LLM_API_KEY` | 可选;非空时优先于 LuckyTri 里保存的模型密钥 |
66
+ | `LUCKYTRI_CHANNEL` | 可选;`onebot` 或 `qqbot`。设置后在界面里不能更改连接方式 |
67
+ | `QQBOT_APP_ID` / `QQBOT_APP_SECRET` | 可选;QQ 官方机器人的凭据,优先于界面里保存的 |
68
+ | `HOST` / `PORT` | 监听地址和端口;本机使用保持默认 |
69
+
70
+ 页面第一次取数据时会弹出窗口要求输入 `ADMIN_TOKEN`。令牌只留在当前标签页。修改 `.env` 后必须重启。不要把 `ADMIN_TOKEN` 填给 QQ 接入端,也不要把 `ONEBOT_TOKEN` 当成管理台密码。
71
+
72
+ ## 3. 先在模拟模式试聊
73
+
74
+ 默认开启模拟模式,消息不会发到 QQ。
75
+
76
+ 1. 打开「对话」,点会话列表上方的「+ 添加会话」,填写测试群号和名称。
77
+ 2. 在中间的聊天里点「模拟消息」,以一位群友的身份输入一句普通消息并发送。
78
+ 3. 点 TA 回复下面的「为什么这么说」查看理由;「TA 扫了一眼」「TA 看了,没出声」会显示成聊天里的分隔条。
79
+
80
+ 没有模型密钥时,模拟回复使用本地样例,不调用模型。有密钥后,同一条路径会走真实发言流程,但仍然不发 QQ。模拟消息写进模拟会话,不会改变 TA 的心情、关系和记忆。
81
+
82
+ 「模拟消息」只在模拟模式下可用。右下角小 TA 的「聊聊」和「天性」右侧的试聊是另一回事:走真实链路,读取 TA 此刻的心智,天性页还可以带上没保存的草稿,但什么都不写入,也不发 QQ。
83
+
84
+ ## 4. 连接模型
85
+
86
+ 打开「系统 → 模型库」,点「+ 新增模型」选择一个预设。
87
+
88
+ - API 地址填写服务商的兼容接口根路径。程序会自己拼接 `/chat/completions`,不要把完整路径再写一遍。
89
+ - 模型名称填写服务商接受的模型 ID。
90
+ - API Key 留空表示保留已保存的密钥。
91
+
92
+ 预设只填入常见地址和模型名,以服务商控制台里的实际模型为准。发言决策和回复生成需要模型返回 JSON 对象。
93
+
94
+ 保存后点「测试此模型连接」。测试不携带群聊原文。更换地址、模型或密钥后需要重新测试。
95
+
96
+ 也可以先保持模拟模式,在「天性」里和 TA 试聊,确认 TA 说话的样子后再让 TA 参加真实 QQ。
97
+
98
+ ### 图片读取
99
+
100
+ 在模型档案里开启「图片理解」,并使用服务商支持视觉的模型。图片从 QQ 地址、本地缓存或图片缓存接口读取,默认接受不超过 32 MiB、8000 万像素的源图,不再把超过 4 MiB 的普通照片和截图直接拒绝。小图经过解码验证后保持原样;大截图先尝试无损压缩,必要时再压缩编码或按比例缩小,照片按方向信息转正,不裁切画面。提交模型的每张图片默认不超过 4 MiB,最长边不超过 8192 像素,像素总量不超过 1600 万;原图不被修改,重复图片仍按原内容识别。
101
+
102
+ 下载默认最多等待 30 秒,读取过程中断或图片损坏只影响该图片,不会阻止同一批其他正常图片。AVIF 会转换成通用格式;动态图本轮读取第一帧,不能据此判断完整动画。QQ 缓存接口返回大 Base64 图片不会再受到普通消息 1 MiB 限制,但普通消息仍保持原有上限。
103
+
104
+ 需要调整时,可在 `.env` 设置 `VISION_SOURCE_MAX_MB`(4–64,默认 32)、`VISION_MODEL_MAX_MB`(1–4,默认 4)和 `VISION_DOWNLOAD_TIMEOUT_SECONDS`(5–120,默认 30),重启生效。图片能否理解还取决于所选模型的视觉能力和服务商接口是否可用。
105
+
106
+ ## 5. 连接 QQ
107
+
108
+ 打开「系统 → 连接 QQ」,顶部有两个选项:**OneBot 11** 与 **QQ 官方机器人**,同一时间只使用一个。切换后会断开当前连接;原来方式下的会话仍可查看,但不能再向它们发送消息。两种方式的差别见 `docs/zh/connect.md`,下面是操作步骤。
109
+
110
+ ### 方式一:OneBot 11
111
+
112
+ LuckyTri 只实现 OneBot 11 的反向 WebSocket 接口,接入端需要你自行准备和管理。
113
+
114
+ 1. 在本机 `.env` 中设置 `ONEBOT_TOKEN`,修改后重启 LuckyTri。
115
+ 2. 在接入端自己的网络设置里添加 OneBot 11 反向 WebSocket 客户端:
116
+
117
+ | 项目 | 本机填写 |
118
+ | ------------- | ----------------------------------- |
119
+ | 协议 | OneBot 11 |
120
+ | WebSocket URL | `ws://127.0.0.1:3210/onebot/v11/ws` |
121
+ | Token | `.env` 里的 `ONEBOT_TOKEN` |
122
+ | 消息格式 | 数组 |
123
+ | 启用 | 开启并保存 |
124
+
125
+ 3. 在接入端完成 QQ 登录。小号需要已加入目标群;LuckyTri 不保存 QQ 密码。
126
+
127
+ 这里的 `3210` 是 LuckyTri 的端口。接入端和 LuckyTri 不在同一台机器或放在容器里时,`127.0.0.1` 指向各自环境,需要改成 LuckyTri 的实际地址,并先配置 `ADMIN_TOKEN`。
128
+
129
+ ### 方式二:QQ 官方机器人
130
+
131
+ 不需要登录 QQ 账号,也不需要额外的接入端,LuckyTri 自己连接 QQ 开放平台。
132
+
133
+ 1. 在 QQ 开放平台创建机器人,记下 **AppID** 与 **AppSecret**,并按平台要求开通群聊与单聊消息、把机器人加入要测试的群或添加为联系人。平台的入口和审核规则会调整,以开放平台当前说明为准。
134
+ 2. 在「系统 → 连接 QQ」选择「QQ 官方机器人」,填写 AppID 和 AppSecret,点击保存。也可以在 `.env` 里设置 `QQBOT_APP_ID` 与 `QQBOT_APP_SECRET`。AppSecret 保存后不会再显示。
135
+ 3. 状态显示「已连接」后,到群里 @ 她或私聊一句。想让她看到群里的全部消息,需要在开放平台开启「接收所有消息」;没有开启时她只能收到 @ 她的消息,连接页会提示这一点。
136
+ 4. 官方接口不提供群名,新会话会以编号显示,可以在「对话」里给会话改名。
137
+
138
+ 两种方式都是:已连接只表示传输通了。到目标群发一条消息后,会话会出现在「对话」的会话列表里,新发现的会话默认暂停参与。
139
+
140
+ ## 6. 让 TA 参与会话
141
+
142
+ 1. 在「系统 → 运行开关」确认允许参与聊天,关闭模拟模式并保存。「此刻」的状态条也能看到这两项。
143
+ 2. 在「对话」选中目标群,在右侧「这个会话」里点「开启参与」,或直接拨动列表里的开关。
144
+ 3. 在 QQ 里 @ 她,或直接叫 TA 的名字。
145
+ 4. 回到「对话」查看消息和「为什么这么说」。
146
+
147
+ 开不开口由 TA 自己决定,没有参与概率和冷却。被点名、私聊、引用 TA 或出现危机信号时,TA 一定会细看,然后选择回答、简短反应、说不想聊或不出声;理由显示在「对话」和「此刻 → 今天的 TA」。普通群消息只在 TA 在意时细看——刚聊过、有人问大家、聊到 TA 的兴趣、熟人在说话、攒了不少消息——其余只扫一眼,不调用模型,下次细看时一起读。觉得 TA 太安静,可以在「天性」里提高「主动」。
148
+
149
+ TA 默认 02:00 睡、08:00 醒,在「天性 → 作息」里拖动月亮和太阳修改,或者关闭。睡着时被叫到,会等醒来再看;这时整个界面会变成夜晚。每分钟发言轮数限制仍然保留,用来避免短时间连续发言。
150
+
151
+ 在模型里打开「图片理解」后,私聊、@ 或引用里的图片会先由本机读取,再交给模型看画面。主模型不能看图时,在「对话 → 这个会话 → 会话设置」里另选一个打开了图片理解的视觉兼容模型。没有打开图片理解的模型不会收到画面。
152
+
153
+ ## 7. TA、记忆和知识
154
+
155
+ 「天性」是唯一由你编写的部分:名字、性格、兴趣、边界、底线和作息,每次保存为新的一版。拖动性格刻度时,旁边的光团会跟着变:温柔让颜色变暖、嘴角上扬,活泼让它跳得更快。
156
+
157
+ 其余的都从经历里长出来:TA 的自我是「内心」星图里的一颗颗星,TA 对每个人的感觉在「人际」,TA 在各群的样子在「人际」下方的群像里。点开可以看到每次变化的来源,觉得不对可以撤销;撤销过的内容之后不会被写回来。很久没被触及的线索会慢慢淡出,退到星图的「远方」;它们没有被删除,被聊到时会重新想起。TA 独处、写日记、夜里整理、回顾和改写自传的记录,以及 TA 在等的事(别人说的安排、TA 答应的事,可以撤销),都在「一生」。每日 Token 上限、后台份额、夜里整理和回顾间隔在「天性 → TA 的日子」里设置。
158
+
159
+ 「记忆」里先选择会话,也可以按人筛选。
160
+
161
+ - TA 对所有会话只有一份记忆:别处知道的事只在相关时想起;私聊里知道的标为「私下知道」,不当众说;被要求保密的只在原来的会话里。可以在每条记忆上调整分寸、锁定或撤销。关于某个人的记忆也会出现在「人际」的人物详情里。
162
+ - 群友说「记住,我……」会直接成为 TA 记得的事(密码、验证码之类不会记)。
163
+ - 资料放进「资料书架」的集合后按当前会话的可见范围检索。群聊不会装入其他私聊集合。
164
+ - 清空「对话」里的上下文不会删除长期记忆。
165
+
166
+ ## 8. 备份与停止
167
+
168
+ ```powershell
169
+ npm run backup
170
+ npm run stop
171
+ npm start
172
+ ```
173
+
174
+ 备份写到 `data/backups/`。数据库里有聊天、记忆和保存在库中的密钥,按私密文件保管。`.env` 不在这份备份里。
175
+
176
+ TA 的一切都存在这一个数据库里,所以服务运行时会自动备份:默认每 24 小时在另一个进程里做一份 `luckytri-auto-*.db`,只保留最近 7 份,做完会检查完整性。自动备份里有聊天、记忆、自我、日记和关系,但不带一天以前的模型调用记录(每条动辄几百 KB,会让备份大上十几倍),所以「为什么这么说」只能回看最近一天的。`npm run backup` 仍然是完整备份。可以在 `.env` 里调整:
177
+
178
+ | 变量 | 含义 | 默认 |
179
+ | ----------------------- | ---------------------------- | -------------- |
180
+ | `BACKUP_INTERVAL_HOURS` | 自动备份间隔,`0` 关闭 | `24` |
181
+ | `BACKUP_KEEP` | 保留几份自动备份 | `7` |
182
+ | `BACKUP_TRACE_DAYS` | 备份里保留最近几天的调用记录 | `1` |
183
+ | `BACKUP_DIR` | 备份目录 | `data/backups` |
184
+
185
+ 想立刻做一份自动备份:`node scripts/backup.js --auto --force`。自动备份失败会记在启动窗口或 `data/launcher.log`,并隔越来越久才重试。备份和数据库放在同一块磁盘上,防不了磁盘损坏;重要的备份请自己复制到别处。
186
+
187
+ 设置 `DB_PATH` 使用另一份数据库时,若未指定 `BACKUP_DIR`,备份默认写到该数据库旁的 `backups` 目录,避免多个实例的副本混在一起。
188
+
189
+ 在「系统 → 数据与备份」可以查看每份历史数据库副本的大小,设置每天的整理时间、完整备份保留天数和最多份数,也可以逐份勾选后手动清理。默认每天服务器本地时间 04:30 检查,完整备份超过 14 天或多于 2 份就会列入整理;数量可设为 0,表示定时不保留完整备份。完整备份和迁移快照没有固定硬锁,可以手动选择全部清理。自动备份继续按上面的独立规则轮换,只轮换本实例有身份的副本,最新两份作为恢复点保留;无法确认身份的旧副本、迁移、旧版本和升级前快照仅手动清理。
190
+
191
+ 执行前必须留下本实例近 72 小时内校验通过的自动备份,最新一份损坏时会尝试另一份。新备份清单带有数据库身份,防止另一实例的副本冒充当前实例的恢复保障;旧清单仍可验证和恢复,但需要更新自动备份后才能作为清理保障。备份和清理共用目录锁,运行库、同一文件的硬链接与正在使用的副本不能清理。清理失败时会列出失败文件,并准确统计已释放空间。要长期保留的历史恢复点,请先连同 `.db.json` 清单复制到另一块磁盘。
192
+
193
+ 恢复时先停止服务,把当前 `data/friend.db` 及同名 `-wal`、`-shm` 移开,再把选中的备份复制为 `data/friend.db`。设置了 `DB_PATH` 时操作该路径。不要把旧的 WAL 文件和恢复后的数据库放在一起。更稳妥的做法是用 `npm run recovery` 校验并恢复到一个全新的文件,见 `docs/zh/recovery.md`。
194
+
195
+ ## 9. 常见问题
196
+
197
+ | 现象 | 先检查 |
198
+ | ------------------------- | -------------------------------------------------------------------------------------------------- |
199
+ | 页面打不开 | Node 是否在运行,地址是否为 `127.0.0.1:3210` |
200
+ | 端口已被占用 | 先 `npm run stop`,不要结束所有 node 进程 |
201
+ | 令牌错误 | 使用 `ADMIN_TOKEN`;改 `.env` 后重启 |
202
+ | OneBot 未连接 | 接入端是否登录,URL、端口、路径和 `ONEBOT_TOKEN` 是否一致 |
203
+ | 官方机器人未连接 | AppID 与 AppSecret 是否正确,机器人是否已开通群聊与单聊消息,网络能否访问 QQ 开放平台 |
204
+ | 官方机器人只收到 @ 的消息 | 在开放平台开启「接收所有消息」;这是平台权限,不是 LuckyTri 的限制 |
205
+ | 连接成功但没有会话 | 在 QQ 再发一条新消息;OneBot 方式下检查消息格式是否为数组 |
206
+ | 群里不回复 | 全局开关、会话开关、模拟模式、密钥;「对话」里是「TA 扫了一眼」还是 TA 细看后选择了不出声 |
207
+ | @ 了也不回 | TA 是否睡着了(「此刻」,界面是夜晚)、每分钟限速、今日 Token 是否用完、模型错误,或 TA 自己的理由 |
208
+ | 模型 401 / 403 | 密钥和权限;环境变量密钥可能覆盖 LuckyTri 里保存的密钥 |
209
+ | 模型 404 | API 地址不要重复拼接 `/chat/completions`,核对模型 ID |
210
+ | 模型不返回 JSON | 换支持 JSON 对象输出的模型,先做连接测试 |
211
+ | 记忆没有出现 | 是否是明确的「记住,我……」,是否被撤销,分寸是否为「要 TA 保密」而当前不在原来的会话 |
212
+ | 界面动得太多 | 左下角「减少动画」;系统开启了减少动态效果时会自动关闭粒子和形变 |
213
+
214
+ 不要把 API 密钥、`.env` 或私聊原文发到公开位置。