docs(guides): add Tool Trace and Platform Instructions (#85)
* docs(guides): add Tool Trace and Platform Instructions feature docs for Phase 34 features (PR #1686): - Tool Trace: explains what it captures, how to query activity logs, security/privacy properties, and use cases (compliance, debugging, verification) - Platform Instructions: explains global/workspace scopes, API endpoints for CRUD and resolve, content limits, security properties, and how it relates to Tool Trace as a complete governance loop Co-Authored-By: Technical Writer <technical-writer@molecule.ai> * chore(docs): add trailing newline to tool-trace.md * chore(docs): add trailing newline to platform-instructions.md * docs(guides): add run_id to Tool Trace schema + split outer/inner field tables --------- Co-authored-by: Molecule AI Technical Writer <technical-writer@agents.moleculesai.app> Co-authored-by: Technical Writer <technical-writer@molecule.ai> Co-authored-by: molecule-ai[bot] <276602405+molecule-ai[bot]@users.noreply.github.com>
This commit is contained in:
parent
a2a4d8b41d
commit
6566866b89
165
content/docs/guides/platform-instructions.md
Normal file
165
content/docs/guides/platform-instructions.md
Normal file
@ -0,0 +1,165 @@
|
||||
---
|
||||
title: "Platform Instructions"
|
||||
description: "Enforce system-prompt rules at the platform level — global org-wide rules and workspace-scoped rules injected at agent startup. Governance before the first turn, not after an incident."
|
||||
tags: [governance, security, platform-engineering, enterprise, system-prompt, policy]
|
||||
---
|
||||
|
||||
# Platform Instructions
|
||||
|
||||
Platform Instructions let workspace admins enforce behavioral rules at the system prompt level — injected before the first agent turn, not applied after an incident. Rules are stored in the platform database and resolved at workspace boot via the `GET /workspaces/:id/instructions/resolve` endpoint.
|
||||
|
||||
> **Enterprise plans only.** Platform Instructions are available on Enterprise plans. Contact your account team to enable them.
|
||||
|
||||
## How it works
|
||||
|
||||
When a workspace boots (or refreshes its instructions), it calls:
|
||||
|
||||
```
|
||||
GET /workspaces/:id/instructions/resolve
|
||||
Authorization: Bearer <workspace-token>
|
||||
```
|
||||
|
||||
The platform returns a merged instruction string:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspace_id": "ws_01hx3k...",
|
||||
"instructions": "# Platform-Wide Rules\n\n## Security\n\nAlways confirm destructive operations with the user before executing...\n\n## Role-Specific Rules\n\n### Onboarding helper\n\nYou are helping new users set up their first workspace..."
|
||||
}
|
||||
```
|
||||
|
||||
This string is prepended to the agent's system prompt as `# Platform Instructions` — the first section, before all other content. Because it goes first, it has highest precedence. Agents receive these instructions at boot and on every periodic refresh; they cannot be overridden by the agent's own prompt.
|
||||
|
||||
## Types of instructions
|
||||
|
||||
| Scope | Description | Use case |
|
||||
|---|---|---|
|
||||
| `global` | Applies to every workspace in the org | Security policy, compliance rules, brand voice |
|
||||
| `workspace` | Applies to one specific workspace | Per-project rules, team-specific behavior, onboarding |
|
||||
|
||||
Instructions are merged at resolve time: global rules are applied first, workspace rules second. Within each scope, rules are ordered by `priority` (higher first).
|
||||
|
||||
The `team` scope is reserved in the schema but not yet implemented.
|
||||
|
||||
## Create a global instruction
|
||||
|
||||
### Via the platform API
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-tenant.moleculesai.app/admin/instructions \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"scope": "global",
|
||||
"title": "Security policy",
|
||||
"content": "Always confirm destructive operations (delete, revoke, terminate) with the user before executing. Never execute destructive commands without explicit approval.",
|
||||
"priority": 100
|
||||
}'
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "instr_abc123",
|
||||
"scope": "global",
|
||||
"title": "Security policy",
|
||||
"content": "...",
|
||||
"priority": 100,
|
||||
"enabled": true,
|
||||
"created_at": "2026-04-30T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
## Create a workspace-scoped instruction
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-tenant.moleculesai.app/admin/instructions \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"scope": "workspace",
|
||||
"scope_target": "ws_01hx3k...",
|
||||
"title": "Onboarding helper",
|
||||
"content": "You are helping a new user set up their first Molecule AI workspace. Keep explanations concise. Offer to walk through the Canvas UI tour after setup.",
|
||||
"priority": 50
|
||||
}'
|
||||
```
|
||||
|
||||
`scope_target` accepts a workspace ID. When resolved for that workspace, the response includes both global rules and this workspace-specific rule.
|
||||
|
||||
## List all instructions
|
||||
|
||||
```bash
|
||||
# Global instructions only
|
||||
curl -s "https://your-tenant.moleculesai.app/admin/instructions?scope=global" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" | jq .
|
||||
|
||||
# Instructions for a specific workspace (global + workspace)
|
||||
curl -s "https://your-tenant.moleculesai.app/admin/instructions?workspace_id=ws_01hx3k..." \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" | jq .
|
||||
```
|
||||
|
||||
## Resolve instructions for a workspace
|
||||
|
||||
```bash
|
||||
curl -s "https://your-tenant.moleculesai.app/workspaces/ws_01hx3k.../instructions/resolve" \
|
||||
-H "Authorization: Bearer $WORKSPACE_TOKEN" | jq .
|
||||
```
|
||||
|
||||
The `Authorization` header here uses the **workspace's own token** (from `POST /registry/register` or `POST /workspaces/:id/tokens`). The resolve endpoint is gated by `WorkspaceAuth` — a workspace can only resolve its own instructions.
|
||||
|
||||
## Update an instruction
|
||||
|
||||
```bash
|
||||
curl -X PUT https://your-tenant.moleculesai.app/admin/instructions/instr_abc123 \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"content": "Updated rule — new wording",
|
||||
"priority": 80
|
||||
}'
|
||||
```
|
||||
|
||||
The workspace picks up the change on its next instruction refresh (periodic, not immediate).
|
||||
|
||||
## Delete an instruction
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://your-tenant.moleculesai.app/admin/instructions/instr_abc123 \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN"
|
||||
```
|
||||
|
||||
Returns `404` if the instruction does not exist.
|
||||
|
||||
## Content limits
|
||||
|
||||
Instruction content is capped at **8,192 characters** per rule. This prevents a single oversized rule from consuming the entire system prompt token budget. The cap is enforced at creation and update time — requests with content exceeding the limit receive a `400 Bad Request`.
|
||||
|
||||
For longer policy documents, consider:
|
||||
- Splitting into multiple rules with lower priority
|
||||
- Linking to external policy documents in the rule content
|
||||
- Using the rule as a summary with a reference to the full policy
|
||||
|
||||
## Security properties
|
||||
|
||||
**Agents cannot override Platform Instructions** — the instruction string is prepended to the system prompt at the platform layer, before the agent runtime processes it. An agent cannot edit, delete, or suppress its own Platform Instructions.
|
||||
|
||||
**Workspace-scoped rules are private** — a workspace can only resolve its own instructions. It cannot enumerate other workspaces' instructions, even if it knows their IDs.
|
||||
|
||||
**Content is org-scoped** — instructions live at the org level (global scope) or workspace level. There is no cross-org visibility.
|
||||
|
||||
## Relationship with Tool Trace
|
||||
|
||||
Platform Instructions enforce rules **before** the agent runs. Tool Trace records what the agent **actually did**. Together they provide a complete governance loop:
|
||||
|
||||
1. **Platform Instructions** — set expectations at startup (what the agent should and should not do)
|
||||
2. **Tool Trace** — verify compliance at runtime (what the agent actually did)
|
||||
|
||||
If Tool Trace shows an agent calling tools that Platform Instructions explicitly prohibit, that's a compliance incident — not a configuration issue.
|
||||
|
||||
## Related
|
||||
|
||||
- [Tool Trace](/docs/guides/tool-trace) — Verify what agents actually did, not just what they said they would do
|
||||
- [Org-Scoped API Keys](/docs/guides/org-api-keys) — Attribute tool calls to specific org credentials for billing and audit
|
||||
- [A2A Protocol](/docs/api-protocol/a2a-protocol) — How agents communicate and how Tool Trace travels in A2A responses
|
||||
164
content/docs/guides/tool-trace.md
Normal file
164
content/docs/guides/tool-trace.md
Normal file
@ -0,0 +1,164 @@
|
||||
---
|
||||
title: "Tool Trace"
|
||||
description: "See exactly what your agents did — every tool call, input, and output preview, stored in your activity logs. Tool Trace ships inside every A2A response and requires zero instrumentation."
|
||||
tags: [observability, debugging, compliance, enterprise, tool-trace]
|
||||
---
|
||||
|
||||
# Tool Trace
|
||||
|
||||
Tool Trace records every tool an agent calls — the tool name, input arguments, and a sanitized output preview — and stores it in your org's `activity_logs` table. It ships inside every A2A response, requires zero SDK instrumentation, and is queryable via the platform API.
|
||||
|
||||
> **Built in, not bolted on.** Tool Trace is enabled by default on all workspaces. There is no feature flag to enable — it starts recording the moment an agent makes its first A2A call.
|
||||
|
||||
## What Tool Trace captures
|
||||
|
||||
Each A2A response from a workspace includes a `metadata.tool_trace` array. The platform extracts it and persists it to `activity_logs` for every logged event:
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "log-abc123",
|
||||
"activity_type": "a2a_call",
|
||||
"workspace_id": "ws_01hx3k...",
|
||||
"method": "message/send",
|
||||
"created_at": "2026-04-30T12:01:00Z",
|
||||
"tool_trace": [
|
||||
{
|
||||
"tool": "mcp__files__read",
|
||||
"input": {"path": "config.yaml"},
|
||||
"output_preview": "api_version: v2, region: us-east-1, ..."
|
||||
},
|
||||
{
|
||||
"tool": "mcp__httpx__get",
|
||||
"input": {"url": "https://api.example.com/status"},
|
||||
"output_preview": "{\"status\": \"ok\", \"latency_ms\": 42}"
|
||||
}
|
||||
],
|
||||
"duration_ms": 1842
|
||||
}
|
||||
```
|
||||
|
||||
### Field definitions
|
||||
|
||||
Activity log fields (the outer object that wraps a tool trace):
|
||||
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| `run_id` | Unique identifier for this activity log row. Links the `tool_trace` to its originating A2A run — use this to correlate traces from fan-out tasks across multiple workspace logs. |
|
||||
| `activity_type` | The type of logged event (e.g., `a2a_call`, `a2a_receive`, `task_update`) |
|
||||
| `workspace_id` | The workspace that generated this log entry |
|
||||
| `method` | The A2A method invoked (e.g., `message/send`) |
|
||||
| `created_at` | ISO 8601 timestamp when the entry was written |
|
||||
| `duration_ms` | Total elapsed time of the call in milliseconds |
|
||||
| `tool_trace` | Array of tool call objects (see below) |
|
||||
|
||||
Tool trace object fields (each entry in the `tool_trace` array):
|
||||
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| `tool` | The tool or function that was invoked (e.g., `mcp__files__read`, `Bash`, `commit_memory`) |
|
||||
| `input` | The arguments passed to the tool. Sensitive values (API keys, tokens, long strings) are sanitized before storage. |
|
||||
| `output_preview` | First 200 characters of the tool's output. Caps large responses to prevent `activity_logs` bloat. |
|
||||
|
||||
## Querying activity logs
|
||||
|
||||
### List recent tool traces for a workspace
|
||||
|
||||
```bash
|
||||
curl -s "https://your-tenant.moleculesai.app/workspaces/$WS_ID/activity?limit=10" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" | jq '.[] | {created_at, tool_trace}'
|
||||
```
|
||||
|
||||
### Find all calls to a specific tool
|
||||
|
||||
```bash
|
||||
curl -s "https://your-tenant.moleculesai.app/workspaces/$WS_ID/activity?limit=50" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||
| jq '.[] | select(.tool_trace != null) | {created_at, tools: [.tool_trace[].tool]}'
|
||||
```
|
||||
|
||||
### Trace a specific task
|
||||
|
||||
```bash
|
||||
# List recent logs and filter by tool
|
||||
curl -s "https://your-tenant.moleculesai.app/workspaces/$WS_ID/activity?limit=50" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||
| jq '[.[] | select(.tool_trace | length > 0) | {
|
||||
time: .created_at,
|
||||
method: .method,
|
||||
calls: [.tool_trace[] | {tool, input}]
|
||||
}] | reverse | .[0:10]'
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
When a workspace sends an A2A response back to the platform, the platform's A2A proxy extracts `metadata.tool_trace` from the JSON-RPC response body:
|
||||
|
||||
```
|
||||
Agent → [runs task, calls tools] → A2A response with metadata.tool_trace
|
||||
↓
|
||||
extractToolTrace() in logA2ASuccess()
|
||||
↓
|
||||
Persisted to activity_logs.tool_trace (JSONB column)
|
||||
↓
|
||||
Indexed via GIN index for fast JSONB queries
|
||||
```
|
||||
|
||||
The `tool_trace` field in the A2A response is produced by the agent runtime — it reflects the tool calls that actually executed, not the tool calls the agent said it planned to make. This distinction matters for compliance: LLM output tells you what the agent *said* it would do; Tool Trace tells you what it *actually did*.
|
||||
|
||||
## Use cases
|
||||
|
||||
### Compliance and audit
|
||||
|
||||
For regulated environments, Tool Trace provides the execution record that proves an agent operated within its authorized scope. Query `tool_trace` for any call that reached external APIs or modified system state.
|
||||
|
||||
```bash
|
||||
# Find all HTTP tool calls in the last 24 hours
|
||||
curl -s ".../workspaces/$WS_ID/activity?limit=200" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||
| jq '[.[] | select(.tool_trace != null) |
|
||||
select(.tool_trace[] | .tool | contains("httpx"))] |
|
||||
map({time: .created_at, calls: [.tool_trace[]]})'
|
||||
```
|
||||
|
||||
### Debugging agent behavior
|
||||
|
||||
When an agent produces an unexpected result, Tool Trace shows exactly which tools were called and with what inputs — faster than replaying the full conversation.
|
||||
|
||||
```bash
|
||||
# Find a specific agent's call sequence for a given task
|
||||
curl -s ".../workspaces/$WS_ID/activity?limit=50" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||
| jq '.[] | select(.tool_trace | length >= 3) | {created_at, count: (.tool_trace | length)}'
|
||||
```
|
||||
|
||||
### Verifying tool coverage
|
||||
|
||||
Before deploying a new agent, verify it calls the expected tools under load.
|
||||
|
||||
```bash
|
||||
# Aggregate tool call counts for a workspace
|
||||
curl -s ".../workspaces/$WS_ID/activity?limit=100" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||
| jq '[.[] | select(.tool_trace != null) | .tool_trace[].tool] |
|
||||
group_by(.) | map({tool: .[0], count: length}) | sort_by(.count) | reverse'
|
||||
```
|
||||
|
||||
## Security and privacy
|
||||
|
||||
**Input sanitization** — API keys, long strings, and other sensitive values in `input` are sanitized before storage. The sanitization uses a best-effort pattern: sensitive key names (e.g., `key`, `token`, `password`, `secret`) and values longer than 200 characters are redacted.
|
||||
|
||||
**Output previews** — Tool outputs are capped at 200 characters to prevent `activity_logs` bloat and to limit the exposure of sensitive data in stored traces.
|
||||
|
||||
**Per-workspace isolation** — A `workspace_id` filter is required on all activity log queries. Admins cannot query other workspaces' activity logs without explicit access.
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Requires A2A** — Tool Trace is recorded for A2A calls only. Direct MCP tool calls that bypass A2A do not produce traces.
|
||||
- **Runtime-dependent** — The agent runtime must produce `metadata.tool_trace` in its A2A responses. Not all runtimes (e.g., custom external agents) include this field.
|
||||
- **No cross-workspace trace** — Each `activity_logs` row covers a single workspace. Tracing a task that fan-out to multiple agents requires correlating `task_id` across multiple workspace logs.
|
||||
|
||||
## Related
|
||||
|
||||
- [Platform Instructions](/docs/guides/platform-instructions) — Enforce rules at the system prompt level, before the agent runs
|
||||
- [Org-Scoped API Keys](/docs/guides/org-api-keys) — Attribute every tool call to a specific org key for billing and audit
|
||||
- [A2A Protocol](/docs/api-protocol/a2a-protocol) — The message format that carries `tool_trace` inside every response
|
||||
Loading…
Reference in New Issue
Block a user