DEEIX Chat 后端是 Go API 服务,负责认证、用户、对话、模型渠道、模型能力、文件处理、MCP 工具、官方原生工具、记忆、计费、支付、系统设置、审计日志与可观测性等核心业务。
- Go 1.26
- Gin
- Gorm
- PostgreSQL + pgvector 或 SQLite + sqlite-vec
- Redis 或进程内 memory cache
- Swagger (
swag) - S3 兼容对象存储(可选)
- OpenTelemetry Trace(可选)
- MCP Streamable HTTP JSON-RPC(可选)
docs/README.md:后端文档索引docs/swagger.json/docs/swagger.yaml:Swagger API 文档
- 启动链路为
cmd -> internal/cli -> internal/app。 - Handler 只负责 HTTP 入参、鉴权上下文、响应转换,不写业务逻辑。
- Application 层承载用例编排,不直接依赖 Gorm、Redis、Docker 等基础设施实现。
- Repository 接口位于
internal/repository,具体实现位于internal/infra/persistence。 - 共享基础设施位于
internal/infra,通用响应、请求元数据等位于internal/shared。 - HTTP DTO 和 Swagger annotation 是传输契约唯一事实源;Handler 在 HTTP 边界把 DTO 转换为 Application Input,不向领域层或基础设施层泄漏 Gin DTO。
- JSON、校验标签和指针类型必须准确表达必填、可选、可空以及显式
0/false;不要让前端修补错误的 Swagger 语义。 - 模型能力 JSON 是请求参数、可视化控件、官方原生工具和图像流式能力的后端事实源。
- 只为明确支持的公开契约保留兼容行为,不增加推测性的兼容 helper;破坏性 API 或数据变更必须说明迁移与兼容影响。
标准响应统一为 errorMsg + data:
{
"errorMsg": "",
"data": null
}分页响应统一放入 data:
{
"errorMsg": "",
"data": {
"total": 0,
"results": []
}
}所有标准接口通过 internal/shared/response 返回,不新增重复 response 包。
默认读取仓库根目录下的 config.yaml,常用配置也支持环境变量覆盖。从 backend/ 目录启动时会读取 ../config.yaml。
本地开发可先在仓库根目录复制示例配置;Docker 部署使用 Docker 示例配置:
cp config.example.yaml config.yaml
# Docker Compose full stack
cp config.full.example.yaml config.yaml
# SQLite + memory cache
cp config.sqlite.example.yaml config.yaml关键配置:
APP_ENV:运行环境,支持dev/development和prod/production;未配置时默认prodHTTP_PORT:HTTP 端口JWT_SECRET:JWT 签名密钥POSTGRES_DSN:PostgreSQL DSNREDIS_ADDR/REDIS_USERNAME/REDIS_PASSWORD/REDIS_DB/REDIS_TLS_ENABLED/REDIS_TLS_INSECURE_SKIP_VERIFY:Redis 连接配置;REDIS_TLS_INSECURE_SKIP_VERIFY会跳过证书校验,除非非标准 TLS 端点要求,否则保持关闭STORAGE_BACKEND:local或s3GEOIP_PROVIDER:ipwhois、ipinfo、mmdb或noneGEOIP_DATABASE_URL/GEOIP_DATABASE_PATH:MMDB 数据库下载地址与本地缓存路径OTEL_ENABLED:是否启用 OpenTelemetry Trace;未设置时,配置了 OTLP Endpoint 会自动启用OTEL_EXPORTER_OTLP_ENDPOINT:OTLP Collector 地址OTEL_EXPORTER_OTLP_HEADERS:OTLP 请求头,格式为key=value,key2=value2OTEL_EXPORTER_OTLP_INSECURE:是否使用明文传输OTEL_EXPORTER_OTLP_PROTOCOL:OTLP exporter 协议,支持grpc、http、http/protobuf,默认grpcOTEL_TRACES_SAMPLER_ARG/OTEL_SAMPLING_RATE:Trace 采样率,范围0~1
对应 YAML:
observability:
tracing:
# 未配置 enabled 时,endpoint 非空会自动启用 Trace。
# enabled 为 true 时 endpoint 必填;enabled 为 false 时强制关闭。
# enabled: true
endpoint: "http://127.0.0.1:4317"
headers: ""
insecure: true
protocol: grpc
sampling_rate: 1config.yaml 是静态基础设施配置入口,环境变量优先级高于 YAML。未显式配置 enabled 时,endpoint 非空会自动启用 Trace;显式配置 enabled: true 时,endpoint 必填。运行时业务设置由数据库 settings 覆盖,不把 OpenTelemetry collector、header/token 等部署层配置放入后台管理。
初始化超级管理员用户名为 admin。当数据库中没有超级管理员时,后端会生成随机密码并只在首次创建账号的启动日志中输出一次,日志关键字为 bootstrap superadmin created。首次登录会强制修改用户名和密码;后续账号变更不通过 config.yaml。
APP_ENV 未配置时默认 prod。dev/development 只用于本地开发;公网生产部署应保持 APP_ENV=prod 或 APP_ENV=production 并使用生产密钥。
邮箱注册可选启用 Cloudflare Turnstile 人机验证,作用范围仅限邮箱注册;OAuth/OIDC 登录或注册不需要 Turnstile 校验。
相关运行时设置:
auth:turnstile_registration_enabled:是否在邮箱注册时启用 Turnstile。auth:turnstile_site_key:前端渲染 Turnstile 组件使用的 Site Key,会通过/api/v1/auth/login-options返回。auth:turnstile_secret_key:后端调用 Cloudflare siteverify 使用的 Secret Key,属于敏感设置。TURNSTILE_SITEVERIFY_URL/security.turnstile_siteverify_url:可选覆盖 siteverify 端点,默认使用 Cloudflare 官方地址。
启用 Turnstile 需要同时启用 auth:email_registration_enabled,并配置 Site Key 与 Secret Key。开启邮箱验证码注册时,前端在 /api/v1/auth/register/email/start 提交 turnstileToken;关闭邮箱验证码但允许邮箱注册时,前端在 /api/v1/auth/register/email/complete 提交 turnstileToken。
Web、App 和桌面端统一通过当前实例完成第三方 OAuth 回调。部署必须提供外部可访问的 PUBLIC_API_BASE_URL,身份源回调格式为:
<PUBLIC_API_BASE_URL>/api/v1/auth/providers/<provider-slug>/callback
POST /auth/providers/:slug/authorize 创建短时事务并使用服务端独立 PKCE 访问上游;GET /auth/providers/:slug/callback 在服务端兑换上游授权码;POST /auth/providers/:slug/exchange 使用公共客户端 PKCE verifier 原子兑换一次性 DEEIX grant。事务与 grant 使用现有 Redis/内存缓存后端,外部 provider code、Client Secret 和 Token 均不会进入公共客户端。旧 /start 与 POST /callback 流程继续保留,用于账号身份绑定与旧版 Web 客户端兼容。
生产环境安全校验:
APP_ENV支持dev/development和prod/production,其他值会启动失败。APP_ENV=prod时,JWT_SECRET不能为空、不能过短、不能使用默认开发值。APP_ENV=prod时,DATA_ENCRYPTION_KEY不能为空、不能过短、不能使用默认开发值。APP_ENV=prod时,CORS_ALLOW_ORIGIN不能为空或*,PUBLIC_API_BASE_URL/PUBLIC_WEB_BASE_URL必须是 HTTPS。
Stripe Webhook 使用公开 API 地址:
https://api.example.com/api/v1/billing/payments/stripe/webhook
在 Stripe Dashboard 中监听 checkout.session.completed,并把生成的 whsec_... 填入后台「计费 / 支付配置 / Stripe Webhook Secret」。
先确保 PostgreSQL 和 Redis 可用。若本机已有依赖,可以只启动默认应用容器;若需要完整本地栈,使用 docker-compose.full.yml:
docker compose up -ddocker compose -f docker-compose.full.yml up -d启动后端:
cd backend
make runSwagger UI:
http://localhost:8080/swagger/index.html
默认本地存储:
storage:
backend: local
local:
root_dir: ./storageS3 兼容对象存储:
storage:
backend: s3
s3:
endpoint: ""
region: auto
bucket: ""
prefix: ""
access_key_id: ""
secret_access_key: ""
force_path_style: trueR2、OSS、MinIO、AWS S3 等统一走 S3 兼容协议,不为不同厂商维护重复实现。
默认使用 HTTP GeoIP 服务:
geoip:
provider: ipwhois生产环境如果希望降低外部依赖并提升审计稳定性,可改用本地 MMDB 数据库:
geoip:
provider: mmdb
database_url: "https://example.com/geoip.mmdb"
database_path: "./data/geoip/geoip.mmdb"
database_max_bytes: 104857600
refresh_interval_hours: 168
timeout_ms: 2500启用 provider: mmdb 时,启动会优先加载本地文件;本地文件不存在或过期时,根据 database_url 下载并校验新数据库。刷新成功后热切换内存中的 reader,刷新失败则保留上一份可用数据库。
文件链路支持三类上下文策略:
- 图片:默认按模型能力直接传原图上下文;开启图片 OCR 后进入 OCR 文本提取链路。
- 文本类文件:小文件可全文注入;超出阈值时按配置走 RAG 或回退策略。
- PDF/Office 等文档:通过内置提取、Tika、Docling、MinerU 或 OCR 引擎提取文本;PDF OCR 回退可单独控制。
MinerU 可在设置中选择处理的文件类型;云端 MinerU 支持 .doc/.docx/.ppt/.pptx/.xls/.xlsx,自部署 MinerU 支持 .docx/.pptx/.xlsx。
OCR 引擎配置由后台文件设置管理,当前支持 RapidOCR、Tesseract OCR、Paddle OCR、腾讯云 OCR、阿里云 OCR、Mistral OCR 与 LLM OCR。服务地址、鉴权密钥和超时时间按具体引擎配置。
用户文件存储配额由运行时设置 storage:user_storage_quota_bytes 管理。后台 /admin/chat-files 页面中的 storage:max_upload_file_bytes、storage:user_storage_quota_bytes、file:image_max_bytes、file:doc_max_bytes 和 file:file_full_context_max_bytes 统一按 MB 输入,设置值在 API、数据库和运行时内部统一按字节保存与计算;值为 0 表示不限制。非零时,上传、分享克隆和文件复用链路都会按用户维度校验并同步最新配额。前端 /files 页支持单个删除和批量删除,后端会在删除后释放对应配额。
模型能力 JSON 支持:
defaultOptions:写入用户侧默认参数 JSON,并作为请求参数来源。lockedOptionPaths:声明不可由用户覆盖的参数路径;对应值仍从defaultOptions读取,后端发送前会恢复为管理员默认值。optionControls:定义用户参数配置对话框的可视化控件,不会单独传给上游。nativeToolKeys:定义当前模型允许的厂商官方原生工具,例如 OpenAI、xAI、Google 和 Anthropic 的原生搜索、代码执行或图片生成能力。image.stream:仅对图像类模型能力生效;未配置时保持默认流式,显式写false时关闭图像流式调用。
用户手写 tools 时,只有命中 nativeToolKeys 的官方原生工具会作为官方工具保留,工具子参数会随该工具透传;普通用户不能通过 JSON 自行启用未被管理员允许的 MCP Tool 或官方原生工具。MCP Tool 仍必须由管理员在工具页配置和启用。
上游和路由的附加请求头支持动态变量模板。变量仅在真实会话生成请求中展开;模型列表、渠道探测、标题与标签生成、上下文压缩和媒体任务不会携带这些标识。
${DEEIX_CONVERSATION_ID}:公开会话 ID,适合自建网关按会话关联日志、缓存和长期记忆。${DEEIX_SESSION_ID}:会话上下文键,适合需要独立会话亲和键的自建网关。${DEEIX_REQUEST_ID}:DEEIX 当前请求 ID,同一轮生成、工具调用和路由重试保持一致,适合关联完整请求链路。${DEEIX_UPSTREAM_REQUEST_ID}:每次上游 HTTP 请求单独生成的 UUID,适合要求请求级唯一标识的 Provider。
功能默认关闭。管理员可在目标上游或路由的附加请求头中显式配置;未配置的官方 Provider 不会收到额外动态标识:
{
"X-Conversation-Id": "${DEEIX_CONVERSATION_ID}",
"X-Session-Id": "${DEEIX_SESSION_ID}",
"X-Request-Id": "${DEEIX_REQUEST_ID}"
}OpenAI 的 X-Client-Request-Id 要求每次请求使用唯一值,应配置为 ${DEEIX_UPSTREAM_REQUEST_ID},不要使用稳定的会话或链路标识。
部分 Codex 兼容中转站会把 Codex CLI 使用的会话头作为账号或缓存分片的亲和键。仅在中转站明确支持该约定时,可在目标路由(不要在官方 OpenAI 上游)配置:
{
"session-id": "${DEEIX_SESSION_ID}",
"thread-id": "${DEEIX_CONVERSATION_ID}",
"x-client-request-id": "${DEEIX_CONVERSATION_ID}"
}其中 session-id 与 DEEIX 发送的 prompt_cache_key 使用同一个会话上下文键。该配置只提供中转站亲和提示,不能替代 promptCache 能力声明,也不应作为所有 OpenAI 兼容上游的全局默认值。
官方 OpenAI Responses 与 Chat Completions 请求会使用同一会话上下文键作为服务端受控的 prompt_cache_key,以保持跨轮缓存亲和;未启用显式模式时仍使用 OpenAI 默认的 implicit breakpoint 行为。兼容中转站默认不接收 OpenAI Prompt Cache 新字段;确认中转站支持后,需要在模型能力 JSON 中显式声明:
{
"promptCache": {
"enabled": true
}
}官方 OpenAI 也可以用 promptCache.enabled=false 显式关闭。缓存策略完全由模型能力配置控制,用户消息请求中的同名 Options 会被忽略。启用显式缓存时,官方 OpenAI 默认发送消息块断点;兼容中转站必须再声明 messageBreakpoints=true 才会收到 prompt_cache_breakpoint。DEEIX 会把最后一条非空的前导 system 消息和每条非空历史 user 消息保留为断点;本轮 user、动态 RAG、本轮图片及其他当前轮上下文始终不标记。旧轮次断点必须继续保留,避免删除 marker 后改写后续缓存前缀;正常连续对话每轮只为上一轮 user 新增一个断点。OpenAI 每个请求最多创建 4 个新写入,并从最近 50 个对话断点中读取最长匹配前缀。该历史策略只作用于 explicit 模式,不改变仅使用稳定 prompt_cache_key 的 implicit 行为:
{
"promptCache": {
"enabled": true,
"mode": "explicit",
"ttl": "30m",
"messageBreakpoints": true
}
}若中转站接受顶层 prompt_cache_options,但拒绝消息内容中的 prompt_cache_breakpoint,省略 messageBreakpoints 或将其设为 false。此时 DEEIX 仍发送稳定的 prompt_cache_key 和显式缓存选项,由中转站选择缓存边界。
隐式缓存可独立配置保留策略:
{
"promptCache": {
"enabled": true,
"mode": "implicit",
"retention": "24h"
}
}显式缓存当前只接受 ttl=30m;隐式缓存的 retention 接受 in_memory 或 24h,两者语义不互相替代。已有模型中的 defaultOptions.prompt_cache_retention 配置仍会生效。未声明能力的兼容中转站不会收到 prompt_cache_key、prompt_cache_options、prompt_cache_retention 或 prompt_cache_breakpoint。DEEIX 不再依赖上游错误文本执行无记忆缓存重试。
MCP 能力由后台工具设置管理:
- 后台配置 MCP Server,服务地址必填,可配置鉴权密钥与请求头。
- Server 创建后默认启用,可同步远端 MCP Tool。
- 工具可单独启停;用户在聊天输入区选择可用工具。
- 单次 run 支持最大 LLM 调用轮数、最大工具调用次数、并发数、超时和失败重试配置。
- 工具调用结果会进入消息处理轨迹,前端与“处理链路 / 思考链路”并列展示工具链路。
计费侧把一次用户触发的多轮 LLM + 工具调用视为一次 run 汇总统计。
官方原生工具按上游返回的调用次数生成独立服务项;是否计费和每次调用价格由管理员在计费设置中统一配置,价格填 0 表示不单独计费。工具返回内容产生的模型 token 仍按模型定价计算。
GET /api/v1/version 是公开接口,返回当前版本、提交、构建时间和 buildID,用于前端定期检测新部署并提示用户刷新。该接口设置为 no-store,避免被 CDN 或浏览器长期缓存。
后端日志保持 JSON 外层字段克制,业务上下文集中写入 msg,/healthz 不输出访问日志。生产环境 Gorm record not found 不作为错误日志输出。
OpenTelemetry Trace 为可选能力,当前覆盖:
- Gin HTTP 请求,跳过
/healthz - PostgreSQL Gorm callback
- Redis 命令
- S3 Put/Open/Delete,其中 Open 覆盖完整 reader 生命周期
- 出站 HTTP:LLM、MCP、Embedding、OAuth/OIDC、GeoIP、文件提取/OCR
- 会话关键路径:发送、RAG 检索、LLM 生成、工具执行、持久化
Trace 不记录 prompt、文件内容、工具参数、API Key 或鉴权密钥。
Apache Tika:
docker compose -f ../docker/tika/docker-compose.yml up -dTesseract OCR:
docker compose -f ../docker/tesseract/docker-compose.yml up -d --buildDocling:
docker compose -f ../docker/docling/docker-compose.yml up -d --buildRapidOCR:
docker build -t deeix-chat-rapidocr ../docker/rapidocr这些服务默认使用 deeix-chat-network。可先执行 docker network create deeix-chat-network,或先启动一次根目录 compose 创建基础网络。
make run
make fmt
make test
make swagger
go build ./cmd/server
go vet ./...
go mod tidy接口或 DTO 变更后必须执行:
make swagger该命令会调用根工作区的 pnpm api:generate,使用 backend/go.mod 中锁定的 swag 版本,并同时更新:
backend/docs/docs.gobackend/docs/swagger.jsonbackend/docs/swagger.yamlpackages/api-contract/src/types.generated.ts
这些文件全部由生成器维护,不允许手工修改。仅检查漂移时,在仓库根目录运行 pnpm api:check。
go build ./cmd/server
go test ./...
go vet ./...
cd ..
pnpm api:check