refactor(agent): update agent_run and loop
This commit is contained in:
parent
c6eae697ff
commit
562a99fae1
2 changed files with 102 additions and 14 deletions
|
|
@ -196,11 +196,13 @@ func (al *AgentLoop) refreshContextBlocks(ctx context.Context, opts processOptio
|
||||||
|
|
||||||
_ = g.Wait()
|
_ = g.Wait()
|
||||||
|
|
||||||
al.ctxBlockCache.Store(opts.SessionKey, ctxBlockCacheEntry{
|
if !useCached {
|
||||||
focusBlock: focusBlock,
|
al.ctxBlockCache.Store(opts.SessionKey, ctxBlockCacheEntry{
|
||||||
knowledge: kb,
|
focusBlock: focusBlock,
|
||||||
cachedAt: time.Now(),
|
knowledge: kb,
|
||||||
})
|
cachedAt: time.Now(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Prune stale entries from other sessions to prevent unbounded growth.
|
// Prune stale entries from other sessions to prevent unbounded growth.
|
||||||
al.ctxBlockCache.Range(func(key, value any) bool {
|
al.ctxBlockCache.Range(func(key, value any) bool {
|
||||||
|
|
@ -616,10 +618,63 @@ func (al *AgentLoop) runStreaming(ctx context.Context, opts processOptions, ac a
|
||||||
// auditStep logs tool calls from a Fantasy step result to the audit log.
|
// auditStep logs tool calls from a Fantasy step result to the audit log.
|
||||||
func (al *AgentLoop) auditStep(_ context.Context, step fantasy.StepResult, sessionKey string) {
|
func (al *AgentLoop) auditStep(_ context.Context, step fantasy.StepResult, sessionKey string) {
|
||||||
toolCalls := step.Content.ToolCalls()
|
toolCalls := step.Content.ToolCalls()
|
||||||
if len(toolCalls) == 0 {
|
toolResults := step.Content.ToolResults()
|
||||||
|
if len(toolCalls) == 0 && len(toolResults) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callByID := make(map[string]fantasy.ToolCallContent, len(toolCalls))
|
||||||
|
for _, tc := range toolCalls {
|
||||||
|
callByID[tc.ToolCallID] = tc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer result-based entries because they carry success/failure semantics.
|
||||||
|
if len(toolResults) > 0 {
|
||||||
|
for _, tr := range toolResults {
|
||||||
|
toolName := ""
|
||||||
|
toolInput := ""
|
||||||
|
if tc, ok := callByID[tr.ToolCallID]; ok {
|
||||||
|
toolName = tc.ToolName
|
||||||
|
toolInput = tc.Input
|
||||||
|
}
|
||||||
|
if toolName == "" {
|
||||||
|
toolName = "unknown_tool"
|
||||||
|
}
|
||||||
|
|
||||||
|
action := "tool_success"
|
||||||
|
output := ""
|
||||||
|
switch out := tr.Result.(type) {
|
||||||
|
case fantasy.ToolResultOutputContentText:
|
||||||
|
output = out.Text
|
||||||
|
case fantasy.ToolResultOutputContentMedia:
|
||||||
|
output = out.Text
|
||||||
|
case fantasy.ToolResultOutputContentError:
|
||||||
|
action = "tool_error"
|
||||||
|
if out.Error != nil {
|
||||||
|
output = out.Error.Error()
|
||||||
|
} else {
|
||||||
|
output = "tool returned error output"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry := &memory.AuditEntry{
|
||||||
|
ID: ids.New(),
|
||||||
|
AgentID: pkg.NAME,
|
||||||
|
SessionKey: sessionKey,
|
||||||
|
Action: action,
|
||||||
|
Target: toolName,
|
||||||
|
Input: toolInput,
|
||||||
|
Output: output,
|
||||||
|
}
|
||||||
|
if !al.enqueueAuditEntry(entry) {
|
||||||
|
logger.WarnCF("agent", "Audit channel unavailable, dropping tool result entry",
|
||||||
|
map[string]interface{}{"tool": toolName, "action": action})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy fallback: if no tool results were emitted, record tool calls.
|
||||||
for _, tc := range toolCalls {
|
for _, tc := range toolCalls {
|
||||||
entry := &memory.AuditEntry{
|
entry := &memory.AuditEntry{
|
||||||
ID: ids.New(),
|
ID: ids.New(),
|
||||||
|
|
@ -629,15 +684,31 @@ func (al *AgentLoop) auditStep(_ context.Context, step fantasy.StepResult, sessi
|
||||||
Target: tc.ToolName,
|
Target: tc.ToolName,
|
||||||
Input: tc.Input,
|
Input: tc.Input,
|
||||||
}
|
}
|
||||||
select {
|
if !al.enqueueAuditEntry(entry) {
|
||||||
case al.auditChan <- entry:
|
logger.WarnCF("agent", "Audit channel unavailable, dropping tool call entry",
|
||||||
default:
|
|
||||||
logger.WarnCF("agent", "Audit channel full, dropping entry",
|
|
||||||
map[string]interface{}{"tool": tc.ToolName})
|
map[string]interface{}{"tool": tc.ToolName})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (al *AgentLoop) enqueueAuditEntry(entry *memory.AuditEntry) (ok bool) {
|
||||||
|
if al.auditChan == nil || entry == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if recover() != nil {
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case al.auditChan <- entry:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// updateToolContexts updates the context for tools that need channel/chatID info.
|
// updateToolContexts updates the context for tools that need channel/chatID info.
|
||||||
func (al *AgentLoop) updateToolContexts(channel, chatID string) {
|
func (al *AgentLoop) updateToolContexts(channel, chatID string) {
|
||||||
// Use ContextualTool interface instead of type assertions
|
// Use ContextualTool interface instead of type assertions
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ type AgentLoop struct {
|
||||||
outputOverride atomic.Value // Owner: command_handler.go — CLI output redirection target for internal messages
|
outputOverride atomic.Value // Owner: command_handler.go — CLI output redirection target for internal messages
|
||||||
toolResultSearch fantasy.AgentTool
|
toolResultSearch fantasy.AgentTool
|
||||||
cortex *cortex.Cortex
|
cortex *cortex.Cortex
|
||||||
|
inflight sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
type outputTarget struct {
|
type outputTarget struct {
|
||||||
|
|
@ -413,6 +414,11 @@ func NewAgentLoop(ctx context.Context, cfg *config.Config, msgBus *bus.MessageBu
|
||||||
if aus, ok := al.memDelegate.(cortex.AuditAnalysisStore); ok {
|
if aus, ok := al.memDelegate.(cortex.AuditAnalysisStore); ok {
|
||||||
auditStore = aus
|
auditStore = aus
|
||||||
}
|
}
|
||||||
|
// DriftStore for domain health monitoring and trend tracking
|
||||||
|
var driftStore cortex.DriftStore
|
||||||
|
if ds, ok := al.memDelegate.(cortex.DriftMemorySource); ok {
|
||||||
|
driftStore = cortex.NewMemoryDriftAdapter(ds)
|
||||||
|
}
|
||||||
cortexTasks := []cortex.Task{
|
cortexTasks := []cortex.Task{
|
||||||
cortex.NewDecayTask(cortex.DefaultDecayConfig(), decayStore),
|
cortex.NewDecayTask(cortex.DefaultDecayConfig(), decayStore),
|
||||||
cortex.NewBackfillTask(cortex.DefaultBackfillConfig(), backfillStore, embedFn),
|
cortex.NewBackfillTask(cortex.DefaultBackfillConfig(), backfillStore, embedFn),
|
||||||
|
|
@ -420,6 +426,7 @@ func NewAgentLoop(ctx context.Context, cfg *config.Config, msgBus *bus.MessageBu
|
||||||
cortex.NewPruneTask(cortex.DefaultPruneConfig(), pruneStore),
|
cortex.NewPruneTask(cortex.DefaultPruneConfig(), pruneStore),
|
||||||
cortex.NewRLTask(rlStore, pkg.NAME),
|
cortex.NewRLTask(rlStore, pkg.NAME),
|
||||||
cortex.NewAuditAnalysisTask(auditStore),
|
cortex.NewAuditAnalysisTask(auditStore),
|
||||||
|
cortex.NewDriftTask(cortex.DefaultDriftConfig(), driftStore),
|
||||||
}
|
}
|
||||||
al.cortex = cortex.New(cortexTasks, 60*time.Second)
|
al.cortex = cortex.New(cortexTasks, 60*time.Second)
|
||||||
|
|
||||||
|
|
@ -440,7 +447,11 @@ func (al *AgentLoop) Run(ctx context.Context) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := al.processMessage(ctx, msg)
|
al.inflight.Add(1)
|
||||||
|
response, err := func() (string, error) {
|
||||||
|
defer al.inflight.Done()
|
||||||
|
return al.processMessage(ctx, msg)
|
||||||
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response = fmt.Sprintf("Error processing message: %v", err)
|
response = fmt.Sprintf("Error processing message: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -480,9 +491,15 @@ func (al *AgentLoop) Run(ctx context.Context) error {
|
||||||
|
|
||||||
func (al *AgentLoop) Stop() {
|
func (al *AgentLoop) Stop() {
|
||||||
al.running.Store(false)
|
al.running.Store(false)
|
||||||
close(al.auditChan)
|
al.inflight.Wait()
|
||||||
<-al.auditDone // wait for audit worker to drain
|
|
||||||
al.sessions.Close()
|
if al.auditChan != nil {
|
||||||
|
close(al.auditChan)
|
||||||
|
<-al.auditDone // wait for audit worker to drain
|
||||||
|
}
|
||||||
|
if al.sessions != nil {
|
||||||
|
al.sessions.Close()
|
||||||
|
}
|
||||||
if al.identitySync != nil {
|
if al.identitySync != nil {
|
||||||
al.identitySync.Close()
|
al.identitySync.Close()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue