Add global knowledge base with RAG search

- KB module: fastembed (AllMiniLML6V2) for CPU embedding, SQLite for
  vector storage with brute-force cosine similarity search
- Chunking by ## headings, embeddings stored as BLOB in kb_chunks table
- API: GET/PUT /api/kb for full-text read/write with auto re-indexing
- Agent tools: kb_search (top-5 semantic search) and kb_read (full text)
  available in both planning and execution phases
- Frontend: Settings menu in sidebar footer, KB editor as independent
  view with markdown textarea and save button
- Also: extract shared db_err/ApiResult to api/mod.rs, add context
  management design doc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 08:15:50 +00:00
parent 1aa81896b5
commit d9d3bc340c
19 changed files with 2283 additions and 53 deletions

54
doc/context.md Normal file
View File

@@ -0,0 +1,54 @@
# Context 管理现状与设计
## 现状
当前没有做 context 长度限制,存在超过 model token limit 的风险。
### 已有的缓解机制
1. **Phase transition 时 clear**`step_messages` 在 planning→executing 和 step→step 切换时会 `clear()`,避免跨阶段累积
2. **单条 tool output 截断**bash 输出限制 8000 bytesread_file 超长时也截断
3. **Step context 摘要**:已完成步骤只保留 summary`step_summaries`),不带完整输出
### 风险场景
- 一个 execution step 内 tool call 轮次过多(反复 bash、read_file`step_messages` 无限增长
- 每轮 LLM 的 assistant message + tool result 都 push 进 `step_messages`,没有上限
- 最终整个 messages 数组超过模型 context window
## 方案设计
### 策略:滑动窗口 + 早期消息摘要
`step_messages` 长度超过阈值时,保留最近 N 轮完整对话,早期的 tool call/result 对折叠为一条摘要消息。
```
[system prompt]
[user: step context]
[summary of early tool interactions] ← 压缩后的历史
[recent assistant + tool messages] ← 完整保留最近 N 轮
```
### 具体实现
1. **Token 估算**用字符数粗估1 token ≈ 3-4 chars 中英混合),不需要精确 tokenizer
2. **阈值**:可配置,默认如 80000 chars约 20k-25k tokens给 system prompt 和 response 留余量
3. **压缩触发**:每次构建 messages 时检查总长度,超过阈值则压缩
4. **压缩方式**
- 简单版:直接丢弃早期 tool call/result 对,替换为 `[已执行 N 次工具调用,最近结果见下文]`
- 进阶版:用 LLM 生成摘要(额外一次 API 调用,但质量更好)
5. **不压缩的部分**system prompt、user context、最近 2-3 轮完整交互
### 实现位置
`run_agent_loop` 中构建 messages 之后、调用 LLM 之前,插入压缩逻辑:
```rust
// agent.rs run_agent_loop 内,约 L706-L725
let (mut messages, tools) = match &state.phase { ... };
// 压缩 context
compact_messages(&mut messages, MAX_CONTEXT_CHARS);
```
`compact_messages` 函数:从前往后扫描,保留 system/user 头部,计算总长度,超限时将早期 assistant+tool 消息替换为摘要。

42
doc/kb.md Normal file
View File

@@ -0,0 +1,42 @@
# 知识库 (KB / RAG)
## 概述
全局知识库,所有 project 的 agent 共享。用户在前端用 markdown 编辑保存时自动切块并索引。Agent 通过 `kb_search``kb_read` 工具查询。
## 数据流
```
用户编辑 markdown textarea
→ PUT /api/kb
→ 原文存 SQLite (kb_content 表,单行)
→ 按 ## heading 切块
→ fastembed (AllMiniLML6V2) 生成 embedding
→ chunk + embedding 存 SQLite (kb_chunks 表)
```
## 切块策略
按 markdown `##` heading 切分,每个 section 作为一个 chunk。无 heading 的开头部分作为一个 chunk。
## Agent 工具
- `kb_search(query: str)` → 向量搜索 top-5返回相关片段
- `kb_read()` → 返回 KB 全文
## API
- `GET /api/kb` → 返回 KB 全文 `{ content: string }`
- `PUT /api/kb` → 保存全文 + 重新切块索引 `{ content: string }`
## 技术选型
- **向量存储**: SQLite (embedding 存为 BLOB暴力余弦搜索)
- **Embedding**: fastembed-rs (AllMiniLML6V2, 384 dim, CPU)
- **原文存储**: SQLite (kb_content 表)
## 前端
- Sidebar 底部 Settings 弹出菜单 → Knowledge Base
- 点击切换到 KB 编辑独立 view与 project view 平级)
- 大 textarea + Save 按钮

View File

@@ -9,10 +9,3 @@ template
---
时间观察app
---
## 代码啰嗦/可精简
- **agent.rs**`NewRequirement``Comment` 分支里「设 final_status → 更新 DB status → broadcast WorkflowStatusUpdate → 查 all_steps → generate_report → 更新 report → broadcast ReportReady」几乎相同可抽成共用函数`finish_workflow_and_report`venv 创建/检查create_dir_all + .venv 存在 + uv venv两处重复可抽成 helper。
- **api/**`projects.rs``workflows.rs``timers.rs``db_err``ApiResult<T>` 定义重复,可提到 `api/mod.rs` 或公共模块。
- **WorkflowView.vue**`handleWsMessage` 里多处 `workflow.value && msg.workflow_id === workflow.value.id`,可先取 `const wf = workflow.value` 并统一判断;`ReportReady` 分支里 `workflow.value = { ...workflow.value, status: workflow.value.status }` 无实际效果,可删或改成真正刷新。
- **PlanSection.vue / ExecutionSection.vue**:都有 `expandedSteps`(Set)、`toggleStep`、以及 status→icon/label 的映射,可考虑抽成 composable 或共享 util 减少重复。