Skip to content

Latest commit

 

History

History
189 lines (152 loc) · 4.76 KB

File metadata and controls

189 lines (152 loc) · 4.76 KB

Gen как библиотека

Целевая схема для другого проекта: в go.mod только github.com/magomedcoder/gen, процесс gen-runner поднимается отдельно.

Роли компонентов

							gRPC (llmrunner.proto)
	gen / или ваш сервис  ---------------------------> gen-runner (LLM)
			|					SendMessage, Embed, ...
        	|                     
        	|
        	|  tools/MCP/RAG/сессии - в коде на gen/pkg/*
        	|
			---> gen-mcp-servers (опционально, отдельные процессы)

go.mod стороннего проекта

module testtest

go 1.26

require (
    github.com/magomedcoder/gen v0.x.x
    github.com/magomedcoder/gen/llm-runner v0.x.x
)

Локальная разработка в монорепо

replace (
    github.com/magomedcoder/gen => ../gen
    github.com/magomedcoder/gen/llm-runner => ../gen-runner
)

Минимальное подключение к gen-runner

import (
    "context"

    "github.com/magomedcoder/gen/pkg/domain"
    "github.com/magomedcoder/gen/pkg/runnerconnect"
    "github.com/magomedcoder/gen/pkg/toolloop"
)

func main() {
    ctx := context.Background()

    conn, err := runnerconnect.FromAddresses(ctx, []string{"127.0.0.1:50052"}, true)
    if err != nil {
        panic(err)
    }
    defer conn.Pool.Close()

    llm := conn.LLM // domain.LLMRepository

    if err := conn.Pool.WarmModelOnRunner(ctx, "127.0.0.1:50052", "my-model"); err != nil {
        panic(err)
    }

    msgs := []*domain.Message{
        domain.NewMessage(1, "Вы - полезный помощник.", domain.MessageRoleSystem),
        domain.NewMessage(1, "Привет", domain.MessageRoleUser),
    }

    gp := &domain.GenerationParams{}

    ch, err := llm.SendMessageOnRunner(ctx, "127.0.0.1:50052", msgs, nil, 120, gp)
    if err != nil {
        panic(err)
    }

    var full string
    for c := range ch {
        full += c.Content
    }

    if blob := toolloop.ExtractToolActionBlob(full); blob != "" {
        // tool-loop parse -> execute -> снова llm.SendMessageOnRunner
        _ = blob
    }
}

Пример использование библиотеки

package main

import (
	"context"
	"flag"
	"fmt"
	"os"

	"github.com/magomedcoder/gen/pkg/agent"
	"github.com/magomedcoder/gen/pkg/domain"
	"github.com/magomedcoder/gen/pkg/runnerconnect"
	"github.com/magomedcoder/gen/pkg/runnerprompt"
	"github.com/magomedcoder/gen/pkg/toolloop"
)

type memStore struct {
	msgs []*domain.Message
	id   int64
}

func (m *memStore) Create(_ context.Context, msg *domain.Message) error {
	m.id++
	msg.Id = m.id
	m.msgs = append(m.msgs, msg)
	return nil
}

type echoExecutor struct{}

func (echoExecutor) Execute(_ context.Context, call toolloop.ExecutableToolCall, _ *domain.GenerationParams, _ *toolloop.LoopEnv) (string, error) {
	return fmt.Sprintf(`{"tool":%q,"ok":true}`, call.ResolvedName), nil
}

// go run ./examples/minimal -runner 127.0.0.1:50052 -model <alias>

func main() {
	runnerAddr := flag.String("runner", "127.0.0.1:50052", "gen-runner gRPC address")
	model := flag.String("model", "", "model alias для LoadModel (обязательно)")

	ctx := context.Background()
	conn, err := runnerconnect.FromAddresses(ctx, []string{*runnerAddr}, true)
	if err != nil {
		fmt.Fprintf(os.Stderr, "runner: %v\n", err)
		os.Exit(1)
	}
	defer conn.Pool.Close()

	if err := conn.Pool.WarmModelOnRunner(ctx, *runnerAddr, *model); err != nil {
		fmt.Fprintf(os.Stderr, "load model: %v\n", err)
		os.Exit(1)
	}

	sessionID := int64(1)
	tools := []domain.Tool{
		{
			Name:           "echo_tool",
			Description:    "Возвращает echo JSON",
			ParametersJSON: `{"type":"object"}`,
		},
	}
	sys := domain.NewMessage(sessionID, "Вы очень полезны.", domain.MessageRoleSystem)
	runnerprompt.EnrichSystemMessage(sys, runnerprompt.SystemToolsOptions{
        Tools: tools,
    })
	user := domain.NewMessage(sessionID, "Вызови echo_tool с пустыми parameters, затем ответь пользователю кратко.", domain.MessageRoleUser)

	gp := &domain.GenerationParams{
		Tools: tools,
	}
	store := &memStore{}

	ch, err := agent.Run(ctx, agent.Config{
		SessionID:      sessionID,
		RunnerAddr:     *runnerAddr,
		SelectedModel:  *model,
		LLM:            conn.LLM,
		InitialHistory: []*domain.Message{sys, user},
		TimeoutSeconds: 120,
		GenParams:      gp,
		MaxRounds:      6,
		Executor:       echoExecutor{},
		Store:          store,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "агент: %v\n", err)
		os.Exit(1)
	}

	for c := range ch {
		if c.Text != "" {
			fmt.Print(c.Text)
		}
	}
	fmt.Println()
	fmt.Fprintf(os.Stderr, "сообщение: %d\n", len(store.msgs))
}