🏛️ 民情直通车2(baisitong2)
民情诉求智能分析与智能派单系统 · AI + 社会治理 双引擎语义分析 + 分类职责矩阵派单 + 工单状态机 + SLA 时限监管,零配置、可离线运行
🎯 为什么 AI 是本系统的关键要素(核心演示论点)
以一条真实风格的复合诉求为例:
"楼下烧烤店每天晚上油烟很大,我家孩子一直咳嗽"
| 引擎 | 分析结果 | 派单结果 |
|---|---|---|
| 规则引擎(离线兜底) | 只能命中"烧烤/油烟"字面关键词 → 单标签 ['环境卫生'] |
仅派综合执法局 |
| 远程 LLM(智能分析) | 推理出"孩子一直咳嗽"是油烟引发的隐含健康风险 → 复合标签 ['环境卫生', '民生保障'] |
主派综合执法局 + 抄送民政局,儿童健康问题不再漏管 |
关键词规则永远无法从"孩子咳嗽"推出"民生保障"——这只有大模型的语义推理做得到。 同时,健康受害的隐含情绪、长期反映的焦灼感,LLM 也能给出更真实的情感分(2 分 vs 规则引擎的 3 分中性), 让风险预警列表第一时间捕捉到这条诉求。这就是"AI 是解决问题的关键要素"的直接证据。
该案例已内置为工作台"一键填充"示例与测试锚点(
tests/test_api.py::test_composite_appeal_offline_full_flow), 离线(无 Key)环境下走规则引擎全流程可用,配置 Key 后即可演示 LLM 复合标签的对照效果。
一分钟跑起来
方式 1:npm(任何装了 Python 3.9+ 的电脑)
npx baisitong2 # 自动建 venv、装依赖、启动,打开 http://localhost:8081
npx baisitong2 --port 9000 # 自定义端口
方式 2:pip 安装
pip install baisitong2 -i https://mirrors.aliyun.com/pypi/simple/
baisitong2 # 或 python -m baisitong2
方式 3:源码运行
./startup.sh # Linux / macOS(Windows 用 startup.bat)
# 或手动:
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python -m baisitong2 --port 8081
零配置即用:默认 SQLite + 内置规则分析引擎(关键词多标签分类 + 情感词典 + 紧急度规则), 无需数据库、无需 API Key、无需联网。配置大模型 Key 后自动切换真模型,见配置。
功能特性
| 模块 | 说明 |
|---|---|
| 🤖 双引擎语义分析 | 远程 LLM(OpenAI 兼容 + Function-Calling 强约束 JSON)↔ 内置规则引擎;超时(15s 可调)/5xx/脏 JSON 自动降级,analysis_source 标记来源 |
| 🏷️ 多标签分类 | 固定 9 类体系(环境卫生/噪音扰民/违章建筑/公共设施/物业管理/民生保障/邻里纠纷/交通安全/其他),复合诉求一次打多标 |
| 💗 情感 + 紧急度 | 情感 1-5 五档(风险预警口径 ≤2)、紧急度 1-3(煤气/漏水/倒塌/伤人等安全词 → 3 级) |
| 📨 智能派单 | 分类 → 职责矩阵自动匹配主责部门;多标签诉求其余部门自动抄送;支持人工纠偏指定部门 |
| 🔁 诉求重分析 | POST /api/appeals/{id}/reanalyze(可 force_rule=true 强制规则引擎);结果变化自动保留历史版本(analysis_history),已派单诉求重析后可重新派单 |
| ⏱️ SLA 时限监管 | 紧急 4h / 一般 24h / 低 72h(环境变量可调),看板临期黄色预警、超时红色告警、剩余 ≤25% 临期提醒 |
| 🔁 工单状态机 | 待派单→已派单→已接单→已完成→已办结主线 + 拒单回退;非法流转直接 400,全程时间戳落档 |
| 🪙 Token 管控(AC-4) | ANALYSIS_COMPACT 省流模式(精简 Prompt + 压缩输出,单条 <600 token 预算);API usage 精确捕获、本地按 1.6字/token 折算;看板今日/累计 Token 卡片 |
| 📦 批量分析 | 并发受 BATCH_CONCURRENCY 管控(Token 管控),单条失败逐条降级,绝不整体失败 |
| 📊 治理看板 | 分类分布、近 7 日趋势、部门工单量与平均办结时效、情感风险预警、SLA 按时率 + 临期列表、Token 消耗 |
| 📄 CSV 导出 | 诉求/工单两份数据按当前筛选导出 UTF-8-BOM CSV(Excel 中文不乱码),标准库实现零依赖 |
| 🌙 深色模式 | CSS 变量全站主题化,data-theme="dark" 整组覆盖;四页右上角一键切换 + localStorage 记忆 + 跟随系统深浅色 + 首帧防闪白;徽章/SLA 色块/看板深色下对比度整组重调 |
| 📱 移动端适配 | 480px 断点:窄屏顶部导航、筛选器两列换行、列表卡纵排、统计表格横向滚动、输入 16px 防 iOS 缩放;筛选条件 localStorage 记忆,刷新自动还原 |
| ⚡ 性能与并发 | stats 聚合无 N+1(SQL 条数护栏测试锁定);单条规则分析 <10ms、看板聚合 <100ms 基准护栏;10 线程并发提交+分析+派单零串扰零失败(工单号取号加锁原子化 + SQLite 引擎多连接化,修复两处真实并发缺陷) |
| 🖥️ 原生前端 | 纯 HTML/CSS/JS 零构建:工作台(示例一键填充)、诉求列表(多维筛选)、工单看板(SLA 倒计时)、统计看板 |
系统架构
┌──────────────────────────────────────────────────────────────────┐
│ 前端 原生 HTML/CSS/JS(无框架、无构建) │
│ 诉求工作台(提交+AI分析卡) · 诉求列表(筛选/派单) │
│ 工单看板(状态分列+SLA倒计时+流转) · 统计看板(趋势/风险) │
└───────────────────────────┬──────────────────────────────────────┘
│ HTTP JSON
┌───────────────────────────▼──────────────────────────────────────┐
│ 接口层 routers/ │
│ appeals(提交/批量/筛选/派单) · workorders(列表/流转) │
│ stats(看板) · health(健康检查) ← 统一 {code,message} 异常兜底 │
├──────────────────────────────────────────────────────────────────┤
│ 逻辑层 services/ │
│ analyzer 双引擎:RemoteAnalyzer(Function-Calling JSON) │
│ ↔ RuleAnalyzer(关键词+情感词典+紧急度规则) │
│ dispatcher 职责矩阵匹配(主责+抄送) · SLA 计算 · 工单号生成 │
│ state_machine 状态流转校验 + 时间戳 + 诉求状态联动 │
│ stats 每日聚合(DailyStat)+ 看板组装 │
├──────────────────────────────────────────────────────────────────┤
│ 数据层 models/ + database.py │
│ Appeal(诉求+分析JSON) · WorkOrder(状态机+SLA) │
│ Department(职责矩阵) · DailyStat(按日聚合) │
│ SQLAlchemy ORM → SQLite(默认) / MySQL 8(可切换) │
└──────────────────────────────────────────────────────────────────┘
│ │
GLM / DeepSeek API 内置规则引擎(离线兜底)
(配置 Key 后启用) 关键词分类 + 词典打分 + 模板摘要
AI 双引擎设计:analyze_appeal() 统一编排——优先远程 LLM
(Function-Calling 强约束返回 {categories, key_info, sentiment, urgency, summary} 严格 JSON),
超时 / 5xx / JSON 解析失败自动降级内置规则引擎,保证演示永不中断,
analysis_source 字段(llm / rule_fallback)全程标记数据来自哪个引擎。
派单管线:多标签分类 → 职责矩阵匹配(首分类定主责,其余部门抄送) → SLA 时限(紧急4h/一般24h/低72h) → 工单号 WO-YYYYMMDD-XXXX → 状态机流转 → 看板监管
状态机
诉 求: pending → analyzing → analyzed → dispatched → resolved
▲ (工单办结联动)
└── 拒单回退(可重新派单)
工 单: pending → dispatched → accepted → completed → resolved
└── dispatch ──┴── reject → rejected(终态)
动作:dispatch(派单) accept(接单) reject(拒单) complete(办结反馈) resolve(确认办结)
非法流转(跨级/终态再动)→ BusinessError → 400 {code, message}
API 一览
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/appeals |
提交诉求,立即分析,返回完整结果(多标签/情感/紧急度/摘要/来源) |
| POST | /api/appeals/batch |
批量分析(并发管控,逐条降级,绝不整体失败) |
| GET | /api/appeals |
列表:分页 + category/status/source/urgency/sentiment(_lte) 筛选 + q 关键词 |
| GET | /api/appeals/export |
诉求 CSV 导出(筛选同列表口径,UTF-8-BOM 流式) |
| GET | /api/appeals/{id} |
诉求详情(含关联工单、分析历史、Token 消耗) |
| POST | /api/appeals/{id}/dispatch |
智能派单(body 可选 department 人工纠偏) |
| POST | /api/appeals/{id}/reanalyze |
重新分析(body 可选 force_rule=true 强制规则引擎;结果变化保留历史) |
| GET | /api/appeals/departments |
分类职责矩阵 |
| GET | /api/workorders |
工单列表:status/department 筛选 + q 工单号搜索 |
| GET | /api/workorders/export |
工单 CSV 导出(status/department 筛选,UTF-8-BOM 流式) |
| GET | /api/workorders/{id} |
工单详情(含诉求摘要与时间线) |
| POST | /api/workorders/{id}/transition |
状态机流转 {action, note?, handler?} |
| GET | /api/stats |
看板:总览/分类分布/近7日趋势/部门时效/风险预警/SLA(含 sla_ending 临期)/Token |
| GET | /api/health |
健康检查(运行模式/SLA/Token 管控配置) |
配置
复制 .env.example 为 .env,全部留空即本地模式:
# LLM(OpenAI 兼容协议,留空 = 内置规则引擎)
LLM_API_KEY=sk-xxx
LLM_BASE_URL=https://open.bigmodel.cn/api/paas/v4 # 或 https://api.deepseek.com/v1
LLM_MODEL=glm-4-flash # 或 deepseek-chat
ANALYSIS_TIMEOUT=15 # 单条分析超时(秒),超时自动降级
BATCH_CONCURRENCY=2 # 批量分析并发上限(Token 管控)
ANALYSIS_COMPACT=true # 省流模式:精简 Prompt + 压缩输出(AC-4 Token 管控)
TOKEN_BUDGET_PER_APPEAL=600 # 单条分析 Token 预算(软约束,超出记告警日志)
SLA_HOURS_URGENT=4 # 紧急件办结时限
SLA_HOURS_NORMAL=24
SLA_HOURS_LOW=72
# 决赛环境切换 MySQL 8:
DATABASE_URL=mysql+pymysql://root:password@localhost:3306/appeal_dispatch
v1.0.0 老库(SQLite/MySQL)直接升级:启动时自动 ALTER TABLE ADD COLUMN 补齐新列
(appeals.tokens_used / appeals.analysis_history / daily_stats.tokens_used),老数据零丢失。
离线部署:无外网机器直接 ./startup.sh 即可 —— SQLite 零配置 + 规则引擎离线分析,
全部 137 个测试也在离线环境跑通(tests/conftest.py 强制弹出 Key 环境变量)。
性能(实测)
| 指标 | 实测(M3,TestClient) | 护栏断言 |
|---|---|---|
| 单条规则分析 | ~0.01ms | <10ms(test_rule_analysis_under_10ms) |
| stats 看板聚合(60 诉求/20 工单,含 HTTP) | ~3.9ms | <100ms(test_stats_endpoint_under_100ms) |
| 诉求列表组合筛选(分类+关键词) | ~1.8ms | — |
| 10 线程并发提交+分析+派单全链 | ~61ms 全部成功 | 零串扰零失败(test_10_concurrent_zero_cross_talk) |
| /api/stats SQL 条数 | 6 条(全量内存聚合) | ≤8 条防 N+1 回归(test_stats_query_count_bounded) |
并发正确性:v1.0.2 修复两处真实缺陷 —— ① SQLite 文件库引擎 StaticPool 单连接在并发下互锁, 改为 QueuePool 多连接 + busy timeout;② 工单号"读当日最大序号+1"竞态撞唯一约束, 取号到落库进程内锁原子化 + 冲突重试。热路径(分析/派单/聚合)耗时均落 INFO 日志可现场观测。
测试
pip install -r requirements-dev.txt
pytest -q # 137 个用例,全程离线
覆盖:规则引擎(复合案例/情感边界/紧急度安全词全覆盖/关键信息)、LLM 分析器(假 httpx 响应:正常/ 超时/5xx/脏JSON/空参数/多余字段/缺字段/归一化)、职责匹配、SLA 三档与超时统计口径、工单号生成、 状态机(主线全流转/跨级非法/终态冻结/重复接单/拒单重派)、提交→分析→派单→流转→办结全闭环、 批量分析降级与脏数据拦截、组合筛选/分页边界、统计口径、health,以及 XSS 防御三件套 (payload 原样 JSON 往返、esc() 引号转义契约、用户字段拼接静态扫描 + JS 语法回归); v1.0.1 新增:Token 管控(折算口径/省流 Prompt 长度与请求体断言/usage 捕获与折算兜底)、 旧库补列迁移(升级 + 幂等)、重分析(历史保留/强制规则/已派单重派/404/422)、 CSV 导出(BOM/表头/行数与筛选一致/部门筛选/422)、SLA 临期四边界(恰好 25% 计入、 33% 排除、超时排除、已完成排除)与看板 Token 字段增长; v1.0.2 新增:深色模式静态契约(变量覆盖/切换/记忆/跟随系统/node 执行首帧脚本行为矩阵)、 移动端断点(480px/导航/表格滚动)、筛选记忆契约、10 线程并发零串扰、 性能基准护栏(规则分析 <10ms/聚合 <100ms)与 stats SQL 条数护栏。
项目结构
baisitong2/
├── baisitong2/ # Python 包
│ ├── main.py # 应用装配 + 异常兜底
│ ├── config.py # 环境变量集中配置
│ ├── database.py # engine / 会话工厂
│ ├── errors.py # 业务异常体系
│ ├── models/ # Appeal / WorkOrder / Department / DailyStat
│ ├── schemas/ # Pydantic DTO
│ ├── routers/ # appeals / workorders / stats
│ ├── services/ # analyzer / dispatcher / state_machine / stats / departments
│ └── static/ # 原生前端(4 页面)
├── bin/cli.js # npm 启动器(探测 Python → ~/.baisitong2/venv)
├── tests/ # pytest:单元 + API 集成(离线)
├── sql/init.sql # MySQL 8 DDL + 预置部门
├── docs/项目流程.md/html # 项目文档
└── startup.sh / startup.bat # 一键启动
License
MIT © yangxuan
Metadata
Release files for baisitong2 1.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| baisitong2-1.0.2.tar.gz | 108.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| baisitong2-1.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 192.1 kB
Release files / baisitong2-1.0.2.tar.gz
| Download URL | baisitong2-1.0.2.tar.gz |
|---|---|
| Size | 108.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
27b1e7cde6515137ba7e1128a0a8d35da95418d775a30ae2c90173577034747a
|
|
BLAKE2b-256 checksum How to use checksums |
9413dddb817552f7eebdc5f9c7c3240d504475f7a73794740460f323c6ca4d22
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / baisitong2-1.0.2-py3-none-any.whl
| Download URL | baisitong2-1.0.2-py3-none-any.whl |
|---|---|
| Size | 83.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0bd4997d1f3e5b35e498d709d90a2e094f04cd103423bc00960948d1c5cb9870
|
|
BLAKE2b-256 checksum How to use checksums |
8d6240953a0fffeed7165a818dcf8dd75aec3ecefbdc62e00195c15941c0d658
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Security for baisitong2 1.0.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.