# Backend 后端指引 (v0.2.0)

本文件适用于 backend/ 下的后端实现。进入 backend/tests/ 后，以子目录 AGENTS.md 为准。

## 后端职责

- 对外提供补全、取消补全、OCR、文档转换和 TTS/ASR 相关接口。
- 组织 Prompt，上下文清洗，调用 OpenAI-compatible 模型接口。
- **通过 Redis Streams 异步任务队列处理各类作业（completion/PRO/web_search/compress/OCR/convert/TTS/ASR）。**
- 负责 API Key 校验、日志记录和队列任务路由。

## 先看哪里

- API 入口和路由：main.py
- **任务队列系统**：job_system.py（Redis Streams 异步任务管理）
- **Worker 进程入口**：worker.py（消费 Redis Streams 的独立 worker）
- **任务处理器注册表**：job_handlers.py（completion/PRO/web_search/compress/OCR/convert/TTS/ASR 处理器）
- **会话管理**：session_store.py（内存 + PostgreSQL 双后端）
- **API/LLM 审计日志**：audit_store.py（PostgreSQL 持久化）
- **风控配置**：risk_config.py（环境变量驱动的配置数据类）
- **风控引擎**：risk_control.py（速率限制、并发控制、熔断器、预算追踪）
- **验证码路由**：captcha_api.py（FastAPI router）
- **文档存储**：docs_store.py（MIME 类型检测、文本/二进制分类）
- **LLM 策略解析**：llm_policy.py（按 job_type 解析模型配置）
- Ollama 调用封装：llm.py
- Prompt 清洗和拼装：prompt.py
- 数据模型：models.py
- 地理位置：geoip.py
- TTS 路由：tts_asr.py
- Prompt 模板：prompts/
- 后端测试：tests/

## 当前接口面

- POST /v1/completions
- POST /v1/completions/cancel
- POST /v1/ocr
- POST /v1/convert
- **POST /v1/web-search** - 网页搜索（SearXNG + Firecrawl）
- **POST /v1/compress** - 文本压缩（用于 OCR/文档转换后的大段文本）
- **POST /captcha/generate** - 验证码生成
- **POST /captcha/verify** - 验证码验证
- /v1/tts-asr/* 由 tts_asr.py 延迟注册

## 请求流转

### /v1/completions

- 读取或生成 request_id。
- privacy_mode 为 false 时，尝试根据客户端 IP 生成 location 文本。
- 调用 prepare_prompt_context 清洗 prefix 和 suffix。
- 调用 build_completion_prompts 生成 system_prompt 和 user_prompt。
- **通过 job_system.py 提交到 Redis Streams 队列，由 worker.py 消费。**
- **成功时返回 JSON：content 和 request_id。**

### /v1/completions/cancel

- 通过 request_id 在 ACTIVE_COMPLETIONS 中查找任务。
- 未找到返回 not_found。
- 已完成返回 already_done。
- 仍在执行则调用 task.cancel() 并返回 ok。

### /v1/ocr

- 把 base64 媒体内容解码成字节。
- 支持 `media_type=image|video` 和 `mime_type`。
- 图片直接调用 call_vlm_ocr。
- 视频先把整段视频送入 OCR 模型，再用 ffmpeg 抽取音轨交给 ASR，最后合并文本。
- **结果通过 job_handlers.py ocr_handler 处理。**
- 返回识别文本、原始文件名，以及视频场景下的 `ocr_text` / `asr_text`。

### /v1/convert

- 接收 base64 文件内容和文件名。
- 当前允许的扩展名只有 txt、docx、pptx、pdf。
- txt 直接解码后清洗。
- 其他格式写入临时文件，用 MarkItDown 转换，再做 Markdown 清洗。
- **结果通过 job_handlers.py convert_handler 处理。**
- 清洗逻辑会移除图片 Markdown 和 img HTML 标签，并压缩多余空行。

### /v1/web-search (新增)

- 接收搜索查询词和可选的引擎列表。
- **通过 job_handlers.py web_search_handler 处理。**
- **调用 SearXNG 搜索 API，再通过 Firecrawl 抓取页面内容。**
- **返回结构化的搜索结果（标题、链接、摘要）。**

### /v1/compress (新增)

- 接收长文本（OCR 结果、文档转换结果等）。
- **通过 job_handlers.py compress_handler 处理。**
- **调用 LLM 进行文本压缩，保留关键信息，移除冗余内容。**
- **返回压缩后的文本和压缩率统计。**

### /captcha/generate (新增)

- 生成随机验证码图片（4-6 位字母数字组合）。
- **通过 captcha_api.py captcha_generate_handler 处理。**
- **返回 base64 编码的验证码图片和明文答案。**

### /captcha/verify (新增)

- 验证用户输入的验证码是否正确。
- **通过 captcha_api.py captcha_verify_handler 处理。**
- **比对用户输入与存储的验证码答案，返回验证结果和错误信息（如有）。**

### /v1/tts-asr/*

- 通过 _register_tts_asr_routes 延迟导入并挂到主应用。
- **TTS 请求通过 job_handlers.py tts_handler 处理。**
- **ASR 请求通过 job_handlers.py asr_handler 处理。**
- **当前实现统一通过 `LLM_BASE_URL` + `LLM_API_KEY` 调用共享 Speech API，默认模型为 `Qwen3-TTS-12Hz-1.7B-VoiceDesign-8bit` 和 `Qwen3-ASR-0.6B-8bit`。**

## 开发命令

- 安装依赖：pip install -r backend/requirements.txt
- **Docker 部署**：docker compose up -d --build（在 /Users/allenyuan/lit/llm-in-text/ 目录下执行）
- 启动：python backend/main.py
- **Worker 进程**：python backend/worker.py（独立运行，消费 Redis Streams）
- 开发启动：uvicorn backend.main:app --reload --port 8001
- 路由相关测试：
	- pytest backend/tests/test_main_endpoints.py -v
	- pytest backend/tests/test_main_cancel.py -v
- Prompt 测试：
	- pytest backend/tests/test_prompt.py -v
	- pytest backend/tests/test_prompt_extended.py -v
- LLM 测试：
	- pytest backend/tests/test_llm.py -v
	- pytest backend/tests/test_llm_extended.py -v
- **新增测试**：
	- pytest backend/tests/test_web_search.py -v（网页搜索功能）
	- pytest backend/tests/test_compress.py -v（文本压缩功能）

## 编码约定

- Python 使用 4 空格缩进。
- 函数、变量使用 snake_case，类使用 PascalCase。
- **新逻辑优先保留显式类型和明确的输入输出。**
- **异步边界要清晰；阻塞操作优先放进 asyncio.to_thread，而不是直接阻塞事件循环。**
- **异常要么转成 HTTPException，要么转成结构化 JSONResponse；不要静默吞掉后端错误。**
- **日志尽量带 request_id 或短 tag，便于把前后端一次请求串起来。**

## 容易误判的点

- **任务队列架构（v0.2.0 新增）**：后端从同步端点转向 Redis Streams 异步队列。job_system.py 定义 JOB_TYPES，worker.py 消费队列，job_handlers.py 注册各类型处理器。
- **会话追踪**：session_store.py 提供 InMemorySessionStore（开发）和 PostgresSessionStore（生产），通过 session_hash + ip_hash 追踪请求身份。
- **风控系统**：risk_config.py + risk_control.py 实现速率限制（滑动窗口）、并发控制、熔断器模式和预算追踪，所有阈值通过环境变量配置。
- **验证码路由**：captcha_api.py 提供 /captcha/generate 和 /captcha/verify 端点，用于前端验证用户输入。
- **文档存储**：docs_store.py 提供 MIME 类型检测、文本/二进制分类和预览提取（8MB 限制）。
- **LLM 策略解析**：llm_policy.py 按 job_type 解析模型配置（模型名、温度、最大 token、思考级别）。
- **OCR 明确关闭思考**：llm.py 的 `call_vlm_ocr` 对 OCR 请求显式设置 `options.think = False` 和 `temperature = 0`。
- **补全接口当前不是流式响应，不要按 SSE 方式改造周边代码。**
- **ACTIVE_COMPLETIONS 在补全和取消路径里都被读写，任务生命周期要谨慎处理。**
- **`/v1/export/pdf` 不是当前主链路**；DOCX/PDF 导出已经转到前端 `src/utils/richExport.js`。
- **API_KEY 存在占位默认值，这更像本地开发兜底，不是推荐的安全模式。**
- **历史 TTS/ASR 文档和部分测试覆盖的是旧实现；代码与文档冲突时，先确认产品方向，再决定修代码还是修文档。**

## 改动时的定位建议

- **如果问题是补全结果不对，先查 prompt.py，再查 llm.py，不要只盯着 main.py。**
- **如果问题是取消不生效，先查 main.py 里的 request_id 生命周期，再对照前端的 X-Request-Id 和 cancel 调用。**
- **如果问题是 OCR 识别为空，先看 main.py 的 base64 解码，再看 llm.py 的 call_vlm_ocr。**
- **如果问题是转换结果脏，重点看 main.py 里的 _sanitize_converted_markdown。**
- **如果问题是 TTS 行为和文档不一致，以 tts_asr.py 为准，不要以 README 为准。**
- **如果问题是 Web Search 失败，检查 SearXNG 和 Firecrawl 服务状态（docker compose ps）。**
- **如果问题是任务队列积压，检查 worker.py 日志和 Redis Streams 长度（INFO keys=stream）。**
- **如果问题是会话丢失，检查 session_store.py 的 InMemory/Postgres 后端切换逻辑。**
- **如果问题是风控触发，检查 risk_config.py 的环境变量配置和风险阈值。**

## 测试映射

- **路由主行为**：tests/test_main_endpoints.py
- **取消逻辑**：tests/test_main_cancel.py
- **Prompt 逻辑**：tests/test_prompt.py、tests/test_prompt_extended.py
- **LLM 包装层**：tests/test_llm.py、tests/test_llm_extended.py
- **GeoIP**：tests/test_geoip.py
- **TTS 相关**：tests/test_tts_asr_*.py
- **网页搜索**：tests/test_web_search.py（新增）
- **文本压缩**：tests/test_compress.py（新增）

## 文档使用原则

- README.md、TTS_ASR_MACOS_FIX.md、tests/TESTING_GUIDE.md 可以作为背景材料。
- **一旦这些文档和 main.py、llm.py、prompt.py、tts_asr.py 冲突，以代码为准。**
- **新增模块（job_system、worker、job_handlers、session_store、audit_store、risk_config、risk_control、captcha_api、docs_store、llm_policy）的文档需与代码保持同步。**
- **Docker 部署相关文档（docker-compose.yml、backend/Dockerfile、Dockerfile.frontend、.dockerignore）需与 docker/ 目录下的配置保持一致。**
