neuron-explorer
Qwen3.5‑4B 전용 조향형 뉴런 탐색기, 모든 MLP 뉴런을 밀어 결과를 보관·탐색
agentfootprint는 설명 가능한 AI 에이전트를 위한 오픈소스 TypeScript 프레임워크, 컨텍스트·결정·도구 추적 및 재실행 가능
agentfootprint is an open-source TypeScript framework for explainable AI agents. Trace context, decisions, and tools, then replay runs to prove what caused each result.
개발 도구TypeScript · MDXGitHub @footprintjs온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
Context engineering, abstracted. Build AI agents whose every LLM call traces back to what was injected, who triggered it, when, and how it cached. Built on footprintjs
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Your agent gave an answer that looks right — and it's wrong. The logs can't tell you who influenced it. Agentfootprint can.
The explainable AI agent framework for TypeScript: every read, write, decision, and tool call becomes
connected evidence as your agent runs. When something goes wrong, you don't grep logs — you ask.
Build agents — skills, steering, RAG, memory, control flow — and debug them like nothing else.
Why is a query, not a guess.
A real run, replayed — rendered with Story Lens ( npm i agentthinkingui ). Every frame is generated from the run's own trace; ▶ watch it live .README 전문 보기 ↗A tool returned no rows. Before an agent says "there are no incidents", the app needs to know which account was searched, what period the read covered, whether it finished, and what it could not reach. A successful empty query and a failed query support different answers.
For example, the bounded claim is: "The search returned no matching incidents for this account and time window. Email reports were not checked." That is an illustration of scope-aware wording, not a claim that no incident exists anywhere. When the evidence cannot support the requested conclusion, the app should make that uncertainty explicit or withhold the answer.
Agentfootprint supplies the execution record and configurable checks for that decision. Your app defines the comparisons and delivery policy. Tool declarations must describe the query truthfully: recording a source's assertion does not prove the source is correct.
Execution provenance describes what ran and what was supplied. It does not expose the model's internal reasoning or independently establish that an external fact is true. Withholding an answer does not undo tool actions already performed.
Installing the framework does not make every answer verified. These mechanisms have different jobs and must be configured for the claims your app needs to check.
| Mechanism | What it provides | Boundary of the guarantee |
|---|---|---|
absent(), coverage() and .limitsTravelWithTheAnswer() | Explicit absence and declared limits, carried into prose or returned beside typed output. | Preserves declarations; does not prove that the tool described its coverage correctly. |
.answerValidation() | App-authored checks on a typed final answer. Enforce mode buffers drafts and withholds failed or unverified answers; observe mode records the report and permits delivery. | Only the configured checks are judged. The app owns their correctness and the trustworthiness of its evidence. |
.namesAndNumbersFromEvidence() | A deterministic detector for unsupported values, with configurable extraction limits and delivery postures. | Default assist flags; rails refuses. A false sentence assembled from real values can pass. This is not general factual verification. |
Tool.search and typed .negativeClaims() | Composed search scope and library-written statements for cited structured negative claims, retaining gaps and withholding or reporting unsupported claims according to policy. | Opt-in, available but not recommended pending benchmark gates. Accuracy and usefulness remain unmeasured. Depends on truthful app-owned search declarations; other answer fields remain unchecked model text. |
.negativeClaims({ prose: true }) | Bounded heuristic warnings on the model's own negative sentences. |
The links above describe this branch. Features marked unreleased in the capability index are not yet available in the published package; check its availability column and the release notes for your installed version. The basic quick start below does not enable these checks.
These frameworks all support agent building and runtime controls. The useful distinction is what each emphasizes, rather than whether it has guardrails.
| Framework | Emphasis and existing runtime controls |
|---|---|
| LangChain / LangGraph | Model and tool integrations, agent loops and stateful orchestration with persistence and human oversight. LangGraph covers execution; LangChain guardrails can check inputs, outputs and tool calls during a run. |
| Mastra | A TypeScript framework for agent applications, with workflows, memory, development tools, tracing and evaluations. Its input/output processors can validate or transform messages during execution. Overview. |
| Agentfootprint | Agent building organized around FootprintJS execution provenance: connected execution records, declared evidence limits, and configured checks that can govern answer delivery. |
Choose Agentfootprint when a central requirement is to connect an answer to what the agent actually read, the limits of that evidence, and the checks applied before delivery. Skills, tools and orchestration build the agent; the execution record also supports its answer policy. This comparison describes design emphasis, not exclusive capabilities or measured superiority in answer accuracy.
npm install agentfootprint footprintjs
import { Agent, defineTool } from 'agentfootprint';
import { mock } from 'agentfootprint/providers';
const weather = defineTool({
name: 'weather',
description: 'Get current weather for a city.',
inputSchema: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
execute: async ({ city }: { city: string }) => `${city}: 72°F, sunny`,
});
const agent = Agent.create({
provider: mock({
replies: [
{ toolCalls: [{ id: 'weather-1', name: 'weather', args: { city: 'Paris' } }] },
{ content: 'The demo weather tool returned 72°F and sunny for Paris.' },
],
}),
model: 'mock',
})
.system('You answer weather questions using the weather tool.')
.tool(weather)
.build();
const result = await agent.run({ message: 'Weather in Paris?' });
console.log(result); // → "The demo weather tool returned 72°F and sunny for Paris."
The tool runs locally and the mock scripts its call and reply. The temperature is fixture data, not a live weather reading; this example demonstrates wiring, not answer validation. For a configured delivery check, run the mock answer-validation example.
To use a real model, replace the scripted mock with a provider from
agentfootprint/providers — anthropic(...) / openai(...) / bedrock(...) /
gemini(...) / ollama(...) — and configure its model and any required credentials.
The agent and tool wiring stays the same. Every provider, including mock, lives
on that subpath so the main barrel stays free of optional provider SDK imports.
No cloud account? ollama('llama3.2') from agentfootprint/providers runs the same agent against a local model for $0 — the free rung between the mock and the bill. Full recipes: Ollama · OpenAI-compatible endpoints.
Whatever a tool returns is what the model reads next. When that value has limits the model must not lose, return it through one of three helpers — each is the tool's response; nothing is added to the system prompt or the tool's schema:
| helper | use it when | the model reads | the record keeps |
|---|---|---|---|
describedResult({ … }) | rows, a series or relationships from a system of record | the data, with grain, provenance and one not_covered line per gap | the whole envelope (tools.semantics_declared) |
coverage(value, { … }) | any other value that has limits | your value, with what was and was not checked | tools.coverage_declared |
absent({ … }) | nothing matched | what was looked for, where, and that a retry returns the same | tools.absent |
Never wrap one in another. Add .limitsTravelWithTheAnswer() and the declared coverage is appended to the final answer — or, for a typed .outputSchema() answer, returned beside it as data by agent.answerCoverage(). Provenance and grain never are.
import { absent, coverage, describedResult } from 'agentfootprint';
execute: ({ vm, summary }) => {
const { exportedAt, source, runs: all } = backupExport; // your system of record
const runs = all.filter((r) => r.vm === vm);
const ground = { checked: [`every backup job in the export of ${exportedAt}`] };
if (runs.length === 0) return absent({ what: `backup runs for ${vm}`, ...ground }); // nothing matched
if (summary) return coverage(`${vm}: ${runs.length} backup runs`, ground); // a verdict with limits
return describedResult({ // rows from the record
facts: runs.map((r) => ({ entity: vm, day: r.day, ok: r.ok === 1 })),
provenance: { measuredAt: exportedAt, source }, // the time comes from the data
coverage: ground,
});
},
What the model reads from each, exactly: Tools → what execute returns.
Skills, steering, RAG, facts, memory, guardrails — every name for context does one thing: it injects into one of three LLM slots. So we abstracted the injection itself.
The abstraction is three rules:
system, messages, tools — the LLM API surface.That's the whole model: Injection = slot × trigger × cache.
system / messages / tools).| Trigger | Flavor | Fires when | Illustration | Default slot |
|---|---|---|---|---|
always | static | Every iteration | .steering(defineSteering({ id, prompt: 'You are a triage agent…' })) | system |
rule | runtime — predicate | Your rule returns true | .instruction(defineInstruction({ id, activeWhen: s => /price|refund/.test(s.userQuery), prompt })) | system |
on-tool-return | runtime — lifecycle | After a specific tool returns | .instruction(defineInstruction({ id, activeWhen: s => s.lastToolResult?.toolName === 'search', prompt: 'Cite source IDs.' })) | system |
llm-activated | runtime — agent-driven | LLM calls read_skill('id') | .skill(defineSkill({ id: 'refund-policy', description, body })) | system (body) + tools |
3 slots × 4 triggers × N flavors = the entire context-engineering surface.
The recorded run also supports debugging. These examples illustrate different kinds of evidence; a similarity score or recorded dependency alone is not proof of why a model chose an answer.
Q: Were refund_full and refund_partial described similarly?
A: margin 0.02 — ⚠ NARROW: the two tool descriptions read nearly identical
(toolChoiceRecorder — and the catalog lint flags the pair before you ever run)
Q: Why was this loan declined?
A: decision ← [control: "DTI above the 0.43 affordability ceiling"] ← dti 0.52 ← monthlyDebt / income
(decide() evidence + the causal slice — every hop is a real recorded edge)
Q: Did removing a piece of context change the outcome in these reruns?
A: ablating fact 'vip-override' flipped the outcome in 3/3 seeded reruns
(localizeContextBug — evidence from those counterfactual trials)
Q: Does this exported bundle pass its integrity check?
A: verifyAuditBundle → valid: false, brokenAt: #16 — the tampered record, named
(hash-chained audit export, offline verification)
The trace toolpack also lets a debugger model inspect the record: in one run it found a planted bug while reading 9.5% of the trace (self-explain). That is one debugging result, not an answer-accuracy benchmark. Integrity checks establish properties of the exported record; they do not establish that its source data was true.
| Checking answers? | Building an agent? | Inspecting a run? |
|---|---|---|
| Declare the evidence and limits; configure checks and the response to uncertainty. | Typed agents with skills, steering, RAG, memory and control flow. | Recorded dependencies, counterfactual reruns, replay and audit-bundle integrity checks. |
| → Checks and guarantees · → Output contracts | → Quick start · → Build an agent | → Debug · → Exporters & audit bundles |
Build the entire app against in-memory mocks with zero API cost, then swap real infrastructure one boundary at a time.
| Boundary | Dev | Prod |
|---|---|---|
| LLM provider | mock(...) | ollama('<model>') free · anthropic() · openai() · bedrock() |
| Memory store | InMemoryStore | RedisStore · AgentCoreStore |
| MCP | mockMcpClient(...) | mcpClient({ transport }) |
| Cache strategy | NoOpCacheStrategy | auto-selected per provider |
The flowchart, recorders, and tests don't change between dev and prod.
AG-UI frontends (CopilotKit, the AG-UI Dojo, any @ag-ui/client app) all speak one protocol: POST a RunAgentInput, read a stream of typed events. An agentfootprint run is already a stream of typed events, so the only missing piece is the adapter between them — agUi(). Hand it to streamHandler, a plain (request) => Promise<Response> that mounts in Next.js, Hono, Bun, Deno or Workers, and the frontend shows streamed text, tool calls with their results, steps and sub-agents.
A browser request is untrusted, so the handler decides nothing on its word: it builds a fresh agent for every request, binds the thread to the caller your verifier proves (verifiedThreads — a checked bearer token or sign-in, never a header read as a name), keeps the conversation on the server (sessions) and takes only the person's newest message from the page. It reads only what the run serves, so anything your redaction policy hides never reaches the browser.
// npm install agentfootprint @ag-ui/core @ag-ui/encoder
import { Agent } from 'agentfootprint';
import { mock } from 'agentfootprint/providers';
import { memorySessions, streamHandler, verifiedThreads } from 'agentfootprint/hosting';
import { agUi } from 'agentfootprint/ag-ui';
// Who is calling — PROVEN, not claimed. A real app: jwksIdentity({ jwksUrl, issuer, audience })
// from 'agentfootprint/security'. This demo verifier knows one token.
const verify = async (token: string) => {
if (token !== 'demo-token-for-ada') throw new Error('unknown token');
return { userId: 'ada' };
};
const handler = streamHandler({
// A fresh agent per request — nothing one caller's run leaves behind reaches another.
agent: () =>
Agent.create({ provider: mock({ reply: 'Hello from agentfootprint.' }), model: 'mock' })
.system('Be brief.')
.build(),
adapter: agUi(),
threads: verifiedThreads({ verify }), // no valid token → 401; the thread is the caller's own
sessions: memorySessions(), // the conversation lives on the server (one turn per thread, per process)
}); // Next.js: export const POST = handler;
// What an AG-UI frontend sends, and what it reads back:
const response = await handler(
new Request('http://localhost/agent', {
method: 'POST',
headers: { 'content-type': 'application/json', authorization: 'Bearer demo-token-for-ada' },
body: JSON.stringify({ threadId: 't1', runId: 'r1', messages: [{ id: 'u1', role: 'user', content: 'Hi' }] }),
}),
);
console.log(await response.text()); // data: {"type":"RUN_STARTED",…} … data: {"type":"RUN_FINISHED",…}
Two turns with a tool call, end to end: examples/deploy/ag-ui.ts · frontend tools, interrupts, what a page may not decide, and what goes out: AG-UI frontends.
agentfootprint is written to be used — and maintained — by coding agents.
npx agentfootprint-setup installs the agentfootprint skill for Claude Code (and rule files for Cursor, Copilot, Windsurf, Cline, Kiro). It loads only when the task is about agentfootprint.examples/ holds 160+ runnable programs, each executed end to end in CI against mock providers.| If you are... | Go here |
|---|---|
| New to agents | 5-minute quick start |
| Coming from LangChain / CrewAI / LangGraph | Migration guide |
| Architecting an enterprise rollout | Production guide |
| Doing due diligence | Architecture overview |
| Researcher / academic background | Citations & prior art |
| Curious about design | Inspiration docs |
Or jump into the examples gallery — every example is also an end-to-end CI test.
sideEffects — import { defineTool } doesn't pull in the Agent runtime, memory stores or providers.agentfootprint plus /providers, /memory, /rag, /observe, /context, /resilience, /security, /hosting, /events, /cache and a few more; package.json exports is the complete list.Proven by tests, not promised: test/esm-packaging.test.ts and test/trace-toolpack/browserGraph.test.ts.
footprintjs — the flowchart pattern for backend code. agentfootprint's decision-evidence capture, narrative recording, and time-travel checkpointing are footprintjs primitives at the runtime layer.
You don't need to learn footprintjs to use agentfootprint — but if you want to build your own primitives at this depth, start there.
agentfootprint is part of a research program on making software systems explain themselves — every run records why it did what it did, as a causal trace. Researching agent transparency, observability, or explainable AI? The ecosystem map is a good starting point.
If you use agentfootprint in academic work, please cite it (or use the "Cite this repository" button on GitHub):
@software{anbalagan_agentfootprint,
author = {Anbalagan, Sanjay Krishna},
title = {agentfootprint: the explainable AI agent framework},
url = {https://github.com/footprintjs/agentfootprint},
license = {MIT},
year = {2025}
}
| Never verifies, blocks or rewrites an answer. The previous detector failed its benchmark; the revised path is unmeasured. No warning does not mean verified. |