news 2026/7/30 9:36:11

拆解 harness9(11):M1 总结与 M2 展望

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
拆解 harness9(11):M1 总结与 M2 展望

M1 完成之后:harness9 为什么要走向本地 Agent OS

关于 harness9

harness9 是一款 Local-First、轻量级、功能完备、生产可用的通用 Go Agent 框架。

  • 官网:https://zhangshenao.github.io/harness9/zh/
  • GitHub:https://github.com/ZhangShenao/harness9

⭐ Star 是对开源工作最直接的支持,欢迎提 Issue 和 PR。


TL;DR

  • Milestone-1 终于完成了!harness9 现在不只是“能跑一下”的 Agent,而是有执行、边界、记忆、隔离和检查的一套本地底座。
  • 对于增加系统复杂度这件事,我们一直相当克制:同一套核心逻辑服务命令行和流式界面,少一份重复,就少一份以后难查的差异。
  • Agent 能做什么、这次能不能做、问题出在哪里,是三件不同的事。M1 把它们分开处理。
  • Sub-Agent、Planning、Context Engineering 和 Memory 等基础能力已经 ready;M2 要补上的,是让多个 Agent 能一起干活、暂停后接得上、过程也说得清的那层协调机制。
  • M2 已经开始搭建 Mission Foundation。它不会急着做一个“什么都能调度”的大平台,而是先把本地开发任务的计划、审批、执行、交接和验收走稳。
  • 这一路花了很久,也投入了很多心血。走到 M1 这个里程碑,不是终点,而是终于有了值得继续往前走的地基!

本文你将学到

  • 你将看懂一个 Agent 从“想一想”到“真的动手”时,harness9 怎样让它不乱跑。
  • 你将理解为什么权限、隔离和工具,不该混成一句提示词里的要求。
  • 你将分清 M1 已经交付的能力,和 M2 正在补齐的协作能力。
  • 你将看到我们怎样用测试和记录来判断“真的做好了”,而不是只看模型说得像不像。
  • 你将知道 M2 想做什么,也知道它刻意不做什么。

M1 我们做了哪些工作?

GitHub M1 已完成,harness9 v1.0.0也已经在 2026-07-21 发布。看到它关闭的那一刻,心里很安静,也很踏实。

这个项目走了很久。一路上,我们不是只在给 Agent 加功能,而是在反复追问:它真要动手时,能不能看得见、拦得住、出了问题能不能找到原因?很多看上去不起眼的细节,背后都是一次次推翻、补测试、再收紧边界。M1 的完成,意味着这些问题终于有了一套经得住使用的回答。

所以,M1 不等于“接上一个模型”。它交付的是一个 Agent 在自己电脑上可以被认真对待的底座:能运行,也有规则;能做事,也留得下记录。M2 才是在这个底座上,让多个 Agent 学会配合。

这张图不能代替代码,但它能帮人一眼看懂 M1 的分工。AgentEngine在中间,却不是一个人扛下所有事。Planning 负责把事情列清楚;Sub-Agent 帮忙拆活;Memory 不让信息只留在一次聊天里;Sandbox 和权限链负责守边界;Observability 与 Evals 则负责留下“发生过什么、做得对不对”的记录。

最容易走偏的做法,是把所有事情都塞进 Agent 的主循环。这样一开始省事,后面每加一个功能都可能牵一发动全身。harness9 选了更慢、但更稳的路:让这些能力围着核心接入,而不是挤进核心。EngineObserver就是一个小例子——它只在一次对话和每一轮开始、结束时接住信息,不去打扰核心工作。

typeEngineObserverinterface{OnInteractionStart(ctx context.Context,sessionID,promptstring)context.ContextOnInteractionEnd(ctx context.Context,turnsint,errerror)OnTurnStart(ctx context.Context,turnint)context.ContextOnTurnEnd(ctx context.Context,turnint,hasToolCallsbool)}

这份约定让默认的noopObserver几乎不用额外成本,也让 OpenTelemetry 能把一次对话里模型和工具的过程串起来。说白了,M1 的一个坚持是:内核要尽量稳定,想加的新功能要有边界,能看得见、查得清。

Agent 的本质还是 ReAct Loop

Agent 的工作方式其实不难理解:它先看看已有的信息和能用的工具,再决定回答,或者调用工具继续做事;工具的结果回来后,它再接着想。M1 把这个来回过程收在同一个runLoop里。

能同时做的事,就不必硬排队。但“同时”不等于放任。每个工具都有时间限制,整个过程也有轮数上限;结果回来时依旧按稳定顺序放回去。我们希望它跑得更快,也希望出现问题时有清晰的排查链路。

看下面这段runLoop。每一轮开始时,它会拿到这次真正允许用的工具。这里不是“拜托模型别乱用”的口头提醒,而是程序只把允许的工具交给模型。在规划阶段,写文件这类工具会被挡在外面。

availableTools:=e.registry.GetAvailableTools()ifplanMode==planning.PlanModePlan{availableTools=filterReadOnlyTools(availableTools)}msgTokensBefore:=memory.EstimateTokens(contextHistory)compactedHistory:=e.applyCompactionWith(comp,contextHistory)msgTokensAfter:=memory.EstimateTokens(compactedHistory)em.tokenUpdate(msgTokensAfter+toolTokens,e.contextWindow)

命令行里的Run,和界面里一边生成一边显示的RunStream,表面上是两种体验,但底下没有各写一套循环。它们只是把结果用不同方式交给用户。这样一来,轮数、压缩、审批和工具调用都只有一份规则。以后修复一个问题,两边一起生效,不会出现“命令行没事,界面又不一样”的尴尬。

工具失败也不急着把整件事掐断。harness9 会把ToolResult{IsError: true}交回给模型,让它知道哪里没走通:它可以换个参数、换条路,或者坦白告诉用户限制在哪。所谓“自愈”,关键是别把错误吞掉,错误也是关键的 Context,它能指导 Agent 的下一步工作。

Prompt 不靠谱,约束 Agent 要靠权限与 Guardrail

本地 Agent 真正让人担心的,不是它会不会写几句话,而是它会不会执行命令、改文件、访问网络。只靠一句“请小心操作”并不可靠。M1 把这件事拆开来做:它有什么能力、这一次是否被允许、就算出错又会被关在哪里,分别处理。

  • Tool Registry 决定 Agent 看到且能调用哪些BaseTool
  • Hook + Human-in-the-Loop 决定某次调用是allowdeny还是ask
  • Sandbox 通过Environment将 bash 和文件操作路由到本地进程或 Docker 容器。

Environment只负责几件很实际的事:跑命令、读文件、写文件、关掉环境。工具不用自己分辨是在电脑上跑,还是在 Docker 容器里跑。打开 Docker 后,bash会进容器执行,文件依旧指向同一个工作目录;不打开时,一切照旧走LocalEnvironment。新能力不能把旧用法弄坏,这也是我们一直守住的底线。

typeEnvironmentinterface{RunBash(ctx context.Context,cmd,workDirstring)(string,error)ReadFile(ctx context.Context,pathstring)([]byte,error)WriteFile(ctx context.Context,pathstring,data[]byte)errorID()stringClose(ctx context.Context)error}

Sub-Agent 也不是看见一句“不要继续派人”就会老实。ResolveTools会直接收紧它能用的工具,并且拿掉task。这样 Sub-Agent 就不能再递归委派,也不能靠几句输入把权限要回来。这个道理很朴素:真正值得信任的,不是 Agent 说自己会克制,而是程序在该拒绝、该暂停、该找人确认的时候,真的能把它拦住。

状态有了,协调还没有

M1 已经把很多“别只靠脑子记”的事情落到了本地。SQLite Session 会保存会话和待办;TodoStore知道一件事是在等着做、正在做,还是已经完成;长期记忆会把跨会话的信息留下来;后台 Sub-Agent 的进度和结果,则由TaskTracker先接住,等主 Agent 接着处理。

不过,我们也不想夸大 M1 的工作。TaskTracker现在负责的是当前进程里的后台任务:它会保护好进度,完成后让主 Agent 拿一次结果。它已经把当下的并发和重复消息管住了,但还不是那种电脑重启后,整张任务关系、谁该重试、谁在占用工作区都能自动接上的系统。

func(t*TaskTracker)DrainCompleted()[]CompletedTask{t.mu.Lock()defert.mu.Unlock()varout[]CompletedTaskfor_,task:=ranget.tasks{iftask.state!=TaskRunning&&!task.injected{task.injected=trueout=append(out,CompletedTask{TaskID:task.id,AgentName:task.agentName,FinalText:task.finalText,IsError:task.isError})}}returnout}

这不是 M1 少做了什么,而是我们给 M2 留下的起点。M1 先把会话、计划、记忆、后台工作和隔离 Agent 各自站稳。M2 再把它们接成一条完整的路:谁负责哪件事,谁能改哪个工作区,前面的事没做完后面的事怎么等,电脑中断后又从哪里继续,以及恢复时会不会绕开原来的权限。

Talk is cheap, show me the trajectory

Agent 很容易给人一种“看起来挺会做”的感觉。但真正做过项目的人都知道,能说出来和能稳定做到,中间差得很远。M1 因此把过程记录和自动检查放在开发当中,而不是等出问题了才去翻日志。

OTELEngineObserver会把一次对话和每一轮的过程串起来;Provider 会留下模型请求和 token;Tool Hook 会记录工具真的做了什么。另一边,EvalHarness用固定脚本去检查:该不该调用工具、最后说了什么、有没有报错、绕了多少轮。该失败的就明确失败,只是提醒的就给提醒。我们不把所有信号揉成一个看上去漂亮、却说不清原因的分数。

到了 M2,这件事只会更重要。多个 Agent 一起干活,可能卡在前后依赖,也可能碰到不同工作区和中断恢复。只看最后一句“我做完了”,根本不知道问题出在安排、权限,还是某个 Agent 越了界。所以 M2 的每一项工作,都要继续把边界、权限、过程记录和验收方式写清楚。

目标展望:M2 要做什么?

harness9 M2 的目标叫“本地 Agent OS”。听起来很大,但我们想做的事情其实很具体:可以在本地批量调度一系列 Agent,让它们协作完成复杂的长程任务,并且整个过程可审批、可监控、可管理、可暂停,也能在中断后有据可查地继续。

这件事已经开始。现在正在推进的是 Mission Foundation:先把任务、计划、审批、工作区和验收结果这些最容易混乱的东西,放到同一套本地记录里。一部分基础状态和本地记录已经落到代码;接下来还要把变更审批、调度和端到端协作继续做实。我们宁愿把这层地基打实,也不急着把“多 Agent”做成热闹的表演。

M2 结果面M1 已有基础M2 正在补齐
多 Agent 一起干活SubAgentDefinitionRunner、前后台执行、独立 Session明确能做什么、何时取消或重试、中断后怎么接上
任务怎么协作TaskTracker、Todo 状态、工具 Hook谁先做、谁后做、消息怎么交接、多人改文件时怎么避免撞车
本地工作区workDir、worktree、Sandbox、Session工作区怎么分配和回收,产物和过程怎么留下来
记忆与知识LTM、FTS5、MEMORY.md物化视图项目、用户和 Agent 的信息怎么区分、更新和由用户掌控
用结果说话Evals、CI gate、OTEL trace更完整的回归检查,以及出了问题能回溯到哪一步
看得见、管得住TUI、CLI、审批、Sandbox/MCP 状态栏在一个地方查看 Agent、任务、成本、过程和长任务状态

顺序比速度更重要。先让所有人都能知道“现在到底发生了什么”,再谈开多少个 Agent 一起跑。没有可靠的任务记录、明确的负责范围和取消规则,多开几个后台 Agent,只会把偶发问题放大成更难复现的麻烦。

权限也是一样。子任务可以少拿权限,不能多拿;系统从中断里恢复,也不能偷偷绕过原本需要人确认的操作。这些听上去不炫,但它们决定了这套系统是不是能让人放心交给它。

M2 也提前说清楚了哪些事不做:不去建设云端的大平台,不让外部账号在没人确认的情况下自动行动,不喊口号要取代操作系统,也不做酷炫的 GUI 交互。
把“不做什么”讲明白,才能把有限的心力留给最重要的事:在开发者自己的机器上,做出一套可信、看得见、能回头检查的协作方式。

结语

M1 终于达成了。它不是一张漂亮的功能清单,而是很多次认真取舍、很多个边角补齐之后,交到我们手里的一个阶段性答案。

我们知道,M2 会更难。当多个 Agent 同时行动、暂停、失败、再恢复时,开发者还能不能一直看清、控制并信任系统?这就是接下来要继续回答的问题。

但今天也值得停下来,认真记住这个时刻:harness9 已经走过了最开始那段只有想法、没有底气的路。谢谢每一份投入的心血。M1 是一个里程碑,也是继续往前的起点!

(PS. 一个人写代码还是蛮无聊的,如果大家对 harness9 这个项目感兴趣,欢迎一起来共建呀~)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/30 9:35:39

职场技能迭代:从提示词到AI管理能力的跃迁

1. 职场技能迭代的残酷现实:当提示词成为基础能力 2023年ChatGPT的爆发让"提示词工程师"一度成为最炙手可热的职业,但三年后的今天,这个曾经的金饭碗正在快速贬值。上周面试了一位自称"提示词专家"的候选人,当…

作者头像 李华
网站建设 2026/7/30 9:35:30

3分钟掌握Godot游戏PCK文件解包:官方工具实战与逆向学习指南

1. 项目概述:为什么我们需要解包Godot游戏资源? 如果你是一个独立游戏开发者,或者对游戏内部机制充满好奇,那么你很可能遇到过Godot引擎开发的游戏。Godot以其开源、轻量和强大的2D/3D支持,在独立游戏圈里非常流行。这…

作者头像 李华
网站建设 2026/7/30 9:34:29

3分钟搞定跨平台词库迁移:开源工具让40+输入法格式互通无阻

3分钟搞定跨平台词库迁移:开源工具让40输入法格式互通无阻 【免费下载链接】imewlconverter ”深蓝词库转换“ 一款开源免费的输入法词库转换程序 项目地址: https://gitcode.com/gh_mirrors/im/imewlconverter 你是否遇到过这样的困扰?换了新手机…

作者头像 李华
网站建设 2026/7/30 9:32:52

Spring Framework漏洞CVE-2024-22243修复实战:从原理到安全升级

1. 项目概述:一次紧急的Spring Framework漏洞修复实战 最近在维护一个线上Spring Boot项目时,安全扫描工具突然亮起了红灯,报告了一个名为CVE-2024-22243的漏洞。这个漏洞涉及Spring Framework的URL解析机制,虽然听起来不像Log4j…

作者头像 李华
网站建设 2026/7/30 9:28:40

LangChain与RAG实战:本地部署大模型应用开发指南

1. 先搞清楚这套组合到底解决什么问题 如果你正在接触大模型应用开发,大概率会听到 LangChain、RAG、Ollama、Agent 这几个词混在一起出现。这套组合最核心的价值是让开发者能在普通机器上,用相对可控的成本搭建具备专业知识问答、逻辑推理和任务执行能力…

作者头像 李华
网站建设 2026/7/30 9:27:46

Go语言反射与并发编程深度解析:从原理到实战应用

1. 从“知其然”到“知其所以然”:为什么Go程序员需要掌握反射与并发 如果你已经写过一段时间的Go,对 struct 、 slice 、 map 这些基本数据结构信手拈来,也能用 goroutine 和 channel 写出能跑起来的并发程序,那么恭喜…

作者头像 李华