Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Backend

本目录是“基于大模型的私有知识库问答系统”后端当前后端实现。

当前已落地

  • cmd/api: API 入口
  • cmd/worker: Worker 入口
  • internal/platform: 配置、数据库连接、HTTP 响应、中间件、鉴权、本地文件存储
  • internal/account: 注册、登录、刷新令牌、退出登录、当前用户信息、修改密码
  • internal/chat: 会话创建、列表、详情、删除;普通聊天与知识库 RAG SSE 聊天
  • internal/kb: 知识库 CRUD、文档上传、文档列表/详情/删除、重建索引任务创建
  • internal/task: 统一任务模型、任务创建与状态推进
  • internal/worker: 异步任务消费执行器
  • internal/admin: 管理端路由占位
  • migrations: Goose 迁移 SQL

启动前准备

  1. 复制环境变量模板
cp backend/.env.example backend/.env

说明:

  • 默认 DATABASE_DSN 已经和 compose.dev.yaml 里的 PostgreSQL 配置对齐
  • 当前默认宿主机端口为 55432
  • 如果你不改端口、用户名、密码和数据库名,那么可以直接使用默认值
  • 现在 API/Worker 会自动读取 backend/.envbackend/.env.local,不需要手动 source .env
  1. 至少确认以下配置
  • DATABASE_DSN
  • AUTH_JWT_SECRET
  • DEEPSEEK_API_KEY
  • AI_EMBEDDING_PROVIDER
  • STORAGE_PROVIDER
  • STORAGE_BUCKET
  • STORAGE_LOCAL_ROOT

embedding 相关说明:

  • 默认 AI_EMBEDDING_PROVIDER=local_hash,不依赖额外远程服务,适合本地开发和课程演示
  • 如果你有可用的 OpenAI 兼容 embedding 服务,可改成:
    • AI_EMBEDDING_PROVIDER=openai_compatible
    • AI_EMBEDDING_BASE_URL=<你的 embedding base url>
    • AI_EMBEDDING_API_KEY=<你的 embedding key>
  • 当前聊天仍然使用 DeepSeek Chat Completions;embedding provider 与聊天 provider 可以分开配置
  1. 准备 PostgreSQL 15+,推荐直接用仓库内的开发容器

Podman Compose:

cd backend
podman-compose -f compose.dev.yaml up -d

Docker Compose:

cd backend
docker compose -f compose.dev.yaml up -d

说明:

  • 容器镜像已内置 pgvector
  • migration 会负责创建 pgcryptovector 扩展
  • 默认上传文件会保存在 backend/data/storage,目录会自动创建
  1. 执行数据库迁移
cd backend
./scripts/migrate-up.sh

如果你更习惯 make

cd backend
make db-up
make migrate-up

本地运行

API:

cd backend
./scripts/run-api.sh

Worker:

cd backend
./scripts/run-worker.sh

如果你更习惯 make

cd backend
make run-api
make run-worker

测试

cd backend
GOCACHE=/tmp/go-build go test ./...

当前 AI 聊天能力

  • 聊天接口:POST /api/v1/sessions/{sessionId}/messages
  • 返回类型:text/event-stream
  • 当前已接入 provider:DeepSeek
  • 当前支持场景:
    • 会话未绑定知识库时的通用聊天
    • 会话绑定知识库时的 RAG 聊天
    • 历史消息自动截取最近若干条拼接到上下文
    • 命中知识库时基于 pgvector 检索 chunk,assistant 消息会保存引用
    • 未命中知识库时自动回退为通用回答,并在 SSE meta.grounded=false 中标记
    • 用户消息和 assistant 消息落库
    • 已支持 POST /sessions/{sessionId}/messages/{messageId}/regenerate
    • 已支持 POST /sessions/{sessionId}/stream/stop
  • 当前限制:
    • regenerate 采用覆盖式更新,不保留回答版本历史
    • stream/stop 会终止当前会话正在生成的流;若成功中断,不会把不完整答案落库

建议至少补齐以下环境变量:

  • DEEPSEEK_API_KEY
  • AI_DEFAULT_CHAT_MODEL
  • AI_CHAT_TIMEOUT
  • AI_MAX_HISTORY_MESSAGES
  • AI_SSE_HEARTBEAT_INTERVAL
  • AI_EMBEDDING_PROVIDER
  • AI_RAG_MAX_CONTEXT_CHUNKS

最小联调示例

下面示例展示“注册 -> 登录 -> 创建普通会话 -> 发起流式聊天”的最短链路。

  1. 注册
curl -X POST http://127.0.0.1:8080/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "username": "demo_user",
    "email": "[email protected]",
    "password": "StrongPass123!"
  }'
  1. 登录并保存 access_token
curl -X POST http://127.0.0.1:8080/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "account": "demo_user",
    "password": "StrongPass123!"
  }'

登录成功后,从返回 JSON 的 data.access_token 中取出令牌。

  1. 创建一个不绑定知识库的普通会话
curl -X POST http://127.0.0.1:8080/api/v1/sessions \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "DeepSeek Curl Smoke",
    "model": "deepseek-chat"
  }'

创建成功后,从返回 JSON 的 data.session_id 中取出会话 ID。

  1. 发起 SSE 聊天
curl -N -X POST http://127.0.0.1:8080/api/v1/sessions/<session_id>/messages \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "你好,请用三句话做一个简短的自我介绍。"
  }'

预期返回示例:

event: meta
data: {"message_id":"uuid","grounded":false,"model":"deepseek-chat"}

event: delta
data: {"content":"你好"}

event: delta
data: {"content":"!我是 DeepSeek ..."}

event: done
data: {"finish_reason":"stop"}
  1. 可选:回查会话详情,确认消息已经落库
curl http://127.0.0.1:8080/api/v1/sessions/<session_id> \
  -H "Authorization: Bearer <access_token>"

知识库 RAG 联调要点

  • 若要测试知识库会话下的 RAG 聊天,最短顺序是:
    • 创建知识库
    • 上传 txt / md / docx
    • 等待 worker 将文档状态处理为 available
    • 创建带 knowledge_base_id 的会话
    • 调用 POST /api/v1/sessions/{sessionId}/messages
  • SSE 的 meta.grounded
    • true 表示命中了知识库上下文
    • false 表示知识库未命中,当前回答已回退为通用回答
  • 回查 GET /api/v1/sessions/{sessionId} 时,assistant 消息的 citations 字段会返回引用文档信息

重生成与停止生成

  • POST /api/v1/sessions/{sessionId}/messages/{messageId}/regenerate
    • 返回类型也是 text/event-stream
    • 会基于原用户问题重新检索并生成回答
    • meta.message_id 会等于原 assistant 消息 ID
    • 成功后会直接覆盖原 assistant 消息内容、token 用量和 citations
  • POST /api/v1/sessions/{sessionId}/stream/stop
    • 返回 JSON:{"code":0,"message":"ok","data":{"stopped":true|false}}
    • stopped=true 表示当前确实中断了一个活跃流
    • stopped=false 表示当前会话没有正在运行的流
  • 同一会话同一时刻只允许一个活跃生成任务
    • 若在已有流未结束时再次调用发送消息或重生成,会返回 409

当前文档上传能力

  • 上传接口:POST /api/v1/knowledge-bases/{kbId}/documents
  • 请求格式:multipart/form-data,字段名固定为 file
  • 上传大小上限默认 20 MiB,可通过 STORAGE_MAX_UPLOAD_BYTES 调整
  • 当前允许上传的 MIME / 扩展名:
    • text/plain / .txt
    • text/markdown / .md / .markdown
    • application/vnd.openxmlformats-officedocument.wordprocessingml.document / .docx
    • application/pdf / .pdf
  • 当前 worker 已实现:
    • document_ingest
    • knowledge_base_reindex
    • resource_cleanup
  • 当前 ingest 已实现的文本提取:
    • txt
    • markdown
    • docx
    • pdf(带可提取文本层的 PDF)
  • 当前 embedding 行为:
    • 默认使用本地 local_hash embedding,把文档 chunk 和查询统一映射到 1536 维向量,适合本地开发与演示
    • 若你配置了 AI_EMBEDDING_PROVIDER=openai_compatible,worker 和 RAG 查询都会改用远程 embedding 服务
  • 当前限制:
    • 扫描件或纯图片型 pdf 若不包含可提取文本层,worker 会将该文档任务标记为 failed
    • 当前仍未实现 OCR,因此这类 PDF 无法入库

下一阶段建议

  1. 为扫描件 PDF 接入 OCR
  2. 若有真实 embedding 服务,切换 AI_EMBEDDING_PROVIDER=openai_compatible
  3. 完成管理员的用户、任务、系统参数、配额和审计接口
  4. 继续补 quota / audit 统计落库