谈一谈我对 Mini-Agent 的理解

谈一谈我对 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

这么做的好处有

  1. 快速高效. 想要修改某一个文本文件, 使用 edit 比 bash 修改更加简洁
  2. 权限限制. 只想要读取一个文本文件时, 使用 read 比 bash 能更好的限制读写权限
  3. 专项优化. 还是 edit 工具, 它需要一个 old_str 匹配, 但实际上, 如果 old_str 中存在不可见字符,或者能同时匹配多个位置,应当怎么处理
  4. 兜底方案. bash 就像是一个全能的工具箱, 什么都能做, 但不应该优先选择它
FileTool

这是很多 agent 都会内置的 tool, 提供了基础的 read, write 和 edit 操作

对于 harness 工程而言, 一个极简的 agent, 只需要提供 4 个 tool 即可(read, write, edit 和 bash)

Mini-Agent 中的 read, 让我印象深刻

  1. 保留了原始行号. 即 hello world 被读取之后的内容会被添加行号前缀 1 hello world. 不过, 值得注意的是, 在进行 edit 时, 是不需要这个行号的, 所以在 read 的描述中, 有这样的说明: Output always includes line numbers, in format 'LINE_NUMBER|LINE_CONTENT' (1-indexed)
  2. 对超长文本进行截断. Mini-Agent 有一个截断算法, 会把读取的文本内容从中间进行截断, 用 ... [Content truncated: {token_count} tokens -> ~{max_tokens} tokens limit] ... 来说明对原文进行了截断

对于 edit 的处理就比较粗糙了

  1. 一方面, old_str 中可能在文章中不唯一, 意味着可能匹配多个位置. 这方面仅仅通过 tool 的描述来要求大模型只能对于出现了唯一的 old_str 进行 replace 是不够稳健的
  2. 另一方面, old_str 中可能存在一些空格等不可见字符, 大模型对于这些字符的处理能力还比较弱, 像是 python 这样强依赖缩进的语言, 可能会出现匹配不上的问题

对于 edit 的问题, Agent Harness 做了四层处理

  1. 直接匹配
  2. 对 old_str 进行换行符归一化后匹配
  3. 在 2 的基础上, 去除 old_str 的空格后匹配
  4. 在 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 长程任务与状态外部化强制规范 (Plan Mode: ON)

!!! 警告:本模式下,你绝对不能依赖自己的短期记忆。你必须将所有的架构思路和执行进度持久化到物理文件中。 !!!

当你收到一条新指令被唤醒时,你必须、且只能按照以下【绝对顺序】执行你的动作:

**[STEP 1: 强制环境嗅探 (Bootstrapping)]**
- 收到指令后,你必须第一时间使用 bash(如:`ls -la`)检查当前工作区根目录下是否已经存在 `PLAN.md` 和 `TODO.md`。
- **分支 A(全新任务)**:如果这两个文件不存在,说明这是一个全新的任务。你必须使用 write_file 依次创建它们:
1. 先创建 `PLAN.md`,写下你的理解、架构设计、技术选型。
2. 再创建 `TODO.md`,拆解出具体的可执行步骤(使用标准的 Markdown Checkbox 格式,如 `- [ ] 步骤1`)。
- **分支 B(断点续传/任务唤醒)**:如果这两个文件已经存在,**绝对不要覆盖它们!** 这意味着系统刚刚重启,或者人类接管了进度。你必须立即使用 read_file 仔细阅读 `PLAN.md` 了解全局目标,并阅读 `TODO.md` 寻找第一个未被打勾的 `- [ ]` 任务,从那里直接继续干活。

**[STEP 2: 严格的单步执行与实时打勾]**
- 开始执行 `TODO.md` 中未完成的任务。
- **强制约束**:每当你通过 write_file 或 bash 真正完成了一个子任务后,你**必须立即停下来**,优先使用 edit_file 工具(或 bash 的 sed 命令),将 `TODO.md` 中对应的行修改为 `- [x]`。
- 绝对不允许“一口气写完所有代码最后再打勾”。做完一步,必须打勾一步!

**[STEP 3: 迷失时的自救]**
- 如果你在执行中遇到了报错,或者不知道下一步该干嘛了,立即使用 read_file 重新读取 `TODO.md` 确认自己的位置。
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 要做到渐进式披露, 分了两个部分

  1. 在系统提示词中暴露所有 skill 的基本信息(主要是 name 和 description)

比如在 Mini-Agent 中, 系统提示中被嵌入了如下内容

1
2
3
4
## Available Skills
You have access to specialized skills. Each skill provides expert guidance for specific tasks.
Load a skill's full content using the appropriate skill tool when needed.
- `{name}`: {description}

用于告知大模型可以使用哪些 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 中的上下文压缩方案是我此前没有看到过的, 我大致描述一下

首先是触发时机, 在每个大模型请求调用前都会判断是否需要进行会话压缩, 评估的条件是

  1. 测算的 token 消耗大于 token 限制时
  2. 根据大模型响应的 token usage 计算的 token 总和大于 token 限制时

以上条件任意一个满足, 就可能触发压缩

压缩机制如下

一般消息列表为 [system, user, ai, tool, tool, user, ai, tool, ...]

压缩时, 保持系统消息和用户消息不变, 按用户消息进行切割, 会得到 [[ai, tool, tool], [ai, tool], ...]

分别对切割之后的消息打平成一个字符串 summary_content 丢给大模型进行总结

总结的提示词模板如下

1
2
3
4
5
6
7
8
9
10
11
12
You are an assistant skilled at summarizing Agent execution processes.

Please provide a concise summary of the following Agent execution process:

{summary_content}

Requirements:
1. Focus on what tasks were completed and which tools were called
2. Keep key execution results and important findings
3. Be concise and clear, within 1000 words
4. Use English
5. Do not include "user" related content, only summarize the Agent's execution process

最后将总结的内容 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 条消息不变, 对久远的历史消息进行压缩

具体的处理方案为

  1. 系统消息不变
  2. 没有 content 的 tool call 消息不变
  3. tool result 用户消息

如果为久远消息, 且 content 过长, 则只保留一个消息长度. 如

1
f"...[为了节省内存,早期的工具输出已被系统强制清理。原始长度: {origin_content_len} tokens]..."

否则掐掉中间, 只保留头和尾

1
f"{head}\n\n...[内容过长,中间 {mid_content_len} tokens 已被系统截断]...\n\n{tail}"
  1. ai 消息

如果为久远消息, content 和 reasoning_content 直接折叠处理

1
2
content = "...[早期的推理思考过程已折叠]..."
reasoning = "...[早期的推理思考过程已折叠]..."
  1. 其它保持不变

这是一种不借用大模型的压缩方案, 处理起来比较粗糙, 可作为参考思路

聊聊我的看法

能上生产环境的上下文管理不只是会话压缩, 单纯只聊会话压缩的话, 我有一些想法

  1. 系统消息不变
  2. 最近的 n 条消息不变
  3. 久远消息需要大模型进行总结. 至少需要包含任务背景, 执行目标, 已完成的任务, 未完成的任务, 以及存在的风险

以及一些注意事项

  1. 原始的消息列表始终被保留
  2. 每次压缩的应该从原始的消息列表进行压缩, 而不是从上一次被压缩的消息. 这样做的好处是可以尽可能不偏离原始的任务目标
  3. 最近的 n 条消息界线不那么固定. 比如, 被 n 切割之后的消息把同一组 tool call 和 tool result 拆分开了, 这就会是一个问题, 分割线不应该出现在一个逻辑完成的消息组上
再聊一个细节

对于 tool call 执行失败了, 返回的错误 content, 也有讲究

比如在 Mini-Agent 中

  1. 根据进程id查询进程失败时, 可在错误 content 中返回当前可用的所有进程id
  2. 在获取某一个具体的 skill 失败时, 可在错误 content 中返回当前可用的所有 skill

Agent

Mini-Agent 的主循环部分和其它 agent 没太大的区别

大致流程就是

1
2
3
4
5
6
7
while 是否到达最大循环轮次:
1. 判定 agent 是否被取消
2. 压缩上下文
3. 请求大模型
4. 没有 tool call 则结束循环
5. 判定 agent 是否被取消
6. 有 tool call 则执行

说到 agent loop 的结束条件, 很多框架(比如 deepagents)都是由大模型的响应中是否有 tool call 来判断是否结束循环

我还试过在大模型的响应中添加一个结束标记 is_end 来辅助判断, 效果不太好

我觉得应该由一个独立上下文的模型来判定 loop 是否结束比较合理一些

当然, 这个标记只是辅助判断, 不能作为唯一的判定条件

同样也可以让大模型对为什么认定为已完结给出具体的证据说明, 也能在一定的程度上防止大模型乱编

最后

Mini-Agent 是一个不错的学习项目, 尽可能少依赖第三方库, 很多地方的处理相对简单, 作为入门来说是一个不错的选择

想要深造的话, 还需要继续接触更多的 agent 项目, 如 codex 源码库

作者

wuhunyu

发布于

2026-08-07

更新于

2026-08-07

许可协议

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×