Astrolabe / ajxpublic /astrolabe
Repository

Agent Core

reference scope internal/agent Verification failed
  • Agent
  • Config
  • Result
  • internal/agent/react.go
  • internal/agent/incremental.go
  • internal/agent/prompts.go
  • State
  • PageState
  • internal/agent/agenttest/reference.go
Relevant source files · 6

The internal/agent package is the engine of astrolabe: it drives an LLM over a code-graph index to produce a verified wiki bundle. One run walks five stages — load the graph, plan the pages, draft each page, verify and re-draft failures, then emit the bundle — and the package doc summarizes exactly this pipeline Pipeline. Everything the CLI exposes is orchestrated through this package's [Agent](Agent Core.md); the surrounding command-line plumbing lives in CLI & Pipeline Orchestration.

How it works

A run is one call to Agent.Run, which opens the graph provider, decides between a full and an incremental build, plans, drafts, verifies, and finally writes the bundle Run. The configuration is collected in Config: it carries the graph provider, the LLM client, page caps, retry counts, prompt budgets, and revision stamping (commit/tag/branch plus forge URL templates) Config. New validates the two required collaborators (a graph.Provider and an llm.Client) and fills in defaults — 50 pages max, 2 retries, and the plan/draft prompt budgets New. The outcome is a Result listing the pages, the verification report, the bundle directory, the build mode, and which slugs were actually re-drafted Result.

Plan. The planner receives a compact JSON digest of the graph — files, node digests, edges — fitted to PlanBudget characters, plus the tool catalogue, and must answer with a single JSON PlanResponse of page entries (title, type, scope, anchor symbols) plan. The architect persona behind it is defined in prompts.go: one page per coherent subsystem plus exactly one overview, with symbols chosen as searchable anchors rather than every member architectSystemPrompt. Oversized plans are silently truncated to MaxPages truncate.

Draft. Each page gets a DraftRequest containing the plan entry, a compact symbol table (no inline sources — the model reads what it needs), the codegraph edges among the page's own symbols as verified material for mermaid diagrams, and the full plan so drafts can link their siblings DraftRequest. A single page failing to draft must not sink the run: draftOrStub substitutes an uncited stub that will fail verification and thereby get a retry-budgeted re-draft, and only cancellation is fatal draftOrStub. The writer persona in the prompt harness forbids JavaDoc-style stub enumeration and prescribes the narrative structure — intro, how-it-works walkthrough, mermaid relationships, reference table writerSystemPrompt, with the completion contract given as a stop hint reactStopDraft.

The ReAct loop. Both stages run on the same multi-round exchange in react.go. The model talks via native function calls on the OpenAI-compatible llm.ToolClient interface; each reply either carries tool calls — executed one by one as execute_tool spans, with results fed back as tool messages — or is a candidate final answer runToolLoop. Rejected answers (empty, truncated, or failing the stage's accept validator, which for the planner means "must parse as the plan JSON") cost a round: the model is told why and asked again rejection handling. The last round drops the tools entirely so models cannot trail off into tool-call markup, and a budget note appended to each tool result tells the model how many lookup rounds remain so it can batch lookups round budget. Individual tool executions go through runToolCall, which wraps each call in a span, validates JSON arguments, and returns errors as text the model can correct runToolCall. The tool catalogue itself and the token/round budgeting are covered in Agent Tools & Budget; the transport this loop assumes is described in ACP Agent Transport and LLM Client Layer.

Verify and re-draft. verifyWithRetries runs the deterministic checker (and, when Config.Review is on, an LLM review pass) over the page set; failed pages are re-drafted with their issues as feedback, bounded by Config.Retries verifyWithRetries. The checker internals live in Verification (Checker & Review).

Emit. The verified pages, the harness report, the graph JSON, and version info go into a wiki.Site written by wiki.Writer, followed by optional Pagefind indexing and the incremental state file emit. The bundle format is the subject of Wiki Bundle & Pages.

Incremental builds

incremental.go makes regeneration cheap. After a full run the agent writes state.json beside the bundle; State records the repo, the commit it was generated from, and per-page entries State. Each PageState tracks the slug, scope, anchor symbols, cited files, last verification status, and the canonical body — kept so forge-pinned citations can be reused verbatim when a source URL template is configured PageState.

On the next run, changedFiles diffs two commits with git diff --name-only -M changedFiles, and computeAffected decides which pages must be re-drafted: any page whose cited files changed, whose scope contains changed files, whose documented symbols left the index, or whose scope vanished computeAffected. The overview is affected whenever any other page is, when route files changed, or when the reference-page count differs from the previous bundle overview rules. draftIncremental then merges reused pages (no LLM call) with freshly drafted ones, and always re-drafts pages whose last verification failed so a red run gets another shot without a commit change draftIncremental.

One subtlety: a re-drafted page may change its slug (a title change). cascadeSlugChanges finds reused pages that still link the old slug and re-drafts them too, running to a bounded fixpoint of three passes; if a cascade re-draft fails, the stale page is kept and left to fail verification cascadeSlugChanges.

Testing without an LLM

The agenttest package ships a reference mock that behaves like a perfect wiki author: it parses astrolabe_stage out of the prompt's JSON blocks, answers plan requests by grouping the digest's symbols per directory into reference pages plus an overview, and drafts pages whose citations always resolve because they are stitched from real docstrings and signatures NewReferenceMock. This lets the whole pipeline — plan, draft, verify, retry — run end-to-end without API keys, as used by the fixtures in Test Fixture (Sample Repo).

Relationships

flowchart LR
    subgraph agent["internal/agent"]
        `Agent` --> `runToolLoop`
        `Agent` --> `draftIncremental`
        `Agent` --> `verifyWithRetries`
        `prompts.go`
        `StateFile`
    end
    `main.go` --> `Agent`
    `Agent` --> `Provider`
    `Agent` --> `ToolClient`
    `runToolLoop` --> `Tool`
    `verifyWithRetries` --> `Checker`
    `verifyWithRetries` --> `Reviewer`
    `Agent` --> `Writer`
    `NewReferenceMock` --> `MockClient`
    `NewReferenceMock` --> `Agent`
    `Agent` --> `telemetry.go`

Reference

Symbol Kind What it does Location
Agent struct Drives the pipeline; holds Config and logger agent.go#L91
Config struct All run knobs: providers, caps, budgets, revision stamping agent.go#L43
Result struct Run outcome: pages, report, bundle dir, mode, redrafted slugs agent.go#L79
Agent.Run method Full pipeline: plan → draft → verify → emit agent.go#L135
Agent.plan method Stage 1: ask the model for the page plan agent.go#L465
Agent.draftOrStub method Stage 2 per page; stubs on failure agent.go#L531
Agent.verifyWithRetries method Stage 3: checker + review, bounded re-drafts agent.go#L683
Agent.runToolLoop method Multi-round ReAct exchange over llm.ToolClient react.go#L47
Agent.runToolCall method Executes one native tool call as a span react.go#L214
agenttest.NewReferenceMock func Deterministic fake author for keyless e2e runs reference.go#L23
changedFiles func Git diff between two commits incremental.go#L14
computeAffected func Which previous pages must be re-drafted incremental.go#L50
Agent.draftIncremental method Merge reused + re-drafted pages incremental.go#L142
Agent.cascadeSlugChanges method Re-draft pages linking renamed slugs incremental.go#L174
architectSystemPrompt const Planner persona and plan contract prompts.go#L15
writerSystemPrompt const Writer persona and page structure rules prompts.go#L26
State struct Incremental-build state written to state.json state.go#L20
PageState struct Per-page inputs tracked for reuse state.go#L28

Generated by astrolabe from commit ecc180b3 (tag 0.4.0).