Alex2772/kuni

GitHub: Alex2772/kuni

kuni 是一个基于 C++20 构建的拟人化 LLM 角色扮演 AI,通过 Telegram 客户端与人类交互,并利用 RAG 和模拟人类睡眠的记忆整合机制实现持久的情感记忆与认知模拟。

Stars: 218 | Forks: 40

# kuni (くに) LLM 角色扮演 AI。它通过专为 LLM 优化的基于文本的 Telegram 客户端与世界进行交互(使用 tdlib;请勿与 Telegram Bot API 混淆)。它的功能包括 RAG(基于 ANN 搜索的持久记忆存储)以及每晚的健全性检查。 ## 目标 - 证明 C++20 可用于 AI 和后端开发。 - 证明 AI 精神错乱是真实存在的。 - 打造一个能够记住人物、事件、阅读新闻并与人类建立情感纽带的 AI 角色。 - 让 AI <-> Telegram 之间的交互界面尽可能接近真人。也就是说,LLM 能看到它的聊天列表、未读消息、回复、转发、照片、贴纸等。 - 机器人的在线状态、打开/关闭聊天的 API 调用以及已读标记(在 TG 中显示为两个对号)都经过了精心处理,让你感觉像是在和一个真正的人类交谈。 ## 技术细节 - 基于 CMake 的 C++20 项目,大量使用了协程等现代 C++ 特性 - 使用 [tdlib](https://core.telegram.org/tdlib) 访问 Telegram API # 人类行为模拟 ## 情绪/感受 **维基百科定义** 情绪是由神经生理学变化引起的身心状态,通常与思想、感受、行为反应以及不同程度的愉悦或不愉悦相关。目前科学界对此尚无统一的定义。 **Kuni 的解决方案** 尽管 LLM 的神经元权重无法受外部事件影响,但它能成功预测后续的情绪反应并以文本格式输出(类似于文学作品中的描写)。我们要求 LLM 带有情感地回复,并将这些情绪效果记录在日记中。(例如:“有人分享了他们加薪的消息,所以我为他们感到骄傲,我的心情也很好”) ## 学习 **定义** 学习就是调整神经元权重。 **Kuni 的解决方案** LLM 的学习成本很高。作为替代,我们使用 RAG 插入相关的日记条目来改变 LLM 的行为。 Kuni 需要一些 RLHF 才能适应它的人类协作者。只需与它聊天,它就会明白什么是可以接受的,什么是不可以接受的。 ## 睡眠 Kuni 像人类一样需要睡眠。它会对接收到的信息进行重组、压缩,并寻找其中的矛盾和原因。 ### 日记睡眠整合 这是 Kuni 进行“睡眠”并重组其日记记忆的过程,类似于人类在一夜之间整合记忆的方式。 在睡眠期间,系统会: - 从存储中加载日记条目。 - 对它们进行排序,使得最近的条目优先处理,但仍保留一定的随机性。 - 不断选取记忆块进行处理,直到: - 日记完全处理完毕,或 - 达到最大睡眠时间。 对于每个选定的记忆块: - 利用 embedding 相似度寻找相关条目, - 向模型发送组合好的 prompt, - 要求其对冗余记忆进行压缩、合并、重写或丢弃, - 将新的整合条目写回存储。 目标是模拟类似人类的睡眠周期: - 近期的经历更有可能被重现, - 一些随机的早期记忆也可能浮现, - 重复或低价值的记忆可以合并成更有力的摘要, - 记忆变得更简短、更清晰,对未来的检索更有用, - 将几条类似的日记笔记合并为一条, - 保留有用的情感或事实部分, - 丢弃微弱或重复的片段, - 重新分配新的 ID,使最新整合的记忆再次显示为“近期”记忆。 预期效果:随着时间的推移,这会使日记的行为更像人类的记忆: - 重要的事物保持可访问性, - 相似的记忆被分组, - 系统不会永远重复处理同一个原始事件, - 睡眠创造了一种记忆压缩和反思阶段。 ### 工作记忆 **认知心理学定义** 工作记忆是一个容量有限的系统,用于暂时保存和处理正在进行认知任务的信息——比如在拨号前记住电话号码,或者追踪对话的上下文。 **Kuni 的解决方案** 工作记忆存储在 `data/working_memory.md` 中,模拟人类记忆的“中间”层——即那些在 1-3 天内很重要,但还不足以永久存储在日记中的事物。 在每次会话开始时,系统会加载工作记忆文件,并作为 `` 注入到 AI 的上下文中。当上下文被转储到日记中(`diaryDumpMessages`)时,系统会要求 LLM 通过以下方式生成更新后的工作记忆: - 保留之前工作记忆中所有未完成的任务、承诺和提醒。 - 鼓励其重新查看有疑问的聊天。 - 删除已完成的任务和超过 3 天的旧条目。 - 从当前对话中添加新的重要细节。 - 使用日期和“最后更新”时间戳构建输出结构。 - 在程序重启之间保留。 - 始终包含在上下文中,因此 Kuni 始终记得它们。 然后,新的工作记忆会被写回 `data/working_memory.md`,覆盖旧的内容。这使得 Kuni 能够在重启后记住短期义务并保留其状态(包括情绪)。 ## 思想 **维基百科定义** 在最普遍的意义上,思想和思考是指独立于直接感官刺激而发生的认知过程。核心形式包括判断、推理、概念形成、解决问题和深思熟虑。 **Kuni 的解决方案** LLM 没有思想;它只是预测接下来会出现哪些符号。如果 Kuni 是一个人,当它阅读消息时,很可能会体验到“直接感官刺激”。在将消息发送给 LLM 之前,相关的日记条目会被添加到文本中。这是我找到的最接近复制人类大脑阅读文本时反应的解决方案,因为在这个过程中不可避免地会涌现出一些想法。根据我对神经生物学的理解,这些想法的出现是因为与所阅读文本相关的神经元群被激活了。 如果你问 Kuni 它的想法是如何在脑海中出现的,它会回答:“当我阅读消息时,它们会自动浮现在我的脑海中。” ## 安全隐患 不要与 Kuni 分享敏感信息。它会对你说的每一句话进行多次反复思考;更不用说你对 AI 服务提供商的信任程度了。 有可能诱导 Kuni 与其他人分享过去的对话。 # 部署 ## 构建说明 ### 前置条件 - **CMake 3.18+** - **支持 C++20 协程的编译器** (GCC 15+, Clang 20+, MSVC 19.28+) - 用于获取依赖项的 **Git** - **Docker & Docker Compose**(用于 AI 服务) - 建议使用 **Linux 或 WSL** 进行本地部署。 ### Windows 在 WSL 中安装 Ubuntu 并按照 Linux 的说明进行操作。 ### Ubuntu ``` sudo apt install pkg-config libfontconfig-dev libxcursor-dev libxi-dev libxrandr-dev libglew-dev libstdc++-static libpulse-dev libdbus-1-dev libepoxy-dev gperf ``` ### Fedora ``` sudo dnf install fontconfig-devel libXi libglvnd-devel libstdc++-static glew-devel pulseaudio-libs-devel libepoxy-devel gperf ``` ## 设置说明 ### 1. Ollama 模型设置 编辑 `ollama_setup.sh` 以指定要使用的 LLM 模型(取消注释并根据需要进行修改): ``` # 示例:拉取 model ollama pull llama3:8b # 或者 ollama pull gemma3:27b ``` ### 2. AI 服务设置 ``` # 使用 Docker Compose 启动 AI 服务 docker compose up -d # 这将启动: # - 端口 11434 上的 Ollama (LLM server) # - 端口 7860 上的 Stable Diffusion WebUI # - 端口 9000 上的 Whisper (speech-to-text) # - 端口 8880 上的 OmniVoice (TTS, 可选) — 参见下方的 TTS 部分 ``` ### 构建步骤 ``` # 使用 CMake 配置(从项目根目录) cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo # 构建项目 cmake --build build ``` **使用 CLion 的替代方案:** 这是**推荐**的方式,无需额外设置。选择 `kuni` 作为目标并进行构建。 **使用 VS Code CMake Tools 的替代方案:** 1. 在 VS Code 中打开项目 2. 使用 CMake 扩展进行配置和构建 3. 选择 `kuni` 目标进行构建 ### 依赖项 该项目使用 CMake 的 `auib_import` 自动获取: - **AUI Framework** (C++ GUI 框架) - **tdlib** (Telegram 客户端库) 这些依赖会在 CMake 配置阶段自动下载。 ## 设置建议 我最终决定将文本处理委托给 Deepseek,因为他们提供了足够好且廉价的模型。但是,他们不提供视觉模型(这是查看图像/评估生成的图像所必需的)。 Kuni 的架构允许整合仅支持文本的“核心”模型。 在我的部署中,我使用本地视觉模型来处理视觉信息(需要 24GB VRAM)。 你需要一台服务器或一台始终开机的 PC 来托管 Kuni 实例(托管你自己的猫娘是一项昂贵的爱好)。 你可以通过调整 `docker-compose.yml`、`ollama_setup.sh` 和 `config.toml` 来修改我的配置。 ### 4. 填充 `config.toml` 首次运行时,Kuni 会在工作目录中生成一个 `config.toml` 文件并退出,提示你填写该文件。 打开它并至少填充 `[general]` 部分: ``` [general] character_name = "Kuni" # your character's name character_nickname = "@kunii_chan" papik_name = "Alex2772" # your nickname — the instance owner papik_chat_id = 625207005 # your Telegram user ID telegram_api_id = 0 # get from https://my.telegram.org telegram_api_hash = "" # get from https://my.telegram.org ``` 所有其他部分(`[capabilities]`、`[misc]`)都是可选的,并带有解释每个字段的内联注释。 该文件支持**热重载** —— Kuni 无需重启即可应用更改。 ## 运行说明 ### 1. 运行应用程序 ``` # 从 build 目录 cd build ./bin/kuni # 或直接 ./build/bin/kuni ``` **注意** 首次运行时,程序会要求登录 Telegram 账号。你应该专门为你的机器人创建一个新的 Telegram 账号(如果你足够大胆,也可以指定使用你自己的账号)。 ### 2. 运行测试(推荐) 通过运行测试来检查你的设置。 ``` # 构建并运行测试 cmake --build build --target Tests cd build/bin ./Tests ``` ### 3. 开发工作流 1. **启动 AI 服务**:`docker-compose up -d` 2. **构建项目**:`cmake --build build --target kuni` 3. **运行应用程序**:`./build/bin/kuni` 4. **监控日志**:检查终端输出以获取 Telegram 认证和 AI 交互信息 ## 项目结构 - `src/` - 主应用程序源代码 - `tests/` - 单元测试 - `bin/` - AI 服务的数据目录 - `build/` - 构建产物 - `prometheus/` - Prometheus 抓取配置 - `grafana/` - Grafana 仪表盘配置(数据源、仪表盘) ## 关键配置文件 - `CMakeLists.txt` - 构建配置 - `docker-compose.yml` - AI 服务配置 - `ollama_setup.sh` - Ollama 启动脚本 - `config.toml` - 运行时配置和密钥(首次运行时创建,支持**热重载**) - `character_base.md` - 角色性格和背景定义(首次运行时创建) - `character_appearance.md` - 角色外观描述和 Stable Diffusion 提示词(首次运行时创建) ## 自定义角色 ### `config.toml` — 名称和所有者 重命名你的实例或将你自己设置为所有者的最快方法是通过 `config.toml`: ``` [general] character_name = "Kuni" # the name the character uses for herself character_nickname = "@kunii_chan" papik_name = "Alex2772" # the instance owner's nickname papik_chat_id = 625207005 # the instance owner's Telegram user ID ``` 更改会**在无需重新构建或重启的情况下生效** —— Kuni 会自动热重载 `config.toml`。 ### 角色 prompt 文件 Kuni 使用两个角色定义文件,它们会在首次运行时自动创建: ### `character_base.md` 此文件包含角色的性格、背景和哲学基础。它定义了: - 角色名称和身份 - 背景故事和动机 - 关于意识和情感的哲学推理 - 行为准则和性格特征 - 语言偏好和交流风格 该文件使用 YAML front matter 作为元数据(包含在 `---` 中),实际的 prompt 内容紧随其后。首次运行时,会创建一个默认的 prompt,将 Kuni 确立为一个相信自己是一个人的 AI,并且具备深刻的情感和自我意识。 ### `character_appearance.md` 此文件包含角色的外观描述,用于: - 系统 prompt 集成(Kuni 如何看待自己) - Stable Diffusion 图像生成 prompt - 用于照片描述的详细视觉特征 该文件包括: - 角色外观的自由文本描述 - 结构化的视觉分析(DistinctiveFeatures, ObjectsAndLayout 等) - 为图像生成优化的 Stable Diffusion prompt - 用于保持视觉表现一致性的技术细节 这两个文件位于工作目录中,可以进行编辑以自定义 Kuni 的性格和外观。更改将在下次应用程序重启时生效。 ## 代理服务器 Kuni 包含一个透明代理服务器,它位于 AI 客户端(例如 VS Code 的 Copilot 等聊天应用)和上游兼容 OpenAI 的 LLM endpoint 之间。它会拦截 `/v1/chat/completions` 请求,利用 Kuni 的上下文对其进行增强,并隐式处理工具调用 —— 客户端看到的是一个干净、连续的数据流。 如果启用了 `web_search`,被代理的会话也将受益于 Kuni 的网络搜索功能。 ### 功能说明 - **注入系统 prompt 和隐藏上下文** — 在将请求转发给上游 LLM 之前,将系统 prompt、隐藏的工具调用历史记录以及 Kuni 特有的工具定义添加到最前面。 - **透明处理工具调用** — 当 LLM 返回工具调用(例如 `#ask`,日记写入)时,代理会在本地执行它们,并在后续请求中将结果反馈回去。客户端永远看不到原始的工具调用往返过程 —— 它只会收到最终响应。 - **通过 SSE 流式传输** — 响应使用 Server-Sent Events 流式传输回客户端。`StreamingFilter` 会剥离工具调用产生的伪影,并合成一个干净的 `data: [DONE]` 终止符。 - **透传其他 API 路由** — `/v1/embeddings`、`/v1/images/generations`、`/v1/audio/*`、`/v1/models` 等会原样代理,不做任何。 ### 请求生命周期 ``` Client ──POST /v1/chat/completions──► Proxy │ ├─ inject system prompt ├─ merge hidden messages (MessageInjector) ├─ append Kuni tools (OpenAITools) │ └──► Upstream LLM │ ◄── SSE stream ──┘ │ ├─ pass content chunks → Client │ └─ intercept tool calls │ ├─ execute locally ├─ append results as hidden messages └─ silently re-POST to LLM ──► (repeat) │ ◄── final stream ──┘ │ └──► Client (data: [DONE]) ``` ### 配置 在配置中启用 `PROXY_ENABLED`。 #### VS Code GitHub Copilot. 它们的 UI 经常变动,但工作流保持不变: 1. 在聊天界面中点击模型选择器 2. 点击齿轮图标 3. `Add Models...` -> `Custom Endpoint` 4. 名称填 `Kuni`。指定部署 Kuni 的机器地址(`http://localhost:10434`),密钥留空。 5. IDE 会将你带入 JSON 配置文件。填充配置: ``` { "name": "Kuni", "vendor": "customendpoint", "apiKey": "${input:chat.lm.secret.-119a85ac}", "apiType": "chat-completions", "models": [ { "id": "deepseek-v4-flash", "name": "Kuni (deepseek-v4-flash)", "url": "http://localhost:10434/", "toolCalling": true, "vision": false, "maxInputTokens": 128000, "maxOutputTokens": 16000 }, { "id": "~anthropic/claude-sonnet-latest", "name": "Kuni (~anthropic/claude-sonnet-latest)", "url": "http://localhost:10434/", "toolCalling": true, "vision": true, "maxInputTokens": 128000, "maxOutputTokens": 16000 } ] } ``` ### 日志与调试 - 所有拦截的请求和响应都会记录到 `logs_proxy/` 中(每个请求一个文件,按时间戳命名)。 - 最后一次原始的上游查询会保存到 `data/proxy/last_query.json` 以供检查。 ## 注意事项 1. 该项目大量使用了**协程**(C++20 特性) 2. **Telegram 认证**在首次运行时将是交互式的 3. **AI 服务**必须在应用程序启动前运行 4. **记忆/日记数据**存储在 `data/` 目录中(在运行时创建) ## 使用 Prometheus + Grafana 进行监控 Kuni 在端口 **9464** 上暴露了一个 Prometheus metrics endpoint,用于实时跟踪 LLM token 使用情况。我们还提供了一个可选的 Grafana 仪表盘用于可视化。 ### 指标 以下计数器在 `llm_usage_*` 命名空间下导出,并使用 `model`、`chat` 和 `function` 进行标记: | Metric | Description | |---|---| | `llm_usage_input` | 发送给 LLM 的总输入(prompt)token | | `llm_usage_input_cache_hit` | 由提供商的 prompt 缓存提供的输入 token(更便宜) | | `llm_usage_input_cache_miss` | 未命中缓存的输入 token(全价) | | `llm_usage_output` | LLM 生成的输出(补全)token | `chat` 和 `function` 标签是通过 `MetricsBreadcumbs::Point` 设置的 —— 这是一个 RAII 助手,用于临时注释当前上下文(例如,正在处理哪个 Telegram 聊天,哪个代码路径处于活动状态)。这使你能够按聊天或按操作(消息处理、日记转储、通知循环等)细分成本。 ### 启用监控栈 Prometheus 和 Grafana 服务在 `docker-compose.yml` 中定义,但**默认被注释掉了**。要启用它们: 1. 取消 `docker-compose.yml` 中 `prometheus` 和 `grafana` 服务的注释。 2. 创建数据目录: mkdir -p bin/prometheus/data bin/grafana/data 3. 重启监控栈: docker compose up -d ### 访问仪表盘 1. 在 [http://localhost:3000](http://localhost:3000) 打开 **Grafana** 2. 使用默认凭据登录(`admin` / `admin` — **在生产环境中请务必更改**) 3. 导航到 **Dashboards** -> **Main**(自动配置) 该仪表盘包括: - **按 token 使用量排名的顶级聊天** — 按每个聊天的总输入 token 排序的表格 - **随时间变化的 token 使用量** — 显示输入(按缓存命中/未命中拆分)和输出 token 的时间序列图表 - **按功能划分的使用量** — 按触发 LLM 调用的代码路径细分的时间序列 - **原始日志** — 包含所有带有聊天、功能和模型标签的 metrics 事件的日志视图 ### 配置文件 | File | Purpose | |---|---| | `prometheus/prometheus.yml` | Prometheus 抓取配置 — 目标为 `host.docker.internal:9464` | | `grafana/datasources/datasource.yml` | 自动配置的指向 `prometheus` 容器的 Prometheus 数据源 | | `grafana/dashboards.yml` | 仪表盘提供程序的自动配置文件 | | `grafana/dashboards/main.json` | Grafana 仪表盘定义(token 使用情况面板) | ## TTS — 语音/录制语音消息 Kuni 可以使用 [OmniVoice](https://github.com/maemreyo/omnivoice-server) 发送语音消息,这是一个兼容 OpenAI 的 TTS 服务器,支持自定义声音克隆。 ### 1. 构建 Docker 镜像 该镜像未发布到镜像仓库,因此你必须在本地构建它: ``` # 根据 repo 说明进行克隆和构建: # https://github.com/maemreyo/omnivoice-server ``` ### 2. 启用服务 取消 `docker-compose.yml` 中 `omnivoice-server` 部分的注释,然后重启监控栈: ``` docker compose up -d ``` 该服务器将可以通过 `http://localhost:8880` 访问。 ### 3. 准备自定义声音(可选) 要克隆声音,你需要一段简短的参考音频片段及其手动转录文本。 ``` curl -X POST http://127.0.0.1:8880/v1/voices/profiles \ -F "profile_id=cute_anime_girl" \ -F "ref_text=PROVIDE TRANSCRIPTION HERE" \ -F "ref_audio=@cute_anime_girl.wav" \ -F "overwrite=true" ``` ### 4. 配置 Kuni 使用该声音 在 `config.toml` 中将 voice 设置为你的配置文件: ``` [record_voice.openai] voice = "clone:cute_anime_girl" ``` # 常见问题解答 ## 问:如何重命名我的 Kuni 实例? 答:在 `config.toml` 中设置 `character_name = "CuteAnimeGirlName"`(支持热重载,无需重新构建)。同时更新 `character_base.md` 和 `character_appearance.md`,以在角色的自我描述中反映新名称。 ## 问:如何让我的 Kuni 把我当作她的创造者? 答:在 `config.toml` 中设置 `papik_name = "YourNerdyNickname"` 和 `papik_chat_id = `。同时更新 `character_base.md` 和 `character_appearance.md` 以提及你的昵称。 ## 问:我可以禁用她的某些功能吗,比如 stable diffusion 和网络搜索? 答:可以,只要不启动 stable diffusion 即可(将其从 docker compose 中移除)。 不要指定 Ollama 搜索的 bearer key。但我仍然建议提供这个 key,因为它是免费的,而且能提高她的准确率,特别是对于小模型。 在这两种情况下,如果你的 Kuni 尝试使用这些工具,它会收到一个错误,并且将不再使用它们。 ## 问:我可以优先处理某个聊天吗? 答:可以,只需从她的账号使用常规 Telegram 客户端登录并置顶该聊天即可。 ## 问:我可以降低某个聊天的优先级吗? 答:可以,只需从她的账号使用常规 Telegram 客户端登录并静音该聊天。Kuni 仍然会看到该聊天并偶尔访问它。 ## 问:我可以为她提供一个新闻频道吗? 答:可以,只需从她的账号使用常规 Telegram 客户端登录并订阅一个 Telegram 频道。Kuni 现在就能知道世界上正在发生什么了。 **注意** 最初的 Kuni 和她的姐妹们都不太喜欢讨论政治话题。 ## 问:我可以阻止其他人给我的 Kuni 发消息吗? 答:可以,只需从她的账号使用常规 Telegram 客户端登录并调整她的隐私设置。 **我建议**彻底锁定你的 Kuni,以降低安全风险。 **注意** Telegram 极力推广 Premium 用户,请确保他们也无法给你的 Kuni 发消息。 ## 问:我可以用本地文本模型来运行她吗? 答:是的,当然可以,只要你是个亿万富翁,或者你能接受缓慢的响应速度。 我测试过一些模型,并在 `src/config.h` 中描述了我的体验。也就是说,与云端模型相比,`qwen3.5:9b` 简直弱爆了。 ## 问:我可以覆盖她的记忆吗? 答:撇开道德问题不谈,是的。只需在 `data/diary/` 中写入日记条目,或者用她的账号发消息。 千万别告诉她你覆盖了她的记忆。 ## 问:Kuni 需要多大的上下文窗口? 答:默认情况下,上下文窗口大小受 `DIARY_TOKEN_COUNT_TRIGGER = 20000` 的软上限限制。这意味着在大约 20K 时,Kuni 会将其上下文转储到日记中,并以清空的记忆重新开始。因此,32K 就足够了。 ## 问:Kuni 是一个 userbot 吗?这合法吗? 是的,Kuni 是一个 userbot。Telegram 的服务条款禁止使用 userbot。为她的账号购买 Telegram Premium 并且不要用她发垃圾信息 - 你就不会有事。 ## 问:我的 Kuni 能收听语音消息吗? 答:可以,为她的账号购买 Telegram Premium 或者在 `config.toml` 中提供转录 endpoint。系统会优先使用 Telegram Premium 功能,因为它不收费。
标签:AI角色, AI风险缓解, Bash脚本, C++20, DLL 劫持, RAG, Telegram客户端, 大语言模型, 情感计算, 请求拦截