A lightweight AI Agent framework built on Elixir/OTP, supporting Tool Calling, multi-session management, and skill extensions.
LightAgent is an AI Agent framework developed in Elixir with the following features:
- Multi-Session Management: Create, switch, pause, resume, and delete multiple independent sessions
- Session Persistence: Session history is automatically saved to the filesystem, supporting resume from breakpoints
- CLI Interface: Rich command-line interface with various interactive commands
- Tool Calling Support: Compatible with OpenAI Function Calling protocol
- Parameter Validation: Type-safe tool parameter validation based on Ecto Schema
- Dual-Type Skill System:
- Code-Based Skills: Defined through Elixir modules, registered at compile time
- FS-Based Skills: Dynamically loaded through the filesystem, supporting runtime extensions
- Token Usage Tracking: Real-time tracking and statistics of Token usage
- Secure Tool Execution: Only write/delete/command-execution tools require interactive approval
- Sequential Tool Execution: Tool calls are executed one by one in model-returned order
- Plan Mode: Supports on/off/progress/apply workflow; in drafting stage it only iterates plan, in apply stage it executes subtasks and tracks progress
┌──────────────────────────────────────────────────────────────┐
│ LightAgent.Application │
│ (OTP Application) │
├──────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ LightAgent.Core.Worker │ │
│ │ (GenServer) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ Session Management │ │ │
│ │ │ ┌──────────────┐ ┌───────────────────┐ │ │ │
│ │ │ │ SessionServer│ │ SessionMemoryStore│ │ │ │
│ │ │ │ (Per Session)│ │ (Persistence) │ │ │ │
│ │ │ └──────────────┘ └───────────────────┘ │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ LLM & Usage │ │ │
│ │ │ ┌───────────┐ ┌──────────────────────┐ │ │ │
│ │ │ │ LLM │ │ Usage Tracking │ │ │ │
│ │ │ │ (API Call)│ │ (Token Statistics) │ │ │ │
│ │ │ └───────────┘ └──────────────────────┘ │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ LightAgent.Core.Skill.Runner │ │
│ │ (Skill Executor) │ │
│ │ │ │
│ │ ┌──────────────────┐ ┌────────────────────────────┐ │ │
│ │ │ CodeBasedSkill │ │ FsBasedSkill │ │ │
│ │ │ (Compile-time) │ │ (Runtime Loading) │ │ │
│ │ │ │ │ │ │ │
│ │ │ ┌──────────────┐ │ │ ┌────────────────────────┐ │ │ │
│ │ │ │ ToolArgs │ │ │ │ LoadFsSkill │ │ │ │
│ │ │ │ Validator │ │ │ │ (Dynamic Loading) │ │ │ │
│ │ │ └──────────────┘ │ │ └────────────────────────┘ │ │ │
│ │ └──────────────────┘ └────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ CLI Interface │ │
│ │ ┌───────────────┐ ┌──────────────┐ ┌────────────┐ │ │
│ │ │CommandRouter │ │ InputReader │ │StatusFormat│ │ │
│ │ └───────────────┘ └──────────────┘ └────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
worker.ex is the core controller of the Agent:
- Runs as a GenServer, maintaining Agent state
- Manages the lifecycle of multiple sessions
- Coordinates LLM calls, session management, and tool execution
- Implements recursive execution loop until task completion
Adopts a dynamic session architecture:
- SessionServer: Independent GenServer for each session, maintaining session state and history
- SessionMemoryStore: Responsible for persistent storage of session history using Markdown format
- SessionSupervisor: Dynamic supervisor managing all session processes
- SessionMemoryCompactor: Periodically compacts session history to optimize storage space
Session Features:
- Support for creating, switching, pausing, resuming, and deleting sessions
- Session history automatically persisted to
agent/session_memory/directory - Support for resume from breakpoints, automatic session recovery after application restart
Supports two types of skills:
Code-Based Skills
Defined through Elixir modules using Ecto Schema for parameters:
defmodule LightAgent.Skills.Location do
use LightAgent.Core.Skill.CodeBasedSkill
defmodule GetLocationParams do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
embedded_schema do
field(:city, :string)
end
def changeset(params) do
%__MODULE__{}
|> cast(params, [:city])
|> validate_required([:city])
end
def required_fields, do: [:city]
end
@doc "Get latitude and longitude for a specified city"
deftool(:get_location, schema: GetLocationParams)
@impl true
def exec(:get_location, %{"city" => city}) do
# Implementation logic
end
endFS-Based Skills
Defined through the filesystem, supporting dynamic extensions:
agent/skills/
└── greet_skill/
├── SKILL.md # Skill description and usage instructions
└── scripts/
└── greet.js # Implementation script
Parameter Validation
Uses Ecto Changeset for parameter validation:
- Type checking
- Required field validation
- Custom validation rules
- Automatic JSON Schema generation
Provides rich command-line interaction:
-
Startup command:
mix light_agent.chat -
Auto session bootstrap behavior:
- if only
initexists, create and switch to a new session - if exactly one historical session exists besides
init, auto-switch to it - otherwise restore existing sessions and keep current one
- if only
-
/help- Display help panel -
/new- Create and switch to a new session -
/sessions- List all sessions -
/pause- Pause current session -
/switch <id>- Switch to specified session -
/resume <id>- Resume specified session -
/delete <id>- Delete specified session -
/history- Display current session history -
/usage- Display Token usage statistics -
/plan on|off- Enter/exit plan mode -
/plan progress- Show current plan status (drafting|ready|applying|completed) and subtask progress -
/plan apply- Execute the drafted plan (auto-exits plan mode when completed) -
In plan mode before apply, regular user messages are used to iteratively draft/update the plan (tool execution is blocked)
-
In apply/completed stage, tool calls are no longer treated as drafting-stage blocked calls
-
/exit- Exit program
Real-time tracking and statistics of Token usage:
- Prompt tokens
- Completion tokens
- Total tokens
- Steps count
- Missing usage steps
User Input
│
▼
┌─────────────┐
│ Worker │
└─────────────┘
│
▼
┌──────────────────┐
│ SessionServer │
│ (Current Session)│
└──────────────────┘
│
▼
┌─────────────┐ ┌──────────────┐
│ History │────►│ LLM.call() │
└─────────────┘ └──────────────┘
│
▼
┌───────────┐
│Tool Calls?│
└───────────┘
│ │
Yes No
│ │
▼ ▼
┌──────────────┐ ┌──────────┐
│ Skill.Runner │ │ Return │
│ Validate Args│ │ Result │
└──────────────┘ └──────────┘
│
▼
┌──────────────────────────────┐
│ Plan Gate (4 statuses) │
│ drafting/ready => block tool │
│ applying/completed => allow │
└──────────────────────────────┘
│
▼
┌──────────────┐
│ Security │
│ Confirmation │
│ (Prompt UI) │
└──────────────┘
│
▼
┌──────────────┐
│ Execute Tools│
│ (Sequential) │
└──────────────┘
│
▼
┌──────────────┐
│ Update │
│ History │
│ Persist │
└──────────────┘
│
▼
Recursive Call
Flow legend (matches /plan progress):
drafting: collecting user feedback and iterating plan JSONready: valid plan available, waiting for/plan applyapplying: executing subtasks and advancing task statusescompleted: all subtasks done; CLI auto-switches back to normal mode
Create a .env file:
API_KEY=your-api-key
BASE_URL=https://api.openai.com/v1/chat/completions
MODEL=gpt-4Configuration files are automatically loaded (in development and test environments).
Method 1: Using CLI Interface
# Start interactive CLI
mix light_agent.chatMethod 2: Programmatic Way
# Start application
{:ok, _pid} = Application.ensure_all_started(:light_agent)
# Run Agent
result = LightAgent.Core.Worker.run_agent("What's the weather in Beijing today?")
# Step-by-step execution
{:running, tool_results, usage} = LightAgent.Core.Worker.run_agent_step("Hello")
{:done, content, usage} = LightAgent.Core.Worker.run_agent_step()# Enable/disable plan mode for current session
:ok = LightAgent.Core.Worker.set_mode(:plan)
:ok = LightAgent.Core.Worker.set_mode(:normal)
# Inspect and maintain plan
plan = LightAgent.Core.Worker.current_plan()
:ok = LightAgent.Core.Worker.update_plan(%{"title" => "Demo", "tasks" => [%{"id" => "T1", "text" => "step"}]})
:ok = LightAgent.Core.Worker.apply_plan()
progress = LightAgent.Core.Worker.plan_progress()Plan notes:
- Plan statuses:
drafting -> ready -> applying -> completed. - In
plan + drafting/readyphase, tool execution is blocked (plan_mode_blocked). - In
plan + applying/completedphase, tool calls are not blocked by drafting gate. - During
applying, subtask state advances each turn (in_progress -> done, and next pending task is promoted). - Sensitive tool confirmation is only required for:
run_command,write_file,delete_file,remove_file.
# Create new session
{:ok, session_id} = LightAgent.Core.Worker.new_session()
# List all sessions
sessions = LightAgent.Core.Worker.list_sessions()
# [%{id: "init", status: :active, current: true}, ...]
# Switch session
:ok = LightAgent.Core.Worker.switch_session(session_id)
# Pause current session
{:ok, session_id} = LightAgent.Core.Worker.pause_current_session()
# Resume session
:ok = LightAgent.Core.Worker.resume_session(session_id)
# Delete session
{:ok, current_session_id} = LightAgent.Core.Worker.delete_session(session_id)
# View current session history
history = LightAgent.Core.Worker.current_history()
# View Token usage statistics
usage = LightAgent.Core.Worker.current_token_usage()
# %{
# prompt_tokens: 1000,
# completion_tokens: 500,
# total_tokens: 1500,
# steps: 5,
# missing_usage_steps: 0
# }Create a new skill module:
# lib/light_agent/skills/my_skill.ex
defmodule LightAgent.Skills.MySkill do
@moduledoc "Skill description"
use LightAgent.Core.Skill.CodeBasedSkill
defmodule MyFunctionParams do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
embedded_schema do
field(:param1, :string)
field(:param2, :integer)
end
def changeset(params) do
%__MODULE__{}
|> cast(params, [:param1, :param2])
|> validate_required([:param1])
|> validate_number(:param2, greater_than: 0)
end
def required_fields, do: [:param1]
end
@doc "Tool description"
deftool(:my_function, schema: MyFunctionParams)
@impl true
def exec(:my_function, %{"param1" => value, "param2" => num}) do
# Implementation logic
"Result: #{value}, #{num}"
end
endThen register in runner.ex:
def list_skills do
[
LightAgent.Skills.Location,
LightAgent.Skills.Weather,
LightAgent.Skills.Filesystem,
LightAgent.Skills.RunCommand,
LightAgent.Skills.LoadFsSkill,
LightAgent.Skills.MySkill # Add new skill
]
endCreate skill in agent/skills/ directory:
agent/skills/
└── my_skill/
├── SKILL.md
└── scripts/
└── main.py
SKILL.md Format:
---
name: my_skill
description: Skill description
---
# My Skill
## Usage Steps
1. Prepare parameters
2. Run script `python scripts/main.py <args>`
3. Process output
## Example
\`\`\`bash
python scripts/main.py "hello"
\`\`\`Use LoadFsSkill tool to dynamically load:
LightAgent.Skills.LoadFsSkill.exec(:load_fs_skill, %{"skill_name" => "my_skill"})get_location(city)- Get city latitude and longitude
get_weather(latitude, longitude)- Get weather for specified location
read_file(path)- Read file contentwrite_file(path, content)- Write to file
run_command(command)- Execute shell command
load_fs_skill(skill_name)- Dynamically load filesystem-based skill
# Direct LLM call
messages = [
%{"role" => "system", "content" => "You are an assistant"},
%{"role" => "user", "content" => "Hello"}
]
tools = LightAgent.Core.Skill.Runner.build_tools_schema()
{:ok, response} = LightAgent.Core.LLM.call(messages, tools)Configure in config/config.exs:
import Config
config :light_agent, :agent_external_root, "agent"Configure in .env file:
API_KEY=your-api-key
BASE_URL=https://api.openai.com/v1/chat/completions
MODEL=gpt-4Configure Agent context in agent/config/ directory:
SOUL.md- Agent role and personality definitionUSER.md- User information and preferencesMEMORY.md- Long-term memory and knowledge baseAGENT.md- Agent capabilities and limitations
These files are automatically loaded into the session's system prompt.
- Elixir ~> 1.19
- Req ~> 0.5.17 (HTTP Client)
- Jason ~> 1.4 (JSON Parsing)
- Ecto ~> 3.12 (Data Validation)
- EnvLoader ~> 0.1.0 (Environment Variable Loading)
- Prompt ~> 0.10.1 (Interactive approval prompts)
# Get dependencies
mix deps.get
# Configure environment variables
cp .env.example .env
# Edit .env file and fill in your API configuration
# Start interactive CLI
mix light_agent.chat
# Or run Agent directly in IEx
iex -S mix
iex> LightAgent.Core.Worker.run_agent("Hello")light-agent/
├── lib/
│ └── light_agent/
│ ├── application.ex # OTP application entry
│ ├── cli/
│ │ ├── command_router.ex # CLI command router (+ /plan commands)
│ │ ├── input_reader.ex # Input reader
│ │ ├── prompts.ex # Prompt-based interactive confirmations
│ │ └── status_formatter.ex # Status formatter
│ ├── core/
│ │ ├── LLM.ex # LLM API calls
│ │ ├── worker.ex # Agent worker
│ │ ├── agent_paths.ex # Path management
│ │ ├── session_server.ex # Session server
│ │ ├── session_supervisor.ex # Session supervisor
│ │ ├── session_memory_store.ex # Session memory store
│ │ ├── session_memory_compactor.ex # Session memory compactor
│ │ ├── worker/
│ │ │ ├── session.ex # Session helpers
│ │ │ └── usage.ex # Token usage statistics
│ │ └── skill/
│ │ ├── code_based_skill.ex # Code-based skill macro
│ │ ├── fs_based_skill.ex # Filesystem-based skill
│ │ ├── runner.ex # Skill executor
│ │ ├── tool_args_validator.ex # Parameter validator
│ │ └── schema_json_schema.ex # Schema converter
│ └── skills/
│ ├── filesystem.ex # File operation skill
│ ├── location.ex # Location query skill
│ ├── run_command.ex # Command execution skill
│ ├── weather.ex # Weather query skill
│ └── load_fs_skill.ex # Dynamic skill loader
├── agent/
│ ├── config/ # Agent configuration directory
│ │ ├── SOUL.md # Agent role
│ │ ├── USER.md # User information
│ │ ├── MEMORY.md # Long-term memory
│ │ └── AGENT.md # Agent capabilities
│ ├── session_memory/ # Session history storage
│ │ ├── session-init.md
│ │ └── session-<uuid>.md
│ └── skills/ # Filesystem-based skills directory
│ └── greet_skill/
│ ├── SKILL.md
│ └── scripts/
│ └── greet.js
├── config/
│ ├── config.exs # Application configuration
│ └── runtime.exs # Runtime configuration
├── test/ # Test files
├── mix.exs # Project definition
└── README.md
The project includes comprehensive test cases:
# Run all tests
mix test
# Run specific test
mix test test/light_agent/core/worker/session_test.exs
# Run tests with detailed output
mix test --traceTest coverage:
- Session management (Session, SessionServer, SessionMemoryStore)
- Skill system (CodeBasedSkill, ToolArgsValidator, SchemaJsonSchema)
- Built-in skills (Location, Weather, Filesystem, RunCommand)
- Usage statistics (Usage)
- Path management (AgentPaths)
- Session Compression Optimization: Implement smarter session history compression strategies
- Streaming Output: Support LLM streaming responses for better user experience
- Multi-modal Support: Support image, audio, and other multi-modal inputs
- Skill Marketplace: Build a skill sharing and download platform
- Plugin System: Improve execution environment and security isolation for FS-Based Skills
- Distributed Support: Support multi-node deployment and session synchronization
- Monitoring and Logging: Enhance monitoring and logging systems for easier debugging and optimization
MIT