DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

AutoGen 重载终止条件后,用户消息也触发了 TERMINATE

AutoGen 0.7.5 保存再加载 TextMentionTermination 时,sources 过滤可能丢失。对照配置往返,显式构造来源规则,并区分 reset 与会话状态。

原本只允许 writer 结束会话,配置保存再加载后,用户发一句包含 TERMINATE 的话也会让团队提前停止。若使用的是 TextMentionTermination(..., sources=["writer"]),需要比较加载前后的来源过滤配置,而不是先给每次运行加 reset()。

本文针对 Python 的 autogen-agentchat 0.7.5,示例使用 Python 3.13。这个版本的 TextMentionTermination 在运行时支持 sources,但组件配置只保存 text,dump_component() 再 load_component() 会丢掉来源限制。其他版本应先检查实际导出的配置,不直接套用这一结论。

比较保存前后的行为

下面的完整示例只检查本地消息,不调用模型,也不启动团队:

import asyncio
from autogen_agentchat.conditions import TextMentionTermination
from autogen_agentchat.messages import TextMessage

async def main() -> None:
    original = TextMentionTermination("TERMINATE", sources=["writer"])
    user_message = TextMessage(
        content="请说明 TERMINATE 的含义", source="user"
    )

    assert await original([user_message]) is None
    config = original.dump_component()
    print(config.config)

    restored = TextMentionTermination.load_component(config)
    stop = await restored([user_message])
    print(stop)

if __name__ == "__main__":
    asyncio.run(main())

在 0.7.5 的配置实现中,导出内容只有 text,没有 sources。恢复时只调用 TextMentionTermination(text=...),来源退回默认的 None,于是任何来源都可以触发匹配。这个版本按消息文字中的子串判断,不要求整条消息恰好等于 TERMINATE;上面的中文句子也包含这个词。

实际定位时同时查看停止原因和导出的配置。团队也可能因为 MaxMessageTermination、最大轮次或其他组合条件而停止,不能只凭“停止得早”认定来源过滤丢失。若配置本来就未设置 sources,来自用户的匹配也可能符合原先的规则。

把来源限制留在应用配置里,再显式构造

暂时不能依赖这一版组件往返保存 sources 时,可以由应用保存完整的终止策略,并在构建团队时重新创建条件。下面用 JSON 字符串模拟应用配置的保存和加载;没有写文件或发送请求:

import asyncio
import json
from autogen_agentchat.conditions import TextMentionTermination
from autogen_agentchat.messages import TextMessage

def build_condition(policy: dict) -> TextMentionTermination:
    # 此示例只接受这一种已约定的策略,拒绝缺字段后的隐式放宽。
    if policy.get("text") != "TERMINATE":
        raise ValueError("Unexpected termination text")
    if policy.get("sources") != ["writer"]:
        raise ValueError("Unexpected termination sources")
    return TextMentionTermination(
        text=policy["text"], sources=policy["sources"]
    )

async def main() -> None:
    saved = json.dumps({"text": "TERMINATE", "sources": ["writer"]})
    condition = build_condition(json.loads(saved))

    assert await condition([
        TextMessage(content="请解释 TERMINATE", source="user")
    ]) is None
    assert await condition([
        TextMessage(content="完成。TERMINATE", source="writer")
    ]) is not None

    await condition.reset()
    assert await condition([
        TextMessage(content="TERMINATE", source="user")
    ]) is None

if __name__ == "__main__":
    asyncio.run(main())

writer 是示例中的真实消息来源标识,接入应用时应与允许结束会话的 Agent 名称一致。上面的校验是这项应用策略的限制,不是 AutoGen 所有场景的统一名单。应用允许多个来源或不同终止词时,应按自己的配置契约验证,不能在字段缺失时默认改为所有来源。

如果还要限制消息数量,可以在构建时把这条条件与 MaxMessageTermination 重新组合。0.7.5 的 OR 条件会逐个导出、加载子条件,组合本身不会补回 TextMentionTermination 遗失的字段。只在导出的字典里手工增加 sources 再交给这一版 load_component(),也不能代替显式构造:该版配置模型和加载函数没有采用它。

reset 清理的是状态,不是过滤规则

直接调用一条已经触发的条件,再次调用前需要重置其终止状态。TextMentionTermination.reset() 在这个版本只把 _terminated 设回 False,不会恢复已经丢失的 _sources。

正常团队运行通过终止条件或最大轮次停止时,团队管理器会重置终止条件。因此,这个问题不能整理成“重复调用团队的 run 都要手动 reset”。团队历史、运行状态保存,以及组件配置的保存加载也不是同一件事;为处理来源限制而清空整个团队,可能另外丢掉本来要继续的对话。

验收应覆盖保存前后同一组消息:用户提到终止词仍不停止,指定 Agent 提到终止词可以停止,其他停止条件继续按设计工作。升级前用目标版本核对同样的配置往返,不只看构造函数仍有 sources 参数。若需要回退,恢复对应版本和完整策略配置;不要保留一个缺字段的已导出组件,再期待运行状态恢复会补齐过滤规则。