news 2026/11/5 16:42:09

LLM上下文管理实战:从滑动窗口到滚动摘要的context-mode设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LLM上下文管理实战:从滑动窗口到滚动摘要的context-mode设计

context-mode 这词,做 AI 应用的同学应该都不陌生。不管是写聊天机器人、知识库问答,还是做编程助手,凡是需要跑多轮对话的场景,最后一定会撞上同一个问题:模型不记得刚才说过什么,你要手动维护上下文,而且上下文越长,代价越高、越容易乱。说白了,“上下文模式”就是把“历史”变成一种可以在请求之间反复使用、随时裁剪、按需组装的数据形态,而不是每次调用都从零开始。

这篇文章就从我自己做的一个 context-mode 小项目说起,把我踩过的坑、试过有效的方案、还有最终的代码结构全部拆开讲。适合刚接触 LLM 应用开发、对上下文管理一头雾水的同学,也适合已经在用 LangChain 这类框架、但总觉得黑盒不透彻的人。读完之后,你至少能自己写出一套够用的上下文管理模块,不用再遇事就翻官方文档。

1. context-mode 到底在解决什么问题——设计思路拆解

1.1 上下文不是“对话记录”,而是模型的临时工作台

先明确一个底层事实:大模型本身没有记忆。所谓上下文,在工程实现上就是拼接到输入里的那一段文本序列。模型每次推理都是独立进行的,它“看到什么,就基于什么回答”,不记得上一个请求里你说过什么。所以 context-mode 的核心任务,不是“帮模型记住”,而是“在每次请求前,帮模型把该看的信息拼齐”。

我习惯用一个类比来理解这件事:假设你每天换一个新实习生来对接,你不能指望他记得上周你交代过什么,只能每次把项目背景、当前进度、客户偏好、禁忌事项重新讲一遍。context-mode 做的事情,就是保证这堆“交接材料”不会越堆越厚、不会漏掉关键项、也不会把过时的信息混进去。

这个理解非常重要。很多人一开始会把 context 等同于“消息历史”,这没错,但不完整。真正生产环境里,上下文的结构应该比你想象中更复杂:它包含系统设定、用户画像、历史对话、工具返回结果、临时检索片段。如果这些都混在一个 messages 数组里,那每一次请求实际上都在赌,赌模型能自己分辨哪些信息重要、哪些可以忽略。这个赌注,早期 demo 能赢,一旦对话变长、业务变复杂,就会不断翻车。

所以我在设计 context-mode 时,先给自己定了一条原则:上下文必须“分层”,每一层都有自己的生命周期和组装规则。系统层永远在场,对话层按窗口滚动,工具层按需临时插入。这样,任何时候我都能回答一个问题:“模型现在到底看到了什么?”如果一个上下文结构不能让工程师在五分钟内说清楚这个问题,那它迟早会变成一个黑洞。

1.2 三个典型的上下文失控现场

我在实际项目中见过太多上下文失控的案例,总结起来基本逃不出下面三类。

第一类:token 膨胀。对话每轮增长几十到几百 token,感觉不多,但跑到第五十轮的时候,一次请求的输入量可能就超过三千 token。如果还开了工具调用、检索增强,输入量轻松上万。后果很明显:请求变慢、账单变厚、部分 API 直接报错。更隐蔽的是,有些模型的“聪明程度”会随着输入变长而下降,它会更容易被中段信息干扰。这个问题在长对话里几乎一定会遇到,只是早晚和严重程度的区别。

第二类:截断后的“失忆”。最粗暴的方案是只保留最近 N 轮,N 取 10 或 20。实现简单,但代价是早期用户说过的关键信息会被挤出去。我之前有个客服场景,用户在前面反复强调“不要用 Markdown 格式回复”,结果到了第十一轮,模型又开始满屏输出标题和列表。用户当然觉得你很蠢,但问题本质不在模型,而是你根本没把那个约束放进上下文里。

第三类:上下文污染。系统提示、参考文档、历史问答全塞在一起,模型分不清哪些是任务指令、哪些是历史记录、哪些是“举例”。有时候它看了旧回答的措辞,就把旧回答当成新规则来执行;更严重的是多用户共用进程时,上下文串了,A 用户的问题里混进了 B 用户的数据。这类问题最难查,因为它不报错,只是行为怪,往往要排查很久才发现是数据结构的问题。

1.3 我的 context-mode 设计原则:三层分离

第一版 context-mode 我做得特别简单,就是把 messages 数组从左到右拼起来,发给模型就完事。结果跑到二十轮以后,效果和成本双双失控。后来我彻底重构,把上下文拆成了三层:

系统层放的是角色设定、固定规则、业务约束。比如“你是客服助手,回复简洁,不要用 Markdown 格式”“价格一律按美元计算”“遇到违规内容必须拒绝回答”。这一层几乎不变,只在业务规则更新时修改。

对话层放的是真正的多轮聊天记录。这一层不是全量保留,而是拆成两个部分:最新几轮完整保留,更早的内容压缩成摘要。这样无论对话多长,这一层的体积基本稳定。

工具层是可选的临时上下文。需要查数据库时,把查询结果塞进去;需要做 RAG 时,把检索到的文档片段塞进去;不需要的时候,这一层完全不出现。

拆完之后,事务性的规则非常清楚:增删系统层规则,只影响系统消息;对话层的摘要策略调整,只影响 messages 组装逻辑;临时检索内容不会污染长期历史。后面别人问我这个工具到底做了什么,我的回答是:两层缓存加一条管道。摘要缓存和最近对话缓存不动,管道负责把工具数据按需接进来。就这样。

2. 核心细节与方案选型——四种上下文处理方式对比

2.1 全量拼接:最简单,但贵且慢

全量拼接是最天然的做法:把 user 和 assistant 的历史消息全部保留,每次请求都放进 messages 数组。我早期做 demo 时就是这么干的,代码量最小,逻辑最直白,适合快速验证想法。

但它的缺点在长对话场景下非常致命。我实测过一个简单测试:模型一次回复大约 200 token,用户一轮输入大约 50 token,跑到第三十轮时,单次请求的输入 token 已经超过七千,首字延迟明显变慢,账单也跟着肉眼可见地增长。而且很多模型有上下文窗口上限,全量拼接超过上限就直接报错,你不得不回头处理截断问题。所以全量拼接只适合短对话、少量轮次的场景,不适合生产环境。

2.2 固定轮次滑动窗口:稳定但会“丢记忆”

滑动窗口是大多数人第二个想到的方案:只保留最近 N 轮完整消息,超出部分直接丢弃。N 取 10、12、20 的都有。它的好处是请求大小基本稳定,token 消耗可控,实现也简单。

但问题是:模型“失忆”的速度比你想的快。如果用户在第 3 轮说过“我叫张三”,窗口只保留最近 10 轮的话,从第 11 轮开始模型就不知道用户叫张三了。更麻烦的是,这种失忆不报错、不明显,你只会发现模型越来越“健忘”,但很难定位到底丢了哪条关键信息。我之前那个 Markdown 场景就是典型例子——约束被挤出窗口,模型重新开始用 Markdown,用户开始投诉。滑动窗口适合临时会话、客服这类“不太依赖早期信息”的场景;但在需要持续记忆用户偏好、历史决策的对话里,单靠滑动窗口是不够的。

2.3 滚动摘要 + 滑动窗口:早期记忆用摘要兜底

真正进入可生产状态的方案,是“滚动摘要 + 滑动窗口”混合模式。核心思路:最近几轮完整保留,保证模型对当前话题有精细理解;更早的轮次压缩成摘要,保证关键历史信息不丢失。摘要可以由模型生成,也可以由规则提取关键字段。

这个方案的优点很明显:输入不会随对话轮数无限增长,同时早期信息还能以“摘要”的形式参与推理。代价是要多维护一个 summary 字段,并且摘要本身也会引入新的风险——如果摘要生成质量不稳,错误信息会被长期保留在上下文里,反而误导模型。想降低风险,可以在每次生成摘要时,把旧摘要和新消息一起交给模型,让它合并成新版摘要,而不是只基于新消息重新生成。

2.4 检索式上下文:不常用但要心里有数

还有一种相对少见的做法:把历史消息切块、向量化存储,等用户提问时,用相似度检索召回相关片段,再拼进当前请求。这种方法适合超长历史(比如几十万字聊天记录)或知识库问答,能覆盖全历史而不会让输入无限膨胀。

但它的成本和复杂度很高:你需要处理切块策略、向量库、召回阈值、相关性排序等一系列问题。对于多数 AI 应用来说,这不是默认选项。我自己的 context-mode 里预留了检索接口,但默认关闭。一句话总结:如果对话历史超过一万 token,且用户会反复提起早期细节,检索式上下文才值得考虑;否则,滚动摘要方案更划算。

四种方案各有适用场景,我做了一个对比表,方便你直接对照选择:

方案实现成本输入大小信息保持适合场景
全量拼接低线性增长完整demo、短对话、调试
滑动窗口低基本稳定近期完整、早期丢失客服、临时会话
摘要+窗口中稳定重点保留、早期可查多数生产环境
检索式高可控全历史可覆盖长历史知识库、专业问答

2.5 token 预算到底怎么算

项目里踩过几次 token 超限的坑之后,我总结了一个经验公式:可用上下文 = 模型窗口上限 × 0.8 − 系统提示 token − 当前问题 token − 函数定义 token − 工具返回 token。

为什么要留这 20% 的余量,而不是把窗口用满?原因有三个:第一,不同 API 的 token 统计存在偏差,你本地算的和服务端算的可能不完全一致;第二,模型输出也要占上下文窗口空间,如果你把输入塞到 99%,模型几乎没法生成内容;第三,部分模型还会把一些内部指令、格式控制信息偷偷算进 token,你根本看不见。我习惯把输入控制在窗口上限的 80% 以内,剩下 20% 作为安全冗余。

还有一个容易踩的坑:很多人只统计 messages 数组里的 token,却忘了 system prompt 和 function calling 的参数定义也要占空间。尤其是工具定义,写详细了可能上百甚至几百 token,比一条普通对话消息还贵。所以一定要用 tiktoken 或对应 SDK 自带的 count_tokens 方法,把整个 payload 完整统计一遍,再做截断判断。

3. 从零实现一个轻量 context-mode——代码与实操

3.1 核心数据结构

我用的消息格式和主流 API 保持一致:每条消息是 {“role”: “system” | “user” | “assistant”, “content”: str}。context-mode 的关键就是维护好这个序列,并且给它加上裁剪和摘要能力。

下面是核心类的骨架,我会逐个字段解释它是干什么的:

from dataclasses import dataclass, field from typing import Dict, List, Optional @dataclass class ConversationContext: system_prompt: str = "" summary: str = "" max_tokens: int = 8192 recent_rounds: int = 10 messages: List[Dict[str, str]] = field(default_factory=list) def add_user(self, content: str) -> None: self.messages.append({"role": "user", "content": content}) def add_assistant(self, content: str) -> None: self.messages.append({"role": "assistant", "content": content}) def set_summary(self, summary: str) -> None: self.summary = summary def should_trim(self) -> bool: # 消息条数超过最近轮数的两倍,就触发摘要压缩 return len(self.messages) > self.recent_rounds * 2 def to_api_messages(self) -> List[Dict[str, str]]: # 把摘要、系统提示、最近消息按顺序组装成 API 可用的 messages msgs = [] if self.system_prompt: msgs.append({"role": "system", "content": self.system_prompt}) if self.summary: msgs.append({"role": "system", "content": f"历史摘要:{self.summary}"}) msgs.extend(self.messages) return msgs

这里几个字段的作用:

  • system_prompt是永远在场的系统设定,不会因为对话变长被挤掉。
  • summary是早期对话的压缩结果,作为一条 system 消息放在消息序列最前面。
  • recent_rounds控制保留多少轮完整消息。太少了容易失忆,太多了 token 成本高。我自己实测下来,8 到 12 轮是比较平衡的区间,核心业务场景可以适当放大。
  • messages只存“需要完整保留”的最近对话,更早的内容会在should_trim()触发时被压缩进 summary。

to_api_messages()是整个模块最关键的方法:无论内部怎么裁剪,最终交给模型的永远是一个干净、有序的消息数组。你不再需要在上层业务代码里手动拼字符串,这样逻辑统一、排查也容易。

3.2 上下文模式切换:如何把历史压缩成摘要

现在说压缩逻辑。我的触发条件很简单:消息条数超过recent_rounds * 2就触发。为什么是两倍?因为每个“轮次”包含用户消息和助手消息各一条,N 轮就是 2N 条消息。超过这个阈值,说明完整保留的负担变大了,该做一次摘要更新了。

核心逻辑大概是这样:

def trim_and_summarize(self, llm_call) -> None: if not self.should_trim(): return # 把最早的一部分消息取出来,准备压缩 overflow_count = len(self.messages) - self.recent_rounds old_messages = self.messages[:overflow_count] self.messages = self.messages[overflow_count:] # 生成摘要:合并旧摘要 + 旧消息 new_summary = self._generate_summary(llm_call, old_messages) self.set_summary(new_summary)

有一个细节非常关键:生成新摘要时,一定要把旧摘要和新拿出来的消息一起作为输入,而不是单独用新消息重新生成。如果不带上旧摘要,那前几次压缩进去的信息,比如“用户是 VIP”“偏好用表格回复”这些关键字段,就会在这一次压缩里彻底丢光。我见过好几个项目犯这个错,现象就是对话越聊越“失忆”,但每次看单次压缩都好像合理。

3.3 让模型自己生成摘要

摘要可以靠规则,比如“提取所有包含‘我叫/我是/我喜欢/不要’的句子”,但效果很硬。更好用的是让模型自己生成,格式固定、字段可控。

我用的摘要提示词大概是这样的:

你是对话摘要引擎。请阅读用户与助手的对话历史,提取并保留以下信息: 1. 用户身份相关:姓名、称呼、VIP 等级、所在地区; 2. 用户明确表达的偏好和禁忌; 3. 当前未完成的事务和待办事项; 4. 重要业务决策。 输出格式: 用户信息:... 偏好/禁忌:... 待办事项:... 业务决策:... 如果某项没有,写“无”。不要输出其他内容。

注意,生成摘要的调用最好单独走一个小的 model 调用,不要和主对话用同一个 system prompt。如果你把摘要提示词塞进主对话的 messages 里,主模型可能会把它当成对话内容来回复,而不是执行摘要任务。这个坑我踩过一次,后来把所有摘要逻辑收敛到_generate_summary方法里,用独立的请求完成。

3.4 持久化:重启后还能继续聊

上下文管理如果没有持久化,就像聊天软件没有云端消息记录,App 一重启就全部失忆。我早期做工具时忽略了这一点,每次服务重启后用户都要重新交代一遍背景,体验非常差。

持久化方案很简单,用 JSON 就能满足小项目需求。把system_prompt、summary、messages、recent_rounds全部存下来,以session_id命名文件即可:

import json def save_context(ctx: ConversationContext, path: str) -> None: with open(path, "w", encoding="utf-8") as f: json.dump({ "system_prompt": ctx.system_prompt, "summary": ctx.summary, "recent_rounds": ctx.recent_rounds, "messages": ctx.messages, }, f, ensure_ascii=False, indent=2) def load_context(path: str) -> ConversationContext: with open(path, "r", encoding="utf-8") as f: data = json.load(f) ctx = ConversationContext( system_prompt=data["system_prompt"], summary=data["summary"], recent_rounds=data["recent_rounds"], ) ctx.messages = data["messages"] return ctx

生产环境可以考虑换成 Redis 或数据库,但逻辑是一样的:以会话 ID 为维度,保存上下文快照。这里有一个很容易忽略的点:保存时要把摘要和消息分开存,不要混在一条消息里。否则读取之后你无法区分哪些是历史摘要、哪些是真实对话,后续压缩逻辑会乱套。

3.5 完整调用示例

把上面的模块串起来,一个使用 context-mode 的请求流程大概是这样的:

ctx = ConversationContext( system_prompt="你是客服助手,回复简洁,不要用 Markdown 格式", max_tokens=8192, recent_rounds=10, ) load_context(ctx, "session_123.json") ctx.add_user("你好,我要退订单 12345") api_messages = ctx.to_api_messages() # 组装好交给模型 reply = call_llm(api_messages) ctx.add_assistant(reply) if ctx.should_trim(): ctx.trim_and_summarize(llm_call) save_context(ctx, "session_123.json")

这个流程里有几个顺序不能乱:先组装再调用、先调用再保存、保存前判断要不要压缩。因为压缩需要在拿到最新模型回复之后再做,否则刚说出口的话可能被直接压进摘要里,丢失细节。运行一段时间之后你会形成肌肉记忆,新增功能的时候只要问自己一个问题:模型这次调用会看到哪些上下文?如果答案含糊,那就是组装逻辑有问题。

4. 常见问题与排查技巧实录

4.1 现象:模型答非所问,且和之前说的自相矛盾

遇到这种情况,我第一反应不是怀疑模型,而是打印to_api_messages()的结果,看模型到底“看到”了什么。很多时候问题是:摘要不存在、摘要过期、或者系统提示被窗口挤出。

排查步骤很简单:

  1. 打印完整 messages 数组,确认摘要是否在第一位;
  2. 核对摘要内容是否包含关键背景(身份信息、偏好、未完成事项);
  3. 检查是不是有代码在某个环节直接 append 了一条 user 消息,破坏了原有的序;
  4. 检查摘要是否在多次压缩后累积了错误信息。

我之前有过一次很奇怪的“失忆”现象:用户从头到尾没换过,但模型在二十轮之后突然忘了业务规则。排查半天发现,是某次代码升级把system_prompt的赋值写到了空字符串,结果系统层消息就没了。这类“静默丢失”最坑人,因为代码不报错,只能靠打印上下文的习惯才能发现。

4.2 现象:请求直接报 token 超限

大多数 token 超限不是因为你消息真的超过窗口上限,而是因为你只统计了消息内容,忘了还有函数定义、工具返回结果和系统提示。特别是如果你用了 function calling,工具 schema 的描述经常写得特别详细,一个工具定义几十行描述,加起来可能几百 token,很容易被忽略。

我的排查思路是:

  1. 把发给模型前整个 payload 一次性 dump 出来;
  2. 用官方计数工具逐字段统计:system_prompt、summary、messages、functions、tools;
  3. 看哪一块占比最高,优先压缩那部分;
  4. 超限时先裁剪messages的早期部分,再重试。

记住一个原则:凡是准备发给模型的东西,都要纳入 token 预算,没有例外。

4.3 现象:长对话响应变慢

大部分人以为响应慢是模型推理变慢了,其实在长对话场景里,输入 token 太大是首字延迟升高的主因。模型需要先处理完前面那一大堆上下文,才开始生成第一个 token。

这种问题要怎么排查?给每个 API 调用加日志,记录三个指标:输入 token 数、输出 token 数、首字延迟。跑上一天,你就能画出趋势图,看到“当输入 token 超过多少时,首字延迟开始明显恶化”。然后对应调整recent_rounds和摘要触发阈值。

我实测下来,把输入控制在两千 token 以内时,首字延迟基本平稳;超过五千 token 后会逐步恶化。不同模型表现不同,但方向一致:输入越小,首字越快。所以如果业务对响应速度敏感,不要过度追求“完美记忆”,给摘要和窗口都留一点压缩空间。

4.4 现象:并发下上下文串了

这是最隐蔽的问题之一。如果你用进程内共享变量存储对话上下文,那多个用户同时访问时,A 用户的消息可能被 B 用户的请求带到模型里。现象像“撞鬼”一样,今天好端端排查,明天又复现。

根本原因是把全局唯一的对象当会话级状态用。解决办法是让每个ConversationContext实例只属于一个 session,用session_id作为 key 存到一个字典或者 Redis 里,绝不能跨会话共享。代码上可以加一层很薄的 SessionManager,统一做 acquire 和 release,避免业务代码里随手 new 一个实例导致上下文丢失。

我做了一个速查表,方便你以后遇到问题直接对号入座:

现象常见原因优先检查项
模型失忆/前后矛盾窗口裁剪丢早期信息summary 策略、系统提示是否完整
请求报 token 超限token 预算只算了消息整个 payload 的 token 统计
响应变慢输入 token 过大日志里的输入 token 数和首字延迟
上下文串话ctx 实例被共享session_id 是否隔离、存储是否加锁
摘要里出现错误信息摘要生成时没合并旧摘要压缩逻辑的输入是否包含旧摘要
重启后忘记一切没有持久化上下文save/load 是否在正确时机调用

4.5 我自己的踩坑清单

最后整理几条真金白银换来的经验:

  • 摘要别用对话模型的主 prompt 来生成。单独写一个摘要专用提示词,否则模型会把摘要任务当成聊天内容回复,返回一长串废话,摘要字段越存越乱。
  • 新摘要必须基于旧摘要加新消息生成。这是最容易犯的错误,不带上旧摘要就等于每次都把所有历史推进一个碎纸机。
  • 系统提示词如果很长,建议拆成多条 role=system 消息,而不是塞成一条长文本。有些模型对单条消息长度比较敏感,拆开后更稳定。
  • 我会把“用户身份、明确偏好、未完成事项”这三类信息作为摘要的强制保留字段。哪怕其他细节全丢,这三类不丢,对话质量基本不会崩。
  • 每次 API 调用前后打点记录三项指标:消息轮数、输入 token 数、摘要长度。这个日志在排查和调优时价值非常大,远胜过任何花哨的可视化面板。跑几天你就能知道自己项目的临界点在哪,什么时候要压缩、什么时候要扩充,完全不用靠猜。

我自己的体会是,context-mode 做得好不好,关键不在某个算法有多高级,而在于你对“哪些信息值得保留”这件事有没有清晰判断。有一次线上对话,用户在第 20 轮提到“我是 VIP”,第 35 轮问“我有没有折扣”,如果摘要没有在这个中途把这个信息捞进来,模型必答错。后来我坚持在摘要里强制保留“用户身份”这个字段,类似的问题再也没出现过。这个设计很小,但对所有业务的上下文管理都通用:上下文的价值,是把关键信息在以正确的顺序送到模型面前,而不是把一切都堆给它。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/24 5:17:12

Ponytail插件:让碎片笔记自动收束成集

如果你现在随便打开一个搜索引擎,敲下ponytail这个词,头几页大概率会被各种各样的发型教程占满,从“三分钟扎出蓬松马尾”到“韩式慵懒低马尾图解”。但如果你混的是笔记工具、自动化脚本、知识管理这类圈子,会发现ponytail其实是…

作者头像 李华
网站建设 2026/11/4 23:39:11

Mac本地RAG实操指南:从PDF到API的完整工程闭环

1. 这不是“又一个AI教程”,而是帮你绕开90%初学者踩坑的RAG实操地图你搜过“RAG教程”“RAG实战”“怎么在mac上搭建rag知识库”,点开十几篇,发现要么是调用几行LangChain代码就收工,要么直接甩出一整套企业级架构图——中间那条…

作者头像 李华
网站建设 2026/10/25 8:40:58

SpringBoot招聘平台源码解析:从环境搭建到二次开发的完整实践

简介:这是一套基于Java、MySQL与SpringBoot开发的毕业生信息招聘平台完整源码,面向计算机相关专业学生、课程设计开发者及需要搭建招聘系统的初学者,可用于毕业设计、课程作业或二次开发学习。系统围绕线上招聘场景设计,涵盖空中宣…

作者头像 李华
网站建设 2026/10/25 9:45:25

Windows下用MSYS2一键安装Verilator并完成数字电路仿真

最近在Windows上重新搭数字电路仿真环境,第一件事就是装Verilator。很多做FPGA或者数字IC验证的朋友都遇到过这个尴尬:官网不提供直接的Windows安装包,GitHub上虽然有release,但想要拿到好用版本要么自己啃源码编译,要…

作者头像 李华
网站建设 2026/10/27 1:40:42

Agent-Reach:AI Agent 如何通过 CLI 真正触达任务与高并发实践

1. 从"Agent-Reach"这个名字说起:它到底想解决什么问题第一次看到"Agent-Reach"这个命名,我的直觉是它想表达两层意思:Agent 代表智能体,Reach 代表触达、延伸、够得着。合在一起,大概率是在讲&qu…

作者头像 李华
网站建设 2026/10/31 19:07:23

智能体工程化落地:从Demo到生产的容错、可观测性与业务接入实践

1. 从本周趋势榜看智能体的阶段切换这周的 GitHub Trending 榜单我翻了两遍,最大的感受是:智能体这个赛道正在经历一次明显的阶段切换。前几个月大家还在比谁的 Demo 更惊艳、谁的框架支持的工具更多、谁的多智能体协作演示更花哨,而这周冲上…

作者头像 李华