make it fancy
This commit is contained in:
+151
@@ -0,0 +1,151 @@
|
||||
# agent0 Architecture Outline
|
||||
|
||||
## Core Overview
|
||||
agent0 is an **agentic TUI** built with Babashka (Clojure) that interacts with local LLMs via Ollama. It features an Elm-architecture UI with background agent processing.
|
||||
|
||||
---
|
||||
|
||||
## File Structure & Responsibilities
|
||||
|
||||
| File | Responsibility |
|
||||
|------|------|
|
||||
| `app.clj` | TUI layer - Elm architecture, event handling, rendering |
|
||||
| `core.clj` | Agent brain - LLM calls, tool execution, async loop |
|
||||
| `context.clj` | Project context loading, skill parsing/expansion |
|
||||
| `markdown.clj` | Markdown → ANSI-styled terminal output |
|
||||
| `web_search.clj` | Web research subagent configuration |
|
||||
| `clojuredocs.clj` | ClojureDocs integration (not shown, imported) |
|
||||
|
||||
---
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
User Input → Skill Expansion → Agent Loop → LLM → Tools → Events → UI
|
||||
```
|
||||
|
||||
1. **Input** → Optional skill expansion (`/name args`)
|
||||
2. **Conversation** → Added to history
|
||||
3. **Agent Loop** (async future) → Calls LLM, executes tools, appends results
|
||||
4. **Events** → Pushed to event queue every 100ms
|
||||
5. **UI** → Polls queue and re-renders
|
||||
|
||||
---
|
||||
|
||||
## Key Functions
|
||||
|
||||
### app.clj (TUI Layer)
|
||||
|
||||
| Function | Purpose |
|
||||
|----------|---------|
|
||||
| `view` | Elm view - renders state as hiccup UI |
|
||||
| `update-fn` | Elm update - handles events, user input, agent polling |
|
||||
| `process-agent-event` | Pushes LLM responses to messages list |
|
||||
| `format-messages` | Converts messages to display lines with ANSI styling |
|
||||
| `wrap-line` / `wrap-text` | Word-wrap with ANSI awareness |
|
||||
| `-main` | Entry point - parses args, loads context/skills, starts TUI |
|
||||
|
||||
### core.clj (Agent Brain)
|
||||
|
||||
| Function | Purpose |
|
||||
|----------|---------|
|
||||
| `run-agent-loop!` | Main async loop - iterates: call LLM → execute tools → repeat (max 50) |
|
||||
| `call-llm*` | HTTP POST to Ollama `/api/chat` |
|
||||
| `call-llm` | Wrapper with tool definitions |
|
||||
| `execute-tool` | Runs tool function, extracts diff for UI |
|
||||
| `tool-call-label` | Human-readable label for tool calls (displayed as "⚙") |
|
||||
| `detect-stuck-loop` | Detects infinite loops (exact repeats + research loops) |
|
||||
| `run-subagent!` | Subagent execution (e.g., web_search) |
|
||||
| `delegate` | Tool to invoke subagents |
|
||||
| `skills-tool` | Tool for listing/running skills |
|
||||
| Session persistence: `save-session!`, `load-session`, `new-session-id` | |
|
||||
| Tools: `read-file`, `list-files`, `edit-file`, `create-file`, `run-shell-command`, `glob-files`, `grep-files` | File & shell operations |
|
||||
|
||||
### context.clj (Context & Skills)
|
||||
|
||||
| Function | Purpose |
|
||||
|----------|---------|
|
||||
| `load-project-context` | Loads `.agent0/context.md`, `CLAUDE.md`, etc. |
|
||||
| `load-skills` | Loads skills from `~/.claude/skills/`, `~/.config/agent0/skills.md`, `.agent0/skills.md` |
|
||||
| `parse-skills` | Parses `/name <arg>` templates with `{param}` substitution |
|
||||
| `expand-skill` | Expands skill invocations like `/test --watch` |
|
||||
| `format-skill-list` | Formats skill list for display |
|
||||
|
||||
### markdown.clj (Markdown Rendering)
|
||||
|
||||
| Function | Purpose |
|
||||
|----------|---------|
|
||||
| `render-markdown` | Converts markdown to ANSI-styled lines |
|
||||
| `apply-inline` | Bold, italic, strikethrough, links |
|
||||
| `protect-code-spans` / `restore-code-spans` | Preserves inline code |
|
||||
| `wrap-words` | ANSI-aware word wrapping |
|
||||
|
||||
---
|
||||
|
||||
## Event Types
|
||||
|
||||
```clojure
|
||||
{:type :text :content "..."} ; Assistant response
|
||||
{:type :tool :label "..."} ; Tool being called (displayed as "⚙")
|
||||
{:type :diff :content "..."} ; File edit preview (diff format)
|
||||
{:type :error :message "..."} ; Error message
|
||||
{:type :done :conversation [...]} ; Loop finished
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tool Pattern
|
||||
|
||||
Tools that modify files return `{:message ".." :diff ".."}`:
|
||||
- `:message` → sent to LLM as tool result
|
||||
- `:diff` → pushed as separate `:diff` event to UI
|
||||
|
||||
---
|
||||
|
||||
## Agent Loop Algorithm
|
||||
|
||||
```
|
||||
1. Call LLM with conversation + tool definitions
|
||||
2. If tool_calls in response:
|
||||
a. Push tool labels to UI
|
||||
b. Execute each tool function
|
||||
c. Push diffs to UI (if any)
|
||||
d. Append tool results to conversation
|
||||
e. Repeat (stuck-loop detection at ~3 repeats)
|
||||
3. If no tool_calls:
|
||||
a. Push final assistant text
|
||||
b. Signal :done
|
||||
```
|
||||
|
||||
**Stuck-loop detection:**
|
||||
- Exact repeat: same tool calls 3x in a row → hard stop
|
||||
- Research loop: `web_search` called 3+ times with varying args → inject system nudge, stop at 6
|
||||
|
||||
---
|
||||
|
||||
## State Management
|
||||
|
||||
**Model state (UI):**
|
||||
```clojure
|
||||
{:messages [...] ; Display messages
|
||||
:input "" ; User input field
|
||||
:conversation [...] ; LLM conversation history
|
||||
:event-queue (atom []) ; Background-to-UI events
|
||||
:session-id ... ; For persistence
|
||||
:agent-running? bool ; Spinner state
|
||||
:agent-handle ... ; Future + cancel atom
|
||||
:skills {...} ; Available skills}
|
||||
```
|
||||
|
||||
**Sessions saved to:** `~/.local/share/agent0/sessions/{uuid}.edn`
|
||||
**Logs saved to:** `~/.local/share/agent0/logs/agent-{timestamp}.log`
|
||||
|
||||
---
|
||||
|
||||
## Key Patterns
|
||||
|
||||
1. **Async agent loop** returns `{:future f :cancel! atom}` for interruption
|
||||
2. **TUI uses clojure-tui** hiccup-like syntax with ANSI escape codes
|
||||
3. **Polling UI** checks event queue every 100ms (drains and processes)
|
||||
4. **Dynamic binding** `*log-file*` passes log context to subagents
|
||||
5. **Skills** use `{param}` substitution from `/name <arg>` format
|
||||
Reference in New Issue
Block a user