docs #25
@@ -35,7 +35,7 @@
|
|||||||
| WebSocket | gorilla/websocket | Go 生态最成熟的 WebSocket 库 |
|
| WebSocket | gorilla/websocket | Go 生态最成熟的 WebSocket 库 |
|
||||||
| 会话存储 | Redis | 高速 KV 存储,适合会话状态和上下文缓存 |
|
| 会话存储 | Redis | 高速 KV 存储,适合会话状态和上下文缓存 |
|
||||||
| 持久化存储 | PostgreSQL | 对话历史、用量统计、用户偏好(MVP 阶段可选) |
|
| 持久化存储 | PostgreSQL | 对话历史、用量统计、用户偏好(MVP 阶段可选) |
|
||||||
| 配置管理 | Viper | 支持多格式配置,环境变量覆盖 |
|
| 配置管理 | Viper | 支持 YAML + 环境变量覆盖,详见 `03-接口文档.md` 第六章 |
|
||||||
| 日志 | Zap | 高性能结构化日志 |
|
| 日志 | Zap | 高性能结构化日志 |
|
||||||
|
|
||||||
### AI 服务
|
### AI 服务
|
||||||
|
|||||||
237
docs/03-接口文档.md
237
docs/03-接口文档.md
@@ -673,7 +673,236 @@ if cfg.Redis.Addr != "" {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 六、数据模型
|
## 六、配置管理
|
||||||
|
|
||||||
|
使用 Viper 加载配置,支持 YAML 文件 + 环境变量覆盖。**环境变量优先级高于配置文件**。
|
||||||
|
|
||||||
|
### 配置文件位置
|
||||||
|
|
||||||
|
```
|
||||||
|
backend/config.yaml # 默认加载
|
||||||
|
backend/config.dev.yaml # 开发环境(go run 时使用)
|
||||||
|
backend/config.prod.yaml # 生产环境
|
||||||
|
```
|
||||||
|
|
||||||
|
Viper 加载顺序:先读 `config.yaml`,再根据 `APP_ENV` 环境变量尝试读 `config.{env}.yaml` 覆盖,最后所有环境变量自动覆盖对应字段。
|
||||||
|
|
||||||
|
### Go 配置结构体
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Config 应用配置。
|
||||||
|
type Config struct {
|
||||||
|
App AppConfig `mapstructure:"app"`
|
||||||
|
Server ServerConfig `mapstructure:"server"`
|
||||||
|
Redis RedisConfig `mapstructure:"redis"`
|
||||||
|
AI AIConfig `mapstructure:"ai"`
|
||||||
|
Storage StorageConfig `mapstructure:"storage"`
|
||||||
|
Log LogConfig `mapstructure:"log"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppConfig struct {
|
||||||
|
Env string `mapstructure:"env"` // "dev" | "prod",默认 "dev"
|
||||||
|
Version string `mapstructure:"version"` // 由编译时注入
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfig struct {
|
||||||
|
Host string `mapstructure:"host"` // 默认 "0.0.0.0"
|
||||||
|
Port int `mapstructure:"port"` // 默认 8080
|
||||||
|
ReadTimeout int `mapstructure:"read_timeout"` // 秒,默认 30
|
||||||
|
WriteTimeout int `mapstructure:"write_timeout"` // 秒,默认 30
|
||||||
|
}
|
||||||
|
|
||||||
|
type RedisConfig struct {
|
||||||
|
Addr string `mapstructure:"addr"` // "localhost:6379"
|
||||||
|
Password string `mapstructure:"password"` // 无密码留空
|
||||||
|
DB int `mapstructure:"db"` // 默认 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type AIConfig struct {
|
||||||
|
STT STTConfig `mapstructure:"stt"`
|
||||||
|
LLM LLMConfig `mapstructure:"llm"`
|
||||||
|
TTS TTSConfig `mapstructure:"tts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type STTConfig struct {
|
||||||
|
Provider string `mapstructure:"provider"` // "deepgram"
|
||||||
|
APIKey string `mapstructure:"api_key"`
|
||||||
|
Endpoint string `mapstructure:"endpoint"` // 默认 "wss://api.deepgram.com/v1/listen"
|
||||||
|
}
|
||||||
|
|
||||||
|
type LLMConfig struct {
|
||||||
|
Provider string `mapstructure:"provider"` // "openai"
|
||||||
|
APIKey string `mapstructure:"api_key"`
|
||||||
|
Model string `mapstructure:"model"` // 默认 "gpt-4o"
|
||||||
|
Endpoint string `mapstructure:"endpoint"` // 默认 "https://api.openai.com/v1"
|
||||||
|
Timeout int `mapstructure:"timeout"` // 秒,默认 10
|
||||||
|
}
|
||||||
|
|
||||||
|
type TTSConfig struct {
|
||||||
|
Provider string `mapstructure:"provider"` // "openai"
|
||||||
|
APIKey string `mapstructure:"api_key"`
|
||||||
|
Voice string `mapstructure:"voice"` // 默认 "alloy"
|
||||||
|
Speed float64 `mapstructure:"speed"` // 默认 1.0
|
||||||
|
Endpoint string `mapstructure:"endpoint"` // 默认 "https://api.openai.com/v1"
|
||||||
|
Timeout int `mapstructure:"timeout"` // 秒,默认 5
|
||||||
|
}
|
||||||
|
|
||||||
|
type StorageConfig struct {
|
||||||
|
Driver string `mapstructure:"driver"` // "memory" | "postgres"
|
||||||
|
DSN string `mapstructure:"dsn"` // PostgreSQL 连接串,driver=postgres 时必填
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogConfig struct {
|
||||||
|
Level string `mapstructure:"level"` // "debug" | "info" | "warn" | "error",默认 "info"
|
||||||
|
Format string `mapstructure:"format"` // "json" | "console",生产用 json
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 配置文件示例
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# config.yaml — 所有环境共享的默认值
|
||||||
|
app:
|
||||||
|
env: dev
|
||||||
|
|
||||||
|
server:
|
||||||
|
host: "0.0.0.0"
|
||||||
|
port: 8080
|
||||||
|
read_timeout: 30
|
||||||
|
write_timeout: 30
|
||||||
|
|
||||||
|
redis:
|
||||||
|
addr: "localhost:6379"
|
||||||
|
password: ""
|
||||||
|
db: 0
|
||||||
|
|
||||||
|
ai:
|
||||||
|
stt:
|
||||||
|
provider: deepgram
|
||||||
|
endpoint: "wss://api.deepgram.com/v1/listen"
|
||||||
|
llm:
|
||||||
|
provider: openai
|
||||||
|
model: gpt-4o
|
||||||
|
endpoint: "https://api.openai.com/v1"
|
||||||
|
timeout: 10
|
||||||
|
tts:
|
||||||
|
provider: openai
|
||||||
|
voice: alloy
|
||||||
|
speed: 1.0
|
||||||
|
endpoint: "https://api.openai.com/v1"
|
||||||
|
timeout: 5
|
||||||
|
|
||||||
|
storage:
|
||||||
|
driver: memory
|
||||||
|
|
||||||
|
log:
|
||||||
|
level: info
|
||||||
|
format: console
|
||||||
|
```
|
||||||
|
|
||||||
|
### 环境变量覆盖规则
|
||||||
|
|
||||||
|
Viper 自动将配置项映射为环境变量,规则:**前缀 `CAMTALK_` + 路径大写用 `_` 连接**。
|
||||||
|
|
||||||
|
| 配置项 | 环境变量 | 示例 |
|
||||||
|
|--------|---------|------|
|
||||||
|
| `server.port` | `CAMTALK_SERVER_PORT` | `8080` |
|
||||||
|
| `redis.addr` | `CAMTALK_REDIS_ADDR` | `redis:6379` |
|
||||||
|
| `redis.password` | `CAMTALK_REDIS_PASSWORD` | — |
|
||||||
|
| `ai.stt.api_key` | `CAMTALK_AI_STT_API_KEY` | — |
|
||||||
|
| `ai.llm.api_key` | `CAMTALK_AI_LLM_API_KEY` | — |
|
||||||
|
| `ai.tts.api_key` | `CAMTALK_AI_TTS_API_KEY` | — |
|
||||||
|
| `ai.llm.model` | `CAMTALK_AI_LLM_MODEL` | `gpt-4o` |
|
||||||
|
| `storage.driver` | `CAMTALK_STORAGE_DRIVER` | `postgres` |
|
||||||
|
| `storage.dsn` | `CAMTALK_STORAGE_DSN` | — |
|
||||||
|
| `app.env` | `CAMTALK_APP_ENV` | `prod` |
|
||||||
|
| `log.level` | `CAMTALK_LOG_LEVEL` | `warn` |
|
||||||
|
| `log.format` | `CAMTALK_LOG_FORMAT` | `json` |
|
||||||
|
|
||||||
|
> API Key 和密码**只通过环境变量注入**,不写入配置文件,避免泄露到版本控制。
|
||||||
|
|
||||||
|
### 配置加载代码
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/config/config.go
|
||||||
|
|
||||||
|
func Load() (*Config, error) {
|
||||||
|
v := viper.New()
|
||||||
|
|
||||||
|
// 1. 读默认配置文件
|
||||||
|
v.SetConfigName("config")
|
||||||
|
v.SetConfigType("yaml")
|
||||||
|
v.AddConfigPath("./config") // go run 时
|
||||||
|
v.AddConfigPath(".") // 二进制运行时
|
||||||
|
if err := v.ReadInConfig(); err != nil {
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||||||
|
return nil, fmt.Errorf("read config: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 按环境覆盖
|
||||||
|
env := os.Getenv("CAMTALK_APP_ENV")
|
||||||
|
if env == "" {
|
||||||
|
env = "dev"
|
||||||
|
}
|
||||||
|
v.SetConfigName("config." + env)
|
||||||
|
v.MergeInConfig() // 忽略文件不存在
|
||||||
|
|
||||||
|
// 3. 环境变量覆盖
|
||||||
|
v.SetEnvPrefix("CAMTALK")
|
||||||
|
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
|
v.AutomaticEnv()
|
||||||
|
|
||||||
|
// 4. 解析
|
||||||
|
var cfg Config
|
||||||
|
if err := v.Unmarshal(&cfg); err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshal config: %w", err)
|
||||||
|
}
|
||||||
|
return &cfg, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### main.go 集成
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
cfg, err := config.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("load config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := gin.Default()
|
||||||
|
// 使用 cfg.Server.Port 替代硬编码 :8080
|
||||||
|
addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)
|
||||||
|
log.Printf("CamTalk gateway starting on %s (env=%s)", addr, cfg.App.Env)
|
||||||
|
r.Run(addr)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 启动方式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 开发环境(默认 config.yaml,API Key 通过环境变量注入)
|
||||||
|
CAMTALK_AI_LLM_API_KEY=sk-xxx \
|
||||||
|
CAMTALK_AI_STT_API_KEY=xxx \
|
||||||
|
go run ./cmd/server
|
||||||
|
|
||||||
|
# 生产环境
|
||||||
|
CAMTALK_APP_ENV=prod \
|
||||||
|
CAMTALK_REDIS_ADDR=redis:6379 \
|
||||||
|
CAMTALK_AI_LLM_API_KEY=sk-xxx \
|
||||||
|
CAMTALK_AI_STT_API_KEY=xxx \
|
||||||
|
CAMTALK_AI_TTS_API_KEY=xxx \
|
||||||
|
CAMTALK_STORAGE_DRIVER=postgres \
|
||||||
|
CAMTALK_STORAGE_DSN="postgres://user:pass@db:5432/camtalk?sslmode=disable" \
|
||||||
|
CAMTALK_LOG_LEVEL=warn \
|
||||||
|
CAMTALK_LOG_FORMAT=json \
|
||||||
|
./bin/camtalk
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、数据模型
|
||||||
|
|
||||||
> 注:Go 和 TypeScript 的数据模型定义见下方。AI 服务层的 Go 模型见上方"AI 服务层接口"章节。
|
> 注:Go 和 TypeScript 的数据模型定义见下方。AI 服务层的 Go 模型见上方"AI 服务层接口"章节。
|
||||||
|
|
||||||
@@ -751,7 +980,7 @@ type ClientMessage =
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 七、扩展接口设计
|
## 八、扩展接口设计
|
||||||
|
|
||||||
通过 Repository 接口隔离存储层,MVP 用内存实现,后续替换为数据库——业务逻辑零改动。
|
通过 Repository 接口隔离存储层,MVP 用内存实现,后续替换为数据库——业务逻辑零改动。
|
||||||
|
|
||||||
@@ -827,7 +1056,7 @@ func NewApp(cfg *Config) *App {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 八、错误码
|
## 九、错误码
|
||||||
|
|
||||||
| 错误码 | 含义 | 客户端处理建议 |
|
| 错误码 | 含义 | 客户端处理建议 |
|
||||||
|--------|------|--------------|
|
|--------|------|--------------|
|
||||||
@@ -842,7 +1071,7 @@ func NewApp(cfg *Config) *App {
|
|||||||
| `TTS_ERROR` | 语音合成失败 | 静默回退到纯文本回复 |
|
| `TTS_ERROR` | 语音合成失败 | 静默回退到纯文本回复 |
|
||||||
| `INTERNAL_ERROR` | 服务端内部错误 | 提示用户重试 |
|
| `INTERNAL_ERROR` | 服务端内部错误 | 提示用户重试 |
|
||||||
|
|
||||||
## 九、连接管理
|
## 十、连接管理
|
||||||
|
|
||||||
**心跳机制**:客户端每 30 秒发送 `ping`,服务端回复 `pong`。超过 60 秒无 `ping`,服务端判定连接断开并清理会话资源。
|
**心跳机制**:客户端每 30 秒发送 `ping`,服务端回复 `pong`。超过 60 秒无 `ping`,服务端判定连接断开并清理会话资源。
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ CamTalk 是一款多模态实时 AI 视觉对话助手。用户通过摄像头
|
|||||||
|------|------|
|
|------|------|
|
||||||
| [01-项目概述](01-项目概述.md) | 项目目标、核心挑战、交付物 |
|
| [01-项目概述](01-项目概述.md) | 项目目标、核心挑战、交付物 |
|
||||||
| [02-系统架构](02-系统架构.md) | 三层架构、技术栈、核心交互流程、前后端模块、存储策略、部署架构 |
|
| [02-系统架构](02-系统架构.md) | 三层架构、技术栈、核心交互流程、前后端模块、存储策略、部署架构 |
|
||||||
| [03-接口文档](03-接口文档.md) | WebSocket 协议、REST API、**AI 服务层接口**、**编排器设计**、**Session Manager(Redis)**、数据模型、错误码、连接管理(**实现时首先阅读**) |
|
| [03-接口文档](03-接口文档.md) | WebSocket 协议、REST API、**AI 服务层接口**、**编排器设计**、**Session Manager**、**配置管理(Viper)**、数据模型、错误码、连接管理(**实现时首先阅读**) |
|
||||||
| [04-技术选型](04-技术选型.md) | 持久化层(PostgreSQL)和前端边缘处理层的选型对比与决策理由 |
|
| [04-技术选型](04-技术选型.md) | 持久化层(PostgreSQL)和前端边缘处理层的选型对比与决策理由 |
|
||||||
| [05-用户故事](05-用户故事.md) | P0/P1/P2 用户故事、验收标准、优先级决策依据 |
|
| [05-用户故事](05-用户故事.md) | P0/P1/P2 用户故事、验收标准、优先级决策依据 |
|
||||||
| [06-语音交互](06-语音交互.md) | VAD → STT → LLM → TTS 全链路、延迟优化 |
|
| [06-语音交互](06-语音交互.md) | VAD → STT → LLM → TTS 全链路、延迟优化 |
|
||||||
|
|||||||
Reference in New Issue
Block a user