尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

learn-claude-code s15:把 25 个工具、权限、记忆、任务、团队与 MCP 装进同一个 `while True` —— Integrated Harness 集成运行时详解

learn-claude-code s15:把 25 个工具、权限、记忆、任务、团队与 MCP 装进同一个 `while True` —— Integrated Harness 集成运行时详解 learn-claude-code s15把 25 个工具、权限、记忆、任务、团队与 MCP 装进同一个while True—— Integrated Harness 集成运行时详解【免费下载链接】learn-claude-codeBash is all you need - A nano claude code–like 「agent harness」, built from 0 to 1项目地址: https://gitcode.com/GitHub_Trending/an/learn-claude-code本篇解析 learn-claude-code 教程系列第 15 章s15_integrated_harness它不引入任何新机制而是把前 14 章分散演示的 tool dispatch、permission、hooks、memory、skills、compaction、task graph、background bash、cron、agent teams、worktree 与 MCP 全部接入同一个 agent 主循环组装出一套可实际运行的集成 harness。读完本文你能完整理解每个机制在模型循环中的确切挂接位置、事件如何回流到同一会话并能在仓库中对照 s15_integrated_harness/code.py约 3000 行逐行验证这些设计。一、问题长时间运行的 coding agent 需要“所有机制同时在线”前 14 章的做法是把每个机制放在独立的、可运行的最小示例里分别演示。但一个真正长时间运行的 coding agent 无法只拥有其中一个机制它必须同时具备tool dispatch 与 permission 边界hook 扩展点todo 计划与 task graphskills、memory、运行时 system prompt 组装compaction 与错误恢复background 任务与 cron 调度team、protocol 与 IDLE 任务认领绑定到 task 的 worktreeMCP 外部工具集成因此 s15 的定位很明确它不是一个新机制的章节而是一个“接线图”章节——展示既有机制进入 model loop 的哪些位置、它们产生的事件又如何回到同一段 conversation。二、总体架构所有机制都挂在同一个while True上s15 的完整运行流程如下user input → UserPromptSubmit hooks → cron/background notification injection → context compact → memory skills MCP state 组装 system prompt → LLM → 存在 tool_use block 否 → Stop hooks → 返回 是 → PreToolUse hooks permission → TOOL_HANDLERS / MCP handlers / background dispatch → PostToolUse hooks → tool_result / task_notification 回到 messages → 下一轮循环本身的骨架始终是 s01 学到的那个调用模型 → 检查响应里有没有tool_useblock → 执行工具 → 把结果追加回messages。是否继续执行工具完全由响应中tool_useblock 的有无决定。在源码中这段骨架对应 agent_loopwhile True: fired consume_cron_queue() # 1. 取出到期的 cron job注入 messages for job in fired: messages.append({role: user, content: f[Scheduled] {job.prompt}}) inject_background_notifications(messages) # 2. 注入已完成的 background 通知 ... prepare_context(messages, active_request) # 3. 压缩管线 context update_context(context, messages) # 4. 刷新 memory/MCP/teammate 状态 tools, handlers assemble_tool_pool() # 5. 每轮重新组装工具池 response call_llm(messages, context, tools, state, max_tokens) ... if not has_tool_use(response.content): trigger_hooks(Stop, messages) remember_after_turn(messages) # 记忆抽取 整合 return # 逐个处理 tool_use blockPreToolUse → handler/background → PostToolUse messages.append({role: user, content: build_user_content(results)})值得注意的是agent_loop并不只服务人类输入。async_event_loop 每秒轮询一次cron_queue、Lead 的收件箱与后台任务状态任何一个事件源出现新内容就会在持有agent_lock的情况下自动触发一轮agent_loop——这就是文档所说“CLI 监视cron_queue、Lead inbox、已结束的后台工作任何事件都能自动唤醒 Agent 一个 turn”的实现。用户交互入口与异步事件入口共用同一个循环体靠互斥锁串行化这是集成 harness 与单机制示例最本质的区别。三、各组件在循环中的精确位置文档给出的位置总表如下这是理解整章的索引位置组件角色user input 周边UserPromptSubmithooks记录、注入、审计用户输入LLM 前cron queue把定时 prompt 注入messagesLLM 前background notifications把完成的后台工作以task_notification注入LLM 前compaction pipeline给大输出设预算、裁剪历史、压缩旧 tool_result、必要时摘要LLM 前memory / skills / MCP state组装 system prompt让模型看到当前能力与长期上下文LLM callerror recovery429/529 重试、max_tokens升级、prompt-too-long 触发压缩工具执行前PreToolUsehooks permission拦截危险命令、越界写文件、破坏性 MCP 工具工具分发assemble_tool_pool组装内置工具与动态 MCP 工具工具执行中background dispatch把显式标记的 bash 工作移入 daemon 线程返回占位结果工具执行后PostToolUsehooks大输出告警、日志、后处理回到循环tool_result每个tool_use对应一个tool_result然后进入下一轮模型调用本轮无 tool_use / 停止时Stophooks统计、清理、审计四、工具与分发25 个内置工具 动态 MCP 工具池s15 的内置工具池共25 个工具在源码中以BUILTIN_TOOLS/BUILTIN_HANDLERS两个结构定义工具定义与 handler 映射bash, read_file, write_file, edit_file, glob todo_write, task, load_skill, compact create_task, list_tasks, get_task, claim_task, complete_task schedule_cron, list_crons, cancel_cron spawn_teammate, list_teammates, send_message request_shutdown, request_plan, review_plan create_worktree connect_mcp这些名字分别对应前文各章的机制todo_writes03、tasks04 subagent、load_skills05/s07、compacts06/s08、task graph 五个动词s07、cron 三个动词s12 前身、team 六个动词s09/s10、create_worktrees12 隔离与connect_mcps14。assemble_tool_pool() 在每一轮重新组装工具池BUILTIN_TOOLS 已连接的 MCP tools BUILTIN_HANDLERS mcp__server__tool handlers也就是说工具池不是进程级常量而是“内置工具 当前已连接 MCP server 的动态工具”的并集。执行connect_mcp(docs)之后下一轮模型就能看到mcp__docs__search这样的工具名。connect_mcp的实现code.py在 s15 中对接的是内置的 mock serverdocs、deploy工具发现后统一注册进全局mcp_clients供assemble_tool_pool每轮读取。五、权限与 Hookspermission 只是 PreToolUse 的一个 hooks15 的一个关键设计决策权限检查不硬编码在工具执行行里而是注册为PreToolUsehook。主循环中的接线只有三行blocked trigger_hooks(PreToolUse, block) if blocked: results.append(tool_result(block.id, blocked)) continuehook 框架本体极小四个事件桶 注册/触发函数hook 框架默认注册了四个 hookcode.pyregister_hook(UserPromptSubmit, user_prompt_hook) register_hook(PreToolUse, permission_hook) register_hook(PreToolUse, log_hook) register_hook(PostToolUse, large_output_hook) register_hook(Stop, stop_hook)由此permission、日志、审计逻辑全部挂到同一个扩展点上Lead 的工具、one-shot subagent 的工具、teammate 的工具都会先过PreToolUse被允许的调用在 handler 执行后再过PostToolUse。permission_hook 的实际策略值得逐条看bash先查DENY_LISTrm -rf /、sudo、shutdown、reboot、mkfs、dd if等硬拒绝未命中则在终端交互式询问Allow? [y/N]。源码里有一处体现“fail closed”的分支if threading.current_thread() is not threading.main_thread()时直接返回Permission denied: interactive shell approval is unavailable during an asynchronous turn——即只有前台用户 turn 允许打开交互式审批cron/后台事件触发的异步 turn 不与主 CLI 抢 stdin直接拒绝。file 工具read_file/write_file/edit_file路径resolve()后必须仍位于WORKDIR之内否则拒绝。MCP 工具策略不信任 MCP server 自报的 description。host 自己维护一张精确 allowlistMCP 策略表如(docs, get_version): allow、(deploy, status): allow、(deploy, trigger): confirm只有表中标allow的已知只读调用免确认其余 MCP 工具一律询问用户异步 turn 下同样 fail closed。trigger_hooks的语义也值得注意callback 返回None表示放行并继续遍历返回非None则立即短路——这正好承载了“某个 PreToolUse hook 可以否决整次调用”的协议。六、两层 Plan 与 Task Systems15 保留了两层计划它们目的相近但实现完全不同todo_write当前 session 的轻量计划保存在内存中。实现上它会整体替换当前会话的检查列表task graph跨 session、依赖感知、可认领claimable的任务文件持久化在.tasks/task_*.json。每个 task record 有稳定 ID 与独立的生命周期字段pending → in-progress → completed支持blockedBy依赖。前者防止单个 agent 跑偏后者是团队协作的地基。文档特别强调了一个容易混淆的点独立工具task的含义是“派遣一个隔离 subagent”它不是 Task System——不要与create_task/claim_task等 task graph 工具混为一谈。七、两种委派one-shot subagent 与 persistent teammates15 里同时存在两类委派机制分别解决不同问题taskone-shot subagent使用独立的messages[]中间上下文全部丢弃只把 final summary 返回主循环。它解决的是context isolation——让一次探索/检索不污染主线对话。spawn_teammatepersistent teammate thread常驻线程解决长期并行协作。其行为规则在文档中定义得相当细源码中的 spawn_teammate_thread 与run_loop一一对应若 Lead 传入 ready 的task_idruntime 会在 thread 启动前先 Claimclaim 失败则线程不启动若不传task_idteammate 以 IDLE 状态等待后续任务没有 assignment 的 teammate 不能使用 file 工具与 Shell 工具生命周期为WORK → result → IDLE没有固定的 tool round 上限模型或 dispatch 失败会向 Lead 发送error线程清理时未完成的 assignment 会被释放回任务板每次模型调用前都会先清空 inbox保证 direct message 与 shutdown 请求不会卡在一串连续 tool-use round 后面IDLE 时先等待MessageBus投递超时后才扫描 ready task并且原子地至多认领 1 件。配套的 Lead 侧行为同样反直觉但重要Lead 启动 teammate 后不在模型循环里反复轮询其状态而是直接结束当前 turn当 team event 进入 Lead 的收件箱时由 runtime即上一节的async_event_loop自动开启下一轮。这是“事件驱动唤醒”与“循环内轮询”两种协作模型的取舍s15 选择了前者。八、Memory、Skills 与 system prompt 的动态组装s15 直接复用 s09 的 Memory runtime代码中通过load_memory_runtime()载入为MEMORY_RUNTIME。时序为每次模型调用前读取.memory/MEMORY.mdcatalog为当前 request 选出相关 record将正文与目录一起交给 assemble_system_prompt(context)turn 结束后extract_memories()保存对未来 session 有用的信息若产生了新 record紧接着运行consolidate_memories()。对应源码 remember_after_turndef remember_after_turn(messages: list) - None: if MEMORY_RUNTIME.extract_memories(messages): MEMORY_RUNTIME.consolidate_memories()同一条 system prompt 还包含identity、工具使用指引、workspace 信息、skills catalog、已连接的 MCP server 列表。注意skills 只贡献 catalog名称描述全文由load_skill(name)按需加载——这是 s07 章节“渐进式披露”原则在集成环境中的落地。update_context()每轮刷新memory_catalog、memories、connected_mcp、active_teammates四项保证 system prompt 反映的是“此刻”的系统状态而非启动时的快照。九、上下文压缩管线与 LLM 错误恢复每次 LLM 调用前都走同一条预算管线源码 prepare_contexttool_result_budget → snip_compact → micro_compact → compact_history超限时def prepare_context(messages: list, active_request: str) - list: messages[:] tool_result_budget(messages) # 单条 tool_result 超预算则落盘 messages[:] snip_compact(messages) # 控制消息条数 messages[:] micro_compact(messages) # 压缩旧的 tool_result if estimate_size(messages) CONTEXT_LIMIT: messages[:] compact_history(messages, active_request) return messages其中tool_result_budget对超预算的大输出调用persist_large_output落盘后在上下文里留指针这与PostToolUse阶段large_output_hook的大输出告警是同一问题的两端处理。模型调用则被 recovery 层包裹with_retryRecoveryState策略为429指数退避重试529指数退避连续失败时可切换 fallback modelmax_tokensagent_loop中先一次性把max_tokens从DEFAULT_MAX_TOKENS升到ESCALATED_MAX_TOKENS重试仍截断则追加 continuation prompt 要求模型继续直到MAX_RECOVERY_RETRIES上限prompt too long捕获后执行reactive_compact每轮只尝试一次state.has_attempted_reactive_compact标记压缩后 retry。另外源码里还有一个文档未展开但真实存在的小机制rounds_since_todo计数器——连续 3 轮没有todo_write时循环会向messages注入一条reminderUpdate your todos./reminder用系统提示对抗长会话中计划漂移。十、Background 任务与 Cron 调度当 bash 调用显式带run_in_backgroundtrue时主循环不等待命令结束should_run_background → start_background_task → placeholder tool_result background done → task_notification → 下一轮注入 messages对应源码中should_run_background判定后调用start_background_taskhandler 在 daemon 线程执行主线程立刻把[Background task {id} started] Result will arrive as a task_notification.作为tool_result回给模型完成后的结果由collect_background_results()在下一轮经build_user_content与inject_background_notifications注入。要点只有显式标记的 bash 调用进入 background 路径命令非零退出或 worker 异常会产生failed通知每条 Shell 命令运行在独立的 process groupAgent 以正常路径或SIGTERM退出时会停止这些 group能创建新 session 的进程可以脱离该 group源码_stop_process_group/_handle_termination_signal实现了这条清理链。Cron 调度器以 daemon 线程运行每秒检查一次到期 job。durable 的一次性 job 会先以pending_delivery持久化再入队直到“包含其 prompt 的那次模型调用成功”才被 ackacknowledge_cron_jobs调用失败会restore_cron_jobs回到队列进程重启后同样会重新入队——因此投递语义是at-least-once而非 exactly-once。schedule_cron工具使用标准 5 字段 cron 表达式min hour dom month dow一次性提醒需自行计算目标分钟并设recurringfalsevalidate_cron会在注册时逐字段校验取值范围。十一、Worktree 任务隔离与 MCP 外部能力从 s13 继承的 task-scoped worktree 在 s15 中负责管理“每个任务在哪个工作副本里干”pending 且无主的 task 可以留在主 workspace也可以用create_worktree(name, task_id)绑定到独立的 branch 与目录创建前会校验 task、name、path、branch 与 Git registryGit 命令失败后会与 registry、branch state 做对账部分创建出来的 checkout 保持未绑定状态并保留供人工恢复IDLE teammate 原子认领一个 ready task 后assignment 同时记录task_id与 effectivecwdLead 也可以直接把 readytask_id传给spawn_teammateclaim 成功线程才启动teammate 的所有 file 工具都使用该cwd只有 owner 能 complete 该 taskassignment 保持选中直到当前模型 turn 结束complete之后同一 turn 内仍持有cwd进入 IDLE 才释放删除权保留在 host 侧的remove_worktree()模型无法调用用户或 host 需先确认 task ownership、assignment lease、background work 与 Git 状态破坏性删除需要单独的用户确认。文档对此有一句很诚实的定性worktree 只是改变了工具的默认工作目录用来隔离 working copy它不是 sandbox——process group 清理无法封禁一个创建了新 session 的进程所以删除操作保持 host-owned。另外两条版本一致性规则Task 的 Claim 或 release 会推进 assignment version使旧的 plan approval 失效而普通send_message只投递文本不改变 Task identity 与 plan state。MCP 则承担外部能力接入s14 的延续connect_mcp(name)连接 servers15 内置docs、deploy两个 mock server并发现工具assemble_tool_pool()把 MCP 工具并入工具池对归一化后的重名直接拒绝normalize_mcp_name先把工具名归一化到模型工具名字表内工具名统一为mcp__server__tool格式权限层再按 host 策略表决定 allow/confirm。十二、与 s14 的对比集成意味着什么Scopes14 MCPs15 Integrated Harness内置工具6 个25 个外部工具已连接的 MCP tools相同的动态 MCP 路径与 host 策略本地机制S04 的 tools、hooks、permission、MCPtodo、subagent、skills、compaction、memory、task graph、background bash、cron、teams、worktrees事件源user input 与 tool resultsuser input、tool results、cron prompts、background notifications、team events对比表揭示的本质是s14 里模型只会被“用户说话”或“工具结果”唤醒而 s15 中cron 定时 prompt、后台任务通知、团队事件都是与用户输入同级的事件源全部汇聚进同一个agent_loop。十三、动手运行与验证要点运行方式在仓库根目录下cd learn-claude-code python s15_integrated_harness/code.py推荐依次尝试的 5 条 prompt检查这个仓库告诉我哪些 Python 文件最重要。在已连接的文档中搜索关于 agent loop 的说明。在两个隔离的 worktree 中并行重构认证模块与登录页编辑前先给我看各自的 plan。3 分钟后提醒我开会。后台安装依赖的同时读取 README.md。观察要点也是验收清单每次工具调用是否都经过 hooks/permissionconnect_mcp之后下一轮是否出现 MCP 工具带run_in_backgroundtrue的 bash 调用是否返回 background placeholdercron 到点时是否自动触发 reminder turnteammate 是否提交 plan 并在 approval 前停住IDLE teammate 是否原子地只认领一个ready taskteammate 的每个 file 工具是否都切换到所认领 task 的cwdcomplete 之后同一 turn 内是否保持 taskcwdIDLE 时是否释放 assignment。仓库的 tests/ 目录包含针对各机制的自动化用例如 test_task_system.py、test_background_tasks.py、test_agents_smoke.py 等可结合本章行为规则做回归验证。十四、小结与下一步s15 给出的工程结论可以概括为一句话机制再多循环只有一个。它验证了三件事其一权限、日志、审计可以统一收敛到 hook 扩展点而不动工具本体其二所有异步事件源cron、后台任务、团队消息都能通过“注入 messages 事件循环唤醒”复用同一个模型循环其三fail-closed 的异步权限、host-owned 的破坏性操作、assignment version 使旧审批失效是把多 agent 系统做“安全”的三个低成本支点。下一章 s16 Workflow Runtime 将在同一 host 上追加Workflow工具把固定的编排路径固化到代码中并记录进度以便同一次 run 中断后恢复——这正是在 s15 集成 harness 之上继续叠加新能力的示范。【免费下载链接】learn-claude-codeBash is all you need - A nano claude code–like 「agent harness」, built from 0 to 1项目地址: https://gitcode.com/GitHub_Trending/an/learn-claude-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表