feat: import agents_and_robots platform as unibots (Matrix-out, unibus transport)
Reemplaza el scaffold del echobot por la plataforma completa de bots traida desde ~/DataProyects/Github/agents_and_robots tras la operacion Matrix-out: los bots ya no hablan por Matrix sino por el bus unibus (modelo todo-rooms + E2E via shell/transportunibus sobre github.com/enmanuel/unibus/pkg/client). - go.mod: replace de unibus -> ../unibus y de fn-registry -> ../../../.. (paths relativos reajustados a la nueva ubicacion dentro de fn_registry). - app.md: bump a 0.2.0, descripcion + arquitectura + comandos + gotchas reales. - modulo Go conservado como github.com/enmanuel/agents (sin reescribir imports). agents_and_robots queda archivado como museo de la era Matrix.
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
package orchestration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
coretypes "github.com/enmanuel/agents/pkg/llm"
|
||||
"github.com/enmanuel/agents/pkg/orchestration"
|
||||
)
|
||||
|
||||
// routeInitial asks the LLM which bot should handle the question first.
|
||||
func (o *Orchestrator) routeInitial(ctx context.Context, question string, participants []string) (orchestration.RoutingDecision, error) {
|
||||
systemPrompt := strings.ReplaceAll(o.routingPrompt, "{{PARTICIPANTS}}", o.buildParticipantsList(participants, ""))
|
||||
|
||||
resp, err := o.llm(ctx, coretypes.CompletionRequest{
|
||||
Model: o.cfg.LLM.Primary.Model,
|
||||
MaxTokens: o.cfg.LLM.Primary.MaxTokens,
|
||||
Temperature: o.cfg.LLM.Primary.Temperature,
|
||||
SystemPrompt: systemPrompt,
|
||||
Messages: []coretypes.Message{
|
||||
{Role: coretypes.RoleUser, Content: question},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return orchestration.RoutingDecision{}, fmt.Errorf("LLM routing call: %w", err)
|
||||
}
|
||||
|
||||
var rd orchestration.RoutingDecision
|
||||
if err := json.Unmarshal([]byte(strings.TrimSpace(resp.Content)), &rd); err != nil {
|
||||
o.logger.Warn("failed to parse routing response, raw", "content", resp.Content, "err", err)
|
||||
return orchestration.RoutingDecision{}, fmt.Errorf("parse routing decision: %w", err)
|
||||
}
|
||||
|
||||
// Validate the chosen bot is actually a participant
|
||||
if !contains(participants, rd.TargetBotID) {
|
||||
o.logger.Warn("LLM chose unknown bot, falling back to first", "chosen", rd.TargetBotID)
|
||||
rd.TargetBotID = participants[0]
|
||||
rd.Confidence = 0.5
|
||||
rd.Reason = "fallback: LLM chose unknown bot"
|
||||
}
|
||||
|
||||
return rd, nil
|
||||
}
|
||||
|
||||
// routeRefinement asks the LLM which bot should improve the response,
|
||||
// excluding the last respondent.
|
||||
func (o *Orchestrator) routeRefinement(
|
||||
ctx context.Context,
|
||||
question string,
|
||||
responses []orchestration.BotResponse,
|
||||
participants []string,
|
||||
excludeBot string,
|
||||
) (orchestration.RoutingDecision, error) {
|
||||
lastResponse := ""
|
||||
if len(responses) > 0 {
|
||||
lastResponse = responses[len(responses)-1].Text
|
||||
}
|
||||
|
||||
systemPrompt := strings.ReplaceAll(o.refinementPrompt, "{{PARTICIPANTS}}", o.buildParticipantsList(participants, excludeBot))
|
||||
systemPrompt = strings.ReplaceAll(systemPrompt, "{{LAST_RESPONSE}}", lastResponse)
|
||||
|
||||
userContent := fmt.Sprintf("Original question: %s\n\nCurrent response that needs improvement:\n%s", question, lastResponse)
|
||||
|
||||
resp, err := o.llm(ctx, coretypes.CompletionRequest{
|
||||
Model: o.cfg.LLM.Primary.Model,
|
||||
MaxTokens: o.cfg.LLM.Primary.MaxTokens,
|
||||
Temperature: o.cfg.LLM.Primary.Temperature,
|
||||
SystemPrompt: systemPrompt,
|
||||
Messages: []coretypes.Message{
|
||||
{Role: coretypes.RoleUser, Content: userContent},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return orchestration.RoutingDecision{}, fmt.Errorf("LLM refinement call: %w", err)
|
||||
}
|
||||
|
||||
var rd orchestration.RoutingDecision
|
||||
if err := json.Unmarshal([]byte(strings.TrimSpace(resp.Content)), &rd); err != nil {
|
||||
o.logger.Warn("failed to parse refinement response", "content", resp.Content, "err", err)
|
||||
return orchestration.RoutingDecision{}, fmt.Errorf("parse refinement decision: %w", err)
|
||||
}
|
||||
|
||||
// Validate: must be a participant and not the excluded bot
|
||||
if rd.TargetBotID == excludeBot || !contains(participants, rd.TargetBotID) {
|
||||
// Pick first available that isn't excluded
|
||||
for _, p := range participants {
|
||||
if p != excludeBot {
|
||||
rd.TargetBotID = p
|
||||
rd.Reason = "fallback: LLM chose excluded or unknown bot"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rd, nil
|
||||
}
|
||||
|
||||
func contains(ss []string, s string) bool {
|
||||
for _, v := range ss {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user