secai

package module
v0.5.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 11, 2026 License: GPL-3.0 Imports: 43 Imported by: 0

README

secai

secai is a Golang framework for Reasoning AI Workflows, implemented using a unique state machine which thrives in complexity. It's a solid foundation for complex, proactive, and long-lived AI Agents with deep and structured memory. Each bot ships with embedded devtools and several UIs. The execution flow is graph-based, which allows for precise behavior modeling of agents, including interruptions, concurrency, and fault tolerance.

It's a sophisticated replacement for frameworks like LangGraph and offers deeply relational consensus of state.

v0.5 - Fully Embedded

Live debugger | Live SQL | Read logs | Browse files

Debugger REPL DB DB Log
Dashboard
Click to see the diagram

v0.4 - Local One

AI-gent Cook on qwen3-vl-30b

Click to see the diagram

v0.2 - User Demo

Screenshots and YouTube are also available.

[!NOTE] User demo (7m captions-only) showcasing a cooking assistant which helps to pick a recipe from ingredients AND cook it.

Click to see screenshots
Intro AI-gent Cook Debugger 1 Debugger 2 Memory & Stories
User Interfaces Outro      
     
Diagram

v0.1 - Platform Demo

Screenshots and YouTube are also available.

[!NOTE] Platform demo (5m captions-only), showcasing all nine ways an agent can be seen, in addition to the classic chat view.

Click to see screenshots
SVG graph am-dbg Grafana Jaeger REPL
SQL IDE Bash Prompts  
 
Diagram

Features

  • multi-prompt agency
    • a single agent/bot is built of several AI prompts
    • each state can have a prompt bound to it, with dedicated history and documents
  • atomic consensus with relations and negotiation
    • eg states excluding each other can't be active simultaneously
  • dedicated DSL layer for bot schema
    • suitable for non-coding authors
  • structured prompt input/output via JSON schemas
  • declarative flow definitions for non-linear flows
  • cancellation support (interrupts)
  • choice menu (list of offers)
  • prompt history
    • embedded SQLite
    • JSONL log
    • "latest prompt" files
  • proactive stories with actors
    • stories have actions and progress
  • LLM-sourced story switching (orienting)
    • on prompts and timeouts
  • dynamic flow graph for the memory
    • LLM creates an actionable state-machine
  • TUIs and WebAssembly PWAs for user interfaces
Goals
  • precision
  • correctness
  • granular debugging
Devtools

All devtools are available on the web, some also as TUIs, and some as regular files. Everything is shipped as a single file.

  • debugger
  • REPL
  • diagrams (D2 SVGs)
  • DB browser
  • log viewer
  • observability (OpenTelemetry, Prometheus, Loki)
Tools
  • websearch (dockerized searxng)
  • HTML scrape (embedded colly)

Implementation

Architecture
  • Agent (actor)
    • state-machine schema
    • prompts
    • tools
  • Tool (actor)
    • state-machine schema
  • Memory
    • state-machine schema
  • Prompt (state)
    • params schema
    • result schema
    • history log
    • documents
  • Stories (state)
    • actors (state machines)
    • actions
    • progress
  • Document
    • title
    • content

Try It

$ ./ai-gent-cook
AI-gent Cook v0.5.0

Web:
- http://localhost:12854
- http://localhost:12854/agent

Files:
- config: config.kdl
- log:    tmp-cook/cook.jsonl

TUI:
- http://localhost:7856
- ssh localhost -p 7955 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no

REPL:
- http://localhost:13179
- ./cook repl

Log:
- http://localhost:12858
- ./cook log --tail
- tail -f tmp-cook/cook.jsonl -n 100 | fblog -d -x msg -x time -x level

Debugger:
- http://localhost:13178
- files: http://localhost:13171
- ssh localhost -p 13172 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no

DB:
- Base: http://localhost:13180
- Agent: http://localhost:13181
- History: http://localhost:13182

https://AI-gents.work

Schema Examples

Code snippets from state and prompt schemas of examples/cook. Both schemas are pure and debuggable Golang code.

State Schema
// CookStatesDef contains all the states of the Cook state machine.
type CookStatesDef struct {
	*am.StatesBase

	// ...

	// prompts

	RestoreJokes string
	GenJokes     string
	JokesReady   string

	GenSteps string
	// StepsReady implies the steps have been translated into actionable memory.
	StepsReady string

	GenStepComments   string
	StepCommentsReady string

	// inherit from AgentLLM
	*ssllm.AgentLLMStatesDef
}

// ...

// CookSchema represents all relations and properties of CookStates.
var CookSchema = SchemaMerge(
	// inherit from AgentLLM
	ssllm.LLMAgentSchema,
	am.Schema{
        
		// gen AI

		ssC.RestoreJokes: {
			Auto:    true,
			Require: S{ssC.DBReady, ssC.CharacterReady},
			Remove:  sgC.Jokes,
		},
		ssC.GenJokes: {
			Require: S{ssC.CharacterReady, ssC.DBReady},
			Remove:  sgC.Jokes,
			Tags:    S{ssbase.TagPrompt},
		},
		ssC.JokesReady: {Remove: sgC.Jokes},

		ssC.GenSteps: {
			Auto:    true,
			Require: S{ssC.StoryCookingStarted},
			Remove:  S{ssC.StepsReady},
			Tags:    S{ssbase.TagPrompt},
		},
		ssC.StepsReady: {
			Require: S{ssC.RecipeReady},
			Remove:  S{ssC.GenSteps},
		},

		ssC.GenStepComments: {
			Auto:    true,
			Require: S{ssC.StoryCookingStarted, ssC.StepsReady},
			Remove:  S{ssC.StepCommentsReady},
			Tags:    S{ssbase.TagPrompt},
		},
		ssC.StepCommentsReady: {Remove: S{ssC.GenStepComments}},

		ssC.Orienting: {
			Multi: true,
			Tags:  S{ssbase.TagPrompt},
		},
		ssC.OrientingMove: {},
	})
Prompt Schema

The comments attached to Params* and Result* are sent over to AI models.

// RECIPE

type PromptRecipePicking = secai.Prompt[ParamsRecipePicking, ResultRecipePicking]

func NewPromptRecipePicking(agent shared.AgentBaseAPI) *PromptRecipePicking {
	return secai.NewPrompt[ParamsRecipePicking, ResultRecipePicking](
		agent, ss.StoryRecipePicking, `
			- You're a database of cooking recipes.
		`, `
			1. Suggest recipes based on user's ingredients.
			2. If possible, find 1 extra recipe, which is well known, but 1-3 ingredients are missing.
			3. Summarize the propositions using the character's personality.
		`, `
			- Limit the amount of recipes to the requested number (excluding the extra recipe).
			- Include an image URL per each recipe
		`)
}

type Recipe struct {
	Name  string
	Desc  string
	Steps string
}

type ParamsRecipePicking struct {
	// List of available ingredients.
	Ingredients []Ingredient
	// The number of recipes needed.
	Amount int
}

type ResultRecipePicking struct {
	// List of proposed recipes
	Recipes []Recipe
	// Extra recipe with unavailable ingredients.
	ExtraRecipe Recipe
	// Message to the user, summarizing the recipes. Max 3 sentences.
	Summary string
}

// ...

var StoryRecipePicking = &shared.Story{
	StoryInfo: shared.StoryInfo{
		State: ss.StoryRecipePicking,
		Title: "Recipe Picking",
		Desc:  "The bot offers some recipes, based on the ingredients.",
	},
	Agent: shared.StoryActor{
		Trigger: amhelp.Cond{
			Is:  am.S{ss.Ready, ss.IngredientsReady},
			Not: am.S{ss.RecipeReady},
		},
	},
}

Read the full state schema and prompt schema.

Documentation

Getting Started

We can use one of the examples as a starting template. It allows for further semver updates of the base framework.

  1. Choose the source example
    • export SECAI_EXAMPLE=cook
    • export SECAI_EXAMPLE=research (broken since v0.4.0)
  2. git clone https://github.com/pancsta/secai.git
  3. install task ./secai/scripts/deps.sh
  4. copy the agent cp -R secai/examples/$SECAI_EXAMPLE MYAGENT
  5. cd MYAGENT && go mod init github.com/USER/MYAGENT
  6. get fresh configs
    1. task sync-taskfile
    2. task sync-configs
  7. start it task start
  8. look around task --list-all
  9. configure the bot $EDITOR config.kdl

Differences

secai differs from other AI agents / workflows frameworks in the way it treats AI prompts. Most frameworks call each prompt an "agent", while secai treats prompts as simple DB queries with IoC (Inversion of Control). Tool usage happens manually through typesafe params / results. This approach increases determinism, safety, and overfall control. This multi-prompt workflow forms an actual bot / agent. This does not mean agents can't be composed into larger groups, which happens simply on the state level (via piping / aRPC), as the underlying workflow engine (asyncmachine) doesn't depend on AI at all.

The flow graph, unlike in regular workflows, is not path-based - each node (state) can be activated anytime (same as calling a function), and the edges between nodes are meant to resolve the state consensus. It's a directed multi-graph of states with a negotiation phase.

Scripting

arpc offers CLI access to remote agents, including subscription. It's perfect for quick and simple integrations, scripts, or experiments.

Example: $ arpc -f tmp/research.addr -- when . Requesting && echo "REQUESTING"

  1. Connect to the address from tmp/research.addr
  2. When the last connected agent (.) goes into state Requesting
  3. Print "REQUESTING" and exit

License

To help keep AI open, this project migrated to GPL starting from v0.3.0.

Acknowledgements

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrHistNil = errors.New("history is nil")
	ErrNoAI    = errors.New("no AI provider configured")
)
View Source
var ErrAI = errors.New("AI error")

ErrAI is for states.AgentBaseStatesDef.ErrAI.

View Source
var ErrDB = errors.New("DB error")

ErrDB is for states.AgentBaseStatesDef.ErrDB.

View Source
var ParseArgs = shared.ParseArgs
View Source
var Pass = shared.Pass
View Source
var PassRpc = shared.PassRPC

Functions

func AddErrAI added in v0.5.0

func AddErrAI(
	event *am.Event, mach *am.Machine, err error, args ...am.A,
) am.Result

AddErrAI adds ErrAI.

func AddErrDB added in v0.5.0

func AddErrDB(
	event *am.Event, mach *am.Machine, err error, args ...am.A,
) am.Result

AddErrDB adds ErrDB.

func ToolAddToPrompts

func ToolAddToPrompts(t ToolApi, prompts ...PromptApi)

Types

type A added in v0.2.0

type A = shared.A

type AgentBase added in v0.4.0

type AgentBase struct {
	*am.ExceptionHandler
	*ssam.DisposedHandlers

	// UserInput is a prompt submitted the user, owned by [schema.AgentBaseStatesDef.Prompt].
	UserInput string
	// OfferList is a list of choices for the user.
	// TODO atomic?
	OfferList []string
	DbConn    *sql.DB
	// contains filtered or unexported fields
}

func NewAgent added in v0.2.0

func NewAgent(ctx context.Context, states am.S, machSchema am.Schema) *AgentBase

func (*AgentBase) AgentImpl added in v0.5.0

func (a *AgentBase) AgentImpl() shared.AgentAPI

func (*AgentBase) BaseDBReadyEnd added in v0.4.0

func (a *AgentBase) BaseDBReadyEnd(e *am.Event)

func (*AgentBase) BaseDBSavingEnter added in v0.4.0

func (a *AgentBase) BaseDBSavingEnter(e *am.Event) bool

func (*AgentBase) BaseDBSavingState added in v0.4.0

func (a *AgentBase) BaseDBSavingState(e *am.Event)

func (*AgentBase) BaseDBStartingState added in v0.4.0

func (a *AgentBase) BaseDBStartingState(e *am.Event)

func (*AgentBase) BuildOffer added in v0.4.0

func (a *AgentBase) BuildOffer() string

func (*AgentBase) CheckingMenuRefsEnter added in v0.5.0

func (a *AgentBase) CheckingMenuRefsEnter(e *am.Event) bool

func (*AgentBase) CheckingMenuRefsState added in v0.5.0

func (a *AgentBase) CheckingMenuRefsState(e *am.Event)

func (*AgentBase) ConfigBase added in v0.4.0

func (a *AgentBase) ConfigBase() *shared.Config

func (*AgentBase) ConfigUpdateEnter added in v0.5.0

func (a *AgentBase) ConfigUpdateEnter(e *am.Event) bool

func (*AgentBase) ConfigUpdateState added in v0.5.0

func (a *AgentBase) ConfigUpdateState(e *am.Event)

func (*AgentBase) DBBase added in v0.5.0

func (a *AgentBase) DBBase() *sql.DB

func (*AgentBase) DBG added in v0.5.0

func (a *AgentBase) DBG() *debugger.Debugger

func (*AgentBase) DBHistory added in v0.5.0

func (a *AgentBase) DBHistory() *sql.DB

func (*AgentBase) ExceptionState added in v0.5.0

func (a *AgentBase) ExceptionState(e *am.Event)

func (*AgentBase) Gemini added in v0.4.0

func (a *AgentBase) Gemini() []*shared.GeminiClient

func (*AgentBase) Hist added in v0.4.0

func (a *AgentBase) Hist() (amhist.MemoryApi, error)

func (*AgentBase) HistMem added in v0.4.0

func (a *AgentBase) HistMem() *amhist.Memory

func (*AgentBase) HistSQLite added in v0.4.0

func (a *AgentBase) HistSQLite() *amhistg.Memory

func (*AgentBase) HistoryDBReadyEnd added in v0.4.0

func (a *AgentBase) HistoryDBReadyEnd(e *am.Event)

func (*AgentBase) HistoryDBStartingState added in v0.4.0

func (a *AgentBase) HistoryDBStartingState(e *am.Event)

func (*AgentBase) Init added in v0.4.0

func (a *AgentBase) Init(
	agentImpl shared.AgentAPI, cfg *shared.Config, logArgs am.LogArgsMapperFn, groups any, states am.States, args any,
) error

func (*AgentBase) InterruptedState added in v0.4.0

func (a *AgentBase) InterruptedState(e *am.Event)

func (*AgentBase) Log added in v0.4.0

func (a *AgentBase) Log(txt string, args ...any)

Log is an slog logger. It can optionally pipe log entries into the machine log.

func (*AgentBase) LogErr added in v0.5.0

func (a *AgentBase) LogErr(msg string, err error, args ...any)

func (*AgentBase) Logger added in v0.4.0

func (a *AgentBase) Logger() *slog.Logger

func (*AgentBase) Mach added in v0.4.0

func (a *AgentBase) Mach() *am.Machine

func (*AgentBase) OpenAI added in v0.4.0

func (a *AgentBase) OpenAI() []*shared.OpenAIClient

func (*AgentBase) Output added in v0.4.0

func (a *AgentBase) Output(txt string, from shared.From) am.Result

Output is a sugar for adding a [schema.AgentBaseStatesDef.Msg] mutation.

func (*AgentBase) PromptEnd added in v0.4.0

func (a *AgentBase) PromptEnd(e *am.Event)

func (*AgentBase) PromptEnter added in v0.4.0

func (a *AgentBase) PromptEnter(e *am.Event) bool

func (*AgentBase) PromptState added in v0.4.0

func (a *AgentBase) PromptState(e *am.Event)

func (*AgentBase) QueriesBase added in v0.4.0

func (a *AgentBase) QueriesBase() *sqlc.Queries

func (*AgentBase) RequestedAIState added in v0.5.0

func (a *AgentBase) RequestedAIState(e *am.Event)

func (*AgentBase) RequestedToolState added in v0.5.0

func (a *AgentBase) RequestedToolState(e *am.Event)

func (*AgentBase) RequestingAIState added in v0.5.0

func (a *AgentBase) RequestingAIState(e *am.Event)

func (*AgentBase) RequestingExit added in v0.4.0

func (a *AgentBase) RequestingExit(e *am.Event) bool

func (*AgentBase) RequestingToolState added in v0.5.0

func (a *AgentBase) RequestingToolState(e *am.Event)

func (*AgentBase) ResumeState added in v0.4.0

func (a *AgentBase) ResumeState(e *am.Event)

func (*AgentBase) SetMach added in v0.4.0

func (a *AgentBase) SetMach(m *am.Machine)

func (*AgentBase) Start added in v0.4.0

func (a *AgentBase) Start() am.Result

Start is a sugar for adding a [schema.AgentBaseStatesDef.Start] mutation.

func (*AgentBase) StartEnter added in v0.4.0

func (a *AgentBase) StartEnter(e *am.Event) bool

func (*AgentBase) StartState added in v0.4.0

func (a *AgentBase) StartState(e *am.Event)

func (*AgentBase) Stop added in v0.4.0

func (a *AgentBase) Stop(disposeCtx context.Context) am.Result

func (*AgentBase) Store added in v0.5.0

func (a *AgentBase) Store() *shared.AgentStore

func (*AgentBase) StoryActivate added in v0.5.0

func (a *AgentBase) StoryActivate(e *am.Event, story string) am.Result

func (*AgentBase) StoryDeactivate added in v0.5.0

func (a *AgentBase) StoryDeactivate(e *am.Event, story string) am.Result

func (*AgentBase) UIMsgEnter added in v0.5.0

func (a *AgentBase) UIMsgEnter(e *am.Event) bool

func (*AgentBase) ValFile added in v0.5.0

func (a *AgentBase) ValFile(ctx context.Context, name string, val any, enc string)

TODO enc enum

type Document

type Document struct {
	// contains filtered or unexported fields
}

func NewDocument

func NewDocument(title string, content ...string) *Document

func (*Document) AddPart

func (d *Document) AddPart(parts ...string) *Document

func (*Document) AddToPrompts added in v0.2.0

func (d *Document) AddToPrompts(prompts ...PromptApi)

func (*Document) Clear

func (d *Document) Clear() *Document

func (*Document) Clone

func (d *Document) Clone() Document

func (*Document) Parts

func (d *Document) Parts() []string

func (*Document) Title

func (d *Document) Title() string

type Prompt

type Prompt[P any, R any] struct {
	Conditions   string
	Steps        string
	Result       string
	SchemaParams P
	SchemaResult R
	State        string
	A            shared.AgentBaseAPI
	// number of previous messages to include
	HistoryMsgLen int
	Msgs          []*PromptMsg
	// contains filtered or unexported fields
}

func NewPrompt

func NewPrompt[P any, R any](agent shared.AgentBaseAPI, state, condition, steps, results string) *Prompt[P, R]

func (*Prompt[P, R]) AddDoc added in v0.2.0

func (p *Prompt[P, R]) AddDoc(doc *Document)

AddDoc adds a document into the system prompt.

func (*Prompt[P, R]) AddTool

func (p *Prompt[P, R]) AddTool(tool ToolApi)

AddTool registers a SECAI TOOL which then exports it's documents into the system prompt. This is different from an AI tool.

func (*Prompt[P, R]) Conversation added in v0.4.0

func (p *Prompt[P, R]) Conversation() (*instrc.Conversation, string)

Conversation will create a conversation with history and system prompt, return sys prompt on the side.

func (*Prompt[P, R]) Exec added in v0.4.0

func (p *Prompt[P, R]) Exec(e *am.Event, params P) (*R, error)

func (*Prompt[P, R]) GenSysPrompt added in v0.4.0

func (p *Prompt[P, R]) GenSysPrompt() string

GenSysPrompt generates a system prompt.

func (*Prompt[P, R]) HistClean added in v0.4.0

func (p *Prompt[P, R]) HistClean()

type PromptApi

type PromptApi interface {
	AddTool(tool ToolApi)
	AddDoc(doc *Document)

	GenSysPrompt() string
	Conversation() (*instrc.Conversation, string)
	HistClean()
}

type PromptMsg added in v0.4.0

type PromptMsg struct {
	From    instrc.Role
	Content string
}

type PromptSchemaless

type PromptSchemaless = Prompt[any, any]

type S

type S = am.S

type SlogWriter added in v0.5.0

type SlogWriter struct {
	Logger *slog.Logger
	Level  slog.Level
}

func (*SlogWriter) Write added in v0.5.0

func (w *SlogWriter) Write(p []byte) (n int, err error)

type Tool

type Tool struct {
	Doc *Document
	// contains filtered or unexported fields
}

func NewTool

func NewTool(
	agent shared.AgentBaseAPI, idSuffix, title string, toolStates am.S, toolSchema am.Schema,
) (*Tool, error)

func (*Tool) Mach

func (t *Tool) Mach() *am.Machine

func (*Tool) SetMach

func (t *Tool) SetMach(m *am.Machine)

type ToolApi

type ToolApi interface {
	Mach() *am.Machine
	SetMach(*am.Machine)
	Document() *Document
}

Directories

Path Synopsis
Package agent_llm is a base agent extended with common LLM prompts.
Package agent_llm is a base agent extended with common LLM prompts.
schema
Package schema contains a stateful schema-v2 for AgentLLM.
Package schema contains a stateful schema-v2 for AgentLLM.
db
examples
cook
Package cook is a recipe-choosing and cooking agent with a gen-ai character.
Package cook is a recipe-choosing and cooking agent with a gen-ai character.
cook/cmd command
research
Package research is a port of atomic-agents/deepresearch to secai.
Package research is a port of atomic-agents/deepresearch to secai.
research/cmd command
scripts
gen_sql_schema command
Package schema contains a stateful schema-v2 for AgentBase, Mem, and Tool.
Package schema contains a stateful schema-v2 for AgentBase, Mem, and Tool.
tools
tui
web
browser/cmd command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL