secai

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jan 18, 2026 License: GPL-3.0 Imports: 33 Imported by: 0

README

AI-gent Workflows

AI-gent Workflows (aka secai) is a platform for AI Agents with a local reasoning layer. It's implemented on top of a unified state graph and makes a solid foundation for complex, proactive, and long-lived AI Agents with deep and structured memory. It offers a dedicated set of devtools and is written in the Go programming language. By having a graph-based flow, secai allows for precise behavior modeling of agents, including interruptions and fault tolerance.

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.

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.

Features

  • multi-prompt agency
    • 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)
  • offer list / menu
  • prompt history
    • in SQL (embedded SQLite)
    • in JSONL (stdout)
  • proactive stories with actors
  • LLM triggers (orienting)
    • on prompts and timeouts
  • dynamic flow graph for the memory
    • LLM creates an actionable state-machine
  • UI components
    • chat
    • stories
    • clock
  • platforms
    • SSH (all platforms)
    • Desktop PWA (all platforms)
    • Mobile PWA (WIP)
Goals
  • precision
  • traceability
  • privacy
Goals
  • precision
  • traceability
  • privacy
Devtools

The following devtools are for the agent, the agent's dynamic memory, and tools (all of which are the same type of state machine).

  • REPL & CLI
  • TUI debugger (+dashboards)
  • automatic diagrams (D2 SVGs)
  • automatic observability (Prometheus, Grafana, Jaeger)
Tools

Implementation

  • pure Golang
  • typesafe state-machine and prompt schemas
  • asyncmachine-go for graphs and control flow
  • instructor-go for the LLM layer
    • OpenAI (DeepSeek, LMStudio), Gemini, Anthropic, Cohere
  • invopop/jsonschema for JSON schemas
  • network transparency (aRPC, debugger, REPL)
  • structured concurrency (multigraph-based)
  • cview, and asciigraph for UIs
Components
  • 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
  • Document
    • title
    • content

Comparison

Feature AI-gent Workflows AtomicAgents
Model unified state graph BaseAgent class
Debugger multi-client with time travel X
Diagrams customizable level of details X
Observability logging & Grafana & Otel X
REPL & CLI network-based X
History state-based and prompt-based prompt-based
Pkg manager Golang in-house
Control Flow declarative & fault tolerant imperative
CLI bubbletea, lipgloss rich
TUI tview, cview textual
Go vs Python
  • just works, batteries included, no magic
  • 1 package manager vs 4
  • single binary vs interpreted multi-file source
  • coherent static typing vs maybe
  • easy & stable vs easy
  • no ecosystem fragmentation
  • million times faster /s
  • relevant xkcd

Try It

Unlike Python apps, you can start it with a single command:

  • Download a binary release (Linux, MacOS, Windows)
  • Set an API key in config.kdl
  • Run ./aigent-cook and copy-paste-run the SSH line into another terminal
  • You'll see files being created in ./tmp
AI-gent Cook v0.4.0

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

Log:
$ tail -f tmp/cook/cook.jsonl -n 100 | fblog -d -x msg -x time -x level

REPL:
$ ./arpc --dir tmp/cook

https://ai-gents.work

Examples

Code snippets from /examples/research (ported from AtomicAgents). Both the state and prompt schemas are pure and debuggable Golang code.

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

    CheckingInfo string
    NeedMoreInfo string

    SearchingLLM string
    SearchingWeb string
    Scraping     string

    Answering string
    Answered  string

    *ss.AgentStatesDef
}

// ResearchGroupsDef contains all the state groups Research state machine.
type ResearchGroupsDef struct {
    Info    S
    Search  S
    Answers S
}

// ResearchSchema represents all relations and properties of ResearchStates.
var ResearchSchema = SchemaMerge(
    // inherit from Agent
    ss.AgentSchema,

    am.Schema{

        // Choice "agent"
        ssR.CheckingInfo: {
            Require: S{ssR.Start, ssR.Prompt},
            Remove:  sgR.Info,
        },
        ssR.NeedMoreInfo: {
            Require: S{ssR.Start},
            Add:     S{ssR.SearchingLLM},
            Remove:  sgR.Info,
        },

        // Query "agent"
        ssR.SearchingLLM: {
            Require: S{ssR.NeedMoreInfo, ssR.Prompt},
            Remove:  sgR.Search,
        },
        ssR.SearchingWeb: {
            Require: S{ssR.NeedMoreInfo, ssR.Prompt},
            Remove:  sgR.Search,
        },
        ssR.Scraping: {
            Require: S{ssR.NeedMoreInfo, ssR.Prompt},
            Remove:  sgR.Search,
        },

        // Q&A "agent"
        ssR.Answering: {
            Require: S{ssR.Start, ssR.Prompt},
            Remove:  SAdd(sgR.Info, sgR.Answers),
        },
        ssR.Answered: {
            Require: S{ssR.Start},
            Remove:  SAdd(sgR.Info, sgR.Answers, S{ssR.Prompt}),
        },
    })

var sgR = am.NewStateGroups(ResearchGroupsDef{
    Info:    S{ssR.CheckingInfo, ssR.NeedMoreInfo},
    Search:  S{ssR.SearchingLLM, ssR.SearchingWeb, ssR.Scraping},
    Answers: S{ssR.Answering, ssR.Answered},
})
Prompt Schema
func NewCheckingInfoPrompt(agent secai.AgentApi) *secai.Prompt[ParamsCheckingInfo, ResultCheckingInfo] {
    return secai.NewPrompt[ParamsCheckingInfo, ResultCheckingInfo](
        agent, ssR.CheckingInfo, `
            - You are a decision-making agent that determines whether a new web search is needed to answer the user's question.
            - Your primary role is to analyze whether the existing context contains sufficient, up-to-date information to
            answer the question.
            - You must output a clear TRUE/FALSE decision - TRUE if a new search is needed, FALSE if existing context is
            sufficient.
        `, `
            1. Analyze the user's question to determine whether or not an answer warrants a new search
            2. Review the available web search results 
            3. Determine if existing information is sufficient and relevant
            4. Make a binary decision: TRUE for new search, FALSE for using existing context
        `, `
            Your reasoning must clearly state WHY you need or don't need new information
            If the web search context is empty or irrelevant, always decide TRUE for new search
            If the question is time-sensitive, check the current date to ensure context is recent
            For ambiguous cases, prefer to gather fresh information
            Your decision must match your reasoning - don't contradict yourself
        `)
}

// CheckingInfo (Choice "agent")

type ParamsCheckingInfo struct {
    UserMessage  string
    DecisionType string
}

type ResultCheckingInfo struct {
    Reasoning string `jsonschema:"description=Detailed explanation of the decision-making process"`
    Decision  bool   `jsonschema:"description=The final decision based on the analysis"`
}

Read the schema file in full.

Screenshots

Screenshots User Demo
Intro AI-gent Cook Debugger 1 Debugger 2 Memory & Stories
User Interfaces Outro      
     
State Schema
Screenshots Platform Demo
SVG graph am-dbg Grafana Jaeger REPL
SQL IDE Bash Prompts  
 
State Schema
Screenshots Dashboards
Dashboard 1
Dashboard 2

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 in 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 dev stuff cp .env.template .env
  10. configure the bot $EDITOR config.kdl

User Interfaces

Several TUIs with dedicated UI states are included in /tui:

Chat TUI
  • senders & msgs scrollable view with links
  • multiline prompt with blocking and progress
  • send / stop button
Stories TUI
  • list of stories with activity status (non-actionable)
  • dynamic buttons and progress bars (actionable)
Clockmoji TUI

Bash Scripts

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 ParseArgs = shared.ParseArgs
View Source
var Pass = shared.Pass

Functions

func ToolAddToPrompts

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

Types

type A added in v0.2.0

type A = shared.A

type AgentAPI added in v0.2.0

type AgentAPI interface {
	Output(txt string, from shared.From) am.Result

	Mach() *am.Machine
	Hist() (amhist.MemoryApi, error)
	HistMem() *amhist.Memory
	HistSQLite() *amhistg.Memory
	HistBBolt() *amhistbb.Memory

	ConfigBase() *shared.Config
	OpenAI() []*OpenAIClient
	Gemini() []*GeminiClient

	Start() am.Result
	Stop(disposeCtx context.Context) am.Result
	Log(msg string, args ...any)
	Err(msg string, err error, args ...any)
	Logger() *slog.Logger

	QueriesBase() *sqlc.Queries
	HistoryStates() am.S
}

AgentAPI is the top-level public API for all agents to implement.

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
	// Messages
	Msgs []*shared.Msg
	// 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) 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) CheckingOfferRefsEnter added in v0.4.0

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

func (*AgentBase) CheckingOfferRefsState added in v0.4.0

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

func (*AgentBase) ConfigBase added in v0.4.0

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

func (*AgentBase) Err added in v0.4.0

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

func (*AgentBase) Gemini added in v0.4.0

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

func (*AgentBase) Hist added in v0.4.0

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

func (*AgentBase) HistBBolt added in v0.4.0

func (a *AgentBase) HistBBolt() *amhistbb.Memory

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) HistoryStates added in v0.4.0

func (a *AgentBase) HistoryStates() am.S

func (*AgentBase) Init added in v0.4.0

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

Init initializes the AgentLLM and returns an error. It does not block.

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 will push a log entry to Logger as Info() and optionally the machine log with SECAI_LOG_AM. Log accepts the same convention of arguments as slog.Info.

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) MsgEnter added in v0.4.0

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

func (*AgentBase) OpenAI added in v0.4.0

func (a *AgentBase) OpenAI() []*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) RequestingExit added in v0.4.0

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

func (*AgentBase) RequestingLLMEnd added in v0.4.0

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

func (*AgentBase) RequestingLLMEnter added in v0.4.0

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

func (*AgentBase) RequestingLLMExit added in v0.4.0

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

func (*AgentBase) RequestingToolEnd added in v0.4.0

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

func (*AgentBase) RequestingToolEnter added in v0.4.0

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

func (*AgentBase) RequestingToolExit added in v0.4.0

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

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

type AgentInit added in v0.4.0

type AgentInit interface {
	Init(
		agentImpl AgentAPI, cfg *shared.Config, logArgs am.LogArgsMapperFn, groups any, states am.States, args any,
	) error
}

AgentInit is an init func for extendable agents (non-top level ones).

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 GeminiClient added in v0.4.0

type GeminiClient struct {
	Cfg *shared.ConfigAIGemini
	C   *instr.InstructorGoogle
}

type OpenAIClient added in v0.4.0

type OpenAIClient struct {
	Cfg *shared.ConfigAIOpenAI
	C   *instr.InstructorOpenAI
}

type Prompt

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

func NewPrompt

func NewPrompt[P any, R any](agent AgentAPI, 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 Tool

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

func NewTool

func NewTool(
	agent AgentAPI, idSuffix, title string, states am.S, stateSchema 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 deepresearch is a port of atomic-agents/deepresearch to secai.
Package deepresearch is a port of atomic-agents/deepresearch to secai.
research/cmd 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.
scripts
gen_sql_schema command
tools
tui

Jump to

Keyboard shortcuts

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