Files
HomeAgent/internal/memory/social/social.go
root 2d314b3e9c 重构: 插件自注册 + .so 动态加载 + 中断打断机制
- 所有内置插件 init() 自注册 (plugin.RegisterFactory), 移除 main.go 硬编码
- 新增 .so 动态加载器 (internal/plugin/dynamic.go), 插件可编译为 plugin.so
- 新增 plugin.json 元数据 (internal/plugin/manifest.go)
- 新增 interceptLoop 独立 goroutine:
  (a) cancelLLM() 取消进行中的 HTTP 请求
  (b) interceptCh → drainInterrupt() 注入 [打断消息] 到 LLM 上下文
  (c) InjectInput 空闲时触发新处理循环
- 新增 internal/plugins/all.go 空白导入触发所有内置插件 init()
- internal/sdk/ 作为 PluginSDK 正式 Go API
- internal/api/ → internal/plugins/webui/ 迁移
- 删除旧 cmd/cli/, 使用 cmd/waiter/ 替代
- 更新 PLAN.md / ARCHITECTURE.md / README.md 文档
2026-07-03 16:53:34 +08:00

299 lines
6.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package social
import (
"fmt"
"strings"
"sync"
"gitcode.com/JianFeeeee/HomeAgent/internal/memory"
)
const (
entityTypePerson = "person"
entityTypeTrait = "trait_value"
traitPrefix = "trait:"
)
type PersonProfile struct {
Name string `json:"name"`
Traits map[string]string `json:"traits,omitempty"`
Relations []SocialRelation `json:"relations,omitempty"`
}
type SocialRelation struct {
Person string `json:"person"`
Relation string `json:"relation"` // 关系类型:朋友/家人/同事/邻居/...
}
type SocialStore struct {
db *memory.GraphDB
mu sync.RWMutex
}
func New(db *memory.GraphDB) *SocialStore {
return &SocialStore{db: db}
}
// GetPerson 获取人物完整档案(特质 + 社交关系)
func (s *SocialStore) GetPerson(name string) (*PersonProfile, error) {
if s.db == nil {
return nil, fmt.Errorf("social store not available")
}
result, err := s.db.Recall([]string{name}, nil, 2, "")
if err != nil {
return nil, err
}
profile := &PersonProfile{
Name: name,
Traits: make(map[string]string),
}
// 查找指定 person 的 ID
var personID int64
for _, e := range result.Entities {
if e.Name == name {
personID = e.ID
break
}
}
if personID == 0 {
return nil, fmt.Errorf("person '%s' not found", name)
}
// 区分 trait 关系和社交关系
for _, r := range result.Relations {
if strings.HasPrefix(r.RelationType, traitPrefix) {
// 特质trait:<特质名>
traitName := strings.TrimPrefix(r.RelationType, traitPrefix)
if r.SourceID == personID {
profile.Traits[traitName] = r.TargetName
} else {
profile.Traits[traitName] = r.SourceName
}
} else if r.SourceID == personID {
profile.Relations = append(profile.Relations, SocialRelation{
Person: r.TargetName,
Relation: r.RelationType,
})
} else if r.TargetID == personID {
profile.Relations = append(profile.Relations, SocialRelation{
Person: r.SourceName,
Relation: r.RelationType,
})
}
}
return profile, nil
}
// SetTrait 设置/更新人物特质。如果同名特质已存在则覆盖
func (s *SocialStore) SetTrait(name, trait, value string) error {
if s.db == nil {
return fmt.Errorf("social store not available")
}
// 先清除旧特质值
oldVal, found := s.GetTrait(name, trait)
if found && oldVal != "" {
s.db.Purge(map[string]string{
"subject_contains": name,
"relation_type": traitPrefix + trait,
}, "soft")
}
triples := []memory.Triple{
{
Subject: name,
SubjectType: entityTypePerson,
Relation: traitPrefix + trait,
Object: value,
ObjectType: entityTypeTrait,
Confidence: 1.0,
},
}
_, _, err := s.db.Commit(triples, "social_trait", 0)
return err
}
// GetTrait 获取指定人物的指定特质值
func (s *SocialStore) GetTrait(name, trait string) (string, bool) {
if s.db == nil {
return "", false
}
result, err := s.db.Recall([]string{name}, nil, 1, "")
if err != nil || result == nil {
return "", false
}
var personID int64
for _, e := range result.Entities {
if e.Name == name {
personID = e.ID
break
}
}
if personID == 0 {
return "", false
}
for _, r := range result.Relations {
if r.RelationType == traitPrefix+trait {
if r.SourceID == personID {
return r.TargetName, true
}
return r.SourceName, true
}
}
return "", false
}
// AddRelation 建立两人之间的社交关系
func (s *SocialStore) AddRelation(personA, relation, personB string) error {
if s.db == nil {
return fmt.Errorf("social store not available")
}
triples := []memory.Triple{
{
Subject: personA,
SubjectType: entityTypePerson,
Relation: relation,
Object: personB,
ObjectType: entityTypePerson,
Confidence: 1.0,
},
}
_, _, err := s.db.Commit(triples, "social_relation", 0)
return err
}
// RemoveRelation 删除两人之间的社交关系
func (s *SocialStore) RemoveRelation(personA, relation, personB string) error {
if s.db == nil {
return fmt.Errorf("social store not available")
}
_, err := s.db.Purge(map[string]string{
"subject_contains": personA,
"target_contains": personB,
"relation_type": relation,
}, "soft")
return err
}
// GetRelations 获取指定人物的所有社交关系
func (s *SocialStore) GetRelations(name string) ([]SocialRelation, error) {
if s.db == nil {
return nil, fmt.Errorf("social store not available")
}
result, err := s.db.Recall([]string{name}, nil, 1, "")
if err != nil {
return nil, err
}
var personID int64
for _, e := range result.Entities {
if e.Name == name {
personID = e.ID
break
}
}
if personID == 0 {
return nil, nil
}
var relations []SocialRelation
for _, r := range result.Relations {
if strings.HasPrefix(r.RelationType, traitPrefix) {
continue
}
if r.SourceID == personID {
relations = append(relations, SocialRelation{Person: r.TargetName, Relation: r.RelationType})
} else if r.TargetID == personID {
relations = append(relations, SocialRelation{Person: r.SourceName, Relation: r.RelationType})
}
}
return relations, nil
}
// GetNetwork 获取指定人物周围 depth 度的社交网络
func (s *SocialStore) GetNetwork(name string, depth int) ([]*PersonProfile, error) {
if s.db == nil {
return nil, fmt.Errorf("social store not available")
}
// 用 Recall 的 BFS 遍历获取多度关联
result, err := s.db.Recall([]string{name}, nil, depth, "")
if err != nil {
return nil, err
}
personMap := make(map[int64]*PersonProfile)
for _, e := range result.Entities {
p := &PersonProfile{
Name: e.Name,
Traits: make(map[string]string),
}
personMap[e.ID] = p
}
for _, r := range result.Relations {
if strings.HasPrefix(r.RelationType, traitPrefix) {
traitName := strings.TrimPrefix(r.RelationType, traitPrefix)
if p, ok := personMap[r.SourceID]; ok {
p.Traits[traitName] = r.TargetName
}
if p, ok := personMap[r.TargetID]; ok {
p.Traits[traitName] = r.SourceName
}
}
}
// 收集关系
for _, r := range result.Relations {
if strings.HasPrefix(r.RelationType, traitPrefix) {
continue
}
sr := SocialRelation{Relation: r.RelationType}
if p, ok := personMap[r.SourceID]; ok {
sr.Person = r.TargetName
p.Relations = append(p.Relations, sr)
}
sr = SocialRelation{Relation: r.RelationType}
if p, ok := personMap[r.TargetID]; ok {
sr.Person = r.SourceName
p.Relations = append(p.Relations, sr)
}
}
var profiles []*PersonProfile
for _, p := range personMap {
profiles = append(profiles, p)
}
return profiles, nil
}
// ListPersons 列出所有已知人物entity.type = person
func (s *SocialStore) ListPersons() ([]string, error) {
if s.db == nil {
return nil, fmt.Errorf("social store not available")
}
result, err := s.db.Recall(nil, nil, 1, "")
if err != nil {
return nil, err
}
var names []string
for _, e := range result.Entities {
if e.Type == entityTypePerson || e.Type == "Person" {
names = append(names, e.Name)
}
}
return names, nil
}