Model or dataset
InternLM/lagent avatar
InternLM/lagent

InternLM/lagent: A PyTorch-Style Layer Model for LLM Agents

A lightweight framework for building LLM-based agents

2,280 stars243 forksPythonApache-2.0

At a glance

What is it?
Lagent is a Python framework that treats agent construction like defining neural network layers and message passing between them. It is a good fit if you want to build agent loops from explicit, inspectable components rather than adopt an opinionated orchestration stack.
Who is it for?
Adopt lagent if you want to assemble agent loops from explicit pieces (LLM wrapper, aggregator, memory, parsers) and read the message flow in plain Python. Do not adopt it if you need a batteries-included multi-agent runtime with stable releases: the newest tag is a release candidate, and the README's examples stop at single-agent message passing.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The Problem Lagent Targets: Agent Logic Buried in Framework Glue

Most agent frameworks ask you to describe an agent in configuration and then hope the runtime does what you meant. Lagent takes the opposite position. The README states that the project is inspired by the design philosophy of PyTorch, with the expectation that the analogy of neural network layers will make the workflow clearer, so users only need to focus on creating layers and defining message passing between them in a Pythonic way. That sentence is the whole design brief. The unit of composition is a Python class, not a YAML file or a graph DSL. The audience is an engineer who already writes Python and wants the control flow of an agent visible in their own source tree, where a debugger can step through it. It is not aimed at people who want a hosted agent product or a drag-and-drop builder. If your mental model of an agent is a chain of typed messages moving between named components, lagent's abstractions will look familiar rather than novel, and that is the point.

AgentMessage as the Wire Format Between Layers

Everything in lagent moves as an AgentMessage. The README's first example constructs one with sender='user' and a content string, passes it to an Agent instance, and prints the result. The printed object exposes fields the framework carries through the loop: content, sender, formatted, extra_info, type, receiver, and stream_state, which in the example is AgentStatusCode.END. Two of those fields are worth noting because they define extension points rather than just data. The formatted field is reserved, per the README, to store information parsed by output_format from the model output. The receiver field exists on the message even though the single-agent example never sets it, which suggests the schema was designed with routing in mind. The stream_state field carries an enum rather than a boolean, so a consumer can distinguish intermediate states from a finished response. If you have written agent code where the return type is sometimes a string and sometimes a dict, this schema is the corrective: one object, fixed fields, explicit status.

Memory Is Written in __call__, Not in forward

The README includes pseudo code for Agent.__call__ that shows the exact sequence: pre_hooks run on the incoming message, the message is added to memory, forward is invoked, its output is added to memory, post_hooks run, and the result is returned. The README is explicit that input and output messages are both added to memory in each forward pass, and that this happens in __call__ rather than forward. That placement is a deliberate separation: if you subclass Agent and override forward, you inherit memory bookkeeping for free and cannot accidentally skip it. You can inspect the accumulated state two ways. agent.memory.get_memory() returns a list of AgentMessage objects. agent.state_dict() returns the same content as plain dicts under a 'memory' key, which is the form you would serialize. Session handling is minimal: agent.reset() clears memory for session_id=0, described as the default. The README does not document how multiple concurrent sessions are isolated beyond that default, so treat multi-session behaviour as something to verify in your own environment rather than assume.

Aggregators: Where Conversation History Becomes Model Input

The bridge between lagent's internal message list and whatever an LLM API expects is the aggregator. The README states that DefaultAggregator is called under the hood to assemble and convert AgentMessage to OpenAI message format, and it shows the forward method calling self.aggregator.aggregate(...) with the session's memory, the agent name, the output format, and the template. Because aggregate is a plain method, replacing it is a matter of subclassing. The README's FewshotAggregator example does exactly that: it stores a list of few-shot messages, prepends them after the system instruction, then walks the memory converting messages whose sender matches the agent name into assistant turns and everything else into user turns. It also merges consecutive user messages into the previous one rather than emitting two adjacent user entries. That merge behaviour is a real implementation detail you would otherwise discover by reading the base class. The example output shows the few-shot pair influencing the reply, which is the intended demonstration. The takeaway is that prompt construction is not hidden in a template engine; it is a method you can read and replace.

Getting It Running and Wiring a Model

Installation is from source. The README gives three commands: git clone https://github.com/InternLM/lagent.git, cd lagent, then pip install -e . There is also a PyPI badge in the header, so a package install exists, but the README's own instructions use the editable source install. Model wiring goes through the LLM wrappers in lagent.llms. The example uses VllmModel with path='Qwen/Qwen2-7B-Instruct', a meta_template of INTERNLM2_META, tp=1, top_k=1, temperature=1.0, stop_words=['<|im_end|>'], and max_new_tokens=1024. Those are the constructor arguments shown, and they map to familiar sampling and tensor-parallel settings. The agent itself takes the model and a system prompt: Agent(llm, system_prompt). Output formatting is opt-in through output_format; when it is set, forward calls parse_response on the raw LLM text and attaches the result to the message's formatted field. The README's tool example imports ToolParser from lagent.prompts.parsers and pairs it with a system prompt instructing step-by-step analysis and Python code, though the snippet is truncated mid-statement in the supplied material. Treat the parser wiring as documented in outline rather than fully shown.

Where Lagent Is the Wrong Tool

Lagent gives you a loop, a message schema, and pluggable prompt assembly. It does not, from the material available, give you a durable runtime. There is no documented persistence layer for agent state beyond state_dict(), no documented retry or rate-limit policy around llm.chat, and no documented sandbox for executing the code that ToolParser is meant to extract. If your agent needs to survive a process restart mid-task, or you need per-session isolation with many concurrent users, the README's coverage stops at agent.reset() for session_id=0. The release picture reinforces the caution: the most recent tag is agentrl_rc0, and the two before it are v0.5.0rc3 and v0.5.0rc2, both release candidates. There is no stable 0.5.0 final in the supplied release list. That does not make the code unusable, but it means interface churn between tags is a live risk, and pinning matters more than usual. A second limitation is conceptual: the layer analogy is a strength for engineers and a cost for everyone else. There is no declarative surface, so a teammate who does not read Python cannot inspect or modify an agent's behaviour.

Compared With LangChain-Style Chain Abstractions

The obvious comparison is LangChain, and the difference is where the abstraction sits. LangChain's core vocabulary is the chain and, in its agent tooling, prebuilt agent types that you configure with a model, a tool list, and a prompt. You get a working agent quickly, and you accept the framework's decisions about how tool calls are parsed, how intermediate steps are represented, and how memory is persisted. Lagent inverts this. The README's own framing is that users focus on creating layers and defining message passing. There is no prebuilt agent type in the material shown; there is an Agent base class with forward, an aggregator you can replace, a memory you can inspect, and parsers you can swap. You write more code and you own more decisions. The payoff is that when the agent misbehaves, the failure is in a method you wrote or a method you can read, not in an orchestration layer several dependencies deep. If your team's problem is that agent behaviour is opaque and hard to debug, lagent's approach is a reasonable answer. If your problem is that you need an agent running this week and you have no strong opinions about message schemas, the extra assembly work is a cost with no immediate return.

Maintenance, Licensing, and What to Check Before Pinning

The repository is Apache-2.0, which permits commercial use and modification and includes an explicit patent grant. It also requires that you preserve copyright and licence notices and state significant changes if you redistribute. That is a summary of the licence's general shape, not legal advice; read the LICENSE file in the repository before shipping anything derived from it. On maintenance, the material supports only limited conclusions. The last push timestamp is 2026-08-03, which indicates recent activity, and the release history shows a cadence of release candidates rather than finals. The README's examples are runnable-looking and specific, which is a good sign for documentation quality, but the tool parser snippet is cut off, so the full path from parsed output to executed tool is not shown in what is available here. Before you pin a version, check three things against the actual installed package: whether the Agent.forward signature in your version still takes session_id as a keyword, whether DefaultAggregator.aggregate has the same four parameters used in the FewshotAggregator example, and whether lagent.llms exposes the model wrapper you need. Those interfaces are the ones the README's code depends on, and they are the ones most likely to move between release candidates.

Editorial conclusion

Adopt lagent if you want to assemble agent loops from explicit pieces (LLM wrapper, aggregator, memory, parsers) and read the message flow in plain Python. Do not adopt it if you need a batteries-included multi-agent runtime with stable releases: the newest tag is a release candidate, and the README's examples stop at single-agent message passing. Before committing, verify which release tag you are pinning, whether the aggregator and parser interfaces you plan to subclass match your installed version, and how session_id behaves once you run more than one conversation.

Official sources

  1. InternLM/lagent on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Community notes

Community notes