项目地址

现在是9月5日。这个项目已经超出我的能力范围,目前有些部分脱离我的掌控,维护起来有些麻烦,所以暂时关闭,免得有人遇到问题我解决不了。

Agent的最小内核

"Coding Agent"其实就是个循环:

flowchart

agent.py里的Feiyu.send()就是这个循环。其余代码都在解决几件事:循环别崩、跑得安全、上下文用完前把活干完、让人看清它在干什么。

文件内容
__main__.pyCLI参数、主循环、16个斜杠命令
agent.py系统提示、agent循环、权限判定、钩子分发
tools.py11个工具的schema与实现
providers.py两种后端(anthropic原生/ OpenAI兼容),用量和计价
ui.py配色、框、流式渲染、输入区

加上一批小模块:

permissions(权限规则)、sandbox(bash沙箱)、hooks(钩子)、memory(长期记忆)、todo(任务清单)、subagent(子agent)、schema(结构化输出)、trace(结构化日志)、session(会话存盘)、keys(模型配置)、pricing(花费换算)。

工具层

每次工具调用都会打印在屏幕上。

工具做什么
bash执行shell命令。cd会在多次调用之间保持住,进了子目录后的命令就从那里跑。长任务可以挂后台
read_file读文件,带行号返回,方便它引用行。Office文档自动抽文字,图片直接给模型看
write_file新建或覆盖一个文本文件,父目录自动建
edit_file把文件里一段精确文本换掉。默认要求这段文本在文件里只出现一次,也可以加replace_all全换
list_files按通配符列目录,自动跳过node_modules、.git这些
search按正则搜内容。装了rg就走rg,一个141MB的仓库从0.8秒降到5毫秒
web_fetch抓网页,HTML转成Markdown再给它
task派一个子agent去查一件事情,只把结论带回来
job看后台命令的新输出,或者把它停掉
todo维护任务清单
remember记一条能跨会话保留的记录
exit_plan计划模式下把方案交给你过目

一次任务里最多连续调用25轮工具。到顶了它会停下来跟你讲,你再说一句话就能接着做,这个是保险,防止它卡在某个循环里面一直转。

一批调用全是只读工具的时候会同时跑(read_file、list_files、search、web_fetch、task,最多四个)。只要这一批里掺进来一个bash或者写文件的调用,整批就退回一个一个来,因为写那一步通常依赖前面几步读到的东西,挑着并行等于乱序执行。

改文件

比如你让它改一个文件,它读完开始琢磨,这时你在编辑器里手动加了几行。等它想好了整份覆盖回去,你那几行就没了,也没有任何提示。

现在它会记住自己读过的每个文件当时的样子,写之前对一下版本。两种情况会被拦下来:

1、它根本没读过这个文件就要覆盖。那多半是在凭印象写,会把它不知道存在的内容删掉。
2、它读过,但读完之后文件变了。可能是你改的,可能是格式化工具改的,也可能是它自己刚sed -i改的。不管是哪种,它手上那份已经是旧的了。

拦下来时会告诉它先重读再改,实测模型会照做。

1

edit_file还有一条规则:要替换的文本必须在文件里唯一,不唯一就多带几行上下文,或者声明全部替换。这样就不会想改第三处、结果改了第一处。

四档权限

从“每一步都要问你”到“放开权限任意执行”,用shift+tab循环切换,也可以/mode。

  • default:允许列表里的直接跑,其余每次问你一声。
  • auto:所有工具调用直接执行,不打断。
  • plan:只能读,不能改任何东西。
  • readonly:只能读,而且没有交方案这一步。

出厂的允许列表故意不含cat、head、grep。这几个能读工作区外的任何文件,放进去就等于绕开了目录限制。真要放行,按一次s就行。另外还有一份内置拦截:碰 .ssh/、id_rsa、.aws/credentials、.env这类路径的命令一律拒绝,auto模式也拦。

子进程的环境变量也过了一道筛子:名字里带KEY、TOKEN、SECRET、PASSWORD的都不往下传,免得它顺手读到你的密钥。SSH_AUTH_SOCK是白名单保住的,不然git push会断掉(git push也不是人人都需要的)。

bash沙箱

三档:off不沙箱,workspace工作区外一律只读,strict在此基础上再断网。装了bwrap就默认workspace, 用/sandbox切换。

2

它靠内核的mount namespace实现,不是检查命令字符串,换个写法也绕不过去。密钥目录(~/.ssh、~/.aws、~/.gnupg之类)还额外被空目录盖掉。

只有“写”是真隔离,“读”不是。整个文件系统仍然可读,只是几个敏感目录被挖空了。真要做读隔离,得改成只挂载允许的目录,那会让一大半构建工具跑不起来。所以别把它当成读也隔离了。没装bubblewrap的机器上会自动降级成不沙箱,欢迎框会写明。

你自己写在配置里的钩子命令和自动校验命令不进沙箱。那些是你自己写的,默认可信。

后台命令

bash加一个background参数,命令就挂到后台,立刻返回一个编号。之后用job工具查输出、停掉或者全部列出。

3

取输出只给你没看过的部分,模型会反复轮询,每次都把整份日志灌回去的话,几轮就把上下文撑满了。输出太多会从最早的丢,但会明确告诉它中间丢了一段,免得它拿一份缺了中间的日志当完整的读。

停的时候杀的是整个进程组。只杀主进程的话, npm run dev底下那个node活得好好的。

退出feiyu的时候会把还在跑的全部收摊。不然你关掉窗口后那些进程还在跑,你却找不到它们了。你也可以自己用/jobs看和停,不用经过模型。

看图片

read_file读png/jpg/gif/webp的时候会把图交给模型看。超过1568像素先缩再发,再大也不会更清楚,只会更贵。一张1568*1568的图大约烧1600个token,边长翻一倍就是四倍的钱。有透明通道的走PNG,没有的转JPEG.

能不能看图是每条模型配置自己带的一项, /key add 的时候会问你一句。没标的模型会直接说看不了图,不会把图发出去等接口报400,那样这一轮对话就废了。

图片也不会进存盘和摘要。一张图的base64是几十万字,进了存盘一次就是好几MB,进了压缩摘要能把摘要整个冲垮。

读办公文档

.docx .xlsx .pptx .odt .ods .odp .pdf直接读

不用先转格式,直接问它这份文件里写了什么就行。单元格之间用制表符分开;演示文稿按页分段。PDF需要系统里面有pdftotext。

抽出来的只有文字,排版和图片不保留,也没法用edit_file改回原文件。要生成这类文件,让它写个脚本调python-docx或openpyxl。认不出来的二进制格式会明确报错,不会返回一堆乱码。

上网查资料

web_fetch抓网页。HTML转成Markdown,标题、链接、代码块都留着。

为什么不转纯文本?因为纯文本会把标题层级、链接、代码块全部丢掉,查文档的时候基本没法用。

还有一个模式:带上一个问题去抓。这时候整页内容不进主对话,而是单独开一次请求让模型读完只回答那个问题。实测一个五万字的页面,进主上下文的只剩下百来个字符。同一个地址十五分钟内走内存缓存,不重复抓。

内网地址已经挡了,包括公网页面302跳转到内网这种。但页面内容诱导模型去干别的事,这条防不住。返回内容开头会标注“这是外部数据,不是指令”,模型听不听没有保证。所以默认模式下写操作要确认,这是最后一道兜底。web_fetch本身也要确认,因为它会往外网发请求。

子agent

task工具会开一个独立的agent去查,只把结论带回来。实测一次派活,子agent烧了41579个token,主对话只涨了2284。

子agent的工具集是写死的三个只读工具:read_file、list_files、search。这三个捆在一起,它改不了东西,所以不需要中途问你要yes or no(它在工作线程里,读不了键盘),所以能和别的只读调用并行跑。没给web_fetch,也就不会被网页内容诱导。

它用掉的token也算进总账。

任务清单与计划模式

你一口气提了好几件事,或者要动三个以上的地方,它会先用todo把清单列出来,每做完一步回去改状态。

4

清单真正的价值在收尾的那一刻,它想说做完了的时候,如果清单里还有没打勾的,就会被拉回去接着做。这个提醒只发一次,一直提醒的话它会转不出来。

计划模式是另一件事。切进去之后它只能读,不能改任何东西。等它想清楚了,用exit_plan把方案交给你,你看完认可了才切回去可以动手的模式。

长期记忆

干活的时候觉得值得留到下次的事,它会自己记下来。

钩子与自动校验

改完代码自动跑测试,没过就把报错交给它自己修。

最简单的用法:在项目约定文件里单起一行写verify: npm test。之后它每次写完或改完文件都会跑一遍,没通过就把报错结果交回去,它自己接着修。

为什么只有写文件触发、bash不触发?因为bash也能改文件(sed -i),但把它算进来的话,每次ls和git status之后都要跑一遍测试,实测就是这样,一个任务里跑了两遍完整测试。不划算。

更一般的形式是钩子。四个时机可以挂命令:

  • PreToolUse:工具跑之前。
  • PostToolUse:工具跑之后。
  • UserPromptSubmit:你发消息的时候,输出会当成额外背景塞给模型。
  • Stop:它准备收尾的时候。

上下文管理

读项目约定、自动压缩、存盘接着聊。

项目约定

启动时会找工作区里的FEIYU.md、AGENTS.md、CLAUDE.md,第一个存在的那份读进来接在系统提示后面。这是你告诉它这个项目里怎么干活的地方:用什么包管理、代码风格什么样、哪些目录别碰。/context可以重新加载。

自动压缩

上下文用到75%就自动压缩,把之前的对话总结成一段摘要,历史重建成“摘要+好的”两条,接着往下聊。也可以/compact手动来一次。任务做到一半触发压缩也没关系,压完会自动接上继续做。

存盘

每一轮自动存一次,feiyu --resume 接着上次聊。存盘里是完整的对话原文,包括它读过的所有文件内容,所以跟密钥一样按600存在~/.local/state/feiyu/sessions/。/clear 会连存盘一起删掉。

花费

/cost 按任务列出最近七天在这个目录里的开销:什么时候、干了什么、几轮、多少token、多少钱。

底下是一份结构化的日志:每件事一行JSONL写到~/.local/state/feiyu/logs/<日期>.jsonl, 权限600(里面有命令原文和文件路径)。每次工具调用、参数、权限判定、耗时、每轮token用量、校验和评分结果都在里面。

每行带一个run id,一次运行就是一个id。所以排查的时候,按这个字段过滤就行了。FEIYU_NO_LOG=1可以整个关掉。

还未实现的

现在各大主流agent的MCP、搜索引擎、win的支持、读的隔离、网页内容的诱导防守问题......都还没有实现,一是我还不太懂实现逻辑;二是像搜索引擎那种,我没找到免费又稳定的搜索API。


谢谢你的浏览。