# 智能体中实现部门知识库隔离方案 ## 🎯 业务场景 **需求**:一个智能体(如"校园助手"),不同部门的用户访问时,只能查询到本部门及公共的知识库。 **示例**: - 教务处用户:可访问"教务知识库" + "公共知识库" - 财务处用户:可访问"财务知识库" + "公共知识库" - 学生:只能访问"公共知识库" --- ## 📋 实现方案(推荐) ### 方案架构 ``` 用户请求 ↓ 1. 获取用户部门和角色 ↓ 2. 查询有权限的知识库列表(已实现✅) ↓ 3. 在Dify对话时动态指定知识库 ↓ 4. 返回结果(只包含授权知识库的内容) ``` --- ## 🔧 技术实现 ### 1. 知识库分类(数据库层) #### 1.1 创建知识库时设置权限 ```java @Service public class AiKnowledgeServiceImpl { @Transactional public ResultDomain createKnowledge( TbAiKnowledge knowledge, KnowledgePermissionType permissionType) { // 1. 获取当前登录用户信息(通过LoginUtil)⭐ TbSysUser currentUser = LoginUtil.getCurrentUser(); List userDeptRoles = LoginUtil.getCurrentDeptRole(); String deptId = userDeptRoles.isEmpty() ? null : userDeptRoles.get(0).getDeptID(); // 2. 保存知识库 knowledge.setCreator(currentUser.getId()); knowledge.setCreatorDept(deptId); knowledgeMapper.insert(knowledge); // 3. 根据权限类型创建权限记录 switch (permissionType) { case PUBLIC: // 公开知识库:所有人可读 createPublicPermission(knowledge.getId()); break; case DEPARTMENT: // 部门知识库:本部门所有人可读写 createDepartmentPermission(knowledge.getId(), deptId); break; case DEPARTMENT_INHERIT: // 部门继承:本部门及子部门可读 createDepartmentInheritPermission(knowledge.getId(), deptId); break; case ROLE: // 角色知识库:特定角色可读(跨部门) createRolePermission(knowledge.getId(), roleIds); break; case PRIVATE: // 私有知识库:仅创建者所在部门的特定角色 createPrivatePermission(knowledge.getId(), deptId, roleIds); break; } return ResultDomain.success(knowledge); } // 创建公开权限 private void createPublicPermission(String knowledgeId) { TbResourcePermission permission = new TbResourcePermission(); permission.setId(UUID.randomUUID().toString()); permission.setResourceType(10); // AI_KNOWLEDGE permission.setResourceId(knowledgeId); permission.setDeptId(null); // NULL表示不限部门 permission.setRoleId(null); // NULL表示不限角色 permission.setCanRead(true); permission.setCanWrite(false); permission.setCanExecute(false); resourcePermissionMapper.insert(permission); } // 创建部门权限 private void createDepartmentPermission(String knowledgeId, String deptId) { TbResourcePermission permission = new TbResourcePermission(); permission.setId(UUID.randomUUID().toString()); permission.setResourceType(10); permission.setResourceId(knowledgeId); permission.setDeptId(deptId); // 指定部门 permission.setRoleId(null); // 部门内所有角色 permission.setCanRead(true); permission.setCanWrite(true); // 部门成员可编辑 permission.setCanExecute(false); resourcePermissionMapper.insert(permission); } // 创建部门继承权限(利用dept_path) private void createDepartmentInheritPermission(String knowledgeId, String deptId) { // 查询部门信息 TbSysDept dept = deptMapper.selectById(deptId); // 为本部门创建权限(已通过dept_path自动继承给子部门) TbResourcePermission permission = new TbResourcePermission(); permission.setId(UUID.randomUUID().toString()); permission.setResourceType(10); permission.setResourceId(knowledgeId); permission.setDeptId(deptId); permission.setRoleId(null); permission.setCanRead(true); permission.setCanWrite(false); // 子部门只读 permission.setCanExecute(false); resourcePermissionMapper.insert(permission); // dept_path机制会自动让子部门继承此权限 } } ``` #### 1.2 权限类型枚举 ```java public enum KnowledgePermissionType { PUBLIC, // 公开(所有人可读) DEPARTMENT, // 部门(本部门可读写) DEPARTMENT_INHERIT, // 部门继承(本部门及子部门可读) ROLE, // 角色(特定角色跨部门可读) PRIVATE // 私有(特定部门+角色) } ``` --- ### 2. 对话时动态过滤知识库(Service层) ```java @Service public class AiChatServiceImpl { @Autowired private AiKnowledgeMapper knowledgeMapper; @Autowired private DifyApiClient difyApiClient; /** * 流式对话(带知识库隔离) */ public void streamChat( String message, String conversationId, String userId, SseEmitter emitter) { // 1. 获取当前登录用户的部门角色信息(通过LoginUtil)⭐ List userDeptRoles = LoginUtil.getCurrentDeptRole(); // 2. 查询用户有权限的知识库列表(自动权限过滤✅) TbAiKnowledge filter = new TbAiKnowledge(); filter.setStatus(1); // 只查询启用的知识库 List availableKnowledges = knowledgeMapper.selectAiKnowledges( filter, userDeptRoles // 自动根据用户部门角色过滤 ); // 3. 提取Dify Dataset IDs List datasetIds = availableKnowledges.stream() .map(TbAiKnowledge::getDifyDatasetId) .filter(Objects::nonNull) .collect(Collectors.toList()); // 4. 调用Dify API(指定知识库列表) DifyChatRequest request = DifyChatRequest.builder() .query(message) .conversationId(conversationId) .user(userId) .datasets(datasetIds) // ⭐ 动态指定知识库 .stream(true) .build(); // 5. 流式响应 difyApiClient.streamChat(request, new StreamCallback() { @Override public void onChunk(String chunk) { emitter.send(chunk); } @Override public void onComplete(DifyChatResponse response) { // 保存消息记录 saveMessage(conversationId, userId, message, response); emitter.complete(); } @Override public void onError(Throwable error) { emitter.completeWithError(error); } }); } } ``` --- ### 3. Dify API请求参数 ```java @Data @Builder public class DifyChatRequest { private String query; // 用户问题 private String conversationId; // 会话ID private String user; // 用户ID @JsonProperty("datasets") private List datasets; // ⭐ 指定知识库ID列表 private Boolean stream; // 是否流式 private Map inputs; // 额外输入 } ``` **Dify API调用示例:** ```json POST /v1/chat-messages { "query": "如何申请奖学金?", "conversation_id": "conv-123", "user": "user-001", "datasets": [ "dataset-edu-001", // 教务知识库 "dataset-public-001" // 公共知识库 ], "stream": true } ``` --- ### 4. 前端展示(可选) 可以在前端显示用户当前可访问的知识库: ```typescript interface ChatPageState { availableKnowledges: Knowledge[]; // 用户可访问的知识库 selectedKnowledges: string[]; // 用户选择的知识库(可多选) } // 用户可以手动选择使用哪些知识库 async function sendMessage(message: string) { const response = await axios.post('/ai/chat', { message, conversationId, knowledgeIds: selectedKnowledges // 从可用列表中选择 }); } ``` --- ## 🔐 权限控制流程 ### 创建知识库流程 ``` 1. 用户创建知识库 ↓ 2. 选择权限类型(公开/部门/角色/私有) ↓ 3. 系统创建知识库记录 ↓ 4. 自动创建权限记录(tb_resource_permission) ↓ 5. 同步到Dify(创建Dataset) ``` ### 对话查询流程 ``` 1. 用户发起对话 ↓ 2. 获取用户部门角色(UserDeptRoleVO) ↓ 3. 查询有权限的知识库(Mapper自动过滤) ↓ 4. 提取Dify Dataset IDs ↓ 5. 调用Dify API(指定datasets参数) ↓ 6. Dify只从指定知识库中检索 ↓ 7. 返回结果 ``` --- ## 📊 权限矩阵示例 | 知识库 | 类型 | 教务处-管理员 | 教务处-教师 | 财务处-管理员 | 学生 | |--------|------|---------------|-------------|---------------|------| | 公共知识库 | PUBLIC | ✅ 读 | ✅ 读 | ✅ 读 | ✅ 读 | | 教务知识库 | DEPARTMENT | ✅ 读写 | ✅ 读写 | ❌ | ❌ | | 财务知识库 | DEPARTMENT | ❌ | ❌ | ✅ 读写 | ❌ | | 教师手册 | ROLE | ✅ 读 | ✅ 读 | ❌ | ❌ | | 管理规范 | PRIVATE | ✅ 读写执行 | ❌ | ✅ 读写执行 | ❌ | --- ## 🎨 前端交互优化 ### 1. 知识库选择器 ```vue ``` ### 2. 知识库来源标注 在AI回答中标注知识来源: ```json { "answer": "申请奖学金需要...", "sources": [ { "knowledge_id": "kb-001", "knowledge_title": "奖学金管理办法", "department": "教务处", "snippet": "第三条..." } ] } ``` --- ## ⚡ 性能优化 ### 1. 缓存用户可访问的知识库 ```java // 注意:缓存key应该使用用户ID + 部门角色组合,确保权限变更后缓存失效 @Cacheable(value = "user:knowledges", key = "#root.target.getCurrentUserCacheKey()") public List getUserAvailableKnowledges() { // 通过LoginUtil获取当前用户的部门角色信息⭐ List userDeptRoles = LoginUtil.getCurrentDeptRole(); return knowledgeMapper.selectAiKnowledges( new TbAiKnowledge(), userDeptRoles ); } // 生成缓存key(包含用户ID和部门角色信息) private String getCurrentUserCacheKey() { TbSysUser user = LoginUtil.getCurrentUser(); List roles = LoginUtil.getCurrentDeptRole(); String roleIds = roles.stream() .map(r -> r.getDeptID() + ":" + r.getRoleID()) .collect(Collectors.joining(",")); return user.getId() + ":" + roleIds; } ``` ### 2. 知识库预加载 在用户登录时预加载知识库列表,缓存到Redis: ```java // 登录时 String cacheKey = "user:" + userId + ":knowledges"; List datasetIds = getDatasetIds(userId); redisTemplate.opsForValue().set(cacheKey, datasetIds, 1, TimeUnit.HOURS); // 对话时直接使用 List datasetIds = redisTemplate.opsForValue().get(cacheKey); ``` --- ## 🔄 知识库共享场景 ### 场景1:跨部门协作知识库 ```java // 教务处和学工处共享的"学生管理"知识库 createKnowledge("学生管理知识库", Arrays.asList( new Permission("dept_academic", null, true, false, false), new Permission("dept_student", null, true, false, false) )); ``` ### 场景2:角色知识库(跨部门) ```java // 所有"教师"角色可访问(不限部门) createKnowledge("教师手册", Arrays.asList( new Permission(null, "teacher", true, false, false) )); ``` ### 场景3:临时授权 ```java // 给特定用户临时授权 @Transactional public void grantTemporaryAccess(String knowledgeId, String userId, Integer hours) { // 创建临时权限记录 TbResourcePermission permission = new TbResourcePermission(); permission.setResourceId(knowledgeId); permission.setUserId(userId); // 扩展字段:用户级权限 permission.setExpireTime(LocalDateTime.now().plusHours(hours)); permissionMapper.insert(permission); } ``` --- ## 📝 实现清单 ### ✅ 已完成 - [x] 数据库表设计(creator_dept字段) - [x] 权限表设计(tb_resource_permission) - [x] Mapper权限过滤(selectAiKnowledges) ### 🔄 需要实现 - [ ] KnowledgePermissionType枚举 - [ ] 创建知识库时的权限创建逻辑 - [ ] 对话时的知识库过滤逻辑 - [ ] Dify API Client(支持datasets参数) - [ ] 前端知识库选择器 - [ ] Redis缓存优化 --- ## 🎯 总结 **核心思路**: 1. **数据库层**:通过`tb_resource_permission`控制知识库访问权限(已实现✅) 2. **应用层**:对话时根据用户权限动态查询可用知识库 3. **Dify层**:通过API的`datasets`参数限制检索范围 **优势**: - ✅ 灵活:支持公开、部门、角色、私有等多种权限模型 - ✅ 安全:数据库层权限控制,无法绕过 - ✅ 性能:利用dept_path支持部门继承,查询高效 - ✅ 可扩展:可以轻松添加新的权限类型 **这个方案充分利用了您现有的权限系统设计!** 🎉