No description
Find a file
2026-06-01 14:10:17 +03:00
docs Проверка через линтер 2026-06-01 14:10:17 +03:00
examples Примеры 2026-06-01 14:07:42 +03:00
.env.example Первая реализация агента 2026-06-01 12:55:09 +03:00
.gitignore Добавление тестов для субагентов 2026-06-01 13:24:59 +03:00
agent.go Упрощение агента 2026-06-01 13:20:26 +03:00
agent_integration_test.go Проверка через линтер 2026-06-01 14:10:17 +03:00
agent_unit_test.go Проверка через линтер 2026-06-01 14:10:17 +03:00
context.go Упрощение агента 2026-06-01 13:20:26 +03:00
coverage.out Добавление тестов для субагентов 2026-06-01 13:24:59 +03:00
errors.go Первая реализация агента 2026-06-01 12:55:09 +03:00
go.mod Примеры 2026-06-01 14:07:42 +03:00
go.sum Примеры 2026-06-01 14:07:42 +03:00
LICENSE first commit 2026-06-01 08:15:41 +03:00
message.go Первая реализация агента 2026-06-01 12:55:09 +03:00
message_test.go Первая реализация агента 2026-06-01 12:55:09 +03:00
options.go Первая реализация агента 2026-06-01 12:55:09 +03:00
README.md Добавление тестов для субагентов 2026-06-01 13:24:59 +03:00
tool.go Первая реализация агента 2026-06-01 12:55:09 +03:00
tool_test.go Первая реализация агента 2026-06-01 12:55:09 +03:00

go-simple-agent

Легковесный Go-пакет для создания LLM-агентов. Работает поверх llm-simple-api.

Установка

go get git.ymnuktech.ru/ymnuk/go-simple-agent

Быстрый старт

package main

import (
	"context"
	"fmt"
	"log"

	llmsdk "git.ymnuktech.ru/ymnuk/llm-simple-api"
	"git.ymnuktech.ru/ymnuk/go-simple-agent"
)

func main() {
	api := llmsdk.NewLLMSimpleAPI(&llmsdk.LLMOpts{
		BaseUrl:     "http://localhost:11434/v1",
		Model:       "gpt-3.5-turbo",
		MaxTokens:   4096,
		NotContinue: true,
	})

	ag, err := agent.NewAgent(api, agent.WithMaxIterations(5))
	if err != nil {
		log.Fatal(err)
	}

	output, _, err := ag.Run(context.Background(), "What is 2+2?", nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(output)
}

Tool

type Tool interface {
    Name() string
    Description() string
    Schema() json.RawMessage
    Call(ctx context.Context, args json.RawMessage) (json.RawMessage, error)
}

Регистрация тула в llm-simple-api (отдельно от агента):

type WeatherArgs struct {
    Location string `json:"location"`
}

tool := &myWeatherTool{}
api.RegisterTool(tool.Name(), tool.Description(), agent.WrapTool[WeatherArgs](tool))

Subagent (агент как Tool)

Любой Agent может быть обёрнут в Tool и вызван другим агентом. Для этого не нужно менять библиотеку — достаточно реализовать Tool, внутри Call которого запускается subagent.

type SubagentTool struct {
    name     string
    subAgent *agent.Agent
}

func (s *SubagentTool) Name() string        { return s.name }
func (s *SubagentTool) Description() string { return "..." }
func (s *SubagentTool) Schema() json.RawMessage { return nil }

func (s *SubagentTool) Call(ctx context.Context, args json.RawMessage) (json.RawMessage, error) {
    var params struct{ Task string `json:"task"` }
    json.Unmarshal(args, &params)
    output, _, err := s.subAgent.Run(ctx, params.Task, nil)
    if err != nil {
        return nil, err
    }
    return json.RawMessage(`{"result": "` + output + `"}`), nil
}

Subagent использует отдельный llm-simple-api со своими тулами и настройками:

subAPI := llmsdk.NewLLMSimpleAPI(...)
subAgent, _ := agent.NewAgent(subAPI, agent.WithMaxIterations(3))
subAgent.SetSystemPrompt("You are a maths expert.")

subTool := &SubagentTool{name: "math_expert", subAgent: subAgent}
api.RegisterTool(subTool.Name(), subTool.Description(),
    agent.WrapTool[struct{Task string `json:"task"`}](subTool))

Сжатие контекста (OnLimitHandler)

Агент предоставляет callback для HistoryOpts.OnLimit в llm-simple-api:

ag, _ := agent.NewAgent(api,
    agent.WithMaxIterations(20),
    agent.WithContextStrategy(myCompressStrategy),
)

api.SetOpts(llmsdk.LLMOpts{
    History: llmsdk.HistoryOpts{
        Threshold: 3000,
        OnLimit:   ag.OnLimitHandler(),
    },
})

Сигнатура стратегии:

type ContextStrategy func(history []llmsdk.Message, promptTokens, totalTokens int) ([]llmsdk.Message, error)

Стратегия получает историю (включая system prompt), сжимает и возвращает. Агент подставляет [system] + compressed обратно. Если стратегия не задана — история возвращается без изменений.

Архитектура

Agent.Run(ctx, input, history)
  │
  ├─ SetHistory(system + history)
  ├─ ChatCompletions(input)      // 1 раунд: запрос → tool_calls → выполнение
  ├─ for IsNeedContinue() && ... // пока есть tool_calls
  │     Continue()
  ├─ GetHistory()
  └─ return (output, newHistory)
  • Цикл tool_callsllm-simple-api (конкурентно через MaxParallelTools)
  • Тулы регистрируются в llm-simple-api напрямую, агенту не нужны
  • Сжатие контекста — через OnLimitHandler() + внешняя ContextStrategy
  • СтатусIsNeedContinue() определяет, нужно ли продолжать

API

func NewAgent(api *llmsdk.LLMSimpleAPI, opts ...AgentOption) (*Agent, error)
func (a *Agent) Run(ctx context.Context, input string, history []Message) (string, []Message, error)
func (a *Agent) SetSystemPrompt(prompt string)
func (a *Agent) OnLimitHandler() llmsdk.HistoryLimitCallback

func WithMaxIterations(n int) AgentOption
func WithContextStrategy(s ContextStrategy) AgentOption

Лицензия

MIT