Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/ccjjfdyqlhy_dsn-exp.json
T
OpenCode 68a9071cf1 feat: reconcile local master with origin (exa/hot-platform hunters, leaks ledger, latest results)
Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
2026-08-07 12:53:47 +08:00

1 line
51 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "ccjjfdyqlhy/dsn-exp", "files": {"README_zh.md": {"1cfce308afc3f2333b59d64d39ffdb3b70bb210a": {"t": 1786068438.7303886, "n": 17545, "h": "83d25c88391445b2", "c": "\n# DSN-exp\n\n**开口即达。** 一个以语音为第一交互界面的 AI 伴侣,运行在你的机器上——你说话它就听,听完就做,做完就说,不等你敲键盘。\n\n[![Python 3.11+](https://img.shields.io/badge/python-3.11+-3776AB?style=for-the-badge&logo=python)](https://python.org)\n[![License](https://img.shields.io/badge/license-MIT-blue?style=for-the-badge)](LICENSE)\n[![GitHub](https://img.shields.io/badge/GitHub-ccjjfdyqlhy%2FDSN--exp-181717?style=for-the-badge&logo=github)](https://github.com/ccjjfdyqlhy/DSN-exp)\n\n[深入了解仓库复杂度](REPORT_zh.md) \n\n[English](README.md) | **简体中文**\n\n---\n\n```\n你走进来,它听见了。\n \"一小时后提醒我推送构建。\"\n你倒咖啡的功夫,它已经处理完了。\n \"设好了。对了,构建日志看起来没问题。\"\n你没让它检查。它知道什么值得说。\n```\n\n---\n\n## 为什么是语音优先?\n\n市面上所谓的\"AI 助手\",本质是套了聊天气泡的网页搜索。DSN-exp 从头到尾是反过来的:\n\n- **输入**:麦克风 → ASR (FunASR paraformer) → ASR 过滤 (1B 分类器滤除环境噪音) → AI\n- **输出**:AI → 流式 TTS (GPT-SoVITS),每句话边生成边播,不等整段合成完\n- **主动**:心跳每 2 秒轮询——提醒、闹钟、甚至摄像头看到场景变化,AI 都会主动开口\n- **感知通话模式**:连续对话,AI 切换为口语短句风格,沉默 = 还在听\n- **万物皆可说**:50+ 技能、100+ 工具、记忆检索、闹钟管理、音乐控制、系统操作——全用嘴完成\n\n**它不等着你打字。它跟你待在同一个房间里。不是云服务。不是 SaaS。一个在你硬盘上苏醒的智能体。**\n\n---\n\n## 🏗️ 系统架构\n\n```mermaid\ngraph TB\n subgraph VoiceClients[\"🎤 语音客户端\"]\n CLI[\"minimal.py<br/>终端 · PvRecorder\"]\n WEB[\"psychoscope/server.py<br/>Web · MediaRecorder\"]\n SEN[\"voice.js<br/>感知模式 · LoudnessGate\"]\n AGT[\"agent_send.py<br/>Agent 命令行\"]\n end\n\n subgraph VoicePipeline[\"🗣️ 语音管线\"]\n ASR[\"FunASR paraformer<br/>16kHz · VAD · 标点恢复\"]\n FILT[\"ASR 过滤器<br/>1B 模型 · FORWARD/HOLD\"]\n TTS[\"GPT-SoVITS<br/>流式合成 · 多音色\"]\n TTS_PRE[\"TTS 预处理器<br/>LLM 文本清理\"]\n end\n\n subgraph Engine[\"🧠 DSNEngine 核心引擎\"]\n PL[\"ChatPipeline · 5个Hook点\"]\n PM[\"PluginManager<br/>插件管理器\"]\n PE[\"PromptEngine<br/>提示词引擎\"]\n ME[\"MemorySystem<br/>记忆系统\"]\n TM[\"TaskManager<br/>任务管理器\"]\n WE[\"WorldEngine<br/>世界引擎\"]\n SM[\"SkillManager<br/>技能管理器\"]\n WS[\"Workspace<br/>工作区\"]\n end\n\n subgraph Pipeline[\"管道执行流程\"]\n direction LR\n F[\"① PRE_FILTER<br/>ASR过滤, 缓存检查\"]\n SP[\"assemble_prompt()<br/>组装提示词\"]\n P[\"② PRE_PROCESS<br/>视觉, 记忆注入<br/>印象, 计划, 主动视觉\"]\n M[\"③ MODEL_INVOKE<br/>ModelsPlugin\"]\n PO[\"④ POST_PROCESS<br/>任务, 工具, 待办<br/>记忆, 人格\"]\n T[\"⑤ POST_TTS<br/>TTS合成\"]\n F --> SP --> P --> M --> PO --> T\n end\n\n subgraph Models[\"🤖 模型层\"]\n OA[\"☁️ OpenAIChat<br/>云API\"]\n LS[\"🏠 LMStudioChat<br/>本地模型\"]\n SCH[\"📊 ModelScheduler<br/>VRAM调度\"]\n end\n\n subgraph Storage[\"💾 存储层\"]\n DB[\"🗄️ SQLite<br/>聊天数据库\"]\n WK[\"📂 工作区文件\"]\n AC[\"🎵 音频缓存\"]\n end\n\n CLI & WEB & SEN & AGT --> ASR --> FILT --> Engine\n Engine --> TTS --> CLI & WEB\n M --> OA & LS\n OA & LS --> SCH\n ME --> DB\n WS --> WK\n\n subgraph Auth[\"🔐 认证系统\"]\n direction LR\n P0[\"配对码\"] --> S1[\"会话\"] --> W2[\"WebAuthn\"] --> T3[\"TOTP\"] --> K4[\"API Key\"]\n end\n\n subgraph Proactive[\"🔔 主动通知系统\"]\n HB[\"心跳 2s\"]\n RM[\"提醒 · 闹钟\"]\n CV[\"CameraWatcher · 视觉\"]\n end\n\n Proactive --> HB --> Engine --> TTS --> VoiceClients\n```\n\n---\n\n## 🎯 核心定位\n\n| 特性 | 说明 |\n|------|------|\n| **🎤 语音优先** | ASR → TTS 管线作为主要交互通道,感知免提模式实现无手对话 |\n| **🔒 完全私有** | 所有数据本地存储,SQLite加密,零云依赖 |\n| **🧠 长期记忆** | 对话自动摘要+向量检索,真正记得你说过的话 |\n| **🎭 多人格系统** | YAML角色卡+50维性格向量,支持人格蒸馏 |\n| **🌍 世界模拟** | 天气、时间、地点动态变化,AI有自己的\"生活\" |\n| **🛠️ 54+技能** | 搜索、文件、GitHub、音乐、文档、系统操作... |\n| **⚡ 异步任务** | 慢速工具后台执行,心跳轮询实时反馈 |\n| **🔐 多层认证** | 配对码/会话/WebAuthn/TOTP/API Key 五层防护 |\n| **🤖 Agent API** | 本地AI Agent专用接口,双向记忆同步 |\n\n---\n\n## ✨ 详细功能\n\n### 🎤 语音交互\n- **实时 ASR**:FunASR paraformer-zh,含 VAD 静音检测、噪音门控、标点恢复\n- **智能过滤**:1B 参数模型实时分类——是对话(FORWARD)还是噪音(HOLD),防止误触发\n- **流式 TTS**:GPT-SoVITS 逐句合成,AI 说一句播一句,没有沉默空档\n- **多音色切换**:运行中切换 TTS Profile,每套独立 GPT/SoVITS 权重\n- **感知通话模式**:连续对话——AI 自动去掉 Markdown,改用口语短句,沉默 = 还在听\n- **TTS 预处理**:LLM 清理文本,\"AI 3秒后处理 HTTP 请求\" → \"人工智能三秒后处理超文本传输协议请求\"——所有行在**一次批量调用**里完成(流式时首行仍走本地正则快路径),不再逐行串行调用 LLM\n- **语义音频缓存**:L1 静态 + L2 向量 + L3 槽位——重复查询直接命中缓存音频\n\n### 💬 智能对话\n- **双后端支持**:OpenAI兼容API (DeepSeek/智谱/OpenAI) + 本地LMStudio\n- **原生Tool Call**:OpenAI Function Calling,100+工具一键调用\n- **工具箱模式**:两阶段工具激活——首轮只发一个 `toolbox` 索引工具,激活后才附带具体工具 schema。约 100 个工具时单任务 prompt token 可省 ~69%(首轮 ~4.7k vs 全量 ~10.9k)。问\"你能做什么\"时直接按索引回答,无需激活任何工具\n- **历史裁剪**:`MODEL_MAX_HISTORY`(默认12)限制对话历史条数,保持 prompt 精简\n- **流式输出**:实时生成,边说边播\n- **语义缓存**:L1静态+L2向量+L3槽位,拦截重复请求\n\n### 🧠 记忆系统\n- **自动摘要**:LLM驱动压缩,AES-256-GCM加密\n- **向量检索**:768维语义搜索,支持模糊查询\n- **全局记忆**:跨聊天历史共享,真正的长期记忆\n- **记忆类型**:对话摘要(`exp`) + 手动备忘(`memo`)\n\n### 🎭 人格系统V3\n- **角色卡**:YAML格式,支持自然语言/语料库/经验条目\n- **蒸馏引擎**:从对话中提取性格特征,生成50维向量\n- **动态合成**:基于当前情绪和语境动态生成提示词\n- **性格维度**:社交、思维、情感、兴趣、行为、价值观...\n- **后台节流**:`fastcache` 模式下,情绪分析与记忆摘要作为延迟后台任务执行,带同用户冷却间隔,避免本地 GPU 推理与主回复争抢\n\n### 🌍 世界系统\n- **世界引擎**:天气、时间、地点自动变化\n- **叙事模型**:独立LLM实例,生成第三人称旁白\n- **动作叙述**:收集AI操作,生成人类可读描述\n- **世界预设**:默认世界 + 自定义世界配置\n\n### 🛠️ 技能系统\n- **54+内置技能**:\n - 🔍 `web_search` - 网络搜索\n - 📂 `file_manager` - 文件管理\n - 🐙 `github` - GitHub操作\n - 🎵 `ncm_music` - 网易云音乐\n - 📄 `document` - 文档处理 (扫描仪/打印机)\n - 💻 `system` - 系统操作\n - 🧠 `plan` - 计划管理\n - 🔧 `browser_use` - 浏览器自动化\n - 📝 `todo` - 待办事项\n - 🎭 `impression` - 印象系统\n - 🧪 `skillmgr` - 技能管理\n - ...\n- **自定义技能**:YAML配置,支持异步执行\n- **技能蒸馏**:从使用中学习,优化调用\n\n### ⏰ 主动通知系统\n- **心跳轮询**:客户端每 2 秒 polling,随时接收 AI 主动消息\n- **提醒系统**:倒计时、每日计划、Cron 周期、习惯打卡——到期自动 AI+TTS 播报\n- **闹钟系统**:完整增删改查,按周循环,语音 dismiss\n- **主动视觉**:CameraWatcher 后台抓帧 → VisionModel 分析场景变化 → AI 自主决定是否开口\n\n### ⚡ 任务系统\n- **复杂度分析**:自动判断是否需要异步执行\n- **异步任务**:慢速工具后台执行\n- **心跳轮询**:前端轮询获取异步结果\n- **实时反馈**:Agent循环每步骤实时TTS进度\n- **超步数汇报**:Agent 用完全部步数仍在执行工具时,追加一轮把工具结果总结给用户,而不是无声终止\n\n### 🖼️ 视觉系统\n- **VisionModel**:通用视觉模型 (GLM-4.6V/GPT-4V)\n- **OCRModel**:文档OCR,支持deepseek-ocr\n- **VISION_OVERRIDE**:接管OCR+2md管线,直接生成Markdown\n- **图片分析**:`describe_image`工具支持本地图片\n- **主动视觉**:后台 CameraWatcher 线程实现场景感知\n- **多摄像头并行**:`look_around` 并行描述所有摄像头(按逻辑名保序),多摄延迟减半\n- **按需帧缓存**:客户端缓存每台摄像头最近一帧,视觉请求零等待复用;心跳降到 2s,请求几乎即时送达\n- **VLM 预热**:启动时后台发一次 dummy 请求,消除首次推理冷启动(首次 VLM 推理可能 ~9s)\n- **look_around 去重**:短窗口内重复观察直接复用上次结果,不再重复抓帧+推理\n- **快速兜底**:客户端离线时 `look_around` 8s 内返回兜底(原 20s),不再拖住整条回复\n\n### 🔐 认证系统\n| 层级 | 方法 | 优先级 | 使用场景 |\n|------|------|--------|----------|\n| L4 | API Key | 1 | Agent API / 自动化 |\n| L1 | Session | 2 | 终端/Web UI |\n| L2 | WebAuthn | 3 | 通行密钥 |\n| L3 | TOTP | 4 | 双因素认证 |\n| L0 | 配对码 | 5 | 首次设备配对 |\n\n### 🧩 其他功能\n- **工作区系统**:用户隔离目录,AI笔记/扫描件/代码仓库\n- **文档系统**:扫描仪/打印机支持,`.hmd`格式\n- **维护系统**:自动内存压缩/人格蒸馏/日志清理\n- **语义缓存**:拦截重复请求,节省Token\n- **Agent API**:本地AI Agent专用接口\n\n---\n\n## 🚀 快速开始\n\n### 最小化配置 (3步启动)\n\n#### 1️⃣ 克隆并安装\n```bash\ngit clone https://github.com/ccjjfdyqlhy/DSN-exp\ncd DSN-exp\npip install -r requirements.txt\n```\n\n#### 2️⃣ 配置 (自动引导)\n```bash\npython main.py\n```\n\n首次运行会进入交互式引导,询问:\n- API选择:云API或本地模型\n- API Key和Base URL\n- 主模型选择\n- 是否启用核心功能\n\n引导完成后自动生成`.env`文件。\n\n#### 3️⃣ 连接客户端\n```bash\n# 终端语音客户端(按住 Enter 说话,松开发送)\npython psychoscope/minimal.py\n\n# Web界面(支持感知免提模式)\npython psychoscope/server.py\n# 然后访问 http://localhost:5000\n```\n\n**就这么简单!** 现在可以开始说话了。\n\n---\n\n### 常见配置场景\n\n#### 🌐 场景1:纯DeepSeek (最简单)\n```bash\n# .env (引导会自动生成)\nOPENAI_API_KEY=sk-your-deepseek-key\nOPENAI_API_BASE=https://api.deepseek.com/v1\nMAIN_MODEL_NAME=deepseek-v4-flash\n```\n\n#### 🏠 场景2:本地LMStudio\n```bash\n# .env\nMAIN_MODEL_TYPE=lmstudio\nMAIN_MODEL_NAME=llama-3-8b-instruct\nLMSTUDIO_BASE_URL=http://localhost:4501\n\n# 先启动LMStudio,加载模型\n# 然后启动DSN-exp\npython main.py\npython psychoscope/minimal.py\n```\n\n#### 🎯 场景3:智谱GLM\n```bash\n# .env\nOPENAI_API_KEY=your-zhipu-key\nOPENAI_API_BASE=https://open.bigmodel.cn/api/paas/v4\nMAIN_MODEL_NAME=glm-4.7\nVISION_API_KEY=your-zhipu-key\nVISION_MODEL_NAME=glm-4.6v\n```\n\n#### 🤖 场景4:Agent API\n```bash\n# 服务器控制台创建Agent\n/agent create MyAgent 1\n# → 输出API Key\n\n# 使用Agent发送消息\npython agent_send.py \"分析代码复杂度\"\n```\n\n---\n\n## 📂 项目结构\n\n```\nDSN-exp/\n├── 🚀 核心\n│ ├── main.py # 服务器入口 + 控制台\n│ ├── engine.py # DSNEngine 核心引擎\n│ ├── boot.py # Flask 应用初始化 + ASR/TTS 模型加载\n│ ├── config.py # 配置管理\n│ └── onboarding.py # 首次启动引导\n│\n├── 🔌 插件系统\n│ ├── plugins/\n│ │ ├── base.py # 插件基类 + HookPoint\n│ │ ├── manager.py # PluginManager\n│ │ ├── pipeline.py # ChatPipeline 对话管道\n│ │ └── builtin/ # 内置插件\n│ │ ├── models_plugin.py # 模型调用\n│ │ ├── memory_plugin.py # 记忆系统\n│ │ ├── vision_plugin.py # 视觉系统\n│ │ ├── task_plugin.py # 任务管理\n│ │ ├── tts_plugin.py # TTS 合成\n│ │ ├── tts_profile.py # TTS 音色配置\n│ │ ├── asr_filter_plugin.py # ASR 语音过滤器\n│ │ ├── active_vision_plugin.py # 摄像头观察器\n│ │ └── ...\n│ │ └── custom/ # 自定义插件\n│\n├── 🧠 记忆系统\n│ ├── memory/core.py # MemorySystem 核心类\n│ └── semantic_cache/ # 语义缓存 (L1/L2/L3)\n│\n├── 🎭 人格系统\n│ ├── prompt/personality_v3/ # PersonalitySystemV3\n│ │ ├── character_card.py # 角色卡定义\n│ │ ├── distillation_engine.py # 蒸馏引擎\n│ │ ├── dynamic_synthesizer.py # 动态合成器\n│ │ ├── traits.py # 50维性格向量\n│ │ └── ...\n│ ├── prompt/personality_v2/ # PersonalitySystemV2\n│ ├── prompt/library.py # PromptLibrary\n│ ├── prompt/engine.py # PromptEngine\n│ └── character_cards/ # YAML 角色卡\n│\n├── 🌍 世界系统\n│ ├── world/\n│ │ ├── engine.py # WorldEngine\n│ │ ├── state_manager.py # WorldStateManager\n│ │ ├── narrative_model.py # NarrativeModel\n│ │ ├── action_narrator.py # 动作叙述\n│ │ └── worlds/ # 世界配置\n│\n├── 🛠️ 技能系统\n│ ├── skills/\n│ │ ├── registry.py # SkillRegistry\n│ │ ├── manager.py # SkillManager\n│ │ ├── builtin/ # 内置技能 54+\n│ │ ├── custom/ # 自定义技能\n│ │ ├── distilled/ # 蒸馏技能\n│ │ └── system/ # 系统技能\n│\n├── ⚡ 任务系统\n│ ├── tasks.py # TaskManager + 复杂度分析\n│ ├── maintenance/ # 后台任务\n│ └── async_task_store.py # 异步任务存储\n│\n├── 🤖 模型客户端\n│ ├── models/\n│ │ ├── clients.py # OpenAIChat + LMStudioChat\n│ │ ├── scheduler.py # ModelScheduler\n│ │ ├── tts_process.py # TTS 预处理\n│ │ └── asr_filter.py # ASR 过滤 (1B 模型)\n│\n├── 🔐 认证系统\n│ ├── auth/\n│ │ ├── auth_manager.py # 认证管理器\n│ │ ├── api_key_manager.py # API Key\n│ │ ├── webauthn_manager.py # WebAuthn\n│ │ ├── totp_manager.py # TOTP\n│ │ ├── pairing.py # 配对码\n│ │ ├── session.py # 会话管理\n│ │ └── endpoints.py # 认证API\n│\n├── 💾 数据库\n│ ├── db/\n│ │ ├── chat.py # ChatDBManager\n│ │ ├── plan_store.py # 计划存储\n│ │ └── plan_engine.py # 计划引擎\n│\n├── 🖼️ 视觉系统\n│ ├── document/ # 文档处理 (.hmd)\n│ └── models/clients.py # VisionModel + OCRModel\n│\n├── 🎤 音频系统\n│ ├── audio/infer.py # VocalExp — GPT-SoVITS 客户端\n│ └── TTS_profiles/ # TTS 音色配置\n│\n├── 🗣️ 语音 API\n│ ├── api/app.py # ASR 识别 + passthrough 端点\n│ ├── api/heartbeat.py # 心跳通知轮询\n│ └── api/alarm.py # 闹钟 CRUD + dismiss\n│\n├── 🖥️ 前端客户端\n│ ├── psychoscope/\n│ │ ├── server.py # Web 服务器\n│ │ ├── minimal.py # 终端语音客户端\n│ │ └── static/\n│ │ ├── index.html\n│ │ ├── js/app.js # 主 Web 应用\n│ │ ├── js/voice.js # 浏览器感知模式\n│ │ └── js/typewriter.js\n│ └── agent_send.py # Agent CLI\n│\n├── 📊 配置文件\n│ ├── .env.example # 配置示例\n│ ├── model_profiles/ # 模型配置\n│ └── world/worlds/ # 世界配置\n│\n├── 🧪 测试与文档\n│ ├── tests/ # 测试\n│ ├── docs/ # 文档\n│ ├── REPORT.md # 技术报告\n│ └── GOALS.md # 开发目标\n│\n└── 📂 工作区与缓存\n ├── .dsn/workspace/ # 用户工作区\n ├── logs/ # 日志(含 ASR/TTS 调试日志)\n └── temp/ # 临时文件\n```\n\n---\n\n## 📦 核心模块介绍\n\n### 🎤 语音管线\n\n**三级语音处理:ASR → 过滤 → AI → TTS**\n\n```\n麦克风 → PCM 16kHz → FunASR (VAD + 识别) → ASR 过滤器 (FORWARD/HOLD)\n │\n ├── FORWARD → ChatPipeline → GPT-SoVITS TTS → 扬声器\n └── HOLD → 丢弃(记录到记忆系统作为环境音)\n```\n\n**组件:**\n- **FunASR**:paraformer-zh 模型,fsmn-vad 做静音检测,ct-punc-c 做标点恢复,支持 CUDA/CPU\n- **ASR 过滤器**:1B 参数 LMStudio 模型分类语音为对话或噪音,维护 20 轮对话历史防误判\n- **GPT-SoVITS TTS**:通过 REST API 流式合成,支持并行/串行架构,多音色切换\n- **TTS 预处理器**:两级清理——正则去 Markdown,LLM 转换数字/缩写到自然语音;多行合并为一次批量调用(流式首行走本地正则快路径)\n- **语义音频缓存**:L1 静态语素 + L2 向量语义 + L3 槽位注册\n\n### 🔄 ChatPipeline 对话管道\n**5个Hook点,完整的对话生命周期管理**\n\n| HookPoint | 触发时机 | 用途 |\n|-----------|----------|------|\n| PRE_FILTER | 管道起始 | ASR 过滤、语义缓存检查、HOLD 时短路 |\n| PRE_PROCESS | 模型调用前 | 记忆注入、视觉处理、主动视觉、印象、计划 |\n| MODEL_INVOKE | 模型调用 | OpenAI/LMStudio调用、工具传递、Agent 循环 |\n| POST_PROCESS | 输出后 | 任务执行、工具调用、记忆更新、人格更新 |\n| POST_TTS | 文本就绪后 | TTS 合成、缓存、音频投递 |\n\n### 🧠 MemorySystem 记忆系统\n**双层架构:摘要 + 向量检索**\n\n```\n对话 → LMSummaryModel → 压缩摘要 → AES-256加密 → SQLite\n ↓\n EmbeddingClient → 768维向量 → 语义检索\n```\n\n**特性:**\n- 自动摘要:每N轮自动压缩对话\n- 向量检索:支持语义模糊搜索\n- 全局记忆:跨聊天历史共享\n- 加密存储:AES-256-GCM保护隐私\n\n### 🎭 PersonalitySystemV3 人格系统\n**50维性格向量,真正的人格建模**\n\n**核心组件:**\n- **CharacterCard**:YAML角色卡,支持自然语言/语料库/经验条目\n- **DistillationEngine**:从对话中提取性格特征\n- **DynamicSynthesizer**:基于情绪和语境动态生成提示词\n- **PersonalityJudge**:50维性格向量:社交/思维/情感/兴趣/行为/价值观\n\n**工作流程:**\n```\n角色卡(YAML) → 蒸馏引擎 → 50维向量 → 动态合成器 → 提示词 → 模型\n```\n\n### 🌍 WorldSystem 世界系统\n**AI有自己的\"生活\"**\n\n**组件:**\n- **WorldEngine**:世界状态管理(天气/时间/地点)\n- **WorldStateManager**:状态更新和事件触发\n- **NarrativeModel**:独立LLM实例,生成旁白\n- **ActionNarrator**:收集AI操作,生成描述\n\n**示例:**\n```\n世界:default\n天气:晴天 ☀️\n时间:2026-07-03 14:30\n地点:书房 📚\n旁白:阳光透过窗户洒在书桌上,AI正在思考用户的请求...\n```\n\n### 🛠️ SkillSystem 技能系统\n**54+内置技能 + 自定义技能**\n\n**技能结构:**\n```yaml\nname: web_search\ndescription: 网络搜索\nversion: 1.0\ntools:\n - name: search\n description: 搜索网络\n parameters:\n query:\n type: string\n description: 搜索关键词\n```\n\n**内置技能列表:**\n- 🔍 `web_search` - 网络搜索\n- 📂 `file_manager` - 文件管理\n- 🐙 `github` - GitHub操作\n- 🎵 `ncm_music` - 网易云音乐\n- 📄 `document` - 文档处理\n- 💻 `system` - 系统操作\n- 🧠 `plan` - 计划管理\n- 🔧 `browser_use` - 浏览器自动化\n- 📝 `todo` - 待办事项\n- 🎭 `impression` - 印象系统\n- 🧪 `skillmgr` - 技能管理\n- ...\n\n### ⏰ 主动通知系统\n**心跳驱动的主动 AI 语音通知**\n\n```\n(时钟滴答) → TaskManager 触发 → task_notifications 表\n ↓\nHeartbeatPoller (2s) → GET /api/heartbeat → has_notification?\n ├── 是 → 构建提示词 → AI 回复 → TTS → 播报\n └── 否 → 继续休眠\n```\n\n**触发来源:**\n- **提醒**:倒计时、每日计划、Cron 周期、习惯打卡\n- **闹钟**:按周循环,支持 dismiss(8 天静音)\n- **主动视觉**:CameraWatcher 后台抓帧 → VisionModel 场景分析 → 变化检测\n\n### ⚡ TaskSystem 任务系统\n**智能异步,实时反馈**\n\n**特性:**\n- **复杂度分析**:自动判断是否需要异步\n- **异步任务**:慢速工具后台执行\n- **心跳轮询**:前端轮询获取结果\n- **实时反馈**:每步骤TTS进度推送\n- **超步数汇报**:步数用尽且工具未结束时不无声终止,追加一轮把结果总结给用户\n\n**工作流程:**\n```\n用户请求 → 复杂度分析 → 高复杂度? → 异步任务 → 心跳轮询 → 结果返回\n ↓\n 低复杂度 → 同步执行 → 直接返回\n```\n\n### 🔐 AuthSystem 认证系统\n**五层防护,安全可控**\n\n**认证层级:**\n1. **L4 API Key**:程序化访问,Agent API\n2. **L1 Session**:终端/Web UI登录\n3. **L2 WebAuthn**:通行密钥,硬件认证\n4. **L3 TOTP**:时间-based双因素认证\n5. **L0 配对码**:首次设备配对\n\n**流程示例:**\n```bash\n# 1. 生成配对码\n/newbind\n# → 配对码: 12345678 (8分钟有效)\n\n# 2. 客户端提交\nPOST /api/auth/pairing/verify\n{\"code\": \"12345678\"}\n\n# 3. 返回Session\n{\"session\": \"eyJhbGciOiJIUzI1NiIs...\"}\n```\n\n### 🖼️ VisionSystem 视觉系统\n**多模态感知**\n\n**组件:**\n- **VisionModel**:通用视觉模型 (GLM-4.6V/GPT-4V)\n- **OCRModel**:文档OCR (deepseek-ocr)\n- **VISION_OVERRIDE**:接管OCR+2md管线\n\n**视觉管线:**\n```\n图片 → VisionModel → 描述文本\n文档 → OCRModel → Markdown → .hmd\n```\n\n---\n\n## 🎮 使用技巧\n\n### 终端语音客户端快捷键\n```\n[Enter] 按住录音,松开发送\n[a 双击] 锁定/解锁面板\n[b 双击] 音乐播放器模式\n[p] 显示人格状态\n[s] 切换待机/唤醒\n[i] 系统信息\n[k] 跳过最新提醒\n[f] 静音闹钟 + 停止TTS\n[r] 手动触发心跳\n[t] 文字输入(同步)\n[=] 异步任务(长时间)\n[h] 显示帮助\n[q/Ctrl+C] 退出\n```\n\n### 服务器控制台命令\n```\n/newbind - 生成新配对码\n/users - 列出所有用户\n/status - 服务器状态\n/agent create - 创建Agent\n/agent bind - 绑定Agent到用户\n/memory query - 搜索记忆\n/memory reindex - 重建向量索引\n/hibernate sleep - 进入待机\n/config set - 动态修改配置\n/stop - 停止服务器\n```\n\n### API调用示例\n```bash\n# 发送消息\ncurl -X POST http://localhost:5000/api/chat/send \\\n -H \"Authorization: Bearer YOUR_SESSION\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"message\": \"你好\"}'\n\n# 语音直通(ASR → AI → TTS)\ncurl -X POST http://localhost:5000/api/asr/passthrough \\\n -H \"Authorization: Bearer YOUR_SESSION\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"audio_b64\": \"BASE64_WAV_DATA\"}'\n\n# Agent发送消息\npython agent_send.py \"分析代码复杂度\"\n\n# 搜索记忆\ncurl \"http://localhost:5000/api/memory/query?q=Python项目&limit=5\"\n```\n\n---\n\n## ⚙️ 核心配置\n\n### 最小配置 (必需)\n```bash\nOPENAI_API_KEY=sk-your-key # API密钥\nOPENAI_API_BASE=https://api.deepseek.com/v1 # API地址\nMAIN_MODEL_NAME=deepseek-v4-flash # 主模型\n```\n\n### 语音完整配置 (推荐)\n```bash\n# 语音\nASR_ENABLED=true\nASR_DEVICE=cuda\nTTS_BASE_URL=http://127.0.0.1:9880\nTTS_PROCESS_ENABLED=true\n\n# 主模型\nMAIN_MODEL_TYPE=openai\nMAIN_MODEL_NAME=deepseek-v4-flash\n\n# 记忆系统\nMEMORY_ENABLED=true\nMEMORY_EMBEDDING_ENABLED=true\n\n# 人格系统\nPERSONALITY_V3_ENABLED=true\n\n# 世界系统\nWORLD_ENABLED=true\nNARRATIVE_ENABLED=true\n\n# 语义缓存\nSEMANTIC_CACHE_ENABLED=true\n```\n\n### 性能与感知调优\n```bash\n# 工具箱:首轮只发 toolbox 索引,激活后才附带具体工具 schema\nTOOLBOX_ENABLED=true\n\n# 主模型历史条数上限(非 system 消息)\nMODEL_MAX_HISTORY=12\n\n# Agent 最大步数;步数用尽但任务未结束时会自动追加一轮向用户汇报\nAGENT_MAX_STEPS=15\n\n# 视觉:启动预热 VLM、look_around 短时去重窗口(秒)\nVISION_WARMUP=true\nVISION_LOOK_AROUND_DEDUP=10\n\n# 客户端侧 (minimal.py):心跳间隔(秒)、缓存帧新鲜度(秒)\n# DSN_HEARTBEAT_INTERVAL=2\n# DSN_FRAME_CACHE_MAX_AGE=3\n\n# 本地 GPU 后台任务节流(秒)\nHIBERNATE_PERSONALITY_COOLDOWN=30\nHIBERNATE_MEMORY_COOLDOWN=60\n```\n\n### 完整配置列表\n查看 [`.env.example`](.env.example) 获取所有配置项。\n\n---\n\n## 📄 许可证\n\n暂未考虑 :/\n\n---\n\n## 链接\n\n[LINUX DO](https://linux.do/) - 下一代技术交流社区\n\n---\n\n**[⬆ 回到顶部](#-dsn-exp)**\n\nMade with ❤️ by [Darkstar](https://github.com/ccjjfdyqlhy)\n"}}, "config.py": {"1cfce308afc3f2333b59d64d39ffdb3b70bb210a": {"t": 1786068483.4200728, "n": 18904, "h": "ea3409a37e55faf2", "c": "\n# DSN-exp/config.py\n# 所有配置均从环境变量或 .env 文件读取,切勿在此文件中硬编码密钥。\n\nimport os\nfrom pathlib import Path\n\n# 加载 .env 文件(如果存在)\ntry:\n from dotenv import load_dotenv\n _env_path = Path(__file__).parent / \".env\"\n if _env_path.exists():\n load_dotenv(_env_path)\nexcept ImportError:\n pass\n\n\ndef _env(key: str, default=None, required: bool = False):\n val = os.environ.get(key, default)\n if required and not val:\n raise EnvironmentError(\n f\"缺少必需的环境变量: {key}\\n请参考 .env.example 创建 .env 文件。\"\n )\n return val\n\n\ndef _env_bool(key: str, default: str = \"false\") -> bool:\n return str(_env(key, default)).lower() in (\"1\", \"true\", \"yes\", \"on\")\n\n\nclass Config:\n # ═══════════════════════════════════════════════════════════════════════\n # 第一层: 必需配置 — 不填系统无法运行\n # ═══════════════════════════════════════════════════════════════════════\n\n # 主对话模型\n MAIN_MODEL_TYPE = _env(\"MAIN_MODEL_TYPE\", \"openai\") # \"openai\" | \"lmstudio\"\n MAIN_MODEL_NAME = _env(\"MAIN_MODEL_NAME\", \"deepseek-v4-flash\")\n\n # OpenAI 兼容 API;主模型使用 openai 后端时 API Key 必填。\n OPENAI_API_KEY = _env(\n \"OPENAI_API_KEY\",\n \"\",\n required=MAIN_MODEL_TYPE == \"openai\",\n )\n OPENAI_API_BASE = _env(\"OPENAI_API_BASE\", \"https://api.deepseek.com/v1\")\n OPENAI_BACKUP_API_KEY = _env(\"OPENAI_BACKUP_API_KEY\", \"\")\n\n # 多账号 FailoverChat: 近期失败过的账号自动排后的窗口(秒)。\n # 该窗口内失败的账号会被自动放到回退链末尾(手动 promote/demote 仍保留,\n # 但\"刚失败过\"优先于提优,避免在坏账号上白等)。窗口过后恢复原优先级排序。\n FAILOVER_DOWN_WINDOW = int(_env(\"FAILOVER_DOWN_WINDOW\", \"3600\"))\n\n # ═══════════════════════════════════════════════════════════════════════\n # 第二层: 服务与存储\n # ═══════════════════════════════════════════════════════════════════════\n\n # ── 服务 ──\n SERVER_HOST = _env(\"SERVER_HOST\", \"0.0.0.0\")\n SERVER_PORT = int(_env(\"SERVER_PORT\", \"5000\"))\n SERVER_BASE_URL = _env(\"SERVER_BASE_URL\", \"\") # 对外访问地址,为空则自动检测\n LOCAL_CALLBACK_PORT = int(_env(\"LOCAL_CALLBACK_PORT\", \"5001\"))\n\n # ── 存储 ──\n DATABASE_PATH = _env(\"DATABASE_PATH\", \"DSN_usrdata.db\")\n LOG_DIR = _env(\"LOG_DIR\", \"logs\")\n LOG_MAX_BYTES = max(1024, int(_env(\"LOG_MAX_BYTES\", str(10 * 1024 * 1024))))\n LOG_BACKUP_COUNT = max(0, int(_env(\"LOG_BACKUP_COUNT\", \"30\")))\n LOG_BUFFER_SIZE = max(1, int(_env(\"LOG_BUFFER_SIZE\", \"200\")))\n LOG_LEVEL = _env(\"LOG_LEVEL\", \"INFO\")\n\n # ── 安全 ──\n JWT_SECRET = _env(\"JWT_SECRET\", \"dsn-exp-auto-secret\")\n\n # ═══════════════════════════════════════════════════════════════════════\n # 第三层: 模型基础设施\n # ═══════════════════════════════════════════════════════════════════════\n\n # ── 主模型参数(LMStudio 后端专用)──\n LMSTUDIO_ENABLED = _env(\"LMSTUDIO_ENABLED\", \"true\").lower() == \"true\"\n LMSTUDIO_BASE_URL = _env(\"LMSTUDIO_BASE_URL\", \"http://localhost:4501\")\n LMSTUDIO_TEMPERATURE = float(_env(\"LMSTUDIO_TEMPERATURE\", \"0.7\"))\n LMSTUDIO_MAX_TOKENS = int(_env(\"LMSTUDIO_MAX_TOKENS\", \"4096\"))\n LMSTUDIO_TIMEOUT = int(_env(\"LMSTUDIO_TIMEOUT\", \"300\"))\n\n # ── 推理模型 ──\n REASONER_ENABLED = _env(\"REASONER_ENABLED\", \"true\").lower() == \"true\"\n REASONER_MODEL = _env(\"REASONER_MODEL\", \"deepseek-v4-pro\")\n REASONER_TIMEOUT = int(_env(\"REASONER_TIMEOUT\", \"1200\"))\n\n # ── 模型共存管理 ──\n MAX_CONCURRENT_LM_MODELS = int(_env(\"MAX_CONCURRENT_LM_MODELS\", \"1\"))\n MODEL_LOAD_TIMEOUT = int(_env(\"MODEL_LOAD_TIMEOUT\", \"180\"))\n MODEL_REQUEST_TIMEOUT = int(_env(\"MODEL_REQUEST_TIMEOUT\", \"300\"))\n\n # ── Token 消耗定价 (USD / 1M tokens) ──\n TOKEN_PRICE_FLASH_INPUT = float(_env(\"TOKEN_PRICE_FLASH_INPUT\", \"0.14\"))\n TOKEN_PRICE_FLASH_OUTPUT = float(_env(\"TOKEN_PRICE_FLASH_OUTPUT\", \"0.28\"))\n TOKEN_PRICE_PRO_INPUT = float(_env(\"TOKEN_PRICE_PRO_INPUT\", \"0.435\"))\n TOKEN_PRICE_PRO_OUTPUT = float(_env(\"TOKEN_PRICE_PRO_OUTPUT\", \"0.87\"))\n DEEPSEEK_FLASH_INPUT_PRICE = TOKEN_PRICE_FLASH_INPUT\n DEEPSEEK_FLASH_OUTPUT_PRICE = TOKEN_PRICE_FLASH_OUTPUT\n DEEPSEEK_PRO_INPUT_PRICE = TOKEN_PRICE_PRO_INPUT\n DEEPSEEK_PRO_OUTPUT_PRICE = TOKEN_PRICE_PRO_OUTPUT\n\n # ═══════════════════════════════════════════════════════════════════════\n # 第四层: 子系统\n # ═══════════════════════════════════════════════════════════════════════\n\n # ── 记忆与摘要 ──\n MEMORY_ENABLED = _env(\"MEMORY_ENABLED\", \"true\").lower() == \"true\"\n MEMORY_SUMMARY_BACKEND = _env(\"MEMORY_SUMMARY_BACKEND\", \"openai\") # \"openai\" | \"lmstudio\"\n MEMORY_MODEL = _env(\"MEMORY_MODEL\", \"deepseek-v4-flash\")\n MEMORY_SUMMARY_LENGTH = int(_env(\"MEMORY_SUMMARY_LENGTH\", \"100\"))\n MEMORY_CONTEXT_WINDOW_SIZE = int(_env(\"MEMORY_CONTEXT_WINDOW_SIZE\", \"80\"))\n MEMORY_REPLACE_THRESHOLD_RATIO = float(_env(\"MEMORY_REPLACE_THRESHOLD_RATIO\", \"0.7\"))\n MEMORY_ASYNC_ENABLED = _env(\"MEMORY_ASYNC_ENABLED\", \"true\").lower() == \"true\"\n MEMORY_SEARCH_THRESHOLD = min(1.0, max(0.0, float(_env(\"MEMORY_SEARCH_THRESHOLD\", \"0.5\"))))\n MEMORY_DETAIL_CHARS_PER_ROUND = max(1, int(_env(\"MEMORY_DETAIL_CHARS_PER_ROUND\", \"4000\")))\n MEMORY_TOTAL_DETAIL_CHARS = max(1, int(_env(\"MEMORY_TOTAL_DETAIL_CHARS\", \"16000\")))\n MEMORY_QUERY_LIMIT = max(1, int(_env(\"MEMORY_QUERY_LIMIT\", \"50\")))\n\n # 摘要模型参数\n SUMMARY_RETRY_COUNT = max(0, int(_env(\"SUMMARY_RETRY_COUNT\", \"3\")))\n SUMMARY_RETRY_BACKOFF_SECONDS = max(0.0, float(_env(\"SUMMARY_RETRY_BACKOFF_SECONDS\", \"1.0\")))\n SUMMARY_CONTEXT_SIZE = max(0, int(_env(\"SUMMARY_CONTEXT_SIZE\", \"10\")))\n SUMMARY_TEMPERATURE = min(2.0, max(0.0, float(_env(\"SUMMARY_TEMPERATURE\", \"0.1\"))))\n SUMMARY_TIMEOUT = max(1, int(_env(\"SUMMARY_TIMEOUT\", \"60\")))\n\n # ── 向量嵌入检索 ──\n MEMORY_EMBEDDING_ENABLED = _env(\"MEMORY_EMBEDDING_ENABLED\", \"false\").lower() == \"true\"\n MEMORY_EMBEDDING_MODEL = _env(\"MEMORY_EMBEDDING_MODEL\", \"text-embedding-nomic-embed-text-v1.5@f16\")\n MEMORY_EMBEDDING_DIMS = int(_env(\"MEMORY_EMBEDDING_DIMS\", \"768\"))\n MEMORY_EMBEDDING_WEIGHT = float(_env(\"MEMORY_EMBEDDING_WEIGHT\", \"0.6\"))\n\n # ── 人格系统 V3 ──\n PERSONALITY_V3_ENABLED = _env(\"PERSONALITY_V3_ENABLED\", \"true\").lower() == \"true\"\n PERSONALITY_V3_OVERRIDE_V2 = _env(\"PERSONALITY_V3_OVERRIDE_V2\", \"true\").lower() == \"true\"\n PERSONALITY_MODEL_NAME = _env(\"PERSONALITY_MODEL_NAME\", \"google/gemma-3-4b\")\n PERSONALITY_MODEL_URL = _env(\"PERSONALITY_MODEL_URL\", None) or _env(\"LMSTUDIO_BASE_URL\", \"http://localhost:4501\")\n DISTILLATION_MODEL = _env(\"DISTILLATION_MODEL\", \"openai\")\n PERSONALITY_V3_DEFAULT_CARD = _env(\"PERSONALITY_V3_DEFAULT_CARD\", \"\")\n\n # ── 叙事世界模型 ──\n WORLD_ENABLED = _env(\"WORLD_ENABLED\", \"true\").lower() in (\"1\", \"true\", \"yes\")\n WORLD_PRESET = _env(\"WORLD_PRESET\", \"default\")\n WORLD_UPDATE_INTERVAL = int(_env(\"WORLD_UPDATE_INTERVAL\", \"60\"))\n\n NARRATIVE_ENABLED = _env(\"NARRATIVE_ENABLED\", \"true\").lower() in (\"1\", \"true\", \"yes\")\n NARRATIVE_MODEL_TYPE = _env(\"NARRATIVE_MODEL_TYPE\", \"lmstudio\")\n NARRATIVE_MODEL = _env(\"NARRATIVE_MODEL\", \"google/gemma-3-4b\")\n NARRATIVE_TEMPERATURE = float(_env(\"NARRATIVE_TEMPERATURE\", \"0.9\"))\n NARRATIVE_MAX_TOKENS = int(_env(\"NARRATIVE_MAX_TOKENS\", \"150\"))\n NARRATIVE_KEEP_HISTORY = _env(\"NARRATIVE_KEEP_HISTORY\", \"false\").lower() in (\"1\", \"true\", \"yes\")\n NARRATIVE_PRE_ENABLED = _env(\"NARRATIVE_PRE_ENABLED\", \"true\").lower() == \"true\"\n\n # ── 工具调用 ──\n TOOL_CALL_MODE = _env(\"TOOL_CALL_MODE\", \"native\") # \"native\" | \"xml\" | \"auto\"\n TOOL_CALL_MODEL = _env(\"TOOL_CALL_MODEL\", \"deepseek-v4-pro\")\n TOOLBOX_ENABLED = _env(\"TOOLBOX_ENABLED\", \"true\") == \"true\"\n # 主模型对话历史裁剪上限(非 system 消息保留条数);0 = 不裁剪\n MODEL_MAX_HISTORY = max(0, int(_env(\"MODEL_MAX_HISTORY\", \"12\")))\n # 客户端未携带 chat_id 时,自动续接该用户最近一个聊天,而不是新建聊天\n CHAT_RESUME_LATEST = _env(\"CHAT_RESUME_LATEST\", \"true\").lower() in (\"true\", \"1\", \"yes\", \"on\")\n\n # ── 文件操作限制 ──\n FILE_READ_MAX_SIZE_MB = max(1, int(_env(\"FILE_READ_MAX_SIZE_MB\", \"1\")))\n\n # ═══════════════════════════════════════════════════════════════════════\n # 第五层: 语音系统\n # ═══════════════════════════════════════════════════════════════════════\n\n # ── ASR ──\n ASR_ENABLED = _env(\"ASR_ENABLED\", \"false\").lower() == \"true\"\n ASR_DEVICE = _env(\"ASR_DEVICE\", \"cuda\")\n ASR_GPU_ID = _env(\"ASR_GPU_ID\", \"\")\n ASR_BATCH_SIZE_SECONDS = max(1, int(_env(\"ASR_BATCH_SIZE_SECONDS\", \"60\")))\n ASR_CONVERT_TIMEOUT = max(1, int(_env(\"ASR_CONVERT_TIMEOUT\", \"15\")))\n DEBUG_ASR = _env(\"DEBUG_ASR\", \"false\").lower() == \"true\"\n\n # ── ASR 过滤器 ──\n ASR_FILTER_ENABLED = _env(\"ASR_FILTER_ENABLED\", \"false\").lower() == \"true\"\n FILTER_MODEL = _env(\"FILTER_MODEL\", \"llama-3.2-1b-instruct\")\n\n # ── TTS ──\n TTS_ENABLED = _env(\"TTS_ENABLED\", \"true\").lower() == \"true\"\n TTS_BASE_URL = _env(\"TTS_BASE_URL\", \"http://127.0.0.1:9880\")\n TTS_PROBE_TIMEOUT = max(0.1, float(_env(\"TTS_PROBE_TIMEOUT\", \"3.0\")))\n TTS_PROBE_ATTEMPTS = max(1, int(_env(\"TTS_PROBE_ATTEMPTS\", _env(\"TTS_PROBE_RETRIES\", \"4\"))))\n TTS_PROBE_RETRIES = TTS_PROBE_ATTEMPTS\n TTS_PROBE_BACKOFF_BASE = max(0.0, float(_env(\"TTS_PROBE_BACKOFF_BASE\", \"2.0\")))\n TTS_PROBE_BACKOFF_MAX = max(TTS_PROBE_BACKOFF_BASE, float(_env(\"TTS_PROBE_BACKOFF_MAX\", \"8.0\")))\n TTS_FAST_FIRST_LINE = _env(\"TTS_FAST_FIRST_LINE\", \"true\").lower() == \"true\"\n\n # ── TTS 文本预处理 ──\n TTS_PROCESS_ENABLED = _env(\"TTS_PROCESS_ENABLED\", \"true\").lower() == \"true\"\n TTS_PROCESS_MODEL = _env(\"TTS_PROCESS_MODEL\", None) or _env(\"MEMORY_MODEL\", \"google/gemma-3-4b\")\n TTS_PROCESS_MAX_TOKENS = int(_env(\"TTS_PROCESS_MAX_TOKENS\", \"1024\"))\n TTS_PROCESS_TEMPERATURE = float(_env(\"TTS_PROCESS_TEMPERATURE\", \"0.2\"))\n TTS_PROCESS_TIMEOUT = int(_env(\"TTS_PROCESS_TIMEOUT\", \"30\"))\n\n # ═══════════════════════════════════════════════════════════════════════\n # 第六层: 视觉系统\n # ═══════════════════════════════════════════════════════════════════════\n\n # ── 视觉模型(GLM-4.6V / GPT-4V 等)──\n VISION_ENABLED = _env(\"VISION_ENABLED\", \"true\").lower() == \"true\"\n VISION_PROMPT = _env(\"VISION_PROMPT\", \"请详细描述这张图片的内容\")\n VISION_API_KEY = _env(\"VISION_API_KEY\", \"\")\n VISION_API_BASE = _env(\"VISION_API_BASE\", \"https://open.bigmodel.cn/api/paas/v4\")\n VISION_MODEL_NAME = _env(\"VISION_MODEL_NAME\", \"glm-4.6v\")\n VISION_OVERRIDE = _env(\"VISION_OVERRIDE\", \"false\").lower() == \"true\"\n # 启动时预热 VLM(后台发一次低 token dummy 请求,摊薄首次 look_around 冷启动)\n VISION_WARMUP = _env(\"VISION_WARMUP\", \"true\").lower() == \"true\"\n # look_around 短时去重窗口(秒):窗口内同 focus+camera 的重复观察直接复用上次结果\n VISION_LOOK_AROUND_DEDUP = float(_env(\"VISION_LOOK_AROUND_DEDUP\", \"10\"))\n\n # ── OCR ──\n OCR_MODEL = _env(\"OCR_MODEL\", \"deepseek-ocr\")\n OCR_BASE_URL = _env(\"OCR_BASE_URL\", \"http://localhost:4502\")\n OCR_UNLOAD_AFTER_USE = _env(\"OCR_UNLOAD_AFTER_USE\", \"true\").lower() == \"true\"\n\n # ── 文档解析 ──\n TWO_MD_API = _env(\"TWO_MD_API\", \"http://localhost:8000\")\n\n # ── 摄像头 ──\n CAMERA_ENABLED = _env(\"CAMERA_ENABLED\", \"true\").lower() == \"true\"\n CAMERA_DEVICE_ID = int(_env(\"CAMERA_DEVICE_ID\", \"0\"))\n\n # ── 主动视觉感知 ──\n ACTIVE_VISION_ENABLED = _env(\"ACTIVE_VISION_ENABLED\", \"false\").lower() == \"true\"\n ACTIVE_VISION_INTERVAL = int(_env(\"ACTIVE_VISION_INTERVAL\", \"300\"))\n ACTIVE_VISION_CAMERA = _env(\"ACTIVE_VISION_CAMERA\", \"\") # 周期观察用哪台摄像头(逻辑名), 空=主摄像头\n ACTIVE_VISION_PROACTIVE_COOLDOWN = int(_env(\"ACTIVE_VISION_PROACTIVE_COOLDOWN\", \"600\"))\n ACTIVE_VISION_PERIODIC_NOTIFY_MIN = int(_env(\"ACTIVE_VISION_PERIODIC_NOTIFY_MIN\", \"30\"))\n\n # ── 闲置时感知 (idle-time sensing) ──\n # 主开关:客户端在未按 Enter 录音时持续监听麦克风,感知到响动就捕捉并上报后端 ASR 存档。\n SENSING_ENABLED = _env_bool(\"SENSING_ENABLED\", \"false\")\n # 同一客户端两次上报之间的最小间隔(秒),服务端/客户端都会用它节流。\n SENSING_COOLDOWN = max(1, int(_env(\"SENSING_COOLDOWN\", \"60\")))\n # 单条感知片段的最大时长(秒)。\n SENSING_MAX_RECORD_SECS = max(1.0, float(_env(\"SENSING_MAX_RECORD_SECS\", \"6.0\")))\n # 允许 AI 通过工具查询闲置时感知记录(false 时工具返回未启用)。\n SENSING_AI_ACCESS_ENABLED = _env_bool(\"SENSING_AI_ACCESS_ENABLED\", \"false\")\n\n # ═══════════════════════════════════════════════════════════════════════\n # 第七层: 高级功能\n # ═══════════════════════════════════════════════════════════════════════\n\n # ── 认证 ──\n AUTH_SESSION_DAYS = int(_env(\"AUTH_SESSION_DAYS\", \"30\"))\n AUTH_PAIRING_DIGITS = int(_env(\"AUTH_PAIRING_DIGITS\", \"8\"))\n AUTH_PAIRING_TIMEOUT = int(_env(\"AUTH_PAIRING_TIMEOUT\", \"300\"))\n AUTH_WEBAUTHN_RP_NAME = _env(\"AUTH_WEBAUTHN_RP_NAME\", \"DSN-exp\")\n AUTH_TOTP_ISSUER = _env(\"AUTH_TOTP_ISSUER\", \"DSN-exp\")\n\n # ── 任务管理 ──\n TASK_MANAGER_ENABLED = _env(\"TASK_MANAGER_ENABLED\", \"true\").lower() == \"true\"\n TASK_MAX_WORKERS = int(_env(\"TASK_MAX_WORKERS\", \"5\"))\n TASK_COMPLEXITY_THRESHOLD = float(_env(\"TASK_COMPLEXITY_THRESHOLD\", \"0.4\"))\n REMINDER_CHECK_INTERVAL = int(_env(\"REMINDER_CHECK_INTERVAL\", \"60\"))\n REMINDER_LIST_LIMIT = max(1, int(_env(\"REMINDER_LIST_LIMIT\", \"50\")))\n TASK_NOTIFICATION_ENABLED = _env(\"TASK_NOTIFICATION_ENABLED\", \"true\").lower() == \"true\"\n ACTION_TIMEOUT = int(_env(\"ACTION_TIMEOUT\", \"300\"))\n\n # ── Agent 循环 ──\n AGENT_MAX_STEPS = max(1, int(_env(\"AGENT_MAX_STEPS\", \"10\")))\n AGENT_TOKEN_BUDGET = max(1, int(_env(\"AGENT_TOKEN_BUDGET\", \"1000000\")))\n AGENT_TIMEOUT_SECONDS = max(1.0, float(_env(\"AGENT_TIMEOUT_SECONDS\", \"120\")))\n\n # ── 双模协同 ──\n DUAL_ENABLED = _env_bool(\"DUAL_ENABLED\", \"false\")\n INSTANT_MODEL = _env(\"INSTANT_MODEL\", \"google/gemma-3-4b\")\n INSTANT_MODEL_URL = _env(\"INSTANT_MODEL_URL\", None) or _env(\"LMSTUDIO_BASE_URL\", \"http://localhost:4501\")\n INSTANT_TEMPERATURE = float(_env(\"INSTANT_TEMPERATURE\", \"0.6\"))\n INSTANT_MAX_TOKENS = int(_env(\"INSTANT_MAX_TOKENS\", \"512\"))\n INSTANT_TIMEOUT = max(1, int(_env(\"INSTANT_TIMEOUT\", \"15\")))\n DUAL_MAIN_WORKERS = max(1, int(_env(\"DUAL_MAIN_WORKERS\", \"3\")))\n INSTANT_CONTEXT_MAX_MESSAGES = max(4, int(_env(\"INSTANT_CONTEXT_MAX_MESSAGES\", \"30\")))\n INSTANT_CONTEXT_COMPRESS_THRESHOLD = max(6, int(_env(\"INSTANT_CONTEXT_COMPRESS_THRESHOLD\", \"40\")))\n\n # ── 售后维护 ──\n MAINTENANCE_ENABLED = _env(\"MAINTENANCE_ENABLED\", \"true\").lower() == \"true\"\n MAINTENANCE_IDLE_TIMEOUT_MINUTES = int(_env(\"MAINTENANCE_IDLE_TIMEOUT_MINUTES\", \"60\"))\n MAINTENANCE_SCHEDULE_STRATEGY = _env(\"MAINTENANCE_SCHEDULE_STRATEGY\", \"predictive\")\n MAINTENANCE_FIXED_HOUR = int(_env(\"MAINTENANCE_FIXED_HOUR\", \"4\"))\n MAINTENANCE_SCHEDULE_CHECK_INTERVAL = int(_env(\"MAINTENANCE_SCHEDULE_CHECK_INTERVAL\", \"60\"))\n HIBERNATE_MAX_QUEUE = int(_env(\"HIBERNATE_MAX_QUEUE\", \"100\"))\n MAINTENANCE_PREDICTIVE_MIN_FREE_HOURS = int(_env(\"MAINTENANCE_PREDICTIVE_MIN_FREE_HOURS\", \"3\"))\n MAINTENANCE_PREDICTIVE_MAX_HOUR = int(_env(\"MAINTENANCE_PREDICTIVE_MAX_HOUR\", \"8\"))\n MAINTENANCE_PREDICTIVE_IDLE_TRIGGER_MINUTES = int(_env(\"MAINTENANCE_PREDICTIVE_IDLE_TRIGGER_MINUTES\", \"60\"))\n MAINTENANCE_PREDICTIVE_MIN_DATA_SAMPLES = int(_env(\"MAINTENANCE_PREDICTIVE_MIN_DATA_SAMPLES\", \"50\"))\n MAINTENANCE_RETRY_ON_FAILURE = _env_bool(\"MAINTENANCE_RETRY_ON_FAILURE\", \"true\")\n MAINTENANCE_RETRY_DELAY_MINUTES = int(_env(\"MAINTENANCE_RETRY_DELAY_MINUTES\", \"30\"))\n MAINTENANCE_ESTIMATE_PER_TASK_SECONDS = int(_env(\"MAINTENANCE_ESTIMATE_PER_TASK_SECONDS\", \"300\"))\n MAINTENANCE_TASK_TIMEOUT_SECONDS = int(_env(\"MAINTENANCE_TASK_TIMEOUT_SECONDS\", \"600\"))\n\n # ── 驻守模型 ──\n STEWARD_ENABLED = _env(\"STEWARD_ENABLED\", \"true\").lower() == \"true\"\n STEWARD_MODEL_TYPE = _env(\"STEWARD_MODEL_TYPE\", \"openai\")\n STEWARD_MODEL_NAME = _env(\"STEWARD_MODEL_NAME\", \"google/gemma-3-4b\")\n STEWARD_TIMEOUT = int(_env(\"STEWARD_TIMEOUT\", \"300\"))\n STEWARD_TEMPERATURE = float(_env(\"STEWARD_TEMPERATURE\", \"0.5\"))\n STEWARD_MAX_TOKENS = int(_env(\"STEWARD_MAX_TOKENS\", \"2048\"))\n STEWARD_HISTORY_LIMIT = int(_env(\"STEWARD_HISTORY_LIMIT\", \"40\"))\n\n # ── 语义缓存 ──\n SEMANTIC_CACHE_ENABLED = _env(\"SEMANTIC_CACHE_ENABLED\", \"false\").lower() == \"true\"\n SEMANTIC_CACHE_DIR = _env(\"SEMANTIC_CACHE_DIR\", \".dsn/semantic_cache\")\n SEMANTIC_CACHE_SIMILARITY_THRESHOLD = float(_env(\"SEMANTIC_CACHE_SIMILARITY_THRESHOLD\", \"0.85\"))\n SEMANTIC_CACHE_MAX_ENTRIES = int(_env(\"SEMANTIC_CACHE_MAX_ENTRIES\", \"5000\"))\n\n # ── 性能模式 ──\n PERFORMANCE_MODE = _env(\"PERFORMANCE_MODE\", \"realtime\") # \"realtime\" | \"fastcache\"\n\n # ── 观察日记 ──\n NOTEBOOK_ENABLED = _env(\"NOTEBOOK_ENABLED\", \"true\").lower() == \"true\"\n NOTEBOOK_FREQUENCY = int(_env(\"NOTEBOOK_FREQUENCY\", \"10\"))\n\n # ── 工作区 ──\n WORKSPACE_DIR = _env(\"WORKSPACE_DIR\", \".dsn/workspace\")\n\n # ── 外部验证 ──\n LITTLESKIN_CLIENT_ID = _env(\"LITTLESKIN_CLIENT_ID\", \"\")\n LITTLESKIN_CLIENT_SECRET = _env(\"LITTLESKIN_CLIENT_SECRET\", \"\")\n\n # ── 调试 ──\n DEBUG_PLAY_AS_MODEL = _env(\"DEBUG_PLAY_AS_MODEL\", \"false\").lower() == \"true\"\n DEBUG_PLAY_AS_MODEL_PORT = int(_env(\"DEBUG_PLAY_AS_MODEL_PORT\", \"5050\"))\n\n # ── 可选客户端 / 管理端 ──\n WEB_ADMIN_ENABLED = _env_bool(\"WEB_ADMIN_ENABLED\", \"false\")\n WEB_ADMIN_HOST = _env(\"WEB_ADMIN_HOST\", \"127.0.0.1\")\n WEB_ADMIN_PORT = int(_env(\"WEB_ADMIN_PORT\", \"8008\"))\n WEB_ADMIN_PASSWORD = _env(\"WEB_ADMIN_PASSWORD\", \"\")\n DSN_BASE_URL = _env(\"DSN_BASE_URL\", \"http://127.0.0.1:5000\")\n DSN_AGENT_TIMEOUT = int(_env(\"DSN_AGENT_TIMEOUT\", \"300\"))\n DSN_HOST = _env(\"DSN_HOST\", \"127.0.0.1\")\n DSN_PORT = int(_env(\"DSN_PORT\", \"5000\"))\n PSYCHOSCOPE_HOST = _env(\"PSYCHOSCOPE_HOST\", \"127.0.0.1\")\n PSYCHOSCOPE_PORT = int(_env(\"PSYCHOSCOPE_PORT\", \"5000\"))\n PSYCHOSCOPE_DEBUG = _env_bool(\"PSYCHOSCOPE_DEBUG\", \"false\")\n\n @classmethod\n def load_skill_configs(cls):\n \"\"\"加载 skills/*/skill.env,让技能能声明自己的环境配置。\"\"\"\n try:\n from dotenv import load_dotenv as _load_dotenv\n except ImportError:\n return\n import logging\n\n logger = logging.getLogger(\"Config\")\n for base_key in (\"builtin\", \"custom\"):\n base_dir = Path(__file__).parent / \"skills\" / base_key\n if not base_dir.exists():\n continue\n for skill_dir in sorted(base_dir.iterdir()):\n env_file = skill_dir / \"skill.env\"\n if not env_file.exists():\n continue\n _load_dotenv(env_file)\n count = 0\n for line in env_file.read_text(encoding=\"utf-8\").splitlines():\n stripped = line.strip()\n if not stripped or stripped.startswith(\"#\") or \"=\" not in stripped:\n continue\n key, raw = stripped.split(\"=\", 1)\n key = key.strip()\n raw = raw.strip()\n value = os.environ.get(key, raw)\n try:\n if raw.lower() in (\"true\", \"false\"):\n value = value.lower() == \"true\"\n elif \".\" in raw:\n value = float(value)\n else:\n value = int(value)\n except (TypeError, ValueError):\n pass\n setattr(cls, key, value)\n count += 1\n logger.info(\"skill.env: %s (%d 项)\", skill_dir.name, count)\n"}}}}