当前位置: 首页 > news >正文

长期智能体记忆管理:基于类型化表示解决来源-角色混淆

1. 项目概述:当长期智能体“记混了”自己的身份

最近在折腾一些需要长期运行的智能体项目,比如自动化的客服助手、持续学习的文档分析工具,或者能陪你打游戏、管理日程的“数字伙伴”。这些智能体不像一次性的问答机器人,它们需要记住过去几天、几周甚至几个月里发生的事情,并根据这些记忆做出连贯的决策。听起来很酷,对吧?但实际操作起来,一个幽灵般的问题总会浮现:来源-角色混淆

简单来说,就是智能体“记混了”。它可能把上周用户A抱怨产品Bug的对话内容,错误地当成了今天用户B询问产品功能时,自己应该扮演的“技术支持专家”角色的一部分依据。或者,在分析一系列市场报告时,它无法清晰区分某条数据是来自权威的官方白皮书(高可信度来源),还是来自某个论坛的匿名讨论(低可信度来源),导致最终判断的依据权重完全错乱。这种记忆的“串台”和“失真”,就是“Provenance-Role Collapse”——来源(这条信息从哪来、何时来、为何来)与角色(智能体在处理该信息时应处的状态、身份和任务)发生了不应有的纠缠和坍塌。

这个问题在短期、单次交互的智能体中不明显,因为上下文窗口短,记忆是“一次性”的。但对于长期智能体,记忆是它的核心资产,也是最大的风险源。混乱的记忆会导致智能体行为不一致、决策依据不可靠、甚至产生不符合其设定角色的输出,严重损害其可用性和可信度。

那么,如何为长期智能体构建一个清晰、稳定、不易混淆的记忆系统?这正是“基于类型化记忆表示”所要解决的核心命题。它不是一个具体的工具,而是一套设计哲学和实现框架,旨在通过给记忆打上精细的“类型标签”,从根本上区隔信息的来源与使用场景,防止记忆的坍塌。接下来,我们就深入拆解这个框架的每一个环节。

2. 核心思路:用“类型化”为记忆建立秩序

面对来源-角色混淆这个难题,最直观的解决方案就是“分门别类”。但简单的分类(比如“用户对话”、“系统日志”、“知识文档”)远远不够。我们需要的是一个多维度的、结构化的类型系统,能够同时刻画记忆的多个属性。

2.1 理解记忆的多个维度

一条记忆并非一个扁平的字符串,它至少包含以下几个关键维度,这些维度共同决定了它的“类型”:

  1. 来源:这条信息是如何产生的?

    • 直接交互:与用户或环境的实时对话、操作记录。
    • 衍生推理:智能体自身对已有信息进行分析、总结、预测后生成的内容。
    • 外部摄取:从数据库、API、文档中读取的静态知识。
    • 系统反馈:环境对智能体行动的奖励、惩罚或状态变更信号。
  2. 时效性与关联:这条信息在时间线和逻辑链中的位置。

    • 时间戳:精确的创建时间。
    • 会话/任务ID:属于哪一次独立的交互或任务周期。
    • 因果链:这条记忆是由哪条先前记忆触发或推导而来的?它又导致了哪些后续记忆或行动?
  3. 情感/意图角色:这条信息所关联的智能体“心态”或任务目标。

    • 任务角色:当前智能体是在执行“客服解答”、“创意写作”还是“数据分析”任务?
    • 情感状态:在处理这条信息时,智能体被设定或推断应处于何种情感基调(如耐心、严谨、幽默)?
    • 对话角色:在对话中,这条信息是“用户提问”、“智能体回答”还是“系统提示”?
  4. 可信度与权重:这条信息的可靠程度如何,应在决策中占多大比重?

    • 来源权威性:来自权威机构报告 vs. 社交媒体流言。
    • 一致性验证:该信息是否与多条其他独立来源的信息相互印证?
    • 新鲜度衰减:信息是否随时间推移而价值降低(如股价信息)或变化(如政策法规)?

2.2 类型化记忆表示的核心:MemIR

要将上述维度落地,我们需要一个中间表示层,这就是Memory Intermediate Representation的核心思想。你可以把它理解为记忆的“标准化描述文件”或“元数据 schema”。

一个基础的 MemIR 结构可能看起来像这样(以 JSON 为例,便于理解):

{ "memory_id": "conv_20231027_1423_abc123", "content": "用户表示对产品X的延迟问题感到不满,并询问退款政策。", "provenance": { "type": "direct_interaction", "session_id": "sess_20231027_1420", "timestamp": "2023-10-27T14:23:05Z", "source_entity": "user_789", "trigger_event": "user_query" }, "role_context": { "active_role": "customer_support", "sub_role": "complaint_handler", "emotional_tone": "empathetic", "task_id": "handle_refund_inquiry_001" }, "metadata": { "confidence": 0.95, "freshness_decay_hours": 72, "tags": ["complaint", "product_x", "refund"], "related_memories": ["kb_refund_policy_v2", "conv_20231026_1100_def456"] }, "embedding_vector": [0.12, -0.45, ...] // 用于语义检索的向量 }

关键点解析

  • provenance对象严格封装了来源信息,回答了“从哪来”的问题。它独立且完整。
  • role_context对象封装了角色信息,回答了“当时我在以什么身份处理”的问题。它与来源绑定,但逻辑分离。
  • metadata包含了其他辅助类型信息,如可信度、关联性等。
  • embedding_vector是用于基于内容的相似性搜索,但检索时必须结合类型过滤

通过这种结构化的表示,一条记忆就不再是“一段关于投诉的文本”,而是变成了“一个在2023年10月27日14:23,由用户789在客服会话中发起,需要以‘投诉处理’子角色并带有关怀语气来回应的,关于产品X退款问题的交互记录”。当智能体需要回忆时,它可以非常精确地指定:“我需要查找在‘客服’角色下,与‘产品X’相关的所有‘用户直接投诉’类记忆,并按时间倒序排列。” 这就从根本上避免了角色和来源的混淆。

注意:MemIR 的设计没有绝对标准,它取决于你的智能体具体需要应对哪些混淆风险。上述字段是一个起点,你需要根据业务场景裁剪和扩展。例如,一个法律咨询智能体可能需要更精细的provenance(如法条版本、判例年份),而一个创意写作智能体可能更关注role_context中的“风格模仿对象”。

3. 系统设计与实现要点

有了 MemIR 的理论模型,接下来就是如何将其嵌入到一个长期智能体的架构中。一个典型的基于类型化记忆的智能体系统包含以下几个核心模块。

3.1 记忆的写入:捕获与类型标注

记忆的创建不是简单保存文本。它是一系列主动的标注过程。

  1. 实时上下文分析器:在每次与用户或环境交互时,系统需要实时分析当前对话的上下文。这包括:

    • 角色识别:基于对话历史、任务状态和系统指令,判断智能体当前应处的核心角色和子角色。这可以通过一个轻量级的分类模型或基于规则的状态机来实现。
    • 意图与情感分析:分析用户输入或环境反馈的意图,并推断智能体应匹配的情感基调。这为role_context.emotional_tone提供依据。
    • 会话边界检测:准确判断一个新会话的开始和结束,为记忆分配正确的session_id
  2. 来源追踪器:这是一个贯穿系统的后台服务。无论信息来自用户输入、网络爬取、内部数据库查询还是模型自身生成,来源追踪器都需要为其打上provenance标签。对于模型自生成的内容(如推理链),其来源应标记为derived_inference,并明确指向其推理所依据的原始记忆ID。

  3. MemIR 组装器:将分析器、追踪器得到的信息,连同原始内容、时间戳、唯一ID等,组装成符合 MemIR Schema 的完整记忆对象。这里是添加confidence(可信度)和freshness_decay(新鲜度衰减)等元数据的关键环节。例如,对于从权威API获取的数据,confidence可设为0.99;对于模型猜测的内容,confidence可能只有0.7。

实操心得:在写入阶段,宁可过度标注,也不要缺失关键类型信息。一个常见的坑是,只标注了高层级的角色(如“客服”),而忽略了子角色(如“技术客服” vs. “账单客服”),当问题涉及交叉领域时,记忆检索就会变得不精确。初期可以设计一个相对冗余的 MemIR 结构,在运行中通过日志观察哪些字段从未被查询或使用,再进行优化。

3.2 记忆的存储:向量数据库与元数据索引

存储系统需要支持两种主要的查询模式:基于内容的语义搜索基于类型的精确过滤。因此,推荐采用混合存储架构。

  1. 向量数据库存储核心:将 MemIR 中的content字段(或content与关键metadata.tags的组合)通过嵌入模型转换为向量,存入如 Pinecone、Weaviate、Qdrant 或 Milvus 这类向量数据库。这是实现“模糊查找”、“联想记忆”能力的基础。

  2. 关系型/文档数据库存储元数据:将完整的 MemIR 对象(尤其是所有类型化字段)存入如 PostgreSQL、MongoDB 或 Elasticsearch。这些数据库擅长对provenance.typerole_context.active_roletimestamp等字段进行高效的等值查询、范围查询和复杂组合查询。

  3. 双向索引关联:确保每条记忆在向量数据库和元数据库中的记录有一个共同的、唯一的memory_id。这样,可以先通过向量数据库找到语义相关的记忆候选集,再用元数据库对候选集进行精确的类型过滤;或者先通过元数据库锁定特定类型和时间的记忆,再加载其内容进行深入处理。

配置示例(概念性)

  • 向量化模型:选用text-embedding-3-smallBAAI/bge-m3等通用或领域微调的嵌入模型。维度通常选择256或384维,在精度和存储成本间平衡。
  • 向量索引:在向量数据库中配置 HNSW 索引,平衡搜索速度和精度。
  • 元数据索引:在 PostgreSQL 中为provenance.session_idrole_context.active_roletimestamp等高频过滤字段建立复合索引。

3.3 记忆的读取与推理:检索、过滤与融合

这是防止来源-角色混淆的最后一道,也是最关键的一道防线。记忆检索不是简单的“找到最相似的文本”。

  1. 检索-过滤两阶段流程

    • 阶段一:语义检索。根据当前查询(如用户问题)从向量数据库中召回 Top-K(例如20条)最相关的记忆。这一步是“开环”的,可能包含各种来源、各种角色的记忆。
    • 阶段二:类型化过滤。利用当前对话的上下文(当前活跃的角色、任务、会话ID),构建一个类型过滤条件。例如:
      filter_condition = { “provenance.type”: [“direct_interaction”, “external_knowledge”], “role_context.active_role”: current_active_role, “provenance.session_id”: {“$ne”: current_session_id}, # 避免引用本次会话内的记忆造成循环 “timestamp”: {“$gt”: “now-30d”} # 仅最近30天 }
      用这个条件对第一阶段召回的记忆候选集进行过滤,只保留那些类型匹配的记忆。
  2. 记忆评分与重排序:通过过滤的记忆,不能直接等权重使用。需要根据其类型元数据进行综合评分:

    • 基础相关性分:来自向量检索的相似度分数。
    • 可信度加权confidence字段直接作为乘数。
    • 新鲜度衰减:根据freshness_decay规则和timestamp计算衰减因子。例如,新闻类记忆24小时后价值减半。
    • 角色一致性奖励:完全匹配当前role_context的记忆获得加分,部分匹配的获得部分加分。 最终,根据加权总分对记忆进行重排序,选出最相关、最可靠、最合时宜的几条记忆送入后续的推理环节。
  3. 在提示工程中明确区分来源与角色:将筛选和排序后的记忆注入到大语言模型的提示词时,必须清晰地格式化,明确标注每条记忆的来源和角色上下文。

    当前角色:客户支持专家(处理投诉) 当前任务:解答用户关于产品X延迟的退款询问 相关历史记忆: [记忆1 - 知识库 | 角色:通用政策查询] 内容:公司退款政策规定,因产品性能问题,30天内可申请全额退款。 来源:内部知识库(版本2.1), 最后更新:2023-09-01。 [记忆2 - 历史对话 | 角色:客服 | 会话:sess_20231026] 内容:用户ABC曾反馈产品X在高峰时段有延迟,经排查为区域网络问题,提供补偿方案后解决。 来源:与用户ABC的直接对话, 时间:2023-10-26。 [记忆3 - 本次会话 | 角色:客服 | 会话:current] 内容:用户表示对产品X的延迟问题感到不满,并询问退款政策。 来源:用户当前提问, 时间:刚刚。

    这种格式强制模型在生成回答时,能意识到不同记忆的“身份”和“背景”,从而避免将记忆2中的“区域网络问题”解决方案,张冠李戴到记忆3中可能完全不同的个案上。

4. 实战:构建一个抗混淆的客服长期智能体

让我们通过一个简化但完整的例子,看看如何应用上述框架。

场景:一个电商客服智能体,需要处理用户的售前咨询、售后投诉和订单查询。它需要记住不同用户的过往交互,并避免在服务A用户时,被B用户的历史投诉记录影响其服务态度。

4.1 定义MemIR Schema

class CustomerSupportMemIR: def __init__(self, content, user_id, session_type, agent_role, emotional_tone_required, source_type, confidence=1.0): self.memory_id = generate_uuid() self.content = content self.timestamp = datetime.utcnow() # Provenance self.provenance = { “type”: source_type, # “user_query”, “agent_response”, “kb_article”, “order_system_event” “user_id”: user_id, “session_id”: get_current_session_id(), “trigger”: “user_initiated” # or “system_triggered” } # Role Context self.role_context = { “primary_role”: “customer_support”, “sub_role”: agent_role, # “pre_sales”, “after_sales_complaint”, “order_helper” “required_tone”: emotional_tone_required, # “neutral”, “empathetic”, “urgent” “task_goal”: None # 可关联具体任务ID } # Metadata self.metadata = { “confidence”: confidence, “tags”: extract_keywords(content), “related_order_id”: extract_order_id(content), “is_sensitive”: check_sensitivity(content) # 标记是否为投诉、隐私信息 } self.embedding = get_embedding(content)

4.2 实现记忆写入与存储

import pinecone import psycopg2 # 初始化连接 vector_index = pinecone.Index(“customer-memories”) pg_conn = psycopg2.connect(database=“mem_metadata”) def save_memory(memir_obj): # 1. 存储向量 vector_index.upsert(vectors=[(memir_obj.memory_id, memir_obj.embedding)]) # 2. 存储元数据 cursor = pg_conn.cursor() insert_query = “”” INSERT INTO memories (id, content, user_id, session_id, source_type, sub_role, tone, tags, timestamp) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) “”” cursor.execute(insert_query, ( memir_obj.memory_id, memir_obj.content, memir_obj.provenance[“user_id”], memir_obj.provenance[“session_id”], memir_obj.provenance[“type”], memir_obj.role_context[“sub_role”], memir_obj.role_context[“required_tone”], memir_obj.metadata[“tags”], memir_obj.timestamp )) pg_conn.commit()

4.3 实现抗混淆的记忆检索

def retrieve_contextual_memories(current_query, current_user_id, current_sub_role, top_k=5): # 阶段1:语义检索 query_embedding = get_embedding(current_query) raw_memories = vector_index.query(vector=query_embedding, top_k=20, include_metadata=False) memory_ids = [match[‘id’] for match in raw_memories[‘matches’]] # 阶段2:类型化过滤 cursor = pg_conn.cursor() placeholders = ‘,’.join([‘%s’] * len(memory_ids)) filter_query = f“”” SELECT * FROM memories WHERE id IN ({placeholders}) AND user_id = %s # 关键:只检索当前用户的记忆 AND sub_role = %s # 关键:只检索与当前角色相符的记忆 AND source_type != ‘agent_response’ # 可选:过滤掉自身之前的回答,避免重复 ORDER BY timestamp DESC LIMIT %s “”” cursor.execute(filter_query, memory_ids + [current_user_id, current_sub_role, top_k]) filtered_memories = cursor.fetchall() # 阶段3:格式化用于提示词 context_parts = [] for mem in filtered_memories: context_parts.append( f“[记忆 - 用户{mem[2]} | 角色:{mem[5]} | 来源:{mem[4]}]\n" f"内容:{mem[1]}\n" f"时间:{mem[8]}\n" ) return “\n---\n”.join(context_parts)

4.4 集成到智能体流程

def handle_customer_request(user_query, user_id): # 1. 分析当前上下文,确定角色 current_sub_role = classify_sub_role(user_query) # 例如 “after_sales_complaint” current_tone = determine_required_tone(user_query) # 例如 “empathetic” # 2. 检索相关且类型安全的记忆 context = retrieve_contextual_memories(user_query, user_id, current_sub_role) # 3. 构建系统提示,明确角色和记忆背景 system_prompt = f“”” 你是一名电商客服助手。当前身份:{current_sub_role}。需要保持的语气:{current_tone}。 当前服务用户ID:{user_id}。 以下是与当前用户和当前角色相关的历史交互记录,供你参考: {context} 请基于以上信息,专业、友好地回应用户的最新请求: 用户说:{user_query} “”” # 4. 调用LLM生成回复 response = call_llm(system_prompt) # 5. 将本次交互作为新记忆保存,并关联正确的类型 new_memory = CustomerSupportMemIR( content=user_query, user_id=user_id, session_type=“user_query”, agent_role=current_sub_role, emotional_tone_required=current_tone, source_type=“user_query” ) save_memory(new_memory) return response

通过这个流程,智能体在服务用户A时,绝不会混入用户B的历史记录。当它处理投诉时,检索到的记忆都是“售后投诉”角色下的历史,而不会被“售前咨询”的记忆干扰。这就是类型化记忆在实战中防止来源-角色混淆的效果。

5. 常见问题与进阶优化

在实际部署中,你可能会遇到以下问题,这里提供一些排查思路和进阶技巧。

5.1 性能与成本考量

  • 问题:向量化和元数据联合查询,尤其是面对海量记忆时,延迟和成本可能很高。
  • 排查与优化
    1. 分层存储:将记忆分为“热记忆”(近期高频访问)和“冷记忆”(历史低频访问)。热记忆使用快速但昂贵的存储(如内存向量数据库),冷记忆可归档至对象存储,并只保留元数据和低精度向量。
    2. 元数据预过滤:在向量检索之前,先利用元数据库进行一轮粗筛。例如,先限定时间范围(最近7天)和用户ID,再将缩小范围后的记忆ID列表送给向量数据库进行相似性查询。这能极大减少向量计算量。
    3. 向量索引优化:调整向量索引的参数(如 HNSW 中的ef_constructionM)。更高的值带来更高的召回率但更慢的构建和查询速度。需要通过基准测试找到业务可接受的平衡点。
    4. 记忆摘要与压缩:对于长对话,不必保存每一句原文。可以定期(如一个会话结束后)用LLM生成一个结构化摘要(包含了关键事实、用户情绪、解决方案),并将摘要作为一条新的“衍生推理”类记忆保存,原始详细对话则可降级存储或删除。这能显著减少存储和检索负担。

5.2 类型定义的动态性与演化

  • 问题:预先定义的角色和来源类型可能无法覆盖所有未来场景。例如,突然需要处理一种新型的“跨界咨询”(既涉及技术又涉及账单)。
  • 排查与优化
    1. 设计可扩展的Schema:在MemIR的role_contextprovenance中预留custom_tagsattributes字段,用于容纳未预见到的类型信息。
    2. 聚类发现新类型:定期对记忆的嵌入向量进行无监督聚类分析。如果发现总有一簇记忆无法被现有类型很好地描述,这可能预示着一个新的角色或来源类别正在形成。可以人工审核这些簇,定义新类型,并回溯性地为相关记忆打上标签。
    3. 基于上下文的动态角色:不要让角色标签完全静态。可以设计一个轻量级模型,根据当前对话的实时上下文,动态微调或加权已有的角色标签。例如,基础角色是“客服”,但在对话中检测到大量技术术语时,可以动态增加“技术专家”的权重,从而在检索时同时考虑这两种角色相关的记忆。

5.3 评估与调试

  • 问题:如何知道我的类型化记忆系统是否真的缓解了混淆?效果如何衡量?
  • 排查与优化
    1. 构建测试集:人工构造或从历史日志中提取一批“易混淆”的对话案例。例如,用户A先投诉后咨询,用户B有类似咨询但背景不同。
    2. 定义评估指标
      • 角色一致性:评估智能体的回复是否符合其被设定的角色(可通过另一个LLM或规则判断)。
      • 来源引用准确性:检查智能体回复中引用的历史信息,是否确实来自正确的用户和会话。
      • 混淆错误率:在测试集上,系统将用户A的记忆错误用于用户B对话的比例。
    3. A/B测试:在线上流量中,分桶对比使用基础记忆检索(无类型过滤)和类型化记忆检索的效果,核心业务指标(如问题解决率、用户满意度)是否有显著提升。
    4. 记忆检索可解释性日志:详细记录每一次记忆检索的过程:原始查询、向量检索返回的Top-K结果、类型过滤条件、过滤后的最终结果。当出现错误回复时,这些日志是诊断问题是出在检索、过滤还是提示工程环节的关键。

5.4 安全与隐私

  • 问题:记忆库中包含大量用户交互数据,如何防止隐私泄露和滥用?
  • 优化实践
    1. 记忆隔离与访问控制:在元数据层严格实施基于user_id的访问控制。确保检索函数永远包含user_id = current_user_id的硬性过滤条件。
    2. 敏感信息脱敏:在记忆写入前,对内容中的个人信息(邮箱、电话、地址)进行自动脱敏或哈希化处理。可以在metadata中标记is_sensitive=True,并在后续使用中对此类记忆的引用施加更严格的限制。
    3. 记忆遗忘机制:实现合规的记忆清理策略。除了基于时间的自动过期,还应支持根据用户请求“删除我的所有历史数据”,这需要能根据user_id在向量库和元数据库中彻底删除所有相关记录。

长期智能体的记忆管理是一个持续迭代的过程。类型化记忆表示提供了一个坚固的框架来对抗来源-角色混淆,但它不是一劳永逸的解决方案。你需要像训练一个员工一样,持续观察智能体的“记忆”表现,调整你的“类型标签体系”和“检索过滤规则”,才能让它真正成为一个可靠、可信、长期的合作伙伴。

http://www.cnnetsun.cn/news/4118920.html

相关文章:

  • 特斯拉智能百叶窗HVAC系统:软件定义汽车座舱环境控制新范式
  • 什么是 RAG 中的分块?为什么需要分块?
  • Axure 汉化原来这么简单:axure-cn 中文语言包 9/10/11 全版本速通指南
  • 锤子助手第124个开关:启用自动下载图片的位置、安全验证与图片隐私边界
  • Capture软件原理图信号联通笔记
  • 电脑掌柜库存管理实战:进销存、库存预警、供应商管理一条龙,0基础电脑店老板 5 分钟上手
  • Unity独立开发艺术展馆漫游:从基础功能到工程化实战
  • XMC1300 ADC读数不稳?从硬件到软件的完整排查与优化指南
  • Python自动化:基于文件名与正则表达式批量分类PDF发票文件
  • 计算机单片机毕设实战-基于 STM32 单片机的防干烧多模式烧水控制系统设计 基于 STM32 的自动手动双模式智能出水装置设计(012104)
  • 构建自主AI智能体的因果推理引擎:反事实思考与时间一致性
  • 算法竞赛制胜关键:构建高效数据结构工具箱,实现降维打击
  • 调度器多副本,不引 ZooKeeper:DB CAS + slot 分片就够了
  • RTL8720DN双模物联网SoC开发:从硬件架构到低功耗实战
  • 脑机接口实战:用Python实现脑电波意念控制与信号处理
  • Sib:用Git版本控制管理AI对话历史的命令行LLM客户端
  • 1.6T光模块:技术演进、市场现状与工程挑战深度解析
  • 【YOLO26创新改进】TIP顶刊 2023 | Conv创新改进篇 | 利用 CSFCN 上下文与空间特征校准网络,使网络能够获得更准确的语义信息,适合目标检测、语义分割、图像分割任务,高效涨点
  • TrenchesWIP:一战战术射击游戏入门指南与BOT对战技巧
  • 信息技术EI期刊发表全攻略:从选刊到检索的实战指南
  • B站视频下载终极指南:免费开源工具一键保存4K大会员与充电专属视频
  • 华为认证高频易错题库解析:攻克IP计算、网络设备与协议核心难点
  • Python实现LSTM时间序列预测教程
  • 让2011年的老Mac跑上macOS Sequoia:OpenCore Legacy Patcher 一次点亮的完整上手指南
  • 电视浏览器(CCTV_Viewer):观看央视卫视直播的安卓 TV 盒子
  • Go源码分析:Mutex与读写锁实现
  • AI如何从混沌中看见世界?解析非结构化数据处理的算法演进与工程实践
  • 保时捷Taycan核心技术解析:800V架构与两速变速箱如何重塑电动性能标杆
  • WSL 2原生Docker环境搭建:告别Docker Desktop,打造高效容器开发平台
  • ExComm:构建抗错多智能体通信,实现测试时稳定扩展