Skip to main content

Artifacts

Artifacts are structured, interactive outputs that agents produce during conversations. Instead of returning plain text, an agent can produce a rich artifact — an email draft, a meeting invite, a CRM records table, a task, or a calendar view — that the user can review, edit, and act on directly in the chat interface.

How artifacts work

When an agent produces an artifact, it creates a typed payload with two components:
  • Content — the structured data the UI renders (e.g., email fields, meeting details, table rows)
  • State — lifecycle status driven by user actions (draft, sent, scheduled, confirmed, cancelled)
Artifacts are tied to conversations and persist across turns. The agent can see summaries of existing artifacts as context, enabling multi-turn refinement (“change the subject line”, “add Sarah to the invite”).

Artifact types

TypeDescriptionActions
Email draftStructured email with to/cc/bcc, subject, bodySend, edit, discard
Meeting inviteCalendar event with attendees, time, agendaSchedule via Google Calendar
TaskAction item with assignee, due date, priorityCreate in task system
Scheduled taskRecurring or time-triggered agent taskActivate, pause, delete
Slack messageFormatted Slack message draftSend to channel
Records tableCRM data table (accounts, contacts, opportunities)Sort, filter, drill-in
CalendarInline calendar viewNavigate, click events
PlanMulti-step execution planReview, modify steps

API

List registered artifact types

GET /artifacts/types
Returns all artifact types with UI metadata — label, icon, default display mode (panel or inline), and streaming support.

Get an artifact

GET /artifacts/{artifact_id}

List artifacts in a conversation

GET /artifacts/conversation/{conversation_id}

Update artifact state

When a user takes action on an artifact (sends an email, schedules a meeting):
PATCH /artifacts/{artifact_id}/state
{
  "sent": true,
  "sent_at": "2026-01-15T10:30:00Z"
}

Edit artifact content

For inline editing (e.g., changing email subject before sending):
PATCH /artifacts/{artifact_id}/content
{
  "subject": "Updated: Q3 Review Follow-up",
  "body": "Hi team..."
}

Execute artifact actions

# Create a task from a draft
POST /artifacts/{artifact_id}/create-task

# Schedule a meeting via Google Calendar
POST /artifacts/{artifact_id}/schedule-meeting

# Create or update a scheduled agent task
POST /artifacts/{artifact_id}/scheduled-task

# Toggle scheduled task active/inactive
POST /artifacts/{artifact_id}/scheduled-task/active

# Record user interaction (e.g., calendar click)
POST /artifacts/{artifact_id}/interaction

Summarizers and the caching layer

Every artifact type has a summarizer — a function that takes the full artifact content and produces a compact summary dict. This powers two critical features:

LLM context efficiency

When tool results are large (CRM record sets, email threads, meeting lists), the full data would consume the entire context window. Instead, AgentFlow:
  1. Caches the full result in a TTL-scoped result cache
  2. Summarizes it into a compact dict (total count, field names, 3-row preview)
  3. Returns the summary to the LLM conversation, with a cache_id reference
  4. The full data remains available through the conversation-scoped cached-results API while the entry is valid
This means a 50-row CRM query result becomes a ~200-token summary instead of consuming 10,000+ tokens — while the full data remains accessible.

Tool-result cache registration

Tools that produce cacheable structured output register their artifact type and cache summarizer together in artifacts/<type>/ARTIFACT.py. The @artifact(...) decorator registers the artifact type and preview summarizer; register_cached_tool(...) wires a tool’s result cache to reuse that same summarizer.
  • Registers a cache configuration (summarizer function, TTL, cache threshold)
  • Registers a matching artifact type (so the UI can render the data as a panel)
  • Links the tool result to both the cache and the artifact system
from framework.artifacts.specs import artifact
from framework.tools.result_summarizers import register_cached_tool


@artifact(type="account_health", label="Account Health", display_mode="panel")
def summarize(content: dict) -> dict:
    return {
        "title": content.get("account_name", "Account health"),
        "score": content.get("health_score"),
        "trend": content.get("trend"),
        "risk_factors": len(content.get("risks", [])),
    }


register_cached_tool(tool_name="get_account_health", summarizer=summarize)
The summarizer follows a dict -> dict interface — it takes the full result and returns the compact summary:
def summarize_account_health(result: dict) -> dict:
    return {
        "title": result.get("account_name", "Account health"),
        "score": result.get("health_score"),
        "trend": result.get("trend"),
        "risk_factors": len(result.get("risks", [])),
        "last_activity": result.get("last_activity_date"),
    }

Built-in summarizers

Tool categoryWhat’s summarizedTTL
CRM recordsTotal count, field names, 3-row preview30 min
EmailsTotal count, field names, 3-row preview1 hour
MeetingsTotal count, field names, 3-row preview1 hour
TasksTotal count, field names, 3-row preview30 min
Slack messagesTotal count, has_more flag, 3-message preview10 min
Record fields/viewsField names, object type, counts24 hours

Agent context injection

Agents automatically receive compact summaries of existing artifacts in the conversation as <system_events> parts. This means the agent knows what drafts are pending, what’s been sent, and what actions the user has taken — enabling natural follow-up interactions like “actually, change the meeting to 3pm” or “add the quarterly numbers to that email”. When a user takes an action (sends an email, schedules a meeting), an <artifact_action> system event is persisted so subsequent agent turns are aware of what changed.

Artifact feedback loop

Artifacts aren’t static outputs — they participate in a closed feedback loop that enables multi-turn refinement. This is the mechanism that makes “change the subject line” or “actually, schedule it for 3pm” work naturally across conversation turns.

How context parts accumulate

Each artifact interaction creates persistent system_event parts in the conversation:
TriggerContext typeWhat it containsVisible in timeline
Agent streams an artifact<artifact_summary>Compact summarizer output (to, subject, body preview)No (hidden)
User sends/schedules/creates<artifact_action>Action name + summary of what changedYes
User edits content inline<artifact_action>Fields that were modifiedYes
User clicks/interacts<artifact_action>Interaction data from the UIYes
These parts are persisted as system_event conversation parts with a stable part_id per artifact per action. On the next agent turn, the history rebuilder includes them as context — so the agent knows the email was sent, the meeting was scheduled, or the user edited the task description.

Revision support

Artifacts can be revised across turns. When the agent targets an existing artifact ID in its structured artifact payload, the middleware updates the existing artifact instead of creating a new one. This enables:
  • “Change the subject line” -> agent emits an updated draft_email payload for art_001
  • “Add Sarah to the invite” -> agent emits an updated draft_meeting_invite payload for art_002
  • The UI reflects the update in-place without creating duplicate cards