fix: correct context pruning order and vector alignment

- Prune context BEFORE processing (LSTM forget gate pattern)
  so LLM only sees relevant context, instead of pruning after the fact
- Fix ensureTrained() to recompute all event vectors after retraining
  vectorizer, fixing feature-space mismatch between stored vectors and
  query vector that made relevance scoring effectively random
- Add read lock to knowledge BuildTree() (data race fix)
- Log writeIndex() errors instead of discarding them
- Fix TOCTOU race in document ContextToDoc() dedup
- Fix healthcheck timing (measure elapsed before cleanup)
- Refactor waiter CLI into separate files (state, conn, config, editor,
  history, builtin) for maintainability
- Add CLI plugin API key authentication
This commit is contained in:
root
2026-07-07 17:07:38 +08:00
parent f794513b39
commit 2edc039351
13 changed files with 1139 additions and 602 deletions

View File

@ -93,7 +93,7 @@ func (s *Store) Insert(doc *Doc) error {
return nil
}
// ContextToDoc — 将一段上下文对话历史提炼为文档
// ContextToDoc — 将一段上下文对话历史提炼为文档(带内容去重)
func (s *Store) ContextToDoc(source string, entries []ContextEntry) (*Doc, error) {
if len(entries) == 0 {
return nil, nil
@ -108,26 +108,46 @@ func (s *Store) ContextToDoc(source string, entries []ContextEntry) (*Doc, error
parts = append(parts, line)
}
content := strings.Join(parts, "\n")
contentHash := simpleHash(content)
// 去重:检查是否已有相同 hash 的文档(在锁内完成创建/更新)
summary := summarizeEntries(entries)
tags := extractTags(entries)
entities := extractEntities(entries)
s.mu.Lock()
for _, d := range s.docs {
if d.Meta != nil && d.Meta["content_hash"] == contentHash {
d.UpdatedAt = time.Now()
d.LastAccess = time.Now()
d.Content = content
d.Source = source
d.Summary = summary
d.Tags = tags
d.Entities = entities
s.dirty = true
s.mu.Unlock()
return d, nil
}
}
id := fmt.Sprintf("doc_%d", time.Now().UnixNano())
doc := &Doc{
ID: fmt.Sprintf("doc_%d", time.Now().UnixNano()),
Summary: summary,
Content: content,
Tags: tags,
Entities: entities,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Source: source,
ID: id,
Summary: summary,
Content: content,
Tags: tags,
Entities: entities,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
LastAccess: time.Now(),
AccessCount: 1,
Source: source,
Meta: map[string]string{"content_hash": contentHash},
}
if err := s.Insert(doc); err != nil {
return nil, err
}
s.docs[id] = doc
s.dirty = true
s.mu.Unlock()
return doc, nil
}
@ -422,3 +442,12 @@ func truncate(s string, max int) string {
}
return s
}
func simpleHash(s string) string {
// 简单的基于内容的哈希,用于去重
h := 0
for _, r := range s {
h = h*31 + int(r)
}
return fmt.Sprintf("h%08x", h)
}