neuron-explorer
Qwen3.5‑4B 전용 조향형 뉴런 탐색기, 모든 MLP 뉴런을 밀어 결과를 보관·탐색
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기A lightweight and elegant AI Agent framework
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗English | 简体中文
lovia is an elegant, restrained Python framework for developers who want to own the agent loop without rebuilding every supporting primitive from scratch. It covers the recurring hard parts of agent applications — tools, sessions, events, context compaction, serving — without turning into a platform.
pip install lovia
from lovia import Agent, tool
@tool
def get_order(order_id: str) -> str:
"""Look up an order's status by id."""
return f"Order {order_id}: shipped 2 days ago, arriving Thursday."
# Configure OPENAI_BASE_URL and OPENAI_API_KEY in your environment.
agent = Agent(
name="support",
instructions="You are a customer-support agent. Look the order up before "
"answering, and reply in one or two concrete sentences.",
model="glm-5.2",
tools=[get_order],
)
# run_sync() suits scripts and notebooks; from async code,
# use `await Runner.run(agent, ...)` instead.
result = agent.run_sync("Where is my order A-1042?")
print(result.output)
Or serve a full chat UI — memory, skills, scheduling, and a workspace on the current directory included — in one line:
pip install "lovia[web]" && lovia web
Anthropic is built in too: configure ANTHROPIC_API_KEY, set
ANTHROPIC_BASE_URL for non-default endpoints, and use the anthropic:
model prefix. Everything else about models lives in
Providers & models.
This README is a quick tour. For the full guide, start with the quickstart and core concepts in the documentation. The examples are a numbered, runnable learning path. What 1.0 keeps stable is spelled out in Stability.
Composable primitives, ordinary Python — no new universe of abstractions:
httpx, pydantic,
and pyyaml; install everything else only when needed.Agent is treated as immutable configuration and
normally varied with clone(); a Runner
executes one run, and @tool wraps a typed callable as a Tool; handoff and agent-as-tool
compose agents; plugins package the rest.Protocol.The design pressure throughout is restraint: if a feature can be a short user-side recipe, it stays out of the framework.
Every stop below has a full guide; each snippet runs as written.
An Agent is declarative configuration — no conversation state, safe to
share, cheap to clone(). Tools, a workspace, and plugins compose directly on
that configuration:
from lovia import Agent, Memory, Skills, tool
from lovia.workspace import Workspace
@tool
def light_travel_time(distance_km: float) -> str:
"""Calculate one-way light-signal delay for a distance in kilometers."""
return f"{distance_km / 299_792.458:.2f} seconds"
agent = Agent(
name="science-writer",
instructions="Explain complex science with vivid, everyday analogies.",
model="gpt-5.5",
tools=[light_travel_time],
workspace=Workspace.local(".", mode="readonly"),
plugins=[
Skills("./skills"),
Memory(),
],
)
→ Agents
One run, three consumption styles — and the stream handle is both async-iterable and awaitable. Iteration never raises: every stream ends with exactly one terminal event.
from lovia import Runner, events
handle = Runner.stream(
agent,
"How long does a signal take to reach Mars at 225 million km?",
)
async for ev in handle:
if isinstance(ev, events.TextDelta):
print(ev.delta, end="", flush=True)
result = await handle.result()
@tool wraps a typed callable as a Tool; the schema comes from signatures,
docstrings, Annotated, and Pydantic Fields. One turn's calls execute concurrently by
default — tools with non-reentrant side effects opt out and become execution
barriers:
from typing import Annotated
from pydantic import Field
from lovia import tool
@tool(strict=True)
def search_docs(
query: Annotated[str, "Search terms"],
limit: Annotated[int, Field(ge=1, le=10)] = 5,
) -> list[str]:
"""Search internal documentation."""
return []
@tool(parallel=False)
async def apply_migration(name: str) -> str:
"""Apply a database migration (never concurrently with other tools)."""
return "applied"
The built-in web tools (web_fetch, web_search) fence what
they fetch, so the model reads it as material rather than instructions;
fence() does the same for your own tools.
→ Tools · Built-in tools
Pass a Pydantic model, dataclass, TypedDict, or plain type; the final
answer is validated — and repaired once, by default, when it doesn't parse:
from pydantic import BaseModel
from lovia import Agent, Runner
class Brief(BaseModel):
title: str
bullets: list[str]
agent = Agent(name="summarizer", model="glm-5.2", output_type=Brief)
result = await Runner.run(agent, "Summarize Transformer for a Python developer.")
print(result.output.title)
Use model strings or provider instances. OpenAI-compatible
endpoints read OPENAI_BASE_URL / OPENAI_API_KEY; Anthropic defaults to
the official endpoint, reads ANTHROPIC_API_KEY, and uses
ANTHROPIC_BASE_URL for non-default endpoints. Prompt caching and reasoning
models are handled per endpoint, and a custom provider is a small
Protocol:
from lovia import Agent, ModelSettings
agent = Agent(
name="assistant",
model="anthropic:<model>",
settings=ModelSettings(temperature=0.2, max_tokens=800),
)
| Your task | Use | Runtime behavior |
|---|---|---|
| Let a specialist take over the conversation | Handoff | The specialist receives full history and continues in the same Run |
| Get a subtask result before the parent continues | Agent-as-tool | The child receives only the delegated prompt and returns a Tool result |
| Run independent work in the background | Subagents | The child runs independently and reports back later |
The three alternatives below show the same researcher in each mode:
from lovia import Agent, Subagents
researcher = Agent(
name="researcher",
instructions="Research the given topic and return a concise report.",
model="glm-5.2",
)
# Handoff: researcher takes over and receives the full conversation
router = Agent(
name="router",
instructions="Transfer to researcher when in-depth research is needed.",
model="deepseek-v4-flash",
handoffs=[researcher],
)
# Agent-as-tool: manager waits for the research, then answers the user itself
manager = Agent(
name="manager",
instructions="Ask researcher for any research needed before answering.",
model="deepseek-v4-flash",
tools=[researcher.as_tool(description="Research a topic and return a report.")],
)
# Subagents: assistant starts background research and keeps working
assistant = Agent(
name="assistant",
instructions="Run independent research in the background while drafting the answer.",
model="deepseek-v4-flash",
plugins=[Subagents([researcher])],
)
lovia web enables Subagents by default and reports completed work back to
the original conversation.
Gate sensitive tools; resolve from your UI, a server-side policy, or an out-of-band channel — unresolved approvals deny, so runs never hang:
from lovia import Runner, events, tool
@tool(needs_approval=True)
async def refund(order_id: str, amount_cents: int) -> str:
"""Issue a refund."""
return "refunded"
async for ev in Runner.stream(agent, "Refund order A123."):
if isinstance(ev, events.ApprovalRequired):
ev.approve() # or ev.reject()
Sessions persist conversations across runs; checkpoints make single runs
crash-safe and idempotent — re-issuing a completed run_id replays its
result without calling the model:
from lovia import CheckpointOptions, Runner, SQLiteCheckpointer, SQLiteSession
session = SQLiteSession("chat.db")
await Runner.run(agent, "My project is called Atlas.", session=session, session_id="u1")
cp = SQLiteCheckpointer("runs.db")
result = await Runner.run(
agent,
"Migrate the report format.",
checkpoint=CheckpointOptions(cp, "report-migration-42"),
)
Long conversations survive the window without rewriting history: compaction is view-only, keeps the prompt prefix stable for provider caches:
from lovia import Agent, Compaction
agent = Agent(
name="companion",
model="glm-5.2",
context_policy=Compaction(context_window=200_000, compact_at=0.85, compact_to=0.60),
)
Guardrails veto runs at the input/output boundary. Reliability follows one placement rule — retry posture on the agent, per-run limits on the run:
from lovia import Agent, GuardrailTripped, RetryPolicy, RunBudget, Runner
async def must_cite(output, ctx):
if "source:" not in str(output).lower():
return "Missing source citation."
agent = Agent(
name="researcher",
model="glm-5.2",
output_guardrails=[must_cite],
retry=RetryPolicy(max_attempts=2)
)
result = await Runner.run(
agent,
"Analyze these logs.",
budget=RunBudget(max_tool_calls=20, max_seconds=60)
)
→ Guardrails · Provider retries · Budgets & limits
Hooks observe every run event (fail-open, same types as streaming); the mailbox is the inbound dual of cancellation — push a message into a live run and the model sees it next turn. A run can even steer itself:
from lovia import AgentHooks, Mailbox, RunContext, Runner, events
hooks = AgentHooks()
@hooks.on(events.TurnStarted)
def deadline(ev, ctx: RunContext):
if ev.turn == 9:
ctx.mailbox.push("Last turn: answer with what you have.")
mailbox = Mailbox()
handle = Runner.stream(agent.clone(hooks=hooks), "Analyze these logs.", mailbox=mailbox)
mailbox.push("Focus on the 5xx spike around 14:00.") # seen next turn
→ Observability · Cancellation & steering
File and shell tools scoped to a root, governed by one allow/ask/deny
policy over paths and commands — ask decisions ride the normal approval
channel. shell(background=true) runs dev servers, watchers, and long
builds as session-owned background processes (poll read_process_output,
stop with kill_process); a foreground cmd & no longer stalls the call:
from lovia import Agent
from lovia.workspace import CommandRule, Workspace
agent = Agent(
name="coder",
instructions="Make small, targeted code changes.",
model="glm-5.2",
workspace=Workspace.local(
".",
mode="coding",
denied_paths=(".env*",),
command_rules=(CommandRule("pytest", "allow"), CommandRule("rm -rf", "deny")),
),
)
Shell commands inherit a minimal environment, including Windows system,
shell, temporary-directory, and user-profile variables so sockets and home
lookups work by default.
API keys and proxy settings are not inherited; pass required variables via
Workspace.local(..., env={...}).
One extension axis: a plugin contributes tools, prompt text, per-turn view injectors, hooks, and guardrails — never control flow:
from lovia import Agent, Skills, Todo
from lovia.plugins.mcp import MCP, MCPServerStdio
agent = Agent(
name="builder",
model="glm-5.2",
plugins=[
Todo(),
Skills("./skills"),
MCP(MCPServerStdio(name="web", command="uvx", args=["mcp-server-fetch"])),
],
)
Todo() — gives the model a checklist for multi-step work; the current
list is re-shown every turn without growing the transcript.Skills(dir) — reusable instruction bundles (SKILL.md + files): a
one-line index stays in the prompt, full content loads on demand.MCP(server) — tools from Model Context Protocol servers, stdio or
HTTP, with per-server name prefixes and approval gates. For web research,
try the Parallel Search MCP recipe.Writing your own is a name plus one async setup(ctx) returning the
contributions.
Long-term memory across conversations — always-in-prompt Notes (facts the user stated or confirmed, never the assistant's own claims; pinned per session so they never bust the prompt cache) plus a searchable Archive, zero-config, self-tidying (a periodic dream pass merges duplicates and prunes stale facts), upgradeable one argument at a time:
from lovia import Agent, Memory
from lovia.plugins import OpenAIEmbedder
agent = Agent(name="assistant", model="glm-5.2",
plugins=[Memory("./.lovia/memory")])
Memory("./memory") # stdlib keyword search (FTS5 bm25)
Memory("./memory", embedder=OpenAIEmbedder()) # + semantic arm -> hybrid recall
Memory("./memory", index=None) # notes only, no archive
→ Memory
A lightweight FastAPI chat app for any agent (pip install "lovia[web]"):
lovia web --port 9000 # python -m lovia.web works too
from lovia.web import create_app, serve
serve(create_app(agent, db_path="lovia.db"), host="127.0.0.1", port=8000)
ui_result(kind, props) to draw UI;
tables, ECharts charts and sandboxed HTML pages come built in
(show_table, show_chart, show_html).ChatUI(...) and assets= change wording, logo,
styles and scripts; scripts drive the page through window.lovia and add
views of their own beside the chat, and run_deps= hands tools the
user's identity and page context./api/docs); create_app(agent, ui=False) serves your own front-end.→ Web UI · Web server · HTTP API
Behavioral test suites: a Case pairs input with checks, any function is a
check, the LLM judge is just another check, and reports diff against a
baseline in CI:
from lovia.eval import Case, contains, evaluate, llm_judge, tool_called
report = await evaluate(agent, [
Case("What is the capital of France?", checks=[contains("Paris")]),
Case("What's 23.4 * 91?", checks=[tool_called("calculator")]),
Case("Write a haiku about spring",
checks=[llm_judge("A 5-7-5 haiku that evokes spring")],
samples=4, pass_threshold=0.75),
])
print(report)
assert report.passed
→ Evals
Everything runs offline against a scripted provider — real tools, real loop, canned model:
from lovia.testing import ScriptedProvider, call, text
provider = ScriptedProvider([
call("add", {"a": 2, "b": 3}, call_id="c1"),
text("The answer is 5."),
])
→ Testing
The examples/ directory is a numbered learning path of thirty
self-contained, runnable scripts — from 01_hello.py to a terminal support
bot — plus one script per built-in tool family (tools/) and the classic
agentic patterns in plain Python (workflows/). Setup and the full index:
examples/README.md.
| Need | Install |
|---|---|
| Core framework | pip install lovia |
| DuckDuckGo search | pip install "lovia[ddg]" |
| Tavily search | no extra — set TAVILY_API_KEY |
| MCP integration | pip install "lovia[mcp]" |
| Web UI | pip install "lovia[web]" |
| Runnable examples | pip install "lovia[examples,web]" |
| Development | pip install -e ".[dev]" |
pip install -e ".[dev]"
.venv/bin/python -m pytest
.venv/bin/python -m ruff check .
.venv/bin/python -m ruff format .
.venv/bin/python -m mypy lovia
Live provider tests are marked live_provider and stay skipped unless
enabled explicitly. Contributor-facing internals are documented in
docs/architecture.md.