feat: 完整实现 NLP 三元组提取系统 + token budget 上下文分配

- 重写 extractor.go: 分句、17条 POS 模板、依存模板 + COO 链、ATT合并
- parser.go: 分句循环 + TransE 向量验证(h+r≈t)
- fallback.go: jieba POS 降级解析器
- bridge.go: nlp.Triple ↔ memory.Triple 转换
- pipeline.go: extractKeyTriples 改用 NLP 提取器, 删除5条旧前缀规则
- distill.go: docToTriples 改用 NLP 提取器
- reorgGraph: 语义相似度增强检测, 保持纯 LLM 决断
- Provider 接口加 MaxContextTokens() + 模型窗口映射表
- tokenbudget.go: 中文 token 估算器 + budget 分配(80%利用率)
- process.go/buildSystemPrompt: 按 token 预算截断 memory+timeline
This commit is contained in:
root
2026-07-27 15:26:23 +08:00
parent d19b7bd13e
commit 1cb3e87dde
30 changed files with 1508 additions and 773 deletions

View File

@ -31,9 +31,6 @@ type Relation struct {
TurnID int `json:"turn_id"`
CreatedAt time.Time `json:"created_at"`
DateBucket string `json:"date_bucket"`
EvalStatus string `json:"eval_status"`
EvalRound int `json:"eval_round"`
EvalAt time.Time `json:"eval_at,omitempty"`
}
type Triple struct {
@ -96,9 +93,6 @@ func (g *GraphDB) initSchema() error {
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
date_bucket TEXT,
eval_status TEXT DEFAULT 'pending',
eval_round INTEGER DEFAULT 0,
eval_at TIMESTAMP,
FOREIGN KEY (source_id) REFERENCES entities(id),
FOREIGN KEY (target_id) REFERENCES entities(id)
)`,
@ -117,20 +111,7 @@ func (g *GraphDB) initSchema() error {
}
}
if err := tx.Commit(); err != nil {
return err
}
migrations := []string{
`ALTER TABLE relations ADD COLUMN eval_status TEXT DEFAULT 'pending'`,
`ALTER TABLE relations ADD COLUMN eval_round INTEGER DEFAULT 0`,
`ALTER TABLE relations ADD COLUMN eval_at TIMESTAMP`,
}
for _, m := range migrations {
g.db.Exec(m)
}
return nil
return tx.Commit()
}
func (g *GraphDB) Commit(triples []Triple, sessionID string, turnID int) (int, int, error) {
@ -278,8 +259,7 @@ func (g *GraphDB) Recall(keywords []string, seedEntities []string, depth int, se
relRows, err := g.db.Query(
`SELECT r.id, r.source_id, r.target_id, e1.name, e2.name,
r.relation_type, r.confidence, r.status, r.session_id,
r.turn_id, r.created_at, COALESCE(r.date_bucket, ''),
COALESCE(r.eval_status, 'pending'), COALESCE(r.eval_round, 0), r.eval_at
r.turn_id, r.created_at, COALESCE(r.date_bucket, '')
FROM relations r
JOIN entities e1 ON r.source_id = e1.id
JOIN entities e2 ON r.target_id = e2.id
@ -295,8 +275,7 @@ func (g *GraphDB) Recall(keywords []string, seedEntities []string, depth int, se
if err := relRows.Scan(&rel.ID, &rel.SourceID, &rel.TargetID,
&rel.SourceName, &rel.TargetName, &rel.RelationType,
&rel.Confidence, &rel.Status, &rel.SessionID,
&rel.TurnID, &rel.CreatedAt, &rel.DateBucket,
&rel.EvalStatus, &rel.EvalRound, &rel.EvalAt); err != nil {
&rel.TurnID, &rel.CreatedAt, &rel.DateBucket); err != nil {
return nil, err
}
result.Relations = append(result.Relations, rel)
@ -361,8 +340,7 @@ func (g *GraphDB) Recall(keywords []string, seedEntities []string, depth int, se
query := fmt.Sprintf(
`SELECT r.id, r.source_id, r.target_id, e1.name, e2.name,
r.relation_type, r.confidence, r.status, r.session_id,
r.turn_id, r.created_at, COALESCE(r.date_bucket, ''),
COALESCE(r.eval_status, 'pending'), COALESCE(r.eval_round, 0), r.eval_at
r.turn_id, r.created_at, COALESCE(r.date_bucket, '')
FROM relations r
JOIN entities e1 ON r.source_id = e1.id
JOIN entities e2 ON r.target_id = e2.id
@ -389,8 +367,7 @@ func (g *GraphDB) Recall(keywords []string, seedEntities []string, depth int, se
if err := relRows.Scan(&rel.ID, &rel.SourceID, &rel.TargetID,
&rel.SourceName, &rel.TargetName, &rel.RelationType,
&rel.Confidence, &rel.Status, &rel.SessionID,
&rel.TurnID, &rel.CreatedAt, &rel.DateBucket,
&rel.EvalStatus, &rel.EvalRound, &rel.EvalAt); err != nil {
&rel.TurnID, &rel.CreatedAt, &rel.DateBucket); err != nil {
relRows.Close()
return nil, err
}
@ -770,89 +747,6 @@ func (g *GraphDB) Archive(days int) (int, error) {
return int(n), nil
}
func (g *GraphDB) RecallPending(limit int) ([]Relation, error) {
g.mu.RLock()
defer g.mu.RUnlock()
rows, err := g.db.Query(
`SELECT r.id, r.source_id, r.target_id, e1.name, e2.name,
r.relation_type, r.confidence, r.status, r.session_id,
r.turn_id, r.created_at, COALESCE(r.date_bucket, ''),
COALESCE(r.eval_status, 'pending'), COALESCE(r.eval_round, 0), r.eval_at
FROM relations r
JOIN entities e1 ON r.source_id = e1.id
JOIN entities e2 ON r.target_id = e2.id
WHERE r.status = 'active'
AND (r.eval_status IS NULL OR r.eval_status = 'pending')
ORDER BY r.created_at DESC
LIMIT ?`, limit,
)
if err != nil {
return nil, err
}
defer rows.Close()
var relations []Relation
for rows.Next() {
var rel Relation
if err := rows.Scan(&rel.ID, &rel.SourceID, &rel.TargetID,
&rel.SourceName, &rel.TargetName, &rel.RelationType,
&rel.Confidence, &rel.Status, &rel.SessionID,
&rel.TurnID, &rel.CreatedAt, &rel.DateBucket,
&rel.EvalStatus, &rel.EvalRound, &rel.EvalAt); err != nil {
return nil, err
}
relations = append(relations, rel)
}
return relations, rows.Err()
}
func (g *GraphDB) UpdateEvalStatus(id int64, status string) error {
g.mu.Lock()
defer g.mu.Unlock()
_, err := g.db.Exec(
`UPDATE relations SET eval_status = ?, eval_round = eval_round + 1, eval_at = CURRENT_TIMESTAMP WHERE id = ?`,
status, id,
)
return err
}
func (g *GraphDB) UpdateEvalStatusBatch(ids []int64, status string) error {
g.mu.Lock()
defer g.mu.Unlock()
if len(ids) == 0 {
return nil
}
for _, id := range ids {
_, err := g.db.Exec(
`UPDATE relations SET eval_status = ?, eval_round = eval_round + 1, eval_at = CURRENT_TIMESTAMP WHERE id = ?`,
status, id,
)
if err != nil {
return err
}
}
return nil
}
func (g *GraphDB) ResolveEvaluating() (int, error) {
g.mu.Lock()
defer g.mu.Unlock()
result, err := g.db.Exec(
`UPDATE relations SET eval_status = 'approved', eval_round = eval_round + 1, eval_at = CURRENT_TIMESTAMP
WHERE eval_status = 'evaluating' AND status = 'active'`,
)
if err != nil {
return 0, err
}
n, _ := result.RowsAffected()
return int(n), nil
}
func (g *GraphDB) Close() error {
return g.db.Close()
}