Agent Config Export

Export an Agent or team to a JSON-safe config dict, and rebuild it

Export any Agent or team to a JSON-safe config dict, and rebuild an equivalent one on the other end.

python
from praisonaiagents import Agent
from praisonaiagents.agent.serialize import agent_to_dict, agent_from_dict

agent = Agent(name="Researcher", role="Analyst", instructions="be thorough", llm="gpt-4o")
blob = agent_to_dict(agent)
same = agent_from_dict(blob)
# same.name == "Researcher", same.role == "Analyst", same.llm == "gpt-4o"
Diagram

πŸ€– Agent

πŸ“€ agent_to_dict

πŸ“„ JSON blob

πŸ“₯ agent_from_dict

πŸ€– Rebuilt Agent

Quick Start

  1. Round-trip a tool-less agent
    python
    from praisonaiagents import Agent
    from praisonaiagents.agent.serialize import agent_to_dict, agent_from_dict
    
    agent = Agent(name="Researcher", role="Analyst", instructions="be thorough", llm="gpt-4o")
    blob = agent_to_dict(agent)
    same = agent_from_dict(blob)
  2. Round-trip with tools
    python
    from praisonaiagents import Agent
    from praisonaiagents.agent.serialize import agent_to_dict, agent_from_dict
    
    def search(q: str) -> str:
        """Search the web."""
        return "..."
    
    agent = Agent(name="R", instructions="x", llm="gpt-4o", tools=[search])
    blob = agent_to_dict(agent)
    # blob["tools"] == ["search"], blob["tools_resolvable"] is True
    
    rebuilt = agent_from_dict(blob, tool_registry={"search": search})

How It Works

A tool exports by name because a callable cannot survive JSON, so import re-binds those names through a tool_registry.

Diagram
Agentagent_from_dictJSON blobagent_to_dictUserAgentagent_from_dictJSON blobagent_to_dictUseragent_to_dict(agent)JSON-safe config dictagent_from_dict(blob)rebuild from constructor fieldsequivalent Agent

What gets exported vs. reset

The exporter derives its field set from the live Agent.__init__ signature, so an attribute that moved into a grouped config never leaks into a config that cannot be imported.

FieldBehaviour
name, role, goal, backstory, instructionsExported when set
llmExported as the model string (client is dropped)
reasoning_effort, reasoning_stepsExported when set
self_reflect, max_reflect, min_reflectExported when set
respect_context_window, max_iterExported when set
markdown, stream, verboseExported when set
versionAlways AGENT_CONFIG_VERSION (1)
toolsExported as a list of tool names
tools_resolvableTrue if every tool exported as a name, else False
chat_history, _session, memory, knowledgeNot exported (runtime state)
Resolved LLM client, cachesNot exported (runtime state)
Grouped configs (output=, reflection=, execution=, caching=, hooks=, skills=, planning=, web=, context=, autonomy=, templates=, learn=, sandbox=, knowledge=, guardrails=, approval=)Not exported in v1 (see limits below)

The tools_resolvable flag

Tools export by name because a callable cannot survive JSON, and the flag reports whether every name could be captured.

tools_resolvable is False when a tool has no usable name/__name__ β€” that config cannot round-trip cleanly, and refusing is safer than silently dropping the tool.

python
blob = agent_to_dict(agent)
if not blob["tools_resolvable"]:
    raise ValueError("this config can't round-trip its tools")

Refusals (fail-loud)

Import refuses loudly rather than rebuilding a subtly-wrong agent.

RefusedOtherwise…
Config declares tools but no tool_registry passedIt would rebuild a tool-less agent that looks like it chose not to act
tool_registry missing a declared tool by nameIt would silently drop that tool
version in config β‰  AGENT_CONFIG_VERSIONIt would half-import into an incompatible build
config isn’t a dict, or is NoneWrong input type
python
from praisonaiagents.agent.serialize import agent_from_dict, SerializationError

try:
    agent_from_dict(blob)  # no tool_registry
except SerializationError as e:
    print(e)  # "This config declares tools ['search'] but no tool_registry was given, …"

Common Patterns

Diff two team configs

python
import json, difflib
from praisonaiagents import Agent
from praisonaiagents.agent.serialize import agent_to_dict

a = agent_to_dict(Agent(name="R", instructions="research", llm="gpt-4o"))
b = agent_to_dict(Agent(name="R", instructions="research deeply", llm="gpt-4o"))

diff = difflib.unified_diff(
    json.dumps(a, sort_keys=True, indent=2).splitlines(),
    json.dumps(b, sort_keys=True, indent=2).splitlines(),
)
print("\n".join(diff))

Persist a team to a file, reload it

python
import json
from praisonaiagents import Agent
from praisonaiagents.agent.serialize import agent_to_dict, agent_from_dict

agent = Agent(name="R", instructions="x", llm="gpt-4o")
with open("agent.json", "w") as f:
    json.dump(agent_to_dict(agent), f, indent=2)

with open("agent.json") as f:
    rebuilt = agent_from_dict(json.load(f))

Hand a Python-built team to the visual builder

python
import json
from praisonaiagents import Agent, PraisonAIAgents
from praisonaiagents.agent.serialize import team_to_dict

team = PraisonAIAgents(
    agents=[
        Agent(name="Researcher", instructions="research", llm="gpt-4o"),
        Agent(name="Writer",     instructions="write",    llm="gpt-4o"),
    ],
    process="sequential",
)
config_blob = json.dumps(team_to_dict(team), indent=2)  # feed to the builder

When to use team_to_dict vs. agent_to_dict

team_to_dict dumps every member’s config in one call, so reach for it whenever more than one agent is involved.

python
from praisonaiagents import Agent, PraisonAIAgents
from praisonaiagents.agent.serialize import team_to_dict

team = PraisonAIAgents(
    agents=[
        Agent(name="Researcher", instructions="research", llm="gpt-4o"),
        Agent(name="Writer",     instructions="write",    llm="gpt-4o"),
    ],
    process="sequential",
)
blob = team_to_dict(team)
# blob == {"version": 1, "name": ..., "process": "sequential",
#          "agents": [ {...}, {...} ], "tasks_included": False}

A tasks_included: False value is the caller’s signal that the team had tasks whose graph did not come across.


Known limits (v1)


Best Practices

Always pass tool_registry= when the config declares tools

Import refuses a tools-declaring config without a tool_registry. That fail-loud path is a feature β€” it stops you rebuilding a tool-less agent that looks like it chose not to act. Pass tool_registry={name: callable} with every declared tool.

Check tools_resolvable before persisting

A config with tools_resolvable=False cannot round-trip its tools. Check the flag right after agent_to_dict and fix the tool’s name before saving the blob.

Don't hand-edit the version field

Round-trip only works when version matches AGENT_CONFIG_VERSION. Leave it alone β€” it bumps only when the schema changes, and a mismatch refuses on import by design.

Use team_to_dict for members, keep task orchestration in code (v1)

team_to_dict exports every member, but the task graph is not serialised in v1. Keep task wiring in Python until task-graph serialisation lands; tasks_included: False tells you when tasks were dropped.


Related