mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 17:38:10 +00:00
feat: complete P0/P1/P2 — WebUI SPA, OpenClaw sidecar+simulator, healthcheck auto-sched+perf
P0: WebUI重构
- 完整 SPA 仪表盘 (7标签页), //go:embed dashboard.html
P1: OpenClaw兼容 (三通道: SKILL.md / sidecar / simulator)
- Node.js 模拟进程统一加载任意 OpenClaw 插件
- JSON-RPC 2.0 over stdio 协议, go:embed 内嵌
P2: Healthcheck 优化
- 定时自动执行 (startAutoCheck, 30min)
- healthcheck_perf 性能监控工具
其他: agentcli/cmd 插件, integration_test, status.go,
test_deepseek 清理, 多项 bug 修复
This commit is contained in:
@ -93,6 +93,9 @@ type Agent struct {
|
||||
|
||||
// 模型思考模式(thinking/reasoning)
|
||||
thinkingEnabled bool
|
||||
|
||||
// 启动时间
|
||||
startTime time.Time
|
||||
}
|
||||
|
||||
type AgentConfig struct {
|
||||
@ -135,6 +138,7 @@ func New(cfg AgentConfig) *Agent {
|
||||
}
|
||||
return &Agent{
|
||||
id: cfg.ID,
|
||||
startTime: time.Now(),
|
||||
provider: cfg.Provider,
|
||||
providerManager: cfg.ProviderManager,
|
||||
io: cfg.IO,
|
||||
@ -291,8 +295,14 @@ func (a *Agent) processTextInput(evt *agentIO.InputEvent, input string) {
|
||||
return
|
||||
}
|
||||
|
||||
noMemory := false
|
||||
if v, ok := evt.Payload["no_memory"].(bool); ok {
|
||||
noMemory = v
|
||||
}
|
||||
|
||||
// === Stage: on_input — 消息到达,插件可拦截 ===
|
||||
stageCtx := a.stageCtxFromInput(input, evt.Source, "")
|
||||
stageCtx.NoMemory = noMemory
|
||||
a.publishEvent(events.EventRawInput, map[string]interface{}{
|
||||
"content": input,
|
||||
"source": evt.Source,
|
||||
@ -337,7 +347,9 @@ func (a *Agent) processTextInput(evt *agentIO.InputEvent, input string) {
|
||||
|
||||
a.emitResponse(evt, response)
|
||||
|
||||
a.emitMemoryCandidate(evt.Source, input, response, toolsUsed)
|
||||
if !stageCtx.NoMemory {
|
||||
a.emitMemoryCandidate(evt.Source, input, response, toolsUsed)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) emitResponse(evt *agentIO.InputEvent, response string) {
|
||||
@ -1825,7 +1837,6 @@ func (a *Agent) processConsolidation(input string) {
|
||||
ToolsUsed: toolsUsed,
|
||||
})
|
||||
_ = a.context.Prune(response, a.maxContextSize, a.docStore)
|
||||
// 只写入记忆,不发外部输出
|
||||
a.emitMemoryCandidate("system", input, response, toolsUsed)
|
||||
log.Printf("[agent] consolidation done (%dms, tools=%v)", time.Since(start).Milliseconds(), toolsUsed)
|
||||
}
|
||||
|
||||
302
internal/agent/core/status.go
Normal file
302
internal/agent/core/status.go
Normal file
@ -0,0 +1,302 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
agentIO "gitcode.com/JianFeeeee/HomeAgent/internal/agent/io"
|
||||
"gitcode.com/JianFeeeee/HomeAgent/internal/memory"
|
||||
"gitcode.com/JianFeeeee/HomeAgent/internal/memory/document"
|
||||
"gitcode.com/JianFeeeee/HomeAgent/internal/memory/social"
|
||||
"gitcode.com/JianFeeeee/HomeAgent/internal/memory/text"
|
||||
"gitcode.com/JianFeeeee/HomeAgent/internal/plugin"
|
||||
"gitcode.com/JianFeeeee/HomeAgent/internal/skill"
|
||||
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
||||
"gitcode.com/JianFeeeee/HomeAgent/internal/tracker"
|
||||
)
|
||||
|
||||
// StatusProvider 内核状态查询接口。插件通过此接口查看内核运行动态。
|
||||
type StatusProvider interface {
|
||||
GetKernelStatus() *KernelStatus
|
||||
}
|
||||
|
||||
// KernelStatus 内核各子系统运行状态的聚合快照。
|
||||
type KernelStatus struct {
|
||||
Uptime string `json:"uptime"`
|
||||
StartTime string `json:"start_time"`
|
||||
|
||||
AgentID string `json:"agent_id"`
|
||||
|
||||
Plugins []PluginInfo `json:"plugins"`
|
||||
Tools []sdk.ToolDef `json:"tools"`
|
||||
Channels []ChannelInfo `json:"channels"`
|
||||
|
||||
Memory MemoryStatus `json:"memory"`
|
||||
Knowledge KnowledgeStatus `json:"knowledge"`
|
||||
Documents DocumentStatus `json:"documents"`
|
||||
TextMemory TextMemoryStatus `json:"text_memory"`
|
||||
Social SocialStatus `json:"social"`
|
||||
Skills SkillsStatus `json:"skills"`
|
||||
|
||||
LLM LLMStatus `json:"llm"`
|
||||
|
||||
Context ContextStatus `json:"context"`
|
||||
|
||||
Runtime RuntimeStatus `json:"runtime"`
|
||||
|
||||
Tracker TrackerStatus `json:"tracker"`
|
||||
}
|
||||
|
||||
type PluginInfo struct {
|
||||
Name string `json:"name"`
|
||||
Loaded bool `json:"loaded"`
|
||||
}
|
||||
|
||||
type ChannelInfo struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Ready bool `json:"ready"`
|
||||
}
|
||||
|
||||
func channelInfoFromIO(ch agentIO.ChannelInfo) ChannelInfo {
|
||||
return ChannelInfo{
|
||||
Name: ch.Name,
|
||||
Type: fmt.Sprintf("%d", ch.Type),
|
||||
Ready: true,
|
||||
}
|
||||
}
|
||||
|
||||
type MemoryStatus struct {
|
||||
Available bool `json:"available"`
|
||||
EntityCount int `json:"entity_count,omitempty"`
|
||||
RelationCount int `json:"relation_count,omitempty"`
|
||||
EntityTypes int `json:"entity_types,omitempty"`
|
||||
}
|
||||
|
||||
type KnowledgeStatus struct {
|
||||
Available bool `json:"available"`
|
||||
ItemCount int `json:"item_count,omitempty"`
|
||||
Items []string `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
type DocumentStatus struct {
|
||||
Available bool `json:"available"`
|
||||
DocCount int `json:"doc_count,omitempty"`
|
||||
VectorCount int `json:"vector_count,omitempty"`
|
||||
}
|
||||
|
||||
type TextMemoryStatus struct {
|
||||
Available bool `json:"available"`
|
||||
FileCount int `json:"file_count,omitempty"`
|
||||
}
|
||||
|
||||
type SocialStatus struct {
|
||||
Available bool `json:"available"`
|
||||
PersonCount int `json:"person_count,omitempty"`
|
||||
}
|
||||
|
||||
type SkillsStatus struct {
|
||||
Available bool `json:"available"`
|
||||
SkillList []string `json:"skill_list,omitempty"`
|
||||
}
|
||||
|
||||
type LLMStatus struct {
|
||||
Available bool `json:"available"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Sources int `json:"sources,omitempty"`
|
||||
}
|
||||
|
||||
type ContextStatus struct {
|
||||
EventCount int `json:"event_count,omitempty"`
|
||||
}
|
||||
|
||||
type RuntimeStatus struct {
|
||||
Goroutines int `json:"goroutines"`
|
||||
MemoryMB int64 `json:"memory_mb"`
|
||||
GoVersion string `json:"go_version"`
|
||||
}
|
||||
|
||||
type TrackerStatus struct {
|
||||
Available bool `json:"available"`
|
||||
Dir string `json:"dir,omitempty"`
|
||||
}
|
||||
|
||||
// collectKernelStatus 聚合内核各子系统状态快照。
|
||||
// 接收所有子系统引用(均为可选——nil 表示不可用),返回统一的状态报告。
|
||||
func collectKernelStatus(
|
||||
startTime time.Time,
|
||||
agentID string,
|
||||
providerName string,
|
||||
sourceCount int,
|
||||
stageHost *StageHost,
|
||||
iom *agentIO.IOManager,
|
||||
pluginReg *plugin.Registry,
|
||||
memDB *memory.GraphDB,
|
||||
ks interface{ List() []string },
|
||||
docStore *document.Store,
|
||||
textMem *text.Memory,
|
||||
socialStore *social.SocialStore,
|
||||
skMgr *skill.Manager,
|
||||
trk *tracker.Tracker,
|
||||
) *KernelStatus {
|
||||
status := &KernelStatus{
|
||||
Uptime: time.Since(startTime).Round(time.Second).String(),
|
||||
StartTime: startTime.Format(time.RFC3339),
|
||||
AgentID: agentID,
|
||||
Runtime: RuntimeStatus{
|
||||
Goroutines: runtime.NumGoroutine(),
|
||||
GoVersion: runtime.Version(),
|
||||
},
|
||||
LLM: LLMStatus{
|
||||
Available: providerName != "",
|
||||
Provider: providerName,
|
||||
Sources: sourceCount,
|
||||
},
|
||||
}
|
||||
|
||||
// Plugins
|
||||
if pluginReg != nil {
|
||||
names := pluginReg.List()
|
||||
for _, n := range names {
|
||||
status.Plugins = append(status.Plugins, PluginInfo{Name: n, Loaded: true})
|
||||
}
|
||||
}
|
||||
|
||||
// Tools
|
||||
if stageHost != nil {
|
||||
status.Tools = stageHost.GetToolDefs()
|
||||
}
|
||||
|
||||
// Channels
|
||||
if iom != nil {
|
||||
for _, ch := range iom.ListChannels() {
|
||||
status.Channels = append(status.Channels, channelInfoFromIO(ch))
|
||||
}
|
||||
}
|
||||
|
||||
// Graph memory
|
||||
if memDB != nil {
|
||||
status.Memory.Available = true
|
||||
if info, err := memDB.Introspect(); err == nil {
|
||||
if ec, ok := info["entity_count"].(int); ok {
|
||||
status.Memory.EntityCount = ec
|
||||
}
|
||||
if rc, ok := info["relation_count"].(int); ok {
|
||||
status.Memory.RelationCount = rc
|
||||
}
|
||||
if et, ok := info["entity_type_count"].(int); ok {
|
||||
status.Memory.EntityTypes = et
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Knowledge
|
||||
if ks != nil {
|
||||
status.Knowledge.Available = true
|
||||
status.Knowledge.Items = ks.List()
|
||||
status.Knowledge.ItemCount = len(status.Knowledge.Items)
|
||||
}
|
||||
|
||||
// Documents
|
||||
if docStore != nil {
|
||||
status.Documents.Available = true
|
||||
stats := docStore.Stats()
|
||||
if dc, ok := stats["doc_count"].(int); ok {
|
||||
status.Documents.DocCount = dc
|
||||
}
|
||||
if vc, ok := stats["vector_count"].(int); ok {
|
||||
status.Documents.VectorCount = vc
|
||||
}
|
||||
}
|
||||
|
||||
// Text memory
|
||||
if textMem != nil {
|
||||
status.TextMemory.Available = true
|
||||
status.TextMemory.FileCount = textMem.FileCount()
|
||||
}
|
||||
|
||||
// Social
|
||||
if socialStore != nil {
|
||||
status.Social.Available = true
|
||||
if persons, err := socialStore.ListPersons(); err == nil {
|
||||
status.Social.PersonCount = len(persons)
|
||||
}
|
||||
}
|
||||
|
||||
// Skills
|
||||
if skMgr != nil {
|
||||
status.Skills.Available = true
|
||||
skills := skMgr.List()
|
||||
status.Skills.SkillList = make([]string, len(skills))
|
||||
for i, sk := range skills {
|
||||
status.Skills.SkillList[i] = sk.Name
|
||||
}
|
||||
}
|
||||
|
||||
// Tracker
|
||||
if trk != nil {
|
||||
status.Tracker.Available = true
|
||||
status.Tracker.Dir = trk.MergeDir()
|
||||
}
|
||||
|
||||
// Memory
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
status.Runtime.MemoryMB = int64(m.Alloc / 1024 / 1024)
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
// GetKernelStatus 返回 Agent 驱动的内核状态快照。
|
||||
func (a *Agent) GetKernelStatus() *KernelStatus {
|
||||
providerName := ""
|
||||
sourceCount := 0
|
||||
if a.providerManager != nil {
|
||||
sourceCount = len(a.providerManager.List())
|
||||
}
|
||||
if a.provider != nil {
|
||||
providerName = a.provider.Name()
|
||||
}
|
||||
|
||||
var textMem *text.Memory
|
||||
if a.textMem != nil {
|
||||
textMem = a.textMem
|
||||
}
|
||||
|
||||
var socialStore *social.SocialStore
|
||||
if a.social != nil {
|
||||
socialStore = a.social
|
||||
}
|
||||
|
||||
var skMgr *skill.Manager
|
||||
if a.skills != nil {
|
||||
skMgr = a.skills
|
||||
}
|
||||
|
||||
var trk *tracker.Tracker
|
||||
if a.tracker != nil {
|
||||
trk = a.tracker
|
||||
}
|
||||
|
||||
ks := collectKernelStatus(
|
||||
a.startTime,
|
||||
string(a.id),
|
||||
providerName,
|
||||
sourceCount,
|
||||
a.stageHost,
|
||||
a.io,
|
||||
a.pluginReg,
|
||||
a.memory,
|
||||
a.knowledge,
|
||||
a.docStore,
|
||||
textMem,
|
||||
socialStore,
|
||||
skMgr,
|
||||
trk,
|
||||
)
|
||||
|
||||
return ks
|
||||
}
|
||||
|
||||
var _ StatusProvider = (*Agent)(nil)
|
||||
Reference in New Issue
Block a user