• [技术干货] 从零开始理解 Agent(六):给 Agent 做一次"断舍离"——上下文压缩
     欢迎阅读「从零开始理解 Agent」系列 —— 我们将从一个极简开源项目 nanoAgent[1] 出发,逐层拆解 OpenClaw / Claude Code 等 AI Agent 背后的全部核心概念。 第一篇:底层原理,只有 115 行 —— 工具 + 循环第二篇:记忆与规划 —— 182 行第三篇:Rules、Skills 与 MCP—— 265 行第四篇:SubAgent 子智能体 —— 192 行第五篇:多智能体协作与编排(本文)—— 270 行第六篇:上下文压缩(本文)—— 169 行第七篇:安全与权限控制 —— 219 行 ★ 项目地址:cid:link_6作者:十一前五篇我们不断给 Agent 加能力:工具、记忆、规划、Rules、SubAgent、Teams……但有一个问题我们一直在回避:Agent 的对话历史会无限增长,直到撑爆 LLM 的 context window。这不是"将来可能遇到的问题",而是"用 Agent 干稍微复杂点的活就一定会遇到的问题"。今天我们回到 agent.py 的极简基础上,只加一个函数(约 30 行),实现最简单的上下文压缩。★ 关于本篇代码的说明:和第四、五篇一样,本篇的 agent-compact.py 是我们新开发的文件(GitHub 源码[2]),不在 nanoAgent 原始仓库中。它基于第一篇的 agent.py,只新增了一个 compact_messages() 函数来演示压缩机制。为了让压缩逻辑尽可能清晰,没有加入记忆、规划、Rules 等其他功能。一、先搞清楚问题:为什么 messages 会爆?回忆第一篇中 Agent 的核心循环。每一轮循环,messages 列表都会新增至少两条消息:第 1 轮: messages += [LLM的回复, 工具的返回结果]第 2 轮: messages += [LLM的回复, 工具的返回结果]第 3 轮: messages += [LLM的回复, 工具的返回结果]...假设一个任务需要 Agent 调用 15 次工具(对于"找到所有 Python 文件、统计行数、排序、写入报告"这样的任务完全正常),messages 就会累积到 30+ 条,其中每条工具返回结果可能包含几百行的命令输出。而任何 LLM 的 context window 都是有限的。不管是几万 tokens 还是几十万 tokens,只要 Agent 读几个大文件(每个几千行)、执行几次 grep 返回大量结果、再来几轮工具调用——窗口就会被迅速填满。尤其是本地部署的小模型,context window 往往只有几千 tokens,几轮循环就会触顶。★ 你可能会想:"现在的模型 context window 越来越大了,还需要压缩吗?" 需要。窗口变大只是推迟了问题,没有消除问题。而且更长的上下文意味着更高的 token 费用、更慢的响应速度、以及 LLM 在超长文本中"迷失重点"的风险(即 lost in the middle 问题)。当 messages 超过 context window,API 直接报错:context_length_exceeded。Agent 挂了,任务半途而废。二、能不能不压缩?在看解决方案之前,先想想有没有其他出路:方案 A:用更大 context window 的模型。 能缓解,但不能根治。窗口再大,Agent 读几个大文件、跑几次搜索也会填满。而且更大的窗口意味着更高的 token 费用、更慢的响应速度、以及 LLM 在超长文本中丢失重点的风险。方案 B:限制最大循环次数。 第一篇中的 max_iterations=5 就是这个思路。但这只是把问题从"撑爆"变成了"做不完"——复杂任务就是需要很多轮。方案 C:截断工具返回结果。 比如 bash 命令输出超过 1000 字符就截断。能减缓增长速度,但治标不治本,而且截断可能丢失关键信息。方案 D:压缩旧的对话历史。 把早期的详细对话压缩成一段摘要,只保留要点。Agent 继续工作时,靠摘要"回忆"之前做了什么,靠最近几条消息保持当前操作的精确上下文。方案 D 就是上下文压缩(Context Compaction)。它不需要换模型,不限制能力,不丢失关键信息——用 LLM 自己来总结自己的历史,然后轻装上阵继续干活。三、压缩的原理:一张图看懂压缩前的 messages(30 条,快爆了):┌────────┐│ system │ ← 永远保留├────────┤│ user │ ← 最初的任务│ assist │ ← LLM 调用了 bash│ tool │ ← bash 输出了 200 行文件列表│ assist │ ← LLM 调用了 read_file│ tool │ ← 文件内容 500 行 ─┐│ assist │ ← LLM 决定统计行数 ││ tool │ ← 统计结果 │ 这些旧消息│ assist │ ← LLM 调用了 grep │ 交给 LLM 做摘要│ tool │ ← grep 结果 300 行 ││ ... │ ← 更多历史 ─┘│ assist │ ← LLM 准备写文件 ─┐│ tool │ ← 写入成功 │ 最近 6 条│ assist │ ← LLM 调用 read 验证 │ 保留原样│ tool │ ← 文件内容 │ (不压缩)│ assist │ ← LLM 准备做最后总结 ││ user │ ← 当前操作 ─┘└────────┘ ↓ compact_messages() ↓压缩后的 messages(9 条,清爽了):┌────────┐│ system │ ← 永远保留(不动)├────────┤│ user │ ← "之前的对话摘要:找到了 42 个 Python 文件,│ │ 统计了行数,最长的是 utils.py (350行)..."│ assist │ ← "明白了,我继续。"├────────┤│ assist │ ← LLM 准备写文件 ─┐│ tool │ ← 写入成功 │ 最近 6 条│ assist │ ← LLM 调用 read 验证 │ 完整保留│ tool │ ← 文件内容 ││ assist │ ← LLM 准备做最后总结 ││ user │ ← 当前操作 ─┘└────────┘核心思想就一句话:记住要点,忘掉细节,保留现场。四、代码实现:只有一个函数整个压缩逻辑只有一个函数 compact_messages(),约 30 行:COMPACT_THRESHOLD = 20 # 超过 20 条就压缩KEEP_RECENT = 6 # 保留最近 6 条不压缩def compact_messages(messages): if len(messages) <= COMPACT_THRESHOLD: return messages # 没超阈值,不压缩 system_msg = messages[0] # system prompt 永远保留 old_messages = messages[1:-KEEP_RECENT] # 旧消息 → 要被压缩 recent_messages = messages[-KEEP_RECENT:] # 最近的消息 → 保留原样 # 把旧消息拼成文本 old_text = "" for msg in old_messages: role = msg.get("role", "unknown") if isinstance(msg, dict) else getattr(msg, "role", "unknown") content = msg.get("content", "") if isinstance(msg, dict) else getattr(msg, "content", "") if content: old_text += f"[{role}]: {content}\n" # 调用 LLM 生成摘要 summary_response = client.chat.completions.create( model=MODEL, messages=[ {"role": "system", "content": "Summarize the following conversation history. Keep all important facts, file paths, command results, and decisions. Be concise but don't lose critical details."}, {"role": "user", "content": old_text} ] ) summary = summary_response.choices[0].message.content # 重新组装 return [ system_msg, {"role": "user", "content": f"[Previous conversation summary]: {summary}"}, {"role": "assistant", "content": "Understood. I have the context from our previous conversation. Let me continue."}, *recent_messages ]4.1 分三刀system_msg = messages[0] # 第一刀:切出 system promptold_messages = messages[1:-KEEP_RECENT] # 第二刀:切出旧消息(要压缩的)recent_messages = messages[-KEEP_RECENT:] # 第三刀:切出最近消息(要保留的)为什么 system prompt 要单独保留?因为它包含 Agent 的核心指令,压缩进摘要会丢失"你是谁、你能做什么"的基础设定。为什么最近 N 条不压缩?因为 Agent 当前正在进行的操作需要精确的上下文——比如上一条工具返回的文件内容、正在写入的文件路径。这些信息一旦被压缩成摘要,LLM 就无法精确引用了。4.2 用 LLM 做摘要summary_response = client.chat.completions.create( model=MODEL, messages=[ {"role": "system", "content": "Summarize... Keep all important facts..."}, {"role": "user", "content": old_text} ])这里有一个"套娃"——用 LLM 来压缩 LLM 的对话历史。这不是浪费吗?不是。因为这次 LLM 调用的唯一任务就是"总结",不带工具,输出简短,token 开销远小于把完整历史塞进每次请求。4.3 在循环中调用Agent 核心循环里只加了一行:def run_agent(user_message, max_iterations=30): messages = [...] for i in range(max_iterations): messages = compact_messages(messages) # ← 就这一行 response = client.chat.completions.create(...) ...每轮循环开始前检查一次。没超阈值就原样返回(零开销),超了就压缩。简洁到几乎不存在。五、压缩过程的实际观察以下是测试中观察到的 messages 数量变化(阈值设为 10):轮次 1: messages = 2 (system + user)轮次 2: messages = 4 (+ assistant + tool)轮次 3: messages = 6轮次 4: messages = 8轮次 5: messages = 10 ↓ 触发压缩!轮次 6: messages = 9 (system + 摘要 + ack + 最近6条)轮次 7: messages = 11 ↓ 再次触发压缩!轮次 8: messages = 9轮次 9: 任务完成messages 数量像锯齿波一样:涨到阈值 → 压缩回去 → 继续涨 → 再压缩。永远不会超过阈值太多,Agent 可以无限工作下去。六、压缩会丢信息吗?会。但关键是丢的是细节,不是要点。比如原始历史中有:[tool]: $ find . -name "*.py" | head -20./src/utils.py./src/main.py./src/config.py./tests/test_utils.py./tests/test_main.py(省略 15 个文件)压缩后摘要可能变成:在当前目录下找到了 20 个 Python 文件,分布在 src/ 和 tests/ 两个目录中。20 个具体文件名丢了,但"有 20 个文件、在 src/ 和 tests/ 下"这个关键事实保留了。对于 Agent 后续的决策(比如"接下来统计行数"),这个摘要已经足够。如果某个细节真的还需要呢?Agent 可以再次调用工具去获取。这就像人类的工作方式——"我记得上周查过这个目录有 20 个 Python 文件,但具体哪些我忘了,让我再 ls 一下。"七、压缩方案的对比:nanoAgent vs 业界nanoAgent 的压缩是最朴素的实现。业界的方案更加精细: 但核心思路完全一致:旧的压缩,近的保留,要点不丢。八、系列回顾:六篇文章的完整拼图前五篇是在给 Agent "加能力",第六篇是在解决加完能力后的"副作用"。能力越强、工具越多、协作越复杂,对话历史就越长——而压缩确保了这一切不会让 Agent 自我窒息。如果把 Agent 比作一个人:第一篇给了他手脚(工具)第二篇给了他笔记本(记忆)和地图(规划)第三篇给了他规章制度和工具箱第四篇让他能叫临时工帮忙第五篇让他组建正式团队第六篇教他学会"抓大放小"——记住要点、忘掉细节、轻装上阵但前六篇一直在回答"Agent 能做什么",有一个同样重要的问题我们还没回答:"Agent 不能做什么?" 当 Agent 试图执行 rm -rf / 时,谁来踩刹车?这就是 第七篇:安全与权限控制 的主题:三道安全防线,让 Agent 从"裸奔"变成"有保险的"。本文基于 agent-compact.py(GitHub 源码[2])分析。完整系列:第一篇[3] → 第二篇[4] → 第三篇[5] → 第四篇[6] → 第五篇[7] → 第六篇(本文) → 第七篇 相关链接[1]nanoAgent: cid:link_6[2]GitHub 源码: cid:link_5[3]第一篇: https://mp.weixin.qq.com/s/gz_vPvgTdozh4FO6vEdF-Q[4]第二篇: https://mp.weixin.qq.com/s/nbGrU9mEYrOFRt1End2nGw[5]第三篇: https://mp.weixin.qq.com/s/6ThsBKAi0RZGekgOzfDTdQ[6]第四篇: https://mp.weixin.qq.com/s/LCIc_cYDEF52tJ9q1yLMiw[7]第五篇: https://mp.weixin.qq.com/s/N7zvu3ecI600nqg27L5thw AGENT交流群,一起玩转AGENT
  • [技术干货] 从零开始理解 Agent(五):从临时工到正式团队——多智能体协作与编排
    欢迎阅读「从零开始理解 Agent」系列 —— 我们将从从一个极简开源项目 nanoAgent[1] 出发,逐层拆解 OpenClaw / Claude Code 等 AI Agent 背后的全部核心概念。第一篇:底层原理,只有 115 行 —— 工具 + 循环第二篇:记忆与规划 —— 182 行第三篇:Rules、Skills 与 MCP—— 265 行第四篇:SubAgent 子智能体 —— 192 行第五篇:多智能体协作与编排(本文)—— 270 行第六篇:上下文压缩—— 169 行第七篇:安全与权限控制 —— 219 行★ 项目地址:cid:link_5作者:十一上一篇我们实现了 SubAgent——主 Agent 可以临时派出一个"专家"来干活。但我们也明确定义了 SubAgent 的本质:一次性临时工,生成 → 干活 → 返回摘要 → 消亡,没有身份,没有记忆。这在很多场景下够用了。但想想现实中的软件开发团队:后端工程师写完 API 后,前端工程师需要知道接口长什么样;测试工程师发现 bug 后,需要告诉开发去修;开发修完后,测试还得再验一遍——同一个人,被多次找到,而且他还记得上次做了什么。SubAgent 做不到这些。每次调用都是一个全新的、失忆的临时工。那怎么办?答案是:从临时工升级为正式团队。★ 关于本篇代码的说明:和第四篇一样,本篇的 agent-teams.py 是我们新开发的文件(GitHub 源码[2]),不在 nanoAgent 原始仓库中。它在 agent-subagent.py 的基础上,用两个类(Agent + Team)实现了多智能体团队协作。一、临时工 vs 正式员工:差什么?要从临时工升级为正式团队,需要补齐三样东西:1. 能跨多轮对话存活的持久智能体 —— Agent 有记忆,被多次 chat() 调用时记得之前做过什么,不会像 SubAgent 那样每次都失忆2. 身份与生命周期管理 —— Agent 有名字、有角色,被创建(入职)、持续存活(干活)、最终解散(离职),而不是用完即弃3. 智能体之间的通信通道 —— Agent 之间可以互相发消息(点对点或广播),而不是彼此隔离、互相看不到接下来看代码怎么实现。二、核心实现:两个类搞定一切整个 agent-teams.py 只有 270 行,核心新增是两个类:Agent 和 Team。工具层(read/write/edit/bash)和 Agent 循环完全复用前几篇的代码。2.1 Agent 类:有状态的持久智能体先回忆 SubAgent 的实现——一个函数:# SubAgent(第四篇)—— 一个函数,用完就没def subagent(role, task): sub_messages = [...] # 局部变量,函数返回即消亡 for _ in range(10): ... return result # 返回后 sub_messages 被垃圾回收,一切归零现在看 Teams 中的 Agent——一个类:class Agent: def __init__(self, name, role): self.name = name # 身份:有名字 self.role = role # 身份:有角色 self.inbox = [] # 通信:收件箱 self.messages = [ # 记忆:持久保持 {"role": "system", "content": f"You are {name}, a {role}. Be concise and focused."} ]区别只有一个,但意义巨大:messages 从函数的局部变量变成了对象的实例属性。局部变量在函数返回后就被垃圾回收。实例属性只要对象还活着,就一直在。这意味着你可以对同一个 Agent 多次调用 chat(),每次的对话历史都会累积在 self.messages 中——Agent 记得之前做过什么。2.2 chat( ) 方法:带收件箱的 Agent 循环def chat(self, task): # 第 1 步:如果 inbox 有新消息,先读取并消化 if self.inbox: mail = "\n".join(f"[来自 {m['from']}]: {m['content']}"for m in self.inbox) self.messages.append({"role": "user", "content": f"你收到了团队成员的消息:\n{mail}"}) resp = client.chat.completions.create(model=MODEL, messages=self.messages) self.messages.append(resp.choices[0].message) self.inbox.clear() # 第 2 步:执行本次任务(和之前的 Agent 循环一样) self.messages.append({"role": "user", "content": task}) for _ in range(10): response = client.chat.completions.create(model=MODEL, messages=self.messages, tools=tools) message = response.choices[0].message self.messages.append(message) ifnot message.tool_calls: return message.content for tc in message.tool_calls: # ... 执行工具,追加结果(和第一篇完全一样)关键在第 1 步:每次 chat() 开始前,Agent 会先检查收件箱。如果有其他 Agent 发来的消息,就先读取、消化(让 LLM 处理一下),然后清空收件箱。这样 Agent 在执行任务时,已经知道了队友们的最新进展。2.3 receive() 方法:通信通道def receive(self, sender, message): self.inbox.append({"from": sender, "content": message})就这一行。往收件箱里追加一条消息。简单到不需要解释。三、Team 类:生命周期管理与通信编排class Team: def __init__(self): self.agents = {} # name → Agent def hire(self, name, role): """招募:创建一个持久 Agent""" agent = Agent(name, role) self.agents[name] = agent return agent def send(self, from_name, to_name, message): """点对点通信""" self.agents[to_name].receive(from_name, message) def broadcast(self, from_name, message): """广播:给团队所有其他人发消息""" for name, agent in self.agents.items(): if name != from_name: agent.receive(from_name, message) def disband(self): """解散:所有 Agent 生命周期结束""" self.agents.clear()四个方法,对应团队协作的四个动作:四、完整协作流程def run_team(task): team = Team() # 第 1 阶段:组建团队 members = plan_team(task) # LLM 自动拆分角色 for m in members: team.hire(m["name"], m["role"]) # 第 2 阶段:逐个执行,每人干完广播成果 for m in members: agent = team.agents[m["name"]] result = agent.chat(m["task"]) team.broadcast(m["name"], f"我完成了任务。摘要: {result[:200]}") # 第 3 阶段:最后一个成员做二次审查 reviewer = team.agents[members[-1]["name"]] review = reviewer.chat("请根据团队成果做最终审查") # 第 4 阶段:解散 team.disband()用一个具体例子来说明。假设输入 "创建一个 TODO 应用,包含 Python 后端和 HTML 前端":[PM] 分析任务,组建团队...[团队] 3 人: 1. alice — backend developer → 用 FastAPI 创建 TODO 后端 API 2. bob — frontend developer → 创建 HTML 前端页面 3. carol — test engineer → 验证前后端能正常工作============================================================ 第 1 阶段: 招募团队============================================================ [创建] alice (backend developer) [创建] bob (frontend developer) [创建] carol (test engineer)============================================================ 第 2 阶段: 协作开发============================================================── [1/3] alice 开始工作 ── [alice] write({"path": "app.py", ...}) [alice] → 已创建 app.py,包含 GET/POST/DELETE 三个接口... [广播] alice → 全体: 我完成了任务。摘要: 已创建 app.py...── [2/3] bob 开始工作 ── (bob 的 inbox 里有 alice 的广播,他知道后端接口长什么样) [bob] write({"path": "index.html", ...}) [bob] → 已创建 index.html,调用了 alice 定义的 API 接口... [广播] bob → 全体: 我完成了任务。摘要: 已创建 index.html...── [3/3] carol 开始工作 ── (carol 的 inbox 里有 alice 和 bob 的广播) [carol] read({"path": "app.py"}) [carol] read({"path": "index.html"}) [carol] bash({"command": "python -c 'import app; print(\"OK\")'"}) [carol] → 后端代码语法正确,前端页面已创建,接口调用地址匹配... [广播] carol → 全体: 我完成了任务。摘要: 验证通过...============================================================ 第 3 阶段: carol 做最终审查============================================================ (carol 被第二次调用 chat(),她还记得第一次测试的结果) [carol] → 最终审查:后端 app.py 包含 3 个接口(GET/POST/DELETE), 前端 index.html 已正确引用后端地址,代码验证通过,可以交付。 注意 carol 被调用了两次 chat() :第一次做测试,第二次做审查。第二次时她还记得第一次做了什么——这就是"持久记忆"的价值。SubAgent 做不到这一点,因为每次调用都是一个全新的、失忆的函数。五、三大核心能力的代码对照回到开头提出的三个要求,逐一对照:能力 1:能跨多轮对话存活的持久智能体# SubAgent:局部变量,函数返回即消亡def subagent(role, task): sub_messages = [...] # 🔴 生命周期 = 这个函数调用 ... return result # sub_messages 被回收# Teams Agent:实例属性,对象存活就一直在class Agent: def __init__(self, ...): self.messages = [...] # 🟢 生命周期 = Agent 对象的生命周期 def chat(self, task): self.messages.append(...) # 每次调用都往同一个列表里追加 ... # 第 1 次 chat():messages = [system, user1, assistant1] # 第 2 次 chat():messages = [system, user1, assistant1, user2, assistant2] # Agent 在第 2 次时能看到第 1 次的全部历史能力 2:身份与生命周期管理team = Team()# 入职:Agent 被创建,开始存活alice = team.hire("alice", "backend developer")bob = team.hire("bob", "frontend developer")# 存活期间:可以多次交互alice.chat("创建后端 API")alice.chat("添加认证中间件") # alice 记得第一次创建的 API# 解散:所有 Agent 生命周期结束team.disband() # alice、bob 都消亡了能力 3:智能体之间的通信通道# 点对点:alice 告诉 bob 接口格式team.send("alice", "bob", "API 接口: GET /todos, POST /todos")# 广播:alice 告诉所有人team.broadcast("alice", "后端已完成,接口文档见 API.md")# bob 下次 chat() 时,会先读 inbox 中的消息bob.chat("创建前端页面") # bob 已经知道了 API 接口格式六、SubAgent vs Teams:什么时候用哪个?五篇文章,从一个 115 行的极简 Agent 出发,逐层叠加能力:从第四篇的 subagent() 函数到第五篇的 Agent 类,变化只有一个:messages 从局部变量变成了实例属性。但这一个变化,让 Agent 从"用完即弃的临时工"进化为了"有记忆、有身份、能协作的团队成员"。 这就是软件工程中最朴素的道理:数据放在哪里,决定了它的生命周期;生命周期决定了能力边界。但能力越强,副作用也越大——Agent 干的活越多、协作越复杂,messages 就越长。长到撑爆 LLM 的 context window 怎么办?在 第六篇:上下文压缩 中,我们用一个 30 行的函数来解决这个"自我窒息"问题。本文基于 agent-teams.py(GitHub 源码[2])分析。完整系列:第一篇[3] → 第二篇[4] → 第三篇[5] → 第四篇[6] → 第五篇(本文) → 第六篇相关链接[1] nanoAgent: cid:link_5[2] GitHub 源码: cid:link_4[3] 第一篇: https://mp.weixin.qq.com/s/gz_vPvgTdozh4FO6vEdF-Q[4] 第二篇: https://mp.weixin.qq.com/s/nbGrU9mEYrOFRt1End2nGw[5] 第三篇: https://mp.weixin.qq.com/s/6ThsBKAi0RZGekgOzfDTdQ[6] 第四篇: https://mp.weixin.qq.com/s/LCIc_cYDEF52tJ9q1yLMiwAGENT交流群,一起玩转AGENT 
  • [技术干货] 从零开始理解 Agent(四):给 Agent 找个帮手——最简 SubAgent 实现
     欢迎阅读「从零开始理解 Agent」系列 —— 我们将通过一个不到 300 行的开源项目 nanoAgent[1],逐层拆解 OpenClaw / Claude Code 等 AI Agent 背后的全部核心概念。第一篇:底层原理,只有 115 行 —— 工具 + 循环第二篇:记忆与规划 —— 182 行第三篇:Rules、Skills 与 MCP —— 265 行第四篇:最简 SubAgent 实现(本文)—— 新开发,192 行第五篇:多智能体协作与编排 —— 270 行第六篇:上下文压缩 —— 169 行第七篇:安全与权限控制—— 219 行 ★ 项目地址:cid:link_4作者:十一 前三篇,我们一路把 Agent 从"会用工具"进化到了"有记忆、会规划、能扩展"。但到目前为止,所有版本都有一个共同特点:永远只有一个 Agent 在干活。想象一下这个场景:你让 Agent "搭建一个博客系统,前端用 React,后端用 FastAPI,数据库用 SQLite"。一个 Agent 要同时精通前端、后端、数据库——它可以做到,但很容易顾此失彼,上下文越来越长,后面写前端的时候把前面后端的细节忘了。现实中我们怎么解决这类问题?找帮手,分工合作。这就是 SubAgent(子智能体)的核心思想:主 Agent 当项目经理,把子任务委派给拥有不同专业身份的 SubAgent,各管一块,互不干扰。★ 关于本篇代码的说明:前三篇分析的 agent.py、agent-plus.py、agent-claudecode.py 都来自 nanoAgent 原始仓库。本篇的 agent-subagent.py 是我们在 agent-claudecode.py 基础上新开发的文件(GitHub 源码[2]),专门用来演示 SubAgent 机制。你可能注意到它只有 192 行,反而比第三篇的 265 行更少了。这是刻意为之——为了让 SubAgent 的核心逻辑尽可能通俗易懂,我们去掉了 Plan(规划)功能,只保留基础工具 + 记忆 + SubAgent。少即是多:去掉 Plan 相关的全局变量、递归调用和特殊分支后,核心循环 run_agent 从 35 行简化到了 12 行,整个代码一目了然。一、一个生活类比秒懂 SubAgent之前(一个人干所有活): 老板 → "小张,你把前端后端数据库全搞定" 小张(一个人扛所有) - 写后端 API... - 写前端页面...(等等,后端那个接口叫啥来着?) - 建数据库表...(前端那个字段是什么格式?) 现在(项目经理 + 专人): 老板 → 项目经理(主 Agent) │ ├── "后端用 FastAPI" → 后端工程师(SubAgent A) ├── "前端用 React" → 前端工程师(SubAgent B) └── "验证能跑通" → 测试工程师(SubAgent C) 每个人只管自己的事,干完把结果交给项目经理汇总。但要注意一个关键点:这个类比不完全准确。现实中的员工有名字、有工位、有记忆,下次还能找他。SubAgent 不是这样的。 SubAgent 的生命周期是:生成 → 接收任务 → 干活(可以调用工具)→ 返回结果摘要 → 消亡一次性的。 没有持久身份,没有跨调用的记忆。主 Agent 第一次派出的"后端工程师"和第二次派出的"后端工程师"之间没有任何关联——它们是两个完全独立的、用完就扔的临时工。这个"用完即弃"的设计是刻意的:SubAgent 解决的是单次任务内的分工问题,不是长期协作问题。它的价值在于给子任务一个干净的上下文和专注的角色,而不是构建一个持久的团队。二、在代码里怎么实现?如果你读过前三篇,这个实现可能会让你惊讶——核心新增只有大约 30 行代码。为什么这么少?因为前三篇已经把所有基础设施搭好了:工具系统(第一篇)、Agent 循环(第一篇)、工具路由表(第一篇)、记忆(第二篇)。SubAgent 要做的,只是复用这些基础设施,再启动一个独立的 Agent 循环。(由于我们去掉了 Plan 功能来保持代码简洁,整个 agent-subagent.py 只有 192 行,核心循环干净到只有 12 行——这让 SubAgent 的逻辑完全没有噪音干扰。)2.1 新增一个工具定义还记得第一篇中的核心洞察吗?★LLM 本身不会执行任何代码。它只是根据工具说明书,输出一段结构化的 JSON。真正的执行发生在我们的 Python 代码里。SubAgent 也不例外。我们要做的第一步,就是写一份"工具说明书"告诉 LLM:"你有一个叫 subagent 的工具,可以指定角色和任务来委派子任务":{ "name": "subagent", "description": "Delegate a task to a specialized sub-agent with its own role and independent context.", "parameters": { "type": "object", "properties": { "role": {"type": "string", "description": "The sub-agent's specialty, e.g. 'Python backend developer'"}, "task": {"type": "string", "description": "The specific task to delegate"} }, "required": ["role", "task"] } }就这么一个 JSON。和 read、write、bash 等工具完全一样的格式——对 LLM 来说,subagent 就是"又一个工具",没有任何特殊之处。2.2 实现 subagent 函数def subagent(role, task): """启动一个独立的 Agent 循环,拥有专属角色和独立上下文""" print(f"\n[SubAgent:{role}] 开始: {task}") # 关键 1:独立的 messages,独立的 system prompt sub_messages = [ {"role": "system", "content": f"You are a {role}. Be concise and focused. Only do what is asked."}, {"role": "user", "content": task} ] # 关键 2:排除 subagent 自身,防止无限递归 sub_tools = [t for t in tools if t["function"]["name"] != "subagent"] # 关键 3:一个完整的 Agent 循环(和第一篇的核心循环一模一样) for _ in range(10): response = client.chat.completions.create( model=MODEL, messages=sub_messages, tools=sub_tools ) message = response.choices[0].message sub_messages.append(message) ifnot message.tool_calls: print(f"[SubAgent:{role}] 完成") return message.content for tc in message.tool_calls: fn = tc.function.name args = json.loads(tc.function.arguments) print(f" [SubAgent:{role}] {fn}({args})") result = available_functions[fn](**args "fn") sub_messages.append({"role": "tool", "tool_call_id": tc.id, "content": result}) return"SubAgent: max iterations reached"2.3 注册到路由表available_functions["subagent"] = subagent完了。就这些。三、等一下——代码里没有调用 subagent 的地方?如果你仔细看完整个代码,会发现一件"奇怪"的事:没有任何地方主动调用 subagent() 函数。没有 if task == "复杂任务": subagent(...),没有任何预编排逻辑。这正是 Agent 和传统程序的根本区别,也是贯穿这整个系列的核心设计思想。让我用一张图还原 subagent 被调用的完整链路:用户: "创建一个 TODO 应用,包含 Python 后端和 HTML 前端" │ ▼ 主 Agent 的 run_agent() 循环启动 │ ▼ (1) 代码把 messages + tools 列表发送给 LLM tools 列表里包含: [read, write, edit, glob, grep, bash, subagent] ^^^^^^^^ LLM 看到了这个工具 │ ▼ (2) LLM 分析任务,决定委派,返回: {"tool_calls": [{"function": {"name": "subagent", "arguments": {"role": "Python backend developer", "task": "用 FastAPI 创建..."}}}]} │ ▼ (3) 核心循环中的通用调度代码执行: fn = "subagent" args = {"role": "Python backend developer", "task": "..."} result = available_functions["subagent"](**args ""subagent"") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 走到了我们写的 subagent() 函数! │ ▼ (4) subagent() 内部启动一个全新的 Agent 循环 - 独立的 system prompt: "You are a Python backend developer." - 独立的 messages 列表 - 可以使用 read/write/edit/bash 等工具 - 循环结束后,返回结果文本 │ ▼ (5) 结果返回给主 Agent,主 Agent 可能继续派出前端 SubAgent...关键在第 (3) 步——available_functions["subagent"](**args ""subagent"") 这行代码。它和 available_functions["bash"](**args ""bash"") 走的是完全相同的调度路径。在核心循环眼里,subagent 和 bash 没有任何区别,都是"LLM 说要调用,那我就执行"。控制流在 LLM 手里,不在代码里。 代码只提供能力(注册工具),LLM 决定何时使用。四、三个关键设计决策4.1 为什么 SubAgent 要有独立的 messages?# 主 Agent 的 messages(可能已经很长了) messages = [system, user, assistant, tool, assistant, tool, ...] # SubAgent 创建全新的 messages(从零开始) sub_messages = [ {"role": "system", "content": f"You are a {role}. ..."}, {"role": "user", "content": task} ]还记得第二篇中的"短期记忆"概念吗?messages 列表就是 Agent 的短期记忆。如果 SubAgent 共享主 Agent 的 messages,它会看到所有历史——前端 SubAgent 会被后端的代码细节干扰,上下文越来越长,token 开销越来越大。独立的 messages 意味着:SubAgent 只知道自己的角色和任务,保持专注。而且这个 sub_messages 在函数返回后就被垃圾回收了——SubAgent 没有任何持久记忆,干完活就消亡,下次调用是一个全新的 SubAgent。4.2 为什么 SubAgent 有不同的 system prompt?# 主 Agent: 协调者 "You are an orchestrator agent. You can delegate to sub-agents..." # SubAgent: 专家 f"You are a {role}. Be concise and focused. Only do what is asked."第三篇中我们讲了 Rules——用声明式文件定制 Agent 的行为。SubAgent 的 system prompt 是同一个思路的极简版:通过不同的角色描述,让同一个 LLM 展现出不同的专业行为。当 role 是 "Python backend developer" 时,LLM 会倾向于用 FastAPI/Flask,写 RESTful 接口;当 role 是 "frontend developer" 时,LLM 会倾向于写 HTML/CSS/JavaScript。同一个模型,不同的人格。4.3 为什么要排除 subagent 工具?sub_tools = [t for t in tools if t["function"]["name"] != "subagent"]这和第三篇中 plan 工具排除自身是同样的思路——防止无限递归。如果 SubAgent 也能派出自己的 SubAgent,而那个 SubAgent 又派出自己的……就会无限嵌套下去。一行代码,一个过滤,问题解决。五、实际运行效果假设用户输入:python agent-subagent.py "创建一个简单的 TODO 应用,包含 Python 后端和 HTML 前端"终端输出大致如下:[Tool] subagent({"role": "Python backend developer", "task": "创建一个 FastAPI ..."}) ================================================== [SubAgent:Python backend developer] 开始: 创建一个 FastAPI 后端... ================================================== [SubAgent:Python backend developer] write({"path": "app.py", ...}) [SubAgent:Python backend developer] bash({"command": "pip install fastapi"}) [SubAgent:Python backend developer] 完成 [Tool] subagent({"role": "frontend developer", "task": "创建一个 HTML 前端..."}) ================================================== [SubAgent:frontend developer] 开始: 创建一个 HTML 前端... ================================================== [SubAgent:frontend developer] write({"path": "index.html", ...}) [SubAgent:frontend developer] 完成 已完成 TODO 应用的创建: - app.py: FastAPI 后端,包含 GET/POST/DELETE 接口 - index.html: 前端页面,包含添加和删除功能注意两个关键现象:主 Agent 自己一行代码都没写。 它只做了两件事:调用 subagent 委派后端任务,再调用 subagent 委派前端任务,最后汇总结果。两个 SubAgent 各管各的。 后端 SubAgent 在写 app.py 时,前端 SubAgent 还不存在。前端 SubAgent 启动时,有自己全新的上下文,不会被后端的细节干扰。六、SubAgent vs 之前的方案:什么时候用哪个?SubAgent 和 Plan 最大的区别:七、系列总结:从 115 行到完整 Agent 架构四篇文章,我们从零搭建了一个完整的 Agent 认知体系:┌───────────────────────────────────────────────────────┐ │ Agent 架构全景 │ │ │ │ ┌──────────────┐ 第四篇 (本文) │ │ │ SubAgent │ 多智能体协作 ── subagent() 工具 │ │ ├──────────────┤ 第三篇 │ │ │ Rules │ 行为约束层 ──── .agent/rules/ │ │ │ Skills │ 技能知识层 ──── .agent/skills/ │ │ │ MCP │ 工具扩展层 ──── .agent/mcp.json │ │ ├──────────────┤ 第二篇 │ │ │ Memory │ 持久记忆层 ──── agent_memory.md │ │ │ Planning │ 任务分解层 ──── create_plan() │ │ ├──────────────┤ 第一篇 │ │ │ LLM │ 推理决策层 ──── OpenAI API │ │ │ Tools │ 工具执行层 ──── bash/read/write │ │ │ Loop │ 核心循环层 ──── for + tool_calls │ │ └──────────────┘ │ └───────────────────────────────────────────────────────┘★ 注:前三个文件来自 nanoAgent 原始仓库[3]。第四个文件是本文新开发的(GitHub 源码),为了聚焦 SubAgent 核心逻辑,刻意去掉了 Plan 功能,因此行数反而比第三篇少。这不是倒退,而是做减法——用最干净的代码展示最核心的概念。四个维度叠加,就构成了 OpenClaw、Claude Code、Cursor Agent、Devin 等产品的完整架构。而贯穿整个系列的核心设计思想只有一个:一切能力都是"工具"。 读文件是工具,写文件是工具,搜索是工具,规划是工具(第三篇),甚至派出一个子智能体也是工具(本文)。LLM 通过统一的 Function Calling 协议按需调用它们,代码通过统一的路由表(available_functions)执行它们。 但 SubAgent 的"一次性"本质也带来了局限:它们之间无法通信,不记得上次做了什么,无法被多次调用。当任务需要真正的团队协作——你写完我来接、测出 bug 你去改、改完我再测——就需要把临时工升级为正式员工。 这就是第五篇:多智能体协作与编排的主题:用两个类(Agent + Team)实现持久记忆、身份管理和通信通道。本文基于 sanbuphy/nanoAgent 的架构扩展。完整系列:第一篇:底层原理 → 第二篇:记忆与规划 → 第三篇:Rules、Skills 与 MCP → 第四篇:SubAgent(本文) → 第五篇:多智能体协作 相关链接[1] nanoAgent: cid:link_4[2] GitHub 源码: cid:link_3[3] nanoAgent 原始仓库: cid:link_4 AGENT交流群,一起玩转AGENT
  • [热门活动] KubeCon Europe 2026| 一图速览华为云精彩议程
    3月23日-26日,华为云重磅参会全球云原生与开源旗舰会议KubeCon + CloudNativeCon Europe 2026。本届大会于荷兰阿姆斯特丹召开,华为云将围绕云原生AI、LLM Inference、 AI Agent 等方向带来议题分享与展览展示,提供面向Agentic AI的 “智能原生” 基础设施解决方案,与全球开发者及行业领袖共探开源创新与产业发展的无限可能。   更多云原生技术动向关注容器魔方  【更多华为云云原生干货推荐】华为云云原生王者之路集训营华为云云原生王者之路集训营为帮助广大技术爱好者快速掌握云原生相关技能,华为云云原生团队与华为云学院联合CNCF开源软件大学启动人才培养计划,推出《华为云云原生王者之路集训营》,从云原生基础知识介绍到最佳实践讲解、底层原理和方案架构深度剖析,层层深入,满足不同云原生技术基础和学习目标人群的需求。本课程还精选数十个企业典型应用场景,作为学员上机实践案例,帮助学员将所学技术快速与企业业务相结合,服务于企业生产。点击免费参加华为云云原生王者之路集训营:cid:link_3 学习后记得小试牛刀,看看测评效果~ 华为云云原生王者之路-黄金课程测评 华为云云原生王者之路-钻石课程测评 华为云云原生王者之路-王者课程测评 
  • [技术干货] 从零开始理解 Agent(三):OpenClaw / Claude Code 的 Rules、Skills 与 MCP 机制
    欢迎阅读「从零开始理解 Agent」系列文章——我们将通过一个不到 300 行的开源项目 nanoAgent[1],逐层拆解 OpenClaw / Claude Code 等 AI Agent 背后的全部核心概念。第一篇:底层原理,只有 115 行 —— 工具 + 循环第二篇:记忆与规划 —— 182 行第三篇:Rules、Skills 与 MCP(本文)—— 265 行第四篇:SubAgent 子智能体 —— 192 行第五篇:多智能体协作与编排 —— 270 行第六篇:上下文压缩 —— 169 行★ 项目地址:cid:link_4作者:十一在前两篇中,我们一步步构建了 Agent 的核心能力:第一篇[2]用 115 行代码搭好了"工具 + 循环"的地基;第二篇[3]用 67 行增量代码装上了记忆和规划。但在第二篇结尾,我们留下了三个未解之谜:工具是硬编码的,没有行为约束,规划是被动触发的。今天我们继续进化—— agent-claudecode.py[4](265 行)。如果你用过 OpenClaw 或 Claude Code,你对 CLAUDE.md 规则文件、.agent/skills/ 技能目录、MCP 工具配置一定不陌生——这些概念正是本篇要拆解的核心。agent-claudecode.py 在前两个版本的基础上,引入了四个新概念来回答那三个问题:一、三个版本全景对比先回顾整个进化路线:二、更精细的工具集:从"能用"到"好用"agent-claudecode.py 首先在基础工具上做了大幅扩充,从 3 个增加到 7 个:base_tools = [     {"name": "read",  "description": "Read file with line numbers", ...},     {"name": "write", "description": "Write content to file", ...},     {"name": "edit",  "description": "Replace string in file", ...},   # 新增     {"name": "glob",  "description": "Find files by pattern", ...},    # 新增     {"name": "grep",  "description": "Search files for pattern", ...}, # 新增     {"name": "bash",  "description": "Run shell command", ...},     {"name": "plan",  "description": "Break down complex task", ...}   # 新增 ]新增的工具不是随意选择的——它们恰好对应了 Claude Code 的核心工具集。其中最值得深入分析的是 edit 和改进后的 read。edit:用约束引导 LLM 行为def edit(path, old_string, new_string):     try:         with open(path, 'r') as f:             content = f.read()         if content.count(old_string) != 1:             return f"Error: old_string must appear exactly once"         new_content = content.replace(old_string, new_string)         with open(path, 'w') as f:             f.write(new_content)         return f"Successfully edited {path}"     except Exception as e:         return f"Error: {str(e)}"这个工具有一个精妙的约束:**old_string 必须在文件中恰好出现一次**。出现零次说明目标不存在,出现多次则无法确定该替换哪一处。这个"唯一性约束"迫使 LLM 在调用 edit 之前先用 read 或 grep 确认上下文,大大降低了误编辑的概率。对比第一篇中 agent.py 的 write_file(直接覆盖整个文件),edit 精确到了字符串级别——这正是 Claude Code 中 str_replace 工具的设计思路。★ 设计启示用工具的约束来引导 LLM 的行为,比在 prompt 中告诫"小心编辑"可靠得多。约束是硬性的,prompt 是软性的。read:行号 + 分页def read(path, offset=None, limit=None):     try:         with open(path, 'r') as f:             lines = f.readlines()         start = offset if offset else0         end = start + limit if limit else len(lines)         numbered = [f"{i+1:4d}{line}"for i, line in enumerate(lines[start:end], start)]         return ''.join(numbered)     except Exception as e:         return f"Error: {str(e)}"相比第一篇中简单的 read_file,这个版本支持 offset 和 limit 分页读取,还会给每行加上行号。行号看似微不足道,但对 LLM 配合 edit 使用时极其重要——LLM 可以精确定位"第 23 行附近的那段代码"。三、Rules:教 Agent "做人的规矩"3.1 加载与注入RULES_DIR = ".agent/rules" def load_rules():     rules = []     ifnot os.path.exists(RULES_DIR):         return ""     try:         for rule_file in Path(RULES_DIR).glob("*.md"):             with open(rule_file, 'r') as f:                 rules.append(f"# {rule_file.stem}\n{f.read()}")         return "\n\n".join(rules) if rules else""     except:         return ""Rules 是存放在 .agent/rules/ 目录下的 Markdown 文件。Agent 启动时全部加载,注入到 system prompt 中:if rules:     context_parts.append(f"\n# Rules\n{rules}")3.2 Rules 是什么、怎么用你可以创建这样的规则文件:<!-- .agent/rules/code-style.md --> - 使用 Python 3.10+ 语法 - 所有函数必须有 docstring - 变量命名使用 snake_case - 不要使用 print 调试,使用 logging 模块<!-- .agent/rules/safety.md --> - 永远不要执行 rm -rf / 或类似的危险命令 - 修改文件前先备份 - 不要修改 .env 文件中的密钥3.3 Rules 的本质Rules 是项目级的 system prompt 扩展。它解决了一个关键问题:不同项目、不同团队、不同场景对 Agent 的要求不同。与其每次在对话中反复叮嘱"记得用 snake_case",不如写一次规则文件,永久生效。如果你用过 OpenClaw 或 Claude Code,你一定对 CLAUDE.md 不陌生——它就是 Rules 的工程化实现。在 Claude Code 中对应 CLAUDE.md 文件和 .claude/rules/ 目录,在 OpenClaw 中也沿用了相同的约定。在 Cursor 中是 .cursorrules,在 GitHub Copilot 中是 .github/copilot-instructions.md。名字不同,本质一样——用声明式文件定制 Agent 的行为边界。回顾第二篇中 agent-plus.py 的 system prompt 构建方式,当时只有"基础指令 + 记忆"两层。现在变成了三层拼接:最终 system prompt = 基础指令 + Rules(项目规则) + Skills(技能描述) + Memory(历史记忆)四、Skills:可插拔的技能注册4.1 加载与注入SKILLS_DIR = ".agent/skills" def load_skills():     skills = []     ifnot os.path.exists(SKILLS_DIR):         return []     try:         for skill_file in Path(SKILLS_DIR).glob("*.json"):             with open(skill_file, 'r') as f:                 skills.append(json.load(f))         return skills     except:         return []Skills 是 .agent/skills/ 目录下的 JSON 文件,以列表摘要的形式注入 system prompt:if skills:     context_parts.append(         f"\n# Skills\n" + "\n".join(             [f"- {s['name']}: {s.get('description', '')}"for s in skills]         )     )一个 Skill 文件可能长这样:{   "name": "docker-deploy",   "description": "Deploy application using Docker Compose. Steps: 1) Check Dockerfile exists, 2) Run docker-compose build, 3) Run docker-compose up -d, 4) Verify containers are running.",   "triggers": ["deploy", "docker", "container"] }★ 关于 Skill 的文件格式: 在 OpenClaw / Claude Code 的实际实现中,Skill 的标准格式是 Markdown(每个 Skill 目录下有一个 SKILL.md,里面详细描述执行步骤、最佳实践、示例代码等)。但 nanoAgent 原始仓库中采用的是 JSON 格式,所以代码里用 json.load() 来解析。这不影响理解核心思路——不管是 Markdown 还是 JSON,本质都是"把技能描述加载出来注入到 system prompt"。格式只是载体,思想是一样的。4.2 Skills vs RulesRules 管约束,Skills 管能力。一个告诉 Agent "做人的底线",一个告诉 Agent "做事的方法"。五、MCP:让 Agent 拥有无限工具的协议5.1 什么是 MCP?回忆第一篇中 agent.py 的工具定义方式——直接在代码里硬编码。想加一个新工具?改代码、重新部署。这在生产环境中完全不可接受。MCP(Model Context Protocol)是 Anthropic 提出的一个开放标准,它定义了 LLM 与外部工具之间的通信协议。你可以把 MCP 理解为"AI 世界的 USB 接口"——任何遵循这个协议的工具服务都可以即插即用地接入 Agent。如果你用过 Claude Code,你一定在配置文件里见过 mcpServers 这个字段——配置一个 GitHub MCP Server,Agent 就能直接操作 PR 和 Issue;配置一个数据库 MCP Server,Agent 就能执行 SQL 查询。这就是 MCP 的威力。5.2 agent-claudecode.py 中的 MCP 实现MCP_CONFIG = ".agent/mcp.json" def load_mcp_tools():     ifnot os.path.exists(MCP_CONFIG):         return []     try:         with open(MCP_CONFIG, 'r') as f:             config = json.load(f)         mcp_tools = []         for server_name, server_config in config.get("mcpServers", {}).items():             if server_config.get("disabled", False):                 continue             for tool in server_config.get("tools", []):                 mcp_tools.append({"type": "function", "function": tool})         return mcp_tools     except:         return []配置文件 .agent/mcp.json:{   "mcpServers": {     "filesystem": {       "disabled": false,       "tools": [{         "name": "list_directory",         "description": "List contents of a directory with metadata",         "parameters": {           "type": "object",           "properties": {"path": {"type": "string"}},           "required": ["path"]         }       }]     },     "database": {       "disabled": true,       "tools": [...]     }   } }5.3 MCP 的精髓:一行代码all_tools = base_tools + mcp_tools这一行是整个 MCP 集成的精髓。MCP 加载的工具和基础工具使用完全相同的 JSON Schema 格式,直接拼接成一个列表传给 LLM。LLM 完全不需要知道某个工具是"内置的"还是"MCP 加载的"——对它来说,工具就是工具。5.4 nanoAgent 简化了什么需要说明的是,nanoAgent 的 MCP 实现是高度简化的——它只实现了"工具注册"(把 schema 加载给 LLM),没有实现"工具执行"(通过网络调用远程 MCP Server)。实际调用时会走到 else 分支返回 "Tool not implemented":虽然不完整,但它展示了 MCP 集成的核心思路:工具定义与工具实现的分离。在完整实现中,那个 else 分支会变成一个 MCP 客户端调用。5.5 MCP 解决的根本问题没有 MCP 的世界:                    有 MCP 的世界: Agent A         Agent B             MCP Server: Slack  MCP Server: GitHub ├── Slack (自写) ├── Slack (自写)         │                │ ├── GitHub(自写) ├── Jira (自写)          └───── 标准协议 ──┘ └── DB (自写)    └── DB (自写)                    │                                          ┌───────┼───────┐ 每个 Agent 各写各的                      Agent A  Agent B  Agent C N × M 的工作量                          工具实现一次,全部共享                                          N + M 的工作量六、Plan-as-Tool:规划从"被动触发"到"自主决策"6.1 进化路径回顾三个版本中,规划经历了清晰的进化: 在 agent-claudecode.py 中,plan 出现在工具列表里:{"name": "plan", "description": "Break down complex task into steps and execute sequentially", ...}这意味着 LLM 遇到复杂任务时可以主动调用 plan 工具进行拆解,无需用户干预。6.2 递归执行与防无限循环plan 工具的执行逻辑是整个文件最复杂的部分:if function_name == "plan":     plan_mode = True     function_response = available_functions[function_name](**function_args "function_name")     messages.append({"role": "tool", "tool_call_id": tool_call.id, "content": function_response})     if current_plan:         results = []         for i, step in enumerate(current_plan, 1):             messages.append({"role": "user", "content": step})             result, messages = run_agent_step(                 messages,                 [t for t in tools if t["function"]["name"] != "plan"]  # 关键:排除 plan             )             results.append(result)         plan_mode = False         current_plan = []         return "\n".join(results), messages三个关键设计:递归调用 run_agent_step。 Plan 生成的每个步骤都通过 run_agent_step 执行,每步内部仍然可以使用 read、write、bash 等工具。这形成了一个两层循环——外层是规划步骤,内层是每步的 ReAct 循环。排除 plan 工具本身。 执行步骤时的工具列表中刻意去掉了 plan,配合 plan_mode 全局变量双重保护,防止"在规划中再次规划"的无限递归。上下文跨步共享。 和第二篇中一样,messages 在所有步骤间共享。 用户: "重构项目的测试框架"   │   ▼ LLM 判断任务复杂 → 主动调用 plan 工具   │   ▼ plan() 返回 4 个步骤   │   ├── Step 1: 分析当前测试结构   │     └── run_agent_step() → [glob, read, grep]   │   ├── Step 2: 创建新的测试目录   │     └── run_agent_step() → [bash, write]   │   ├── Step 3: 迁移现有测试文件   │     └── run_agent_step() → [read, edit, write]   │   └── Step 4: 验证所有测试通过         └── run_agent_step() → [bash]七、全部模块如何组装在一起 def run_agent_claudecode(task, use_plan=False):     print("[Init] Loading ClaudeCode features...")     # 1. 从文件系统加载所有外部配置     memory = load_memory()        # 历史记忆     rules = load_rules()          # 行为规则     skills = load_skills()        # 技能注册     mcp_tools = load_mcp_tools()  # MCP 外部工具     # 2. 合并工具列表(基础工具 + MCP 工具)     all_tools = base_tools + mcp_tools     # 3. 构建 system prompt(基础指令 + Rules + Skills + Memory)     context_parts = ["You are a helpful assistant..."]     if rules:   context_parts.append(f"\n# Rules\n{rules}")     if skills:  context_parts.append(f"\n# Skills\n...")     if memory:  context_parts.append(f"\n# Previous Context\n{memory}")     messages = [{"role": "system", "content": "\n".join(context_parts)}]     # 4. 执行 ...┌─────────────────────── 文件系统 ───────────────────────┐ │                                                         │ │  .agent/rules/*.md    → load_rules()    → system prompt │ │  .agent/skills/*.json → load_skills()   → system prompt │ │  .agent/mcp.json      → load_mcp_tools()→ tools 列表    │ │  agent_memory.md      → load_memory()   → system prompt │ │                                                         │ └─────────────────────────────────────────────────────────┘                           │                           ▼               ┌──── Agent 运行时 ────┐               │                      │               │  system prompt =     │               │    基础指令           │               │    + Rules           │               │    + Skills          │               │    + Memory          │               │                      │               │  tools =             │               │    base_tools (7个)   │               │    + mcp_tools (N个)  │               │                      │               └──────────────────────┘这个架构揭示了一个重要原则:Agent 的能力由两个正交维度定义prompt 维度(知道什么):Rules、Skills、Memory 扩展的是 LLM 的认知tools 维度(能做什么):MCP 扩展的是 LLM 的行动能力两者独立变化、自由组合,构成了 Agent 的完整能力空间。八、从 115 行到 265 行的认知地图三篇文章读下来,我们在 265 行代码里看到了 Agent 的全部核心概念。用一张七层架构图来做最后的回顾:┌───────────────────────────────────────────────────────┐ │                    Agent 架构全景                       │ │                                                        │ │  ┌──────────────┐  第三篇:agent-claudecode.py         │ │  │  Rules       │  行为约束层 ──── .agent/rules/       │ │  │  Skills      │  技能知识层 ──── .agent/skills/      │ │  │  MCP         │  工具扩展层 ──── .agent/mcp.json     │ │  │  Plan Tool   │  自主规划层 ──── plan() 作为工具      │ │  ├──────────────┤  第二篇:agent-plus.py               │ │  │  Memory      │  持久记忆层 ──── agent_memory.md     │ │  │  Planning    │  任务分解层 ──── create_plan()       │ │  │  Multi-step  │  多步编排层 ──── 步骤间上下文共享     │ │  ├──────────────┤  第一篇:agent.py                    │ │  │  LLM         │  推理决策层 ──── OpenAI API          │ │  │  Tools       │  工具执行层 ──── bash/read/write     │ │  │  Loop        │  核心循环层 ──── for + tool_calls    │ │  └──────────────┘                                      │ └───────────────────────────────────────────────────────┘每一层都在回答一个关键问题: 这七层架构,就是当今所有主流 Agent 框架(OpenClaw、Claude Code、Cursor Agent、Devin、OpenHands 等)的共同骨架。nanoAgent 用不到 300 行 Python 代码,把这个骨架完整地呈现了出来。九、结语 如果你跟着这三篇文章走了下来,你已经理解了 Agent 最核心的架构要素。但还有一个问题我们没有触及:当任务复杂到一个 Agent 忙不过来时怎么办?能不能让 Agent 自己找帮手?这就是多智能体协作——SubAgent 的领域。在 第四篇:SubAgent 子智能体 中,我们将用不到 30 行新增代码,让主 Agent 学会"分工派活"。★ "The question is not what you look at, but what you see." — Henry David ThoreaunanoAgent README 的这句引言,放在这里再合适不过。看过这 265 行代码之后,当你再打开 OpenClaw、Claude Code、Cursor 或任何 Agent 产品时,你看到的不再是"魔法",而是——一个循环、几个工具、一段记忆、一份规则。本文基于 sanbuphy/nanoAgent 的 agent-claudecode.py 分析。完整系列:第一篇:底层原理→ 第二篇:记忆与规划 → 第三篇:Rules、Skills 与 MCP(本文) → 第四篇:SubAgent 子智能体。相关链接 [1] nanoAgent: cid:link_4 [2] 第一篇: cid:link_2 [3] 第二篇: cid:link_3[ 4] agent-claudecode.py: cid:link_4/blob/main/agent-claudecode.py   AGENT交流群,一起玩转AGENT  
  • [技术干货] 从零开始理解 Agent(二):OpenClaw / Claude Code 如何实现记忆与规划,只需182 行
    欢迎阅读 「从零开始理解 Agent」系列 文章—— 我们将通过一个不到 300 行的开源项目 nanoAgent,逐层拆解 OpenClaw / Claude Code 等 AI Agent 背后的全部核心概念。第一篇:底层原理,只有 115 loc(*lines of code) —— 工具 + 循环第二篇:记忆与规划(本文)—— 182 loc第三篇:Rules、Skills 与 MCP] —— 265 loc★ 项目地址:cid:link_0作者:十一上一篇我们用 115 行代码理解了 Agent 的核心公式:LLM + 工具 + 循环。但在结尾我们也指出了它的致命缺陷——没有记忆、不会规划。如果你用过 OpenClaw 或 Claude Code,你会发现它们可以在一个长对话中持续记住之前的操作,面对复杂需求时也会先制定计划再逐步执行。这些能力不是"魔法",而是可以用很少的代码实现的架构设计。今天我们看 nanoAgent 的第二个版本 agent-plus.py(182 行),它只多了 67 行代码,却解决了两个根本问题:让 Agent 记住过去,让 Agent 规划未来。一、从 agent.py 到 agent-plus.py:多了什么?工具层完全没变——新增的 67 行全部用来构建更高层的能力。这恰好印证了第一篇的结论:工具 + 循环只是地基,记忆和规划才是让 Agent 真正可用的关键。二、记忆系统:最朴素但最本质的方案2.1 记忆的存储:一个 Markdown 文件MEMORY_FILE = "agent_memory.md"def save_memory(task, result):    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")    entry = f"\n## {timestamp}\n**Task:** {task}\n**Result:** {result}\n"    try:        with open(MEMORY_FILE, 'a') as f:            f.write(entry)    except:        passagent-plus.py 用了一种极其朴素的方案——把历史任务和结果追加写入一个 Markdown 文件。没有数据库,没有向量索引,就是纯文本。每次任务执行完毕,Agent 把"什么时间、做了什么、得到什么结果"追加到文件末尾:## 2026-03-12 14:30:00**Task:** 统计当前目录下的 Python 文件数量**Result:** 当前目录下共有 42 个 Python 文件。## 2026-03-12 15:00:00**Task:** 创建一个 hello.py**Result:** 已创建 hello.py,内容为打印 Hello World。2.2 记忆的加载:滑动窗口def load_memory():    ifnot os.path.exists(MEMORY_FILE):        return""    try:        with open(MEMORY_FILE, 'r') as f:            content = f.read()        lines = content.split('\n')        return'\n'.join(lines[-50:]) if len(lines) > 50else content    except:        return ""加载出来的记忆被拼接到 system prompt 末尾,以 "Previous context" 的形式注入。LLM 在处理新任务时能"看到"之前的历史。2.4 记忆流程全景第 1 次运行                          第 2 次运行───────────                        ───────────用户: "创建 hello.py"               用户: "读取 hello.py 并加上注释"        │                                  │        ▼                                  ▼  system prompt:                    system prompt:  "You are a helpful               "You are a helpful   assistant..."                    assistant...                                                                        Previous context:                                    ## 2026-03-12 14:30                                    Task: 创建 hello.py                                    Result: 已创建..."        │                                  │        ▼                                  ▼  Agent 执行任务                     Agent 执行任务        │                           (知道之前创建过 hello.py)        ▼                                  │  save_memory() ──写入──▶ agent_memory.md ◀─── save_memory()2.5 记忆的本质这个方案虽然原始,但揭示了一个根本原理:LLM 本身没有持久记忆,所有"记忆"都是通过在 prompt 中注入历史信息来实现的。无论是 Claude 的 Memory、ChatGPT 的记忆功能,还是更复杂的 RAG 系统,底层都遵循这个模式——只是在"存在哪、怎么找、搬多少"上做得更精细。我们会在文末讨论这些进化方向。三、规划系统:让 Agent 学会"先想再做"3.1 为什么需要规划?回忆第一篇中 agent.py 的工作方式:把整个任务丢给 LLM,让它在循环中自行摸索。简单任务没问题,但面对复杂任务(比如"重构整个项目的目录结构"),LLM 容易迷失在细节中,忘记全局目标。agent-plus.py 引入了一个可选的规划阶段:def create_plan(task):    print("[Planning] Breaking down task...")    response = client.chat.completions.create(        model=os.environ.get("OPENAI_MODEL", "gpt-4o-mini"),        messages=[            {"role": "system", "content": "Break down the task into 3-5 simple, actionable steps. Return as JSON array of strings."},            {"role": "user", "content": f"Task: {task}"}        ],        response_format={"type": "json_object"}    )    try:        plan_data = json.loads(response.choices[0].message.content)        steps = plan_data.get("steps", [task])        print(f"[Plan] {len(steps)} steps created")        for i, step in enumerate(steps, 1):            print(f"  {i}. {step}")        return steps    except:        return [task]3.2 规划的设计细节这段代码有几个值得细品的地方:用 LLM 来做规划。 规划本身也是一次 LLM 调用,但不带任何工具——纯粹的"思考"。system prompt 要求 LLM 把任务拆解为 3-5 个可执行的步骤,以 JSON 格式返回。结构化输出。 通过 response_format={"type": "json_object"} 强制 LLM 返回 JSON,避免格式解析问题。优雅降级。 如果 JSON 解析失败,except 分支回退到 [task]——即不拆解,整个任务当作一步执行。这种防御性编程在 Agent 开发中非常重要。3.3 两种执行范式的对比agent.py 和 agent-plus.py 代表了 Agent 领域的两种经典范式:agent.py (ReAct)                  agent-plus.py (Plan-then-Execute)思考 → 行动 → 观察                规划(全局思考)  ↑         │                         │  └─────────┘                      步骤1 → 步骤2 → 步骤3                                   (每步内部仍是 ReAct)ReAct 灵活但容易迷失,Plan-then-Execute 有全局视角但规划可能不准确。agent-plus.py 通过 --plan 参数让用户自行选择——这种"默认简单,按需复杂"的设计在工程上很实用。四、多步执行:步骤之间的上下文传递4.1 从 run_agent 到 run_agent_step 的关键变化对比第一篇中 agent.py 的 run_agent 函数,agent-plus.py 的 run_agent_step 有两个关键变化:def run_agent_step(task, messages, max_iterations=5):    messages.append({"role": "user", "content": task})    actions = []    for _ in range(max_iterations):        response = client.chat.completions.create(            model=os.environ.get("OPENAI_MODEL", "gpt-4o-mini"),            messages=messages,            tools=tools        )        message = response.choices[0].message        messages.append(message)        ifnot message.tool_calls:            return message.content, actions, messages        for tool_call in message.tool_calls:            function_name = tool_call.function.name            function_args = json.loads(tool_call.function.arguments)            print(f"[Tool] {function_name}({function_args})")            function_response = available_functions[function_name](**function_args "function_name")            actions.append({"tool": function_name, "args": function_args})            messages.append({"role": "tool", "tool_call_id": tool_call.id, "content": function_response})    return "Max iterations reached", actions, messages变化一:messages 从内部创建变为外部传入。 这意味着多个步骤可以共享同一个对话历史。步骤 1 执行 grep 搜索到的结果,在步骤 2 中仍然可见。变化二:返回值包含 messages。 函数把更新后的消息列表返回给调用方,供下一步继续使用。4.2 编排层:把步骤串起来all_results = []for i, step in enumerate(steps, 1):    if len(steps) > 1:        print(f"\n[Step {i}/{len(steps)}] {step}")    result, actions, messages = run_agent_step(step, messages)    all_results.append(result)final_result = "\n".join(all_results)save_memory(task, final_result)messages 变量在整个步骤循环中是同一个列表对象。 步骤 1 执行过程中产生的所有工具调用和结果,都会累积在这个列表中,步骤 2 的 LLM 调用能看到步骤 1 的完整执行轨迹。这引出了一个重要的概念区分——短期记忆与长期记忆:  五、完整运行时序示例以 python agent-plus.py --plan "找到所有 TODO 并整理到 todo.md" 为例:[Planning] Breaking down task...[Plan] 3 steps created  1. 使用 grep 递归搜索所有 TODO 注释  2. 整理搜索结果为 Markdown 清单格式  3. 将清单写入 todo.md[Step 1/3] 使用 grep 递归搜索所有 TODO 注释[Tool] execute_bash({"command": "grep -rn 'TODO' --include='*.py' ."})  → ./app.py:23: # TODO: add error handling  → ./utils.py:7: # TODO: refactor this function  → ./main.py:45: # TODO: add logging找到 3 处 TODO 注释。[Step 2/3] 整理搜索结果为 Markdown 清单格式(LLM 看到步骤 1 的 grep 结果,直接整理,无需再次搜索)已整理为以下清单:- app.py:23 - add error handling- utils.py:7 - refactor this function- main.py:45 - add logging[Step 3/3] 将清单写入 todo.md[Tool] write_file({"path": "todo.md", "content": "# TODO List\n\n..."})已将 TODO 清单写入 todo.md。注意步骤 2 没有调用任何工具——LLM 直接从 messages 中读取了步骤 1 的 grep 输出并整理。这就是上下文传递的威力。六、记忆方案的局限与进化方向nanoAgent 的记忆方案堪称"最小可行记忆",但它清晰地暴露了四个需要进化的方向:记忆无差别截断 → 向量数据库 + 语义检索。只保留最后 50 行会丢失重要的早期信息。更好的方案是把记忆存入向量数据库(如 Chroma、Pinecone),每次只检索与当前任务语义相关的记忆。这就是 RAG 的核心思路。无记忆压缩 → 记忆蒸馏。当记忆超过阈值时,用 LLM 自动压缩旧记忆——提取关键事实,丢弃细节。比如把"执行了 grep -rn TODO,找到 app.py:23、utils.py:7、main.py:45 三处"压缩为"项目中有 3 处 TODO 待处理"。全量注入 prompt → 记忆作为工具。不把记忆塞进 system prompt,而是提供一个 search_memory 工具让 Agent 按需查询。Agent 自己决定什么时候需要回忆、回忆什么。单层记忆 → 分层记忆架构。参考人类记忆的工作方式:工作记忆(当前 messages)→ 短期记忆(最近几次对话摘要)→ 长期记忆(压缩后的关键事实)。每层的信息密度和保留时间不同。七、总结与下一篇预告三个核心启示:1. 记忆的本质是"信息搬运"。 LLM 没有真正的记忆,所有记忆都是把外部存储的信息搬运到 prompt 中。不同方案的区别只在于"存在哪、怎么找、搬多少"。2. 规划让 Agent 有了全局视角。 没有规划的 Agent 像是蒙着眼走迷宫;有规划的 Agent 至少先看了一眼地图。虽然地图可能不准确,但总比没有强。3. 上下文传递是多步执行的关键。messages 列表在步骤间的共享,让后续步骤能利用前面步骤的执行结果,避免重复工作。现在我们的 Agent 有了记忆和规划能力,但还有三个问题没解决:工具是硬编码的——想接入 Slack、GitHub、数据库等外部服务怎么办?没有行为约束——不同项目、不同团队对 Agent 的要求完全不同,怎么定制?规划是被动触发的——用户必须手动加 --plan 参数,Agent 自己不知道什么时候该规划。这三个问题,引出了 Agent 架构中最后也是最精彩的三个概念:MCP(工具协议)、Rules(行为规则)、Skills(技能注册)。如果你用过 OpenClaw 或 Claude Code,你一定见过 CLAUDE.md 规则文件和 MCP 工具配置——它们正是这三个概念的工程化产物。在 第三篇:Rules、Skills 与 MCP 中,我们将看到 agent-claudecode.py 如何用 265 行代码,把 nanoAgent 进化为一个接近 OpenClaw / Claude Code 的完整 Agent 框架。*本文基于 sanbuphy/nanoAgent 的 agent-plus.py 分析。如果你还没有读过系列第一篇,建议先从 底层原理,只有 115行 开始。相关链接[1] nanoAgent: cid:link_0[2] agent-plus.py: https://github.com/GitHubxsy/nanoAgent/blob/main/agent-plus.py[3] agent.py: https://github.com/GitHubxsy/nanoAgent/blob/main/agent.py[4] 第一篇:底层原理,只有 115 行 —— 工具 + 循环: https://mp.weixin.qq.com/s/gz_vPvgTdozh4FO6vEdF-Q AGENT交流群,一起玩转AGENT  
  • [技术干货] 从零开始理解 Agent(一):OpenClaw / Claude Code 的底层原理,只有 115 行
    欢迎阅读 「从零开始理解 Agent」系列 文章,我们将通过一个不到 300 行的开源项目 nanoAgent,逐层拆解 OpenClaw / Claude Code 等 AI Agent 背后的全部核心概念。第一篇:底层原理,只有 115 loc(*lines of code)—— 工具 + 循环(本文)第二篇:记忆与规划 ——182 loc第三篇:Rules、Skills 与 MCP——265 loc★ 项目地址:cid:link_0作者:十一很多人用过 ChatGPT、Claude 这样的对话式 AI,也听说过 AI Agent 这个概念。最近 OpenClaw、Claude Code 这类 Agent 火遍了整个开发者圈子——它们能自主写代码、发邮件,完成以前需要人类手动操作的整个工作流程。但 Agent 到底和普通对话有什么区别?OpenClaw / Claude Code 这类工具的底层原理是什么?Agent 是怎么"使用工具"的?本文通过逐行解读一个仅 115 行的极简 Agent 实现—— sanbuphy/nanoAgent,带你彻底搞懂这些问题。理解了这 115 行代码,你就理解了 OpenClaw、Claude Code、Cursor Agent 等一切 Agent 的共同底座。一、先说结论:Agent 和普通对话的核心区别在深入代码之前,先建立一个直觉:一句话总结:Agent = LLM + 工具 + 循环。普通对话是"你问我答",Agent 是"你给我一个目标,我自己想办法完成"。这三个要素缺一不可。没有 LLM,就没有"思考"能力;没有工具,就无法作用于真实世界;没有循环,就做不了多步任务。接下来我们看 nanoAgent 是怎么用 115 行代码实现这三要素的。二、nanoAgent 全局架构nanoAgent 的 agent.py 只有 115 行,但五脏俱全。整体结构可以拆成四个部分:下面逐层拆解。三、逐层解读源码3.1 LLM 客户端初始化import osimport jsonimport subprocessimport sysfrom openai import OpenAIclient = OpenAI( api_key=os.environ.get("OPENAI_API_KEY"), base_url=os.environ.get("OPENAI_BASE_URL"))这里用的是 OpenAI 的 Python SDK,但通过 base_url 环境变量,可以指向任何兼容 OpenAI API 格式的服务(比如 DeepSeek、Qwen、本地 Ollama 等)。这是一个非常实用的设计——Agent 框架不绑定具体模型。3.2 工具定义:告诉 LLM "你有哪些能力"tools = [ { "type": "function", "function": { "name": "execute_bash", "description": "Execute a bash command on the system", "parameters": { "type": "object", "properties": { "command": {"type": "string", "description": "The bash command to execute"} }, "required": ["command"] } } }, # ... read_file, write_file 类似]这是 OpenAI Function Calling 的标准格式。这段 JSON Schema 本质上是一份工具说明书,它会随着每次 API 请求一起发送给 LLM。LLM 读到这份说明书后,就"知道"自己可以执行 bash 命令、读文件、写文件。nanoAgent 定义了三个工具:★ 关键洞察:LLM 本身不会执行任何代码。它只是根据工具说明书,输出一段结构化的 JSON,表达"我想调用 execute_bash,参数是 ls -la"。真正的执行发生在我们的 Python 代码里。这个"LLM 输出意图、代码执行动作"的分工,是理解所有 Agent 系统的关键。3.3 工具实现:把 LLM 的"意图"变成"行动"def execute_bash(command): try: result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=30) return result.stdout + result.stderr except Exception as e: returnf"Error: {str(e)}"def read_file(path): try: with open(path, 'r') as f: return f.read() except Exception as e: returnf"Error: {str(e)}"def write_file(path, content): try: with open(path, 'w') as f: f.write(content) returnf"Successfully wrote to {path}" except Exception as e: returnf"Error: {str(e)}"这三个函数就是工具的"真身"。几个值得注意的细节:错误处理:每个函数都用 try-except 包裹,确保即使执行出错也能把错误信息返回给 LLM,而不是让整个程序崩溃。这很重要——LLM 看到错误后可以自行修正策略。timeout=30:bash 命令有 30 秒超时限制,防止死循环或长时间阻塞。shell=True:意味着可以执行管道、重定向等复杂 shell 语法,能力很强,但安全风险也很大。接下来是一个路由表,把工具名映射到实际函数:available_functions = { "execute_bash": execute_bash, "read_file": read_file, "write_file": write_file}这个字典是工具调度的核心——当 LLM 说"我要调用 execute_bash"时,代码通过这个字典找到对应的 Python 函数并执行。3.4 Agent 核心循环:最精华的 20 行代码def run_agent(user_message, max_iterations=5): messages = [ {"role": "system", "content": "You are a helpful assistant that can interact with the system. Be concise."}, {"role": "user", "content": user_message} ] for _ in range(max_iterations): # Step 1: 把完整对话历史 + 工具列表发给 LLM response = client.chat.completions.create( model=os.environ.get("OPENAI_MODEL", "gpt-4o-mini"), messages=messages, tools=tools ) message = response.choices[0].message messages.append(message) # Step 2: 如果 LLM 没有调用工具 → 任务完成,返回文本回答 ifnot message.tool_calls: return message.content # Step 3: 如果 LLM 要调用工具 → 逐个执行,把结果追加到对话历史 for tool_call in message.tool_calls: function_name = tool_call.function.name function_args = json.loads(tool_call.function.arguments) print(f"[Tool] {function_name}({function_args})") function_response = available_functions[function_name](**function_args "function_name") messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": function_response }) return"Max iterations reached"这 20 多行代码是整个 Agent 的灵魂。让我逐步拆解这个循环里发生了什么。四、Agent 循环的运行时序(核心重点)以一个具体例子来说明。假设用户运行:python agent.py "统计当前目录下有多少个 Python 文件,并把结果写入 count.txt"Agent 的执行过程如下:第 1 轮循环发送给 LLM 的 messages:[system] You are a helpful assistant...[user] 统计当前目录下有多少个 Python 文件,并把结果写入 count.txtLLM 返回: 不是普通文本,而是一个 tool_call:{ "tool_calls": [{ "function": {"name": "execute_bash", "arguments": "{\"command\": \"find . -name '*.py' | wc -l\"}"} }]}代码执行: 调用 execute_bash("find . -name '*.py' | wc -l"),得到结果 "42\n"追加到 messages:[tool] 42第 2 轮循环发送给 LLM 的 messages: 现在包含了完整历史(system + user + assistant的tool_call + tool结果)LLM 看到结果是 42,决定写入文件:{ "tool_calls": [{ "function": {"name": "write_file", "arguments": "{\"path\": \"count.txt\", \"content\": \"Python files: 42\"}"} }]}代码执行: 调用 write_file("count.txt", "Python files: 42")第 3 轮循环LLM 看到文件写入成功,判断任务已完成,返回纯文本:"已统计完成,当前目录下共有 42 个 Python 文件,结果已写入 count.txt。"not message.tool_calls 为 True → 退出循环,返回结果。用一张图来表示:用户任务 │ ▼┌──────────────────────────────────────────────────┐│ Agent Loop ││ ││ ┌─────────┐ ┌──────────┐ ┌──────────────┐ ││ │ 发送给 │───▶│ LLM 决策 │───▶│ 有tool_call? │ ││ │ LLM │ │ │ └──────┬───────┘ ││ └─────────┘ └──────────┘ │ ││ ▲ Yes │ No ││ │ ┌─────┴─────┐ ││ │ ▼ ▼ ││ ┌────┴────────┐ ┌──────────┐ 返回文本 ││ │ 结果追加到 │◀─────────│ 执行工具 │ ──────▶ ││ │ messages │ └──────────┘ 结束 ││ └─────────────┘ │└──────────────────────────────────────────────────┘五、深入理解几个关键设计5.1 为什么需要 max_iterations?for _ in range(max_iterations): # 默认 5 次这是一个安全阀。如果 LLM 陷入死循环(比如反复执行同一个失败的命令),max_iterations 确保程序最终会停下来。在生产级 Agent 中,这个值通常更大(比如 Claude Code 可以连续执行数十步),同时会配合更复杂的终止策略。5.2 messages 列表为什么如此重要?messages 是 Agent 的短期记忆。每一轮循环,它都会累积 LLM 的回复(包括它想调用什么工具)以及工具的执行结果。当这个列表在下一轮发送给 LLM 时,LLM 能看到完整的"行动-观察"历史,从而做出更合理的下一步决策。这就是 Agent 和简单对话的本质区别——Agent 维护了一条包含行动轨迹的上下文链。但请注意,这里的 messages 只在单次运行中存在。程序退出后,一切归零。Agent 下次运行时完全不记得上次做过什么。这个"失忆"问题,正是我们在第二篇连载中要解决的。5.3 LLM 是怎么"决定"调用工具的?这是最容易产生误解的地方。LLM 并没有真的在"执行代码"或"调用函数"。实际发生的是:我们在 API 请求中传入了 tools 参数(工具说明书)LLM 经过训练,学会了在适当的时候输出一种特殊的结构化格式(tool_calls)这个格式本质上就是一段 JSON,描述"我想调用哪个函数、传什么参数"我们的代码解析这段 JSON,执行真正的函数,再把结果喂回给 LLM所以整个过程可以理解为一种协作协议:LLM 的职责:思考、决策、生成工具调用指令代码的职责:解析指令、执行工具、返回结果LLM 是"大脑",代码是"手脚"。5.4 tool_call_id 的作用messages.append({    "role": "tool",    "tool_call_id": tool_call.id,    "content": function_response})tool_call_id 是 OpenAI API 的要求,用于将工具返回结果与对应的调用请求关联起来。当 LLM 在一次回复中同时调用多个工具时(并行调用),这个 ID 确保每个结果能正确匹配到对应的调用。六、这个 Agent 还缺什么?nanoAgent 的极简设计让核心概念一目了然,但如果你仔细想想,会发现它有几个根本性的缺陷:1. 没有记忆。 每次运行都是一张白纸。昨天让它创建的文件,今天问它"你昨天干了什么",它一脸茫然。2. 没有规划。 面对"重构整个项目"这样的复杂任务,它只能走一步看一步,容易迷失在细节中。3. 工具是硬编码的。 只有 3 个工具,想加新工具必须改代码。没有任何扩展机制。4. 没有行为约束。 它可以执行 rm -rf /,没有任何规则告诉它什么该做、什么不该做。这些缺陷,恰好对应了 Agent 架构中更高层次的需求。nanoAgent 的作者也意识到了这一点,所以他写了两个进化版本来逐一解决。七、从 nanoAgent 看 Agent 的本质回到最初的问题:Agent 到底是什么?通过 nanoAgent 的 115 行代码,我们可以提炼出 Agent 的三个本质要素:1. 感知(Perception)—— 通过工具获取外部信息(read_file、execute_bash 的输出)2. 决策(Reasoning)—— LLM 根据任务目标和已有观察,决定下一步行动3. 行动(Action)—— 通过工具作用于外部环境(write_file、execute_bash)这三者在一个循环中不断迭代,直到 LLM 判断任务完成(不再调用工具)。这就是 Agent 最朴素、最本质的运行方式——**"思考 → 行动 → 观察"(ReAct)**范式。无论是 OpenClaw、Claude Code、Cursor 还是 Devin,底层都遵循这个范式。当你在 OpenClaw 中看到它自动 grep 搜索代码、edit 修改文件、bash 跑测试时,背后就是这样一个循环在驱动。nanoAgent 用最少的代码,把这个范式展现得淋漓尽致。八、动手试一试如果你想亲手体验,只需:如果你想亲手体验,只需:# 克隆项目git clone cid:link_0.gitcd nanoAgent# 设置环境变量(可以用任何兼容 OpenAI API 的服务)export OPENAI_API_KEY="your-key"export OPENAI_BASE_URL="https://api.openai.com/v1" # 或 DeepSeek/Qwen 等# 运行python agent.py "帮我创建一个 hello.py 文件,内容是打印当前时间"然后观察终端输出的 [Tool] 日志,你就能清晰地看到 Agent 的每一步决策和行动。下一篇预告现在我们有了一个能干活的 Agent,但它像一条金鱼——做完就忘。如何让 Agent 拥有记忆,记住之前做过的事?如何让它面对复杂任务时先规划再执行,而不是蒙头乱撞?这些问题,我们在 第二篇:记忆与规划——182 loc 中解答。代码只多了 67 行,但能力产生质变。本文基于 sanbuphy/nanoAgent 项目分析,感谢项目作者用极简的代码诠释了 Agent 的核心思想。
  • [技术干货] KubeEdge 1.23.0版本发布!性能与可靠性持续提升!
    京时间2026年3月11日,KubeEdge 发布 1.23.0 版本。新版本通过深度优化 Windows 兼容性、引入设备异常检测框架以及重构边缘数据库,显著提升了边缘侧的运维能力、数据处理可靠性和整体性能。同时发布了新版本 Dashboard,在用户交互上带来全新体验。KubeEdge v1.23.0 新增特性:Windows 操作系统下的 EdgeCore 与 Keadm 能力增强 新增设备异常检测能力优化边缘侧查询节点流程,降低边云通道带宽占用使用 Gorm 替换 Beego,并重构边缘数据库升级 K8s 依赖到1.32Dashboard 新版本发布:国际化(中文)支持、性能提升与页面优化  新特性概览  ▍Windows 操作系统下的 EdgeCore 与 Keadm 能力增强在 1.23.0 版本中,我们对 EdgeCore 和 keadm 在 Windows OS 的能力进行了如下增强:提供了本地DMI服务:由于 Windows 不支持 Unix Domain Socket,我们引用了Windows 命名管道(Named Pipes)实现本地网络通信;keadm 升级/下载增强:在v1.23.0中,keadm 会检测本地 edgecore.exe 的版本信息,如果或有更高版本可用,则自动重新下载 EdgeCore 包,避免因本地已存在 edgecore.exe 而导致升级被跳过;可观测性增强:在新版本中,EdgeCore 日志会被重定向到可配置的日志文件,优化Windows环境下的运维与故障排查。➤  更多信息可参考:cid:link_3cid:link_4cid:link_5▍新增设备异常检测能力v1.23.0 引入了设备异常检测框架,您可在 Device CRD 的 pushMethod 字段中指定异常检测相关配置apiVersion: devices.kubeedge.io/v1beta1kind: Devicespec: properties: pushMethod: anomolyDetection: ... // 指定异常检测字段,如设备工作状态等同时我们在 Mapper 中实现了设备异常检测处理逻辑,您可以定制化设计处理设备异常数据。 另外,我们在 Example 仓库提供了设备异常检测的Demo,方便您快速了解并试用新的能力。详情查看:cid:link_11➤  更多信息可参考:cid:link_6cid:link_7▍优化边缘侧查询节点流程,降低边云通道带宽占用之前的版本中,EdgeCore 需要通过边云通道远程查询节点信息,在大规模场景下,边云通道带宽消耗尤其显著。在新版本中,EdgeCore 直接从边缘数据库查询节点,同时,CloudCore 检测到节点信息更新时会自动同步到边缘数据库,显著提升了大规模边缘场景下的系统性能和可靠性。➤ 更多信息可参考:cid:link_7▍使用 Gorm 替换 Beego,并重构边缘数据库原有的边缘数据库使用的 Beego 框架,实际上仅用到了 ORM 部分。在新版本中,我们使用更轻量的 Gorm 替换 Beego 框架。同时,对边缘数据库进行重构,在 MetaManager 中引入统一的数据库操作入口,使数据库交互更清晰、易维护。➤ 更多信息可参考:cid:link_2cid:link_8▍升级K8s依赖到1.32新版本将依赖的 Kubernetes 版本升级到 v1.32.10,您可以在云和边缘使用新版本的特性。➤ 更多信息可参考:cid:link_9▍Dashboard 新版本发布:国际化(中文)支持、性能提升与页面优化Dashboard v0.2.0 正式发布,包括如下更新:引入 Backend-for-Frontend(BFF)架构,建立数据处理中间层,优化数据处理逻辑,提升前端性能;引入国际化语言框架,并新增中文语言包支持;全面优化 Dashboard的UI 体验,统一页面风格,重点优化 PodTable、TableView 等表单组件,提升用户交互体验。➤ 更多信息可参考:cid:link_1 版本升级注意事项 v1.23.0 开始,Device CRD 的 Status 字段将分离出来,单独作为 DeviceStatus CRD 使用。该变更兼容旧版CRD,但需注意,在后续版本中设备状态需要通过新的 DeviceStatus CRD 获取。➤ 更多信息可参考:cid:link_10▍致谢感谢KubeEdge社区技术指导委员会(TSC)、各SIG成员对v1.23版本开发的支持与贡献,未来KubeEdge将持续在新场景探索与支持、稳定性、安全性、可扩展性等方面持续发展与演进!▍相关链接➤  Release Notes:cid:link_0添加小助手k8s2222回复KubeEdge进群
  • [技术干货] 【LLM推理专栏】深入解析 Kthena Router
     ▍1. 前言随着大语言模型(LLM)日益成为现代应用的核心,支持它们的基础设施必须不断演进,以满足苛刻的性能、可扩展性和成本要求。在生产环境中部署 LLM 面临着独特的挑战:模型需要大量资源,推理工作负载变化显著,用户期望低延迟和高吞吐量。传统的负载均衡器和 API 网关虽然在传统 Web 服务中表现出色,但缺乏智能路由 AI 推理流量所需的感知能力。Kthena[1] Router 正面应对这些挑战。它是一个 Kubernetes 原生的独立推理Router,专为 LLM 服务工作负载而设计。与通用代理或负载均衡器不同,Kthena Router 具有模型感知能力,根据推理引擎的实时指标做出智能路由决策。这使得其能够实现复杂的流量管理策略,显著提高吞吐量、降低延迟并降低运营成本。Kthena Router 能够与现有 API 网关基础设施无缝集成,同时提供专为 AI 工作负载设计的高级功能:模型感知路由:利用推理引擎(vLLM、SGLang、TGI)的实时指标做出智能路由决策LoRA 感知负载均衡:智能路由到已加载所需 LoRA Adapter 的 Pod,将数百毫秒的 Adapter Swap 延迟降低到接近零高级调度算法:包括Prefix Cache感知、KV Cache感知和公平性调度等PD分离:原生支持 xPyD(x-prefill/y-decode)部署模式Kthena Router 作为独立二进制文件部署,极简依赖,确保轻量级运行和简单部署。它持续监控推理引擎指标,以获取有关模型状态的实时信息,包括当前加载的 LoRA Adapter、KV Cache 利用率、请求队列长度和延迟指标(TTFT/TPOT)。这种实时感知能力使 Router 能够做出传统负载均衡器根本无法实现的最优路由决策。▍2. 架构Kthena Router 实现了一个清晰的模块化架构,专为性能和可扩展性而设计。该系统由几个核心组件协同工作,提供智能请求路由。Kthena Router 架构2.1 核心组件概述Router: 负责接收、处理和转发请求的核心执行框架。它协调所有其他组件之间的交互,并维护从初始接收到最终响应的请求生命周期。Listener: 管理 HTTP/HTTPS 监听器并处理指定端口上的传入流量。它为不同协议提供灵活的配置,并可以绑定到多个地址以服务各种类型的请求。监听器确保高效的连接处理,并支持流式和非流式请求模式。Controller: 一个 Kubernetes 原生组件,用于同步和处理 Pod 和自定义资源(CR),例如 ModelRoute 和 ModelServer。Controller监视集群中的变化,并相应地更新Router的内部状态,确保路由决策始终基于当前的集群拓扑。Filters: 包含两个关键子模块,在请求到达后端之前处理请求:Auth:处理流量认证和授权,支持 API Key、JWTRateLimit:管理全面的速率限制策略,包括输入token和输出token限制Backend: 提供访问各种推理引擎的抽象层。它掩盖了不同框架(如 vLLM、SGLang 和 TGI)之间Metrics接口访问方法和Metrics命名约定的差异,向调度器提供统一的接口。Metrics Fetcher: 持续从模型 Pod 上运行的推理引擎端点收集实时Metrics。它收集关键性能数据,包括:KV Cache利用率当前加载的 LoRA Adapter请求队列长度延迟指标(TTFT,TPOT)Datastore: 一个统一的数据存储层,提供对 ModelServer 到 Pod 关联、Base Model/LoRA 配置和运行时Metrics的高效访问。它作为所有路由相关信息的中央存储库,并支持实时更新的回调。Scheduler: Router的大脑,实现复杂的流量调度算法。它由一个调度框架和各种可插拔的调度算法插件组成。该框架集成并运行不同的调度插件,以过滤和评分与 ModelServer 对应的 Pod 集合,选择全局最优的 Pod 作为最终访问目标。Kthena Router 组件▍3. Router APIKthena Router 的路由行为由两个关键的自定义资源定义(CRD)控制:ModelServer 和 ModelRoute。这些声明式 API 允许您使用熟悉的 Kubernetes 模式定义复杂的路由策略。3.1 ModelRouteModelRoute 根据请求特征定义流量路由规则。它根据模型名称、LoRA Adapter、HTTP Header和其他条件确定哪些 ModelServer 应该处理请求。关键字段包括:ModelName:要在传入请求中匹配的模型名称LoRAAdapters:此路由支持的 LoRA 适配器名称列表Rules:有序的路由规则列表,每个规则包含:ModelMatch:匹配请求的条件(Header、URI 等)TargetModels:要路由到的 ModelServer 列表,可选权重RateLimit:基于token的速率限制配置有关 ModelRoute 的更多详细信息,请参阅 定义[2]。3.2 ModelServerModelServer 定义推理服务实例及其访问策略。它标识运行模型的 Pod,指定正在使用的推理框架,并定义如何处理流量。关键字段包括:WorkloadSelector:通过标签标识 Pod,并支持 PD(prefill-decode)组规范Model:指定服务器托管的Base Model名称InferenceFramework:指示推理引擎(vLLM、SGLang、TGI 等)WorkloadPort:定义推理服务监听的端口TrafficPolicy:配置超时、重试策略和其他流量处理行为KVConnector:为 PD 分离部署指定 KV Connector类型(HTTP、Nixl、LMCache、Mooncake)有关 ModelServer 的更多详细信息,请参阅 定义[3]。3.3 示例:基于HTTP Header的多模型路由对于分层服务产品,根据头将用户路由到不同大小的模型:apiVersion: networking.serving.volcano.sh/v1alpha1kind: ModelRoutemetadata: name: deepseek-multi-models namespace: defaultspec: modelName: "deepseek-multi-models" rules: - name: "premium" modelMatch: headers: user-type: exact: premium targetModels: - modelServerName: "deepseek-r1-7b" - name: "default" targetModels: - modelServerName: "deepseek-r1-1-5b"---apiVersion: networking.serving.volcano.sh/v1alpha1kind: ModelServermetadata: name: deepseek-r1-7b namespace: defaultspec: workloadSelector: matchLabels: app: deepseek-r1-7b workloadPort: port: 8000 model: "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B" inferenceEngine: "vLLM" trafficPolicy: timeout: 10s---apiVersion: networking.serving.volcano.sh/v1alpha1kind: ModelServermetadata: name: deepseek-r1-1-5b namespace: defaultspec: workloadSelector: matchLabels: app: deepseek-r1-1-5b workloadPort: port: 8000 model: "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B" inferenceEngine: "vLLM" trafficPolicy: timeout: 10s流量处理过程:请求到达,模型名称为 "deepseek-multi-models"Router检查HTTP Header中是否存在 user-type: premium高级用户 → 路由到更大的 7B 模型以获得更好的质量普通用户 → 路由到更小的 1.5B 模型以提高成本效率测试高级路由:curl http://$ROUTER_IP/v1/completions \ -H "Content-Type: application/json" \ -H "user-type: premium" \ -d '{"model": "deepseek-multi-models", "prompt": "Explain quantum computing"}'这个示例演示了 ModelRoute 和 ModelServer CRD 如何通过标准 Kubernetes API 提供对复杂路由策略的灵活声明式控制。▍4. 核心功能4.1 智能调度插件真正将 Kthena Router 与传统负载均衡器区分开的是其一套模型感知调度插件。这些插件利用实时推理引擎指标做出智能路由决策,显著提高性能。4.1.1 LoRA 亲和性调度LoRA(低秩适应)Adapter 能够在不重新部署 Base Model 的情况下实现微调的模型行为。然而,加载和卸载 Adapter 会引入延迟。LoRA 亲和性插件通过智能路由最小化这种开销。工作原理:持续跟踪每个 Pod 上当前加载的 LoRA Adapter 状态将需要特定 LoRA 的请求优先路由到已加载该 Adapter 的 Pod通过缓存命中避免 Adapter 加载/卸载操作优势:消除 Adapter Swap 延迟: 将数百毫秒的 Adapter 切换延迟降至接近零提高响应速度: 避免动态加载 Adapter 带来的额外等待时间支持多 LoRA 场景: 高效处理多个 LoRA Adapter 的混合工作负载4.1.2 Least Latency 调度Least Latency 插件根据实时延迟指标将请求路由到响应最快的 Pod,优化用户体验和整体生成速度。工作原理:持续监控推理引擎的延迟指标综合评估 TTFT(首 token 时间)和 TPOT(每输出 token 时间)将请求路由到延迟最低的 Pod动态适应 Pod 性能变化关键指标:TTFT(首 token 时间): 影响流式响应和用户感知延迟TPOT(每输出 token 时间): 决定整体生成速度和吞吐量4.1.3 Least Request 调度Least Request 插件基于请求队列状态进行负载均衡,将请求路由到最不繁忙的 Pod,确保均匀的负载分布。工作原理:监控推理引擎的 num_requests_running 和 num_requests_waiting 指标计算每个 Pod 的总待处理工作负载(运行中 + 等待中)将新请求路由到负载最低的 Pod动态平衡各 Pod 之间的工作分配优势:防止热点: 避免部分 Pod 过载而其他 Pod 空闲均衡负载: 确保所有 Pod 的利用率趋于一致提高吞吐量: 通过最优负载分配最大化整体处理能力4.1.4 GPU Usage 调度GPU Usage 插件基于 GPU 缓存使用率进行负载均衡,将请求路由到缓存压力较小的 Pod,优化 GPU 资源利用率。工作原理:持续监控每个 Pod 的 GPU 缓存使用率指标计算可用缓存容量:score = (1.0 - GPU缓存使用率) × 100  优先将请求路由到 GPU 缓存使用率较低的 Pod避免因缓存耗尽导致的性能下降和请求失败优势:防止缓存溢出: 避免将请求发送到缓存已满的 Pod提高稳定性: 减少因缓存不足导致的请求失败和重试负载均衡: 确保 GPU 缓存资源在所有 Pod 间均衡使用提升吞吐量: 保持各 Pod 在最优缓存利用率区间运行4.1.5 Prefix Cache 感知调度现代推理引擎(如 vLLM 和 SGLang)实现 Prefix Cache 机制,将常用的 Prompt 前缀缓存起来以避免冗余计算。Prefix Cache 感知插件通过智能路由策略最大化前缀缓存的命中率,显著提升推理性能。核心机制:滚动哈希生成:将 Prompt 按固定大小(默认 64 字节)划分为块使用 xxHash 算法为每个块生成滚动哈希每个哈希值结合前一个块的哈希,形成依赖链哈希匹配时保证所有前置块也匹配,实现高效前缀识别内存缓存存储:使用三层映射结构:模型 → 哈希 → Pod采用 LRU(最近最少使用)缓存策略管理内存自动清理过期条目,保持缓存效率默认缓存容量 50,000 条,返回 Top-5 最佳匹配智能评分算法:根据前缀匹配长度对 Pod 评分评分公式:score = (匹配块数 / 总块数) × 100 优先选择具有最长匹配前缀的 Pod提高缓存命中率和推理效率适用场景:多轮对话系统,用户在同一 session 中发送多个相关请求具有固定 system prompt 的应用场景问答系统中频繁使用相同上下文的查询4.1.6 KV Cache Aware 调度KV Cache Aware 插件是一个高级调度插件,通过基于token级别的块匹配来智能路由请求,最大化 KV 缓存命中率。与简单监控缓存利用率不同,该插件实现了细粒度的内容感知调度,显著提升性能。核心机制:Token 块哈希:将传入的prompt分词后按固定大小(默认128个token)划分为块使用 SHA-256 为每个块生成标准化哈希值支持可配置的最大块数(默认128块)以平衡性能和准确性分布式缓存跟踪:使用 Redis 维护全局的块到 Pod 映射Redis key 格式: matrix:kv:block:{model}@{hash}  每个 key 存储包含该块的 Pod 列表及其缓存时间戳通过 Redis Pipeline 批量查询实现高效的分布式协调智能评分算法:查找每个token块在哪些 Pod 上已缓存从第一个块开始计算连续匹配长度优先选择具有最长连续匹配前缀的 Pod评分公式: score = (匹配块数 / 总块数) × 100Tokenizer 集成:支持多种tokenizer backend(本地和远程 vLLM)自动处理不同模型的分词差异确保一致的token到块的映射优势:精确匹配: 不仅考虑缓存容量,还考虑实际缓存的内容避免冷启动: 将请求路由到已经处理过相似prompt的 Pod提升吞吐量: 减少重复计算,在long system prompt场景下可实现 2.73 倍吞吐量提升降低延迟: TTFT 可降低 73.5%,显著改善用户体验更多技术细节请参考 KV Cache Aware 插件设计文档[4]。4.1.7 插件配置这些插件通过调度器框架协同工作。您可以通过Router配置来配置启用哪些插件及其相对权重。调度器框架按顺序运行启用的插件:Filter:插件过滤不合适的 Pod(例如,缓存不足、错误的 LoRA)Score:插件根据其条件对剩余 Pod 评分Select:为请求选择得分最高的 Pod这种可组合的架构允许您根据特定的工作负载需求定制路由行为。4.2 公平性调度公平性调度确保基于token消耗历史在用户之间公平分配资源。工作原理:跟踪每个用户每个模型的累积token使用量(输入 + 输出)分配与历史使用量成反比的请求优先级将请求排队并按优先级顺序处理防止任何单个用户垄断资源用例:具有共享基础设施的多租户平台具有公平共享策略的研究集群需要基于使用量的节流的 SLA 驱动系统4.3 PD分离支持对于高级部署模式,Kthena Router 原生支持PD分离(xPyD),其中计算密集型的Prefill阶段与token生成Decode阶段分离。工作原理:从 ModelServer CRD 识别 PD 组配置将Prefill请求路由到Prefill优化的 Pod通过可配置的连接器(HTTP、Nixl 等)传输 KV 缓存状态将Decode请求路由到Decode优化的 Pod对客户端透明地协调两阶段过程优势:通过将工作负载特征与硬件匹配来优化硬件利用率通过为每个阶段使用专用硬件来减少延迟通过更好的资源分配提高成本效率4.4 基于token的RateLimitKthena Router 提供全面的RateLimit功能,以保护您的推理基础设施免受过载,并确保跨用户的公平资源分配。Input token限制:控制每个用户或 API 密钥的传入prompt token速率Output token限制:限制生成的token以管理计算成本Local RateLimit:在每个Router实例基础上实施限制。Global RateLimit:使用 Redis 等中央存储在所有Router实例之间实施共享限制。4.5 可观测性Kthena Router 提供为生产 LLM 服务设计的全面可观测性功能:指标:在 /metrics endpoint公开详细指标,包括请求延迟、token消耗、调度器插件性能和RateLimit统计信息结构化访问日志:以 JSON 或文本格式记录完整的请求生命周期,包括路由决策、用时分布和token跟踪Debug endpoint:提供 /debug/config_dump/* API 来检查内部状态、ModelRoute/ModelServer 配置和实时 Pod 等指标标准集成:与 Prometheus、Grafana、ELK 等可观测性堆栈无缝协作,用于监控、告警和故障排除▍5. 性能Kthena Router 中的 ScorePlugin 模块利用可配置的可插拔架构来实现推理请求的多维评分和智能路由。为了演示智能调度的影响,我们基于 DeepSeek-R1-Distill-Qwen-7B 模型构建了一个标准化的基准测试环境,以评估不同调度策略在长短system prompt场景下的性能。实验结果表明,在long system prompt场景中,KV Cache Aware Plugin + Least Request Plugin 组合实现了 2.73 倍的吞吐量,并将 TTFT 延迟降低了 73.5%,显著优化了整体推理服务性能,验证了Cache感知调度对大规模模型推理的核心价值。5.1 实验设置使用 DeepSeek-R1-Distill-Qwen-7B 模型构建了标准化的基准测试环境,以评估不同调度策略的性能。表 1:实验环境配置5.2 Long System Prompt Scenario(4096 token)表 2:性能指标 - Long System Prompt ▍6. 结论Kthena Router 代表了 LLM 服务基础设施的重大飞跃。通过超越简单的负载均衡,实现模型感知、指标驱动的路由,它释放了以前无法实现的显著性能改进和成本节省。它是开源的,现在就可以使用。Kthena文档[5]提供了安装、配置和部署的全面指南。Kthena-router 示例目录[6]包含用于常见场景的即用型配置。无论您是运行单个模型还是管理复杂的多租户 LLM 平台,Kthena Router 都提供了最大化性能、最小化成本和提供卓越用户体验所需的智能路由功能。作者 | YaoZengzeng   相关链接[1] Kthena: https://kthena.volcano.sh/[2] ModelRoute 定义: cid:link_1[3] ModelServer定义: cid:link_2[4] KV Cache Aware 插件设计文档: cid:link_3[5] Kthena文档: https://volcano-sh.github.io/kthena/[6] Kthena-router 示例目录: cid:link_4 扫码进群,社区小助手k8s2222
  • [技术干货] Volcano v1.14 重磅发布!迈向 AI 统一调度新纪元
    Volcano[1] 社区 v1.14 现已正式发布。随着 AI 业务形态从单一的离线训练向在线推理、Agent 智能体等多元化场景延伸,调度系统面临着前所未有的挑战。v1.14[2] 通过架构级的创新,在保持大规模批量计算优势的同时,补齐了对延迟敏感型业务的调度短板,向着 “AI训推、RL、Agent全场景统一调度平台” 的目标迈出了坚实一步。  版本亮点  📌 v1.14.0 版本带来以下重磅更新:统一调度平台架构多调度器架构升级:动态节点分片机制 (Alpha)AI Agent 工作负载极速调度能力 (Alpha)网络拓扑感知调度增强HyperNode 级 Binpack 策略SubGroup 级精细化拓扑感知PodGroup 与 SubGroup 多层级 Gang SchedulingVolcano Job 分区支持混部能力增强全面支持通用操作系统(Ubuntu、CentOS 等)Cgroup V2 全面适配CPU 动态压制基于 Cgroup V2 的内存 QoS支持 CPU Burst支持 systemd driver 自动检测异构硬件支持昇腾 vNPU 调度(支持 MindCluster 和 HAMi 模式)Volcano Global 增强HyperJob 多集群作业自动拆分数据感知多集群调度Volcano Dashboard 增强PodGroup 全景可视化Job / Queue 全生命周期管理▍多调度器架构升级:动态节点分片机制 (Alpha)随着 Volcano 承载的工作负载类型日益丰富、规模持续扩大,单一调度器架构逐渐显露瓶颈。批量训练、AI Agent、微服务等不同类型的工作负载,对调度时延、资源利用模式的诉求各不相同。单调度器难以兼顾,而静态资源划分又会造成利用率低下。新引入的 Sharding Controller 构建了一套可扩展的多调度器架构,能够根据集群实时状态,为每个调度器动态计算候选节点池。与传统的静态分区不同,Sharding Controller 采用动态计算的方式划分资源,而非强制硬隔离。这种灵活机制让 Volcano 真正成为"一个平台调度所有负载"的统一调度平台,同时保持高吞吐、低时延。核心能力:动态分片策略:支持多种策略来计算动态候选节点池。当前版本率先支持基于 CPU 利用率的分片策略,并采用了可扩展的架构设计,以便未来轻松集成更多分片算法。节点池化管理:引入 NodeShard CRD,为特定调度器管理专属的动态候选节点池。支持大规模集群:通过在多个调度器之间灵活分配负载,天然适配大规模集群场景。多调度器协同:支持多种调度器组合的无缝协作。无论是部署多个 Batch Scheduler 进行负载分担,还是混合部署 Agent Scheduler 与 Batch Scheduler 以应对不同业务需求,都能灵活适配。配置示例:# Sharding Controller 启动参数--scheduler-configs="volcano:volcano:0.0:0.6:false:2:100,agent-scheduler:agent:0.7:1.0:true:2:100"--shard-sync-period=60s--enable-node-event-trigger=true# 参数格式: name:type:min_util:max_util:prefer_warmup:min_nodes:max_nodes🔗 相关 PR:cid:link_9设计文档:Sharding Controller Design[3]感谢社区开发者:@ssfffss, @Haoran, @qi-min▍AI Agent 工作负载极速调度 (Alpha)AI Agent 类业务对时延极度敏感,任务创建频繁且生命周期短,对调度器的响应速度和吞吐量提出了严苛要求。原生 Volcano Batch Scheduler 专为批量计算设计,按固定周期处理 Pod,难以满足 Agent 场景的毫秒级响应需求。为打造兼容批量计算与延迟敏感型业务的统一调度平台,v1.14 引入了专用的 Agent Scheduler。它通过 Sharding Controller 与批量调度器协同工作,各司其职又无缝配合,真正实现"一个平台、多种负载"。核心能力:极速调度通道:专为延迟敏感型负载(如 AI Agent)打造的独立调度器,提供极致响应速度。多 Worker 并行处理:采用多 Worker 并发消费调度队列的架构,大幅提升调度吞吐量。乐观并发控制:引入 Conflict-Aware Binder 机制,在实际绑定前预先解决冲突,减少无效操作。增强型调度队列:优化队列机制,支持紧急任务重试,确保关键任务不阻塞。统一平台融合:通过 Sharding Controller 与批量调度器无缝协作,共享集群资源。 🔗 相关 PR:  https://github.com/volcano-sh/volcano/pull/4804,  cid:link_11,  cid:link_12设计文档:Agent Scheduler Design[4]感谢社区开发者:@qi-min, @JesseStutler, @handan-yxh▍网络拓扑感知调度增强Volcano v1.14.0 对网络拓扑感知调度进行了进一步增强,满足分布式工作负载(包括 LLM 训练、推理、HPC 和其他网络密集型应用)日益增长的需求。核心增强:SubGroup 级精细化拓扑感知:支持在 SubGroup / Partition 粒度设置网络拓扑约束,调度颗粒度更精细。灵活的网络层级约束:新增 highestTierName,支持按名称指定允许跨越的最高网络层级。多层级 Gang Scheduling:同时支持 PodGroup 级别和 SubGroup 级别的 Gang Scheduling,确保分布式任务的整体性。Volcano Job 分区:支持将 Job 拆分为多个分区(Partition),便于管理 TP/PP/DP 等并行策略,并优化网络亲和性。HyperNode 级 Binpacking:在 HyperNode(如交换机、机架)层级进行资源装箱,减少网络碎片,提升通信效率。配置示例 - Volcano Job:apiVersion:batch.volcano.sh/v1alpha1 kind:Job metadata: name:llm-training-job spec: networkTopology: mode:hard highestTierAllowed:2 # 整个 Job 最多跨越 Tier 2 HyperNode tasks: - name:trainer replicas:8 partitionPolicy: totalPartitions:2 # 拆分为 2 个分区 partitionSize:4 # 每个分区 4 个 Pod minPartitions:2 # 至少需要 2 个分区 networkTopology: mode:hard highestTierAllowed:1 # 单个分区必须在 Tier 1 内 template: spec: containers: - name:trainer image:training-image:v1 resources: requests: nvidia.com/gpu:8 🔗 相关 PR:  cid:link_13,  cid:link_14,  cid:link_15,  cid:link_16,  cid:link_17设计文档:Network Topology Aware Scheduling[5]感谢社区开发者:@ouyangshengjia, @3sunny, @zhaoqi, @wangyang0616, @MondayCha, @Tau721▍混部能力全面升级:支持通用操作系统本次发布对 Volcano 的混部能力进行了全面改进,其中一个重要里程碑是:Volcano 混部能力正式支持通用操作系统(Ubuntu、CentOS 等),不再局限于 OpenEuler。这意味着更多用户可以使用 Volcano Agent 实现在离线混部,提升集群整体资源利用率。CPU 动态压制 (CPU Suppression)在线业务流量通常具有潮汐特性。为了在保障在线业务 SLA 的同时最大化资源利用,离线 Pod 的 CPU 配额需要随在线用量动态调整:在线用量高时压制离线配额,用量回落时逐步恢复,实现自适应的资源分配。核心设计:根据节点可分配 CPU 和实时用量,动态调整 BestEffort root cgroup 的 CPU 配额。采用"监控-事件-处理"架构,并实施保守更新策略,有效抑制抖动。配置示例:cpuThrottlingConfig: enable:true cpuThrottlingThreshold:80 # BE 配额上限为可分配 CPU 的 80% cpuJitterLimitPercent:1 # 配额变化超过 1% 才触发更新 cpuRecoverLimitPercent:10 # 单次恢复上限 10%内存 QoS (Cgroup V2)基于 Cgroup V2 实现混部场景的内存隔离。新增 ColocationConfiguration CRD,支持为指定工作负载配置内存 QoS 策略。核心能力:New API:通过标签选择器定义内存隔离策略的 ColocationConfiguration CRD动态计算:memory.high = pod.limits.memory * highRatio %memory.low = pod.requests.memory * lowRatio %memory.min = pod.requests.memory * minRatio %统一接口:可靠检测和支持 Cgroup V2 环境使用示例:apiVersion:config.volcano.sh/v1alpha1 kind:ColocationConfiguration metadata: name:colo-config1 spec: selector: matchLabels: app:offline-test memoryQos: highRatio:100 # memory.high = memory.limits * 100% lowRatio:50 # memory.low = memory.requests * 50% minRatio:0 # memory.min = memory.requests * 0%CPU Burst 与 Cgroup V2 全面支持CPU Burst 能力已扩展至通用操作系统。同时,Volcano Agent 现已全面适配 Cgroup V2 环境,支持自动检测 Cgroup 版本及驱动类型(如 systemd driver),无需人工干预即可在现代 Linux 发行版上无缝运行。 🔗 相关 PR: cid:link_18,  cid:link_19,  cid:link_20,  cid:link_21设计文档:CPU Throttle Design[6], Agent Cgroup V2 Adaptation[7]感谢社区开发者:@Haibara-Ai97, @JesseStutler, @ouyangshengjia▍昇腾 vNPU 调度v1.14 原生集成了昇腾 vNPU(虚拟 NPU)调度能力,实现昇腾 AI 处理器在多个工作负载之间的高效算力复用。提供两种模式,灵活适配不同部署场景。支持模式:1. MindCluster 模式集成自 Ascend MindCluster 调度插件:https://gitcode.com/Ascend/mind-cluster支持昇腾 310P 系列的动态虚拟化2. HAMi 模式由 HAMi 社区开发同时支持昇腾 310 和 910 系列支持异构昇腾集群(910A、910B2、910B3、310P)调度器配置:# MindCluster 模式 - name:deviceshare arguments: deviceshare.AscendMindClusterVNPUEnable:true # HAMi 模式 - name:deviceshare arguments: deviceshare.AscendHAMiVNPUEnable:true deviceshare.SchedulePolicy:binpack # 或 spread 🔗 相关 PR: cid:link_22,  cid:link_23使用文档:How to Use vNPU[8] 感谢社区开发者:@JackyTYang, @DSFans2014▍Volcano Global 增强Volcano Global v0.3.0 引入了两个重要功能,通过基于计算资源和数据局部性的智能调度,显著扩展了 Volcano Global 对 AI/ML 和大数据工作负载的能力。HyperJob:多集群作业自动拆分随着 AI 训练工作负载规模和复杂性的增长,企业越来越面临跨多个异构集群管理大规模训练作业的挑战。HyperJob 是构建在 Volcano Job 之上的更高级抽象。它组合多个 Volcano Job 模板,将训练能力扩展到单集群边界之外,同时保留每个集群内现有 Volcano Job 的全部能力。核心能力:Karmada 深度集成:自动生成 PropagationPolicy,精准配置集群亲和性与副本调度。状态统一聚合:将各集群子任务状态汇总为统一的 HyperJob 状态,全局可观测。自动资源生成:根据 ReplicatedJob 定义自动创建 VCJob 和 PropagationPolicy。HyperJob 资源示例(跨 2 个集群拆分大规模训练作业,共 256 个 GPU):apiVersion:training.volcano.sh/v1alpha1 kind:HyperJob metadata: name:llm-training spec: replicatedJobs: - name:trainer replicas:2 templateSpec: tasks: - name:worker replicas:128 template: spec: containers: - name:trainer image:training-image:v1 resources: requests: nvidia.com/gpu:1数据感知调度在 AI 训练和大数据分析等高性能计算场景中,任务执行不仅依赖计算资源,还严重依赖数据资源。在多集群环境中,调度器可能会将任务分派到与数据源物理距离较远的集群,导致跨地域带宽成本过高和 I/O 延迟过高。数据感知调度框架引入 DataDependencyController,打通了逻辑数据需求与物理集群分布的壁垒。通过外部插件(如 Amoro)实时获取数据分布信息,自动将调度约束注入 Karmada,实现"计算随数据而动"的全自动工作流。核心能力:插件化架构:可扩展支持 Amoro、Hive、S3 等多种数据系统。声明式 API:DataSourceClaim / DataSource CRD,采用"声明-缓存"模式。自动亲和注入:将数据局部性转化为 ClusterAffinity 约束,注入 ResourceBinding。详见: Volcano Global v0.3.0 Release Notes[9]感谢社区开发者:@JesseStutler, @fx147, @Monokaix, @zhoujinyu, @anryko, @tanberBro▍Volcano Dashboard v0.2.0Volcano Dashboard v0.2.0 对资源管理能力进行了重大增强,使得通过 Web 界面管理 Volcano 资源更加便捷。核心增强:PodGroup 全景可视化:跨命名空间查看、搜索、过滤 PodGroup,支持 YAML 语法高亮。Job 生命周期管理:直接在界面创建、删除 Volcano Job,操作更便捷。Queue 管理增强:在线编辑 Queue 配额、权重,支持 YAML 直接修改。安全加固:默认配置 SELinux、Seccomp、非 root 运行及禁止提权,保障生产安全。详见: Volcano Dashboard v0.2.0 Release Notes[10]感谢社区开发者:@vzhou-p, @Shrutim1505, @JesseStutler, @karanBRAVO, @Sayan4444, @jayesh9747, @Alivestars24, @kuldeep, @Monokaix▍调度器稳定性与性能提升Reclaim 重构与增强对 Reclaim Action 进行了全面重构,并修复了 Capacity Plugin 中的关键逻辑问题,大幅提升多租户集群资源回收的准确性、稳定性和性能。主要改进:Reclaim Action 重构:重构了 reclaim 工作流,提高代码可读性、可维护性和测试覆盖率。增强的 Capacity Plugin 逻辑:修复了 reclaimableFn 和 preemptiveFn,正确处理标量资源并防止错误的抢占决策。稳定性提升:解决了资源计算中的边缘情况,防止调度死循环和误驱逐。 🔗 相关 PR: cid:link_24,  cid:link_25,  cid:link_26感谢社区开发者:@guoqinwill, @hajnalmt▍支持 Kubernetes 1.34Volcano 版本紧跟 Kubernetes 社区。v1.14 已全面支持最新的 Kubernetes v1.34,并通过完整的单元测试和 E2E 测试保障功能与稳定性。🔗 相关 PR:cid:link_27感谢社区开发者:@suyiiyii, @tunedev  总结:Volcano v1.14.0 — AI 时代的统一调度平台  Volcano v1.14 是一个里程碑式的版本。通过引入多调度器架构和 Agent Scheduler,Volcano 正式迈入统一调度平台新阶段,既能高效处理批量 AI 训练,又能满足 AI Agent 的极致时延要求。网络拓扑感知增强、通用操作系统混部支持、昇腾 vNPU 集成,进一步夯实了 Volcano 在 AI 基础设施领域的领先地位。同时,Volcano Global v0.3.0 通过 HyperJob 实现大规模分布式训练和数据感知调度,扩展了多集群能力。Volcano Dashboard v0.2.0 通过全面的资源管理功能显著改善了用户体验。立即体验 Volcano v1.14,共启 AI 时代统一调度新篇章!v1.14.0 发布地址:cid:link_8Volcano Global v0.3.0 发布地址:cid:link_6Volcano Dashboard v0.2.0 发布地址:cid:link_7   致   谢   Volcano v1.14 生态版本(含 Volcano Global v0.3.0、Dashboard v0.2.0)共有 55 位社区贡献者参与。衷心感谢每一位贡献者:相关链接[1] Volcano: https://volcano.sh/en/[2] Volcano v1.14.0: cid:link_8[3] Sharding Controller Design: cid:link_2[4] Agent Scheduler Design: cid:link_5[5] Network Topology Aware Scheduling: cid:link_0[6] CPU Throttle Design: cid:link_3[7] Agent Cgroup V2 Adaptation: cid:link_1[8] How to Use vNPU: cid:link_4[9] Volcano Global v0.3.0 Release Notes: cid:link_6[10] Volcano Dashboard v0.2.0 Release Notes: cid:link_7 扫码回复“Volcano” 进入技术群
  • [技术干货] Kthena v0.3.0 Release:Production-Ready 的推理编排
    作者:Kthena MaintainersKthena 是一个专为 Kubernetes 设计的云原生、高性能 LLM 推理路由和编排、调度系统。作为 Volcano 的子项目,Kthena[1] 致力于帮助 Volcano[2] 扩展除 AI 训练之外的边界,打造训推一体的完整解决方案。Kthena v0.3.0[3] 现已正式发布,标志着 Kthena 已经成为一个更加健壮且具有可扩展性的 AI 推理平台。此版本在 ModelServing、Router 和 ModelBooster 方面引入了重大增强。核心亮点包括:与 LeaderWorkerSet 的无缝集成、针对 PD 分离(Prefill-Decode Disaggregation)的高级网络拓扑感知调度,以及完善的 Router 可观测性框架。此外,本版本还带来了原生的 ModelServing 版本控制、对 vLLM 数据并行部署的支持,以及完整的 Router E2E测试套件,确保了生产环境的高稳定性和可靠性。  关键新特性概览  LeaderWorkerSet 支持:集成 LeaderWorkerSet (LWS) API,实现对分布式推理工作负载的高级管理。角色级 Gang 调度与拓扑感知:利用 Volcano 的 subGroupPolicy 新特性,实现精细化的角色级 Gang 调度和网络拓扑感知。ModelServing 分区版本控制:引入了基于 Revision 的原生 ModelServing 版本控制系统。Router 可观测性与调试:提供完善的 Router 可观测性文档和框架,并新增专用调试端口。增强的滚动更新:支持 maxUnavailable 参数,可调节更新速度以实现更快地发布。插件支持:为 ModelServing 提供灵活的插件架构,用于注入自定义配置逻辑。▍ModelServing 角色支持 LeaderWorkerSet分布式推理工作负载通常需要复杂的拓扑结构,其中一个 Leader Pod 管理多个 Worker Pod。手动配置这些关系容易出错。通过集成 Kubernetes LeaderWorkerSet (LWS) API,Kthena 简化了这些工作负载的部署和管理。核心能力:直接集成:ModelServing 角色(Role)现在可以利用 LWS 自动管理 Leader-Worker 组。简化拓扑:降低了定义需要严格协调的分布式推理服务的复杂性。相关内容:🧩 PR:cid:link_4cid:link_5贡献者: @zhiweideren▍角色级 Gang 调度与拓扑感知在 Prefill-Decode (PD) 分离场景中,Prefill 实例和 Decode 实例之间的通信开销至关重要。确保这些实例在物理位置上更接近(例如在同一个交换机或机架下)可以显著提升性能。Kthena 现在通过 Volcano 的 subGroupPolicy 实现了对 Gang 调度和网络拓扑感知的精细化、角色级控制。核心能力:声明式拓扑策略:直接在 ModelServing 规范中为整个 ServingGroup(groupPolicy)以及单个角色(rolePolicy)配置不同的网络拓扑限制。自动 Pod 分组:Controller自动为 Pod 打上 modelserving.volcano.sh/role 和 modelserving.volcano.sh/role-id 标签,使 Volcano 能够形成子组(subGroup)以进行精确的拓扑感知放置。性能优化:通过将相关任务部署在网络邻近的节点上,最小化角色间通信延迟并最大化带宽利用率,适用于高强度分布式推理任务。角色级 Gang 调度:subGroupPolicy 还强制执行角色级 Gang 调度,确保属于特定角色(例如所有 prefill-0 Pod)的所有 Pod 作为一个原子单元共同调度。这保证了角色不会出现部分部署的情况,这对于分布式推理工作负载的正确性至关重要。💡 注意:此特性需要针对 subGroupPolicy 支持的 Volcano v1.14+ 版本。相关内容:提案: Network Topology[4]🧩 PR: cid:link_6贡献者: @LiZhenCheng9527▍ModelServing 分区版本控制Kthena ModelServing 中的 partition 字段定义了滚动更新的边界,允许你对更新过程进行分区,以便在更新一部分 ServingGroup 的同时,让其他部分保持在旧版本。这主要用于金丝雀部署、分阶段发布以及在需要严格控制更新顺序的有状态应用中进行灰度更新。核心能力:Revision 追踪:自动追踪 ModelServing 配置的变更。分区保护:支持基于分区的更新,确保发布过程中的服务连续性。回滚:轻松回退到之前稳定的 Revision。相关内容:🧩 PR: cid:link_7cid:link_8cid:link_9贡献者: @FAUST-BENCHOU, @LiZhenCheng9527▍Router 可观测性与调试对推理 Router 的深度洞察对于诊断延迟问题和确保 SLA 合规性至关重要。新的可观测性框架和调试端口为运维人员提供了必要的工具。核心能力:调试端口:专用端口(默认 15000),用于实时查看路由表和上游健康状况。综合指标:提供详细的文档和配置,用于监控请求延迟、吞吐量和错误率。端到端测试:鲁棒的 E2E 测试框架涵盖了大多数路由场景,确保了可靠性。相关内容: 🧩PR & Issue:cid:link_10cid:link_11cid:link_2贡献者: @yashisrani, @FAUST-BENCHOU, @YaoZengzeng, @katara-Jayprakash  其他显著变化  [ModelServing] 支持 ModelServing 滚动更新中的 maxUnavailable 参数(@LiZhenCheng9527) PR: https://github.com/volcano-sh/kthena/pull/640 [ModelServing] 实现扩展插件框架(@hzxuzhonghu) PR: cid:link_13 [ModelServing] 支持 vLLM 数据并行部署和专家并行(Expert Parallel)模式[CLI] 为 PD 分离使用场景添加模板 (@huntersman) PR: cid:link_3 [Client] 使客户端 QPS 和 Burst 可配置 (@FAUST-BENCHOU) PR: cid:link_14 [Webhooks] 在 Helm chart 中默认启用 ModelServing Webhook (@VanderChen) PR: cid:link_15  [Infra] 通过 hack/local-up-kthena.sh 实现源码一键部署 (@FAUST-BENCHOU PR: cid:link_16  此外,Kthena v0.3.0 还对多项bug进行了修复,感谢 @WHOIM1205 @LiZhenCheng9527 @VanderChen @hzxuzhonghu @FAUST-BENCHOU。📌 即刻访问体验 Kthena v0.3.0: cid:link_1   贡献者  感谢所有为此次Release做出贡献的伙伴:  相关资料[1] Kthena: https://kthena.volcano.sh/[2] Volcano: https://volcano.sh/en/[3] Kthena v0.3.0: cid:link_1[4] Network Topology: cid:link_0 添加社区小助手回复“Kthena”进入技术交流群
  • [技术干货] 重磅!Volcano发布AgentCube,构建AI Agent时代的云原生基础设施
    今天,Volcano 社区[1]很高兴地宣布新的子项目 AgentCube。基于 Volcano 在大规模高性能计算调度领域多年的生产实践积累,AgentCube 将这种高并发、高吞吐的调度能力延伸至 AI 领域,构建了一套面向智能体(Agent)工作负载的 Serverless 编排层,旨在为高并发、长会话、对延迟极度敏感的智能体(Agent)工作负载提供 Serverless 化的编排与极速调度能力。  从 Kubernetes 到 Agent Native  随着大语言模型(LLM)技术的成熟,技术架构正从“无状态推理”向“自主智能体(Autonomous Agents)”演进。在这一进程中,Kubernetes 凭借其成熟的生态和对异构算力的标准化管理,已成为构建 AI 基础设施的事实标准。虽然原生 Kubernetes 提供了通用的容器编排原语以及多样化的工作负载抽象,但在面对 AI Agent 这种“高并发、短时效、强状态依赖”的新型负载时,仍存在着显著的粒度错配与机制缺位:1. 启动延迟与交互体验的矛盾: Agent 的交互通常要求毫秒级响应。然而,原生的 K8s Pod 启动流程(调度、IP 分配、镜像拉取、容器启动)往往在秒级甚至分钟级。对于需要频繁拉起 Code Interpreter(代码解释器)或临时子 Agent 的场景,这种冷启动延迟是用户无法接受的。2. 资源利用率的挑战:Agent 是典型的密集型负载。在一次会话中,90% 的时间 Agent 可能都在等待 LLM 生成 Token 或等待外部工具响应。如果在 K8s 上为每个 Agent 独占一个 Pod,会导致大量的 CPU/Memory 资源在等待期间被闲置浪费,却无法被其他任务复用。3. 会话状态管理的缺失:K8s 对“无状态(Stateless)”工作负载天然友好,但 Agent 高度依赖“上下文(Context/Memory)”。在原生 K8s 中,Pod 重启意味着内存数据丢失,开发者被迫在应用层通过外部存储重建上下文,这带来了巨大的复杂性和网络开销。4. 安全隔离难题:高级 Agent(如 Data Analyst)需要运行由 LLM 生成的不可信代码。但普通的 runC 容器如果运行 rm -rf / 具有极高风险。企业级 Agent 平台迫切需要一种既能快速启动,又能提供强隔离(如 MicroVM)的沙箱环境。为了在 Kubernetes 坚实的算力底座之上,填补上述机制空白,AgentCube 应运而生。  AgentCube 是什么?AgentCube 是一个构建在 Volcano 之上的高性能 AI Agent 编排层。它通过扩展 Kubernetes API,将 Agents 和 Tools(Code Interpreters、BrowserUse等) 提升为集群的一等公民。它不仅仅是一个 CRD,更是一套 面向 Agent 的 Serverless 操作系统。▍核心架构与抽象AgentCube 引入了两个核心的 CRD 来定义 Agent 工作负载:1. AgentRuntime: 面向长会话、复杂的对话式 Agent。支持定义会话的生命周期、资源配额以及持久化策略。2. CodeInterpreter: 面向短任务、高频的代码执行环境。强调“用完即毁”和极致的安全隔离,天然适配 MicroVM(如 Kuasar, Kata Containers, Firecracker)。AgentCube后续还将提供BrowserUse、ComputerUse、MobileUse等工作负载抽象支持。  AgentCube 关键技术亮点  为了解决上述痛点,AgentCube 在架构设计上引入了多项创新:▍1. 极速启动为了消除冷启动的挑战,AgentCube 实现了 Warm Pool(预热池) 机制。系统会预先启动并暂停一组持有基础环境的 MicroVM 沙箱。当 Agent 请求到来时,AgentCube 能够通过 "Claim-and-Go" 的方式,在毫秒级将预热的沙箱分配给会话,实现近乎零延迟的启动体验。▍2. 极速调度借助 Volcano 的 Agent Scheduler,AgentCube显著提升了Agent调度的吞吐和时延。高吞吐、低时延: 针对 Agent 突发流量,采用了乐观并发控制和精简的调度策略,大幅提升调度 TPS。统一调度支持: Volcano 的 Agent Scheduler 可以与原有的 Batch Scheduler 无缝配合,在协调 Agent 与传统的 Batch 作业潜在调度冲突的同时,确保整体集群的资源利用率和关键业务的 SLA。▍3. 原生会话管理AgentCube 引入了 Session ID 作为核心路由标识,便于保证业务上下文的连续性。请求路由: AgentCube Router 能够识别请求中的 x-agentcube-session-id,自动将其路由到对应的活跃沙箱。自动的沙箱激活: 当前会话对应的沙箱处于休眠状态时,AgentCube Router 能够自动激活沙箱。基于会话的端到端隔离: AgentCube 会自动为每个会话分配独立的沙箱环境,确保计算、内存与文件系统的完全隔离,防止跨租户的数据泄露。▍4. Serverless 化的弹性伸缩AgentCube 能够根据会话的活跃度自动管理沙箱生命周期。闲置的沙箱会被自动回收或休眠,释放物理资源供其他高优先级任务使用,真正实现资源按需分配与极致利用。  AgentCube 架构概览  AgentCube 采用了经典的控制面与数据面分离的架构设计,确保了系统的高可用性与扩展性:数据面 : 由 AgentCube Router 承载。它作为流量入口,负责鉴权、限流以及基于 Session ID 的智能路由。对于新会话,它向控制面申请资源;对于活跃会话,它直接将请求转发至对应的 Sandbox (MicroVM)。控制面 : 核心组件 Workload Manager 负责沙箱的全生命周期管理。它监控预热池 (Warm Pool) 的水位,自动补充 MicroVM 实例,并根据会话活跃度策略(如 TTL)执行沙箱的回收与垃圾清理。调度层: 集成 Volcano Agent Scheduler,通过异步并行调度和乐观锁机制,实现高并发下的毫秒级资源分配。  生态协作:共建标准化的 Agent 基础设施  作为开源中立的基础设施项目,AgentCube 旨在通过标准接口连接上下游生态,协作解决从容器编排到智能体应用落地的“最后一公里”难题。1. 南向兼容:基于标准接口的运行时适配AgentCube 坚持开放架构设计,通过深度集成 kubernetes-sigs/agent-sandbox 接口及 OCI 标准,实现对底层异构运行时的统一抽象与无感适配。运行时解耦: 支持通过 RuntimeClass 机制接入 Kuasar、Kata Containers、Firecracker 等安全容器技术,允许用户根据安全与性能需求灵活选择底层隔离方案。前沿探索: 社区正在评估 Wasm (WebAssembly) 技术,计划在未来版本中探索其在极轻量级 Agent 任务中的应用,以提供更多样化的算力供给。2. 北向集成:服务主流Agent框架在应用层,AgentCube 致力于成为 Dify、LangChain、CrewAI、LlamaIndex 等Agent框架的标准基础设施底座,相关适配工作正在快速迭代中。声明式管理: 将通过 Operator 模式提供声明式资源接口,帮助上层框架剥离底层的沙箱预热池管理与网络配置等逻辑。统一底座: 目标是实现业务编排与资源调度的解耦,使不同框架开发的应用能复用同一套云原生运维体系,降低基础设施的维护成本与碎片化程度。  灵活接入:兼顾开发与运维体验  AgentCube 设计了分层的接入接口,旨在同时满足上层业务开发者与底层平台工程师的诉求,让基础设施不再成为黑盒。1. 面向 Agent 开发者:标准 API 接入为了进一步降低接入门槛,AgentCube 提供了开箱即用的 Python SDK[2]。开发者无需深入理解 Kubernetes 的复杂概念,即可像调用本地函数一样申请、执行和释放沙箱。这使得 AgentCube 能够轻松集成到 Dify、LangChain、CrewAI、LlamaIndex 等主流框架中。📄 示例1:在agent代码中动态拉起一个CodeInterpreter并运行临时代# python from agentcube import CodeInterpreterClient # Initialize client (uses env vars for configuration) with CodeInterpreterClient() as client: # 1. Run a simple shell command print("User: whoami") print(client.execute_command("whoami")) # 2. Execute Python code code = """ import math print(f"Pi is approximately {math.pi:.4f}") """ output = client.run_code("python", code) print(f"Result: {output}")📄 示例2:通过kubectl agentcube命令行工具创建一个Agent# 1. Package an existing agent: kubectl agentcube pack -f examples/hello-agent --agent-name "my-agent" # 2. Build the container image: kubectl agentcube build -f examples/hello-agent # 3. Publish to AgentCube: kubectl agentcube publish \ -f examples/hello-agent \ --image-url "docker.io/username/my-agent" \ # 4. Invoke your agent: kubectl agentcube invoke -f examples/hello-agent --payload '{"prompt": "Hello World!"}' # 5. Check status: kubectl agentcube status -f examples/hello-agent2. 面向平台工程师:声明式 CRD 管理AgentCube 延续了云原生的声明式管理模式。运维团队可以通过 CRD (AgentRuntime, CodeInterpreter) 精细化定义资源池策略,直接复用现有的 Kubernetes 运维体系与工具链进行统一管理。📄 示例:定义一个CodeInterpreter,始终保持 10 个热备沙箱# YAML apiVersion: runtime.agentcube.volcano.sh/v1alpha1 kind: CodeInterpreter metadata: name: simple-codeinterpreter namespace: default spec: template: image: ghcr.io/volcano-sh/picod:latest sessionTimeout: "15m" maxSessionDuration: "8h" warmPoolSize: 10 # 预热水位想亲自体验?完整的安装部署文档与 Demo 示例,请访问 GitHub 仓库:cid:link_1  加入社区  AgentCube 是 Volcano 社区的一部分,遵循开源开放的原则。我们诚挚邀请对 AI Infra、Kubernetes 调度、Serverless 架构 感兴趣的开发者加入我们!GitHub: https://github.com/volcano-sh/agentcube( 欢迎Star ⭐️) Slack: CNCF Slack # volcano 频道Community Meeting: 关注  Volcano 社区双周会 [3]。让我们一起,为 AI Agent 时代构建更强大的基础设施!相关链接[1] Volcano 社区官网: https://volcano.sh/en/[2] Python SDK: cid:link_0[3] Volcano 社区双周会: https://zoom.us/j/91804791393 Volcano 是业界首个云原生批量计算引擎,也是 CNCF 首个和唯一的批量计算项目。项目主要用于 AI、大数据、基因、渲染等诸多高性能计算场景,对主流通用计算框架均有很好的支持。目前,Volcano在人工智能、大数据、基因测序等海量数据计算和分析场景已得到快速应用,已完成对 Spark、Flink、Ray、 Tensorflow、PyTorch、Argo、MindSpore、Paddlepaddle 、Kubeflow、MPI、Horovod、Mxnet、KubeGene 等众多主流计算框架的支持,并构建起完善的上下游生态。Website:https://volcano.shGitHub: cid:link_2每周例会:https://zoom.us/j/91804791393 添加社区小助手回复“Volcano”进入技术交流群
  • [公告] Karmada 用户组再迎新成员 | 商汤(SenseTime)正式加入
    Karmada 用户组再迎重要新成员,商汤科技[1]正式加入。 作为云原生计算基金会(CNCF)旗下项目,Karmada 致力于为用户提供强大的多集群管理和调度能力,帮助企业在复杂的分布式环境中实现高效的应用部署和管理。 商汤科技的加入将进一步加强 Karmada 社区,为项目的持续创新注入新的活力,标志着社区发展及 Karmada 在多样化生产环境中应用的又一个重要里程碑。   关于商汤科技  商汤科技坚持“AI 基础设施(大装置)-大模型(日日新)-应用”三位一体战略。 商汤大装置作为商汤科技前瞻打造的高效率、低成本、规模化的新一代 AI 基础设施, 以大模型开发、生成、应用为核心,赋能人工智能生产新范式。 通过与大模型迭代的联合调优,打造“最懂大模型的 AI 基础设施”。2022年,作为商汤大装置重要载体的人工智能计算中心(上海临港 AIDC)正式投入运营, 成为亚洲最大人工智能计算中心之一。过去三年, 商汤持续投入建设并自持全国首个 5A 级智算中心上海临港 AIDC, 以及通过运营模式将算力总规模提升至32,000 PFlops(截至2025年12月)。 强大算力可支撑超过 20 多个千亿参数超大模型同时训练, 并支持万亿参数大模型的全生命周期生成。作为行业先行者,商汤大装置已构建起“算力-平台-方案-服务”的端到端系统化能力, 既为商汤日日新大模型的迭代提供了核心动力, 也快速承接了各行业客户的大模型创新需求, 持续赋能 AIGC、具身智能、AI4S、产业智能化等相关领域创新。  关于 Karmada 用户组  Karmada 用户组是一个由在其环境中成功采用 Karmada 的组织和用户组成的社区。作为连接社区与用户的核心纽带,Karmada 用户组致力于打造一个深度融合、开放协作的高价值平台,推动成员间的高效联动与经验共享,助力云原生多云多集群生态系统的蓬勃发展。成为 Karmada 用户组成员具有以下优势:社区认可:作为云原生多集群管理领域的领导者来展示您的组织,在 CNCF 和 Karmada 社区中获得知名度;协作与交流:与其他采用者建立联系,分享最佳实践,并在实际用例和解决方案上进行协作;保持更新:及时接收重要更新通知,包括关键功能、错误修复和安全建议;活动参与:受邀参与 Karmada 相关活动,包括 KubeCon + CloudNativeCon、网络研讨会和聚会;职位发布:有机会在 Karmada 社区支持的职位公告板上发布与 Karmada 相关的职位空缺(暂不可用);扩展商业机会:与 Karmada 生态系统的其他成员建立潜在的商业联系和合作。您可以在 GitHub 社区仓库中了解更多关于 Karmada 用户组[2] 的信息, 并在 karmada.io/adopters [3] 查看完整的公开的采用者列表。截至目前,Karmada 用户组已吸纳来自全球的 40+ 家机构和组织。更多使用场景及案例研究请查阅:https://karmada.io/adopters   加入 Karmada 用户组   Karmada 用户组对当前正在生产环境中使用 Karmada 的最终用户和供应商开放。这包括:最终用户:在其生产环境中运行 Karmada 的组织;供应商:提供基于 Karmada 的产品或服务,并有客户在生产环境中使用这些产品或服务的公司。您是否在生产环境中使用 Karmada 并有兴趣加入 Karmada 用户组?访问下方 Karmada 用户组申请表单 [4],提交 issue 申请,即可接收申请进度。手机端可扫描下方二维码快捷填写申请表单。 扫码申请加入用户组更多信息,请访问:[1] 商汤科技: https://www.sensetime.com/[2] Karmada 用户组: cid:link_0[3]Karmada 采用者列表: http://karmada.io/adopters[4]Karmada 用户组申请表单: https://github.com/karmada-io/community/issues/new?template=adopter-group-application.yamlKarmada Adopter Group 欢迎您的加入!期待与您共同创建一个友好而活跃的空间,共享知识、最佳实践和经验,为企业与社区发展缔造更多可能。如需了解更多关于 Karmada Adopter Group 的信息,请联系: Maintainer Mailing Listcncf-karmada-maintainers@lists.cncf.io  Karmada 是CNCF 首个多云多集群容器编排项目(孵化级),旨在帮助用户像使用单个集群一样轻松管理跨云多集群,让基于 Karmada 的多云方案无缝融入云原生技术生态。社区吸引了来自华为、道客、浙江大学、滴滴、腾讯、中国电子云等60多家公司的全球贡献者,广泛分布于22个国家和地区。Karmada 现已在华为云、道客、兴业数金、中国移动、中国联通、携程、360集团、新浪、中通快递等众多企业单位生产应用,为企业提供从单集群到多云架构的平滑演进方案。Karmada官网:https://karmada.io/项目地址:cid:link_1Slack地址:https://slack.cncf.io/(#karmada) 扫码回复“Karmada” 进入技术群 
  • [行业前沿] 华为云CCE Autopilot:从"黑盒运维"到"智能运维",重塑沙特家电零售巨头的数字引擎
    面对互联网大促,技术团队最为焦虑的莫过于“系统能否扛住”,如何打破“峰值体验差”与“资源成本失控”的魔咒?沙特家电零售巨头 Alsaif Gallery 借助华为云 CCE Autopilot,实现系统时延降低 75%、日常运维负担减少 80%、成本优化 30%。当2025年沙特国庆日销售战绩定格在单日5万订单时,Alsaif Gallery 技术团队感受到的不仅是喜悦,更是一种久违的从容。就在数月前,同样的流量洪峰还意味着紧绷的神经与对系统中断的深深焦虑。如今,这一切已成为历史。运维工作从过去如“黑盒”般不可预测,到如今“智驾”般自动高效,曾经困扰 Alsaif Gallery 许久的“流量峰值用户体验”和“失控的资源成本”两大难题,已不再是平台业务爆发增长的绊脚石。这家沙特家电零售巨头的数字化转型之路,因一次关键的云迁移而彻底提速。业务狂奔,技术绊脚作为沙特前三的家电零售商,Alsaif Gallery线上业务增长迅猛。日均超3万笔订单的电商平台,是其当之无愧的业务核心引擎。然而,这台引擎的基础设施却长期饱受压力。原有云架构的复杂性,让技术团队在每次大促前都如履薄冰。他们面临着三重挑战:排障如“黑盒”:平台故障排查依赖原第三方厂商跨国支持,响应迟缓,排障效率低,并且缺乏全链路观测工具,定位问题如同大海捞针。运维靠“人扛”:中东本地资深的K8s与SRE专家稀缺,导致现有K8s集群只能维持基本运行,无法进行深度性能优化。同时,高度复杂且耦合的系统,运维严重依赖少量核心人员,使得性能瓶颈与运维风险并存。成本“过山车”:面对突发流量,原有基于虚拟机的资源模式扩缩速度慢,人工配置的扩缩策略难以自适应多变的流量。促销时资源“扩不上去”,影响用户体验;促销后资源“缩不下来”,白白浪费成本。对于依赖“斋月”、“白色星期五”等关键销售季的中东零售业而言,这不仅是技术问题,更是直接关乎品牌声誉的业务风险。Alsaif Gallery&华为云:一场始于信任的“无感迁移”面对与旧云服务深度捆绑的复杂架构,Alsaif Gallery对迁移最初持谨慎态度。华为云团队用专业与诚意破除了这层顾虑。这不是简单搬家,而是联创设计。华为云交付团队与架构师提前介入,通过超10次的深度技术交流与业务梳理,将客户“提升系统稳定性、降低运维成本”的核心诉求,转化为一套量身定制的“上云+优化”组合方案。不回避难题,而是逐一攻克。针对容器搬迁、函数改造、安全配置等十余项具体技术挑战,华为云拉通产品专家驻场支持,将迁移风险一一化解。这种直面复杂性的务实态度,赢得了客户的最终信任,决心将核心容器业务迁至华为云CCE Autopilot。系统运维跃迁:从“手动挡”到“智能驾驶”系统整体迁移仅仅花费一个月,但变化效果立竿见影。华为云CCE Autopilot的Serverless能力,为Alsaif Gallery带来了运维模式的根本性变革:全面自动化:节点、操作系统、集群升级全面自动化,让技术团队从繁琐的基础设施维护中解放出来。无缝兼容:100%兼容K8s生态,原有应用平滑迁移,业务0改造。精准弹性:基于真实负载的秒级伸缩,实现所得即所需的理想状态。迁移后,Alsaif Gallery成果直接体现在三个关键指标上:负担锐减:日常运维工作量降低80%,让技术团队得以聚焦创新。成本优化:凭借按需计费与精准弹性,资源利用率提升20%,成本降低30%以上。体验提速:系统时延从120ms大幅降低至30ms以内,用户端操作响应更加流畅。通过此次迁移,让Alsaif Gallery技术专家角色由此转变:从过去疲于奔命的“消防员”,转型成为了驾驭高效引擎、驱动业务增长的“设计师”。持续共建:从云迁移到长期战略合作本次迁移的成功,不是终点,而是Alsaif Gallery与华为云更长远合作的起点。基于建立起的信任,双方合作正向纵深发展:从大数据治理中挖掘零售电商高增长业务,到构建AI知识库提升内部效率,一系列新的数字化规划已提上日程。而华为云在其中的独特价值在于:不仅是技术的提供者,更是深谙本地市场的同行者。在沙特,华为云拥有超过200名本地技术专家,能提供7*24小时无时差响应,并深刻理解区域合规与市场特性,真正做到了“全球能力,本地服务”。云计算新范式:从“资源开销”变成“业务引擎”在沙特“2030愿景”掀起的数字化浪潮中,企业的竞争本质上是效率与敏捷性的竞争。华为云通过CCE Autopilot,为Alsaif Gallery提供的不仅是一个更强大的云平台,更是一套将复杂运维自动化、让运营成本可视可控的系统。这标志着一种全新范式的转变:云计算从“资源开销”变为企业真正的“业务引擎”。当数字基石稳固而智能,企业的增长便只需聚焦于前方更广袤的市场。 
  • [技术干货] 重新定义大模型智能推理:Volcano社区发起Kthena子项目
    作者:Volcano Maintainers今天,我们激动地向全球开发者和 MLOps 工程师宣布,Volcano 社区迎来了一个新的子项目 Kthena!Kthena 是一个专为 Kubernetes 设计的云原生、高性能 LLM 推理路由和编排、调度系统。它旨在解决在生产环境中大规模编排、部署和服务 LLM 所面临的核心挑战,通过其独特的超节点拓扑感知的亲和性调度,KV Cache 感知的流量调度、Prefill/Decode 分离路由等高级功能,显著提升 GPU/NPU 资源利用率和吞吐,降低推理延迟,赋予企业前所未有的灵活性和控制力。作为 Volcano 的子项目,Kthena 将致力于帮助 Volcano 扩展除 AI 训练之外的边界,打造训推一体的完整解决方案。  LLM 服务化的“最后一公里”困境  大语言模型(LLM)正在以前所未有的速度重塑各行各业,但将其高效、经济地部署在生产环境中,特别是基于 Kubernetes 的云原生平台上,仍然困难重重。开发者们普遍面临以下挑战:1.  资源利用率低:LLM 推理,尤其是其独特的 KV Cache 机制,对 GPU、NPU 显存的占用是动态且巨大的。传统的负载均衡一般采用Round-Robin算法,无法感知这种负载特性,导致 GPU、NPU 资源闲置与请求排队并存,成本高昂。2.  延迟与吞吐量难以兼顾:LLM 推理分为“Prefill”(处理输入提示)和“Decode”(生成 Token)两个阶段,前者是计算密集型,后者是访存密集型。将两者混合调度,常常导致无法针对性优化,影响整体服务的响应速度和吞吐能力。因此PD分离的部署已经成为主流,但如何高效路由和调度,仍是一个难题。3.  多租户与多模型管理复杂:在企业环境中,通常需要同时提供多个不同模型、不同版本或经过 LoRA 微调的模型。如何实现请求的公平调度、优先级管理以及动态路由,是一个复杂的工程难题,业界甚至有些方案将AI网关与大模型一一对应。4.  缺乏K8s原生集成:许多现有的解决方案要么是外部系统,与 Kubernetes 生态割裂;要么过于复杂,无法满足生产级所需的简单易用性和灵活运维。  Kthena:云原生 LLM 推理的智能大脑  为了攻克上述难题,Kthena 应运而生。它并非要取代现有的 LLM 服务框架(如 vLLM, sgLang),而是作为它们上层的智能“交通枢纽”和“调度中心”,深度集成于 Kubernetes 之中。Kthena 架构图Kthena 的核心由两大组件构成:1)Kthena Router:一个独立、高性能面向多模型的router,负责接收所有推理请求,并根据 ModelRoute 规则,智能地将请求分发到后端的 ModelServer。2)Kthena Controller Manager:Kubernetes 控制平面的控制器,它主要包含多种控制器,负责 LLM 工作负载的编排与生命周期管理。它持续调谐并联动多类 CRD(如 ModelBooster、ModelServing、AutoScalingPolicy/AutoScalingPolicyBinding、以及 ModelRoute/ModelServer),将声明式API转化为运行时资源:ModelServing 控制器编排 ServingGroup 与 Prefill/Decode 角色分组;支持网络拓扑亲和调度和Gang调度、滚动升级与故障恢复;基于 AutoScalingPolicy 实现弹性扩缩容。这种架构使得 Kthena 成为连接用户请求与 LLM 模型的高度可编程的桥梁。  核心特性与优势  Kthena 的强大之处在于其专为 LLM 推理场景设计的核心功能:1) 生产级推理编排(ModelServing)LLM工作负载三层架构设计:ModelServing -> ServingGroup -> Role,一个API,支持LLM原生部署、PD分离部署,乃至大EP部署等多种部署形态,简化管理多LWS的负担。例如对于PD分离的大规模部署,可用一个ModelServing表示,根据负载的大小每个ModelServing可以包含任意数目的 ServingGroup(xPyD 分组), 每个ServingGroup包含多个角色(Prefill Decode,他们通常部署在同一个超节点内以提升推理性能),相同的角色可以等价为一个LeaderWorkerSet,支持TP/PP/EP等多节推理并行计算。原生支持Prefill-Decode分离部署:将计算密集型的 Prefill 实例调度到配备高性能计算卡的节点组,而将访存密集型的 Decode 实例调度到配备高带宽显存的节点组,实现资源的最佳匹配和极致的端到端延迟优化。另可以独立伸缩,动态调整Prefill-Decode的比例,更灵活的应对各种复杂的业务场景(如长短句混合、实时推理等)。多并行范式支持:TP/PP/DP/EP 等并行模式灵活配置,最大化提升资源利用率和SLO内置拓扑感知、Gang 调度支持:Gang调度确保ServingGroup/Role“成组原子化”落地,避免资源浪费;拓扑感知调度通过将Role内的一组Pod调度到网络拓扑更优的节点,提升并行计算的数据传输时延。2) 开箱即用的模型上线(ModelBooster)针对主流的大模型,提供包括PD分离在内的多种部署范式模板,自动生成ModelRoute/ModelServer/ModelServing/Autoscaling等路由策略和生命周期管理资源覆盖通用的部署场景,至于更灵活的编排可通过ModelServing进行细粒度的控制3) 智能、模型感知的路由(Kthena Router)多模型路由:兼容OpenAI API,根据请求头或Body体内容,将流量调度到不同的基础模型。插件化调度算法:提供最少请求、最小时延、KV Cache 感知、Prefix Cache 感知、LoRA 亲和、GPU 利用率感知、公平调度等多种负载均衡算法,满足用户不同业务场景和部署形态的需求LoRA 模型热插拔无中断:感知推理引擎加载的LoRA 适配器,提供无中断的插拔和路由能力丰富的流量治理策略:基于权重的模型路由,金丝雀发布、Token级流控、故障转移·All-in-one实现架构,无需部署Envoy Gateway,原生支持PD分离的流量调度,将多层路由合并成一层,易于维护4) 成本驱动的自动扩缩容(Autoscaler)同构伸缩:支持稳定、突发双模式,按业务指标(CPU/GPU/内存/自定义)精准扩缩异构部署优化:在多推理引擎/异构加速器组合中按“成本-能力”贪心分配,最大化性价比5) 主流推理引擎与异构硬件支持支持多种主流推理引擎vLLM、SGLang、Triton/TGI 等,统一API抽象、标准化指标支持GPU/NPU 等异构混部,配合异构 Autoscaling 实现成本与 SLO 的动态平衡6) 内置流量控制与公平性调度公平调度:支持基于优先级和历史Token消耗的的公平调度,既兼顾用户的优先级,对高优先级用户提供更好的服务,又防止低优先级用户“饿死”流量控制:支持按照用户、模型、token长度进行精细化流量控制。  极致的性能提升  基于 Kthena Router 的调度插件架构,在长系统提示词场景(如 4096 tokens)下,采用“KV Cache 感知 + 最少请求”策略相较随机基线:吞吐可提升约 2.73 倍TTFT 降低约 73.5%端到端时延降低超过 60%短提示词场景差距会随提示词长度收敛,但在多轮对话、模板化生成、前缀高度相似的业务中,KV Cache 感知策略优势显著。实际收益与模型规模、Prompt长短、硬件紧密相关,但“按需组合、按场景选型”已被验证有效。  社区展望 / Call for Contribution  Kthena 在项目规划和发展的初期便得到了部分社区用户单位的关注和支持,但这只是一个开始。我们计划在未来支持更高效的调度算法、更广泛的大模型最佳部署实践,并持续深耕 LLM 推理的大规模部署和性能优化。“ 开源是技术创新的源头活水,也是推动产业标准化的最强引擎。作为Volcano项目的发起单位,华为云很荣幸能够与社区其他伙伴一起推出全新的Kthena分布式推理项目。这不仅是Volcano社区技术演进的重要里程碑,更是华为云在云原生AI领域长期投入与持续创新的有力见证。它将与华为云CCE(云容器引擎)、CCI(云容器实例)等基础设施深度结合,进一步释放包括昇腾(Ascend)在内的多元算力价值,为客户提供极致的算力性价比。我们希望通过Kthena,与全球开发者与伙伴,共建、共享一个开放、繁荣的云原生AI生态,为千行万业的智能化升级构筑最坚实的算力底座。”—— 祁小波,华为云通用计算服务产品部部长“ Kthena进一步巩固了Volcano在智能计算调度领域的领先地位。我们的平台利用Volcano的统一调度与资源池化能力,一站式满足通用计算与智能计算中训练、推理等多类算力需求。这使得算力资源能够在不同场景间灵活流转,有效避免了资源割裂的问题。展望未来,我们期待 Kthena结合Volcano的弹性伸缩能力与Volcano Global的跨集群调度特性,共同推动算力资源利用率进一步提升!”—— 杨磊,中电信人工智能公司 PaaS研发总监“ Volcano 项目自诞生之日起,便始终与社区以及各类 AI 场景深度共建、同频演进,逐步沉淀出一整套面向 AI 工作负载的调度与批处理生态。今天,Kthena 的出现,不仅将这条共建链路进一步拓展到大模型推理领域,把推理这一关键一环真正纳入 Volcano 生态之中,更是在统一编排与智能路由层面,将 Volcano 在调度、弹性伸缩以及多算力适配上的多年实践,凝练成一个令人振奋的里程碑式能力。借助既有的 Kubernetes / Volcano 生态,更多团队可以用更低的成本,获得更智能的调度决策和更高效的算力利用,并在开放协作的基础上持续演进。这不仅为道客解决了在推理场景中遇到的实际问题,也是我们所期待的云原生 AI 形态——一个足够开放、足够智能、值得我们长期投入和深度参与的社区方向。”—— 徐俊杰,DaoCloud 开源团队负责人,Kubernetes 社区指导委员会成员“ 自建大模型推理服务的生产级部署和运维难题,是一个覆盖推理服务全生命周期管理(部署、运维、弹性、故障恢复等),GPU集群稳定性,资源调度效率、推理服务性能提升,推理流量智能调度、AI可观测等领域的系统工程。而这也正是Kthena项目的技术定位。早在Kthena的规划阶段,小红书云原生团队就和Kthena贡献者做了深度的沟通,在推理流量智能调度方向,一起设计了多种流量调度策略和路由实现。未来,双方将继续在AI网关方向合作,结合小红书内部业务经验,一起为社区提供更精细化的AI流量智能调度能力,模型API管理能力,MCP协议支持等多种生产可用能力。”—— 空古(陈华昌),小红书云原生业务网关负责人“ 在深入调研并试用Kthena这一云原生AI推理平台后,联通云对其展现出的前瞻能力印象深刻。我们尤为看好其与Volcano实现的联合调度特性,其网络拓扑感知与Gang Scheduling功能,能够有效解决大规模分布式模型推理场景下中,关于效率与可靠性的核心诉求,为破解复杂调度难题提供了极具潜力的解决方案。我们相信,Kthena卓越的低延迟、高吞吐与多模型智能路由能力,将为开源社区带来真正具备生产级的AI推理解决方案,助力开发者更高效地构建和管理云原生环境下的智能应用。”—— 卢照旭,联通云智算能力中心团队长“ 开放和协作是构建社区的未来、加速技术创新的核心动力。在CNCF,我们持续致力于推动基础设施向‘AI Native’演进,为整个云原生生态提供标准、中立且可扩展的基础能力。Volcano社区通过孵化Kthena子项目,将其在大规模批量计算和调度上积累的拓扑感知、Gang调度等核心经验,精准地应用到了LLM在线推理这一关键场景。Kthena的价值在于,它提供了一套专为大模型设计、可供业界参考借鉴的云原生调度原语和抽象,这有助于将复杂的LLM推理工作负载,真正以Kubernetes原生的一等公民身份进行高效管理。这不仅是Volcano项目技术演进的重要一步,更是社区生态在解决AI规模化部署挑战中贡献的一份重要实践经验。我们诚挚邀请全球的开发者、研究人员和所有云原生爱好者加入,共同贡献智慧,完善这些关键AI基础设施,加速 AI Native 进程。”—— Kevin Wang,Volcano Maintainer、CNCF TOC 副主席  立即开始探索 Kthena  GitHub 仓库: cid:link_1Volcano 官网: https://kthena.volcano.sh/社区(加入我们的 Slack): https://cloud-native.slack.com/archives/C011GJDQS0N让我们一起,为 LLM 插上云原生的翅膀,释放 AI 的全部潜能! Volcano 是业界首个云原生批量计算引擎,也是 CNCF 首个和唯一的批量计算项目。项目主要用于 AI、大数据、基因、渲染等诸多高性能计算场景,对主流通用计算框架均有很好的支持。目前,Volcano在人工智能、大数据、基因测序等海量数据计算和分析场景已得到快速应用,已完成对 Spark、Flink、Ray、 Tensorflow、PyTorch、Argo、MindSpore、Paddlepaddle 、Kubeflow、MPI、Horovod、Mxnet、KubeGene 等众多主流计算框架的支持,并构建起完善的上下游生态。 更多云原生技术动向关注容器魔方
总条数:166 到第
上滑加载中