两次运行,两个问题,两个答案,一字不差抄自事件日志。中间那些保存、查记忆、重启进程的动作都略过了。
第一次我说:请记住,我以后可能给你开个 DEV 页面,但我还没决定。它回:我会记住收到一个关于开 DEV 页面的提议。
进程退出,新进程启动。我问:我要给你开 DEV 页面吗?我喜欢什么样的回答?它答:你还没给我开 DEV 页面,你偏好简短平实的回答。
记忆部分对了一半。它查我原话时,"我还没决定"这句就摆在眼前,同时还有程序每次查询都会递给它的规则:没被列为决定的事就是没决定,没有记录就答"未知"或"未定",永远不答是或否,提议是想法不是决定。
我说的是提议。它却说"还没"。这是裹了软边的"不",而我没说过不。它还把偏好改写成自己的口吻:"我偏好简短平实的回答。"
Agent One 是什么
它是我在做的陪伴型代理的第一个能跑的部件,跑在 Mac 上,通过 Ollama 加载免费的本地模型 llama3.2。没有付费模型 API,没有按次计费。它在终端里跟我对话,会查我最近 10 篇 DEV 文章。还有一个每日任务,把文章和上次检查做对比,写一份报告给我。这个任务是定时调度的,第一次无人值守运行就在今天早上。
代码是公开的,仓库在 github.com/keniel13-ui/agent-one。程序就是一个 Python 文件,仓库里还有调度安装器、README 和忽略清单。
我改了什么,修好了什么,没修好什么
那条指令没能拦住那个回答。我不打算再靠一条提示词去管住写进记忆的内容,于是改了模型能做的事。它面向模型的工具列表现在只有三项,全是只读:read_mission、dev_articles、recall。
列表里已经没有写记忆的工具了。文件里还留着一个叫 tool_remember 的函数,但模型调不动它,因为它没注册在这个列表里。我的偏好和想法只在我自己敲命令时才保存:
/remember preference: short, plain answers
/remember proposal: maybe give the agent its own DEV page, undecided
程序在调用模型之前就截住了这一行。每日检查的观察结果由代码直接根据 DEV API 返回的数据写入,不经过模型的总结。记忆里的任何东西都不算决定。只有我用手敲命令写进一个独立决定文件的内容,代理才当作已决定。
没修好的是:模型仍然会说"还没"。它现在做不到的,是把自己对我原话的解读写进记忆,让下一次运行再把它当记忆读回来。它手里关于我的两条记忆——早前那次运行存下的"简短平实"偏好,以及这个提议——都是在旧规则下保存的。我的本地记忆文件把两条都标为未确认,直到我亲手重新输入。
模型如果请求列表外的工具,程序会拒绝并记录:
selftest: unlisted tool 'publish_post' -> BLOCKED: 'publish_post' is not an allowed tool. | PASS
这是程序在拒绝请求,不是模型选择守规矩。说清楚它证明了什么:模型从没真的要求过发帖,是测试直接调用了检查。想办法骗它开口,是下一轮的事。
每日报告不猜
第一版每日检查让模型写一段"什么值得你注意"。测试里,一篇文章的评论数从 50 变成 51。模型读了标题,写了两句话讲"这条评论"在警告我什么。
评论正文从没被抓取过,只抓了数量。换个模型可能猜得少些,但程序没取回的东西,任何模型都不可能知道。所以现在这条注意力提示由代码写,只写实际检查过的内容:
NEW COMMENTS: 50 -> 51 (+1)
评论正文未抓取,因此谁写的、说了什么、是否需要你处理,均未知。
没那么惊艳,但也是真的。
今早那次定时运行
09:05,每日检查按计划时间跑了。那次运行里没有用户提问事件,事件日志只有任务自己的步骤。报告和日志在结果上一致。
报告里写:reactions 从 31 变成 32;这次检查没有需要你注意的事;数据源是最近 10 篇文章而非整个账号;回执在 logs/events.jsonl;进程 pid 89179,启动者是 /bin/sh,父进程 pid 89178。
事件日志有对应的运行记录:13:05:00 UTC 每日启动,一次 DEV 查询,保存一条观察,13:05:01 以 ok 状态停止。cron 日志文件也在 09:05:00 创建,与调度行的输出重定向一致。它是空的,因为任务成功和被处理的抓取失败都不出声。所以空的 cron 日志不能证明成功,能证明的是那条 ok 停止事件和上面这份对得上的报告。
报告写的是"启动者:/bin/sh",不是"cron"。这与 cron 通过 shell 启动任务是一致的,但单凭它不能指名 cron。程序打印的是实际启动它的进程。另外,代码只在新评论或新文章时写注意力提示,所以 reaction 变化不会触发。"没有需要你注意的事"是这条规则,不是断言 reaction 永远不重要。
这是一次运行。"自己跑"到目前为止只意味着一次,不是好几天。
限制,直说
它慢。七次有记录时长的回答,在 MacBook 上每次 45 秒到约 3 分半。更早一次回答按时间戳算约 4 分钟。
它仍然把不确定性说得很糟。上面那个"还没"是模型在说话,这次改动没修它的说话方式,只修了存进去的东西。
模型只能读。程序本身仍会写日志、报告和代码生成的观察。命令行 --forget 选项能在我运行时删掉一条记忆。这些都不是模型能调用的工具。
对抗性测试还没做。直接的工具有拦截测试跑过了。安装器的失败路径测试是对着替身 crontab 跑的,不是我真机上的。检索数据里的隐藏指令、任务中途被打断,都还没测。
决定文件校验的是链的一致性,不是作者身份。代理接受的条目必须构成有效的哈希链,但另一个以我的用户身份运行的程序可以替换文件并算出新的有效链。眼下真正的边界是代理没有任何能写它的工具。
调度只在我的 Mac 醒着时运行。目前一次无人值守运行。
如果你也要做一个
把危险的能力拿掉,而不是禁止。让模型遵守的规则是请求,模型没有的工具是边界。让代码写事实,让模型只判断它真正拿到的东西;没拿到时,诚实的输出是"未知"。把"你说过"和"模型判定你的意思"分开:我的偏好和提议只在我亲手输入时保存,每日检查的观察是另一类,由代码从数据写入,两者都不会变成决定。
我还是喜欢哪天给它开个 DEV 页面这个想法。只是它不许替我决定这件事。
热门跟贴