mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 09:28:14 +00:00
- 根目录清理: branding/docs/knowledge -> assets/, package/tools/deploy -> deploy/ - meta.go: Version 0.7.2, SDKCompatibleVersion 语义改为最高兼容 - Makefile: 版本回退 0.7.2 - registry.go: 系统提示词改用 meta.Version 格式化 - Agent 心跳: reorgGraph 拆分为三个独立循环(archive/merge/review),各自可配间隔 - GraphDB: 新增 sentences 表 + 关系句子溯源 + ClearSentenceID + CleanupOrphanedSentences - Knowledge: 支持词嵌入向量化器 - NLP 四阶段流水线: Parse -> Extract -> Verify -> Fuse + SentenceRef - 移除远程 HTTP 解析器(remote_parser.go) - 新增内嵌 ONNX 模型(vocab + dep_parser.onnx): +build onnxruntime: 全量 ONNX Runtime 推理 !build onnxruntime: 内嵌词表规则式降级解析器 - config: core.agent.onnx_model_path 替代 dep_parser_url
193 lines
4.9 KiB
Go
193 lines
4.9 KiB
Go
package nlp
|
||
|
||
import (
|
||
"sort"
|
||
|
||
"gitcode.com/JianFeeeee/HomeAgent/internal/memory/vector"
|
||
)
|
||
|
||
// Parser 依存句法分析器接口
|
||
type Parser interface {
|
||
Parse(text string) (*ParseResult, error)
|
||
}
|
||
|
||
// defaultParser 包级默认解析器,由 SetDefaultParser 设置
|
||
var defaultParser Parser
|
||
|
||
// SetDefaultParser 设置包级默认解析器。
|
||
// 设置后,NewExtractor(nil) 将使用此解析器而非纯降级模式。
|
||
func SetDefaultParser(p Parser) {
|
||
defaultParser = p
|
||
}
|
||
|
||
// GetDefaultParser 返回当前包级默认解析器
|
||
func GetDefaultParser() Parser {
|
||
return defaultParser
|
||
}
|
||
|
||
// Vectorizer 向量化接口,复用 memory/vector 或 memory/static_embedder
|
||
type Vectorizer interface {
|
||
Vectorize(text string) vector.Vector
|
||
}
|
||
|
||
// Extractor 三元组提取器
|
||
type Extractor struct {
|
||
parser Parser
|
||
fallack Parser // 降级用 POS 模板解析器
|
||
embedder Vectorizer // 可选:用于 TransE 语义验证
|
||
}
|
||
|
||
// NewExtractor 创建提取器。
|
||
// parser 为 nil 时尝试使用包级默认解析器 (SetDefaultParser),
|
||
// 若仍未设置则纯用 fallback (POS 模板匹配)。
|
||
func NewExtractor(parser Parser) *Extractor {
|
||
if parser == nil {
|
||
parser = defaultParser
|
||
}
|
||
return &Extractor{
|
||
parser: parser,
|
||
fallack: newFallbackParser(),
|
||
}
|
||
}
|
||
|
||
// SetEmbedder 设置词嵌入向量化器,用于候选三元组的语义验证
|
||
func (e *Extractor) SetEmbedder(ev Vectorizer) {
|
||
e.embedder = ev
|
||
}
|
||
|
||
// Extract 从文本中提取三元组(完整四阶段流水线)
|
||
// Phase 1: 句法解析(LTP 分词 → POS 标注 → 依存句法树)
|
||
// Phase 2: 结构初筛(依存模板 / POS 模板 → 候选三元组 + syntax_conf)
|
||
// Phase 3: 语义验证(TransE h+r≈t → vector_conf)
|
||
// Phase 4: 融合裁决(线性加权 → 阈值截断 → 降序输出)
|
||
func (e *Extractor) Extract(text string) *TripleSet {
|
||
if text == "" {
|
||
return &TripleSet{Src: "", Err: nil}
|
||
}
|
||
|
||
var allTriples []Triple
|
||
src := ""
|
||
|
||
sentences := splitSentences(text)
|
||
for _, sentence := range sentences {
|
||
if sentence == "" {
|
||
continue
|
||
}
|
||
var triples []Triple
|
||
|
||
// ——— Phase 1 & 2: 句法解析 + 结构初筛 ———
|
||
if e.parser != nil {
|
||
result, err := e.parser.Parse(sentence)
|
||
if err == nil && result != nil && len(result.Tokens) > 1 {
|
||
triples = extractFromDep(result, sentence)
|
||
if len(triples) > 0 {
|
||
src = "dep_parser"
|
||
}
|
||
}
|
||
}
|
||
|
||
// 降级:POS 模板匹配
|
||
if len(triples) == 0 && e.fallack != nil {
|
||
result, err := e.fallack.Parse(sentence)
|
||
if err == nil && result != nil && len(result.Tokens) > 1 {
|
||
triples = extractFromPOS(result, sentence)
|
||
if len(triples) > 0 {
|
||
src = "fallback"
|
||
}
|
||
}
|
||
}
|
||
|
||
// ——— Phase 3: 语义验证 (TransE h+r≈t) ———
|
||
if len(triples) > 0 && e.embedder != nil {
|
||
triples = verifyTriples(triples, e.embedder)
|
||
}
|
||
|
||
// ——— Phase 4: 融合裁决 ———
|
||
if len(triples) > 0 {
|
||
triples = fuseTriples(triples)
|
||
}
|
||
|
||
allTriples = append(allTriples, triples...)
|
||
}
|
||
|
||
if len(allTriples) > 0 {
|
||
return &TripleSet{Triples: allTriples, Src: src}
|
||
}
|
||
return &TripleSet{Src: src}
|
||
}
|
||
|
||
// ——— Phase 3: 语义验证 ———
|
||
|
||
// verifyTriples 使用 TransE 打分 (h+r≈t) 计算 vector_conf
|
||
// 输入:候选三元组(带 syntax_conf)
|
||
// 处理:cos(h+r, t) → vector_conf
|
||
// 输出:带 vector_conf 的候选三元组
|
||
func verifyTriples(triples []Triple, embedder Vectorizer) []Triple {
|
||
for i := range triples {
|
||
t := &triples[i]
|
||
h := embedder.Vectorize(t.Subject)
|
||
r := embedder.Vectorize(t.Relation)
|
||
tv := embedder.Vectorize(t.Object)
|
||
|
||
hr := addVectors(h, r)
|
||
sim := vector.CosineSimilarity(hr, tv)
|
||
|
||
// 将 cos 映射到 [0, 1] 区间(原始可能在 [-1, 1])
|
||
t.VectorConf = (sim + 1.0) / 2.0
|
||
}
|
||
return triples
|
||
}
|
||
|
||
// ——— Phase 4: 融合裁决 ———
|
||
|
||
const (
|
||
fusionAlpha = 0.4 // syntax_conf 权重
|
||
fusionBeta = 0.6 // vector_conf 权重
|
||
fusionThreshold = 0.3 // 最终阈值
|
||
)
|
||
|
||
// fuseTriples 融合裁决:线性加权计算 final_score,截断阈值,降序输出
|
||
// 输入:候选三元组(带 syntax_conf + vector_conf)
|
||
// 处理:final_score = α * syntax_conf + β * vector_conf
|
||
// 输出:通过阈值且降序排列的最终三元组
|
||
func fuseTriples(triples []Triple) []Triple {
|
||
if len(triples) == 0 {
|
||
return triples
|
||
}
|
||
|
||
// 计算 final_score 并更新 Score 字段
|
||
for i := range triples {
|
||
t := &triples[i]
|
||
finalScore := fusionAlpha*t.Score + fusionBeta*t.VectorConf
|
||
t.Score = finalScore
|
||
}
|
||
|
||
// 截断低分项
|
||
kept := make([]Triple, 0, len(triples))
|
||
for _, t := range triples {
|
||
if t.Score >= fusionThreshold {
|
||
kept = append(kept, t)
|
||
}
|
||
}
|
||
|
||
// 降序排列
|
||
sort.Slice(kept, func(i, j int) bool {
|
||
return kept[i].Score > kept[j].Score
|
||
})
|
||
|
||
return kept
|
||
}
|
||
|
||
// ——— 向量工具 ———
|
||
|
||
// addVectors 向量加法 (h + r)
|
||
func addVectors(a, b vector.Vector) vector.Vector {
|
||
out := make(vector.Vector)
|
||
for k, v := range a {
|
||
out[k] = v
|
||
}
|
||
for k, v := range b {
|
||
out[k] += v
|
||
}
|
||
return out
|
||
} |