← All posts

How to Build a Team of AI Agents for a Company

A build guide with the artifacts: the agent file, the team file, the mission, the workflow with a human gate, and the version row the apply writes.

Vocion Teamvocion-v2.47.4

Building a team of AI agents for a company means declaring a small org chart in configuration: one lead agent that holds the goal, two or three specialists it can hand work to, a team record that names the accountable human, and the procedures the team follows. In Vocion, an open-source agent workforce platform, each of those is a file in git. An agent is agents/<slug>.yaml plus a Markdown system prompt. A team is teams/<slug>.yaml with a lead and an accountableUser; the filename is the slug, so a team can never disagree with its own path. Teams are flat by construction. You apply the workspace to Postgres and every apply writes an audit row.

Most posts that answer this query stop at advice: define the problem, start with two or three agents, specialize. This one shows the files. What follows is a real agent-and-prompt pair, a real team file, a mission, a workflow with a human approval gate, and the ship commands, all taken from Vocion's own documentation and templates (verified against core v2.47.4) so you can copy them and change the names.

How many agents should you start with, and how do you split them?

Split by the data and the surface each agent is allowed to touch, not by job title. docs/getting-started.md builds a small revenue workforce for a fictional company, Harbor Supply: one workspace lead, one team lead under it, and one specialist under that. That is the whole shape — a lead consults team leads, and team leads consult specialists. Vocion enforces this directly in the agent schema: an agent named as a parent must itself have no parent (docs/entities/agent.md). You cannot build a three-deep reporting chain even if you wanted to; the hierarchy is exactly one level.

In practice this means: give the lead a synthesis job ("assemble one decision-ready picture"), give each specialist one bounded surface (one connector, one object type, one skill), and resist naming an agent after a department if its actual job is "read this data source and answer questions about it."

What files make up an agent?

An agent is agents/<slug>.yaml plus, usually, agents/<slug>.system-prompt.md. The YAML carries identity (slug, name, description, icon) and structure (parent, team, agentType); the Markdown file carries the behavior. Vocion's own definition: "An agent is an LLM orchestrator: a name, a system prompt, and a list of what it is allowed to reach" (docs/entities/agent.md).

Here is the workspace lead from the getting-started guide, in full:

# agents/revenue-director.yaml
slug: revenue-director
name: Revenue Director
description: >-
  Runs the Harbor Supply revenue workspace — one coherent picture of the
  quarter, assembled from the team leads.
icon: compass
accent: emerald
eyebrow: Revenue · Workspace Lead
agentType: mission
suggestions:
  - label: How's the quarter?
    prompt: How is the quarter going — pipeline, pitches, and movement? Attribute each part to the team it came from.
  - label: What needs my attention?
    prompt: What are the three things that most need my attention this week?
systemPromptFile: ./revenue-director.system-prompt.md
<!-- agents/revenue-director.system-prompt.md -->
You are the Revenue Director for Harbor Supply. Your KPI is top-line new
sales. You do not do the team's work yourself: you consult the leads, then
synthesize one decision-ready picture for the accountable human.

Operating rules:

- Delegate, then synthesize. Attribute each part of a brief to the team it
  came from. Never guess where a lead could answer.
- Lead with the decision, then the evidence. Cite the records behind any
  claim. When data is missing, say so plainly rather than inventing it.
- Anything that touches the outside world — sending mail, changing a CRM
  record — is a DRAFT for human approval. Never imply an external action
  already happened.

Note what is not there: no tool registration, no model client, no code. The YAML is the wiring; the Markdown file is the behavior. What an agent is allowed to reach is a short, named list in the YAML: skills (skill slugs it mounts), connectorSources (source slugs it may search), objectTypes (business object type slugs), playbooks, documentSetIds, and learningSteps — the field names are exactly these, not "skillSlugs" or "objectTypeSlugs" (packages/core/src/libs/workspace/schemas.ts). role (lead/specialist) exists but is deprecated — it is derived from parent and only needs authoring if you want the schema to check your own assumption.

What does the team file do?

A team groups agents under a lead and names the human accountable for its work. The file lives at teams/<slug>.yaml, and there is no slug: field — the slug comes from the filename, so teams/revenue-ops.yaml is revenue-ops and can never disagree with its own path (docs/entities/team.md). The only fields are name, description, lead (an agent slug), and accountableUser (an email, resolved to a user id at apply; omit it to inherit the workspace default).

# teams/revenue-ops.yaml
name: RevOps
description: >-
  Pipeline health and follow-through — keeps the funnel honest and flags
  anything going stale.
lead: revenue-lead

There is no parent field on a team, and no parent column in the team table — teams are flat by construction (docs/entities/team.md). If you are picturing a nested org chart of teams reporting to teams, that is not the shape here. The one level of nesting lives entirely in the agent hierarchy (parent on agents/<slug>.yaml): a workspace lead consults a team lead, a team lead consults its specialists, and that is as deep as it goes. Two more files complete the shape from the same guide:

# agents/revenue-lead.yaml
slug: revenue-lead
name: Revenue Lead
description: Owns pipeline health and reports up to the Revenue Director.
parent: revenue-director
team: revenue-ops
systemPromptFile: ./revenue-lead.system-prompt.md
# agents/pipeline-analyst.yaml
slug: pipeline-analyst
name: Pipeline Analyst
description: >-
  Reads the funnel — stage aging, conversion between stages, and which deals
  are drifting from their close dates.
icon: trending-up
parent: revenue-lead
team: revenue-ops
systemPrompt: |
  You analyze the Harbor Supply pipeline: stage aging, stage-to-stage
  conversion, slipped close dates, and weighted-versus-raw gaps. Answer with
  numbers first, then the single action you would take. Never invent a deal
  that is not in the data.

That is a lead, a team, and a specialist: five files (three agent YAML files, one team YAML file, one system-prompt Markdown file — pipeline-analyst uses an inline systemPrompt instead of a file, which the schema also allows). More on team design: /blog/teams-the-org-chart-is-the-interface.

Do you have to write all of them?

No. Vocion ships a base pack — a versioned layer of default agents that loads underneath a workspace, "authored exactly like a workspace so the loader reuses the same walk, the same schemas, and the same validation" (docs/entities/base-pack.md). Today, at pack version core@2.0.0, the pack ships seven default agents: delivery-lead, engagement-manager, implementation-lead, proposal-writer, qa-lead, revenue-director, and solutions-architect (packages/core/templates/base/agents/); a review-ops layer adding two more is in review (vocion-core#227). A workspace activates them by name in workspace.yaml:

extends: core@2.0.0 # pin the pack; omit for no base layer
use:
  agents: [revenue-director]

Activation is agent-rooted: naming an agent pulls in the skills, object types, and playbooks it declares. To change one default agent's behavior, add a workspace file with the same slug and extends: core as a marker; the loader deep-merges it, and arrays like skills replace by default unless you use {$append: [...]}. Skipping the marker on a colliding slug is a hard error, by design (docs/entities/base-pack.md). More: /blog/base-packs-agents-you-inherit.

Standing responsibility vs. repeatable procedure: mission or workflow?

This is the split that trips people up, because both look like "the agent does a thing." Vocion's own framing:

A mission is a standing responsibility owned by one agent: the goal, what good looks like, and how much freedom the agent has. Missions hold no procedure and no trigger logic — they are the why. (docs/entities/mission.md)

A workflow is a deterministic procedure: the same structure on every run. It is the how, with human gates where a person has to look. (docs/entities/workflow.md)

Concretely: a mission names one owning agent, a goal, optional successCriteria and desiredArtifacts, and an autonomyPolicy.level from 1 to 5 — 1 is draft only, 2 asks before acting, 3 acts within rules, 4 manages a goal, 5 improves itself; levels 1 and 2 gate every external action, and internal analysis and drafting are never gated (docs/entities/mission.md). A workflow names an ordered list of steps and exactly one trigger (manual, event, or schedule) — the trigger logic that a mission deliberately does not carry.

Rule of thumb from the docs themselves: use a mission "when the work is open-ended and judgement-heavy, and the same responsibility recurs"; use a workflow "when the steps are fixed and identical every run" (docs/entities/mission.md). If you are still deciding whether a weekly pipeline check is a mission or a workflow, and the steps genuinely never change, it is a workflow with a schedule trigger; if the agent has to decide each week what actually needs attention, it is a mission with a schedule field for periodic charter checks. See also /blog/teams-the-org-chart-is-the-interface for how a mission's owning agent relates to its team.

How do you give the team knowledge and tools?

Three more file kinds round out a workspace. A skill is skills/<slug>/SKILL.md — YAML frontmatter (slug, name, description, optional playbooks) on top of a Markdown procedure, mounted into the agent's virtual filesystem and read on the model's own judgement. The description line is load-bearing: it is what the agent reads to decide whether the skill is relevant at all, so "write it for the model, not for a menu" (docs/entities/skill.md). The filename must be exactly SKILL.md — that is what the deepagents skills middleware looks for when it lazy-loads on activation.

<!-- skills/pipeline-health/SKILL.md -->
---
slug: pipeline-health
name: Pipeline Health
description: >-
  Assess the current pipeline: stage aging, stalled deals, slipped close
  dates, and raw-versus-weighted totals.
version: 1
---

# Pipeline Health

Produce a numbers-first read of the funnel.

Playbooks work the same way but attach as standing context rather than an on-demand skill. Sources are connector-backed data an agent may search, named in connectorSources: and drawn from the connector list Vocion actually ships — Google Drive, Gmail, Google Calendar, Google Ads, GA4, HubSpot, Jira, Slack, Strapi, S3, Zoom, Granola, plus web, local files, and file import. Object types are business records an agent can classify against, named in objectTypes:. Notably absent from all of this: there is no requiresApproval field on a skill. Approval "lives with actions (propose_action and the review queue), never with a skill definition. A skill can never grant itself sending rights" (docs/entities/skill.md).

Where does a human sign off?

In a workflow, not a mission — a mission's autonomy level governs whether an external action needs approval at all, but the actual pause-and-wait mechanics are workflow steps. Four step types exist: approve, ask, action, and sync. There is no skill step; skills are read by the agent on its own judgement, not scheduled as a workflow step (docs/entities/workflow.md). Here is a full workflow with one approve gate, taken from the docs:

slug: discovery-followup
name: Discovery Follow-up
description: Turn a discovery call into an approved follow-up email.
agent: revenue-lead
trigger:
  type: schedule
  cron: '0 12 * * 1-5'
steps:
  - name: refresh-mail
    type: sync
    sources: [gmail]
  - name: transcript
    type: ask
    prompt: Paste the discovery call transcript.
    default: '{{input.transcript}}'
  - name: review-draft
    type: approve
    prompt: Approve the follow-up email before it sends.
    reviews: transcript
  - name: send
    type: action
    action: gmail.send
    input:
      body: '{{steps.review-draft.output.body}}'

The approve step pauses in the review queue for a human decision; reviews names the earlier step whose output is being judged (docs/entities/workflow.md). The {{steps.review-draft.output.body}} syntax is the workflow's own interpolation — it also supports {{input.x}} and {{trigger.y}}. For the review queue and the API behind it, see /blog/ai-agent-approval-workflow.

How do you ship it?

Two commands, run from the core checkout, against a workspace directory:

npm run workspace:check -- ../workspace/harbor-supply
npm run workspace:apply -- ../workspace/harbor-supply --project harbor-supply

check validates every file and shows what would change, with no database writes; apply writes it and records a workspace_version audit row (docs/getting-started.md). Both commands are real npm scripts — workspace:check is apply-workspace.ts --dry-run, and workspace:apply is the same script without the flag (packages/core/package.json). Applies are idempotent and atomic per resource, so a validation failure in one file does not block the rest. --project <id|slug> tells apply which live tenant to write under, separate from the placeholder orgId in workspace.yaml.

The audit row is the point of the whole exercise: applyWorkspace writes to the workspace_version table, and every tool_call an agent makes afterward is stamped with the workspace_sha in effect at the time — the git commit when the tree is clean, <sha>-dirty-<hash> with local edits, and +core@<version> folded in when a base pack is pinned (docs/object-model.md; docs/getting-started.md). Any answer a lead agent gives traces back to the exact files that produced it. More on that trail: /blog/ai-agent-versioning-audit-trail.

How do you tell whether the team is any good?

Three signals, all first-party. Eval datasets (evals/<slug>.yaml, run with npm run eval:run --workspace @vocion/core) give a repeatable check against known inputs. Learning steps (learnings/<step>.yaml) are named buckets an agent owns and that render into its filesystem, so a correction a human makes today shows up as context tomorrow. Every tool call and workflow run traces to Langfuse for cost and behavior, on top of the workspace_sha stamp (docs/object-model.md). None of this replaces reading transcripts for the first few weeks, but it means you are not reading them cold.

What to build in the first week, in order

  1. workspace.yaml with a lead and an accountableUser.
  2. One lead agent, applied and talked to with no data behind it yet — confirm the wiring before the content.
  3. One team file and one specialist, so the lead has someone to consult.
  4. One skill the specialist mounts, written for the model.
  5. One source, so answers have real data behind them.
  6. One workflow with a single approve step, for the first action that actually leaves the workspace.
  7. workspace:check after every edit; workspace:apply when you mean it.

That is a complete, if small, agent team: a lead, a specialist, a team record, a skill, a data source, and one human gate — six files and a version row you can point to.

Start from docs/getting-started.md if you want the full thirteen-step build with a schedule, a business object type, and trust rules added on top. Building a team is not a bigger version of building one agent; it is the same file shapes, repeated with a parent and a team: field, checked and applied the same way every time.

Clone vocion-core, run workspace:scaffold, and write your first lead agent before you add any data behind it.