把智能交给模型,把能力交给文本,把信任交给日志。
thin-agent 是一个单文件、零依赖的薄内核智能体——LLM 与 skill 之间的粘合剂,为受限环境而生。
粘合剂的职责是让两个面贴合:向下贴合内网的模型端点,向上贴合可评审的技能文本。凡是替模型思考、替技能执行的机制,都是粘合剂里的杂质。"薄"不是代码行数上的姿态,而是把三样东西交还给更合适的承担者:
一、把智能交给模型。 智能是对话中涌现的,不是代码编排出来的。内核里没有规划器、没有反思循环、没有多 agent 图——循环、工具、上下文,就是全部。内核对模型的义务只有一条:不拖后腿。由此有一条重要推论:确定性逻辑一律下沉到技能脚本,模型只做编排与解读。概率系统负责理解与决策,确定性系统负责计算与搬运——这是 32B 级开源模型也能可靠干活的前提。
二、把能力交给文本。 skill 是一个目录,不是需要安装的插件;分发是 scp,不是包管理;声明能力靠 frontmatter,不是注册中心。凡是能以文本形式通过安全评审的东西,就不要让它变成软件。这是 thin-agent 能进企业内网的根本原因:受限环境里,每个进程都是要评审的软件,而文本不是软件。
三、把信任交给日志。 在没有沙箱的环境里,确认闸门就是沙箱,审计日志就是合规产物。每条命令过同一个闸门,每个决策落一行 JSONL;会话是 append-only 的事件日志,喂给模型的消息由纯函数从事件链投影——"模型看到的"与"日志记录的"恒等,历史不可篡改。信任不来自"我们承诺安全",来自"每一件事都查得到"。
薄还有一条可验证的判据,凡是想进内核的机制都要先回答:去掉它,skill 还能不能跑? 能,就不进内核。压缩、记忆、会话管理都是在这个判据下反复拷问后幸存的——它们不是能力,是上下文的搬运工。与之配套的是同等认真的非目标清单(见下文),以及一条硬线:单文件、纯标准库、Python 3.6+。突破硬线的那天,就是 thin-agent 死掉的那天。
很多企业内网的真实约束是:
- 生产环境只有 Linux bash + 原生 python3,装不了 Node、pip 不了包;
- 内网部署了 DeepSeek / Qwen(vLLM、SGLang 等 OpenAI 兼容端点),但没有外网;
- 任何软件进场都要过安全评审——评审一条 npm 依赖链不现实,评审一个几百行的 Python 文件和一堆文本目录可以。
主流 agent 框架在这个场景里全部失效。thin-agent 的回答是:让执行层薄到一个脚本——小到一个脚本的东西,有病毒式传播的潜力:拷走即运行,粘贴即部署。
- 内网运维排障:机器体检、日志分诊、资源排查——纯读命令自动放行,写操作逐条确认;
- 自动化嵌入:
-u一次性调用接进 crontab / 工单系统 / ChatOps 机器人,-g目标模式跑长任务; - 离线数据处理:数据分析、格式转换等流程用 skill 沉淀,按项目目录组织,随目录切换;
- 老旧与边缘环境:Python 3.6 即可运行,CentOS 7 时代的老机器、精简容器都装得下。
插件注册表、包管理、热加载、参数 schema 强校验、长驻服务、运行时自我改造。判断一个特性该不该进内核,标准只有一条:去掉它,skill 还能不能跑。踩到这些需求时,为单个问题引入最小机制,而不是把内核升级成框架。
# 1. 拿到文件(clone、scp,或者直接粘贴 agent.py 的内容——Paste and Go)
git clone https://github.com/vicfei/thin-agent.git && cd thin-agent
# 2. 指向你的模型端点(三行,也可以用文件顶部配置区)
export AGENT_BASE_URL=http://10.0.0.8:8000/v1
export AGENT_MODEL=Qwen3-32B
export AGENT_API_KEY=sk-xxx # 无鉴权可留空
# 3. 跑起来
python3 agent.py # 交互式(自动延续本目录会话)
python3 agent.py -u '看看这台机器状态如何' # 一次性
python3 agent.py -agu '汇总 /var/log 最近的错误' # 目标模式就这么多了。没有第四步。
| 参数 | 作用 |
|---|---|
-a, --allow-all |
命令免确认(硬拒绝仍然生效) |
-u, --user-ask |
一次性问答(默认不保存会话) |
-s, --with-session |
搭配 -u:载入并保存目录级会话 |
-g, --goal |
目标模式:自主循环直到模型执行 agent-done |
-l / -c |
列出 / 清空本目录会话 |
--text |
强制文本协议(不走 tool calling) |
交互模式内:/skills 查看技能(含被过滤原因)、/compact 手动压缩、/clear 开新话题(历史保留成分支)、/exit 退出。运行中按一次 Ctrl+C 打断当前推理/命令,会话不丢;空输入处连按两次才退出。
| 层级 | 行为 | 例子 |
|---|---|---|
| 硬拒绝 | 任何模式下都不执行,留痕 | rm -rf /、mkfs、dd of=/dev/*、fork 炸弹、关机等 |
| 需确认 | 逐条人工确认;非交互且无 -a 时拒绝 |
一切写命令、脚本执行 |
| 纯读直通 | 整条命令无 shell 元字符且首词在白名单 | cat、df、ps、grep…… |
- 所有决策写入
<cwd>/.agent/audit.jsonl;会话为 append-only 事件日志,消息由纯函数投影,不可篡改历史 - 白名单与硬拒绝表是
agent.py顶部两张可编辑的表,按公司安全基线定稿即可 - 已知边界:
- 闸门判定是保守的(含
;|&等元字符的命令一律走确认); read_file尚未纳入闸门与审计(可读用户可读的任意文件),属已知缺口;- 内核自身不做沙箱——需要强隔离时请把整个 agent 放进容器跑,隔离是部署层决策;
- 同目录多进程并发写同一会话未做互斥,避免多 agent 同时指向一个
.agent/。
- 闸门判定是保守的(含
skill 就是一个目录,遵守一份文件约定(完整版见 docs/Skill最小规范-v0.2.md):
skills/csv-stats/
├── SKILL.md # frontmatter(name/description/requires)+ 写给模型的操作手册
└── scripts/ # bash / python3 脚本,显式解释器调用,{SKILL_DIR} 占位符引用
最小的 SKILL.md 只有几行:
---
name: hello
description: 演示技能。当用户说"打个招呼"时使用。
---
# hello
运行:bash {SKILL_DIR}/scripts/hi.sh- 替换 = 文件操作:
AGENT_SKILLS_DIR整套换包、./skills随项目走、同名时靠前路径遮蔽 - 能力协商:
requires声明依赖(bins / python 版本 / 环境变量),启动时探测,不满足的技能静默过滤并留审计——装不了的技能,模型根本看不见 - 渐进式披露:常驻上下文的只有索引行,正文按需读取;上百个技能待命也不涨提示词
- 不热加载:新技能下次启动生效。这是刻意为之——热加载意味着插件系统回来了
内置示例:csv-stats(CSV 质量统计)、sys-doctor(只读机器体检)、log-triage(日志分诊)、skill-author(把流程沉淀成新技能的元技能)。
thin-agent/
├── agent.py # 内核:全部(单文件、纯标准库、Python 3.6+)
├── skills/ # 示例技能包
├── demo/ # 演示数据
├── tests/ # mock LLM + 冒烟测试(离线,无需真实模型)
│ └── run_smoke.py # python3 tests/run_smoke.py → 7 场景端到端
└── docs/ # Skill 最小规范
薄不等于简陋——每一层机制都有出处:
| 层 | 借鉴 | 机制 |
|---|---|---|
| 循环与执行语义 | mini-SWE-agent | 无状态子进程动作、协议可换解析、预算计数器、显式终止 |
| 部署与交互面 | EVA | 配置区+环境变量、目录级会话、读写二分、Ctrl+C 语义 |
| 上下文机制 | Pi | AGENT.md 分层指令、压缩即可替换 prompt、memory 文件 |
| 审计纪律 | Cordis / dsh | 单一执行闸门、append-only 日志唯一事实源、纯函数投影 |
支持哪些模型? 任何 OpenAI 兼容端点。tool calling 不稳的模型会自动降级到文本协议。经验法则:运维自动化任务 32B 级开源模型够用,复杂编码任务上可用的最大模型——薄 scaffold 不拖后腿,天花板由模型决定。
为什么不做成 MCP / 插件? 受限环境里每个进程都是要评审的软件。skill 是数据不是软件,这是它能进内网的原因。需要长驻服务时,skill 可以声明并拉起本地服务,内核不变。
Windows? 内核逻辑可移植(EVA 验证了这条路),当前闸门与技能示例面向 Linux,欢迎 PR。