# 知识反馈时机设计文档

## 文档维护规范

0. **先改文档，再动代码** - 新功能或重大修改需先完成文档更新、并完成审阅后，再进行代码实现；除非改动较小、不被文档涵盖
1. **文档分层，链接代码** - 重要或复杂设计可以另有详细文档；关键实现需标注代码文件路径；格式：`module/file.py:function_name`
2. **简洁快照，日志分离** - 只记录最重要的、与代码准确对应的或者明确的已完成的设计的信息，避免推测、建议、决策历史、修改日志、大量代码；决策依据或修改日志若有必要，可在 `knowhub/docs/decisions.md` 另行记录

---

## 背景

### 现有反馈机制的缺陷

当前的知识反馈存在以下问题（来自 `feedback-optimization-proposal.md`）：

- **反馈时机不明确**：没有明确定义何时、由谁来评估知识的有效性
- **缺少使用状态追踪**：知识被注入后，无法知道它是否真的被用到了
- **评估粒度粗糙**：只有 helpful/harmful 计数，缺少"为什么有用/无用"的上下文

### 设计目标

1. 记录每条知识的完整生命周期（注入 → 使用 → 评估）
2. 在自然的执行节点（Goal 完成、压缩、任务结束）触发评估，不打断主流程
3. 为后续上报 KnowHub 提供结构化的评估数据

---

## 核心概念

### Knowledge Log（知识注入日志）

每个 trace 维护一个 `knowledge_log.json`，记录该 trace 中所有被注入的知识及其评估状态。

**位置**：`.trace/{trace_id}/knowledge_log.json`

**数据结构**：

```json
{
  "trace_id": "trace-xxx",
  "entries": [
    {
      "knowledge_id": "knowledge-20260305-a1b2",
      "goal_id": "1",
      "injected_at_sequence": 42,
      "injected_at": "2026-03-20T10:00:00.000000",
      "task": "知识的原始task描述",
      "content": "知识内容摘要（截断至500字符）",
      "eval_result": {
        "eval_status": "helpful",
        "reason": "评估理由"
      },
      "evaluated_at": "2026-03-20T10:05:00.000000",
      "evaluated_at_trigger": "goal_completion"
    }
  ]
}
```

**字段说明**：

| 字段 | 类型 | 说明 |
|---|---|---|
| `knowledge_id` | string | KnowHub 中的知识 ID |
| `goal_id` | string | 注入时的 Goal ID（如 `"1"`, `"2.1"`） |
| `injected_at_sequence` | int | 注入时的消息序列号 |
| `injected_at` | datetime | 注入时间（ISO 格式，含毫秒） |
| `task` | string | 知识的原始 task 描述 |
| `content` | string | 知识内容（写入时截断至 500 字符） |
| `eval_result` | object/null | 评估结果对象；未评估时为 `null` |
| `evaluated_at` | datetime/null | 评估时间；未评估时为 `null` |
| `evaluated_at_trigger` | string/null | 触发评估的事件（见下表）；未评估时为 `null` |

**`evaluated_at_trigger` 可能的值**：

| 值 | 含义 |
|---|---|
| `"goal_completion"` | 由 Goal 完成（`completed` 或 `abandoned`）触发 |
| `"compression"` | 由上下文压缩触发（压缩前必须先评估） |
| `"task_completion"` | 由任务自然结束触发（主路径无工具调用退出时兜底） |

> 注意：同一个 `knowledge_id` 可能在不同 Goal 中被多次注入，每次产生独立 entry。评估时优先更新最近注入（`injected_at_sequence` 最大）的未评估条目。

---

## 评估触发机制

### 触发点 1：Goal 完成

**时机**：Goal status 变为 `completed` 或 `abandoned`

**触发逻辑**（`agent/trace/store.py:update_goal`）：

```
Goal 完成
  ↓
查询 knowledge_log 中 eval_result == null 的条目
  ↓
如果有待评估条目
  → 在 trace.context 中设置标志：
      pending_knowledge_eval = true
      knowledge_eval_trigger = "goal_completion"
  ↓
Runner 主循环下一次迭代开头检测到标志（agent/core/runner.py:_agent_loop）
  → 清除标志
  → 将 "knowledge_eval" 加入 force_side_branch 队列
```

### 触发点 2：压缩（Compression）

**时机**：上下文 token 数超过阈值，即将执行压缩

**触发逻辑**（`agent/core/runner.py:_manage_context_usage`）：

```
压缩条件触发
  ↓
查询 knowledge_log 中 eval_result == null 的条目
  ↓
如果有待评估条目
  → 在 trace.context 中设置：
      knowledge_eval_trigger = "compression"
  → 将侧分支队列设为：
      ["reflection", "knowledge_eval", "compression"]（启用知识提取时）
      ["knowledge_eval", "compression"]（未启用知识提取时）
  → 返回"需要进入侧分支"信号，暂缓压缩
  ↓
依次执行侧分支队列后再压缩
```

**原因**：压缩会删除消息历史，必须在压缩前完成评估，否则执行上下文永久丢失。

### 触发点 3：任务结束（兜底）

**时机**：主路径出现无工具调用的回复，Agent 即将结束任务

**触发逻辑**（`agent/core/runner.py:_agent_loop`，无工具调用分支）：

```
主路径无工具调用（任务即将结束）
  ↓
查询 knowledge_log 中 eval_result == null 的条目
  ↓
如果有待评估条目
  → 在 trace.context 中设置：
      knowledge_eval_trigger = "task_completion"
  → 将 ["knowledge_eval"] 加入 force_side_branch 队列
  → continue（不 break，下一轮执行评估侧分支）
  ↓
评估完成后再退出
```

---

## 评估分类（eval_status）

| 状态 | 含义 |
|---|---|
| `irrelevant` | 知识的 task 与当前任务无关 |
| `unused` | 知识与任务相关，但执行过程中没有被使用 |
| `helpful` | 知识对当前任务有实质帮助 |
| `harmful` | 知识对当前任务产生了负面作用 |
| `neutral` | 知识与任务相关但无明显影响 |

---

## 侧分支评估流程

### 侧分支类型

复用现有 `SideBranchContext` 机制，新增 `"knowledge_eval"` 类型（`agent/trace/models.py:Message.branch_type`）：

```python
SideBranchContext(
    type="knowledge_eval",
    branch_id=f"knowledge_eval_{uuid.uuid4().hex[:8]}",  # 如 "knowledge_eval_1c5fffaf"
    max_turns=config.side_branch_max_turns               # 默认 5
)
```

`trigger_event` 记录在 `trace.context["active_side_branch"]["trigger_event"]` 中，侧分支退出后写入 `evaluated_at_trigger`。

### 评估 Prompt 结构

完整实现见 `agent/core/runner.py:_build_knowledge_eval_prompt`，结构如下：

```
你是知识评估助手。请评估以下知识在本次任务执行中的实际效果。

## 当前任务（Mission）       ← trace.task
## 当前 Goal                ← goal_tree.current 的 description
## 待评估知识列表            ← 所有 eval_result == null 的条目
  - knowledge_id / task / content / injected_at_sequence / goal_id
## 评估维度                  ← helpfulness + relevance
## 评估分类                  ← 5 个 eval_status 选项
## 输出格式                  ← JSON
```

> Prompt 中**不包含消息历史**。LLM 依据对话上下文中已有的执行过程作出判断。

### 评估输出格式

LLM 直接输出 JSON，**无需调用工具**：

```json
{
  "evaluations": [
    {
      "knowledge_id": "knowledge-20260305-a1b2",
      "eval_status": "helpful",
      "reason": "1-2句评估理由"
    }
  ]
}
```

### 即时写入机制（`agent/core/runner.py:_agent_loop`）

每次 LLM 回复后立即尝试解析，三种策略依次降级：整体解析 → ` ```json ` 代码块 → 正则裸对象。

```
LLM 输出评估 JSON
  ↓
解析成功 → 立即调用 store.update_knowledge_evaluation() 写入每条评估结果
  ↓
侧分支达到退出条件（无工具调用 或 超过 max_turns）→ 恢复主路径
```

解析失败时记录日志，不中断主流程。

---

## 数据流

```
知识注入（agent/trace/goal_tool.py:inject_knowledge_for_goal）
  ↓
写入 knowledge_log.json（eval_result=null）
  ↓
  ┌─────────────────────────────────────────────┐
  │  触发点 A：Goal 完成（goal_completion）       │
  │  触发点 B：压缩执行前（compression）          │
  │  触发点 C：任务自然结束（task_completion）    │
  └─────────────────────────────────────────────┘
  ↓
Runner 进入 knowledge_eval 侧分支
  ↓
LLM 直接输出 JSON 评估结果（无工具调用）
  ↓
Runner 每轮即时解析并写入 knowledge_log.json
  ↓
侧分支退出 → 恢复主路径
```

---

## 与现有系统的集成点

| 集成位置 | 文件 | 说明 |
|---|---|---|
| 知识注入时写 log | `agent/trace/goal_tool.py:inject_knowledge_for_goal` | `goal(focus=...)` 触发知识搜索后写入 `knowledge_log.json` |
| Goal 完成时设置标志 | `agent/trace/store.py:update_goal` | 设置 `trace.context["pending_knowledge_eval"]` 标志 |
| 主循环检测 Goal 完成标志 | `agent/core/runner.py:_agent_loop` | 每轮迭代开头检测标志，触发 `["knowledge_eval"]` 侧分支 |
| 压缩前触发评估 | `agent/core/runner.py:_manage_context_usage` | 压缩前检查 pending，先评估再压缩 |
| 任务结束兜底 | `agent/core/runner.py:_agent_loop` | 任务退出前检查 pending，强制触发评估 |
| 侧分支类型扩展 | `agent/trace/models.py:Message.branch_type` | Literal 中包含 `"knowledge_eval"` |
| 即时写入评估结果 | `agent/core/runner.py:_agent_loop` | 存储 assistant 消息后即时解析 JSON 并写入 |
| Log 文件管理 | `agent/trace/store.py` | `append_knowledge_entry` / `update_knowledge_evaluation` / `get_pending_knowledge_entries` |
