MiniMax Mini-Agent 深度解析:從零建構 Production-Grade CLI Agent
MiniMax 推出的 Mini-Agent 是一個定位獨特的開源 Agent 框架。它不像 LangGraph 或 CrewAI 那樣追求通用性,而是以「如何用最精簡的程式碼為特定模型建構完整 CLI Agent」為核心目標。整個框架核心僅約 2000 行 Python,但涵蓋了 Agent Loop、工具系統、記憶管理、上下文摘要、Skill 漸進式載入、MCP 整合與 Interleaved Thinking 等完整功能。本文將從原始碼角度逐一拆解這些設計。
定位與 MiniMax M2.5 的關係
Mini-Agent 的定位是 MiniMax 自家旗艦模型 M2.5 的最佳實踐參考實現。它不是一個通用的 Agent 開發框架,而是一個緊密耦合於 MiniMax 模型生態的 CLI 工具與參考架構。
MiniMax M2.5 提供 Anthropic 相容 API 與 OpenAI 相容 API 兩種介面,Mini-Agent 的預設配置直接指向 api.minimax.io(海外)或 api.minimaxi.com(中國大陸)。框架會自動偵測 MiniMax 域名並附加正確的路徑後綴——Anthropic 模式附加 /anthropic,OpenAI 模式附加 /v1。這個設計讓框架既能緊密服務自家模型,又保留了接入第三方 API(如 SiliconFlow)的通用性,只要對方相容 Anthropic 或 OpenAI 協議即可。
技術棧方面,Mini-Agent 採用 Python 3.10+ 搭配 asyncio,使用 Pydantic 2.x 驅動所有資料模型,httpx 作為 HTTP 客戶端,tiktoken 計算 token,prompt-toolkit 提供互動式 CLI,並以 MIT 授權開源。
架構總覽
Mini-Agent 的模組結構清晰,可分為五個核心區塊:
mini_agent/
├── agent.py # Agent Loop、摘要、取消機制
├── cli.py # 互動式 REPL、工具初始化
├── config.py # YAML 設定管理、優先級搜尋
├── llm/
│ ├── llm_wrapper.py # 統一 LLM 介面、自動路由
│ ├── anthropic_client.py # Anthropic SDK(含 thinking)
│ └── openai_client.py # OpenAI SDK(含 reasoning_details)
├── tools/
│ ├── bash_tool.py # Shell 執行 + 背景程序管理
│ ├── file_tools.py # Read/Write/Edit + token 截斷
│ ├── note_tool.py # Session Note 記憶系統
│ ├── mcp_loader.py # MCP 工具載入(STDIO/SSE/HTTP)
│ ├── skill_loader.py # SKILL.md 解析與路徑處理
│ └── skill_tool.py # Progressive Disclosure Level 2
└── skills/ # Claude Skills(git submodule)
核心設計模式包含:單一 Agent + 工具呼叫迴圈(經典 ReAct 模式)、統一介面 + 策略模式(LLMClient 路由到不同 SDK 實作)、Progressive Disclosure(Skill 三層載入)、Pydantic 驅動(所有資料模型統一管理)與裝飾器模式(重試機制非侵入式注入)。
Agent Loop:執行迴圈的核心
Agent Loop 是整個框架的心臟,位於 agent.py 的 Agent.run() 方法中。其運作流程如下:
async def run(self, cancel_event=None) -> str:
step = 0
while step < self.max_steps:
if self._check_cancelled():
self._cleanup_incomplete_messages()
return "Task cancelled by user."
await self._summarize_messages() # 上下文管理
response = await self.llm.generate(
messages=self.messages, tools=list(self.tools.values())
)
# 記錄 token 用量
if response.usage:
self.api_total_tokens = response.usage.total_tokens
# 加入歷史
self.messages.append(Message(
role="assistant",
content=response.content,
thinking=response.thinking,
tool_calls=response.tool_calls,
))
# 沒有 tool calls = 任務完成
if not response.tool_calls:
return response.content
# 執行工具
for tool_call in response.tool_calls:
tool = self.tools[tool_call.function.name]
result = await tool.execute(**tool_call.function.arguments)
self.messages.append(Message(
role="tool",
content=result.content if result.success else f"Error: {result.error}",
tool_call_id=tool_call.id,
name=tool_call.function.name,
))
step += 1
幾個關鍵設計值得注意:
訊息歷史是核心狀態。self.messages: list[Message] 保存所有對話歷史,每次 LLM 呼叫都帶入完整歷史。工具查找透過字典 self.tools = {tool.name: tool} 實現 O(1) 查詢。
取消機制透過 asyncio.Event 實現,在步驟開始、工具執行前、每次工具執行後三個檢查點偵測中斷。取消後呼叫 _cleanup_incomplete_messages() 移除最後一個不完整的 assistant message 及其 tool results,確保訊息歷史的一致性。
錯誤處理上,工具執行異常被捕獲並轉為失敗的 ToolResult,不會中斷整個迴圈。這意味著單一工具失敗不會導致整個任務中止,LLM 可以根據錯誤資訊決定下一步行動。
工具系統
基底類別設計
所有工具都繼承自統一的 Tool 基底類別,定義了 name、description、parameters(JSON Schema 格式)與 execute 四個核心介面,並提供 to_schema() 和 to_openai_schema() 兩個方法將工具定義轉換為不同 API 協議所需的格式:
class Tool:
@property
def name(self) -> str: ...
@property
def description(self) -> str: ...
@property
def parameters(self) -> dict: ...
async def execute(self, **kwargs) -> ToolResult: ...
class ToolResult(BaseModel):
success: bool
content: str = ""
error: str | None = None
BashTool:最複雜的內建工具
BashTool 的實作遠超簡單的 shell 執行。它支援跨平台(Windows PowerShell / Unix bash)、前景命令超時控制(預設 120 秒,最大 600 秒),以及完整的背景程序管理。
背景程序管理是 BashTool 最大的亮點。設定 run_in_background=True 後會啟動背景程序並回傳唯一 bash_id。BackgroundShellManager 是類別級別的 singleton,管理所有背景程序的生命週期。每個背景程序有獨立的監控 task(asyncio.create_task)持續讀取 stdout,支援 regex 過濾輸出(filter_str 參數),並實現優雅終止流程(先 SIGTERM 再 SIGKILL)。搭配 BashOutputTool(讀取背景程序輸出)和 BashKillTool(終止背景程序),形成完整的程序管理工具鏈。
FileTool:智慧 Token 截斷
ReadTool 讀取檔案後會用 tiktoken 計算 token 數量,超過 32000 token 時執行智慧截斷——保留檔案頭部和尾部,中間插入截斷提示,並在最近的換行符處斷開以避免破壞行結構:
def truncate_text_by_tokens(text, max_tokens):
chars_per_half = int((max_tokens / 2) / ratio * 0.95)
head_part = text[:chars_per_half] # 找最近的換行
tail_part = text[-chars_per_half:] # 找最近的換行
return head_part + truncation_note + tail_part
WriteTool 提供完整覆寫,EditTool 則實現精確的字串替換編輯。值得注意的是,EditTool 使用 content.replace(old_str, new_str) 會替換所有出現位置,不像某些框架要求唯一性檢查。
記憶與 Session Notes
Mini-Agent 的記憶系統由 record_note(SessionNoteTool)和 recall_notes(RecallNoteTool)兩個工具組成。筆記以 JSON 格式儲存在 workspace/.agent_memory.json,每筆記錄包含時間戳、分類標籤(如 user_preference、project_info、decision)和內容:
{
"timestamp": "2026-02-20T15:30:00",
"category": "user_preference",
"content": "User prefers concise responses"
}
因為資料存在檔案系統,所以天然支援跨 session 持久化,新的 Agent 實例可以回憶之前記錄的筆記。不過這套系統比較基礎——沒有向量搜尋,只有精確的 category 過濾;沒有自動遺忘機制,筆記會無限累積;Agent 需要被提示「主動使用」這些工具,並非自動觸發。
Context Summarization:上下文摘要
當對話歷史過長時,Mini-Agent 透過上下文摘要機制壓縮歷史,而非簡單截斷。
觸發條件有兩個(滿足其一即觸發):本地 tiktoken 估算超過 token_limit(預設 80000),或 API 回報的 total_tokens 超過該閾值。
摘要策略的核心理念是「保留意圖、壓縮執行」:
async def _summarize_messages(self):
estimated_tokens = self._estimate_tokens()
should_summarize = (estimated_tokens > self.token_limit or
self.api_total_tokens > self.token_limit)
if not should_summarize:
return
user_indices = [i for i, msg in enumerate(self.messages)
if msg.role == "user" and i > 0]
for i, user_idx in enumerate(user_indices):
execution_messages = self.messages[user_idx + 1 : next_user_idx]
if execution_messages:
summary_text = await self._create_summary(execution_messages, i + 1)
具體做法是保留所有 user messages(代表使用者意圖),將每輪 user 訊息之間的 assistant/tool 訊息摘要為一條精簡的 summary,使用獨立的 LLM 呼叫生成。摘要後的訊息結構變為:system -> user1 -> summary1 -> user2 -> summary2 -> ...。
為避免連續觸發,摘要完成後設置 _skip_next_token_check = True 旗標,等下次 LLM 呼叫更新 api_total_tokens 後才再次檢查。
Skill 系統:Progressive Disclosure
Skill 系統是 Mini-Agent 架構設計上最精巧的部分之一,採用三層漸進式揭露機制,有效控制 context window 用量。
Level 1(Metadata):系統提示詞中注入所有 skill 的名稱與描述(每個 skill 約 100 字),讓 LLM 知道有哪些能力可用,但不佔用大量 context。
Level 2(Full Content):Agent 判斷需要某個 skill 時,呼叫 get_skill(skill_name) 載入完整的 SKILL.md 內容。
Level 3+(Resources):Skill 內容中可引用 scripts/、references/、assets/ 等附加資源,Agent 按需讀取。
載入流程從 SkillLoader.discover_skills() 開始,遞迴搜尋 skills/ 目錄下所有 SKILL.md,解析 YAML frontmatter 與 Markdown body,並將相對路徑轉為絕對路徑。路徑轉換涵蓋三種 pattern:scripts/xxx.py 轉為絕對路徑、文字引用加上 (use read_file to access) 提示、Markdown 連結 [text](./file.md) 轉為絕對路徑。
skill-name/
├── SKILL.md # YAML frontmatter + Markdown 指令
├── scripts/ # 可執行腳本
├── references/ # 參考文件
└── assets/ # 輸出資源
內建的 15 個 Skills 來自 Anthropic 的 anthropics/skills Git submodule,涵蓋文件處理(docx、pdf、pptx、xlsx)、設計創意(生成藝術、視覺設計、GIF 動畫)、開發技術(MCP 伺服器開發、Playwright 測試)與企業溝通等領域。值得注意的是,這些 Skills 本質上是 Markdown 指令而非可程式化的 plugin 系統,它們的效果完全依賴 LLM 理解並遵循指令的能力。
MCP 整合
Mini-Agent 透過 MCP SDK 支援三種連線類型:stdio(標準輸入/輸出,用於本地 CLI 工具)、sse(Server-Sent Events,用於遠端 HTTP 伺服器)、streamable_http(新式 HTTP + SSE 協議)。
設定透過 mcp.json 管理:
{
"mcpServers": {
"minimax_search": {
"type": "stdio",
"command": "uvx",
"args": ["--from", "git+https://...", "minimax-search"],
"env": {"API_KEY": "..."},
"connect_timeout": 10.0,
"execute_timeout": 60.0
},
"remote_server": {
"url": "https://example.com/mcp",
"type": "streamable_http",
"headers": {"Authorization": "Bearer ..."}
}
}
}
每個 MCP 工具被包裝為 MCPTool 實例,持有 MCP ClientSession 引用,透過 session.call_tool() 執行。超時保護分為三層:connect_timeout(預設 10 秒)、execute_timeout(預設 60 秒)、sse_read_timeout(預設 120 秒),支援全域設定和每個伺服器個別覆寫。
預設 MCP 伺服器包括 MiniMax 自家的搜尋工具(search、parallel_search、browse)與 @modelcontextprotocol/server-memory(知識圖譜記憶系統)。
雙協議 LLM 支援
Mini-Agent 同時支援 Anthropic 和 OpenAI 兩種 API 協議,透過 LLMClient wrapper 實現統一介面:
class LLMClient:
MINIMAX_DOMAINS = ("api.minimax.io", "api.minimaxi.com")
def __init__(self, api_key, provider=LLMProvider.ANTHROPIC,
api_base="https://api.minimaxi.com", model="MiniMax-M2.5"):
if is_minimax:
if provider == LLMProvider.ANTHROPIC:
full_api_base = f"{api_base}/anthropic"
elif provider == LLMProvider.OPENAI:
full_api_base = f"{api_base}/v1"
else:
full_api_base = api_base
if provider == LLMProvider.ANTHROPIC:
self._client = AnthropicClient(...)
elif provider == LLMProvider.OPENAI:
self._client = OpenAIClient(...)
策略模式的設計讓切換 provider 只需修改設定,不需要改動任何業務邏輯。重試機制以裝飾器模式非侵入式注入,支援指數退避(delay = initial_delay * base^attempt,上限 max_delay),預設最多重試 3 次。
Interleaved Thinking:交錯思考
Interleaved Thinking 是 Mini-Agent 最核心的技術特色。大多數 Agent 框架不處理 LLM 的 thinking/reasoning 內容,但 Mini-Agent 在兩種協議下都完整保留並回傳思考過程,使得 M2.5 模型能在多輪 tool-use 中維持連貫的推理鏈。
Anthropic 模式下,回應中的 thinking type content block 被保留在訊息歷史中,下次呼叫時回傳 thinking block 以維持思維鏈連續性。
OpenAI 模式下,透過 extra_body 傳入 reasoning_split: True,回應中的 reasoning_details 包含思考內容。關鍵在於歷史訊息中必須保留 reasoning_details,否則思維鏈會中斷:
# OpenAI 模式的關鍵處理
if msg.thinking:
assistant_msg["reasoning_details"] = [{"text": msg.thinking}]
這個設計讓 Agent 在執行複雜的多步驟任務時,LLM 不僅能看到之前的行動結果,還能回顧自己的推理過程,從而做出更連貫、更準確的決策。
技術亮點總結
上下文摘要的精妙設計。不是粗暴截斷,而是保留使用者意圖、壓縮執行過程,結合本地估算與 API 回報的雙重觸發條件和防連續觸發機制,在實用性和穩健性之間取得了良好的平衡。
背景程序管理的完整性。唯一 ID 追蹤、持續輸出監控、regex 過濾、優雅終止、singleton 管理器,這套機制在同類框架中相當少見。
Progressive Disclosure 的 context 控制。三層載入機制讓 15+ Skills 不會在初始化時就塞滿 context window,只在需要時才載入完整內容,配合自動路徑轉換確保 skill 在任何工作目錄下都能正確引用資源。
非侵入式重試。裝飾器模式的重試機制完全不修改業務邏輯,支援自訂回調和指數退避,是工程上的好實踐。
侷限與觀察
在架構層面,Mini-Agent 是純粹的單一 Agent 設計,不支援 multi-agent 協作或 agent-to-agent 通訊。LLM 回應是一次性返回的,CLI 模式下沒有串流輸出。token_limit(80000)和 max_tokens(16384)都是硬編碼的預設值,沒有根據模型動態調整。框架也不支援 workflow/graph 結構,純 loop 不支援條件分支或子 agent。
Skill 系統方面,所有 Skills 來自 Anthropic 的 Git submodule 而非 MiniMax 自研。Skills 是 Markdown 指令而非可程式化的 plugin 系統,效果完全依賴 LLM 的指令遵循能力。沒有 skill 版本管理機制,所有 skills 是快照式引入。
工具系統方面,EditTool 的 replace 會替換所有匹配位置,缺少唯一性檢查。RecallNoteTool 雖然在 __all__ 中匯出,但在 CLI 的 add_workspace_tools 中並未自動添加,需要手動處理。MCP 工具沒有快取機制,每次啟動都需要重新連線所有 MCP 伺服器。
與同類框架的定位比較
從定位上看,Mini-Agent 可視為「MiniMax 版的 Claude Code」。它與 Claude Code 在架構上高度相似(file tools、bash、skill system、session notes),但規模小得多,核心程式碼約 2000 行。相較於 LangGraph 這類通用 graph-based agent 框架,Mini-Agent 專注於特定模型的 CLI Agent 場景。相較於 OpenAI Agents SDK,Mini-Agent 更專注於互動式終端體驗而非 API-first 的設計。
Mini-Agent 不適合作為通用的 Agent 開發框架使用,但作為「如何用 Python 從零建構一個 production-grade CLI Agent」的參考案例,它的設計品質相當高。簡潔的程式碼量、清晰的模組邊界、完整的 Interleaved Thinking 支援與精巧的 Skill 漸進式載入機制,都是值得學習和借鑑的工程實踐。