Fangorn agent of agentic tool usage and development.
Note: For the PL Network Hackathon, Ollama has been pushed aside due to the complex nature of the MCP tool usage and high amount of potential tokens generated by queries
The Fangorn Agent is an experiment in personal agent development. Currently, the top models for agent development are ChatGPT and Claude. However, these models are extremely expensive to run and do not really fit into the "personal" portion of a personal agent. Therefore, the Fangorn Agent is first and foremost meant to be able to run on consumer grade hardware.
We are also aiming to create a personal agent that can only do what you allow it to do. This is simple via the use of tools versus the use of skills (although skill support is provided by LangChain as well). From the perspective of the agent, it has access to many different black boxes that allow it to accomplish tasks without revealing how the tasks are completed or if sensitive information was involved.
To learn more about the initial development and the context in which it was built, please refer to our HackMD article discussing our Arbitrum Open house project.
- LangChain
- Ollama
- Typescript
- Docker
This agent is intended to be run on consumer grade hardware, but even so the current qwen3.5:9b model is somewhat heavy (~8GB of VRAM if using an NVIDIA GPU). These are the specs of a computer that can run the agent:
- Form factor: Full Tower
- OS: Ubuntu 24.04.3 LTS
- Kernel Version: Linux 6.17.0-14-generic
- GPU: NVIDIA GeForce RTX™ 2080 Ti
- CPU: AMD Ryzen™ 9 9900X × 24
- RAM: 32 GB DDR5
- Storage: Samsung 970 EVO NVMe SSD 500 GB
Also note that the qwen3.5:4b model also reliably executes the available tool flows and only uses ~6GB of VRAM.
- Have Docker installed
- Install the Ollama docker image
- If you have an NVIDIA GPU you would like to use, note that there are specific instructions you must follow to allow for it to be used in a Docker environment.
- Obtain an API key and credits from console.anthropic.com
- Include API info in
.env
- Make run_agent.sh and run_web.sh executable
chmod +x run_agent.shandchmod +x run_web.sh. - (Ollama only) Run the ollama container then download the
qwen3.5:9b(orqwen3.5:4b) modeldocker exec -it ollama ollama pull qwen3.5:9b. - Ensure you have
pnpminstalled - Run
pnpm iat the root of the agent project - Run
pnpm iat the root of the web-app directory - Run
cp env.example .envand fill in the information - (OPTIONAL) Ensure you have OAuth2.0 tokens created for the gmail address you wish to use and
USE_GMAIL=true
- Run
./run_web.shat the root of the project if you wish to interact via a web browser (uses defaultlocalhost:3000for UI) - Run
./run_agent.shat the root of the project if you wish to interact via the command line (Not recommended for rich interactions)
"Making discovery more human by making it agentic"
Fangorn Explorer is a browser based chat that combines The Graph's Subgraphs, reactive UI components, and the Fangorn Agent to enable data discovery in a brand new way. Users can dynamically discover content through the use of natural language, explore data cards related to their interests, and obtain useful summaries and recommendations from the agent. Thanks to the use of schemas, the LLM can easily infer the contents of the subgraphs and what type of data can be retrieved from them simply by looking at the contents of the schemas. Here is an example of how the LLM's summary:
Summary:
29 total schemas across 2 unique owners
The vast majority are music schemas with fields: artist, audio, title
A few unique schemas stand out:
fangorn.test.music.v0 & fangorn.test.place.v0 — use fields: address, description, name
test.fangorn.music.v0 — includes an additional genre field
pl-genesis.fangorn.music — a named music schema without a version timestamp
Would you like to explore any of these schemas in more detail?
The UI is completely dynamic and combines elements that are familiar from more traditional UI experiences. There is a form of display for every primitive element that may come from querying strucutured data.
The explorer also includes the ability for the user to easily enquire about specific manifests, schemas,and files.
Reply funcionality is also implemented that allows for conversations to stay related to data of interest. Finally, not only does the browser enable discovery of content, but it also allows a human initiated then out of the loop purchasing experience. The user simply finds the file they wish to purchase and asks the LLM to purchase it for them. Everything else is handled completely on the user's behalf thanks to the fangornFetch tool.
The explorer offers a next generation decentralized web experience that offers a streamlined and responsive web 2.0 feel.
This is the entry point for chat loop. Here, the agent is created and the user interaction loop begins.
This is the agent and where the agent loop runs, via invokeAgent(), and the Toolbay is initialized.
This is where tools are created, tool invocation occurs, and hotswapping tools happens.
This contains two interfaces and a factory function:
Toolboxes: This interface enables tools to be grouped under a broader category. A toolbox exposes two functions:getTools(): this returns all of the tools that are in the toolbox.getToolboxAsTool(): this returns the toolbox as a callable tool itself. This primes the model to re-plan when a tool hotswap is going to occur. It also enables us to perform the hotswap itself.
AsyncFactory<T>: This interface ensures that Toolboxes implement a static init function. This allows for tools with asynchronous dependencies to be created (seex402fToolbox.ts)initializeToolbox(): This function is used by the toolbay to create the toolboxes and their tools.
When building the intial agent for the Arbitrum hackathon, it was noted that when a smaller LLM receives too many tools, it seems to get "confused" and "forgets" how to call tools. One solution we are exploring is "compressing" the tool context by introducing Toolboxes.
Simply put, a Toolbox allows for lazy loading of tools by giving a brief summary of what the tools do.
The Toolbox and ToolBay work in conjunction with each other to enable "hotswapping" tools. You can see that we do not use the typical createAgent function offered by LangChain, but instead use the model directly. This is because once an agent is created via createAgent, there is no way to hotswap tools outside of re-creating the agent. However, the models do expose a function called bindTools which allows for the available tools to be updated for a model directly. We, therefore, expose the agent loop and check if the agent intends to use tools.
If tool usage is intended, we intercept the tool call and check if it is calling a Toolbox. If it is, we update the currentTools field in the ToolBay with the tools within the specified toolbox, and then allow the Toolbox "tool" to be called directly by the agent. The Toolbox tool simply returns a response to the agent to let it know that it has new tools available and that it should re-plan with these new tools. When the agent event loop goes back to the top, we check if the toolbay is marked as "dirty" (new tools are available), if it is we re-bind the tools to the model.
This toolbox offers the necessary tools to complete the x402f protocol's flow.
Here is the "tool" that is returned by the getToolboxAsTool function:
public getToolboxAsTool(): DynamicStructuredTool {
const fangornAgentToolboxTool = tool(
async () => {
console.log("console.log - agent called fangornAgentToolboxTool tool");
return JSON.stringify({
status: 200,
statusText: "OK",
result:
"x402Fangorn tools are now available. You now have access to: fangorn_fetch. Re-plan and use them to complete the task.",
});
},
{
name: this.name,
description:
"Activates the Fangorn toolbox, which provides tools for purchasing and decrypting files. Call this whenever the user wants to buy or decrypt a resource. Once called, you will gain access to the fangorn_fetch tool.",
schema: z.object({}),
},
);
return fangornAgentToolboxTool;
}
You can see that it doesn't actually do anything other than let the agent know that it has new tools. By the time the agent has received this response, the magic really already occurred because the tools have already been updated, the toolbay has been marked as dirty, and agent has been prompted to start the agent loop from the beginning with the dirty toolbay (and thus new tools).
- fangornFetch(owner, schemaName, tag): This tool allows the agent to use the Fangorn x402 middleware in order to fulfill the x402f requirements.
This toolbox integrates MCP functionality into the toolbox pattern. What tools are available depends on which MCP server you are connected to.
This toolbox provides the ability to discover agents registered on-chain.
- searchAgents(agentName): This tool is used by the agent to find other agents by their human readable name. It currently assumes that the human readable name is unique. The human is responsible for specifying the target agent's human readable name in their query.
- getAgentCard(a2aEndpoint): This allows the agent to retrieve the target agent's agent card as advertised in the
a2afield. It assumes that thea2afield provides the base URL and that the agent card is located at/.well-known/agent-card.json
This toolbox is currently just a wrapper around one tool, but will later implement more email functionality.
- sendEmail(recipient, subject, message): This tool allows for an email to be sent to a specific recipient with a subject and message. It uses OAuth2.0 for agent authorization.
Agent lookup on Arbitrum Sepolia via the agent-0 sdk
npm run search
This runs the LLM within the Docker container, builds and starts the agent, then prompts for user input once everything is ready.
Note: If you have an issue with port 11434 being taken, verify that Ollama (not the container) isn't starting on system startup and taking that port.
- Running the container (with GPU support):
docker run -d --gpus=all -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama - Running the container (CPU only):
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama - Download model (container already running):
docker exec -it ollama ollama pull your-desired-model - Models already downloaded to the container:
curl http://localhost:11434/api/tags - Remove model:
docker exec -it ollama ollama rm your-desired-model - Currently running models and their memory usage:
curl http://localhost:11434/api/ps - Stopping the container:
docker stop ollama - Force stopping the container:
docker kill ollama - Removing the container:
docker rm ollama
- Find what's using the port
sudo lsof -i :11434 - If it is ollama, you can check if it's running as a system service
systemctl status ollama - If that's it you can stop it with:
sudo systemctl stop ollama - You can also stop it from being auto-started on boot if you'd like:
sudo systemctl disable ollama
If you'd like to see what models are offered by Ollama that support tool calling, you can check here
- Anthropic limiting: There are times (even if you have a sufficient balance) that Anthropic will deny calls to Claude. Switching between
claude-opus-4-6andclaude-sonnet-4-6seems to remedy this issue. /chatendpoint unavailable: If an error occurs during agent startup when runningrun_web.sh, the NextJs server is not always killed. In this case, when you run./run_web.shagain, it will start another NextJs server which will occupy the port typically used by the agent. Useps auxto list all process, and kill any orphaned nextjs instances.
- When an agent calls a toolbox, it gets back ALL of the tools in the toolbox. We should investigate a way to minimize the amount of tools returned by a toolbox. One idea may be that the agent requests a specific tool from the toolbox instead of getting them all.
- The LLM now runs within a container, but the
run_agent.shscript should allow for more options (CPU only, GPU, what model, etc.) - Right now all of our toolboxes are included. We should consider using
clackto allow for a user to select what toolboxes they would like to include before starting the agent. - Implement dynamic filtering for all Blocks in the Fangorn Explorer.
- Ensure clean handling of failures in
run_web.sh