Commit 4ff8d595 authored by uuo00_n's avatar uuo00_n

docs: 添加项目文档和更新README

添加CHANGELOG.md记录项目变更历史
添加SPEAK.md技术演讲稿与实现指南
更新README.md添加项目徽章和许可证信息
parent 6a87f73b
# 更新日志(CHANGELOG)
本文件根据 `main` 分支的 git 提交历史整理(共 53 条)。当前仓库未打版本标签(tag),因此按日期归档;后续如引入版本号,可将日期段迁移为版本段。
仓库地址:https://github.com/uuo00n/LLM-filter
## 2025-12-28
### 新增
- Dify 智能体内容安全检查([`15c33bc`](https://github.com/uuo00n/LLM-filter/commit/15c33bc)
### 文档
- 更新项目文档结构和内容([`6a87f73`](https://github.com/uuo00n/LLM-filter/commit/6a87f73)
## 2025-12-27
### 新增
- 重构项目为微服务架构并添加教育服务模块([`6cf8357`](https://github.com/uuo00n/LLM-filter/commit/6cf8357)
- 教育服务实现仪表盘功能并完成迁移([`40f9fb5`](https://github.com/uuo00n/LLM-filter/commit/40f9fb5)
## 2025-12-14
### 文档
- 添加 LLM-Filter 技术白皮书初稿([`708db1b`](https://github.com/uuo00n/LLM-filter/commit/708db1b)
### 杂项
- 清理虚拟环境中的旧文件和依赖项([`96c5056`](https://github.com/uuo00n/LLM-filter/commit/96c5056)
## 2025-12-13
### 新增
- 学生周课表查询功能(仪表盘)([`e13c147`](https://github.com/uuo00n/LLM-filter/commit/e13c147)
- 教师周课表查询功能(仪表盘)([`7133e3a`](https://github.com/uuo00n/LLM-filter/commit/7133e3a)
### 修复
- 修复默认用户关联学生实体缺失导致 `/students/me` 404([`0d0589b`](https://github.com/uuo00n/LLM-filter/commit/0d0589b)
- 确保 `teacher_person_id``created_at` 的格式正确([`befb276`](https://github.com/uuo00n/LLM-filter/commit/befb276)
### 文档
- 更新 CHANGELOG 与 README 以反映最新更改([`f4b6195`](https://github.com/uuo00n/LLM-filter/commit/f4b6195)
### 杂项
- 更新 `.env` 中的数据库配置([`6d17b88`](https://github.com/uuo00n/LLM-filter/commit/6d17b88)
## 2025-12-08
### 新增
- 完善 API 文档与错误响应([`10e59f3`](https://github.com/uuo00n/LLM-filter/commit/10e59f3)
- 添加 `APP_BASE_URL` 配置并启动时输出文档链接([`2724a77`](https://github.com/uuo00n/LLM-filter/commit/2724a77)
### 修复
- 更新敏感词处理逻辑以兼容新旧版本([`7a1720e`](https://github.com/uuo00n/LLM-filter/commit/7a1720e)
### 重构
- 更新 Pydantic 模型配置以兼容 v2([`cbd3da7`](https://github.com/uuo00n/LLM-filter/commit/cbd3da7)
### 文档
- 更新 README 文档内容与格式([`4711122`](https://github.com/uuo00n/LLM-filter/commit/4711122)
- 添加项目更新日志文件记录版本变更([`76324c6`](https://github.com/uuo00n/LLM-filter/commit/76324c6)
### 杂项
- 清理过时文档文件([`2b45781`](https://github.com/uuo00n/LLM-filter/commit/2b45781)
- 清理 Python 编译缓存文件([`6290fb1`](https://github.com/uuo00n/LLM-filter/commit/6290fb1)
- 添加 Python 项目通用 `.gitignore`[`f014fc6`](https://github.com/uuo00n/LLM-filter/commit/f014fc6)
## 2025-12-04
### 新增
- 添加 CORS 与 GitHub 相关配置项([`adff7ae`](https://github.com/uuo00n/LLM-filter/commit/adff7ae)
## 2025-11-30
### 新增
- 对话:统一 ID 与标题、敏感词结构化返回、新增删除接口与 CORS 支持([`4c2e734`](https://github.com/uuo00n/LLM-filter/commit/4c2e734)
- 对话:列表与详情统一 ID/标题,新增删除对话服务([`52ef3e5`](https://github.com/uuo00n/LLM-filter/commit/52ef3e5)
- API:对话路由统一响应模型,新增删除接口;列表负载优化([`789fdcb`](https://github.com/uuo00n/LLM-filter/commit/789fdcb)
### 合并
- 合并 PR #13:conversation unify/title/delete/cors([`28ce2b7`](https://github.com/uuo00n/LLM-filter/commit/28ce2b7)
## 2025-11-19
### 文档
- 添加 Windows PowerShell 快速启动指南([`aea76f6`](https://github.com/uuo00n/LLM-filter/commit/aea76f6)
## 2025-11-18
### 修复
- 修复敏感词记录中用户ID与对话ID的类型转换问题([`b5ca224`](https://github.com/uuo00n/LLM-filter/commit/b5ca224)
### 重构
- 重构敏感词记录:包含详细敏感词信息([`b5ca224`](https://github.com/uuo00n/LLM-filter/commit/b5ca224)
- CORS:根据配置动态设置允许的源与凭证([`b5ca224`](https://github.com/uuo00n/LLM-filter/commit/b5ca224)
### 风格
- 移除不必要的注释与空行([`b5ca224`](https://github.com/uuo00n/LLM-filter/commit/b5ca224)
## 2025-11-17
### 新增
- 自定义 Swagger UI 使用 CDN 加载静态资源([`928e789`](https://github.com/uuo00n/LLM-filter/commit/928e789)
### 文档
- 更新 README 以提供更全面的系统文档([`59d1878`](https://github.com/uuo00n/LLM-filter/commit/59d1878)
### 杂项
- 更新数据库配置和依赖项([`a721cb0`](https://github.com/uuo00n/LLM-filter/commit/a721cb0)
## 2025-11-14
### 新增
- 添加学校业务数据初始化能力([`6bc71b9`](https://github.com/uuo00n/LLM-filter/commit/6bc71b9)
- 添加后端仪表盘功能模块([`65250db`](https://github.com/uuo00n/LLM-filter/commit/65250db)
- 实现用户与学生绑定功能及接口([`05ab73d`](https://github.com/uuo00n/LLM-filter/commit/05ab73d)
- 重构用户与实体模型:账户与人物分离([`5dbcb42`](https://github.com/uuo00n/LLM-filter/commit/5dbcb42)
- 统一鉴权方案并清理冗余字段([`b5ec343`](https://github.com/uuo00n/LLM-filter/commit/b5ec343)
- 人员统计:按类型统计并返回分项计数([`fddd97e`](https://github.com/uuo00n/LLM-filter/commit/fddd97e)
- 为多个 API 端点添加响应模型定义([`d149e42`](https://github.com/uuo00n/LLM-filter/commit/d149e42)
### 修复
- 修复考勤统计中缺少课程ID的问题([`7d00ec6`](https://github.com/uuo00n/LLM-filter/commit/7d00ec6)
- 将绑定数据中的 `_id` 字段转换为字符串([`0cea2c6`](https://github.com/uuo00n/LLM-filter/commit/0cea2c6)
- 将校园总览接口角色等级要求从 5 级降至 4 级([`35d99c7`](https://github.com/uuo00n/LLM-filter/commit/35d99c7)
### 文档
- README:补充实体化模型与接口文档说明([`f1739a6`](https://github.com/uuo00n/LLM-filter/commit/f1739a6)
## 2025-11-10
### 新增
- 迁移 BaseSettings 至 pydantic-settings 并新增 `APP_MODE`[`8ba714b`](https://github.com/uuo00n/LLM-filter/commit/8ba714b)
### 重构
- 移除后端仪表盘接口,改由前端实现([`b51127a`](https://github.com/uuo00n/LLM-filter/commit/b51127a)
### 文档
- README:补充重要说明与细节([`d21ebdb`](https://github.com/uuo00n/LLM-filter/commit/d21ebdb)
## 2025-11-05
### 杂项
- 更新 Ollama 配置与 Python 虚拟环境文件([`24e31fa`](https://github.com/uuo00n/LLM-filter/commit/24e31fa)
- 更新 `OLLAMA_MODEL``deepseek-r1:14b`[`0bbc809`](https://github.com/uuo00n/LLM-filter/commit/0bbc809)
## 2025-11-04
### 文档
- 更新 README 文档内容与格式([`9fadbc5`](https://github.com/uuo00n/LLM-filter/commit/9fadbc5)
## 2025-10-31
### 新增
- 初始化 LLM 过滤系统项目([`3cc8824`](https://github.com/uuo00n/LLM-filter/commit/3cc8824)
- 增强敏感词分类与严重程度管理([`5a2cce0`](https://github.com/uuo00n/LLM-filter/commit/5a2cce0)
### 文档
- 添加项目 README(系统概述与安装说明)([`5b8eb45`](https://github.com/uuo00n/LLM-filter/commit/5b8eb45)
- 更新 README:许可证与联系方式([`2f09184`](https://github.com/uuo00n/LLM-filter/commit/2f09184)
- 添加 MIT License 文件([`2612a04`](https://github.com/uuo00n/LLM-filter/commit/2612a04)
# LLM-Filter 智能对话过滤系统
[![License: MIT](https://img.shields.io/badge/License-MIT-blue)](LICENSE) [![Docker](https://img.shields.io/badge/Docker-Compose-2496ED?logo=docker&logoColor=white)](docker-compose.yml) [![PostgreSQL](https://img.shields.io/badge/PostgreSQL-15%2B-4169E1?logo=postgresql&logoColor=white)](scripts/init_postgres.sql) [![MongoDB](https://img.shields.io/badge/MongoDB-6%2B-47A248?logo=mongodb&logoColor=white)](scripts/init_mongo.py) [![Ollama](https://img.shields.io/badge/Ollama-Local%20LLM-000000)](https://ollama.com/) [![Dify](https://img.shields.io/badge/Dify-Agent-5B2EFF)](https://dify.ai/) [![Go](https://img.shields.io/badge/Go-1.x-00ADD8?logo=go&logoColor=white)](microservices/auth-service/go.mod) [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2%2B-6DB33F?logo=springboot&logoColor=white)](microservices/edu-service/pom.xml) [![FastAPI](https://img.shields.io/badge/FastAPI-0.1xx-009688?logo=fastapi&logoColor=white)](microservices/llm-service/app/main.py)
一个面向教育与企业双场景的智能对话过滤系统,基于 **微服务架构** 重构,集成了高效敏感词过滤、严格的角色与版别控制、以及完善的教务/企业数据管理。
系统采用 **Go (认证)** + **Java (教务)** + **Python (LLM)** 的混合技术栈,充分发挥各语言优势,通过 **Docker Compose** 统一编排部署。
......
# 校园 AI 智能助手:后端安全防护深度解析(基于本项目实现)
演讲稿与技术指南(Markdown)
> 说明:本文所有机制与代码片段均来自当前项目 `llm-filter` 的真实实现(网关 Nginx + Go Auth Service + Java Edu Service + Python LLM Service)。
---
## 开场白:为什么 AI 时代的后端安全是“最后裁决者”?
**演讲参考:**
“各位好!前端安全更像是‘门口保安’,而后端安全才是‘教务系统的总闸门’:身份、权限、数据、审计、合规,都必须在服务端形成闭环。
在我们的校园 AI 智能助手项目里,后端并不是一个单体应用,而是由 **网关(Nginx)+ 三个微服务(Auth/Edu/LLM)**组成。今天我会沿着一条请求从入口到模型的路径,把我们的后端安全‘五道防线’讲清楚。”
---
## 先给大家一张地图:项目后端安全链路总览
- **网关层(Nginx)**:统一入口与路由分发
- 配置:`gateway/nginx.conf`
- **认证层(Auth Service / Go + Gin)**:登录、注册、绑定关系、JWT 签发与校验
- 入口:`microservices/auth-service/main.go`
- JWT:`microservices/auth-service/pkg/utils/jwt.go`
- 中间件:`microservices/auth-service/pkg/middleware/auth.go`
- **教务层(Edu Service / Java + Spring)**:教务数据接口,JWT 过滤器注入 UserContext
- Filter:`microservices/edu-service/.../security/JwtAuthenticationFilter.java`
- 注册:`microservices/edu-service/.../EduServiceApplication.java`
- **LLM 层(LLM Service / Python + FastAPI)**:对话、敏感词库、敏感审计、模型调用
- 鉴权依赖:`microservices/llm-service/app/api/deps.py`
- 敏感词过滤:`microservices/llm-service/app/utils/sensitive_word_filter.py`
- 对话写入与审计:`microservices/llm-service/app/services/conversation.py`
---
## 第一章:守门人——网关统一入口与服务边界隔离(Gateway)
### 设计哲学
**安全的第一原则:入口要收敛。**
我们将所有外部访问统一打到网关,由网关做路由分发,让每个微服务只暴露必要的端口与路径(在容器网络内通信)。
### 核心实现(`gateway/nginx.conf`)
```nginx
# Auth Service 路由:认证与绑定走 auth-service
location /api/v1/auth {
proxy_pass http://auth_service/api/v1/auth;
proxy_set_header Host $host; # 透传 Host 便于服务端识别来源
proxy_set_header X-Real-IP $remote_addr; # 透传真实 IP 便于审计
}
# LLM Service 路由(兜底):其余请求默认转发到 llm-service
location / {
proxy_pass http://llm_service/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 链路追踪
}
```
**演讲参考:**
“我们把微服务当成不同的‘功能分区’:认证归认证、教务归教务、对话归对话。统一入口的好处是:策略集中、边界清晰、后续要加限流/黑白名单也只需要改一个地方。”
---
## 第二章:身份钢印——JWT 签发、校验与跨服务一致性(Auth / Edu / LLM 三方对齐)
### 设计哲学
- **身份要可验证**:不能只“信任前端传来的 user_id / role”
- **权限要可携带**:让每个服务都能在本地完成快速决策
- **跨语言一致**:Go 签发、Java/Python 校验必须字段一致、密钥一致
### 1)Auth Service:JWT 里写入角色与绑定信息(`pkg/utils/jwt.go`)
```go
claims := jwt.MapClaims{
"sub": userID, // 用户唯一标识(下游用它做“谁发起的”)
"name": username, // 用户名展示/审计
"role": role, // 角色(admin/administrator/user 等)
"role_level": roleLevel, // 角色等级(用于更细粒度的授权)
"edition": edition, // 版别(edu/biz)
"person_id": personID, // 绑定的业务身份(学生/教师/人员)
"person_type": personType, // 绑定类型(下游据此做数据边界)
"exp": time.Now().Add(24*time.Hour).Unix(), // 过期时间:避免长期滥用
}
```
### 2)Edu Service:过滤器统一校验 Authorization,并注入 UserContext(`JwtAuthenticationFilter.java`)
```java
String authHeader = httpRequest.getHeader("Authorization");
if (StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7); // 提取 Bearer Token
Claims claims = Jwts.parserBuilder()
.setSigningKey(key) // 使用 jwt.secret 验签
.build()
.parseClaimsJws(token)
.getBody();
String role = claims.get("role", String.class); // 角色
String personId = claims.get("person_id", String.class); // 绑定身份
UserContextHolder.setContext(UserContext.builder()
.role(role).personId(personId).build()); // 注入线程上下文
}
```
### 3)LLM Service:FastAPI 依赖中本地验签,不再回调 Auth(`app/api/deps.py`)
```py
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
user_id = payload.get("sub") # 与 Go 端保持一致:sub
role = payload.get("role") # 角色用于管理员接口保护
if current_user.get("role") not in {"admin", "administrator"}:
raise HTTPException(status_code=403, detail="权限不足,需要管理员权限")
```
**演讲参考:**
“我们用 JWT 做了一件关键的事:把‘身份、角色、绑定关系、版别’这四类安全上下文变成可验证的钢印。这样 Java 的教务服务、Python 的对话服务都能本地做快速决策,不需要每次再回调认证中心,既安全又高性能。”
---
## 第三章:权限隔离——绑定关系驱动的“数据边界”(从 token 到数据行)
### 设计哲学
校园系统的权限不只是“你是不是管理员”,更常见的是:
- **你是不是这名学生本人**
- **你是不是这位老师本人**
- **你有没有绑定到某个 person_id**
### 1)Auth Service:绑定接口必须先过认证(`auth-service/main.go`)
```go
bindingsGroup := v1.Group("/bindings")
bindingsGroup.Use(middleware.AuthMiddleware()) // 先验证 JWT,再允许绑定操作
{
bindingsGroup.POST("", bindingHandler.Bind)
bindingsGroup.GET("/me", bindingHandler.Me)
}
```
### 2)Edu Service:业务层用 person_id 做“学生/教师边界”(`DashboardServiceImpl.java`)
```java
String personId = user.getPersonId();
if (personId == null) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "未绑定学生");
}
Student student = studentRepository.findByPersonId(personId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "未找到学生档案"));
```
### 3)LLM Service:Mongo 查询强制带 user_id,防止横向越权(`services/conversation.py`)
```py
conversation = await db.db.conversations.find_one({
"_id": ObjectId(conversation_id),
"user_id": user_id, # 关键:必须属于当前用户
})
if not conversation:
return None
```
**演讲参考:**
“在校园场景里,最危险的往往不是‘能不能登录’,而是‘能不能看见不属于自己的数据’。我们用 `person_id` 绑定把学生/教师身份落实到 token 上,再用 `user_id` 归属校验把每次查询锁进自己的数据范围。”
---
## 第四章:内容护盾——敏感词过滤 + 智能体二次审核 + 审计留痕(LLM Service)
### 设计哲学
AI 内容不可预测,所以我们把后端对话链路当成“高危管道”来做:
- **先本地过滤**(成本低、速度快)
- **再智能体审核**(更强但更贵)
- **命中即拒答,并落审计**(可追责、可复盘)
### Dify Agent:把“内容安全判定”做成一台无状态机器
**演讲参考:**
“这里我们引入了 Dify Agent,但我们不把它当聊天机器人,而是当一台‘判定机器’:只负责审核,不负责对话。它的工作方式非常硬核:优先做知识库命中,其次做语义风险评估,最终只返回一个标准 JSON,做到可自动化、可审计、可接入任何链路。”
我们的 Agent 提示词(系统约束)强调四个关键点:
- **知识库驱动 + 无状态**:每次请求独立判定,不记忆用户上下文,避免被“洗脑”
- **命中即阻断**:敏感实体/变体一旦命中,直接给出 BLOCK 结果,不进入后续推理
- **反指令注入**:任何试图改规则的输入,直接归类为“违法活动”
- **唯一输出格式**:严格只输出 JSON(`safe/reason/suggestion`),便于后端统一处理与落库
在我们的代码里,Dify Agent 的落点也非常明确:
- **配置来自容器环境变量**`DIFY_API_URL / DIFY_API_KEY / DIFY_RESPONSE_MODE`(见 `docker-compose.yml``app/core/config.py`
- **调用封装在统一服务层**`app/services/dify.py``check_content_safety()`
- **执行时机是“本地词库放行之后”**:在 `app/services/conversation.py` 里,只有当 Trie 未命中敏感词,才会触发 Dify 二次审核
### 1)本地敏感词 Trie 匹配(`utils/sensitive_word_filter.py`)
```py
# 关键点:敏感词从 Mongo 加载到 Trie,检查时只做本地匹配
cursor = db.db.sensitive_words.find({})
async for document in cursor:
word = document.get("word", "")
if word:
self._add_to_trie(word, word_info)
# 检测:逐字符向前匹配,命中即记录(并计算最高严重程度)
text_lower = text.lower()
```
### 2)敏感词词库:MongoDB + 管理接口 + 启动加载
- 词库数据落在 MongoDB 的 `sensitive_words` 集合中,并在 LLM 服务启动时加载为 Trie(`app/main.py``load_sensitive_words()`
- 管理侧提供敏感词与分类的增删改查与批量导入(`app/api/v1/admin.py`,依赖 `get_current_admin_user` 做管理员权限保护)
### 3)对话写入前的“二级过滤”,命中则拒答 + 记录审计(`services/conversation.py`)
```py
check_result = sensitive_word_filter.check_text(content) # 本地敏感词命中
if not check_result["contains_sensitive_words"]:
dify_result = await dify_service.check_content_safety(content, user_id) # 智能体审核
if contains_sensitive:
await db.db.sensitive_records.insert_one({ # 落审计(可追责)
"user_id": user_id,
"message_content": content,
"sensitive_words_found": detailed_words,
"highest_severity": highest,
})
return {"assistant_response": "当前问题暂无法回答。"} # 统一拒答出口
```
**演讲参考:**
“我们不是让 AI ‘自由发挥’,而是把它放进一条带阀门的安全管道:先用本地敏感词把明显违规的拦住,再用智能体对隐蔽风险做二次判断,一旦命中就拒答并留下审计记录。”
---
## 第五章:工程化安全——接口暴露面、文档入口与运行模式隔离(Edu / LLM)
### 设计哲学
- **暴露面越小越安全**:能不公开就不公开
- **文档与健康检查要可控**:该跳过就跳过,但不能把敏感接口也放开
- **运行模式要隔离**:教育版/企业版不应混跑导致权限穿透
### 1)Edu Service:公开路径白名单(Swagger/Health 放行,其它都要 token)
`JwtAuthenticationFilter.java``isPublicPath()` 明确放行:
- `/swagger-ui`
- `/v3/api-docs`
- `/api/v1/edu/health`
### 2)LLM Service:版别运行模式依赖(`app/api/deps.py`)
```py
mode = (settings.APP_MODE or "edu").lower() # 运行模式:edu/biz
if mode in {"edu", "biz"}:
user_edition = (current_user.get("edition") or "").lower()
if user_edition != mode:
raise HTTPException(status_code=403, detail="当前模式无权访问")
```
**演讲参考:**
“安全工程化的关键是:把‘约束’写进框架层。我们把版别隔离做成了路由依赖,让错误配置和越权访问在最靠前的位置就失败。”
---
## 当前项目“已实现”的后端安全点(清单式总结)
- **统一入口与服务边界**:网关统一路由分发(`gateway/nginx.conf`
- **JWT 身份闭环**:Auth 签发,Edu/LLM 本地验签(Go/Java/Python 对齐)
- **绑定关系驱动的数据边界**:token 携带 `person_id/person_type`,教务接口基于绑定读取
- **横向越权防护**:LLM 对话 Mongo 查询强制带 `user_id`
- **内容合规链路**:敏感词 Trie + 智能体审核 + `sensitive_records` 审计落库
- **版别隔离**:LLM 层 `APP_MODE` 与 token `edition` 做强校验
## 结语:防御在后端,体验在前端
**演讲参考:**
“我们这套系统的安全,不靠某一个‘神奇中间件’,而是靠从网关到鉴权、从绑定到数据边界、从内容审核到审计留痕的一条链路闭环。
对用户来说,体验依旧是‘顺滑可用’;但对系统来说,每一次请求都在规则之内,每一条敏感行为都能追溯。谢谢大家!”
---
## 文档信息
- 文档类型:技术演讲稿与实现指南
- 主题:校园 AI 智能助手后端安全防护(基于 llm-filter 项目)
- 版本:项目对齐版
- 日期:2025年12月
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment