Multi-Agent Collaboration: A 100-Minute AI Coding Walkthrough from Requirements to Deployment
This article explains how teams can move from single-prompt AI coding to coordinated multi-agent delivery. It covers capability-based role division, contract-first collaboration, shared workspace rules, and task selection by verifiability. It then walks through a suggested 100-minute exercise: structuring requirements, implementing backend and database logic, refining the frontend with visual agents, generating release videos with code, and handling deployment and observability. It also highlights common pitfalls and shows how to turn the workflow into reusable engineering assets.
From Single-Point Productivity to Coordinated Delivery
Many teams have already developed the habit of handing a requirement to AI and waiting for it to generate code. This pattern clearly pays off for standalone scripts and small pages. But when a task involves backend APIs, database structures, frontend interfaces, and release assets at the same time, a single assistant often falls short. The reason is not that the prompt is written poorly. Different tasks place very different demands on capability: some emphasize process reasoning and state consistency, while others emphasize reproducing design intent and pixel-level detail.
In practice, the same assistant often exceeds expectations on one type of task and requires repeated rework on another. Tasks that tend to perform well include logical reasoning, workflow orchestration, API definition, and structured output. Tasks that tend to be more challenging include fine-grained visual design, such as accurately reproducing spacing, hierarchy, font weight, and whitespace from a design draft while keeping the interface coherent across screen sizes. Such tasks depend both on knowledge of the design language and on sensitivity to visual details, which is exactly where general-purpose coding assistants are often weakest.
Therefore, building a high-quality delivery process is less about finding the single “strongest” assistant and more about designing a reasonable division of labor and handoff mechanism: let models that excel at logic handle backend logic and databases, let models that excel at visuals handle frontend interfaces, and introduce a programmable video generation approach so that release asset creation can also be automated.
This exercise targets a “release package” for a small task management service. It covers APIs, database, interface, and release video, helping readers walk through a complete multi-agent collaboration process in a relatively short time. The time allocation below is a suggested practice rhythm, not a promise about tool speed. First-time practice can be more relaxed; experienced teams can compress it further.
Coordinated Division of Labor: Assign Work by Capability Boundaries
Role Division
Split deliverables into distinct responsibility areas and assign a primary owner for each.
| Role | Responsibility | Core Requirements | Main Output |
|---|---|---|---|
| Logic-type agent | APIs, data models, business rules, automated tests | Strict process, traceable state, verifiable results | Runnable APIs and test reports |
| Vision-type agent | Page structure, style system, interaction states, accessibility | Consistent design language, precise details, high fidelity | Interactive interfaces and visual regression snapshots |
| Video generation approach | Release videos, feature demos, version notes | Code-friendly visuals, reproducible rendering | Video files and storyboard scripts |
| Human owner | Requirement confirmation, API freeze, final acceptance | Accurate judgment, timely decisions | Contract documents and acceptance conclusions |
Contracts First: Let Agents Build from a Frozen Specification
The most fragile part of multi-agent collaboration is the handoff, not the implementation quality of each agent. When two agents interpret requirements differently, the API fields, state values, and error codes they generate may not match. This multiplies correction costs during integration.
The solution is to freeze key agreements first with a short “contract first” step, then let each agent work independently under those constraints. Three documents are recommended:
- API contract: paths, methods, request and response fields, status codes, and error codes.
- Data model: table structures, field types, indexes, and constraints.
- Acceptance criteria: one executable check item per requirement, for example “return 400 when the title is empty.”
Example API contract:
# api-contract.yaml - Single source of truth shared by frontend and backendservice: task-serviceversion: v1conventions:ids: string # Use strings consistently to avoid precision loss for large integerstime: RFC3339 UTC # Use one time format and timezone consistentlyerrors: { code: string, message: string }endpoints:- method: POSTpath: /tasksrequest: { title: string!, projectId: string! }response: { id: string, status: queued|running|done, createdAt: string }errors: [400 invalid_input, 401 unauthenticated, 409 duplicate_title]- method: GETpath: /tasks/{id}response: { id: string, status: string, result: object }errors: [401 unauthenticated, 404 not_found]
Once the contract is frozen, both agents can move forward in parallel. The logic-type agent implements the APIs, while the vision-type agent can generate type definitions and mock data based on the API contract and start building pages without waiting for all APIs to be completed.
Shared Workspace and Handoff Rules
Multiple agents may modify the same codebase in parallel, which can cause overwrites or conflicts. Recommended rules:
- Directory partitioning: Agent A only writes backend and database directories; Agent B only writes frontend directories. Cross-directory changes are not allowed.
- Task-list-driven work: Break work into numbered small tasks and write them into a task list. Mark each task before starting it to avoid duplicate work.
- Handoff checkpoints: After each stage, run full tests and static checks, and output a change summary.
- Version traces: Commit a version snapshot at each stage so that deviations can be quickly traced back to the step that introduced them.
- Minimal tool authorization: Configure available tools and directory scopes separately for each agent. Irreversible operations such as deployment and database deletion should be excluded by default.
# agents.yaml - Use configuration files to constrain agent working scopeagents:logic-agent:cwd: server/write_scope: [server/src/**, server/migrations/**]commands: [npm test, npm run lint]forbidden: [deploy, db:drop]vision-agent:cwd: web/write_scope: [web/src/**]commands: [npm test]forbidden: [deploy]
Use Verifiability to Filter Tasks
A practical criterion when assigning tasks is whether acceptance criteria can be checked automatically. Whether an API returns the correct status code can be verified with test assertions. Whether page layout deviates from a design can be checked with screenshot comparisons. Whether a video frame contains incorrect copy can be checked frame by frame. Verifiable tasks are suitable for agents. Tasks that are not verifiable or rely heavily on subjective judgment should remain under human decision-making, such as product positioning, brand messaging, and final visual tradeoffs.
Phase 1 (About 5 Minutes): Structure the Requirements
Requirement structuring is the foundation of the entire pipeline. If you simply give an agent a sentence like “build a task management feature,” the result often lacks clear boundaries. A more effective approach is to write the requirement as a one-sentence summary plus explicit acceptance conditions.
Requirement summary template:
Background: A task management feature for a team, used by internal members to record, track, and close tasks.Scope: Task creation, task querying, and status transitions. Does not include notification push or permission tiers.Non-goals: No multi-tenancy, no file upload, and no native mobile app.Acceptance criteria:1. When the title is empty, the creation API returns 400 with error code invalid_input.2. When the title is duplicated within the same project, the creation API returns 409.3. Status can only transition one way: queued -> running -> done. Invalid transitions return 400.4. The task list is paginated by creation time in descending order by default, with 20 items per page.
Then ask the logic-type agent to produce a first draft of the API contract and data model based on the summary. After human review, freeze them. This step does not take much time but can significantly reduce later rework.
Phase 2 (About 35 Minutes): The Logic-Type Agent Builds Backend and Data
Turn the Contract into Migration Scripts
The database structure is the foundation of backend logic. A recommended approach is to first have the agent generate migration scripts based on the data model, then have a human check whether indexes and constraints are reasonable.
-- 0001_create_tasks.sqlCREATE TABLE tasks (id BIGSERIAL PRIMARY KEY,project_id BIGINT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,title VARCHAR(200) NOT NULL CHECK (length(btrim(title)) > 0),status VARCHAR(16) NOT NULL DEFAULT 'queued'CHECK (status IN ('queued','running','done')),created_at TIMESTAMPTZ NOT NULL DEFAULT now());-- Keep indexes aligned with the real query shape: filter by project and order by creation time descendingCREATE INDEX idx_tasks_project_created ON tasks (project_id, created_at DESC);-- Enforce unique title per project at the database level to avoid duplicate writes under concurrencyCREATE UNIQUE INDEX uq_tasks_project_title ON tasks (project_id, title);
There are two design choices worth noting. First, non-null and value-range constraints are guaranteed by the database. Even if the application layer misses something, invalid data will not be written. Second, the unique index is placed at the database layer rather than relying only on application-level checks, because two concurrent requests may both pass a duplicate-title check.
Implement API Handlers and Enforce State Transitions
Next, have the agent generate handler code according to the API contract and explicitly implement state transition rules:
// server/src/tasks.ts (illustrative code)const ALLOWED_TRANSITIONS: Record<string, string[]> = {queued: ["running"],running: ["done"],done: [],};export async function createTask(input: CreateTaskInput) {const title = input.title.trim();if (!title) throw new HttpError(400, "invalid_input", "title must not be empty");const dup = await db.query("SELECT 1 FROM tasks WHERE project_id = $1 AND title = $2",[input.projectId, title]);if (dup.rowCount) throw new HttpError(409, "duplicate_title", "title already exists");const row = await db.query(`INSERT INTO tasks (project_id, title) VALUES ($1, $2)RETURNING id, status, created_at`,[input.projectId, title]);return row.rows[0];}export async function transitionTask(id: number, to: string) {const cur = await getTask(id);if (!ALLOWED_TRANSITIONS[cur.status]?.includes(to)) {throw new HttpError(400, "invalid_transition", `${cur.status} -> ${to} is not allowed`);}return updateStatus(id, to);}
After implementation, ask the agent to add test cases covering normal paths, boundary values, and error paths:
// server/test/tasks.test.ts (illustrative code)test("Returns 400 invalid_input when title is empty", async () => {await expect(createTask({ projectId: 1, title: " " })).rejects.toMatchObject({ status: 400, code: "invalid_input" });});test("Returns 409 duplicate_title when title is duplicated within the same project", async () => {await createTask({ projectId: 1, title: "Release launch" });await expect(createTask({ projectId: 1, title: "Release launch" })).rejects.toMatchObject({ status: 409, code: "duplicate_title" });});test("Cannot transition from done status", async () => {await expect(transitionTask(doneTaskId, "running")).rejects.toMatchObject({ status: 400, code: "invalid_transition" });});
A few practices are worth following when asking an agent to write backend code:
- Ask for tests before implementation: Have the agent write tests first, then let the tests drive implementation. This clearly reduces code that “looks runnable but gets edge cases wrong.”
- Specify technical constraints explicitly: For example, “use parameterized queries for all database access” can avoid SQL injection risks caused by string concatenation.
- Require change summaries: At the end of each task, list modified files and reasons so humans can review quickly.
The value of a logic-type agent is that it compresses repetitive structured work into minutes: migration scripts, CRUD APIs, validation logic, and test cases are all deterministic tasks with clear input/output specifications.
Phase 3 (About 25 Minutes): The Vision-Type Agent Refines the Frontend
The Special Challenge of Visual Tasks
Frontend visual quality is hard to measure simply by whether tests pass because it involves fidelity to design intent. Common problems include: spacing without a system, with elements using arbitrary values such as 13px or 17px; unclear hierarchy, where titles and body text lack sufficient contrast; missing interaction states, such as loading, empty lists, and error prompts; and rough responsive handling, where elements overlap on narrow screens.
These problems are rarely caught by functional tests, but they directly affect usability. Therefore, the frontend stage should use a model that excels at visual details and provide it with clear inputs.
Use Design Tokens as the Shared Input
The first material given to the vision-type agent should be design tokens, not a vague description. Design tokens define visual decisions such as colors, spacing, font sizes, and corner radii as variables. This ensures consistency and gives the agent a clear reference.
/* design-tokens.css */:root {--color-bg: #ffffff;--color-fg: #1f2329;--color-primary: #2a6ef5;--color-muted: #646a73;--space-1: 4px; --space-2: 8px; --space-3: 12px;--space-4: 16px; --space-6: 24px; --space-8: 32px;--radius-md: 8px;--shadow-card: 0 1px 2px rgba(0,0,0,.06), 0 1px 3px rgba(0,0,0,.10);--font-body: 14px;}
Correspondingly, require components generated by the agent to consume tokens only, with no magic numbers:
/* task-row.css */.task-row {display: flex; align-items: center; gap: var(--space-3);padding: var(--space-3) var(--space-4);border-radius: var(--radius-md);box-shadow: var(--shadow-card);font-size: var(--font-body); color: var(--color-fg);}.task-row__time { color: var(--color-muted); margin-left: auto; }
Four States Must Be Complete
A practical checklist: every page must include loading, empty, error, and normal content states. Missing empty states are one of the most common frontend delivery problems. They make users see a blank page on first use and cannot tell whether there is no data or loading failed.
// TaskList.tsx (illustrative code, React-style component)export function TaskList({ status, tasks, error }: Props) {if (status === "loading") return <Skeleton rows={5} />;if (status === "error") return <EmptyState title="Failed to load" action="Retry" />;if (tasks.length === 0) return <EmptyState title="No tasks yet" hint="Create your first task to start tracking" />;return <ul>{tasks.map(t => <TaskRow key={t.id} task={t} />)}</ul>;}
Interaction and Accessibility Requirements
Explicitly asking the agent to handle interaction details is often more efficient than fixing them afterward:
- Buttons, links, and inputs should have hover, focus, and disabled states.
- Form validation errors should be displayed near the corresponding field instead of being summarized at the top.
- Color contrast should meet WCAG AA requirements. Body text contrast against the background should be at least 4.5:1.
- Interactive elements should use semantic tags and include necessary ARIA attributes.
- Keyboard operation should be supported, with Tab order matching visual order.
The strength of a vision-type agent is that it can quickly generate many layout options and detailed styles, but it also needs constraints. Putting design tokens, state checklists, and accessibility requirements into the prompt can noticeably improve output quality.
Visual Regression: Guard Consistency with Screenshot Comparisons
After frontend changes, functional tests alone are not enough to catch style regressions. A screenshot comparison step can be added to the pipeline: take screenshots of key pages at fixed viewport widths and compare them with baseline images. If the pixel difference exceeds a threshold, stop the merge. This makes the impact of style changes visible and quantifiable.
Phase 4 (About 10 Minutes): Code-Driven Video Generation for Release Assets
From Changelog to Storyboard Script
Once the feature is complete, release videos are a common promotion requirement. Traditional editing relies on manual work, which is costly and slow to update. Code-driven video generation offers another approach: describe each frame in code, render frames with a renderer, and then compose them into a video file. Its advantages are version control, repeatable rendering, and automatic regeneration as features change.
The first step is to turn the changelog into a storyboard script:
{"width": 1920, "height": 1080, "fps": 30,"scenes": [{ "id": "title", "duration": 90, "text": "Task Management Service v1.0" },{ "id": "backend", "duration": 180, "text": "Backend and data layer completed" },{ "id": "frontend", "duration": 180, "text": "Frontend refined and ready" },{ "id": "cta", "duration": 120, "text": "Try it now" }]}
Scene Components and Rendering Entry
Each scene can be implemented as a pure functional component. It takes the current frame number as input and outputs the corresponding image. This design makes every frame predictable and independently debuggable.
// scenes/BackendScene.tsx (illustrative code)export const BackendScene = ({ frame, text }: SceneProps) => {const progress = Math.min(1, frame / 180);return (<div style={{ padding: 64, opacity: progress }}><h1 style={{ fontSize: 72 }}>{text}</h1><div style={{ height: 8, width: `${progress * 100}%`, background: "#2a6ef5" }} /></div>);};// Rendering entry: driven by the storyboard script, iterate over frame numbers to generate imagesexport const RemotionRoot = () => {const script = loadStoryboard();return script.scenes.map(s => (<Compositionkey={s.id}id={s.id}component={SCENE_MAP[s.id]}durationInFrames={s.duration}fps={script.fps}defaultProps={{ text: s.text }}width={script.width}height={script.height}/>));};
Rendering Performance and Asset Management
Rendering time for code-driven video is directly related to resolution, frame rate, and scene complexity. For 1080p video at 30 fps, about 10 seconds requires roughly 300 frames; more frames mean longer rendering time. Several optimization directions are useful in practice:
- Lower the frame rate: 24 fps is enough for explainer videos. The visual difference is small, and rendering volume drops by about 20%.
- Parallelize by scene: Since scenes have no dependencies on each other, render them in parallel and then combine them.
- Reuse static frames: Layers that do not change over time, such as text and icons, can be cached as image assets and reused to avoid repeated drawing.
- Store outputs in object storage: Upload rendered results to object storage and distribute them to the team via links instead of consuming codebase size.
Deployment and Runtime Observability
Delivering code is only the beginning. Deployment can also be handled by agents, but key configurations must be reviewed by humans.
Recommended deployment checks include:
- Externalized configuration: Sensitive information such as database connections and keys should be injected through environment variables, not written into the codebase.
- Reversible migrations: Every database migration should have a corresponding rollback script, validated in a pre-release environment.
- Health checks: Expose liveness and readiness probe endpoints so the container platform can determine instance status.
- Containerized delivery: Release as container images to keep development and production environments consistent.
Runtime observability usually includes three capabilities: logging collects and retrieves application logs; monitoring and alerting tracks core metrics such as latency, error rate, and resource usage and triggers notifications; and tracing identifies latency bottlenecks across service calls. If you use related services from Baidu AI Cloud, these capabilities can be configured in the console without building your own observability stack.
The instruction to an agent can be written like this:
Task: Generate deployment configuration for task-service.Requirements:1. Generate a Dockerfile with multi-stage builds and a final image based on a slim runtime.2. Generate health check endpoints /healthz and /readyz.3. Generate docker-compose.yml with two services: the application and the database. Persist data with volumes.4. List all configuration items that must be read from environment variables and explain them in the README.5. Do not write any real secrets. Use placeholders for example values.
One point to remember: changes involving production environments are high-risk operations. It is reasonable to let agents generate deployment scripts, but executing deployments or modifying database structures should retain human confirmation to avoid irreversible mistakes.
Common Pitfalls and How to Avoid Them
Out-of-sync contracts. After the API contract changes, only one agent is notified while the other still generates code according to the old version. To avoid this, put the contract file under version control, treat any change as an explicit handoff action, and update type definitions for both sides.
Duplicate work. Two agents modify the same file at the same time, and the later write overwrites the earlier one. To avoid this, strictly enforce directory partitioning and mark file ownership in the task list.
Test coverage bias. Agents tend to write tests for normal paths and ignore boundary and exception cases. To avoid this, explicitly require in the prompt that “every API must include at least one error-path test case,” and check them one by one during review.
Context loss. In long tasks, agents may forget early constraints. To avoid this, write key constraints into a specification file inside the project and have the agent read it at the beginning of each session instead of relying on conversation history.
Visual consistency regressions. After multiple revisions, spacing and colors gradually drift away from design tokens. To avoid this, add screenshot comparisons to the pipeline and check for hardcoded values during code review.
Conclusion: Turn Collaboration into a Workflow
This exercise covers a complete delivery chain: requirement structuring, backend logic and data layer implementation, frontend visual refinement, release video generation, and deployment and observability configuration. The core of multi-agent collaboration is not “having multiple models write code together,” but decomposing complex tasks into subtasks with clear boundaries, explicit acceptance criteria, and parallel progress, then using contracts and checkpoints to maintain handoff quality.
Once the workflow is stable, the next step is to turn it into reusable engineering assets: store task templates, API contract templates, design tokens, and checklists in the project repository. A new project can start quickly by replacing the business content. For teams, this means AI coding no longer depends on one person’s prompt-writing skill, but becomes an inheritable, measurable, and continuously improvable engineering practice.
