谈一谈我对 Mini-Agent 的理解
Mini-Agent 是什么
minimax 出品的一个使用 python 编写的最小化的 agent demo 项目
仓库地址为 github(https://github.com/MiniMax-AI/Mini-Agent)
此前我学完了极客时间上的专栏《从 0 开始构建 Agent Harness》(以下简称“Agent Harness”)
结合我对 ai agent 的理解, 我想要简单地记录一下我从 Mini-Agent 项目中学到了什么
Tool
大模型的智能程度很高, 理论上, 我们只需要提供一个 bash 工具给大模型, 它就可以独立完成所有的事情
但一般而言, 我们会提供一些专用的方法而不仅仅是 bash
这么做的好处有
- 快速高效. 想要修改某一个文本文件, 使用 edit 比 bash 修改更加简洁
- 权限限制. 只想要读取一个文本文件时, 使用 read 比 bash 能更好的限制读写权限
- 专项优化. 还是 edit 工具, 它需要一个 old_str 匹配, 但实际上, 如果 old_str 中存在不可见字符,或者能同时匹配多个位置,应当怎么处理
- 兜底方案. bash 就像是一个全能的工具箱, 什么都能做, 但不应该优先选择它
FileTool
这是很多 agent 都会内置的 tool, 提供了基础的 read, write 和 edit 操作
对于 harness 工程而言, 一个极简的 agent, 只需要提供 4 个 tool 即可(read, write, edit 和 bash)
Mini-Agent 中的 read, 让我印象深刻
- 保留了原始行号. 即
hello world被读取之后的内容会被添加行号前缀1 hello world. 不过, 值得注意的是, 在进行 edit 时, 是不需要这个行号的, 所以在 read 的描述中, 有这样的说明:Output always includes line numbers,in format 'LINE_NUMBER|LINE_CONTENT' (1-indexed) - 对超长文本进行截断. Mini-Agent 有一个截断算法, 会把读取的文本内容从中间进行截断, 用
... [Content truncated: {token_count} tokens -> ~{max_tokens} tokens limit] ...来说明对原文进行了截断
对于 edit 的处理就比较粗糙了
- 一方面, old_str 中可能在文章中不唯一, 意味着可能匹配多个位置. 这方面仅仅通过 tool 的描述来要求大模型只能对于出现了唯一的 old_str 进行 replace 是不够稳健的
- 另一方面, old_str 中可能存在一些空格等不可见字符, 大模型对于这些字符的处理能力还比较弱, 像是 python 这样强依赖缩进的语言, 可能会出现匹配不上的问题
对于 edit 的问题, Agent Harness 做了四层处理
- 直接匹配
- 对 old_str 进行换行符归一化后匹配
- 在 2 的基础上, 去除 old_str 的空格后匹配
- 在 3 的基础上, 对原文和 old_str 进行 line-by-line 匹配
有了以上的四层方案, 可以大大增强 old_str 的匹配率
NoteTool
关于持久化记忆, 很多 agent 会使用 markdown 来记录, 比如 openclaw 的工作空间下, 有 USER.md, TOOLS.md, IDENTITY.md 等等文档来设置 openclaw 的角色定位
在 Mini-Agent 中, 暴露了两个工具来持久化会话重点
- record_note. 记录关键事件, 用户偏好和决策
- recall_notes. 检索之前记录的关键事件, 用户偏好和决策
record_note 和 recall_notes 其实就充当了 openclaw 中 markdown 的角色, 只不过在 Mini-Agent 中, 通过两个 tool 具象化了
在 Agent Harness 中, 会话的计划和待办事项, 是通过在系统提示词中加入如下内容来实现的. 它不依赖于专用的 tool, 而是借用已经存在的 read, write 和 edit 对 markdown 进行编写
1 | # 长程任务与状态外部化强制规范 (Plan Mode: ON) |
MCP Tool
这部分没太多好说的, MCP 其实提供了三种能力, Prompts, Resources 和 Tools, 但现在很多的 agent 都只用到了 Tools 能力
其做法也是把 MCP Tool 包装成一个普通的 tool
Skill Tool
skill 是渐进式披露加载的, agent 初始启动时, 有关于 skill 的信息一般只有 name 和 description, 当有需要的时候才会进一步加载 SKILL.md 的正文, 当有更进一步需要的时候, 会通过 read 等工具获取 skill 的其它信息(比如, 模板, 引用等)
在 Mini-Agent 中, 也是分了两个部分, 启动时, 先加载到系统内存中, 后通过 skill tool 读入大模型的上下文
有一个值得注意的地方是, Mini-Agent 对于 SKILL.md 中出现的路径都做了绝对路径替换, 这么做的好处在于后续需要读取这些文件时不会出现相对路径找不到的错误
skill 要做到渐进式披露, 分了两个部分
- 在系统提示词中暴露所有 skill 的基本信息(主要是 name 和 description)
比如在 Mini-Agent 中, 系统提示中被嵌入了如下内容
1 | ## Available Skills |
用于告知大模型可以使用哪些 skill
随后又把 get_skill tool 注册上来, 之后可能根据需要使用 get_skill 加载 skill 的正文
如果 skill 正文部分还引用了其它文档, 就需要大模型自己根据引用的路径调用 read 来查阅了
BashTool
Mini-Agent 中提供了两种类型的 tool, 即 run_in_background 为 true 时为后台异步任务; run_in_background 为 false 时为前台同步任务
其实我有点没想明白在 Mini-Agent 中为什么提供了后台任务
要知道一次大模型应答中, 可能包含了多个 tool call, 一般 agent 为了加快处理, 会对 tool 进行分组, 可并发执行和不可并发执行
对于可并发执行的且没有先后依赖的 tool, 并发执行会比较有效率
但 tool 之所以可以并发执行, 是需要先进行并发执行分析的
后台执行的任务如果和下一轮执行的 tool call 起了文件编辑冲突, 是比较难处理的一个问题
我认为给一把神兵利刃, 但却无法驾驭它, 比给一把菜刀更加不可信任
上下文管理
大模型的上下文窗口都是有限的, 虽然现在主流高端的大模型已经来到了 1M
Mini-Agent 会话压缩方案
Mini-Agent 中的上下文压缩方案是我此前没有看到过的, 我大致描述一下
首先是触发时机, 在每个大模型请求调用前都会判断是否需要进行会话压缩, 评估的条件是
- 测算的 token 消耗大于 token 限制时
- 根据大模型响应的 token usage 计算的 token 总和大于 token 限制时
以上条件任意一个满足, 就可能触发压缩
压缩机制如下
一般消息列表为 [system, user, ai, tool, tool, user, ai, tool, ...]
压缩时, 保持系统消息和用户消息不变, 按用户消息进行切割, 会得到 [[ai, tool, tool], [ai, tool], ...]
分别对切割之后的消息打平成一个字符串 summary_content 丢给大模型进行总结
总结的提示词模板如下
1 | You are an assistant skilled at summarizing Agent execution processes. |
最后将总结的内容 summary_text 封装成一条用户消息返回消息列表中
1 | ("user", f"[Assistant Execution Summary]\n\n{summary_text}") |
即, 消息列表从
1 | [system, user, ai, tool, tool, user, ai, tool, ...] |
变成了
1 | [system, user, user(summary message), user, user(summary message), ...] |
Agent Harness 会话压缩方案
策略是保留最近的 n 条消息不变, 对久远的历史消息进行压缩
具体的处理方案为
- 系统消息不变
- 没有 content 的 tool call 消息不变
- tool result 用户消息
如果为久远消息, 且 content 过长, 则只保留一个消息长度. 如
1 | f"...[为了节省内存,早期的工具输出已被系统强制清理。原始长度: {origin_content_len} tokens]..." |
否则掐掉中间, 只保留头和尾
1 | f"{head}\n\n...[内容过长,中间 {mid_content_len} tokens 已被系统截断]...\n\n{tail}" |
- ai 消息
如果为久远消息, content 和 reasoning_content 直接折叠处理
1 | content = "...[早期的推理思考过程已折叠]..." |
- 其它保持不变
这是一种不借用大模型的压缩方案, 处理起来比较粗糙, 可作为参考思路
聊聊我的看法
能上生产环境的上下文管理不只是会话压缩, 单纯只聊会话压缩的话, 我有一些想法
- 系统消息不变
- 最近的 n 条消息不变
- 久远消息需要大模型进行总结. 至少需要包含任务背景, 执行目标, 已完成的任务, 未完成的任务, 以及存在的风险
以及一些注意事项
- 原始的消息列表始终被保留
- 每次压缩的应该从原始的消息列表进行压缩, 而不是从上一次被压缩的消息. 这样做的好处是可以尽可能不偏离原始的任务目标
- 最近的 n 条消息界线不那么固定. 比如, 被 n 切割之后的消息把同一组 tool call 和 tool result 拆分开了, 这就会是一个问题, 分割线不应该出现在一个逻辑完成的消息组上
再聊一个细节
对于 tool call 执行失败了, 返回的错误 content, 也有讲究
比如在 Mini-Agent 中
- 根据进程id查询进程失败时, 可在错误 content 中返回当前可用的所有进程id
- 在获取某一个具体的 skill 失败时, 可在错误 content 中返回当前可用的所有 skill
Agent
Mini-Agent 的主循环部分和其它 agent 没太大的区别
大致流程就是
1 | while 是否到达最大循环轮次: |
说到 agent loop 的结束条件, 很多框架(比如 deepagents)都是由大模型的响应中是否有 tool call 来判断是否结束循环
我还试过在大模型的响应中添加一个结束标记 is_end 来辅助判断, 效果不太好
我觉得应该由一个独立上下文的模型来判定 loop 是否结束比较合理一些
当然, 这个标记只是辅助判断, 不能作为唯一的判定条件
同样也可以让大模型对为什么认定为已完结给出具体的证据说明, 也能在一定的程度上防止大模型乱编
最后
Mini-Agent 是一个不错的学习项目, 尽可能少依赖第三方库, 很多地方的处理相对简单, 作为入门来说是一个不错的选择
想要深造的话, 还需要继续接触更多的 agent 项目, 如 codex 源码库
谈一谈我对 Mini-Agent 的理解
https://wuhunyu.top/ai/agent/2026/08/thoughts-on-mini-agent/index.html




