Skip to content
tnsaiDocs
GitHub

Auto team

AutoTeamBuilder in com.tnsai.autoteam (in tnsai-intelligence) asks an LLM to design a group of agents for a task, then builds those agents. You describe the task and hand over the tools; the LLM picks the roles, the prompts and the dependencies between agents.

AutoTeamBuilder.Result result = AutoTeamBuilder.create()
    .llm(llmClient)
    .toolPojos(searchTools)
    .principalProvider(new LocalIdentityProvider())
    .authorityScope(AuthorityScope.unrestricted(Duration.ofHours(1)))
    .build("Research quantum computing advances and write a summary report");

AgentGroup group = result.group();
TeamPlan plan = result.plan();

Every generated agent gets an identity from the principalProvider and the authorityScope you set, as a hand-built agent does; see Principal and authority. Without both, build throws IllegalStateException. liabilitySink(...) is optional.

Builder

MethodDefaultMeaning
llm(client)requiredDesigns the team and is the LLM of every generated agent
toolPojos(...)noneObjects whose @Tool methods make up the tool pool
dynamicTool(tool), dynamicTools(list)noneTools built at runtime, such as MCP proxies
maxAgents(n)10Most agents kept from the plan; must be at least 1
temperature(t)0.4Temperature of the two design calls, between 0 and 2
agentCustomizer(fn)noneChanges each AgentBuilder before the agent is built
principalProvider(provider)required by buildIssues each agent's identity
authorityScope(scope)required by buildWhat each agent is allowed to do
liabilitySink(sink)noneWhere each agent records liability

How a team is designed

build(task) makes two LLM calls and then builds the agents:

  1. Decomposition. The LLM gets the task and the names of all tools, and answers with JSON: a team name, its reasoning, a suggested formation, the agents with their roles, capabilities, tool names and an isLead flag, and a map of which agent depends on which. If the answer has no JSON that can be parsed, the plan falls back to one "General Assistant" agent.
  2. Cut to size. When there are more agents than maxAgents, the first maxAgents are kept. The dependency map is not trimmed.
  3. Prompts. A second call writes a system prompt for each agent. An agent the answer leaves out gets a prompt built from its role, the task, its capabilities and its tools.
  4. Formation. TopologySelector replaces the suggested formation, as in the table below.
  5. Agents. Each agent gets a role named after the generated role, with the prompt as the role's goal, and the tools whose names it was given. Tool names that are not in the pool are skipped. The agents are collected in an AgentGroup.
PlanFormation
One agentPARALLEL
A lead agent and some dependenciesHIERARCHICAL
Dependencies that form a single chainSEQUENTIAL
A dependency path two or more steps longHIERARCHICAL
A lead agent and no dependenciesHIERARCHICAL
No dependenciesPARALLEL
Anything elseThe LLM's suggestion, or PARALLEL if it is not one of the three

The rows are checked from top to bottom. There is no way to force a formation.

Previewing the plan

plan(task) runs the same design steps and returns the TeamPlan without building any agents, so it needs no principal provider or authority scope. It still makes both LLM calls.

TeamPlan preview = AutoTeamBuilder.create()
    .llm(llmClient)
    .toolPojos(searchTools)
    .maxAgents(5)
    .plan("Build a market analysis dashboard");
TeamPlan accessorMeaning
taskDescription()The task
teamName(), reasoning()Written by the LLM
agents()One GeneratedAgentConfig per agent
suggestedFormation()PARALLEL, SEQUENTIAL or HIERARCHICAL
dependencies()Agent id to the ids it depends on
agentCount(), hasDependencies()Counts from the two above

A GeneratedAgentConfig has an id, a role, a systemPrompt, toolNames, capabilities and parameters; isLead() reads the isLead parameter.

Running the team

build returns a Result with the group, the plan and the agents. An AgentGroup holds the agents but does not run a task. To run them, put them in a Team with the chosen formation:

TeamBuilder teamBuilder = Team.builder()
    .name(plan.teamName())
    .mission(plan.taskDescription())
    .formation(TeamFormation.valueOf(plan.suggestedFormation()));
for (Agent agent : result.agents()) {
    teamBuilder.member(agent, TeamRole.MEMBER);
}
Team team = teamBuilder.build();
team.start();
TeamResult teamResult = team.execute(plan.taskDescription());

The plan's dependency map is not passed to the team; the formation alone decides how the members work together. See Group topologies for teams and formations.