Files
ylhan 35e107c290 助手本体:会话引擎、LLM 路由、记忆、身份、语音适配
从 Obsidian 笔记库剥离为独立仓库。规划文档与 P0 报告留在笔记库,
代码(助手本体、TTS 服务、P0 基准脚本、ESP32 固件)在此。

架构:服务器是助手,本地是客户端
- 助手本体跑在 cadadmin-server,N5095 只做 USB 外设桥与网络桥接
- 起因:实测 N5095 无 AVX2,跑不动可用的语音模型

当前技术栈
- LLM:DeepSeek deepseek-flash(云端,必须关思考)
- TTS:Qwen3-TTS 1.7B(服务器 3090,独立 HTTP 服务)
- STT:Zipformer 中英双语 int8(CPU,中文识别率未测)

实测:端到端 16/16 通过,首音延迟 1545ms
(对比:本地 LLM + vits-zh-ll 是 179ms,但音色不满意——音质与延迟
在此硬件上此消彼长)

设计要点
- MemoryStore 接口按"可能远程"设计(全 async、可超时、可失败)
- user_id / session_id / track_id 三者分开,人脸只作候选不自动切身份
- 记忆查询强制带 owner_user_id,无按 id 单独取的口子
- 自动抽取的记忆是"候选",默认不参与检索
- 异步写记忆绑定创建时的身份,不读"当前用户"
- 引擎与传输解耦,只用文件音频即可端到端测试

模型与音频产物不进版本管理(1.1GB),由 download_models.sh 恢复
2026-09-15 11:05:09 +08:00

48 lines
1.7 KiB
Python
Raw Permalink 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.
"""DeepSeek 云 API(备用后端)。
实测数据(P0-DeepSeek验证报告.md):
首 token P50 786ms / P95 1100ms(关思考,N=8)
视觉可用——正确读出图中数字
模型清单只有 deepseek-flash 与 deepseek-v4-pro
约束(重要,踩过):
默认开启思考模式,此时 API 要求把上一轮 assistant 消息里的
`reasoning_content` 原样回传,否则任何多轮/工具回灌一律 400:
"The `reasoning_content` in the thinking mode must be passed back to the API."
手工构造 assistant 消息(content=None / content="" / 不带 tools)三种写法
实测全部 400,只有回传服务端原始对象才行。
所以会话主路径必须显式关闭思考。
另:本服务思考字段名是 reasoning_content(本机 vLLM 是 reasoning),
基类两个都认。
"""
from __future__ import annotations
from typing import Sequence
from .base import LLMProvider
class DeepSeekProvider(LLMProvider):
supports_vision = True
def _build_body(self, messages: list[dict], *, tools: Sequence[dict] | None,
max_tokens: int) -> dict:
body: dict = {
"model": self.cfg.model,
"messages": messages,
"stream": True,
"max_tokens": max_tokens,
"stream_options": {"include_usage": True},
}
# 显式声明思考开关。不传会走默认(开),多轮与工具回灌会 400。
body["thinking"] = {"type": "enabled" if self.cfg.thinking else "disabled"}
if tools:
body["tools"] = list(tools)
body["tool_choice"] = "auto"
return body
__all__ = ["DeepSeekProvider"]