尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
中学生智能体测穿戴系统实战(二):FastAPI 后端从数据模型、路由分组到 MQTT 接入与 AI 报告引擎的完整实现
系列第 2 篇。上一篇我们把整个项目的业务场景和硬件选型聊清楚了这一篇钻进fitness-server/backend/把后端这一锅主菜完整端出来。代码全部来自我们比赛仓库的真实实现没有任何一段是事后补写的教学示意。一、开篇一份后端要伺候三类完全不同的客户端在动手之前先把一件事想明白——这个后端要同时被三种东西调用Web 管理后台老师/管理员用浏览器打开走标准 HTTPS REST要登录、要 JWT、要按班级做权限隔离。Android 老师端 App本质上和 Web 是同一套 REST API只是 UA 不同复用同一组路由即可。学生手腕上的智能手表电量小、网络不稳、不适合做 OAuth它需要的是零认证、push 一下就走的轻量上报通道——我们用 MQTT 做主通道HTTP 作为兜底。基于这个现实我在main.py里做了非常直白的分层HTTP 业务路由全部收进routers/MQTT 订阅单独放在mqtt_handler.pyJWT 工具函数放在根目录的auth.py注意这是个工具模块不是路由评分与 AI 引擎分别是scoring.py和ai_engine.py。这样浏览器、App、手表三条链路在代码上一眼就能区分不会互相污染。另一个现实约束是并发模型老师在后台同时看一个班 40 个学生的图表、手表在以秒级频率往上报心率、AI 报告还要去打一次大模型接口——这三件事如果走同步阻塞光一个 AI 请求就能把整个事件循环卡死。所以我从头到尾选了全异步栈SQLAlchemy 2.0 的 async session、httpx.AsyncClient 打大模型、FastAPI 的 async def 路由paho-mqtt 虽然是同步客户端也通过run_coroutine_threadsafe桥回主循环避免阻塞主进程。在钻进代码之前先用一张项目 PPT 里的三端总体架构图定位一下后端在整个系统里到底站在哪个位置。手表负责采集、Android App 负责老师现场批改与查看、Web 后台负责运营管理三者最终都汇聚到我们这台 FastAPI 服务器图里右下那一块绿色的服务器就是本篇要拆的对象——它既要给浏览器和 App 暴露 RESTful 接口又要作为 MQTT 订阅方接住手表 push 上来的设备数据还要连数据库、连大模型。下文我们就按这个边界往里拆。二、项目骨架fitness-server/backend/ ├── main.py # FastAPI 入口、lifespan、CORS、路由注册 ├── config.py # pydantic-settings 配置 ├── database.py # SQLAlchemy 异步 engine / SessionLocal / get_db ├── models.py # ORM 数据模型 ├── schemas.py # Pydantic 请求/响应模型 ├── auth.py # JWT、bcrypt、get_current_user、require_admin ├── scoring.py # 体测评分表 calculate_score ├── ai_engine.py # 大模型调用 报告生成 ├── mqtt_handler.py # paho-mqtt 订阅客户端 ├── init_db.py # 一次性建表脚本 ├── requirements.txt ├── fitness.db # 开发用 SQLite 数据库文件 └── routers/ ├── auth.py # 登录 / 注册 / me ├── students.py # 学生 CRUD 批量导入 ├── teacher_class.py # 教师-班级多对多绑定 ├── devices.py # 设备注册 / 解绑 / 心跳 ├── fitness.py # 成绩上传 / 查询 / 手动录入 ├── statistics.py # 总览 / 班级统计 / 趋势 / 排行榜 └── ai_report.py # AI 配置 周报/月报生成 手表端读取上面这棵目录树是个静态视角那运行起来各模块之间怎么互相调用项目 PPT 里那张FastAPI 全异步架构图把组件、REST 端点和 MQTT topic 一次性画清了——这就是后端上线后的真实全景图里可以看到四个核心件FastAPI 本身承载 20 REST 端点、PostgreSQL开发期换成 SQLite、MQTT Brokermosquitto / EMQX、JWT 鉴权中间件。手表往fitness/{device_id}/data和fitness/{device_id}/status两个 topic push浏览器和 App 走/api/v1/*这一组 REST——两条数据通道在FitnessRecord表里汇合。为了把我们这份代码内部的依赖关系再讲透一点我补一张自画的模块关系图FastAPI 进程客户端services —— 业务能力平铺在根目录routers/ —— 协议层MQTTHTTP 兜底HTTP 兜底HTTPWeb 浏览器Android 老师端学生手表auth.pystudents.pydevices.pyfitness.pystatistics.pyteacher_class.pyai_report.pyauth.pyJWT/bcryptscoring.py评分表ai_engine.py大模型调用mqtt_handler.pypaho-mqtt 订阅models.pySQLAlchemy ORMPostgreSQL / SQLite大模型厂商OpenAI/DeepSeek/智谱/豆包/Kimi/MiMo看图有几个要点① routers 只做协议解析和参数校验真正的算分在scoring.py、鉴权在auth.py、调大模型在ai_engine.py②mqtt_handler.py是个独立的订阅入口绕过 routers 直接调scoring.py和models.py但和 HTTP 路由共用同一套 ORM 表③ 大模型是外部依赖通过 httpx 异步调用不阻塞主事件循环。依赖清单requirements.txt挑重点看fastapi0.115.0、uvicorn[standard]0.30.0、sqlalchemy[asyncio]2.0.35、aiosqlite0.22.1、asyncpg0.29.0、python-jose[cryptography]3.3.0、passlib[bcrypt]1.7.4、paho-mqtt2.1.0、httpx0.27.2。整个后端是全异步栈——SQLAlchemy 2.0 asyncpg/aiosqlitehttpx 用来打大模型paho-mqtt 走的是同步客户端但通过run_coroutine_threadsafe桥回主事件循环后面 MQTT 那节会讲。三、配置与数据库连接config.py用的是pydantic-settings一个Settings类把所有环境变量收进来。我把真实字段列一下密钥一律用***占位# fitness-server/backend/config.py class Settings(BaseSettings): APP_NAME: str 中学生体测数据管理平台 APP_VERSION: str 1.0.0 SECRET_KEY: str os.getenv(SECRET_KEY, ***) ALGORITHM: str HS256 ACCESS_TOKEN_EXPIRE_MINUTES: int 1440 # 24h DB_TYPE: str os.getenv(DB_TYPE, sqlite) # sqlite | postgresql SQLITE_PATH: str os.getenv(SQLITE_PATH, fitness.db) POSTGRES_HOST: str os.getenv(POSTGRES_HOST, localhost) POSTGRES_PORT: int int(os.getenv(POSTGRES_PORT, 5432)) POSTGRES_USER: str os.getenv(POSTGRES_USER, ***) POSTGRES_PASSWORD: str os.getenv(POSTGRES_PASSWORD, ***) POSTGRES_DB: str os.getenv(POSTGRES_DB, fitness_db) REDIS_HOST: str os.getenv(REDIS_HOST, localhost) REDIS_PORT: int int(os.getenv(REDIS_PORT, 6379)) MQTT_HOST: str os.getenv(MQTT_HOST, 127.0.0.1) MQTT_PORT: int int(os.getenv(MQTT_PORT, 1883)) MQTT_USER: str os.getenv(MQTT_USER, ) MQTT_PASSWORD: str os.getenv(MQTT_PASSWORD, ) CORS_ORIGINS: list [*] property def DATABASE_URL(self) - str: if self.DB_TYPE sqlite: return fsqliteaiosqlite:///{self.SQLITE_PATH} return (fpostgresqlasyncpg://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD} f{self.POSTGRES_HOST}:{self.POSTGRES_PORT}/{self.POSTGRES_DB})这里有个我必须坦白的点AI 大模型的 API Key 并不在config.py里。原因是我们希望学校管理员可以在 Web 后台随时切换 OpenAI / DeepSeek / 智谱 / 通义 / 豆包 / Kimi / 小米 MiMo不需要重启服务、不需要改环境变量——所以api_key被设计成了数据库里的一张表AIConfig运行时动态加载。这一点后面 AI 报告那节再展开。切换 SQLite → PostgreSQL 只需要改环境变量DB_TYPEpostgresql加上一组POSTGRES_*代码零改动。database.py里 engine 也据此分了个叉# fitness-server/backend/database.py if settings.DB_TYPE sqlite: engine create_async_engine(settings.DATABASE_URL, echoFalse) else: engine create_async_engine(settings.DATABASE_URL, echoFalse, pool_size10, max_overflow20) async_session async_sessionmaker(engine, class_AsyncSession, expire_on_commitFalse) class Base(DeclarativeBase): pass async def get_db(): async with async_session() as session: try: yield session await session.commit() except Exception: await session.rollback() raise finally: await session.close()get_db这个依赖我刻意让它在 yield 之后统一 commit——路由里就只写db.add()、db.flush()不用每个函数都 commit异常自动 rollback干净。四、数据模型models.py一共十张表挑最核心的几张讲。关系上要注意一个细节Student的主键是自增id但对外业务编号student_id学号字符串才是真正被外键引用的那个字段——手表上报、设备绑定、AI 报告全部用学号字符串关联这样换数据库、换主键都不影响设备端协议。表关键字段关系Userusername, password_hash, real_name, role(admin/teacher), phone系统用户TeacherClassteacher_id → users.id, class_id → classes.id教师-班级多对多Schoolname, address1 对多 Class、StudentClassschool_id, grade, class_name, head_teacher1 对多 StudentStudentstudent_id(唯一索引), name, gender(M/F), birth_date, grade, class_id, school_id, height_cm, weight_kg被 Device / FitnessRecord / AIReport 引用Devicedevice_id(唯一索引), student_id → students.student_id, firmware_version, battery_level, last_online多对一 StudentFitnessRecordstudent_id, device_id, test_type, test_date, duration_sec, count, distance_m, time_sec, avg_hr, max_hr, hr_zones(JSON), score, grade, raw_data多对一 StudentAIConfigprovider, model_name, api_key, api_base, is_active全局唯一激活配置AIReportstudent_id, week_start, week_end, report_html(Text), report_summary, provider, model_name多对一 Student举一个FitnessRecord的真实定义片段看字段怎么摆# fitness-server/backend/models.py class FitnessRecord(Base): __tablename__ fitness_records id: Mapped[int] mapped_column(Integer, primary_keyTrue, autoincrementTrue) student_id: Mapped[str] mapped_column(ForeignKey(students.student_id), indexTrue) device_id: Mapped[str | None] mapped_column(String(50)) test_type: Mapped[str] mapped_column(String(30), nullableFalse, indexTrue) test_date: Mapped[datetime] mapped_column(DateTime, nullableFalse, indexTrue) count: Mapped[int | None] mapped_column(Integer) distance_m: Mapped[float | None] mapped_column(Float) time_sec: Mapped[float | None] mapped_column(Float) avg_hr: Mapped[int | None] mapped_column(Integer) max_hr: Mapped[int | None] mapped_column(Integer) hr_zones: Mapped[dict | None] mapped_column(JSONType) score: Mapped[int | None] mapped_column(Integer) grade: Mapped[str | None] mapped_column(String(10)) raw_data: Mapped[str | None] mapped_column(Text) uploaded_at: Mapped[datetime] mapped_column(DateTime, server_defaultfunc.now())count、time_sec、distance_m三个字段并列——因为跳绳是次/分钟、跑步是秒、跳远是米不同项目量纲不一样与其拆表不如在一行里允许其他字段为 NULL。评分时再按test_type挑对应的那一个量去换算。五、路由分组七个 router各司其职main.py里一行include_router注册一个前缀都收在各自文件里# fitness-server/backend/main.py app.include_router(auth.router) app.include_router(students.router) app.include_router(devices.router) app.include_router(fitness.router) app.include_router(statistics.router) app.include_router(teacher_class.router) app.include_router(ai_report.router)1)routers/auth.py—— 登录与 JWT。前缀/api/v1/auth三个端点# fitness-server/backend/routers/auth.py router.post(/login, response_modelToken) async def login(req: LoginRequest, db: AsyncSession Depends(get_db)): ... router.post(/register) async def register(username: str, password: str, real_name: str, role: str teacher, db: AsyncSession Depends(get_db), _Depends(get_current_user)): ... router.get(/me) async def get_me(user: User Depends(get_current_user)): ...JWT 的签发与校验放在根目录的auth.py工具模块里用python-josepasslib[bcrypt]create_access_token({sub: username, role: role})过期时间由settings.ACCESS_TOKEN_EXPIRE_MINUTES24h控制get_current_user依赖OAuth2PasswordBearerrequire_admin是在它之上再做一层role admin的校验。2)routers/students.py—— 学生 CRUD 与批量导入。前缀/api/v1/students。这里有一个贯穿全后端的权限套路——凡是教师登录只能看到自己绑定班级的学生# fitness-server/backend/routers/students.py router.get(, response_modellist[StudentOut]) async def list_students(grade: str | None None, class_id: int | None None, keyword: str | None None, page: int Query(1, ge1), page_size: int Query(50, ge1, le200), db: AsyncSession Depends(get_db), userDepends(get_current_user)): ... router.post(/batch) async def batch_import_students(students: list[StudentCreate], ...): ..._get_teacher_class_ids(user, db)这个小函数在students.py、fitness.py、statistics.py里被复制粘贴了三份——它的逻辑很简单admin返回None表示不限制否则查TeacherClass表拿到这个老师绑定的class_id列表后续 SQL 一律where(class_id.in_(...))。3)routers/teacher_class.py—— 教师-班级绑定。前缀/api/v1/teacher-class。核心端点是GET /my-classes、POST /bind、DELETE /unbind以及管理员专用的GET /bindings、POST /admin-bind、DELETE /admin-unbind后面这三个走Depends(require_admin)。4)routers/devices.py—— 设备绑定与心跳。前缀/api/v1/devices。这里要特别注意两个无需认证的端点——手表启动时不可能提前知道 JWT# fitness-server/backend/routers/devices.py router.post(/register, response_modelDeviceOut) async def register_device(data: DeviceRegister, db: AsyncSession Depends(get_db)): 设备注册无需认证手表端直接调用 ... router.post(/{device_id}/heartbeat) async def device_heartbeat(device_id: str, battery: int | None None, db: AsyncSession Depends(get_db)): 设备心跳上报无需认证设备直接调用 ...心跳就是把last_online刷成当前 UTC 时间、顺带更新一下电量。运维在/health接口里能一眼看到哪些设备掉线了。5)routers/fitness.py—— 成绩上报与查询。前缀/api/v1/fitness。最关键的三个# fitness-server/backend/routers/fitness.py router.post(/upload, response_modelApiResponse) async def upload_record(data: FitnessRecordCreate, db: AsyncSession Depends(get_db)): 设备端单条数据上传无需认证用 device_id 标识 ... router.post(/upload/batch, response_modelApiResponse) async def upload_batch(data: DeviceUpload, db: AsyncSession Depends(get_db)): ... router.post(/records/manual, response_modelFitnessRecordOut) async def manual_record(data: FitnessRecordCreate, db: AsyncSession Depends(get_db), userDepends(get_current_user)): 老师手动录入成绩 ...不管是 HTTP 上传还是 MQTT 进来落库前都会先按test_type把原始量纲挑出来喂给calculate_score再把score/grade一起写进FitnessRecord——这样前端就不用自己算分。6)routers/statistics.py—— 班级/学生聚合。前缀/api/v1/statistics。四个端点GET /overview总览学生数、记录数、今日新增、平均分、及格率、优秀率、GET /class/{class_id}班级按项目分组统计、GET /trend/{student_id}个人趋势、GET /leaderboard按max(score)排序的排行榜。所有端点都套了一层教师只能看自己班的过滤。7)routers/ai_report.py—— AI 配置与报告。前缀/api/v1/ai。这是本篇最后要重点讲的先埋个伏笔。六、评分引擎scoring.py怎么换算这个文件基本就是一张查表字典。我把《国家学生体质健康标准》里初中段的阈值硬编码成 Python 字典计数类越大越好计时类越小越好逻辑非常直白# fitness-server/backend/scoring.py SCORE_TABLES { jump_rope: { M: {180: 100, 170: 95, 160: 90, 140: 80, 120: 70, 100: 60, 80: 50, 0: 0}, F: {170: 100, 160: 95, 150: 90, 130: 80, 110: 70, 90: 60, 70: 50, 0: 0}, }, 50m_run: { # 秒值越小越好 M: {0: 100, 7.0: 95, 7.3: 90, 7.5: 80, 7.8: 70, 8.0: 60, 8.5: 50, 99: 0}, ... }, sit_reach: { # 坐位体前屈cm M: {23: 100, 20: 95, 17: 90, 13: 80, 9: 70, 5: 60, 1: 50, -99: 0}, ... }, ... } GRADE_MAP [(90, excellent, 优秀), (80, good, 良好), (60, pass, 及格), (0, fail, 不及格)] def calculate_score(test_type: str, gender: str, value: float) - tuple[int, str]: ... is_time_based test_type in (50m_run, 800m_run, 1000m_run) if is_time_based: for threshold in sorted(gender_table.keys()): if threshold 0: continue if value threshold: score gender_table[threshold]; break else: for threshold in sorted(gender_table.keys(), reverseTrue): if value threshold: score gender_table[threshold]; break ...代码里实际支持的项目一共就 8 个jump_rope、50m_run、800m_run、1000m_run、long_jump、sit_ups、pull_up、sit_reach通过TEST_TYPE_LABELS映射成中文名。注意一个单位换算坑HTTP/MQTT 上传时跳远给的是distance_m米但评分表用的是 cm所以在fitness.py和mqtt_handler.py里都做了value data[distance_m] * 100的换算——这种小坑将来扩展新项目时非常容易漏。七、MQTT 接入手表怎么把数据送进来在讲代码之前先看一张项目 PPT 里的端到端数据流图——从学生佩戴手表开始到数据落库评分整条链路是怎么走的这张图把手表端、上报通道、服务器侧评分与回包串成了一个闭环手表本地先做一轮预处理心率过滤、动作识别再通过 MQTT 主推、HTTP 兜底双通道上报服务器收到后立刻跑calculate_score写库再由 AI 引擎在需要时生成报告回流到手表。下面这节讲的就是图里服务器侧这一段的代码。mqtt_handler.py用paho-mqtt 2.1起了一个订阅客户端broker 本身是系统里独立装的 mosquitto默认127.0.0.1:1883不是内嵌的。在main.py的lifespan里随应用一起启动# fitness-server/backend/main.py asynccontextmanager async def lifespan(app: FastAPI): await init_db() await mqtt_handler.start() yield await mqtt_handler.stop()订阅的 topic 通配符只有两条# fitness-server/backend/mqtt_handler.py def _on_connect(self, client, userdata, flags, rc, *args): if rc 0: client.subscribe(fitness//data) # 体测数据 client.subscribe(fitness//status) # 心跳 / 电量手表发布的 topic 形如fitness/{device_id}/datapayload 是一段 JSON里面至少要有student_id和test_type。消息进来后走_on_message因为 paho 是同步回调我用asyncio.run_coroutine_threadsafe把它桥回 FastAPI 的主事件循环再走_handle_fitness_data——后面那段评分、写FitnessRecord、顺手刷一下Device.last_online的逻辑和fitness.py /upload几乎是镜像复制。MQTT 和 HTTP 的分工手表端默认走 MQTT省电、长连接、离线时 mosquitto 可以缓存 QoS1 消息HTTP/api/v1/fitness/upload作为网络条件差时的兜底通道。两条路最终都写同一张fitness_records表评分逻辑共用scoring.calculate_score。MQTTHandler还维护了一个_stats计数器received / processed / failed通过/api/v1/mqtt/status暴露出来运维可以直接看有没有消息堆积。重连策略也在_start_client里设好了reconnect_delay_set(min_delay2, max_delay30)broker 临时挂掉不用人工干预paho 会自己按指数退避重连。这里要坦白一个取舍我们没有在后端内嵌 mosquitto而是假设运维在同机或同网段独立部署一个 mosquitto 服务choco install mosquitto一条命令的事。这样做的好处是 broker 升级、调参、加 TLS 都不会动到 FastAPI 进程坏处是部署时要多管一个进程后面第 7 篇讲 Docker 的时候会把它一起塞进 compose 文件。八、AI 报告引擎这部分是整个后端我最满意的一块。设计目标学校管理员可以在 Web 后台随时换模型、换 Key、换服务商不用重启手表端学生也能一键拉到一份自己的本月分析。数据上有两张表AIConfig保存 provider / model_name / api_key / api_base / is_active和AIReport保存生成出来的 HTML 报告。routers/ai_report.py把这堆事拆成三段配置管理GET/POST /api/v1/ai/config、PUT /config/{id}/activate、DELETE /config/{id}——全走Depends(require_admin)api_key返回给前端时用mask_key()掩码只露前 4 后 4。报告生成POST /reports/generate/{student_id}单人、POST /reports/generate-batch全量——管理员手动触发。手表端读取GET /watch/report/latest、/watch/reports、/watch/analyze——免认证用student_id查询专门给手表这种没有登录态的客户端。真正的调用逻辑在ai_engine.py# fitness-server/backend/ai_engine.py PROVIDER_BASES { openai: https://api.openai.com/v1, deepseek: https://api.deepseek.com/v1, zhipu: https://open.bigmodel.cn/api/paas/v4, qwen: https://dashscope.aliyuncs.com/compatible-mode/v1, doubao: https://ark.cn-beijing.volces.com/api/v3, kimi: https://api.moonshot.cn/v1, mimo: https://token-plan-cn.xiaomimimo.com/v1, } async def call_ai(config: AIConfig, prompt: str) - str: base_url config.api_base or PROVIDER_BASES.get(config.provider, ) url f{base_url}/chat/completions headers {Authorization: fBearer {config.api_key}, api-key: config.api_key, Content-Type: application/json} payload {model: config.model_name, messages: [ {role: system, content: 你是一位专业的青少年体能训练师...}, {role: user, content: prompt}, ], temperature: 0.7, max_tokens: 2000} async with httpx.AsyncClient(timeout60) as client: resp await client.post(url, headersheaders, jsonpayload) resp.raise_for_status() return resp.json()[choices][0][message][content]因为 OpenAI 协议已经是事实标准DeepSeek、智谱、通义、豆包、Kimi、MiMo 全部兼容/chat/completions这个端点所以一份call_ai函数就能搞定七家厂商——只要换base_url和model_name。API Key 是从数据库里那行AIConfig读出来的在代码里、在日志里、在本文里我都不会贴出来前端看到的永远是sk-1234****abcd这种掩码。Prompt 的组织分两步先把这个学生本周或本月的所有FitnessRecord按项目分组、算平均分、最高心率拼成一段中文文本_build_records_summary再交给_build_prompt套上青少年体能训练师的人设要求模型输出适合手表小屏阅读的 HTML 片段只允许 h3/p/ul/li/strong/span并明确要求心率170 或最高200 时给出安全提示。最后generate_weekly_report/generate_monthly_report把模型吐出来的 HTML 做一次 markdown 代码块剥离连同一个一行摘要report_summary一起写进AIReport表。还有一个兜底细节如果管理员还没配 AI Key手表端调用/watch/analyze时generate_monthly_report会走_fallback_monthly_report——用规则出一份纯 HTML 的简要分析保证手表不会因为没配 Key 就白屏。这个兜底的意义在于手表端是学生日常会打开的入口不能因为后台配置缺位就直接报错宁可给一份本月共 12 次测试、平均分 78这种干巴巴的规则分析也不能让用户看到一个 loading 转圈最后超时。九、部署形态一句话开发阶段一行命令起服务uvicorn main:app --host 0.0.0.0 --port 8000 --reload真正的 Dockerfile、docker-compose、Postgres 持久化、mosquitto 配置这些留给本系列第 7 篇专门讲。系列导航《中学生智能体测穿戴系统实战一项目全景、硬件选型与整体架构》《中学生智能体测穿戴系统实战二FastAPI 后端从数据模型、路由分组到 MQTT 接入与 AI 报告引擎的完整实现》本篇《中学生智能体测穿戴系统实战三教师-班级权限模型与多角色数据隔离的设计细节》《中学生智能体测穿戴系统实战四手表端 MQTT 数据上报协议与断网重连策略》《中学生智能体测穿戴系统实战五Web 管理后台与数据可视化大屏的实现》《中学生智能体测穿戴系统实战六Android 老师端 App 开发与手表联调》《中学生智能体测穿戴系统实战七Docker 容器化部署、Postgres 持久化与上线清单》《中学生智能体测穿戴系统实战八多厂商大模型接入与个性化体测报告的调优实录》
RELATED

相关推荐

TickIt: LLM-Driven Online Ticket Escalation for Cloud Support (arXiv:2504.08475)

TickIt: LLM-Driven Online Ticket Escalation for Cloud Support (arXiv:2504.08475)

### 主要内容随着云计算发展,云服务平台的客户支持工单处理愈发重要。传统手动工单升级流程效率低、易出错,现有机器学习模型在处理工单升级时也存在不足。本文提出TickIt,一种基于大语言模型的在线工单升级框架。1. **相关工作**:自动工单升级方法有二元分类、特征工程等,…

📅 2026/10/24 4:05:53
AstrBot Desktop数据目录全解析:.astrbot目录结构与备份迁移完整指南

AstrBot Desktop数据目录全解析:.astrbot目录结构与备份迁移完整指南

【免费下载链接】AstrBot-desktop The desktop edition of AstrBot, designed for fast local installation and convenient access to ChatUI and plugins. 项目地址: https://gitcode.com/gh_mirrors/as/AstrBot-desktop 点击查看 免费下载 AstrBot Desktop 是 A…

📅 2026/10/24 4:00:53
题解:洛谷 P1842 [USACO05NOV] 奶牛玩杂技

题解:洛谷 P1842 [USACO05NOV] 奶牛玩杂技

本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。 欢迎大…

📅 2026/10/24 4:00:53
MORE NEWS

更多资讯

📰

MT5 ZeroMQ通信协议栈:低延迟双向IPC集成方案

简介:本资源是面向量化交易开发者与MT5平台进阶用户的MQL5-JSON-API实战工具包,聚焦于通过ZeroMQ套接字实现MetaTrader 5与外部系统间低延迟、跨语言的实时行情(Bid/Ask、OHLC等)与历史数据交互。它解决了MQL5原生网络能力受限下难…

📰

Linux登录审计:last与lastlog命令实战与排查流程

在 Linux 运维和服务器安全排查中,有一类问题非常典型:某个服务突然异常、某台机器上出现了陌生进程、某个配置被改过,排查到一半却卡在同一个问题上——这段时间到底有哪些账号登录过这台机器?如果平时没有做过登录记录留存和审计…

📰

无人机俯拍行人检测数据集:小目标、多尺度、强鲁棒性训练实战

简介:本资源是面向计算机视觉研究者与深度学习工程师的无人机视角行人检测专用数据集,聚焦于解决低空航拍场景下小目标、多尺度、遮挡严重等检测难点,适用于安全监控、应急搜救、城市交通巡检等实际应用的算法训练与模型评估。压缩包共1895个…

📰

IDEA数据库表一键生成Spring Boot+MyBatis完整代码

简介:这是一款专为IntelliJ IDEA用户设计的独立数据库代码生成插件,面向使用Spring Boot与MyBatis框架的Java开发者,尤其适用于需频繁根据数据库表结构快速搭建CRUD基础模块的中高级后端工程师。插件无需依赖项目源码,可直连数据库…

📰

Linux文件与目录rwx权限详解:从Permission denied到安全配置

在实际运维和开发中,Permission denied大概是出现频率最高的报错之一。很多人第一次遇到它时,第一反应是chmod 777,把所有权限开到最大,问题确实"消失"了,但为什么消失、这样做有什么后果,往往说…

📰

十万条数据导出实战:避免内存爆掉的终极技巧

上周帮同事处理一个后台导出需求,数据量大概十万条出头,一开始用最土的办法拼 CSV,结果运营那边打开就乱码,还丢了几列。后来换成 PhpSpreadsheet,折腾了一下午总算跑通,顺手把踩过的坑记下来,给…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬