learn-harness-engineering 项目 04 实战:以运行时反馈与架构约束修正 Agent 行为——增量索引的可观测性工程
2026/9/24 8:28:29 网站建设 项目流程

【免费下载链接】learn-harness-engineering

Harness engineering beginner tutorial, from 0 to 1

项目地址:https://gitcode.com/gh_mirrors/le/learn-harness-engineering
点击查看免费下载

本篇是 learn-harness-engineering 教程中「Project 04: Runtime Feedback and Structural Control」的完整实战指南(对应日文版 docs/ja/projects/project-04-incremental-indexing/index.md,中文版 projects/project-04/README-CN.md)。它承接 Project 03 的知识库应用,教你为 Agent 补齐运行时可观测性(启动日志、导入/索引日志、错误状态)与层级边界约束(防越界架构检查),并在一个被刻意植入缺陷的索引服务上完成「诊断 → 修复 → 验证」的完整闭环。读完本文,你将掌握结构化日志、分层架构护栏脚本、增量索引机制,以及一套可复现的「同一任务跑两遍」的 Harness 实验方法。

为什么 Agent 需要运行时反馈:本项目的核心命题

在 Project 01~03 中,我们已经验证了能力再强的 Agent 也会失败:它看不见自己程序的运行时状态,只能靠猜测。Project 04 直接把这个痛点做成了一门调试课——给 Agent 配上"眼睛"(结构化日志)和"边界"(架构检查脚本),再让它去修复一个被植入的运行时缺陷

相关讲义的论证是:Agent 的失败常常源于任务边界模糊(講義 07)与缺乏约束的工作范围(講義 08)。Project 04 把这两条教训落成具体产物:

  • 运行时反馈(Runtime Feedback):服务启动、文档导入、增量索引、Q&A 问答每个环节都输出机器可解析的 JSON 日志,Agent 不再"盲修";
  • 范围控制(Scope Control):用check-architecture.sh强制 renderer / preload / main / services 四层各守其责,Agent 的改动无法越界;
  • 增量索引(Incremental Indexing):索引服务按文档粒度增量推进,配合index-meta.json记录已索引状态,让长任务可以分段收敛。

这套组合正是本项目的Harness 机制ランタイムフィードバック + スコープ制御 + 増分インデックス(运行时反馈 + 范围控制 + 增量索引)。

任务总览:同一份工作,跑两遍

原文档给出的任务非常明确——同一份工作执行两次

  1. 第一遍(对照组):在没有任何日志与架构约束的环境下让 Agent 完成修复,观察它要花多久才能抵达根本原因;
  2. 第二遍(实验组):在配有结构化日志、架构边界文档与检查脚本的环境下让同一 Agent 完成修复,对比 logs 与 boundary checks 是否让修复更快、影响范围更小。

这种"A/B 对照"正是 Harness 工程的核心方法论:不靠口号判断工具链价值,而是用可重复的实验数据说话。合格的解法必须有诊断证据(日志输出、检查脚本通过记录),而不只是口头声称"修好了"。

动手前需要准备的环境(原文档"ツール"一节):

工具用途
Claude Code 或 Codex作为被测的编码 Agent
Git记录变更、回滚实验、对比两遍结果
Node.js + Electron运行 Electron 知识库应用(依赖见 solution/package.json,含 react 18、electron 33、vite 6、vitest 2)

starter 与 solution:一个"弱信号"起点 vs 一个"护栏完备"的参考实现

仓库中项目位于 projects/project-04/,包含两个可运行的完整切片:

目录包含什么实验要对比的量
starter/基于 Project 03 的代码,诊断信号弱;IndexingService中被植入了 indexing 缺陷,超过 1000 字符的大文件 chunking 会被破坏;没有架构检查脚本在没有运行时信号的情况下,Agent 抵达根本原因所需的时间
solution/结构化 logger、架构边界文档与检查脚本、已修复的 chunking 逻辑、clean-state-checklist.mdlogs 与 boundary checks 是否让修复更快、影响范围更小

两者的差异可以精确对应到具体文件(对照 README-CN.md 的任务对应表):

功能 / 产物starter 状态solution 证据
结构化日志无共享 logger 服务,仅零散console.logsrc/services/logger.ts 及 main、ipc-handlers、各 services 中的日志调用
导入/索引诊断运行输出难以定位失败导入、索引开始/完成、Q&A 失败路径的结构化日志
架构边界无脚本检查 renderer/main/service 越界scripts/check-architecture.sh、docs/ARCHITECTURE.md、AGENTS 边界规则
植入的 chunking bug大文件可能产生空 chunk修复后的 src/services/indexing-service.ts
干净交接无最终检查清单clean-state-checklist.md

提示:solution 刻意保持比后续项目更精简的 Harness。按 AGENTS.md 的说明,本项目不包含feature_list.jsonclaude-progress.mdinit.shsession-handoff.md——那些产物在更后面的项目阶段才引入。做本项目时不要臆造这些文件存在。

运行时可观测性落地:解剖结构化 Logger

原文档点名要重点研读 projects/project-04/solution/src/services/logger.ts。它是本项目"运行时反馈"的基石,把原来不可解析的散乱输出统一为带时间戳、分级、可 JSON 解析的日志条目

核心设计如下(对照源码逐段解读):

  • 四个日志级别DEBUGINFOWARNERRORLogLevel枚举,源码 L9-L14),并按LEVEL_ORDER定义优先级顺序(L26-L31);
  • 日志条目结构LogEntry,L16-L22):timestamp(ISO 8601)、levelservice(服务名)、message、可选data(任意结构化字段Record<string, unknown>);
  • 级别过滤shouldLog()用级别序号比较,低于最小级别(默认DEBUG)的日志直接丢弃,避免噪音淹没信号(L37-L41);
  • 按级别分流输出emit()ERRORconsole.errorWARNconsole.warn、其余走console.log,且全部输出 JSON 字符串(L43-L56),保证 stdout/stderr 都能被工具链(如grepjq)继续消费;
  • 服务级子 loggerforService(name)返回ServiceLogger,让每个服务只传消息与数据、自动带上服务名(L91-L121),例如IndexingService内部持有logger.forService('IndexingService')
  • 可配置最小级别:单例logger从环境变量LOG_LEVEL读取最小级别,缺省DEBUG(L123-L126),意味着可以用LOG_LEVEL=ERROR npm run dev一键压噪,或LOG_LEVEL=DEBUG全量观测。
// 实际调用形态(摘自 solution 各服务) this.log.info('Indexing document', { docId: doc.id, title: doc.title, contentLength: content.length }); this.log.error('Document content not found', { documentId });

这种"每条日志都自带 service + 结构化 data"的做法,直接服务于 AGENTS.md 的调试指引:排查时检查服务初始化事件、IPC 调用及其参数、索引 chunk 数与内容长度、Q&A 置信度与 citation 数量。

架构约束:四层边界与 check-architecture.sh 源码拆解

ARCHITECTURE.md 定义的四层模型

docs/ARCHITECTURE.md 把 Electron 应用明确划分为四层(自顶向下):

Renderer (React UI) → Preload (contextBridge) → Main Process (IPC Handlers) → Services (Business Logic) → Persistence (Filesystem)

每层职责与红线(不可越界):

职责约束(MUST NOT)
Renderer(src/renderer/)渲染 React 组件、处理用户输入、通过window.knowledgeBaseAPI 与主进程通信不得 importfs/path/os/child_process等 Node 核心模块;不得直接访问 Electron API;不得承载业务逻辑
Preload(src/preload/)contextBridge.exposeInMainWorld暴露类型化 API;映射 IPC 通道名不得包含业务逻辑;不得直接 import services;只用ipcRenderer.invoke
Main Process(src/main/)创建管理 BrowserWindow、注册 IPC handler 并委托给 services不得承载路由之外的业务逻辑;不直接访问持久化层
Services(src/services/)实现全部业务逻辑(文档管理、索引、Q&A)不得 import Electron API(ipcMainBrowserWindow等);不得 import React;所有文件系统访问必须经PersistenceService

所有 IPC 通信的通道名收敛在 src/shared/types.ts 的IPC_CHANNELS常量(单一事实来源),例如documents:listdocuments:importindexing:startindexing:chunksqa:ask等。数据流方向固定:Renderer 调用window.knowledgeBase.*→ Preload 转成ipcRenderer.invoke(channel, ...)→ Main 的 IPC handler 委托给 service → service 经PersistenceService落盘 → 结果沿 IPC 返回渲染层。

check-architecture.sh 的三种越界检查

scripts/check-architecture.sh 用纯 bash 实现了架构护栏(set -euo pipefail保证失败即退出,退出码 0 = 全部通过,1 = 存在违规)。它做三件事:

  1. 检查 renderer 是否 import Node 核心模块:遍历src/renderer下所有.ts/.tsx,用grep -qE "import.*\b(fs|path|os|child_process)\b"命中即记为违规(L21-L33);
  2. 检查 services 是否触碰 Electron:遍历src/services.ts,既查import ... from 'electron',也查ipcMainipcRendererBrowserWindow关键字(L36-L56);
  3. 检查 services/main 是否 import React:遍历src/services src/main下的.ts,命中import ... from 'react'即违规(L59-L74)。

脚本末尾汇总违规数并按结果exit 0/1(L78-L85),因此它可以无缝挂进 CI 或 Agent 的 pre-commit 流程。这正好把 ARCHITECTURE.md 文档化的规则变成了可执行、可证伪的检查——Agent 想偷偷越界,脚本会当场拦下。

bash scripts/check-architecture.sh # 期望输出:PASS: All architecture boundary checks passed(退出码 0)

植入缺陷分析:1000 字符魔咒与增量索引的修复对比

starter 中被植入的 bug

对比 starter/src/services/indexing-service.ts 与 solution 版本,缺陷藏得很"自然":chunk 生成逻辑里悄悄加了一个条件——当content.length > 1000时,把 chunk 内容置为空字符串:

// starter 版本中的缺陷(L106-L119) const chunkContent = content.length > 1000 ? '' : buffer.trim();

后果是:任何超过 1000 字符的文件,索引后产出的 chunk 全是空内容。空 chunk 会沿检索链向下传导——Q&A 的关键词匹配拿不到任何命中、citation excerpt 为空、答案置信度骤降。而 starter 只有零散的console.log,日志中"chunkDocument produced N chunks"看起来一切正常,Agent 若无运行时信号,极难定位到问题出在"大文件的分块内容被清空"。

solution 的修复与增量索引机制

solution/src/services/indexing-service.ts 的修复版删除了那个三元条件,chunk 内容始终取真实文本:

  • 分块策略CHUNK_SIZE = 500(约 500 字符),先按/\n\s*\n/(空行)拆成段落、过滤空白段落,再贪心合并段落直至接近 500 字符边界(L110-L146);
  • 每个 chunk 携带元数据createChunk记录charCountwordCount(L148-L159),日志里也输出totalChars总和用于校验分块完整性(L139-L143);
  • 增量索引startIndexing()支持按单个documentId增量索引;批量模式读取documents-meta.jsonindex-meta.json只处理尚未记录在chunksMeta中的文档if (chunksMeta[doc.id]) continue;,L53-L54),索引结果写入chunks/<docId>.json并把 chunk id 列表登记回index-meta.json——这样重复运行不会重复消费已索引文档,长任务天然可断点续跑;
  • 状态机getStatus()依据"已索引数 == 文档总数"推导idle | indexing | ready | error四种状态,供 UI 与日志同步展示(L75-L89)。

修复版还在每个关键节点埋了结构化日志:批量开始(记录 totalDocs / alreadyIndexed)、单文档索引(记录 contentLength / chunkCount)、内容缺失跳过(WARN 级),让 Agent 从日志就能判断"这个文档到底有没有被正确分块"。

Q&A 侧的可观测锚点

qa-service.ts 是缺陷的"受害方",也提供了诊断信号:检索时按查询词与 chunk 内容的包含关系打分、取 Top 2 作为 citation(L66-L98);每次回答都记录confidencecitationCountanswerLength到结构化日志(L111-L115)。当答案置信度低、citation 数量为 0 时,日志会直接指向"索引层产出异常"——这正是"运行时反馈驱动定位"的完整演示链路。

完整复现步骤:把两遍实验跑起来

按照 README-CN.md 与 AGENTS.md 的启动工作流:

# 第一遍:弱信号环境 cd starter npm install npm run dev # 观察:Agent 能否仅凭 console.log 定位 chunking bug # 导入一个大文件(>1000 字符),观察分块结果异常(空 chunk) # 第二遍:带护栏环境 cd ../solution npm install npm run dev # 对比:结构化日志如何加速诊断

每个会话的标准验证命令(solution 内):

npm run check # tsc 双 tsconfig 类型检查 npm run build # tsc -p tsconfig.node.json && vite build bash scripts/check-architecture.sh # 架构边界护栏,必须 PASS npm run dev # 启动应用,确认结构化日志出现在控制台

复现要求:修复前后都用长文档各复现一次,保留日志证据(导入事件、索引 chunk 数、Q&A 置信度),证明"诊断证据存在"而不只是声称修复通过。

干净的交接:clean-state-checklist 与 AGENTS 工作流

本项目强调"Session 结束时仓库必须可重启、可交接"。清单 clean-state-checklist.md 按五组核对:

  • 构建npm run check无类型错误、npm run build成功;
  • 架构bash scripts/check-architecture.sh零违规、renderer 无 Node 核心模块 import、services 无 Electron IPC、services/main 无 React import;
  • 运行时:应用无错启动、启动时出现结构化日志、文档导入正常(日志含 IMPORT_DOCUMENT 事件)、各种大小的文档索引正常、Q&A 返回带 citation 的答案(日志含 ASK_QUESTION 事件);
  • 数据完整性:已索引文档无空 chunk(用 GET_CHUNKS 验证)、Q&A 历史跨重启持久化、文档元数据与实际文件一致;
  • 仓库:git status 无意外文件、无敏感数据入库、最终总结记录当前状态/验证记录/未决风险、AGENTS.md / ARCHITECTURE.md / 本清单与实际文件保持一致。

配套的 AGENTS.md 还定义了长期运行 Agent 的操作纪律:动手前先跑启动工作流(pwd→ 读 ARCHITECTURE.md →git log --oneline -5npm installnpm run check→ 架构检查),一次只做一个功能,不静默改动验证规则,优先把结论沉淀为仓库内持久产物而非聊天摘要。其Definition of Done明确要求"目标行为已实现 + 验证确实运行过 + 证据已记录 + 仓库可从标准路径重启 + 架构检查通过",这正是避免 Agent 过早宣布胜利(对应 講義 09 的主题)的工程化落地。

关键文件速查

关注点路径
项目说明与 A/B 任务projects/project-04/README-CN.md
结构化日志实现projects/project-04/solution/src/services/logger.ts
架构边界文档projects/project-04/solution/docs/ARCHITECTURE.md
架构检查脚本projects/project-04/solution/scripts/check-architecture.sh
已修复的增量索引projects/project-04/solution/src/services/indexing-service.ts
植入缺陷的对照组projects/project-04/starter/src/services/indexing-service.ts
检索与 citation 日志锚点projects/project-04/solution/src/services/qa-service.ts
IPC 通道与共享类型projects/project-04/solution/src/shared/types.ts
会话交接清单projects/project-04/solution/clean-state-checklist.md
Agent 运行纪律projects/project-04/solution/AGENTS.md

关联讲义:講義 07:エージェントのタスク境界を明確に引く · 講義 08:機能リストでエージェントの作業を制約する。

小结:Project 04 用一次可重复的 A/B 实验,证明了 Harness 的核心主张——Agent 的修复速度与准确度,取决于它能否看见运行时真相、能否被边界约束住。结构化日志提供"反馈",架构脚本提供"约束",增量索引提供"可控的推进粒度";三者合一,才是让长运行 Agent 从"猜测式编码"走向"证据驱动修复"的最小闭环。

【免费下载链接】learn-harness-engineering

Harness engineering beginner tutorial, from 0 to 1

项目地址:https://gitcode.com/gh_mirrors/le/learn-harness-engineering
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询