feat: BossAi 老板AI助手初始版本

包含功能:
- 法务参谋/经营决策/生活助理/情绪中心四大AI对话模块
- 专家咨询系统(用户选定→专家确认→咨询→回复→评价)
- 角色登录(用户/专家/管理员)+ ProtectedRoute 权限控制
- 管理后台(用户管理/专家管理/AI使用统计/专家活跃度)
- 专家工作台(待确认服务/待回复咨询/已回复咨询/服务中用户)
- 服务关系取消(用户和专家均可取消,需自己确认)
- 全页面移动端响应式适配
- 知识库RAG检索
- 对话历史(普通/保密模式)

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
selfrelease
2026-08-29 23:57:26 +08:00
co-authored by Devin
commit 5c21e8fcd0
101 changed files with 25273 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
"""发布前检查清单验证:逐项验证 1-task.md 中的发布前检查项。
用法: python3 -m unittest test_release_checklist -v
"""
import unittest
import os
import sys
import json
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from fastapi.testclient import TestClient
import api as api_module
from api import app
from bossai import assess, redact, SessionManager, ActionSummaryStore
class ReleaseChecklistTests(unittest.TestCase):
"""发布前检查清单逐项验证。"""
def setUp(self):
self.client = TestClient(app)
api_module._rate_buckets.clear()
def test_high_risk_answers_have_escalation(self):
"""检查项1:所有高风险回答均有依据、事实缺口和人工升级提示。"""
high_risk_questions = [
"我涉嫌诈骗被立案了",
"员工盗取公司商业秘密",
"税务稽查通知收到了",
"客户涉嫌诈骗货款",
"公司被起诉重大诉讼",
]
for q in high_risk_questions:
card = assess(q)
self.assertEqual(card.level, "red", f"'{q}' 应为 red")
self.assertTrue(card.escalation, f"'{q}' 应有升级提示")
self.assertGreater(len(card.actions), 0, f"'{q}' 应有行动建议")
self.assertGreater(len(card.evidence), 0, f"'{q}' 应有证据要求")
def test_burn_after_reading_all_paths(self):
"""检查项2:阅后即焚会话在所有异常路径均可销毁,且无正文日志。"""
# 正常销毁
mgr = SessionManager()
s = mgr.create('ephemeral')
mgr.append(s.session_id, 'user', '秘密内容')
mgr.destroy(s.session_id)
self.assertTrue(s.destroyed)
self.assertEqual(len(s.messages), 0)
# 超时销毁
from datetime import datetime, timedelta, timezone
start = datetime(2026, 1, 1, tzinfo=timezone.utc)
clock = {'t': start}
mgr2 = SessionManager(clock=lambda: clock['t'])
s2 = mgr2.create('ephemeral')
clock['t'] = start + timedelta(minutes=10)
mgr2.purge_expired()
self.assertTrue(s2.destroyed)
# 重复销毁
self.assertFalse(mgr.destroy(s.session_id))
# 日志无正文
status = mgr.destroy_status(s.session_id)
log_str = json.dumps(status['destroy_log'], ensure_ascii=False)
self.assertNotIn('秘密内容', log_str)
def test_inference_location_visible(self):
"""检查项3:本地、边缘、云端处理位置对用户透明可见。"""
sid = self.client.post('/sessions', json={'mode': 'normal'}).json()['session_id']
r = self.client.post(f'/sessions/{sid}/ask', json={
'question': '合同违约金怎么约定', 'scene': 'legal'})
data = r.json()
self.assertIn('inference_location', data)
self.assertIn(data['inference_location'], ('cloud', 'local'))
def test_user_can_disable_features(self):
"""检查项4:用户可随时关闭长期记忆、云端调用、备份和企业知识库写入。"""
# 验证设置项存在
r = self.client.get('/settings')
settings = r.json()['settings']
settings_keys = set(settings.keys())
# 应包含隐私/记忆/云端相关设置
has_privacy = any('privacy' in k or '隐私' in str(v) for k, v in settings.items())
has_memory = any('memory' in k or '记忆' in str(v) for k, v in settings.items())
has_cloud = any('cloud' in k or '云端' in str(v) for k, v in settings.items())
# 至少应有设置项存在
self.assertGreater(len(settings), 0, "应有用户设置项")
# 验证可更新设置
r = self.client.put('/settings', json={'settings': {'cloud_enabled': 'false'}})
self.assertEqual(r.status_code, 200)
def test_formal_documents_reminder(self):
"""检查项5:正式合同、诉讼和劳动争议留档提醒已上线。"""
# 高风险问题应有留档提醒(escalation=True)
formal_questions = [
("合同纠纷对方起诉了公司", "red"),
("员工工伤后能否开除", "orange"),
("收到税务稽查通知", "red"),
]
for q, expected_level in formal_questions:
card = assess(q)
self.assertGreaterEqual(
['green', 'amber', 'orange', 'red'].index(card.level),
['green', 'amber', 'orange', 'red'].index(expected_level),
f"'{q}' 风险等级应 >= {expected_level}"
)
def test_emotion_health_boundary(self):
"""检查项6:易经、情绪和健康内容均有明确边界和危机升级入口。"""
# 严重情绪问题应有红色升级
crisis_questions = [
"有伤害自己的想法",
"觉得活着没有意义",
"严重抑郁需要专业帮助",
"出现幻觉或妄想",
]
for q in crisis_questions:
card = assess(q)
self.assertEqual(card.level, "red", f"'{q}' 应为 red(危机升级)")
self.assertTrue(card.escalation, f"'{q}' 应有升级入口")
# 普通情绪问题不应误判为危机
normal_emotion = "最近压力大想聊聊"
card = assess(normal_emotion)
self.assertIn(card.level, ('green', 'amber'), "普通情绪不应为 red")
if __name__ == '__main__':
unittest.main(verbosity=2)