# mini_agent **Repository Path**: jswrt/mini_agent ## Basic Information - **Project Name**: mini_agent - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-23 - **Last Updated**: 2026-09-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 项目介绍 (AI生成) ## 0. 一句话概括这个项目 `mini_agent` 是一个**很小的 AI 智能体框架**: - 它把一个大任务交给一个大语言模型(LLM); - LLM 每一回合输出一段“代码块”(比如 bash 命令、Python 代码、要写入的文件); - 框架负责执行这些代码块,把执行结果(观察)再喂回给 LLM; - 如此循环,直到任务完成或达到最大回合数。 你可以把它理解成“**让 LLM 通过写代码来操作电脑完成任务**”的最小实现。 框架本身不绑定具体任务:`run/` 里的脚本负责把「任务 + 环境」接进来,`games/` 里放的是被接入的具体环境(比如 ALFWorld、Robotouille)。核心循环、技能系统与环境是解耦的。 --- ## 1. `core/agent.py`:整个框架的心脏 这个文件定义了 `Agent` 类,是整个项目的核心。读它的时候,建议按下面的顺序理解。 ### 1.1 `Agent` 是什么 一个 `Agent` 实例代表一个“正在执行任务的智能体”。它保存了: | 属性 | 含义 | |------|------| | `task` | 要完成的任务描述 | | `llm` | 使用哪个大模型(比如 deepseek) | | `name` | 智能体名字,用于日志 | | `max_turns` | 最多循环多少回合 | | `state` | 全局共享状态,`state[技能名]` 是该技能自己的字典 | | `skill_objs` | 已加载的技能对象 | | `trajectory` | 执行轨迹(每回合做了什么) | | `last_observation` | 上一回合的观察结果 | | `final_answer` | 最终答案(调用 terminate 后才有) | ### 1.2 一个 Agent 的生命周期 ``` __init__ → run() → 循环 { 构建 prompt → 问 LLM → 解析代码块 → 执行 → 记录观察 } ``` 对应到代码里的几个关键方法: 1. **`_load_skill(skill_name)`** 加载一个技能:读取 `skills/<名字>/SKILL.md`,解析出“接口 / 工作流 / 工具协议 / 状态”等小节,并把 `## 状态` 里的 JSON 变成初始状态。 2. **`_scan_available_skills()`** 扫描 `skills/` 目录,把所有技能的“触发条件”列出来,放进系统技能 `_agent` 的状态里,这样 LLM 知道有哪些技能可以加载。 3. **`_build_prompt()`** 把「任务 + 每个技能的契约 + 当前状态 + 最新观察」拼成一段文字,作为发给 LLM 的 prompt。 4. **`_parse_response(response)`** 从 LLM 的回复里,用正则提取出所有 ```` ```lang ... ``` ```` 代码块。每个代码块有 `lang`(语言/类型)和 `content`(内容)。 5. **`_execute_blocks(blocks)`** 执行这些代码块,支持三种类型: - `bash`:逐行当命令行执行(用 `subprocess`); - `python-exec`:用 `exec` 执行 Python 代码,结果写到 `result` 变量; - `path=<文件路径>`:把代码块内容写进文件。 6. **`run()`** 主循环:构建 prompt → 调 LLM → 解析 → 执行 → 记录观察,直到 `final_answer` 被设置(任务完成)或达到 `max_turns`。 7. **`_make_exec_namespace()`** 为 `python-exec` 准备命名空间,让代码里可以访问: - `state`(各技能状态) - `_agent`(当前 Agent 自己) - `<技能名>`(技能包的模块对象,比如 `robot`、`subagent`) ### 1.3 读 `agent.py` 的建议 先看 `run()` 主循环,理解“一问一答一执行”的节奏;再看 `_parse_response` 和 `_execute_blocks`,理解“LLM 的输出怎么变成动作”;最后看技能加载相关的方法。其它细节可以暂时跳过。 --- ## 2. `core/` 里的其它文件 | 文件 | 作用(一句话) | |------|----------------| | `core/log.py` | 日志系统。把每回合的 prompt、回复、观察、错误写进一个 HTML 文件,并在终端用彩色打印。 | | `core/sandbox.py` | 沙箱。在 Linux/macOS 上尝试用 `firejail` / `sandbox-exec` 把程序限制在“只能读写当前目录”,避免误伤系统文件。 | | `core/skill.py` | 技能解析器。把 `SKILL.md` 这种“Markdown + 缩进列表”的文本解析成 Python 的 dict / list,并提供 `render()` 把状态再渲染回 Markdown。 | 简单理解: - `log.py` 负责“记录发生了什么”; - `sandbox.py` 负责“限制能做什么”; - `skill.py` 负责“把技能说明书翻译成程序能懂的结构”。 --- ## 3. `skills/`:技能包 ### 3.1 目录构成 ``` skills/ ├── _agent/ ← 系统技能(每个 Agent 默认加载) ├── coding/ ← 读写文件、执行命令 ├── robot/ ← 操作 Gym 风格环境(比如机器人做汉堡) └── subagent/ ← 任务分解、派发子代理 ``` 每个技能文件夹一般包含: - `SKILL.md`:技能契约(给 LLM 看的说明书,定义工具、状态、规则); - `__init__.py`:技能的实际 Python 实现(LLM 通过 `<技能名>.<函数名>(...)` 调用)。 注意:`coding` 只有 `SKILL.md`,因为它的“工具”就是框架内置的 bash / path / python-exec 代码块,不需要额外 Python 函数。 ### 3.2 每个技能的作用(简明版) - **`_agent`(系统技能)** 所有 Agent 默认加载。定义 Agent 的基本工作流(读任务 → 决定下一步 → 执行 → 等观察),并提供两个核心动作: - `load_skill(skill_name)`:加载新技能; - `terminate(final_answer)`:结束任务。 - **`coding`** 负责“读写文件、跑命令、验证代码”。它维护一份块状状态(`files` / `symbols` / `todos` / `known_issues` / `notes` / `history`),让 LLM 能记住“我看过哪些文件、写过什么、遇到什么问题”。 - **`robot`** 包装任何有 `reset()` / `step(action)` 的 Gym 风格环境,只暴露两个工具: - `get_info(...)`:按需获取环境说明、当前观察、当前合法动作; - `task_action(valid_action=...)`:执行一个当前合法动作。 它还会把环境描述、目标、规则、世界状态等记进 `state["robot"]`。 真正被包装的环境对象由运行脚本注入:`skills.robot.env = GymEnv(real_env)`。`GymEnv`(在 `skills/robot/wrapper.py`)负责把任意 `reset()` / `step()` 环境适配成统一的 `env_description` / `valid_actions` / `current_observations` 属性。 - **`subagent`** 负责“任务分解 + 派发子代理”。当任务可以拆成多个自包含小任务时,用 `spawn_subagent(task=...)` 创建子 Agent 去执行,父 Agent 只做规划和重规划。这样可以隔离上下文,避免一个 Agent 越跑越乱。 --- ## 4. 其它文件夹 | 文件夹 | 作用 | |--------|------| | `llm_api/` | 各个大模型 API 的调用封装。`deepseek.py`、`glm.py`、`siliconflow.py` 三个文件结构几乎一样,都是流式请求 + 打印 reasoning/content。 | | `games/` | 具体环境的适配层。`alfworld_env/` 和 `robotouille/` 各有一个 `*_interface.py`,把第三方环境(ALFWorld、Robotouille)包装成统一的 `reset()` / `step()` / `valid_actions` / `env_description()` 接口,供 `skills/robot` 的 `GymEnv` 使用。`robotouille/` 下还有一批 `example_*.py` 演示脚本。 | | `run/` | 具体的“运行脚本”。`coding.py` 跑代码任务,`robotouille.py` 跑机器人做汉堡任务,`robotouille_subagent.py` 是带子代理的版本,`alfworld_test.py` 跑 ALFWorld 文本任务,`tools.py` 提供 `get_llm()`、`print_result()` 等公共小工具。 | --- ## 5. 推荐的阅读顺序 1. 先读这份 README,知道每个文件夹大概干什么。 2. 读 `core/agent.py` 的 `run()`,理解主循环。 3. 读 `core/skill.py` 的 `parse_skill_md()` 和 `render()`,理解 SKILL.md 怎么变成程序数据。 4. 打开 `skills/_agent/SKILL.md`,对照 `core/agent.py` 看“契约”和“实现”怎么对应。 5. 再看 `skills/coding/SKILL.md`、`skills/robot/SKILL.md`、`skills/subagent/SKILL.md`,理解不同技能的设计套路。 6. 看 `run/` 里的一个脚本(比如 `run/coding.py`)和它对应的 `games/` 适配层,把“怎么跑起来”补上。 7. 最后看 `llm_api/`,了解模型调用是怎么封装的。 读代码时遇到不懂的函数,先看它的**输入和输出**,再猜它内部做了什么,最后才逐行看实现。这样比一上来就抠语法要高效得多。 --- ## 6. 目录结构速查 ``` mini_agent/ ├── core/ # 框架核心 │ ├── agent.py # Agent 主循环 │ ├── log.py # HTML + 终端日志 │ ├── sandbox.py # firejail / sandbox-exec 沙箱 │ └── skill.py # SKILL.md 解析与渲染 ├── llm_api/ # 各模型 API 封装 │ ├── deepseek.py │ ├── glm.py │ └── siliconflow.py ├── games/ # 被接入的具体环境 │ ├── alfworld_env/ │ │ ├── alfworld_interface.py │ │ └── download_text_data.py │ └── robotouille/ │ ├── robotouille_interface.py │ ├── env_description.md │ └── example_*.py ├── run/ # 运行入口脚本 │ ├── coding.py │ ├── robotouille.py │ ├── robotouille_subagent.py │ ├── alfworld_test.py │ └── tools.py └── skills/ # 技能包 ├── _agent/ ├── coding/ ├── robot/ └── subagent/ ```