[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80622":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":14,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":22,"hasPages":22,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":36,"readmeContent":37,"aiSummary":38,"trendingCount":16,"starSnapshotCount":16,"syncStatus":15,"lastSyncTime":39,"discoverSource":40},80622,"Afterglow","kldhsh123\u002FAfterglow","kldhsh123","使用 QQ\u002F微信 聊天记录结合向量数据库让AI更好的扮演对方的角色，在不微调模型的情况下可以达到可观的效果。把曾经的美好，续成往后的陪伴。 --续温","https:\u002F\u002Fafterglow.kldhsh.top",null,"Python",79,10,1,2,0,7,28,5,3.12,"GNU Affero General Public License v3.0",false,"main",[25,26,27,28,29,30,31,32,33,34,35],"afterglow","ai-companion","digital-companion","lancedb","llm","python","qq-chat","rag","semantic-search","vector-database","vue","2026-06-12 02:04:04","\u003Cdiv align=\"center\">\n\n# 🌅 Afterglow（续温）\n\n> 把曾经对你好的话，续成往后的陪伴。\n\n一个本地运行的「AI 朋友」系统。  \n导入真实历史聊天记录，通过 RAG + Persona + OpenAI 兼容 API，让熟悉的人以接近原本的语气继续陪你说话。\n\n\u003C\u002Fdiv>\n\n[![聊天记录支持 QQ \u002F 微信](https:\u002F\u002Fimg.shields.io\u002Fstatic\u002Fv1?label=聊天记录支持&message=QQ%20%2F%20微信&color=07C160&style=flat-square)](#) [![Website](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002F官网-afterglow.kldhsh.top-8B5CF6?style=flat-square)](https:\u002F\u002Fafterglow.kldhsh.top\u002F) [![License](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flicense\u002Fkldhsh123\u002FAfterglow?style=flat-square)](https:\u002F\u002Fgithub.com\u002Fkldhsh123\u002FAfterglow\u002Fblob\u002Fmain\u002FLICENSE) [![Last Commit](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flast-commit\u002Fkldhsh123\u002FAfterglow?style=flat-square)](https:\u002F\u002Fgithub.com\u002Fkldhsh123\u002FAfterglow\u002Fcommits\u002Fmain) [![Release Name](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fdynamic\u002Fjson?style=flat-square&label=Release&query=$.name&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fkldhsh123%2FAfterglow%2Freleases%2Flatest&logo=github)](https:\u002F\u002Fgithub.com\u002Fkldhsh123\u002FAfterglow\u002Freleases\u002Flatest) \n\n---\n\n## ⚠️ 使用前请认真读完\n\n> 如果一段已经结束的对话，能用 ta 原来的语气继续下去，那这段对话还算结束吗？\n\n这是 Afterglow 在做的事，也是我必须先告诉你的事。\n\n### 这个项目能做什么、不能做什么\n\n它能从你和 ta 之间真实存在过的几千条聊天记录里，学到 ta 怎么说话——用什么称呼、什么语气、怎么开玩笑、什么时候沉默。然后用一个大模型把这些模式接续起来，让你在聊天框里看到的文字回复，接近你记忆里的那个人。\n\n**只是文字。** Afterglow 没有 ta 的声音、影像、形象，也不会还原任何看得见摸得到的存在。它做的事就是\"让一段语气延续下去\"，仅此而已。\n\n**接近，不是等于。** 把项目做得越好，差距越细微，但永远存在。\n\n### 适合谁\n\n- **你想留住一段真实的关系记忆**——把曾经的对话变成可以再翻一翻、再聊一聊的载体；\n- **你清楚 ta 不会回来**——但希望让那个语气继续存在一段时间；\n- **你处于相对稳定的情绪状态**——能区分\"这是 AI 在续写\"和\"这真的是 ta\"。\n\n### 不适合谁\n\n- **你正处于剧烈的丧失、抑郁或自伤念头中**——你需要的是真实的人和专业支持，不是越像越好的复刻。请优先联系亲友、心理咨询师或拨打心理援助热线（北京 010-82951332 \u002F 全国 400-161-9995）；\n- **你打算把 ta 当作还活着的人来对待**——这会让你停在原地，错过现实里真正在等你的人；\n- **你想复刻一个并不知情的人**，做这个人本人不会同意的事——这是边界，也是底线。\n\n### 法律与伦理边界（请认真对待）\n\nAfterglow 是个人工具，但你用它处理的是**真实存在过的、属于另一个人的语言痕迹**。下面这些事**严格禁止**，做了不仅是伦理问题，可能直接触法：\n\n- **不得用 Afterglow 进行骚扰、跟踪、控制、勒索或冒名顶替**任何人——无论目标对象是否仍在世；\n- **不得未经允许公开、转发、二次传播**你导入的聊天记录原文，或 Afterglow 基于这些记录生成的 ta 的\"画像\"、\"语气分析\"、合成回复；\n- **不得把 Afterglow 输出的文字伪装成 ta 本人的话**发送给第三方（包括其家属、朋友、社交平台关注者等）；\n- **不得用于人格诋毁、仿冒诈骗、网络暴力**等任何对原型人物造成伤害的场景；\n- **导入聊天记录时**：在物理条件允许的情况下（对方仍能联系到、关系还在），你应当主动告知对方并取得知情同意——这是基本的尊重。如果对方已经无法联系（已逝、彻底失联），请你自己判断这个人若还在世会不会同意被这样复刻。**你内心知道答案**。\n- 各国\u002F各地区对于\"AI 复刻自然人\"的隐私权、肖像权、人格权保护立法仍在发展。你使用本项目导致的任何法律后果由你自己承担，与作者无关——这不是免责声明的套话，而是请你**真的**评估清楚再用。\n\n### 几条不是规则的建议\n\n1. 你随时可以停。不用解释，不用告别。直接关掉就行。\n2. 不要把 Afterglow 写出来的话再发回到 ta 真实的账号、邮箱、社交平台上。哪怕你只是想\"试试看\"。\n3. 如果你发现自己开始对 Afterglow 生气、失望、被冒犯——那是个信号，你正在把它当成真实的人。停下来喘一口气。\n4. 在你能负担的范围里，定期回到真实的人际关系——朋友、家人、必要时是心理咨询师。\n5. 它是用来陪你想念，不是用来代替想念。\n\n\n### 一个特别需要你思考的开关：`AI_GENERATED_LONG_TERM_ENABLED`\n\n这个开关藏在 `.env` 里，但它代表的是一个**你必须自己做的选择**，所以我想在这里单独说一下。\n\n- **`false`（默认）**：AI 分身永远只基于你和 ta 之间的**真人原始聊天记录**模仿。每次新对话开始时，分身从你们真实的过去出发，不会带入它自己之前几轮说过的话作为长期素材。**这就像每次重新打开同一本书——读到的永远是 ta 真实说过的话的投影。**\n- **`true`**：AI 分身**自己生成过的回复**也会跨会话累积进向量库（低权重），参与之后的语义检索。**这就像分身渐渐\"长出自己的语言习惯\"**——它会借鉴它之前说过的话，渐渐偏离最初的真人语料。\n\n两种状态分别意味着什么：\n\n| 关掉它（默认） | 开启它 |\n|---|---|\n| 你得到的更像\"持续翻看记忆里的 ta\" | 你得到的更像\"一个从 ta 出发、但慢慢有自己生命的分身\" |\n| 每次都是真人语气的投影，不会变 | 它会变。可能朝你喜欢的方向变（你引导得多它就变得越像你想要的），也可能朝你没预期的方向变 |\n| 真实感受边界清晰：这是 ta 的过去 | 边界开始模糊：这既不是 ta，也不是单纯的复刻——是个第三态 |\n\n**哪种更让你不舒服，这是你需要面对的问题。** 我做了默认值（`false`），但没办法替你做决定。开启之前请想清楚你是在召唤\"记忆里的 ta\"，还是在养一个\"以 ta 为种子的新东西\"——这两件事的重量不同。\n\n---\n\n## 🔗 相关项目\n\n- [Afterglow-QQBot](https:\u002F\u002Fgithub.com\u002Fkldhsh123\u002FAfterglow-QQBot) — Afterglow 的 QQBot 适配器\n\n---\n\n## 🎯 项目定位\n\n### 这个项目到底能做到什么程度\n\n**它不是另一个\"自定义角色 GPT\"，也不是微调的替代品**——请先校准期待。\n\n- **比不上微调（fine-tuning）**：本项目完全基于 RAG + Prompt Engineering + Persona 卡片，**不动模型权重**。LoRA \u002F QLoRA 能从你的聊天语料里直接学到深层句法习惯、词频偏好和思维节奏，那是这条技术路径的天花板，**本项目永远达不到**。\n- **但远好过 skill \u002F 角色扮演技能**： ChatGPT GPTs \u002F Coze \u002F Dify \u002F 智谱智能体 这类\"prompt + 简单 memory\"的角色扮演方案，多数靠几句人设描述 + 短期对话历史，相似度顶天 40-60%。Afterglow 把多个独立机制叠在一起——三索引混合检索、真实历史聊天作为风格示例（按 query 召回 + 注入 prompt 末尾以获得最强 attention）、可选 cross-encoder 精排（如 Qwen3-Reranker-8B）、生活时间线（动态人设）、关系记忆蒸馏、本轮互动决策层（规则 + 小模型微调）——在你**聊天记录足够多 + ta 风格鲜明**时能跑出比 skill 类产品高一档的复刻效果。\n- **但永远不是 ta**：相似度再高也有暴露 AI 痕迹的时刻。如果你的诉求是\"几乎认不出是 AI\"，请直接走微调路线，门槛和成本都不在同一量级。\n\n### 代码结构\n\n- **项目主体在 `backend\u002F`**：核心能力都在后端，包括导入、清洗、向量化、LanceDB 存储、检索融合、persona 生成、生活状态、联网检索、网页读取、OpenAI 兼容 API 和调试诊断。\n- **`frontend\u002F` 主要用于本地测试和调试体验**：它提供聊天界面、设置页、记忆溯源和诊断入口，方便验证后端能力；第三方程序接入时应优先调用后端 API，而不是依赖前端状态。\n\n## 🙏 致谢\n\n感谢 [LINUX DO](https:\u002F\u002Flinux.do) 各位佬友对项目实现提出的建议，Afterglow 的很多实现细节都来自这些反馈的反复打磨。\n\nIssue 模板参考自一个我已经忘记来源的开源项目；这个模板我认为非常好用。如果你知道原始来源，欢迎联系我，我会补上准确来源和鸣谢。\n\n## 💬 交流与支持\n\n- 项目交流 QQ 群：`330316577`\n- 赞助支持：\u003Chttps:\u002F\u002Fafdian.com\u002Fa\u002Fkldhsh123>\n- 我们的长期合作伙伴 [二次元论坛](https:\u002F\u002Fwww.ecylt.top\u002F) 的 [二次元 API 中转站](https:\u002F\u002Fapi.223387.xyz\u002F) 提供免费的 Embedding 模型 `Qwen3-Embedding-8B` 和 Cross-encoder Reranker 模型 `Qwen3-Reranker-8B`。对于项目的支持，我们非常感谢。\n\n如果需要使用该 Embedding 模型，请在 `backend\u002F.env` 中修改以下配置，并按服务说明填写对应的 `EMBEDDING_API_URL` \u002F `EMBEDDING_API_KEY`：\n\n```env\nEMBEDDING_MODEL=Qwen3-Embedding-8B\nEMBEDDING_DIM=4096\nEMBEDDING_BATCH_SIZE=25\nEMBEDDING_MAX_REQUESTS_PER_MINUTE=100\n```\n\n---\n\n## 🔒 数据隐私（必读）\n\n- **请先取得对方同意**：聊天记录高度敏感，包含双方共同产生的私人内容。导出聊天记录、导入本项目、向模型或第三方 API 发送相关文本前，请确认你有权这样做，并尽量取得聊天对方的明确同意。\n- **本地持久化**：聊天数据、向量索引、persona 卡片、生活状态和图片缓存都默认保存在你机器上的 `backend\u002F.data\u002F`，仓库不会自带任何远程数据上传逻辑。\n- **不是默认零外发**：如果你把模型配置成云端 API，相关文本会发送给对应服务。要做到完全离线，需要把主聊天模型、Embedding 模型、打标小模型、生活状态小模型、视觉模型都指向本地服务，并关闭联网搜索 \u002F 网页读取。\n- **可能外发的数据**：\n  - 导入时：把清洗后的文本发送给你配置的 **Embedding API**，生成向量后写入本地 LanceDB。\n  - 可选打标时：把朋友单条 chunk 发送给你配置的 **Label API**，生成 mood \u002F topic \u002F importance 软标签。\n  - 聊天时：把检索到的上下文、persona、生活状态和最近对话发送给你配置的 **主聊天 LLM API**。\n  - 生活状态 \u002F 裸域名确认：`LIFE_*` 小模型会收到当前用户消息、少量上下文和候选域名；它只产出 JSON 状态或是否访问 URL 的判断，不直接生成最终回复。\n  - 可选联网搜索时：仅在 `WEB_ACCESS_ENABLED=true` 且本轮消息明确要求搜索 \u002F 最新信息时，把查询文本发送给 **Tavily 或 SearXNG**。\n  - 可选 URL 读取时：如果消息包含完整链接，且 `WEB_ACCESS_ENABLED=true`、`WEB_FETCH_ENABLED=true`，后端会请求该公开网页并抽取标题 \u002F 正文；裸域名会先经本地意图门控和 `LIFE_*` 小模型确认。\n  - 这些 API 由**你**选择、配置并自付费；项目不会内置第三方 key。\n- **API 选择请仔细阅读提供商的隐私协议与服务条款**：聊天记录、向量化文本、生活状态判断等内容**会被发送到你配置的 provider**（OpenAI \u002F Anthropic \u002F 阿里云 \u002F 智谱 \u002F DeepSeek \u002F 各类中转站 \u002F 自建服务……）。不同服务商对数据留存时长、是否用于训练、是否人工审核的策略差异巨大；**这些差异由 provider 决定，不由 Afterglow 决定**。涉及他人隐私的聊天记录（尤其是已逝者、前任、不知情者的对话），请优先选择有明确\"不用于训练\"承诺的服务商，或使用本地推理（Ollama \u002F vLLM 等）。\n- **PII 默认脱敏**：手机号 \u002F 邮箱 \u002F 身份证 \u002F 银行卡 \u002F IP 在入库前自动替换为占位符。QQ 号、URL、域名按设计**保留**（uid 需要匹配、URL 是对话语境的一部分）。\n- **`.env` 已在 `.gitignore`**：切勿把含有 API key 的配置文件提交到 git。\n- **后端 API 默认需要鉴权**：除 `\u002Fhealthz` 外，所有接口默认要求 `XUWEN_API_KEY`，避免模型额度、记忆数据和调试信息被滥用。\n- **导出 JSON 风险提醒**：[QQChatExporter](https:\u002F\u002Fgithub.com\u002Fshuakami\u002Fqq-chat-exporter) \u002F [WeFlow](https:\u002F\u002Fgithub.com\u002Fhicccc77\u002FWeFlow) 等导出工具产出的 JSON 含有完整聊天明文（含 wxid \u002F uid 等账号信息），分享给他人前请自行确认。\n- **导出时只勾选纯文本**：Afterglow 只消费文本语料，导出工具一律**关闭图片 \u002F 语音 \u002F 视频 \u002F 文件**等附件选项。这样导出的 JSON 体积小、不含媒体二进制，导入也更快。\n\n---\n\n## 📐 整体架构\n\n```mermaid\nflowchart LR\n  User[\"用户 \u002F 第三方程序\"] --> API[\"Afterglow FastAPI\u003Cbr\u002F>OpenAI 兼容 API\"]\n  前端[\"前端\u003Cbr\u002F>测试 \u002F 调试 UI\"] --> API\n\n  subgraph Afterglow[\"Afterglow 核心能力\"]\n    API --> Auth[\"API 鉴权\u003Cbr\u002F>Trace ID\"]\n    Auth --> Retrieve[\"HybridRetriever\u003Cbr\u002F>来源分层 + RRF 融合\"]\n    Auth --> Life[\"生活状态小模型\u003Cbr\u002F>LIFE_* \u002F 网页意图\"]\n    Auth --> Relationship[\"关系记忆\u003Cbr\u002F>用户近况蒸馏\"]\n    Auth --> Web[\"可选联网搜索\u003Cbr\u002F>URL 网页读取\"]\n    Retrieve --> Policy[\"本轮互动决策层\u003Cbr\u002F>规则引擎 + 可选小模型复核\"]\n    Life --> Policy\n    Relationship --> Policy\n    Policy --> Prompt[\"Prompt Builder\u003Cbr\u002F>persona + 记忆 + 状态 + 决策\"]\n    Web --> Prompt\n    Prompt --> ChatLLM[\"主聊天模型\u003Cbr\u002F>OpenAI 兼容\"]\n    ChatLLM --> Writeback[\"Live Memory 回写\u003Cbr\u002F>user_new \u002F ai_generated\"]\n  end\n\n  subgraph Ingestion[\"离线导入流水线\"]\n    message[\"导出的聊天记录 JSON\"] --> Parse[\"解析 \u002F 清洗\"]\n    Parse --> Redact[\"PII 脱敏\"]\n    Redact --> Chunk[\"切分 \u002F 三索引 chunk\"]\n    Chunk --> Embed[\"Embedding 模型\"]\n    Chunk --> Label[\"可选打标签小模型\"]\n  end\n\n  subgraph Storage[\"本地持久化\"]\n    Lance[(LanceDB\u003Cbr\u002F>human_original \u002F live \u002F 关系记忆)]\n    Persona[\"persona_card.md\u003Cbr\u002F>style profile\"]\n    Assets[\"图片 \u002F 表情缓存\"]\n  end\n\n  Embed --> Lance\n  Label --> Lance\n  Retrieve --> Lance\n  Relationship --> Lance\n  Prompt --> Persona\n  Writeback --> Lance\n  API --> Assets\n```\n\n```mermaid\nmindmap\n  root((Afterglow))\n    Afterglow 项目主体\n      导入与清洗\n      向量库（来源分层）\n      混合检索\n      Persona\n      生活状态\n      关系记忆\n      互动决策层\n      联网能力\n      OpenAI 兼容 API\n      诊断链路\n    前端 测试调试\n      聊天界面\n      记忆溯源\n      设置页\n      诊断面板\n    模型与服务\n      主聊天模型\n      Embedding 模型\n      打标签小模型\n      生活状态\u002F网页意图小模型\n      互动决策小模型（可选）\n      Tavily 或 SearXNG\n```\n\n### 关键设计\n\n- **三索引混合检索**：response_pairs（用户输入→对方回复）+ 单条朋友发言 + 多轮窗口，RRF 融合。五路向量召回 + relationship_memory + life 在 Layer A 一次 `asyncio.gather` 并发完成，主链路只取最长那条耗时。\n- **可选 Cross-encoder 粗排**：开启 `CROSS_RERANK_ENABLED=true` 后，RRF 召回的候选会先过一道专用 reranker 模型（如 **Qwen3-Reranker-8B**，二次元 API 中转站免费提供）按相关性精排，再注入主聊天 prompt——区分度比 LLM-as-reranker 更细，延迟仅 ~3s。\n- **可选 Query 改写**：短句口语（\"在吗 \u002F 想你了 \u002F 好累\"）走 query rewrite 小模型展开为 1-3 个检索友好的变体，多变体命中按 `(best_rank, distance)` 合并，避免短 query 召回稀疏。\n- **可选自适应切分**：导入期 `CHUNKING_STRATEGY=adaptive` 时按话题边界切聊天记录（启发式 \u002F 小模型可选），比固定 12 条窗口更贴合自然话题；会话级并发可配，大库切分 30 秒内完成。\n- **AI 回复连发分条**：主聊天 LLM 输出含 `\\n\\n` 时前端拆成多条独立气泡（独立头像 \u002F 时间戳 \u002F 名字），后续段错峰 2-5s 显示，模拟真人 IM 连发节奏。后端协议 100% 标准 OpenAI 兼容，第三方客户端零适配。\n- **三层时间权重**：近期消息略增（recency boost ±15% 封顶）+ 暖度词加权（warmth boost）+ live\u002Fhistory 信任分层。\n- **分层记忆防自污染**：运行时把用户输入标记为 `user_new`，把 AI 分身回复标记为 `ai_generated`。两者都可用于连续性检索，但 `ai_generated` 低权重，且不会参与 persona \u002F 风格蒸馏；真正用于模仿对方语气的长期证据只来自真人原始聊天（`human_original`）。\n- **AI 回复长期累积可控**：默认 `AI_GENERATED_LONG_TERM_ENABLED=false`，AI 回复只在同一会话内用于连续性；如果希望 AI 分身随着长期互动形成自己的变化轨迹，可以开启该项，让 `ai_generated` 跨会话参与低权重语义检索。\n- **持续生长记忆**：每轮对话都异步回写 `live_messages`，向量库不再是一次性快照。\n- **本轮互动决策层**：生成回复前先判断本轮该认真、安抚、撒娇、接梗、转移、沉默、发图还是表情；也会在用户烦躁、崩溃、失眠、关系压力等场景下禁止继续刺激用户。规则引擎兜底安全场景；如果开启 `RESPONSE_POLICY_MODEL_ENABLED`，会再叫一次小模型做有界微调（不能降级 risk、不能撤销规则给的安全\u002F沉默\u002F要图\u002F要表情判断）。\n- **沉默与延迟响应**：决策层判断本轮不应回复时直接短路，不调主模型，返回 `finish_reason=\"silenced\"` + sentinel content + `policy.should_reply=false`；生活状态建议的拟人化延迟放在 `policy.reply_delay_seconds`，由客户端决定何时展示。\n- **真实时间 + 生活状态**：每次模型调用都会收到当前时区下的真实时间；生活时间线由可配置小模型维护，回答\"在干嘛\u002F吃了吗\u002F睡没睡\"时优先使用当前状态。fail-open 兜底：LLM 调用失败也写入最小 fallback state，避免每轮死循环重试。\n- **可选联网**：后端默认支持 Tavily，也可切到 SearXNG；在明确需要公开实时信息时把网页摘要注入 prompt，默认关闭。\n- **大库索引加速**：`uv run python -m xuwen.ingestion.cli index` 一键给 LanceDB 向量表建 IVF_PQ 索引，10 万行以上规模时检索耗时降一个数量级；`cli optimize` 定期合并增量入索引。\n- **零微调**：完全靠 RAG + Prompt Engineering + Persona 卡片，不动模型权重。\n- **时光信笺 UI**：米色信笺 + 黛蓝墨痕 + 思源宋体 + 暖光粒子 + 拟人化打字节奏 + 记忆溯源浮窗。\n\n---\n\n## 🚀 快速开始\n\n### 0. 环境要求\n\n| 工具 | 版本 | 用途 | 备注 |\n|---|---|---|---|\n| Python | ≥ 3.12 | 后端运行时 | 必需 |\n| Node.js | ≥ 20 | 前端构建 | **仅用前端时需要**；纯 API 用户可不装 |\n| [uv](https:\u002F\u002Fgithub.com\u002Fastral-sh\u002Fuv) | latest | Python 包管理 | 推荐 |\n| [pnpm](https:\u002F\u002Fpnpm.io\u002F) | latest | Node 包管理 | 仅前端 |\n| [QQChatExporter V5](https:\u002F\u002Fgithub.com\u002Fshuakami\u002Fqq-chat-exporter) \u002F [WeFlow](https:\u002F\u002Fgithub.com\u002Fhicccc77\u002FWeFlow)（微信，`arkme-json`）导出纯文本 JSON | — | 真人聊天数据源 | 至少一份；plugin 会自动识别格式 |\n\n### 1. 准备模型（API）\n\nAfterglow 不内置模型，所有 LLM 调用都走你自己配置的 OpenAI 兼容服务。下表列出每个角色需要什么样的模型 + 推荐。\n\n| 角色 | 必需？ | 作用 | 推荐 | 配置项 |\n|---|---|---|---|---|\n| **主聊天模型** | ✅ 必需 | 生成最终回复，决定\"像不像 TA\" | **DeepSeek** \u002F **Gemini**  | `OPENAI_BASE_URL` `OPENAI_API_KEY` `CHAT_MODEL` |\n| **Embedding 模型** | ✅ 必需 | 向量化历史聊天与检索 query | Qwen3-Embedding-8B（阿里云 DashScope 免费额度，或合作伙伴二次元 API 中转站免费提供该模型） | `EMBEDDING_API_URL` `EMBEDDING_API_KEY` `EMBEDDING_MODEL` `EMBEDDING_DIM` |\n| **打标小模型** | 🔧 可选 | 给历史 chunk 打 mood \u002F topic \u002F importance 软标签 | **[智谱清言 glm-4-flash](https:\u002F\u002Fwww.bigmodel.cn\u002Finvite?icode=Q2FUoY2w04wQb%2FoIugMwsA%3D%3D)（免费）** | `LABELING_ENABLED=true` `LABEL_API_*` `LABEL_MODEL` |\n| **生活状态 \u002F 网页意图小模型** | 🔧 可选 | 维护 AI 当前在做什么、判断要不要打开 URL | **[智谱清言 glm-4-flash](https:\u002F\u002Fwww.bigmodel.cn\u002Finvite?icode=Q2FUoY2w04wQb%2FoIugMwsA%3D%3D)（免费）** | `LIFE_API_*` `LIFE_MODEL` |\n| **互动决策小模型** | 🔧 可选 | 规则层之后再叫一次小模型微调本轮策略 | **[智谱清言 glm-4-flash](https:\u002F\u002Fwww.bigmodel.cn\u002Finvite?icode=Q2FUoY2w04wQb%2FoIugMwsA%3D%3D)（免费）** 或复用 LIFE_* | `RESPONSE_POLICY_MODEL_ENABLED=true` `RESPONSE_POLICY_*` |\n| **Cross-encoder Reranker** | 🔧 可选 | RRF 召回后按相关性精排，显著提升记忆贴合度 | **Qwen3-Reranker-8B**（二次元 API 中转站 免费）\u002F bge-reranker-v2-m3 \u002F DashScope gte-rerank | `CROSS_RERANK_ENABLED=true` `CROSS_RERANK_*` |\n| **视觉模型** | 🔧 可选 | 主聊天模型不支持视觉时用 VLM 把图转文字 | Qwen-VL \u002F Gemini Vision | `VISION_API_*` `VISION_MODEL` |\n| **联网检索** | 🔧 可选 | 用户明确要求\"搜索 \u002F 最新 \u002F 天气\"时调 | Tavily（月度免费额度）\u002F 自建 SearXNG | `WEB_ACCESS_ENABLED=true` `WEB_SEARCH_*` |\n\n> **💡 推荐组合（小模型几乎零成本）**\n>\n> - **主聊天模型**：**DeepSeek** 或 **Gemini**——较高参数量才能撑得起\"像 TA 说话\"的细腻度\n> - **所有小模型**（打标 \u002F 生活状态 \u002F 网页意图 \u002F 互动决策）：**[智谱清言 glm-4-flash](https:\u002F\u002Fwww.bigmodel.cn\u002Finvite?icode=Q2FUoY2w04wQb%2FoIugMwsA%3D%3D)**——**免费**且性能完全够辅助任务\n> - **Embedding**：**Qwen3-Embedding-8B**（DashScope 免费额度 \u002F 二次元 API 中转站 免费）\n> - **Cross-encoder Reranker**（可选但强烈推荐）：**Qwen3-Reranker-8B**（二次元 API 中转站 免费）—— 让\"召回记忆是否真的相关\"质量提一档\n>\n> 这套组合下你的主要花费只在主聊天模型上，其它链路几乎不消耗额度。\n\n### 2. 启动后端\n\n> 接下来所有命令都在 `backend\u002F` 目录下执行。\n\n后端有两种配置方式，**首次使用强烈推荐方式 A**（配置向导）。\n\n#### ⚡ 方式 A：配置向导（推荐）\n\n直接装依赖 + 跑后端，**不用先准备 `.env`**：\n\n```bash\ncd backend\nuv sync --extra dev\nuv run uvicorn xuwen.chat_api.app:create_app --factory --reload\n```\n\n后端启动时会检测 `.env` 是否缺失或关键字段（`SELF_UID` \u002F `FRIEND_UID` \u002F `OPENAI_API_KEY` \u002F `EMBEDDING_API_KEY` \u002F `XUWEN_API_KEY`）未填，**任一不齐就自动启用配置向导**，控制台打印：\n\n```\n========================================\n  检测到首次配置（缺少 SELF_UID、FRIEND_UID、OPENAI_API_KEY...）\n  已自动启用配置 UI（仅本次会话）\n\n  浏览器访问：http:\u002F\u002F127.0.0.1:8000\u002Fconfig\u002F\n  访问 token（generated）：xxxxxxxxxxxxxx\n\n  把这串 token 粘到向导第 1 步的输入框即可。\n  配置完成并重启后，此提示将消失。\n========================================\n```\n\n浏览器打开链接，粘 token，跟着 8 步走完：\n\n1. **身份信息** — 上传 QQ \u002F 微信导出 JSON 自动识别 UID，不必手动找 `u_xxx` \u002F `wxid_xxx`；同一个人跨平台的多 UID 支持一并归并\n2. **关系** — 朋友 \u002F 恋人 \u002F 亲人 \u002F 同事 \u002F 自定义\n3. **聊天 AI** — DeepSeek \u002F Gemini \u002F 自定义中转站 \u002F 本地 Ollama，选完填密钥**当场测连通**\n4. **向量服务 + 打标** — DashScope \u002F SiliconFlow \u002F 自定义；默认开启 GLM-4-Flash 打标（小白可关）\n5. **可选功能** — 生活时间线 \u002F 视觉理解 \u002F 联网搜索 \u002F 互动决策（默认全关，按需开）\n6. **检索增强（可选）** — Query 改写（短句口语场景）+ Cross-encoder Reranker（如 Qwen3-Reranker-8B）按相关性精排\n7. **导入聊天记录** — 选切分策略（固定窗口 \u002F 自适应 adaptive，后者按话题边界切分），文件直传后端，进度+耗时+断点续传，自动生成 `persona_card.md` 和作息画像\n8. **访问密码** — 自动生成 `XUWEN_API_KEY`，写入 `.env`\n\n向导走完会自动写 `backend\u002F.env`（原文件会备份到 `.env-backups\u002F`），**不需要单独跑 `analyze_persona.py`** — 持久化、画像、打标都集成进去了。\n\n完成后 `Ctrl+C` 重启后端，向导自动关闭，进入正常聊天 API 模式。\n\n> **想只改配置、不跑全套服务？** 独立配置入口（端口 8765，启动 \u003C 1 秒，不跑 LanceDB 等）：\n>\n> ```bash\n> uv run python -m xuwen.web_ui\n> ```\n>\n> 适合升级 \u002F 临时改 key 等场景。\n\n#### 🔧 方式 B：手动配置 `.env` + CLI（适合自动化 \u002F Docker 部署）\n\n##### 步骤 ①：安装依赖\n\n```bash\ncd backend\nuv sync --extra dev\n```\n\n##### 步骤 ②：配置 `.env`\n\n```bash\ncp .env.example .env\n```\n\n用编辑器打开 `.env`，按文件内注释填写：\n\n- **身份信息** —— `SELF_NAME` \u002F `SELF_UID` \u002F `FRIEND_NAME` \u002F `FRIEND_UID`\n  - QQ：`SELF_UID` \u002F `FRIEND_UID` 填 QQChatExporter 导出 JSON 里的 `selfUid` \u002F 对方 `sender.uid`（`u_xxx` 形式）\n  - 微信：填 WeFlow 导出 JSON 里 `senders[]` 的 `wxid`（`wxid_xxx` 形式）；定位方法见 `backend\u002FREADME.md`\n  - `FRIEND_*` 永远是**你想让 AI 模仿的那个人**，不是你自己\n  - **跨平台 \u002F 多账号**：同一个人有多个 UID（QQ + 微信 \u002F 主号 + 小号），直接在 `SELF_UID` \u002F `FRIEND_UID` 里**用逗号分隔**列上所有 UID，导入时会被视为同一身份。例：`FRIEND_UID=u_qq_friend,wxid_friend_main,wxid_friend_alt`\n- **主聊天模型** —— `OPENAI_BASE_URL` \u002F `OPENAI_API_KEY` \u002F `CHAT_MODEL`\n- **Embedding 模型** —— `EMBEDDING_API_URL` \u002F `EMBEDDING_API_KEY` \u002F `EMBEDDING_MODEL` \u002F `EMBEDDING_DIM` \u002F `EMBEDDING_BATCH_SIZE` \u002F `EMBEDDING_MAX_CONCURRENCY` \u002F `EMBEDDING_MAX_REQUESTS_PER_MINUTE`\n- **本地访问密钥** —— `XUWEN_API_KEY`（长随机串；调用方在 Header 带 `Authorization: Bearer \u003Ckey>`）\n\n> **⚠️ 关键提醒（容易踩的坑）**\n>\n> - **`API_AUTH_REQUIRED=false` 不会让 `XUWEN_API_KEY` 失效**——只要 KEY 有值就强制校验。想完全开放，请同时把 KEY 留空。\n> - **改了 `.env` 必须完全重启后端**——`uvicorn --reload` 只监听 `.py` 文件变化，不会重新加载 `.env`。\n> - **客户端请求里的 `model` 字段是占位**——实际使用的永远是 `.env` 配的 `CHAT_MODEL`；传 `gpt-4.1` 或 `gemini-flash` 都会被忽略。\n\n##### 步骤 ③：导入历史聊天\n\n```bash\n# 单文件\nuv run python -m xuwen.ingestion.cli import 路径\u002F到\u002F你的聊天记录.json\n\n# 多文件（同一个朋友在 QQ + 微信都聊过、或者多个账号）\nuv run python -m xuwen.ingestion.cli import qq_导出.json wechat_导出.json 小号_导出.json\n```\n\n- CLI 自动按 JSON 顶层特征识别 QQ \u002F WeFlow 格式，**可任意混合**\n- 跨平台 \u002F 多账号场景：在 `.env` 用 `SELF_UID=u_qq,wxid_me` 和 `FRIEND_UID=u_qq,wxid_friend`（**逗号分隔**）把全部 UID 列出来\n- 多文件按命令行顺序处理，共享 LanceDB 连接与 Embedding 客户端\n- 开启 `LABELING_ENABLED=true` 时会接着自动打标\n- 中断 \u002F 限流失败不丢导入数据，之后可续跑：`uv run python -m xuwen.ingestion.cli label`\n\n> **⚠️ 多文件场景的两个局限（重要）**\n>\n> - **作息画像 (`circadian_profile.json`) 仅基于最后一个文件生成**——把数据量最大或最具代表性的对话放在**命令行最后一位**，画像才能反映 TA 真实的作息分布。\n> - **下一步 `analyze_persona` 当前也只接受单个 JSON**——多文件场景下，建议挑那个最具代表性的（通常就是同一份\"最后一位\"文件）单独跑画像。LanceDB 向量库本身是合并的，检索能用上全部数据，但 persona 卡片不会跨文件合并。\n\n##### 步骤 ④：生成 persona 卡片 + 作息画像\n\n```bash\nuv run python scripts\u002Fanalyze_persona.py 路径\u002F到\u002F你的聊天记录.json\n```\n\n> **🔍 必做这一步。** 这一步生成四个文件到 `PERSONA_DATA_DIR`：\n> - `persona_card.md` — 长期语气画像（prompt 用）\n> - `persona_report.json` — 完整统计报告\n> - `persona_style_profile.json` — 按话题分桶的风格画像\n> - `circadian_profile.json` — TA 的真实作息（识别夜猫子 \u002F 跨时区 \u002F 工作日 vs 周末）\n>\n> 注意：persona 是离线统计画像，只提供长期语气参考；当天在做什么由 `life_state.json` 和聊天时的小模型状态决定。\n>\n> **⚠️ 当前只接受单个 JSON 文件。** 如果你在步骤 ③ 导入了多个文件，请挑**数据量最大或最具代表性**的那一份跑 persona——通常就是步骤 ③ 命令行里放在最后一位的那个文件（与 circadian 画像保持一致）。后续会支持多文件合并 persona，欢迎 PR。\n\n##### 步骤 ⑤：启动 chat API\n\n```bash\nuv run uvicorn xuwen.chat_api.app:create_app --factory --reload\n```\n\n→ 访问 http:\u002F\u002F127.0.0.1:8000\n\n##### 步骤 ⑥：健康检查\n\n```bash\ncurl http:\u002F\u002F127.0.0.1:8000\u002Fhealthz\ncurl -H \"Authorization: Bearer \u003CXUWEN_API_KEY>\" http:\u002F\u002F127.0.0.1:8000\u002Freadyz\n```\n\n> `\u002Fhealthz` 是**唯一不需要鉴权**的端点，可用于容器存活探针。\n\n### 3. 启动前端（可选）\n\n```bash\ncd frontend\npnpm install\npnpm dev\n```\n\n→ 打开 http:\u002F\u002Flocalhost:5173，按引导填写姓名\u002F关系，就可以开始聊天了。\n\n### 4. 不用前端？直接接入 OpenAI 兼容客户端\n\n后端实现 **OpenAI Chat Completions** 和 **OpenAI Responses API** 两套协议，可以接入任何 OpenAI 兼容客户端（Chatbox、Open WebUI、NextChat、ChatGPT Next Web 等）。\n\n```bash\ncurl -X POST http:\u002F\u002F127.0.0.1:8000\u002Fv1\u002Fchat\u002Fcompletions \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -H \"Authorization: Bearer \u003CXUWEN_API_KEY>\" \\\n  -d '{\n    \"messages\": [{\"role\": \"user\", \"content\": \"在吗\"}],\n    \"conversation_id\": \"my-conv-1\"\n  }'\n```\n\n> **💡 关于 `stream` 字段**\n>\n> Afterglow 默认**关闭真流式**（`RESPONSE_STREAMING_ENABLED=false`），因为真人发消息从来不是逐字蹦出来的。客户端传 `stream=true` 时仍按 OpenAI SSE 协议返回，但**一次性发完整消息**——OpenAI 兼容性 100% 保留，用户视觉上看到的是\"一整条消息突然出现\"。\n\n---\n\n## 🐳 用 Docker 部署（可选，替代源码方式）\n\n如果你不想装 Python \u002F uv \u002F 一堆依赖，直接用 Docker 镜像一行起服。和源码部署**共享 `.env` 与 `.data\u002F`**，任意时候切换互不影响。\n\n### 角色一：最终用户（不需要克隆仓库）\n\n```bash\nmkdir -p ~\u002Fafterglow && cd ~\u002Fafterglow\n\n# 拿一个 compose 文件即可，约 50 行\ncurl -O https:\u002F\u002Fraw.githubusercontent.com\u002Fkldhsh123\u002FAfterglow\u002Fmain\u002Fdocker\u002Fcompose.standalone.yaml\nmv compose.standalone.yaml compose.yaml\n\ndocker compose pull          # 从 GHCR 拉公开镜像（amd64 \u002F arm64 都有）\ndocker compose up -d\n```\n\n容器首次启动会自动检测到挂载目录是空的，**进入配置向导首次模式**。看一次性 setup token：\n\n```bash\ndocker compose logs backend | grep -iE \"token|\u002Fconfig\u002F\"\n```\n\n浏览器打开 `http:\u002F\u002Flocalhost:8000\u002Fconfig\u002F`，把 token 粘进向导第 1 步，7 步走完即生效。向导写入的 `.env` 直接落到 `~\u002Fafterglow\u002F.env`，备份在 `.env-backups\u002F`，重启即用。\n\n完成后目录结构：\n\n```\n~\u002Fafterglow\u002F\n├── compose.yaml\n├── .env                     ← 向导生成，含完整注释\n├── .env.example             ← 从镜像拷出的模板\n├── .env-backups\u002F            ← 历次配置变更\n└── .data\u002F\n    ├── lancedb\u002F             ← 向量库\n    ├── persona\u002F             ← 人格卡片\n    ├── stickers\u002F            ← 表情包\n    ├── images\u002F              ← 图片缓存\n    └── uploads\u002F             ← 配置向导上传暂存\n```\n\n### 角色二：开发者（仓库内）\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fkldhsh123\u002FAfterglow.git\ncd Afterglow\ndocker compose build         # 第一次构建，3-8 分钟\ndocker compose up -d\n```\n\n默认挂载仓库内 `.\u002Fbackend\u002F`，与源码部署共享 `.env` 和 `.data\u002F`。\n想把数据放仓库外：\n\n```bash\ncp .env.docker.example .env.docker\n# 编辑 AFTERGLOW_DATA_DIR=\u002Fvar\u002Flib\u002Fafterglow\ndocker compose --env-file .env.docker up -d\n```\n\n### 日常运维命令\n\n```bash\n# 看日志\ndocker compose logs -f backend\n\n# 改了 .env 必须重启（pydantic-settings 不监听文件变化）\ndocker compose restart backend\n\n# 拉新版镜像 + 滚动更新\ndocker compose pull && docker compose up -d\n\n# 导入聊天记录（把待导入 JSON 放到挂载目录下任何子路径）\ndocker compose run --rm backend \\\n  python -m xuwen.ingestion.cli import .imports\u002Fqq.json\n\n# 大库建索引\ndocker compose run --rm backend python -m xuwen.ingestion.cli index\n\n# 临时停 \u002F 完全停（数据保留）\ndocker compose stop\ndocker compose down\n\n# 进容器排错\ndocker compose exec backend bash\n```\n\n### 前端怎么办\n\n镜像里**只有后端**。前端继续按 [#3](#3-启动前端可选) 走 `pnpm dev` 或者自己用 Nginx 托管 `frontend\u002Fdist`。后端容器对外暴露 8000，前端发到这个端口即可。\n\n### 与源码部署互操作\n\n完全可以并存，只要别同时跑（端口冲突）：\n\n```bash\n# 今天用源码模式\ncd backend && uv run uvicorn xuwen.chat_api.app:create_app --factory --reload\n\n# 关掉后明天换 Docker\ndocker compose up -d\n```\n\n两边读写同一份 `backend\u002F.env` 与 `backend\u002F.data\u002F`，**无需任何迁移**。\n\n### 几个 Docker 模式特有的注意点\n\n- **首次冷启动会自动注入 `CONFIG_UI_LOCALHOST_ONLY=false`** 到挂载目录的 `.env`，因为容器外浏览器访问 `\u002Fconfig\u002F` 时请求源 IP 是 docker 网桥，会被 localhost-only 拒。配置完成后想重新收紧的话手动改回 `true` 即可。\n- **配置向导写入是 atomic rename，直接落到宿主机 `.env`**——不是写在容器层，重启不丢。\n- **容器 entrypoint 会动态对齐 uid\u002Fgid 到挂载目录所有者**，避免 WSL drvfs \u002F Linux 原生 \u002F NFS 等跨 uid 场景下向导写文件 `EPERM` 报错。\n- **改 `.env` 后必须 `docker compose restart`**，与源码模式一致——`pydantic-settings` 只在启动时加载。\n\n### 故障排查\n\n| 现象 | 大概率原因 | 处理 |\n|---|---|---|\n| 向导 PUT `\u002Fconfig\u002Fvalues` 500 + EPERM | 挂载目录 uid 与容器内不一致，且 entrypoint 没对齐成功 | 看启动日志有没有 `[entrypoint] 调整 afterglow 用户匹配`；没有就贴日志反馈 issue |\n| LanceDB deprecation warning 刷屏 | 上游 lance crate 的弃用提示，不影响功能 | `.env` 加 `RUST_LOG=lance=error` 屏蔽 |\n\n---\n\n## 🎨 自定义人格模板\n\n5 个内置预设：`xuwen`（默认）\u002F `friend` \u002F `lover` \u002F `family` \u002F `colleague`，在 `.env` 设：\n\n```env\nPERSONA_TEMPLATE=lover\n```\n\n完全自定义：\n\n```env\nPROMPT_TEMPLATE_DIR=\u002Fpath\u002Fto\u002Fyour\u002Ftemplates\nPERSONA_TEMPLATE=my_template\n# 会去 \u002Fpath\u002Fto\u002Fyour\u002Ftemplates\u002Fmy_template.md.j2 读\n```\n\n模板可用变量：`friend_name` \u002F `self_name` \u002F `relationship_description` \u002F `persona_card` \u002F `retrieved_friend_examples` \u002F `retrieved_dialogue_windows` \u002F `recent_conversation` \u002F `current_user_message` \u002F `today` \u002F `current_date` \u002F `current_time` \u002F `current_datetime` \u002F `current_weekday` \u002F `timezone`。其中 `retrieved_friend_examples` 会优先包含 response_pairs 样例。`persona_card` 只应作为长期语气参考，不要当作当天事实来源。\n\n---\n\n## 📁 项目结构\n\n```\nAfterglow\u002F\n├── backend\u002F                 # Python 后端（FastAPI + LanceDB + RAG）\n│   ├── xuwen\u002F\n│   │   ├── core\u002F            # 领域模型、错误类型、时间工具\n│   │   ├── ingestion\u002F       # 解析、清洗、PII 脱敏、切分、chunking、向量化\n│   │   ├── memory\u002F          # LanceDB schema、CRUD、检索融合、回写队列、记忆来源策略\n│   │   ├── persona\u002F         # 离线人格画像、prompt 模板（Jinja2）\n│   │   ├── companion\u002F       # 生活时间线、关系记忆、本轮互动决策层\n│   │   ├── chat_api\u002F        # FastAPI 服务（OpenAI 兼容）\n│   │   └── web_ui\u002F          # 配置向导（首次模式自动启用）+ 静态资源\n│   ├── web_ui_src\u002F          # 配置向导前端源码（Vue + Vite，构建到 xuwen\u002Fweb_ui\u002Fstatic\u002F）\n│   ├── scripts\u002F             # 离线脚本（导入、画像、检索评估）\n│   ├── pyproject.toml\n│   └── README.md            # 后端详细文档\n│\n├── frontend\u002F                # Vue 3 + Vite 前端（时光信笺）\n│   ├── src\u002F\n│   │   ├── api\u002F             # SSE \u002F fetch 封装\n│   │   ├── components\u002F      # chat \u002F memory \u002F common \u002F layout \u002F onboarding\n│   │   ├── composables\u002F     # useTypewriter \u002F useAutoScroll \u002F markdown\n│   │   ├── stores\u002F          # Pinia: settings \u002F chat \u002F memory\n│   │   └── views\u002F           # HomeView \u002F SettingsView\n│   ├── tailwind.config.js\n│   ├── package.json\n│   └── README.md            # 测试\u002F调试前端说明\n│\n└── 开发缓存\u002F                 # 你的 QQ 导出 JSON（.gitignore）\n```\n\n---\n\n## ❓ FAQ\n\n**Q：什么是\"配置向导\"？必须用吗？**\nA：可选但**推荐首次用户用**。后端启动时若发现关键字段（`SELF_UID` \u002F `OPENAI_API_KEY` \u002F `EMBEDDING_API_KEY` \u002F `XUWEN_API_KEY`）任一未填，会**自动启用** `\u002Fconfig\u002F` 网页向导，控制台打印一次性 token；浏览器跟着 7 步走完即可生成完整 `.env`、导入聊天记录、生成 persona 卡片。已经配过的人启动后不会触发向导。完全不想用 UI 的可以照「方式 B」手动改 `.env`。\n\n**Q：必须用阿里云 Qwen3-Embedding 吗？**\nA：不必，任何 OpenAI 兼容的 `\u002Fembeddings` 接口都可以。改 `EMBEDDING_API_URL` \u002F `EMBEDDING_MODEL` \u002F `EMBEDDING_DIM` 即可。本地 ollama 也支持。\n\n**Q：必须用 OpenAI 吗？**\nA：不必，任何 OpenAI 兼容的 `\u002Fchat\u002Fcompletions` 接口都可以（DeepSeek、Moonshot、Qwen、ollama 等）。改 `OPENAI_BASE_URL` 即可。\n\n**Q：能不能不脱敏 PII？**\nA：可以。`.env` 设 `ENABLE_PII_REDACTION=false`，或通过 `PII_RULES_PATH` 加载自定义规则。\n\n**Q：QQ 号 \u002F URL 为什么不脱敏？**\nA：QQ 号在导出文件里到处都是（uid 关联需要）；URL 是对话语境的一部分（朋友分享 B 站视频是有意义的）。脱敏列表只覆盖一旦泄漏就造成实质损失的\"硬隐私\"。\n\n**Q：能否导入微信 \u002F Telegram \u002F Discord 数据？**\nA：已内置两个导入 plugin —— [QQChatExporter V5](https:\u002F\u002Fgithub.com\u002Fshuakami\u002Fqq-chat-exporter)（QQ）和 [WeFlow](https:\u002F\u002Fgithub.com\u002Fhicccc77\u002FWeFlow) `arkme-json`（微信）。CLI 会按 JSON 顶层特征自动识别，无需额外参数。导出时记得**只勾选纯文本，不要带图片\u002F语音\u002F文件等附件**。其它平台目前没有内置 plugin，但写一个新 plugin 输出 `NormalizedMessage` 即可，下游流水线无需改动，欢迎 PR。\n\n**Q：会不会越聊越不像？**\nA：每轮对话都会异步回写到 `live_messages`（`trust_level=0.35`，权重远低于历史 `1.0`）。前端可在设置页\"暂停回写\"避免污染。\n\n**Q：怎么删除某条记忆？**\nA：调 `DELETE \u002Fmemory\u002Ffriend_messages\u002F{chunk_id}` 或 `DELETE \u002Fmemory\u002Fresponse_pairs\u002F{pair_id}`（软删除）。\n\n**Q：能本地完全离线吗？**\nA：可以。LLM 用 ollama \u002F vLLM；embedding 用 `nomic-embed-text` \u002F `bge` 等本地模型。\n\n---\n\n## 🛠️ 开发\n\n```bash\n# 后端\ncd backend\n\n# 前端\ncd frontend\npnpm dev                        # 开发服务器\npnpm build                      # 类型检查 + 生产构建\n```\n\n更多文档：\n\n- [后端 API 文档](docs\u002FAPI.md)\n- [开发文档](docs\u002FDEVELOPMENT.md)\n\n---\n\n## 📜 License\n\nAGPL-3.0-or-later\n\n---\n\n\u003Cdiv align=\"center\">\n\n\u003Cpicture>\n  \u003Csource\n    media=\"(prefers-color-scheme: dark)\"\n    srcset=\"https:\u002F\u002Fapi.star-history.com\u002Fsvg?repos=kldhsh123\u002FAfterglow&type=Date&theme=dark\"\n  \u002F>\n  \u003Csource\n    media=\"(prefers-color-scheme: light)\"\n    srcset=\"https:\u002F\u002Fapi.star-history.com\u002Fsvg?repos=kldhsh123\u002FAfterglow&type=Date\"\n  \u002F>\n  \u003Cimg\n    alt=\"Star History Chart\"\n    src=\"https:\u002F\u002Fapi.star-history.com\u002Fsvg?repos=kldhsh123\u002FAfterglow&type=Date\"\n  \u002F>\n\u003C\u002Fpicture>\n\n\u003Csub>如果 Afterglow 帮你留住了一些温度，欢迎点一颗 ⭐。\u003C\u002Fsub>\n\n\u003C\u002Fdiv>\n","Afterglow 是一个本地运行的 AI 朋友系统，通过导入 QQ 或微信的真实历史聊天记录，结合向量数据库和 RAG 技术，在不微调模型的情况下让 AI 以接近原对话者的语气继续与用户交流。项目采用 Python 开发，并利用 LanceDB 作为向量数据库支持语义搜索，兼容 OpenAI API。它适合那些希望保留一段真实关系记忆、在相对稳定情绪状态下延续对话体验的用户使用，但不适合处于剧烈情感波动或意图冒充他人的情况。","2026-06-11 04:01:25","CREATED_QUERY"]