LLM应用会话管理:从原理到实践,构建可靠对话记忆系统

news2026/5/13 12:56:10
1. 项目概述一个为LLM应用量身定制的会话管理利器如果你正在开发基于大语言模型LLM的应用无论是聊天机器人、智能客服还是复杂的多轮对话系统那么“会话管理”这个环节大概率是你绕不开的痛点。想象一下用户和你精心调教的模型聊得正欢突然刷新了页面或者半小时后再次回来却发现对话历史一片空白模型完全忘记了之前的上下文。这种体验的割裂感足以让用户瞬间流失。今天要聊的这个项目——iamgagan/llm-session-manager就是为解决这类问题而生的。它是一个专门为LLM应用设计的会话状态管理库核心目标就是帮你把用户与模型之间每一次交互的上下文以一种可靠、高效且易于扩展的方式持久化下来。简单来说它扮演了你LLM应用中的“记忆中枢”。当用户发起一次对话它会创建一个唯一的会话ID并将用户输入、模型回复、以及可能包含的元数据如时间戳、用户ID、使用的模型名称等打包存储起来。下次同一用户或同一会话继续时它能精准地找回历史记录无缝衔接上下文让模型“记得”之前聊过什么。这听起来像是基础功能但自己从头实现尤其是在考虑高并发、多数据源、历史记录裁剪Token限制和安全性时会变得异常复杂。llm-session-manager将这些复杂性封装起来提供了一个简洁的API让你能专注于业务逻辑而不是底层的数据存储和状态同步。这个项目尤其适合那些已经用上了像LangChain、LlamaIndex这类框架或者直接调用OpenAI、Anthropic、本地部署模型API的开发者。它不绑定任何特定的LLM提供商是一个中立的后勤保障角色。无论你的应用架构是简单的单机服务还是分布式的微服务它都能通过适配不同的存储后端如内存、Redis、数据库来满足需求。接下来我们就深入拆解它的设计思路、核心用法以及在实际项目中如何避坑。2. 核心架构与设计哲学2.1 为什么需要独立的会话管理器在深入代码之前我们先要厘清一个根本问题为什么不能直接用HTTP Session或者数据库简单存一下聊天记录这里的关键在于LLM对话的特殊性。首先上下文长度Context Window限制是核心约束。主流模型都有Token上限你不能无限制地把所有历史对话都塞给模型。一个优秀的会话管理器必须支持智能的“记忆裁剪”策略比如只保留最近N轮对话或者根据重要性进行摘要。其次多模态与复杂消息结构。现代LLM对话不仅仅是文本还可能包含图像、文件、函数调用Function Calling的输入输出。存储层需要能灵活处理这些结构化数据。再者并发与状态一致性。在高并发场景下同一会话可能被多个请求同时修改虽然不常见但需考虑需要妥善处理。最后是可观测性与调试。开发者需要能方便地查询、回溯甚至手动修正某次会话的历史这对排查问题至关重要。llm-session-manager的设计正是围绕这些挑战展开的。它采用了经典的仓库模式Repository Pattern将“会话”这个概念抽象成一个核心实体而将数据存储的具体实现是存在内存里还是Redis或是PostgreSQL通过接口进行隔离。这意味着你可以根据应用规模轻松切换存储方案而业务代码几乎无需改动。这种关注点分离的设计让库本身保持了轻量和专注。2.2 核心组件拆解项目结构通常围绕几个核心接口和类展开Session实体这是核心数据模型。它至少包含一个唯一的session_id一个用于存储消息列表的messages字段每条消息通常包含角色role、内容content等以及一些元数据如created_at、updated_at、user_id、metadata自定义键值对等。messages的格式通常会兼容OpenAI的API消息格式system,user,assistant这已成为行业事实标准保证了广泛的适用性。SessionRepository接口定义了数据存取的核心操作例如create(session: Session) - Session创建新会话。get(session_id: str) - Optional[Session]根据ID获取会话。update(session: Session) - Session更新会话通常是追加消息。delete(session_id: str)删除会话。list_by_user(user_id: str)列出某用户的所有会话如果支持。这个接口是抽象的关键所有具体的存储实现如RedisSessionRepository,PostgresSessionRepository都必须遵守它。SessionManager服务类这是开发者主要交互的类。它内部持有一个SessionRepository实例并在此基础上提供更高级、更业务友好的方法。例如start_session(user_idNone, initial_messageNone)初始化一个会话并可选地添加第一条消息。add_message(session_id, role, content)向指定会话添加一条消息。这里就是魔法发生的地方SessionManager在添加消息后可以自动执行上下文窗口管理策略比如检查总Token数是否超限如果超限则触发裁剪。get_messages(session_id, limitNone)获取会话消息可选限制返回条数用于构造最终的LLM API调用提示。ContextWindowManager策略类负责实现具体的裁剪策略。这是一个可插拔的组件。简单策略可能是“保留最近10条消息”更复杂的策略可能涉及计算Token需要集成tiktoken或类似库并优先裁剪掉最早的非系统消息或者尝试对早期历史进行摘要化。项目可能提供几种默认策略并允许用户自定义。存储后端实现这是项目的扩展性体现。InMemoryRepository基于字典的存储用于开发、测试或极小流量场景。重启即丢失。RedisRepository利用Redis的快速读写和过期特性适合生产环境支持高并发和设置会话自动过期TTL。SQLRepository如基于SQLAlchemy使用关系型数据库PostgreSQL, MySQL便于复杂的查询和分析数据持久化最可靠。文件系统或云存储后端对于特定场景也可能提供将会话序列化为文件存储的实现。通过这种架构llm-session-manager在提供强大功能的同时保持了高度的灵活性和可维护性。3. 快速上手指南与基础集成3.1 安装与初始化假设项目是Python实现这是LLM生态中最常见的语言安装通常很简单pip install llm-session-manager接下来根据你的环境选择存储后端。这里以Redis和内存存储为例展示如何初始化。方案A使用内存存储快速开始/测试from llm_session_manager import InMemorySessionRepository, SessionManager # 1. 创建存储仓库 repo InMemorySessionRepository() # 2. 创建会话管理器可以传入自定义的上下文窗口策略这里用默认 manager SessionManager(session_repositoryrepo) # 现在就可以使用了 session manager.start_session(user_iduser_123) print(f新会话创建ID: {session.session_id})方案B使用Redis存储生产环境推荐import redis from llm_session_manager import RedisSessionRepository, SessionManager # 1. 创建Redis客户端 redis_client redis.Redis(hostlocalhost, port6379, db0, decode_responsesTrue) # 2. 创建Redis仓库可以设置默认TTL例如7天过期 repo RedisSessionRepository(redis_client, default_ttl604800) # 3. 创建管理器 manager SessionManager(session_repositoryrepo)注意在生产环境中使用Redis务必配置密码认证、考虑集群模式并为不同环境开发、测试、生产使用不同的数据库索引或键前缀避免数据混乱。3.2 核心API调用示例让我们模拟一个完整的用户与AI助手的交互流程。# 假设 manager 已经初始化 # 场景1用户开始新的咨询 session manager.start_session( user_idaliceexample.com, metadata{app_version: 1.2.0, topic: 技术支持} ) session_id session.session_id # 用户发送第一条消息 manager.add_message(session_id, roleuser, content我的订单#12345为什么还没发货) # 此时模拟LLM生成回复这里用固定文本代替 ai_response 已查询到您的订单#12345目前状态为‘已打包’预计明天发出。 manager.add_message(session_id, roleassistant, contentai_response) # 场景2用户稍后再次提问例如在新的HTTP请求中 # 我们需要根据传来的 session_id 恢复会话 historical_messages manager.get_messages(session_id) print(当前会话历史) for msg in historical_messages: print(f{msg[role]}: {msg[content][:50]}...) # 预览 # 将历史消息构造为LLM API所需的格式 # 注意manager.get_messages() 返回的可能已经是裁剪后、适合直接喂给API的列表 llm_api_messages historical_messages # 假设调用LLM API得到了新的回复 new_ai_response 物流信息已更新您的包裹已由快递员取件运单号是SF123456789。 manager.add_message(session_id, roleassistant, contentnew_ai_response) # 场景3查询用户的所有会话如果功能需要 user_sessions manager.list_sessions_by_user(aliceexample.com) print(f用户Alice共有{len(user_sessions)}个历史会话。)这个流程清晰地展示了会话管理器如何贯穿整个对话生命周期创建、持久化、检索、更新。3.3 与现有LLM框架集成与LangChain集成 LangChain的ConversationBufferMemory等内存类本身就提供了会话记忆功能但通常比较基础且与链Chain绑定较紧。llm-session-manager可以作为更强大、更独立的后端。你可以创建一个自定义的ChatMessageHistory类其内部使用SessionManager来存储和读取消息然后将这个历史对象注入到你的Chain中。from langchain.memory import ChatMessageHistory from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_core.runnables import RunnableWithMessageHistory # 自定义一个适配器将 llm-session-manager 桥接到 LangChain 的 ChatMessageHistory class SessionManagerChatMessageHistory(ChatMessageHistory): def __init__(self, session_manager, session_id): self.manager session_manager self.session_id session_id # 初始化时加载现有消息 self.messages self.manager.get_messages(self.session_id) or [] def add_user_message(self, message: str): self.manager.add_message(self.session_id, roleuser, contentmessage) # 更新本地缓存保持同步或重新从manager加载 self.messages self.manager.get_messages(self.session_id) def add_ai_message(self, message: str): self.manager.add_message(self.session_id, roleassistant, contentmessage) self.messages self.manager.get_messages(self.session_id) def clear(self): # 实现清空逻辑可能调用 manager 的删除或清空消息方法 pass # 在你的链中使用 llm ChatOpenAI(modelgpt-4) prompt ChatPromptTemplate.from_messages([ (system, 你是一个专业的客服助手。), MessagesPlaceholder(variable_namehistory), (human, {input}) ]) chain prompt | llm # 假设你已经有了 session_manager 实例 def get_session_history(session_id: str) - SessionManagerChatMessageHistory: return SessionManagerChatMessageHistory(session_manager, session_id) conversational_chain RunnableWithMessageHistory( chain, get_session_history, input_messages_keyinput, history_messages_keyhistory, )与FastAPI/Flask等Web框架集成 在Web应用中session_id通常可以通过前端在请求头如X-Session-ID或Cookie中传递。后端接口的逻辑非常清晰from fastapi import FastAPI, Header, HTTPException app FastAPI() # ... 初始化 session_manager ... app.post(/chat) async def chat_endpoint( message: str, x_session_id: str Header(None), # 从Header获取会话ID ): # 如果没有session_id则创建新会话 if not x_session_id: session session_manager.start_session() x_session_id session.session_id else: # 验证会话是否存在 if not session_manager.get_session(x_session_id): raise HTTPException(status_code404, detailSession not found) # 1. 添加用户消息到历史 session_manager.add_message(x_session_id, user, message) # 2. 获取处理后的历史消息可能已被裁剪 history_for_llm session_manager.get_messages_for_llm(x_session_id) # 3. 调用你的LLM服务这里为示例 llm_response await call_your_llm_api(history_for_llm) # 4. 将AI回复也存入历史 session_manager.add_message(x_session_id, assistant, llm_response) # 5. 返回响应和session_id如果是新会话前端需要保存 return { response: llm_response, session_id: x_session_id }这种集成模式干净利落将会话状态管理完全从业务逻辑中解耦。4. 高级特性与深度配置4.1 上下文窗口管理与智能裁剪策略这是会话管理器的灵魂功能。直接存储所有对话很快会触及模型的Token上限。llm-session-manager允许你配置裁剪策略。基于条数的简单策略from llm_session_manager import SessionManager, LastNTurnsContextWindow # 只保留最近10轮对话1轮用户消息助手消息 strategy LastNTurnsContextWindow(max_turns10) manager SessionManager(session_repositoryrepo, context_window_managerstrategy)基于Token计数的精确策略这需要集成Token计算库如tiktoken用于OpenAI模型或transformers用于开源模型。from llm_session_manager import SessionManager, TokenAwareContextWindow import tiktoken class OpenAITokenCounter: def __init__(self, model_namegpt-3.5-turbo): self.encoder tiktoken.encoding_for_model(model_name) def count(self, text): return len(self.encoder.encode(text)) token_counter OpenAITokenCounter() # 策略当历史消息总Token数超过3500时优先移除最早的非系统消息直到低于3000。 strategy TokenAwareContextWindow( max_tokens3500, token_countertoken_counter.count, reserve_system_messageTrue # 通常系统提示词很重要保留 ) manager SessionManager(session_repositoryrepo, context_window_managerstrategy)摘要化策略高级对于超长对话更高级的策略是当历史过长时调用另一个LLM对早期对话进行摘要然后用摘要替换掉原始的长文本从而在保留核心信息的前提下大幅节省Token。这需要更复杂的集成但思路可以是在ContextWindowManager的裁剪逻辑中加入一个“摘要生成”的钩子函数。4.2 元数据与自定义字段的妙用Session对象中的metadata字段是一个强大的扩展点。你可以用它来存储各种与会话相关的业务信息。# 创建时附加元数据 session manager.start_session( user_iduser_001, metadata{ language: zh-CN, client: mobile_app_v2, initial_query: 产品价格咨询, assigned_agent: bot_v1, # 记录是哪个AI模型/版本处理的 priority: normal } ) # 在后续流程中可以根据元数据做路由或个性化处理 def route_session(session): if session.metadata.get(language) en: return use_gpt4_model() elif session.metadata.get(priority) high: return use_faster_model_queue() # ...这相当于为每个会话打上了标签便于后续的分析、检索和差异化服务。4.3 多租户与数据隔离在SaaS或平台型产品中你需要为不同客户租户隔离数据。llm-session-manager可以通过在session_id或存储键的设计上融入租户信息来实现。一种常见做法是使用复合键{tenant_id}:{session_id}。在创建SessionRepository时可以传入一个key_prefix或者让所有方法都接受tenant_id参数。class MultiTenantSessionManager: def __init__(self, repo): self.repo repo def _make_tenant_key(self, tenant_id, session_id): return f{tenant_id}:{session_id} def start_session_for_tenant(self, tenant_id, user_idNone): # 创建原始session raw_session Session(user_iduser_id) # 存储时使用复合键 tenant_session_id self._make_tenant_key(tenant_id, raw_session.session_id) raw_session.session_id tenant_session_id # 或者存储原始ID在metadata return self.repo.create(raw_session) def get_for_tenant(self, tenant_id, session_id): key self._make_tenant_key(tenant_id, session_id) return self.repo.get(key)这样在存储层如Redis不同租户的会话数据就会通过键前缀自然隔离。5. 生产环境部署与性能调优5.1 存储后端选型对比存储后端适用场景优点缺点注意事项内存 (InMemory)开发、测试、单机原型、极低流量速度极快零延迟无需外部依赖数据易失重启丢失无法跨进程/服务共享内存占用随会话增长而增长绝对不要用于生产。可用于单元测试。Redis绝大多数生产环境需要高并发、低延迟内存级读写速度支持TTL自动过期数据结构丰富列表、哈希支持集群数据持久化依赖于配置RDB/AOF有数据丢失风险视配置而定内存成本较高1.启用持久化至少AOF每秒同步。2. 设置合理的内存淘汰策略如allkeys-lru。3. 为会话键设置TTL避免无限增长。4. 使用连接池。PostgreSQL / MySQL对数据持久化和复杂查询要求极高的场景需要事务支持数据绝对持久化支持复杂的关联查询和分析ACID事务保证读写速度远低于Redis在高并发写入场景下可能成为瓶颈需要管理数据库连接和模式迁移1. 为session_id、user_id、created_at建立索引以加速查询。2. 考虑使用JSONB类型存储messages和metadata便于灵活查询。3. 对于超大规模考虑分库分表。混合架构超大规模、对性能和持久化都有极高要求结合两者优势架构复杂维护成本高常用模式热数据存Redis提供低延迟读写异步同步到数据库用于持久化和分析。可以在SessionRepository的update方法中实现双写或通过消息队列异步处理。实操建议对于初创项目或中小型应用Redis是首选。它的速度和易用性在大多数情况下都足够了。务必配置maxmemory并设置合理的TTL例如7天或30天。对于金融、医疗等对数据可靠性要求极高的领域可以在使用Redis的同时增加一个到数据库的异步归档确保数据有最终落盘的地方。5.2 性能优化要点序列化/反序列化优化messages列表是频繁读写和网络传输的对象。使用高效的序列化格式至关重要。JSON是通用选择但可以考虑MessagePack或Protocol Buffers它们体积更小编码/解码更快。在Redis中可以将整个Session对象序列化后存为一个字符串或者将messages单独存为一个列表LPUSH/RPUSH后者在仅追加消息时可能更高效。连接管理对于Redis或数据库务必使用连接池。为每个请求创建新连接是性能杀手。在Web框架如FastAPI中可以使用依赖注入在应用启动时创建连接池并在整个生命周期内复用。批量操作与管道在某些场景下例如用户批量导出会话历史需要进行多次读取。利用Redis的pipeline或数据库的批量查询可以显著减少网络往返时间。缓存热点会话对于特别活跃的会话如正在进行的客服对话可以在应用服务器的内存中增加一层短期缓存例如LRU Cache在极短时间内如几秒直接响应减少对中央存储的访问压力。但要注意缓存一致性问题。5.3 监控与可观测性将会话管理器纳入你的应用监控体系。关键指标session_create_rate会话创建速率。session_read_latency读取会话的延迟P50, P95, P99。session_update_latency更新/追加消息的延迟。active_sessions活跃会话数可通过扫描键空间估算需谨慎。storage_size会话数据占用的总存储大小Redis内存使用量DB表大小。日志记录在SessionRepository的关键方法get,update中添加结构化的日志记录会话ID、操作耗时、结果成功/失败。这对于追踪特定用户的对话流和排查问题至关重要。错误处理网络超时、存储连接失败、数据序列化错误等都必须被捕获并妥善处理。通常对于读操作失败可以降级返回空历史或错误提示对于写操作失败需要向上层抛出明确异常由业务逻辑决定是重试、告知用户还是记录后跳过。6. 常见问题排查与实战经验在实际集成和使用llm-session-manager的过程中你肯定会遇到一些坑。以下是我总结的一些典型问题及解决方案。6.1 会话丢失或数据不一致问题现象用户反映对话历史突然清空或者看到不属于自己的对话内容。排查思路检查会话ID的生成与传递这是最常见的原因。确保前端在每次请求中都正确携带了session_id通常放在HTTP Header或请求体中。检查后端是否在创建新会话时正确地将新的session_id返回给了前端。前端存储session_id的方式也很关键LocalStorage比SessionStorage生命周期更长但都要考虑用户清空缓存的情况。可以考虑将session_id也加密后放在用户认证的Token或Cookie中。检查存储后端的持久化配置如果你用的是Redis并且发现重启Redis后数据没了那一定是没配置持久化。检查Redis的appendonly和save配置。在生产环境appendonly yes和appendfsync everysec是推荐的配置。检查键冲突如果session_id生成算法碰撞概率高比如用时间戳或者多租户环境下键前缀设计不当可能导致数据互相覆盖。确保使用足够随机的ID生成器如UUID或Snowflake算法。检查并发写虽然罕见但如果两个请求同时修改同一会话可能会造成消息丢失或顺序错乱。如果使用Redis可以考虑使用WATCH/MULTI/EXEC命令实现乐观锁或者使用LPUSH/RPUSH命令的原子性来追加消息。6.2 性能瓶颈问题现象聊天接口响应变慢尤其是在对话历史很长之后。排查与优化分析慢日志查看Redis或数据库的慢查询日志。对于Redis检查是否执行了KEYS *这样的阻塞命令绝对禁止在生产环境使用应使用SCAN命令替代。检查序列化开销如果messages列表非常大每次读写整个列表的序列化/反序列化会成为瓶颈。考虑改变存储结构例如在Redis中将会话的元数据和消息列表分开存储。消息列表可以用多个小列表按时间分片存储或者只将最新的N条消息存为热数据更早的归档到冷存储。评估裁剪策略你的上下文裁剪策略是否在每次add_message时都触发一次全量的Token计算对于长会话这可能很重。可以优化为增量计算或者只在总条数超过某个阈值后再触发精确的Token计算。数据库索引如果使用数据库务必为session_id(主键)、user_id、created_at等常用查询字段建立索引。对于JSONB字段内的查询也可以建立GIN索引。6.3 上下文裁剪导致“失忆”问题现象用户提到很久之前聊过的事情但AI似乎完全不记得了。原因与解决裁剪策略过于激进如果你设置的是“只保留最近5条消息”那么5轮之前的对话自然就丢了。需要根据你的业务场景和模型上下文长度调整策略。对于需要长期记忆的客服场景可以保留更多轮次如50轮或者启用摘要功能。系统提示词被意外裁剪很多应用会将重要的指令放在rolesystem的第一条消息中。如果你的裁剪策略是“移除最早的消息”并且没有将系统消息排除在外那么这条关键指令可能会被删掉。务必确保你的ContextWindowManager策略配置了reserve_system_messageTrue或者将系统指令设计成不会被裁剪的形式。摘要化策略的副作用如果使用了AI摘要摘要可能丢失了原始对话中的某些细节或情感色彩。需要对摘要的提示词Prompt进行精心设计明确要求保留关键事实、用户诉求和待办事项。6.4 安全与隐私考量敏感信息泄露对话历史中可能包含用户的个人信息、联系方式、地址等。必须确保存储后端数据库/Redis的访问权限受到严格控制。在传输和存储时考虑对敏感字段进行加密。对于展示给管理员的后台要对聊天记录进行脱敏处理。会话ID的安全性session_id本质上是一个访问令牌。如果被泄露攻击者可能读取甚至篡改他人的对话历史。确保session_id足够随机不可预测并且考虑为其设置合理的过期时间通过Redis TTL实现。对于高安全场景可以将session_id与用户的登录态绑定每次操作前验证当前用户是否有权访问该会话。数据合规与清理根据法律法规如GDPR用户可能要求删除其个人数据。你需要提供能力能够根据user_id删除其所有的会话历史。llm-session-manager的list_by_user和delete方法为此提供了基础。此外实现一个定期的数据清理任务自动删除超过一定时间如180天的无效会话是良好的数据治理实践。7. 扩展思路与进阶玩法当你熟练使用基础功能后可以基于llm-session-manager构建更强大的功能。1. 会话分析与洞察将会话数据同步到数据仓库如ClickHouse、BigQuery你可以进行多维度的分析平均对话轮次、最常讨论的话题通过对用户消息进行聚类或关键词提取、用户满意度通过分析情感或后续评分。这能为产品优化提供宝贵的数据支持。2. 实现“会话快照”与“分支对话”允许用户在某一个对话节点创建“快照”然后沿着不同的分支继续提问。这类似于Git的分支概念。可以在Session模型中增加parent_session_id和branch_name字段来实现。这对于探索性、研究性的对话场景非常有用。3. 与向量数据库结合实现长期记忆对于需要真正“长期记忆”的应用可以将每次对话的核心信息通过LLM提取实体、摘要或嵌入向量存储到向量数据库如Pinecone、Weaviate。当用户开启新会话或提到相关话题时先从向量库中检索相关的历史记忆作为上下文注入到新对话中。llm-session-manager管理近期对话向量库管理长期知识两者结合相得益彰。4. 实现多模态会话管理当前的消息格式主要针对文本。如果要支持图像、音频、文件需要扩展Message模型使其能存储对这些多媒体文件的引用如URL或存储路径。存储层也需要能处理这些更复杂的对象。这要求对库进行更深度的定制但设计思路是通用的定义好媒体附件的元数据模型并在裁剪、序列化时妥善处理。5. 构建会话状态机在一些复杂的交互式应用中如订票机器人、游戏NPC对话本身具有明确的流程和状态。你可以基于Session的metadata字段来存储一个状态机例如“state”: “awaiting_departure_date”。业务逻辑根据当前状态来决定下一步该问用户什么问题或者执行什么动作。这将会话管理器从被动的“记录员”升级为主动的“流程协调者”。iamgagan/llm-session-manager这类项目其价值在于它抓住了LLM应用开发中一个普遍、重复且复杂的痛点并通过良好的抽象提供了优雅的解决方案。它可能不是功能最全的但它的设计理念——轻量、专注、可扩展——非常值得学习。在实际项目中你可以直接使用它也可以借鉴其思想来构建符合自己特定需求的会话管理系统。核心在于理解会话状态管理的挑战并设计出可靠、高效且易于维护的架构来应对它。毕竟让AI记住对话是让对话变得真正有用的第一步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2596435.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…