Files
SuperBizAgent-java/mvp/engineering/harness/Harness 整体架构学习笔记-从装配到入口到记忆到知识库写入.md
zhuyongxin 83193bdf4a docs(harness): add overall architecture learning note
- Add architecture note: config assembly (three-layer organization),
  HTTP entry (thin controller + SSE state machine + disconnect cancel),
  session & memory system (PreviousTurn injection, terminology calibration,
  skill as procedural long-term memory), knowledge base write pipeline
2026-08-10 18:35:23 +08:00

11 KiB
Raw Permalink Blame History

Harness 整体架构学习笔记:从装配到入口到记忆到知识库写入

更新日期:2026-08-04 主题:整体架构五块补充(了解层面)——配置装配中心 / HTTP 入口层 / 会话系统与记忆体系 / 知识库写入链路 配套:九域主线笔记(core/retry/progress/tool/guard/release/agent/audit/contract 全 ✅)

1. 配置装配中心(整体怎么搭起来)

1.1 装配全景(Bean 拓扑)

flowchart LR
    C["ChatHarnessProperties<br/>配置集中(yml)"] --> K["DiagnosisHarnessCore<br/>总闸门:超时/预算/重试/收敛"]
    K --> B["ToolBoundary<br/>工具底座:canonical/门禁/投影"]
    B --> A1["RagToolAdapter"]
    B --> A2["QueryLogsToolAdapter"]
    B --> A3["MysqlToolAdapter<br/>(可选装配)"]
    A1 --> E["HarnessEvidenceTools<br/>组装注册"]
    A2 --> E
    A3 --> E
    K --> G["GuardModelCall<br/>(守卫/修复/路由共用底座)"]
    G --> SG["SemanticGuard"]
    G --> ER["EvidenceRepair"]
    G --> R["IntentRouter"]
    E --> F["DiagnosisAgentFactory"]
    F --> UC["DiagnosisAgentUseCase"]
    UC --> D["DiagnosisChatExecutor"]
    R --> D
    R --> S["SystemChatExecutor"]
    R --> KQ["KnowledgeQueryExecutor"]
    D --> A["ChatApplicationUseCase<br/>应用入口"]
    S --> A
    KQ --> A

1.2 三层组织(话术版)

① 总闸门(core):能花多少钱/跑多久/怎么重试/何时停——配置集中,改一处全局生效
② 工具底座:所有工具统一留痕(canonical)/拦截(ToolBoundary)/裁剪(投影)——行为整齐划一
③ 具体工具:RAG/Logs/MySQL 按需装配(没配数据源不装死工具)→ 组装注册给 Agent

串联:先判意图(路由)→ 走对应分支 → 全程在总闸门管辖下
一句话:边界集中、执行统一、工具可插拔

1.3 关键设计点

设计 为什么
单一装配入口(HarnessChatConfiguration) 读 Bean 签名 = 读架构拓扑
一切围绕 core 所有链路共享同一套门禁(超时/预算/重试/收敛)
配置属性集中(@EnableConfigurationProperties) 一处改全局生效,不会有的环节漏管
工具可选装配(mysqlEnabled 判断) 没配置不装死工具;ObjectProvider 可选后端
守卫/修复/路由共用 GuardModelCall LLM judge 模式:同一轻量模型底座
Redis 存 canonical 跨实例共享 + TTL 过期
线程池 AbortPolicy 队列满直接拒绝(fail fast)

2. HTTP 入口层(薄协议适配)

2.1 请求流时序

POST /api/chat {Id, Question}
  → 校验 → new SseEmitter + ChatSseSession(= ChatApplicationObserver)
  → chatWorkerExecutor.execute(...)   ← 异步:HTTP 线程不跑模型
  → 立即返回 200 + TEXT_EVENT_STREAM
  → worker 线程执行编排,经 session 推事件
  → 队列满 → 503(RejectedExecutionException)

2.2 SSE 状态机 + 五类事件

状态机:NEW → OPEN → TERMINAL(收尾)/ DISCONNECTED(断连)
  每个方法 requireState 校验顺序——防乱序推送

事件协议:
  metadata → {session_id, run_id}(首推)
  status   → 编排进度(ROUTING / DIAGNOSIS_RUNNING / SAFETY_VALIDATING…)
  content  → 最终内容(content_type + payload)
  failure  → 失败码 + 消息
  done     → 终态(SUCCESS/FALLBACK/FAILED)★ CANCELLED 对外不可见

2.3 断连取消链路(贯穿到 Harness)

客户端断开 → emitter.onTimeout/onError/onCompletion → session.disconnect()
  → 状态 DISCONNECTED → runControl.cancelClientDisconnect()
  → Harness 取消机制接管(checkActive / 拦截器 / 线程池 cancel)
onStarted 时若已断连:直接取消——不白跑

2.4 失败两层出口

SSE 通道:ChatApplicationException → session.fail(failure + done(FAILED))
         其他 RuntimeException → INTERNAL_FAILURE 通用信息(不泄露细节)
REST 通道:GlobalExceptionHandler → 404(SessionNotFound)/ 400(参数/文档/文件超限)/ 500(兜底)

→ 编排异常走 SSE failure,REST 异常走 HTTP 状态码——都不暴露内部细节

3. 会话系统与记忆体系(术语精确校准)

3.1 会话存储:不存历史,存「可重放的发布结果」

ChatSession(chat_session 表):只存元数据(status/messagePairCount/时间戳)
  ——「message history is not persisted here」
DiagnosisSession:诊断快照(query/answer/selfEvaluation/feedback)
真正的历史:DiagnosisRun(每次运行一行)+ PublishedResult(JSON 落库)

3.2 PreviousTurn 注入链路(短期记忆)

ChatApplicationUseCase 开头读 findPreviousTurn(sessionId)
  → 查最近 SUCCESS+DIAGNOSIS+publishedResult 非空的 Run
  → 反序列化 PublishedResult → PublishedResultPolicy 生成【有界】摘要
    (limitations 10 条×500 字 / 源文档 10 个 / 字段限长——有界在生成时)
  → 传 executePath → DiagnosisChatExecutor
  → new DiagnosisAgentInput(query, previous_turn)
  → 序列化成输入 JSON → agent.call(inputJson) → 模型从输入读到

设计三决策(话术版):
  诊断短流程 → 只取上一轮(更早记忆靠多轮逐层传递)
  非 SUCCESS 误导 → SUCCESS 才准入(且非 SUCCESS 轮次根本没写 PublishedResult)
  token 爆炸 → 有界摘要(PreviousTurnLimits)
补充:可回放——模型看有界摘要,审计看全量 JSON(两层分离)

3.3 记忆术语校准(重要认知)

判定标准:记忆 = 会被【注入 prompt/上下文】的东西(不是存了什么)

  PreviousTurn → 注入输入 JSON(user message)→ ✅ 短期记忆(当前会话)
  lookup_knowledge → 工具调用动态获取(tool result)→ ❌ 记忆,是检索增强(RAG)
  知识库 → 检索源,规模太大无法全量注入 → 归检索侧(工具检索是正确形态)
  案例库 → 有结构有写入、缺检索注入 → 长期记忆的【候选原料】
  运行档案 → 元数据层永不注入 → 审计数据

项目真实情况:只有短期记忆(PreviousTurn)+ 检索增强(RAG),【没有】长期记忆层

3.4 长期记忆设计路径(如果要做)

筛选标准:规模可控 + 跨会话价值 + 可注入形态

案例库最符合:root_cause+solution 结构化摘要、注入 top 2-3 条、相似故障复用解法
PreviousTurn 扩展:最近 N 轮结论摘要(短期 → 中期记忆)
Feedback 偏好:用户偏好摘要
知识库不符合:全量太大 → 保持工具检索

案例 → skill 提炼(项目已实现):
  6 个 SKILL.md(diagnose-mysql-connection-pool 等)
  结构:Workflow / Required Evidence / Stop Conditions / Report Rules / Eval Anchor
  注入:ClasspathSkillRegistry → SystemPromptTemplate → system prompt(程序性长期记忆)
  情景记忆(案例)→ 程序记忆(skill)→ 常驻注入 ✅ 长期记忆的正确形态
现有缺口:单技能激活(只放行 1 个)/ 静态加载(无按 query 自动匹配)/ skill 与案例库断开

4. 知识库写入链路(RAG 写半边)

上传(/api/documents/upload)
  → TextExtractorService(文本提取)
  → DocumentChunkService.chunkDocument(分块)★
  → VectorEmbeddingService(dense embedding)
  → VectorIndexService.indexDocumentChunks(写 Milvus)★
  → 检索侧(lookup_knowledge)读同一份索引

关键设计

① 分块:按章节分块(非定长硬切)+ 相邻 chunk 保留 overlap(减轻边界断裂)
        双条件限制:maxSize(字符)+ maxTokens(token)
② hybrid 写入:dense(应用侧 embedding → vector 字段)
               + BM25(buildSearchText → search_text 字段)
   ★ 关键:dense embedding 输入 与 BM25 search_text 【同源】——
     同一个「增强文本」既喂 embedding 又写 BM25 字段
     → 两路召回看到完全一致的文档内容,混合检索才公平
③ 弃用:legacy MilvusServiceClient(旧 SDK)/ Spring AI VectorStore#add(无 hybrid schema)
   唯一后端:MilvusHybridKnowledgeStore(Milvus SDK v2)

5. 易错点

易错 正确
Controller 做业务编排 薄适配层:校验+开 SSE+异步+写回,编排在 Application
SSE 顺序不重要 requireState 状态机校验——防乱序推送
取消对外可见 Done 拒绝 CANCELLED——客户端只看到 SUCCESS/FALLBACK/FAILED
知识库 = 长期记忆 是检索源(工具动态获取);记忆 = prompt 注入——项目无长期记忆层
案例 = 长期记忆 是候选原料——缺检索注入;skill 才是程序性长期记忆(已实现)
工具预算在工具层 maxToolCalls/收敛参数都在 core(总闸门)
分块定长硬切 按章节 + overlap + 双条件限制

6. 面试话术(30 秒)

6.1 整体架构怎么组织

"整个系统从下往上三层:最底层一套全局规则(超时/预算/重试/收敛,配置集中改一处全局生效);中间一层工具共用的底座(调用留痕、统一拦截、结果裁剪);上层按需装配具体工具(知识库/日志/数据库,配了才装)。最后串成应用入口——先判意图再走分支,全程在总闸门管辖下。一句话:边界集中、执行统一、工具可插拔。"

6.2 记忆体系

"记忆的判定标准是会不会被注入上下文:PreviousTurn 是短期记忆(注入输入 JSON,上一轮 SUCCESS 的有界摘要);知识库是检索增强不是记忆(工具动态获取);项目没有长期记忆层——skill(案例提炼的诊断方法)注入 system prompt 是程序性长期记忆的正确形态;案例库是候选原料,缺检索注入。"

7. 代码位置索引

块 文件
装配中心 config/HarnessChatConfiguration.java(+ config/ChatHarnessProperties.java)
HTTP 入口 controller/ChatController.java + controller/sse/ChatSseSession.java / ChatSseEvent.java / SseEmitterChatSink.java
异常映射 exception/GlobalExceptionHandler.java
会话存储 harness/application/persistence/JpaChatRunStore.java / PreviousTurnLimits.java / PublishedResultPolicy.java
记忆注入 harness/application/ChatApplicationUseCase.java + harness/application/executor/DiagnosisChatExecutor.java + harness/agent/DiagnosisAgentUseCase.java
skill 机制 config/SkillConfig.java + src/main/resources/skills/*/SKILL.md
知识库写入 service/DocumentChunkService.java / VectorIndexService.java / VectorEmbeddingService.java / KnowledgeBaseInitService.java + controller/DocumentController.java / KnowledgeBaseController.java