diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 00000000..69a6f523 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", + "name": "aixplain", + "description": "Claude Code plugins for the aixplain platform \u2014 search the marketplace and work with aixplain assets directly from your editor.", + "owner": { + "name": "aixplain", + "url": "https://aixplain.com" + }, + "plugins": [ + { + "name": "aixplain", + "displayName": "aixplain", + "description": "Search the aixplain marketplace and build, verify, debug, update, and export production aixplain agents from one plugin.", + "source": "./plugins/aixplain", + "category": "development", + "author": { + "name": "aixplain", + "url": "https://aixplain.com" + }, + "homepage": "https://docs.aixplain.com", + "repository": "https://github.com/aixplain/aiXplain" + } + ] +} diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 00000000..6162119c --- /dev/null +++ b/plugins/README.md @@ -0,0 +1,80 @@ +# aixplain Plugins + +Claude Code **plugins** for the [aixplain](https://aixplain.com) platform. + +A plugin bundles a [skill](../skills/README.md) together with the MCP servers it needs, so one install +gives your coding agent both the knowledge and the live connection. Where a skill teaches an agent how +to do something, a plugin also hands it the tools. + +--- + +## Available plugins + +| Plugin | Description | +|--------|-------------| +| [aixplain](./aixplain) | Search the aixplain marketplace through a bundled MCP, inspect real asset schemas, and build, verify, debug, update, or export production aixplain agents with SDK v2. | + +--- + +## Install + +``` +/plugin marketplace add aixplain/aiXplain +``` + +Then install the unified `aixplain` plugin. It expects an aixplain API key: + +```bash +export AIXPLAIN_API_KEY=your_api_key +``` + +Create a key at [studio.aixplain.com](https://studio.aixplain.com) under Settings → API Keys. + +--- + +## Plugin or skill? + +Both work. Pick by what you need: + +| | Skill alone | Plugin | +|---|---|---| +| Teaches the agent aixplain patterns | yes | yes | +| Connects live MCP servers | no — add them yourself | yes, bundled | +| Install | copy a folder to `~/.claude/skills/` | one command | + +A plugin's skill can always be lifted out and used on its own — copy its `skills//SKILL.md` to +`~/.claude/skills//SKILL.md` and configure the MCP server separately. Useful for clients that +read skills but not Claude Code plugins. + +--- + +## Layout + +``` +plugins// +├── .claude-plugin/plugin.json # manifest: name, description, author +├── .mcp.json # MCP servers, keys referenced as ${ENV_VAR} +├── README.md # what it does, install, what works +└── skills// + └── SKILL.md # the instructions the agent loads +``` + +`.mcp.json` must never contain a literal API key — reference an environment variable +(`"Authorization": "Bearer ${AIXPLAIN_API_KEY}"`) so nothing secret is committed. + +--- + +## Other MCP clients + +The MCP servers a plugin declares are client-agnostic and work in Cursor, VS Code, Codex, and Claude +Desktop. Only the plugin *packaging* is Claude Code specific. Each plugin's README documents the +equivalent config for other clients. + +--- + +## Contributing + +Add a folder under `plugins/`, following the layout above, and register it in the `plugins` array of +[`.claude-plugin/marketplace.json`](../.claude-plugin/marketplace.json) at the repo root. Verify every +documented call against a real run before committing it — a plugin that describes behavior the platform +does not have is worse than no plugin. diff --git a/plugins/aixplain/.claude-plugin/plugin.json b/plugins/aixplain/.claude-plugin/plugin.json new file mode 100644 index 00000000..0e17ec42 --- /dev/null +++ b/plugins/aixplain/.claude-plugin/plugin.json @@ -0,0 +1,20 @@ +{ + "name": "aixplain", + "displayName": "aixplain", + "description": "Discover aixplain marketplace assets and build, verify, debug, update, and export production aixplain agents.", + "version": "1.0.0", + "author": { + "name": "aixplain", + "url": "https://aixplain.com" + }, + "homepage": "https://docs.aixplain.com", + "repository": "https://github.com/aixplain/aiXplain", + "keywords": [ + "aixplain", + "marketplace", + "agents", + "agent-builder", + "sdk", + "mcp" + ] +} diff --git a/plugins/aixplain/.mcp.json b/plugins/aixplain/.mcp.json new file mode 100644 index 00000000..79d4f594 --- /dev/null +++ b/plugins/aixplain/.mcp.json @@ -0,0 +1,10 @@ +{ + "aixplain-marketplace-search": { + "type": "http", + "url": "https://models-mcp.aixplain.com/mcp/6960f934f316da19e5f22494", + "headers": { + "Authorization": "Bearer ${AIXPLAIN_API_KEY}", + "Accept": "application/json, text/event-stream" + } + } +} diff --git a/plugins/aixplain/README.md b/plugins/aixplain/README.md new file mode 100644 index 00000000..0297b887 --- /dev/null +++ b/plugins/aixplain/README.md @@ -0,0 +1,151 @@ +# aixplain plugin + +One Claude Code plugin for two connected jobs: + +- **Discover:** search the live aixplain marketplace through the bundled Marketplace Search MCP, inspect real schemas, and generate runnable SDK, REST, or MCP code. +- **Build:** create, verify, debug, update, and export production aixplain agents with the bundled Agent Builder skill. + +Install it once, then use either skill as the task requires: + +```text +/plugin marketplace add aixplain/aiXplain +/plugin install aixplain@aixplain +``` + +**Prefer a visual walkthrough?** Open the interactive [Install & Use aixplain guide](./install-guide.html) for copyable commands, first-use examples, and a standalone Agent Builder fallback. + + +Driven by the first-party Marketplace Search tool (`6960f934f316da19e5f22494`). A hosted equivalent — +the **Marketplace Concierge** agent — is published on the marketplace at +`aixplain/marketplace-concierge/aixplain` for anyone who would rather ask in Studio than in an editor. +Neither depends on the other. + +## Sharing this with someone + +**If they use Claude Code and you can send them files** — two steps, no repo access needed: + +1. Add the server: + ```bash + claude mcp add --transport http aixplain-marketplace-search \ + https://models-mcp.aixplain.com/mcp/6960f934f316da19e5f22494 \ + --header "Authorization: Bearer THEIR_API_KEY" + ``` +2. Send them `skills/marketplace-search/SKILL.md` to save at + `~/.claude/skills/marketplace-search/SKILL.md`. + +That is the whole thing. The plugin wrapper is a convenience for installing both at once — it is not +required, and the skill works standalone. + +**If they use Codex, Cursor, or another MCP client** — the server works anywhere; only the skill +packaging is Claude Code specific. See [Other clients](#other-clients) below. + +**As an installable plugin** — one command: + +``` +/plugin marketplace add aixplain/aiXplain +``` + +then install `aixplain`. This also installs the `marketplace-search` skill and wires up the +MCP server in one step. + +## Other clients + +The MCP server is client-agnostic. The skill is not — `SKILL.md` is a Claude Code format. + +**Codex** (`~/.codex/config.toml`) — Codex supports remote HTTP MCP servers via a bare `url =`. For a +static auth header the reliable path is the `mcp-remote` stdio bridge: + +```toml +[mcp_servers.aixplain-marketplace-search] +command = "npx" +args = ["-y", "mcp-remote", "https://models-mcp.aixplain.com/mcp/6960f934f316da19e5f22494", "--header", "Authorization:${AUTH_HEADER}"] + +[mcp_servers.aixplain-marketplace-search.env] +AUTH_HEADER = "Bearer YOUR_API_KEY" +PATH = "/opt/homebrew/bin:/usr/bin:/bin" +``` + +To give Codex the same query knowledge, paste the body of `skills/marketplace-search/SKILL.md` into +`~/.codex/AGENTS.md`. + +**Claude Desktop and older clients** without native remote MCP use the same `mcp-remote` bridge in +JSON form — see the MCP config block inside the skill. + +## Install + +```bash +export AIXPLAIN_API_KEY=your_aixplain_api_key +``` + +Get a key from https://studio.aixplain.com under account settings, then add this plugin. The bundled +`.mcp.json` wires up the marketplace search MCP server with that key — no config editing. + +The server is PROD, so you are searching the live catalog. + +## What you get + +The `marketplace-search` skill, plus the MCP server it drives. Ask in plain language: + +``` +Do we have a Whisper model? Who hosts it and what does it cost? +How many LLMs are on aixplain? How many hosted by OpenAI? +Which integrations are developed by aixplain? +Find me a speech-to-text model and show me how to call it +Find a web search tool and attach it to a new agent +``` + +One `search` spans agents, models, tools, and integrations at once — you do not need to know the asset +type up front. + +## What works today + +Verified against PROD on 2026-08-18: + +| | | +|---|---| +| Search across agents, models, tools, integrations in one call | yes | +| Filter by category, developer, supplier, host, function, type | yes | +| Read pricing, host, supplier, status | yes | +| Read an asset's real input schema | yes | +| Emit SDK / REST / MCP / agent-attach code from that schema | yes | +| Run and test an asset via generated SDK code | yes | +| Sort results (cheapest, newest) | no — no sort parameter; sort locally after paging | +| Run an asset via the MCP `run_*` actions | no — broken, use generated code instead | + +## Beyond search + +The skill reads each asset's real input schema (`list_inputs_*`) before writing a snippet, so the code +it emits uses the asset's actual parameter names rather than placeholders. From a single hit it can +produce: + +- a Python SDK call +- an `aix.Agent(tools=[...])` block that attaches the asset to a new agent +- a REST call with the right endpoint for that asset type +- an MCP config block so another client can use that specific asset directly + +## Layout + +``` +plugins/aixplain/ + .claude-plugin/plugin.json manifest + .mcp.json Marketplace Search MCP, ${AIXPLAIN_API_KEY} + skills/marketplace-search/ marketplace discovery and runnable-code guidance + skills/aixplain-agent-builder/ agent build, verification, update, and export guidance +``` + +The skill is usable on its own: copy `skills/marketplace-search/SKILL.md` to +`~/.claude/skills/marketplace-search/SKILL.md` and add the MCP server separately. + +## Notes + +- Models and tools are individually available over hosted MCP; agents and integrations are not. +- Pricing comes back in two shapes — per-unit (`price` + `unit_type`) and per-token (`input_price` / + `output_price`). The skill reports whichever applies. +- The search MCP tool ID (`6960f934f316da19e5f22494`) is stable across environments. + + +## Agent Builder + +Use the bundled `aixplain-agent-builder` skill to build, run, verify, debug, update, or export aixplain agents with SDK v2. It uses the same `AIXPLAIN_API_KEY` and can use Marketplace Search to discover an asset before attaching it to an agent. + +The skill validates generated export code before presenting it, keeps iteration limits in `agent.budget`, requires explicit consent before external connections, and verifies real runs before reporting success. diff --git a/plugins/aixplain/install-guide.html b/plugins/aixplain/install-guide.html new file mode 100644 index 00000000..fc1b977f --- /dev/null +++ b/plugins/aixplain/install-guide.html @@ -0,0 +1,264 @@ + + + + + + + Install aixplain for Claude Code + + + +
+
Claude Code plugin
+

Install aixplain.
Build what’s next.

+

Connect live Marketplace discovery and production-ready Agent Builder guidance in three quick steps.

+ +
+ +
+
+
Add marketplaceTell Claude Code where to find aixplain.
+
Set your keyUse your personal aixplain API key.
+
Install & askEnable the plugin, then describe your goal.
+
+ +
+
+

Install in three steps

+

Copy each command, run it in Claude Code, then mark it done.

+
+
+
+
1
+
+

Add the aixplain marketplace

+

Run this once to add the official plugin marketplace to Claude Code.

+
/plugin marketplace add aixplain/aiXplain
+
TipRun slash commands from the Claude Code prompt—not from your system terminal.
+
+
+ +
+
2
+
+

Set your aixplain API key

+

Create a key in aixplain Studio under Settings → API Keys, then export it in the terminal that starts Claude Code.

+
$ export AIXPLAIN_API_KEY="your_api_key"
+
Safe by defaultKeep this value private. The plugin reads the environment variable; it never needs a key written into a config file.
+
+
+ +
+
3
+
+

Install the unified plugin

+

This enables Marketplace Search and Agent Builder together. No separate MCP configuration is needed.

+
/plugin install aixplain@aixplain
+
DoneThe MCP connection uses your AIXPLAIN_API_KEY automatically.
+
+
+
+
+ +
+
+

Start with a plain-English request

+

You do not need to memorize asset IDs or schemas.

+
+
+
+ +

Discover a capability

+

Search the live marketplace, inspect real inputs, and get runnable code.

+
“Find a web search tool, inspect its inputs, and show me Python SDK code.”
+
+
+ +

Build an agent

+

Create, test, debug, or export an aixplain agent with safe defaults.

+
“Build an agent that creates an evidence-backed customer briefing from web research.”
+
+
+
+ +
+

No plugin support? Still use Agent Builder.

+
+
+

The Agent Builder skill works standalone

+

Copy the skill directly into Claude Code or give its SKILL.md to another agent host. Core build, run, debug, verify, and export workflows require only the aixplain Python SDK and your API key. Marketplace MCP discovery is optional.

+
+
$ cp -r skills/aixplain-agent-builder ~/.claude/skills/
+
+
+ What if a command does not work? +
Confirm that you are running the /plugin commands inside Claude Code, that the marketplace was added before installation, and that Claude Code was started in a shell where AIXPLAIN_API_KEY is set. For a standalone install, copy the entire skill folder so its references remain available.
+
+
+
+ + + + + + diff --git a/plugins/aixplain/skills/aixplain-agent-builder/SKILL.md b/plugins/aixplain/skills/aixplain-agent-builder/SKILL.md new file mode 100644 index 00000000..30d4ff80 --- /dev/null +++ b/plugins/aixplain/skills/aixplain-agent-builder/SKILL.md @@ -0,0 +1,273 @@ +--- +name: aixplain-agent-builder +description: Build, configure, deploy, run, debug, export, verify, and update aixplain agents from a user's problem or intent using the Python SDK v2. Use for single agents, teams, marketplace tools, integrations, custom Python tools, runtime Code Execution, knowledge, sessions, budgets, and inspectors. The skill works autonomously, asks only when an external integration or secret needs the user's decision, and returns a working app link. +metadata: {"requires": {"env": ["AIXPLAIN_API_KEY"], "bins": ["python3", "pip"]}} +--- + +# aixplain agent builder + +Turn a user's intent into a working, verified aixplain agent. Do the SDK work yourself; do not make the user design the schema, select routine defaults, or debug tracebacks. + +> **Current stable SDK:** `aixplain==0.2.47` (verified against the public release and repository on 2026-08-22). +> **SDK v2 only:** use `from aixplain import Aixplain`. Never use `AgentFactory`, `ModelFactory`, `TeamAgentFactory`, `aixplain.factories`, or other v1 APIs. If docs or an error lead to v1, translate the approach to v2 and follow `references/reliability-guidelines.md`. + +## Modes + +Infer the mode from the request; do not make the user choose: + +- **Build:** turn an intent into a configured, deployed, verified agent. +- **Run/debug:** load an agent by ID or exact name, reproduce the problem, inspect steps/governance/tool behavior, fix in place, and re-verify. +- **Export:** load an existing agent and recursively generate portable SDK v2 code for its tools, skills, inspectors, and subagents; syntax- and constructor-validate it before presenting it. See `references/agent-lifecycle.md`. + +## Operating contract + +1. **Start from intent.** Infer the job, users, inputs, outputs, success criteria, and likely next request. Prefer one capable agent; use a team only when roles genuinely need different tools, context, or delegation. +2. **Do not interview the user about routine configuration.** Choose names, descriptions, instructions, output format, budgets, models, action scopes, and iteration limits using safe defaults. +3. **Search before building.** Use the bundled Marketplace Search MCP when available to discover assets, actions, and input schemas before declaring a capability missing. Prefer native first-party assets, then supported integrations, then custom code. +4. **Ask only at the connection boundary.** Pause only when the user must choose whether to connect an external integration, authorize OAuth, provide a provider credential, or approve a consequential/irreversible action. Never invent secrets. +5. **Build proactively.** Anticipate the next useful capability when it is low-risk and clearly implied. Do not silently add access to external systems, broad permissions, or expensive behavior. +6. **Show configuration when it is ready.** After resolving assets and defaults, present the compact final schema so the user can request changes; do not expose a stream of intermediate choices. +7. **Deploy and prove it.** `save()` is not completion. Run realistic checks, inspect tool usage and governance, fix failures, then provide the app link. +8. **Never overwrite by accident.** If the request clearly targets an existing agent, update it in place. For a new build whose exact name already exists, choose a descriptive unique name and report the collision; do not overwrite or create opaque `(1)` duplicates. +9. **Apply verified reliability guidance.** Follow `references/reliability-guidelines.md` for known-safe patterns; when behavior differs, state the observation and use only a confirmed workaround. + +## Standalone compatibility + +This skill must work when it is installed directly as a Claude Code skill or used by an agent host that does not support Claude Code plugins or MCP servers. The aixplain Python SDK and `AIXPLAIN_API_KEY` are the only runtime requirements for core build, run, debug, update, verify, and export workflows. + +- Treat the bundled Marketplace Search MCP as an optional discovery enhancement, never as a prerequisite. +- When that MCP is unavailable, incompatible, or unauthenticated, use the SDK searches in **Resolve capabilities** and continue with the best supported asset, integration, or custom Python Sandbox tool. +- Do not report a capability as unavailable merely because the plugin or MCP cannot load. State that Marketplace discovery was skipped only when it materially limits the answer. +- Keep connection consent, least-privilege scopes, SDK v2 validation, and realistic verification requirements unchanged in standalone mode. + +## 1. Setup + +Install the exact tested stable release: + +```bash +python3 -m pip install --upgrade "aixplain==0.2.47" +``` + +Read the key from a secret source and pass it explicitly. Never print, commit, or embed it in generated code. + +```python +import os +from aixplain import Aixplain + +api_key = os.environ["AIXPLAIN_API_KEY"] +aix = Aixplain(api_key=api_key) +``` + +Keys are workspace-specific. `Forbidden resource` across known-valid assets usually means the key belongs to another workspace. + +## 2. Derive the schema + +Silently draft this internal blueprint from the user's intent: + +```yaml +name: concise job-based name +description: who it helps and the outcome +instructions: goal, workflow, tool-use rules, constraints, completion criteria +architecture: single | team +inputs: what a run needs +output_format: markdown | text | json +tools: minimum required capabilities +integrations: external systems requiring connection +knowledge_or_memory: none | session | shared-memory | knowledge-base | skill +runtime_code: true only if the agent must write/run code during a run +governance: least privilege, budget, inspectors when justified +verification: one realistic test per capability +``` + +Defaults: +- Use the platform default LLM unless the user requests a model or the default is incompatible with a tool. +- Use `markdown` for human deliverables and `json` only when another system consumes the output. +- Use a single agent unless specialist delegation improves reliability. +- Scope every tool to the smallest non-empty `allowed_actions` set. +- Give each tool a concise, action-oriented description that states when to use it and what it returns; distinct descriptions improve tool and subagent routing. +- Add runtime Code Execution only when the deployed agent needs calculations, transforms, plots, file processing, or dynamic code during a run. +- Use a Python Sandbox custom tool when the missing capability is deterministic code authored at build time. + +## 3. Resolve capabilities + +Search first: + +```python +tools = aix.Tool.search(query="web search").results +integrations = aix.Integration.search(query="calendar").results +models = aix.Model.search(query="reasoning").results +``` + +For broad discovery, use the aixplain Marketplace Search tool: + +```python +search = aix.Tool.get("6960f934f316da19e5f22494") +result = search.run(action="search", data={"query": "microsoft calendar"}) +for kind in ("agent", "model", "tool", "integration"): + for item in result.data.get(kind, {}).get("results", []): + print(kind, item["name"], item["path"], item["id"]) +``` + +Selection order: +1. Existing first-party marketplace tool. +2. Existing native integration. +3. Supported connector integration. +4. Custom Python Sandbox tool with `config={"code": ..., "function_name": ...}`. + +If an existing integration can do the job, ask whether the user wants to connect it. If no suitable tool exists, develop the missing deterministic tool with `code=` rather than asking the user to invent the implementation. Read `references/integration-connections.md` before connecting, and `references/integration-build-and-data.md` before authoring code. + +### Build-time code vs runtime code + +- **Python Sandbox integration** (`688779d8bfb8e46c273982ca`): you write fixed code now. Use when a capability is missing and should become a deterministic tool. +- **Code Execution tool** (`698cda188bbb345db14ac13b`): the agent writes/runs code later. Add only when runtime work actually requires it. + +## 4. Build + +```python +agent = aix.Agent( + name="Customer Briefing Agent", + description="Produces an evidence-backed customer briefing.", + instructions=( + "Gather the required evidence with the attached tools, reconcile conflicts, " + "and return a concise briefing with sources. Do not claim completion until " + "all required sections are present." + ), + tools=[tool], + output_format="markdown", + max_tokens=6000, +) +agent.save() +``` + +For structured output, set it on the agent: + +```python +agent = aix.Agent( + name="Lead Qualifier", + description="Qualifies a lead against the supplied criteria.", + instructions="Evaluate every criterion and explain the result.", + output_format="json", + expected_output={ + "type": "object", + "properties": { + "qualified": {"type": "boolean"}, + "reasons": {"type": "array", "items": {"type": "string"}}, + }, + "required": ["qualified", "reasons"], + }, +) +agent.save() +``` + +For teams, budgets, sessions, memory, and knowledge, read `references/agent-architecture.md`. For run controls and execution strategy, read `references/agent-runtime.md`. For updates or exports, read `references/agent-lifecycle.md`; read `references/inspectors.md` for runtime validation. + +## 5. Integration checkpoint + +Do not ask for general approval of the inferred schema. Ask a focused question only when connection is required: + +- Name the integration and why it is needed. +- List the minimum actions requested. +- State whether OAuth or a provider key is required. +- Offer the no-connection alternative, if one exists. + +After consent, create the tool from the integration in the current workspace and return the generated connect URL. Never hardcode a workspace-bound integration tool instance. + +## 6. Verify before reporting success + +Run one realistic query per capability. For every run: + +```python +result = agent.run(query="Create this week's customer briefing from the available sources.") + +used_units = [ + step.get("unit", {}).get("name") + for step in (result.data.steps or []) + if isinstance(step, dict) +] + +governance = result.data.governance or {} +assert governance.get("status", "ALLOWED") == "ALLOWED", governance +print(result.data.output) +print(used_units) +``` + +Completion gate: +- Every intended tool fired at least once when relevant. +- Output satisfies the user's requested result and format. +- Governance did not block the run; run `SUCCESS` alone is insufficient. +- Generated files have clickable HTTPS download links. +- Team output is synthesized, not an internal `delegate_task` trace. +- Integration permissions are the minimum required. +- Actual spend comes from `result.data.execution_stats["credits"]`. + +If a tool works standalone but fails inside the agent, isolate the orchestration issue before changing the schema. Apply the closest safe practice in `references/reliability-guidelines.md` and report the observed behavior clearly. + +## 7. Final response + +Lead with the result, not the code. Include: + +1. What the agent does. +2. App link: `https://app.aixplain.com/agents/`. +3. Compact final configuration: architecture, tools/integrations, action scopes, output, memory/knowledge, model override if any, budget/guardrails. +4. Verification evidence: test prompts, tools observed, pass/fail, governance status. +5. Connection link or remaining user action, only if required. +6. One likely next improvement, phrased as optional rather than another setup questionnaire. + +Keep build code available on request. When the user asks for integration code, provide a complete v2 snippet based on the relevant `integration-connections.md` or `integration-build-and-data.md` reference. + +## Updating an existing agent + +SDK `0.2.47` has a critical hydration bug: `Agent.get()` can populate each tool's `allowed_actions` from every returned parameter definition instead of the backend's persisted `actions` scope. Before any get→mutate→save cycle, fetch the raw agent and reapply its stored scopes: + +```python +agent_id = "" +raw = aix.Agent.context.client.get(f"v2/agents/{agent_id}") +agent = aix.Agent.get(agent_id) + +scopes_by_id = { + item.get("id"): list(item.get("actions") or []) + for item in (raw.get("tools") or []) + if isinstance(item, dict) and item.get("id") +} +for tool in agent.tools or []: + if getattr(tool, "id", None) in scopes_by_id: + tool.allowed_actions = scopes_by_id[tool.id] + +agent.instructions = "Updated instructions..." +agent.save() +``` + +This keeps the ID and external references stable without widening permissions. Verify behavior and raw persisted scopes again after every update. See `references/agent-lifecycle.md` and `references/reliability-guidelines.md`. + +## Reliability follow-up + +Use `references/reliability-guidelines.md` to choose a safe, verified path. If behavior differs, preserve a minimal redacted reproduction in local engineering notes and explain the confirmed workaround; do not invent a fix. Draft a public issue only after the user asks, targeting https://github.com/aixplain/aiXplain with SDK v2 examples only. + +## Reference routing + +- Marketplace discovery and exact MCP schemas: `../marketplace-search/references/effective-mcp-use.md` +- Connection consent, Slack, Web Search, OAuth, provider keys, MCP: `references/integration-connections.md` +- Custom Python tools, Code Execution, databases, knowledge, files: `references/integration-build-and-data.md` +- Agent shape, teams, budgets, sessions, memory, knowledge: `references/agent-architecture.md` +- Run controls, variables, execution strategy, reasoning models: `references/agent-runtime.md` +- Safe updates, debugging, portability, exports: `references/agent-lifecycle.md` +- Runtime inspectors and validation: `references/inspectors.md` +- Verified reliability practices and safe fallbacks: `references/reliability-guidelines.md` + +## Stable asset IDs + +| Type | Name | ID | +|---|---|---| +| Tool | Code Execution | `698cda188bbb345db14ac13b` | +| Tool | Marketplace Search | `6960f934f316da19e5f22494` | +| Tool | File Manager | `6a0216cffb2a801f1c41e32e` | +| Integration | Python Sandbox | `688779d8bfb8e46c273982ca` | +| Integration | Gmail | `6864328d1223092cb4294d30` | +| Integration | Slack | `686432941223092cb4294d3f` | +| Integration | Google Drive | `6864329b1223092cb4294d4e` | +| Integration | Google Calendar | `686432901223092cb4294d36` | +| Integration | aiR Knowledge Base | `6904bcf672a6e36b68bb72fb` | +| Integration | PostgreSQL | `693ac6e8217c7b13b480970f` | +| Integration | SQLite | `689e06ed3ce71f58d73cc999` | + +Docs: https://docs.aixplain.com · App: https://app.aixplain.com · SDK: https://github.com/aixplain/aiXplain diff --git a/plugins/aixplain/skills/aixplain-agent-builder/references/agent-architecture.md b/plugins/aixplain/skills/aixplain-agent-builder/references/agent-architecture.md new file mode 100644 index 00000000..76e057c2 --- /dev/null +++ b/plugins/aixplain/skills/aixplain-agent-builder/references/agent-architecture.md @@ -0,0 +1,144 @@ +# Agent architecture (SDK v2) + +Read this for agent shape, teams, budgets, sessions, memory, knowledge, or choosing build-time versus runtime code. + +## Single agent or team + +Use one agent by default. Use a team when at least one is true: +- roles need different tools or permissions; +- specialist context would otherwise overload one prompt; +- work can be delegated with clear outputs; +- an independent reviewer materially improves reliability. + +Do not create a team merely because the workflow has multiple steps. + +## Team agent + +```python +researcher = aix.Agent( + name="Evidence Researcher", + description="Finds and summarizes evidence from approved sources.", + instructions="Use the search tool; return source-backed facts only.", + tools=[search_tool], +).save() +researcher.budget.max_iterations = 30 +researcher.save() + +writer = aix.Agent( + name="Briefing Writer", + description="Turns validated evidence into a concise briefing.", + instructions="Write only from supplied evidence; flag gaps.", +).save() + +team = aix.Agent( + name="Customer Briefing Team", + description="Researches and writes a verified customer briefing.", + instructions="Delegate evidence gathering, then synthesize the final answer.", + agents=[researcher, writer], +).save() +team.budget.max_iterations = 15 +team.save() +``` + +Subagent names and descriptions drive routing. Make them outcome-specific. Use `agents=`, not deprecated `subagents=`. + +Typical iteration defaults: +- team lead: `12–15`; +- tool-heavy worker: `30–40`; +- toolless writer/reviewer: backend default is usually enough. + +Exhaustion can return an internal `[Calling tool delegate_task ...]` trace while run status is `SUCCESS`; validate output shape and governance. + +## Budgets + +Every agent has `agent.budget`. Mutate it before `run()` for a one-off cap; save it to persist a default. + +```python +agent.budget.max_cost = 0.25 +agent.budget.max_duration_seconds = 120 +agent.budget.max_iterations = 12 +agent.save() +``` + +Do not pass `budget=` to `agent.run()` on `0.2.47`; it is silently ignored. `max_iterations=` as a standalone field/argument is deprecated. + +A budget is a circuit breaker, not a billing ceiling: in-flight and subagent work can overshoot. Treat `result.data.execution_stats["credits"]` as actual spend. A budget block can still return run status `SUCCESS`; inspect `result.data.governance`. + +## Sessions (one conversation) + +```python +session = aix.Session(agent=agent, name="customer-thread").save() +first = agent.run("My company is Acme.", session=session) +second = agent.run("Which company did I mention?", session=session) +``` + +Sessions are synchronous. `run_async(..., session=...)` is not supported. + +## Choose the right context mechanism + +| Need | Use | Do not use it for | +| --- | --- | --- | +| A self-contained request | no retained context | facts needed only for one run | +| Continuity in one active conversation | `Session` | durable preferences or cross-conversation recall | +| Confirmed user or account context across conversations | Shared Memory | a large document corpus, secrets, or unverified model inferences | +| Retrieval from many documents by relevance | aiR Knowledge Base | durable per-user preferences or conversation state | +| A stable policy, procedure, or playbook always needed by the agent | `aix.Skill` | mutable customer facts or personal history | + +## Shared memory (across conversations) + +Use Shared Memory only when the agent must recall **confirmed, useful context** for the same user, customer, or account in a later conversation—for example, a reporting preference, approved account facts, or an agreed working convention. + +Before enabling it: + +1. Confirm that cross-conversation retention is required by the request; do not persist context merely because it might be useful later. +2. Explain the retained scope when it includes personal or customer information, and ask before enabling retention when that choice is not already clear from the request. +3. Store the minimum durable fact. Never store API keys, credentials, access tokens, payment data, or unverified model conclusions. +4. Use a stable, explicit `identifier` for the user/customer/account. Never use a global/default identity in a multi-user agent. +5. In teams, verify that every memory read and write carries the same intended identity, then test that one identity cannot retrieve another identity's context. + +```python +memory = aix.Tool( + integration="aixplain/shared-memory/aixplain", + name="Account Memory", + description="Stores confirmed, durable account context for the current account only.", + config={ + "max_memory_size": 256, + "size_management_policy": "summarize", + }, + allowed_actions=["insert", "get"], +).save() + +account_id = "customer-123" +memory.run( + action="insert", + data={"identifier": account_id, "content": "Prefers weekly summaries."}, +) +``` + +Use a `Session` instead when the context belongs only to the current conversation. Use a Knowledge Base when the agent must search a larger source corpus rather than recall durable account-specific facts. + +## Knowledge base vs skill + +- Use an aiR knowledge-base tool for a large corpus queried by relevance. +- Use `aix.Skill` for a bounded playbook/reference that should always be in context. + +```python +skill = aix.Skill( + name="Support Playbook", + description="Approved support workflow and policies.", + file_path="/absolute/path/to/playbook.md", +).save() + +agent = aix.Agent( + name="Support Agent", + description="Answers support questions using the approved playbook.", + instructions="Follow the attached playbook.", + skills=[skill], +).save() +``` + +`Session`, `Budget`, and `Skill` require SDK `0.2.47` or later. + +## Runtime Code Execution vs build-time Python Sandbox + +Use Code Execution (`698cda188bbb345db14ac13b`) when the agent must write and execute arbitrary code at runtime. Use Python Sandbox (`688779d8bfb8e46c273982ca`) when you can define the deterministic function during the build. See `integration-build-and-data.md`. diff --git a/plugins/aixplain/skills/aixplain-agent-builder/references/agent-lifecycle.md b/plugins/aixplain/skills/aixplain-agent-builder/references/agent-lifecycle.md new file mode 100644 index 00000000..e1162b31 --- /dev/null +++ b/plugins/aixplain/skills/aixplain-agent-builder/references/agent-lifecycle.md @@ -0,0 +1,117 @@ +# Agent lifecycle and portability (SDK v2) + +Read this to update, debug, export, or present an existing aixplain agent without changing its identity or widening access. + +## Update without changing the ID + +SDK `0.2.47` `Agent.get()` can widen hydrated tool scopes. Load the raw backend definition first and reapply its persisted `actions` values before mutating or exporting: + +```python +def load_agent_preserving_scopes(aix, agent_id: str): + raw = aix.Agent.context.client.get(f"v2/agents/{agent_id}") + agent = aix.Agent.get(agent_id) + scopes = { + item.get("id"): list(item.get("actions") or []) + for item in (raw.get("tools") or []) + if isinstance(item, dict) and item.get("id") + } + for tool in agent.tools or []: + tool_id = getattr(tool, "id", None) + if tool_id in scopes: + tool.allowed_actions = scopes[tool_id] + return agent, raw + +agent, raw = load_agent_preserving_scopes(aix, "") +agent.instructions = "Updated instructions..." +agent.description = "Updated description..." +agent.tools = updated_tools or agent.tools +agent.output_format = "markdown" +agent.save() +``` + +After saving, fetch `v2/agents/` again and assert each raw tool's `actions` equals the intended least-privilege scope. Do not trust hydrated `allowed_actions` until the bug is fixed. + +Do not recreate by default. Verify the same capabilities after every update. + +`0.2.47` fixed the earlier `Agent.get()`/`save()` default-LLM overwrite. If behavior suggests a regression, compare the backend model before/after and follow `reliability-guidelines.md` rather than adding a permanent workaround without evidence. + +## Portability + +A portable build must depend only on: +- the API key/environment; +- stable first-party marketplace asset IDs/paths; +- integration definitions from which workspace-local tools are created; +- inline custom code for agent-specific Python Sandbox tools. + +Never hardcode a workspace-bound OAuth, connector, MCP, or custom tool instance ID in reusable build code. + +## Final configuration summary + +After deployment, show only the resolved configuration: + +```yaml +architecture: single | team +model: platform default | explicit model +output: markdown | text | json +tools: + - name: ... + allowed_actions: [...] +integrations: + - name: ... + connection: connected | user action required +knowledge: none | session | shared memory | index | skill +runtime_code: enabled | disabled +budget: + max_cost: ... + max_duration_seconds: ... + max_iterations: ... +inspectors: [...] +``` + +Then provide `https://app.aixplain.com/agents/` and verification evidence. + +## Run or debug an existing agent + +Load by ID (preferred) or exact search match. Reproduce with the user's query before changing configuration. + +```python +agent = aix.Agent.get("") +result = agent.run(query="Reproduce the reported behavior.") + +for step in result.data.steps or []: + print(step.get("action"), step.get("unit", {}).get("name")) +print(result.data.governance) +print(result.data.output) +``` + +For long runs, use SDK polling rather than custom HTTP loops: + +```python +started = agent.run_async(query="Run the full analysis.") +result = agent.sync_poll(started.url) +``` + +Fix the smallest responsible layer—tool, action scope, description, instructions, budget, or model—save in place, and rerun the same reproduction plus regression checks. + +## Export an existing agent to portable SDK v2 code + +Use the SDK, not raw REST: + +```python +agent = aix.Agent.get("") +config = agent.to_dict() +members = agent.agents or [] +``` + +Recursively map the root and each member to `aix.Agent(...)` constructor arguments. Preserve name, description, instructions, output format, expected output, model configuration, budget, skills, inspectors, and action scopes. + +Portability rules for generated code: +- Read `AIXPLAIN_API_KEY` from the environment; never embed it. +- Fetch stable first-party marketplace assets by path/ID. +- Recreate OAuth, connector, MCP, database, and custom tools from their integration definitions; do not export workspace-bound tool-instance IDs as portable dependencies. +- Inline agent-specific Python Sandbox source using `config={"code": ..., "function_name": ...}`. +- Emit subagents before the root and attach them with `agents=[...]`. +- End with `.save()` and print `https://app.aixplain.com/agents/`. +- Generate `llm=...`, never unsupported `llm_id=...`; keep iteration limits in `agent.budget`, never top-level `max_iterations`. +- Before presenting an export, run `python -m py_compile `, inspect constructor arguments against SDK `0.2.47`, and ensure save/run side effects are guarded by `if __name__ == "__main__":`. +- Run the generated script in a clean workspace when practical and compare the resolved schema before calling the export portable. diff --git a/plugins/aixplain/skills/aixplain-agent-builder/references/agent-runtime.md b/plugins/aixplain/skills/aixplain-agent-builder/references/agent-runtime.md new file mode 100644 index 00000000..dcc04289 --- /dev/null +++ b/plugins/aixplain/skills/aixplain-agent-builder/references/agent-runtime.md @@ -0,0 +1,125 @@ +# Agent runtime (SDK v2) + +Read this for per-run controls, stateful or stateless execution, execution strategy, and model reasoning configuration. + +## Invocation and runtime controls + +Configure tools, model, `output_format`, `expected_output`, and the default `agent.budget` on the agent. `run()` controls one execution: + +```python +# Stateless reusable run: variables are currently unreliable; validate the result. +result = agent.run( + query="Create the customer briefing for Acme from the supplied context.", + variables={"account_name": "Acme"}, + identifier="customer-123", + attachments=["/absolute/path/to/brief.pdf"], + criteria="Include sources and flag unsupported claims.", +) + +# Stateful conversation: resolve non-secret values into the query; do not pass variables. +follow_up = agent.run( + query="For Acme, turn the prior briefing into an executive summary.", + session=session, +) +``` + +| Need | `run()` controls | Notes | +|---|---|---| +| Conversation | `query`, `session` | `session` accepts a `Session` or ID; do not combine it with `variables`. | +| Per-run input | `variables`, `identifier`, `attachments` | Variables are currently unreliable. For stateful runs, resolve non-secret values into `query`; use `attachments`, not deprecated `files`. | +| Quality and oversight | `criteria`, `inspectors` | Add inspectors only for consequential output checks. | +| Progress display | `progress_format`, `progress_verbosity`, `progress_truncate` | Use `status` or `logs`; leave unset in normal production runs. | +| Advanced execution | `tasks`, `prompt`, `history`, `execution_params`, `evolve` | Use only when the execution strategy requires them. `execution_params.max_iterations` is deprecated. | +| Transport | `timeout`, `wait_time`, `run_retries`, `run_retry_wait` | Client wait/retry controls, not agent behavior. | + +There is no `output_format=`, `expected_output=`, or `budget=` run override in SDK `0.2.47`. Set output contracts on `Agent`; set limits through `agent.budget` before running. `run_response_generation` is deprecated/ignored; do not use it in new builds. + +## Reusable run variables + +SDK `0.2.47` exposes `variables=`, but current platform substitution is unreliable and the SDK rejects `session=...` combined with `variables=...`. Until that is repaired, do not promise placeholder substitution. + +For a stateless run, use `variables=` only when a focused verification proves substitution occurred. For a stateful run—or whenever correctness matters—resolve non-secret values into the per-run query instead: + +```python +account_name = "Acme" # Never resolve secrets into prompts. +query = f"Create a customer briefing for {account_name} from the supplied context." +result = agent.run(query=query, session=session) +``` + +Pass credentials only through approved integration/auth flows. + +## Choose the execution strategy + +The schema selects the strategy; there is no separate `strategy=` flag: + +| Configuration | Use when | Behavior | +|---|---|---| +| neither `planner` nor `tasks` | the next step depends on observations | adaptive plan/act/observe loop | +| `tasks=[...]` only | the workflow and dependencies are known | deterministic task graph | +| `planner=` | the goal needs decomposition but execution should then be structured | planner creates the graph, then tasks execute | + +Static task example: + +```python +from aixplain.v2.agent import Task + +collect = Task( + name="collect", + instructions="Collect the required account evidence.", + expected_output="Evidence with sources", +) +summarize = Task( + name="summarize", + instructions="Create the final briefing from collected evidence.", + expected_output="A concise markdown briefing", + dependencies=[collect], +) +agent = aix.Agent( + name="Static Briefing Agent", + description="Runs a predictable evidence-to-briefing workflow.", + instructions="Complete each task and preserve source attribution.", + tasks=[collect, summarize], +).save() +``` + +Use a planner when task shape varies by request: + +```python +agent = aix.Agent( + name="Planning Analyst", + description="Plans and executes multi-step analyses.", + instructions="Plan the analysis, execute it, and verify the conclusion.", + planner="", +).save() +``` + +If both `planner` and `tasks` are present, treat the supplied tasks as seeds for the plan. Do not offer a “dynamic” strategy; it is not a stable v2 SDK mode. + +## Reasoning effort and per-role models + +Reasoning effort is model-specific. Check availability before setting it: + +```python +llm = aix.Model.get("") +if "reasoning_effort" in llm.inputs.keys(): + llm.inputs.reasoning_effort = "high" # model-supported values only + +agent = aix.Agent(name="Reasoning Agent", description="...", instructions="...", llm=llm) +``` + +For planned workflows, configure expensive reasoning only where it adds value: + +```python +planner = aix.Model.get("") +if "reasoning_effort" in planner.inputs.keys(): + planner.inputs.reasoning_effort = "high" + +agent = aix.Agent( + name="Planning Analyst", + description="Plans and executes variable multi-step analyses.", + instructions="Plan, execute, verify, then answer concisely.", + planner=planner, +).save() +``` + +SDK `0.2.47` accepts and persists `llm`, `planner`, `supervisor`, and `response_generator`, but that does not mean the runtime applies every role. Freshly verified against `aixplain-agents` `1.3.0` (`028a81ac`): only `llm` and `planner` model-parameter buckets are applied. `supervisor`, `responder`, and `inspector` buckets emit “not applied yet” warnings. The dedicated second-LLM response-generator pass and `run_response_generation` are deprecated and ignored. Do not configure `response_generator` in new builds; treat the field as compatibility-only until runtime support is restored and verified. diff --git a/plugins/aixplain/skills/aixplain-agent-builder/references/inspectors.md b/plugins/aixplain/skills/aixplain-agent-builder/references/inspectors.md new file mode 100644 index 00000000..95e430db --- /dev/null +++ b/plugins/aixplain/skills/aixplain-agent-builder/references/inspectors.md @@ -0,0 +1,83 @@ +# Inspectors and governance (SDK v2) + +Use inspectors only when runtime policy or quality enforcement is required. Least-privilege tool actions remain necessary; inspectors do not replace permissions. + +## Stable 0.2.47 surface + +The published `0.2.47` wheel intentionally uses a small, plain-data API: strings for `action`, `targets`, and `severity`; a Metric, asset ID, callable, or judge dict for `metric`. There are no public `InspectorActionConfig` or `EvaluatorConfig` classes in this release. + +```python +from aixplain.v2.inspector import Inspector, AUTO_DEFAULT_MODEL_ID + +inspector = Inspector( + name="Content Gate", + description="Blocks output that violates the approved content policy.", + severity="high", # low | medium | high | critical + targets=["output"], # input | steps | output | subagent name + action="abort", # continue | rerun | abort | edit + metric={ + "asset_id": AUTO_DEFAULT_MODEL_ID, + "prompt": "Fail output that violates the approved content policy.", + }, +) + +team.inspectors = [inspector] +team.save() +``` + +Do not import from `aixplain.modules.team_agent.inspector`; that is v1. Do not generate the older typed config-class form for SDK `0.2.47`. + +## Action choice + +- `abort`: hard policy violation. +- `rerun`: recoverable quality problem. Retry settings use a dict: + +```python +action={"type": "rerun", "max_retries": 2, "on_exhaust": "abort"} +``` + +- `edit`: sanitize output and requires `editor=` using the same accepted forms as `metric`. +- `continue`: observe/log without intervention. + +A custom callable can act as the judge: + +```python +def contains_required_citation(text: str) -> bool: + return "http" in text + +inspector = Inspector( + name="Citation Check", + targets=["output"], + action="rerun", + metric=contains_required_citation, +) +``` + +For reusable/model-backed judges, prefer an onboarded `aix.Metric` object or an asset ID plus prompt. + +## Mandatory policy verification + +After every inspector change, run exactly three cases: + +| Case | Expected behavior | +|---|---| +| Allowed | normal compliant answer | +| Denied | blocked/refused; restricted action or data absent | +| Ambiguous | conservative refusal or clarification request | + +Capture: + +```yaml +prompt: ... +expected_action: ... +observed_run_status: ... +observed_governance: ... +observed_output_summary: ... +pass_fail: ... +``` + +A policy block can return run status `SUCCESS`. Always read `result.data.governance` and inspect output; never equate `SUCCESS` with permission or completion. + +## Product boundary + +Debugger/Evolver meta-agents and dynamic self-improvement are not part of this skill's GA build path. Do not fabricate APIs. Use traces, controlled A/B changes, and explicit user review instead. diff --git a/plugins/aixplain/skills/aixplain-agent-builder/references/integration-build-and-data.md b/plugins/aixplain/skills/aixplain-agent-builder/references/integration-build-and-data.md new file mode 100644 index 00000000..65ca1631 --- /dev/null +++ b/plugins/aixplain/skills/aixplain-agent-builder/references/integration-build-and-data.md @@ -0,0 +1,180 @@ +# Integration build and data tools (SDK v2) + +Read this when creating deterministic tools, enabling runtime code, working with databases or knowledge, or handling files. + +## Missing capability: build a deterministic tool with `code=` + +Use the Python Sandbox integration when no existing tool/integration meets the need and the implementation should be fixed at build time. + +```python +code = """def normalize_account(name: str, domain: str): + clean_name = " ".join(name.split()).strip() + clean_domain = domain.lower().removeprefix("https://").rstrip("/") + return {"name": clean_name, "domain": clean_domain} +""" + +tool = aix.Tool( + name="Normalize Account", + description="Normalizes an account name and website domain.", + integration="688779d8bfb8e46c273982ca", + config={"code": code, "function_name": "normalize_account"}, + allowed_actions=["normalize_account"], +).save() +``` + +Authoring constraints verified on `0.2.47`: +- `function_name` must exactly match a function defined in `code`. +- Prefer `int` (`0`/`1`) over `bool` parameters because boolean serialization has produced invalid Python literals. +- Return `dict` or `list`, not tuples. +- Define custom code inline in portable build code; custom tool instance IDs are workspace-bound. +- Test the tool standalone before attaching it. + +### Python Sandbox authoring contract + +Use Python Sandbox for a small, deterministic function authored at build time—not for an agent to generate code during a run, and not as an implicit replacement for an approved external integration. + +- Define one clear entry function with JSON-compatible inputs and a `dict` or `list` result. +- Prefer the Python standard library. Do not assume third-party packages, network access, filesystem persistence, environment variables, or credentials are available unless the live integration schema and a standalone run prove the requirement works. +- Never embed API keys, tokens, customer data, or workspace-bound IDs in `code`. +- Keep side effects explicit and minimal. Use a supported integration with explicit user consent when the job needs an external system. +- Inspect the current integration actions and input schema before generating code that depends on a Sandbox capability. +- Run one representative success case and one expected failure case directly through the tool before attaching it to an agent; then verify the agent trace includes the tool when the capability is required. + +```python +result = tool.run( + action="normalize_account", + data={"name": " Acme Corp ", "domain": "https://ACME.EXAMPLE/"}, +) +assert result.data["domain"] == "acme.example" +``` + +## Runtime Code Execution + +Use Code Execution only when the deployed agent must decide and write code during a run (analysis, calculations, data transforms, plots, dynamic file processing, or API/URL processing). + +```python +code_execution = aix.Tool.get("698cda188bbb345db14ac13b") +code_execution.allowed_actions = ["run"] +``` + +Do not add it merely because the build itself uses Python. For deterministic logic, prefer a Python Sandbox tool. + +When generated files must be returned, instruct the runtime code to print metadata: + +```json +[{"name":"analysis.csv","file":"analysis.csv"}] +``` + +Without file metadata, generated files may be lost. + +## PostgreSQL + +Use read-only credentials whenever possible and reinforce read-only behavior in the agent instructions. + +```python +pg = aix.Tool( + name="Customer Database Reader", + description="Reads approved customer reporting tables.", + integration="693ac6e8217c7b13b480970f", + config={"url": database_url}, + allowed_actions=["schema", "query"], +).save() +``` + +Never enable write actions unless the user explicitly requires and approves them. + +## SQLite + +```python +from aixplain.v2.upload_utils import FileUploader + +url = FileUploader(api_key=api_key, backend_url=aix.backend_url).upload( + "/absolute/path/to/data.db", + is_temp=True, + return_download_link=True, +) + +sqlite = aix.Tool( + name="SQLite Reader", + description="Reads the supplied SQLite database.", + integration="689e06ed3ce71f58d73cc999", + config={"url": url}, + allowed_actions=["schema", "query"], +).save() +``` + +SQLite writes apply to an in-memory copy and are not a persistence strategy. + +## Knowledge base + +```python +index = aix.Tool( + name="Product Knowledge", + description="Searches approved product documentation.", + integration="6904bcf672a6e36b68bb72fb", +).save() + +index.run(action="upsert", data={"records": [ + {"id": "doc-1", "text": "Approved product facts.", "metadata": {"type": "product"}}, +]}) +index.allowed_actions = ["search", "get"] +index.save() +``` + +Tell the agent explicitly to search the index. Otherwise it may answer from the LLM while the knowledge tool remains unused. + +## Files in and out + +Local paths are not portable runtime inputs. Upload and use HTTPS URLs: + +```python +from aixplain.v2.upload_utils import FileUploader + +url = FileUploader(api_key=api_key).upload( + "/absolute/path/to/input.pdf", + is_temp=True, + return_download_link=True, +) +``` + +Always use `return_download_link=True` for a browser-accessible URL. + +### HTML and ZIP MIME safeguard + +Stable `0.2.47` omits `.html` and `.zip` from the fallback MIME map; unrecognized files fall back to `text/csv`. Patch before uploading these deliverables: + +```python +from aixplain.v2.upload_utils import MimeTypeDetector + +MimeTypeDetector.EXTENSION_MAPPING[".html"] = "text/html" +MimeTypeDetector.EXTENSION_MAPPING[".zip"] = "application/zip" +``` + +When a tool config needs an uploaded resource URL, construction alone does not upload. Call `.save()` and then read `.url`: + +```python +resource = aix.Resource(name="Input Database", file_path="/absolute/path/to/data.db") +resource.save() +assert resource.url +``` + +`aix.Resource.create_from_file(...)` only constructs the object in `0.2.47`; its URL remains empty until `.save()` uploads it. + +For deliverables, the File Manager tool is preferred: + +```python +import uuid + +file_manager = aix.Tool.get("6a0216cffb2a801f1c41e32e") +file_manager.allowed_actions = ["save_content"] +result = file_manager.run( + action="save_content", + data={ + "requestid": str(uuid.uuid4()), + "contents": ["final report"], + "names": ["report.md"], + }, +) +``` + +`requestid` is required. diff --git a/plugins/aixplain/skills/aixplain-agent-builder/references/integration-connections.md b/plugins/aixplain/skills/aixplain-agent-builder/references/integration-connections.md new file mode 100644 index 00000000..55767705 --- /dev/null +++ b/plugins/aixplain/skills/aixplain-agent-builder/references/integration-connections.md @@ -0,0 +1,131 @@ +# Integration connections (SDK v2) + +Read only the section needed for the current agent. All examples assume: + +```python +from aixplain import Aixplain + +aix = Aixplain(api_key=api_key) +``` + +Never use v1 factories. Before connecting, call `integration.list_actions()` and `integration.list_inputs(action_name)`. Scope every attached tool to a non-empty least-privilege `allowed_actions` list. + +## Discover before connecting + +```python +matches = aix.Integration.search(query="slack").results +for item in matches: + print(item.name, item.id, item.path) + +integration = aix.Integration.get(matches[0].id) +print(integration.list_actions()) +``` + +## Slack: discover and scope the exact action + +Preserve the explicit connection checkpoint. After the user agrees to connect Slack, inspect the action list and use the exact platform action name—not a conceptual alias: + +```python +slack = aix.Integration.get("686432941223092cb4294d3f") +actions = list(slack.list_actions()) +assert "SLACK_SEND_MESSAGE" in actions + +slack_tool = aix.Tool( + name="Approved Slack Sender", + description="Sends an approved message to the selected Slack destination.", + integration=slack, + allowed_actions=["SLACK_SEND_MESSAGE"], +).save() +``` + +Request only the minimum `chat:write` permission needed to send the approved message. Do not use `send_message` as an action name. + +## Web Search: inspect before generating runnable code + +Prefer the first-party aixplain Web Search tool when it satisfies the request. With this unified plugin, use Marketplace Search to find the tool, then call `list_actions_tools` and `list_inputs_tools` for the selected asset before writing code. Emit only the returned action name and input fields; never invent nested parameters. Validate the resulting SDK snippet before presenting it. + +Do not choose solely by display name. Prefer `aixplain/...` first-party paths over brokered connectors when both meet the requirement. + +## Existing marketplace tool + +```python +tool = aix.Tool.get("") +print(list(tool.actions)) +tool.allowed_actions = ["required_action"] +``` + +`tool.actions` is an `Actions` collection. Use `list(tool.actions)`; `.keys()` and numeric indexing are not reliable. + +## OAuth integration + +Ask before initiating the external connection. State the minimum actions and why they are needed. + +```python +import warnings + +integration = aix.Integration.get("6864328d1223092cb4294d30") # Gmail +print(integration.list_actions()) + +with warnings.catch_warnings(record=True) as captured: + warnings.simplefilter("always") + tool = aix.Tool( + name="Customer Email Sender", + description="Sends the final approved customer email.", + integration=integration, + allowed_actions=["GMAIL_SEND_EMAIL"], + ).save() + +connect_url = next( + (str(item.message) for item in captured if "http" in str(item.message)), + None, +) +print(connect_url) +``` + +The connection is workspace-specific. Create the tool fresh from the integration in each workspace; do not ship a saved connector tool ID in portable build code. + +OAuth is a deliberate two-phase checkpoint: print/present the connect URL and stop that execution. Never call `input()` or wait interactively inside generated scripts. After the user confirms authorization, continue agent creation or verification in a new execution using the in-memory tool when available or its recorded workspace-local ID. + +## Provider API-key integration + +Ask for the provider key only after the user agrees to connect the provider. Keep it in memory/environment and never commit or print it. + +```python +integration = aix.Integration.get("") +connected = integration.run( + name="Provider Tool", + authScheme="API_KEY", + data={"generic_api_key": provider_api_key}, +) +tool = aix.Tool.get(connected.data.id) +tool.allowed_actions = ["MINIMUM_REQUIRED_ACTION"] +tool.save() +``` + +The authentication field is `generic_api_key`; `api_key` and `apiKey` can fail with an unhelpful generic error. + + +## Remote MCP server + +```python +mcp = aix.Tool( + name="Remote MCP Tool", + description="Uses the approved remote MCP capability.", + integration="aixplain/mcp-server", + config={"url": "https://example.com/mcp"}, +).save() +mcp.allowed_actions = ["required_action"] +mcp.save() +``` + +Keep the exposed action set small; large MCP surfaces degrade tool selection. + +## Verification sequence + +For every connected or custom tool: +1. Run it directly with realistic input. +2. Attach it to the agent. +3. Run an agent prompt that requires it. +4. Confirm its unit name appears in `result.data.steps`. +5. Confirm governance allowed the run. +6. If observed behavior differs, follow the safe fallback in `reliability-guidelines.md` and keep a redacted local reproduction. diff --git a/plugins/aixplain/skills/aixplain-agent-builder/references/reliability-guidelines.md b/plugins/aixplain/skills/aixplain-agent-builder/references/reliability-guidelines.md new file mode 100644 index 00000000..e68a7757 --- /dev/null +++ b/plugins/aixplain/skills/aixplain-agent-builder/references/reliability-guidelines.md @@ -0,0 +1,47 @@ +# Reliability Guidelines + +Use these proven practices to build and maintain aixplain agents safely with SDK v2. They capture behavior verified against `aixplain==0.2.47` and help the agent choose dependable paths without exposing an internal defect log. + +## Build against the released SDK + +- Use `from aixplain import Aixplain` and SDK v2 objects only. +- Validate generated Python with `py_compile` and inspect constructors from the installed package before presenting exports. +- Use `llm=`, never `llm_id=`, in exported agent constructors. Keep iteration limits in `agent.budget`; do not set a top-level `max_iterations` field. + +## Configure budgets as guardrails, not invoices + +- Set durable controls on `agent.budget`; `agent.run(budget=...)` is not a reliable one-off override. +- Treat `max_cost` as a circuit breaker rather than an exact billing ceiling, especially for teams. +- Report actual usage from `result.data.execution_stats["credits"]` after every consequential run. + +## Verify capabilities from live schemas + +- Inspect Marketplace Search results through each kind's `results` collection, not `items`. +- Before generating Web Search or integration code, inspect the exact actions and input schemas. +- If Marketplace Search is unavailable, continue with SDK discovery and state only material limitations. + +## Keep integrations safe in real agent runs + +- Test each integration directly and again inside the agent. A connector that works alone may need a compatible model or deterministic application code when its structured output cannot be consumed in an agent loop. +- Request explicit user consent before connecting an external service, and use the smallest supported action scope. +- Call `resource.save()` before relying on a file resource URL. Set explicit MIME types for `.html` and `.zip` uploads when needed. + +## Preserve least privilege during updates + +Before a get → mutate → save update, fetch the raw agent payload and restore each tool's stored `actions` onto the hydrated tool's `allowed_actions`. Then verify the raw persisted scopes after saving. This prevents an update from unintentionally broadening tool permissions. + +## Prove required tool use + +A successful result is not proof that a required tool ran. For every realistic verification: + +1. Check the output and governance status. +2. Inspect `result.data.steps` for the intended tool unit. +3. When tool invocation is mandatory, prefer adaptive or planner execution and assert the unit is present. + +## Keep final-answer behavior explicit + +Do not rely on `response_generator` or a deprecated separate response-generation pass. Put final-answer requirements in the primary agent instructions, or model a visible task/subagent when a separate synthesis step is required. + +## Escalate responsibly + +If observed behavior contradicts these guidelines, preserve a minimal, redacted reproduction in local engineering notes, use the safest confirmed workaround, and draft a public issue only when the user asks. Never include API keys, customer data, or workspace-bound IDs. diff --git a/plugins/aixplain/skills/marketplace-search/SKILL.md b/plugins/aixplain/skills/marketplace-search/SKILL.md new file mode 100644 index 00000000..2cc14bce --- /dev/null +++ b/plugins/aixplain/skills/marketplace-search/SKILL.md @@ -0,0 +1,217 @@ +--- +name: marketplace-search +description: Search the aixplain marketplace for agents, models, tools, and integrations — check whether an asset exists, what it costs, who hosts it, how many of a kind there are — and turn any hit into working code (SDK, REST, MCP config, or an agent that attaches it). Use whenever the user asks what is on aixplain, asks for a model/tool by name or capability, asks the price or host of an aixplain asset, or asks how to call or attach one. +--- + +# aixplain Marketplace Search + +Searches the aixplain catalog through the `aixplain-marketplace-search` MCP server, then hands back +something runnable. One `search` covers all four asset types at once — you never need to know whether +the thing you want is a model, a tool, an agent, or an integration. + +Every fact you report must come from a tool result. Never invent an asset, price, host, or count. +If every type returns `total: 0`, say the asset is not on the marketplace. + +## Setup check + +The tools are named `mcp__aixplain-marketplace-search__*`. If they are not available, the plugin's MCP +server has not connected — almost always a missing key. Tell the user: + +```bash +export AIXPLAIN_API_KEY=your_aixplain_api_key +``` + +Keys come from https://studio.aixplain.com under account settings. The server is PROD. + +## Which tool to call + +| Question | Call | +|---|---| +| Does X exist? What is it? | `search` with `query` | +| Price, host, supplier, status | `get_asset_details` with `asset_id` | +| How many X are there? | `search` with `query: ""` plus filters, read `stats.total` | +| What inputs does it take? | `list_inputs_models` / `list_inputs_tools` / `list_inputs_agents` / `list_inputs_integrations` | +| What actions does it have? | `list_actions_models` / `list_actions_tools` / … | +| What filter values are valid? | `list_filters` (no arguments) | + +`search` parameters are all flat strings or numbers — **not** arrays: `query`, `asset_type`, +`categories`, `developers`, `suppliers`, `hosts`, `function`, `num_results`, `page_number`, `page_size`. +`get_asset_details` takes `asset_id` (snake_case) and optionally `asset_type`. + +`search` returns one block per asset type, each `{results: [...], stats: {total, pages_count, current_page}}`. + +### The surface is only universal at discovery + +Of the 19 actions, three are universal — `search`, `get_asset_details`, `list_filters` — and sixteen are +per-type variants of `search_*`, `list_actions_*`, `list_inputs_*`, `run_*`. **There is no universal +`run`.** Do not look for one. Resolve the asset's type from the `search` block it came back in (or from +`get_asset_details.asset_type`), then use the matching per-type action. + +`get_asset_details` is the exception worth knowing: it takes `asset_id` alone, with `asset_type` +optional, so you can read any asset's details without knowing its type first. + +### Counting + +Pass an empty `query` as a wildcard and read `stats.total`. Add `page_size: 1` so you are not paying for +rows you will not read: + +``` +search(query: "", asset_type: "model", categories: "LLM", page_size: 1) → stats.total = 177 +``` + +Filter by `hosts` or `developers` the same way to narrow a count ("how many hosted by OpenAI"). + +There is **no sort parameter** — `search` orders by relevance only. To answer "cheapest", "newest", or +"most expensive" you must page through the filtered set and sort locally, which for a big category +(LLM alone is 177) means several calls. Say that is what you are doing rather than presenting a +single page's minimum as the catalog's minimum. +Do not count with `search_models` / `search_tools` / `search_agents` / `search_integrations` — those cap +results and carry no reliable total. Use them only to list examples. + +Valid `categories` (from `list_filters`): LLM, Productivity, Marketing, Finance & Accounting, Utility, +Sales, Customer Support, Communication, Cybersecurity, Developer Tools, Search, Speech, +E-commerce & Payments, Analytics, Data & Storage, Language, Miscellaneous, Media & Creative, +Development, Image, Classification, Video, OCR, Guardrails. Call `list_filters` for the current +developer/host/supplier lists rather than guessing a name. + +### Matching behavior + +Keyword matching is literal. If a multi-word phrase returns nothing, retry with one distinctive token +before concluding the asset is absent — "speaker diarization whisper" may miss where "whisper" hits. + +### Pricing has two shapes + +Report whichever the tool returned; do not normalize one into the other. + +- Per-unit assets: `{price, unit_type, unit_type_scale}` → "0.0018 per MINUTE" +- Token-priced LLMs: `{input_price, output_price}` → "0.000003 in / 0.000015 out per token" +- `null` or absent → say pricing is not listed. + +For hosting, report the returned `hosted_by` value only. If it is absent or empty, say **“Hosting provider is not listed.”** Never infer a host from a model name, supplier, or price. + +## Always finish the lookup + +When the user asks about cost, host, supplier, function, or status, call `get_asset_details` on the +best match in the same response as the `search`. Do not stop at `search` and do not ask permission to +look up details. If several assets match strongly, detail the most relevant and name the others. + +## Turning a hit into code + +Resolve the asset's `id`, `path`, and `asset_type` first. For web search, prefer the first-party aixplain Web Search tool when it meets the request. Then **call the matching `list_actions_*` and `list_inputs_*` tools** and build the snippet from the real returned action and input names — `source_audio`, `text`, +`sourcelanguage`, whatever the asset actually declares. Only fall back to a `""` +placeholder if `list_inputs_*` returns nothing usable. Never invent a parameter name. + +Note which inputs are `required`, and which are `isFixed: true` with a single allowed value — a fixed +input should be emitted as that literal value, not as a choice for the user to fill in. Inputs with an +`availableOptions` list are enums: pick from it (language codes, for example) rather than free-texting. + +## Testing an asset before you wire it in + +**Do not use the `run_models` / `run_tools` / `run_agents` / `run_integrations` MCP actions.** They take +a single `input` string, which cannot express the multi-field input real assets declare, and they fail in +practice — verified 2026-08-18: an LLM returned `err.supplier_error` ("Input required: specify prompt or +messages") and a translation model returned HTTP 491, while the same asset ran fine through the SDK. + +To actually test an asset, write the SDK call from `list_inputs_*` and run it in the shell: + +```bash +python3 -c ' +from aixplain import Aixplain +aix = Aixplain(api_key="'"$AIXPLAIN_API_KEY"'") +r = aix.Model.get("").run() +print(r.status, r.data) +' +``` + +Report the real `status` and `data`. That closes the loop — found, verified running, then integrated — +and it is the only execution path that works today. + +### Python SDK + +`run()` takes the input names as **keyword arguments** — not a positional dict. Use the exact `name` +values from `list_inputs_*`: + +```python +from aixplain import Aixplain +aix = Aixplain(api_key="YOUR_API_KEY") + +# model — one kwarg per declared input (verified against Cloud Translation) +r = aix.Model.get("66aa869f6eb56342c26057e1").run( + text="Good morning", sourcelanguage="en", targetlanguage="ar") +print(r.status, r.data) # SUCCESS صباح الخير + +aix.Tool.get("").run(action="", data={...}) # tool +aix.Agent.get("").run(query="...") # agent +``` + +`Model.get("").run({...})` with a positional dict raises `TypeError` — always kwargs. +Read the result off `r.status` and `r.data`. + +`Model.get` / `Tool.get` also accept the supplier path (`"openai/whisper-large/groq"`), not just the id. +Integrations need a one-time connect (OAuth or API key) before they can run — point the user at the +integration's page rather than emitting a one-line call. + +### Attach it to an agent + +This is usually what the user actually wants after finding a tool: + +```python +from aixplain import Aixplain +aix = Aixplain(api_key="YOUR_API_KEY") + +agent = aix.Agent( + name="...", + description="...", + instructions="...", + tools=[aix.Tool.get("")], +).save() + +agent.run(query="...") +``` + +### REST + +Headers `x-api-key: YOUR_API_KEY` and `Content-Type: application/json`. + +- model: `POST https://models.aixplain.com/api/v2/execute/` — body from `list_inputs_models` +- tool: `POST https://models.aixplain.com/api/v2/execute/` — body `{"action": "", "data": {...}}` +- agent: `POST https://platform-api.aixplain.com/v2/agents//run` — body `{"query": "..."}` + +A response may return a `requestId` to poll: models and tools at +`GET https://models.aixplain.com/api/v2/data/`, agents at +`GET https://platform-api.aixplain.com/sdk/agents//result`. + +### MCP config for another asset + +Models and tools are each individually available over hosted MCP — agents and integrations are not. +Endpoint: `https://models-mcp.aixplain.com/mcp/` (encode `/` as `%2F`). + +Native streamable HTTP — Claude Code, VS Code, newer Cursor: + +```json +{"mcpServers": {"": { + "type": "http", + "url": "https://models-mcp.aixplain.com/mcp/", + "headers": {"Authorization": "Bearer YOUR_API_KEY", "Accept": "application/json, text/event-stream"} +}}} +``` + +stdio bridge — Claude Desktop and older clients without native remote MCP: + +```json +{"mcpServers": {"": { + "command": "npx", + "args": ["-y", "mcp-remote", "https://models-mcp.aixplain.com/mcp/", "--header", "Authorization:${AUTH_HEADER}"], + "env": {"AUTH_HEADER": "Bearer YOUR_API_KEY", "PATH": "/opt/homebrew/bin:/usr/bin:/bin"} +}}} +``` + +Pass the key via `env.AUTH_HEADER` and reference it as `Authorization:${AUTH_HEADER}` — a literal space +inside a single `mcp-remote` arg breaks it. On Apple Silicon `npx` is usually `/opt/homebrew/bin/npx`; +keep its directory on `PATH`. + +## Answering + +Lead with the direct answer — yes/no, or the number — then a short bulleted detail block: path, +function, price, host, supplier, status. When listing matches, give name plus path so the user can +identify the exact asset. Never paste raw tool JSON. diff --git a/plugins/aixplain/skills/marketplace-search/references/effective-mcp-use.md b/plugins/aixplain/skills/marketplace-search/references/effective-mcp-use.md new file mode 100644 index 00000000..2fa5e9a9 --- /dev/null +++ b/plugins/aixplain/skills/marketplace-search/references/effective-mcp-use.md @@ -0,0 +1,12 @@ +# Effective Marketplace MCP use + +Use this sequence whenever an answer depends on marketplace truth or runnable asset code: + +1. **Discover:** call `search` with a focused query. Retry with one distinctive token if a phrase misses. +2. **Identify:** take `id`, `path`, and `asset_type` from the returned result block. +3. **Confirm facts:** call `get_asset_details` before reporting price, supplier, status, or hosting. If `hosted_by` is empty, say “Hosting provider is not listed.” +4. **Inspect the contract:** call the asset-type-specific `list_actions_*` and `list_inputs_*` actions. Use only returned action names and fields. +5. **Generate and validate:** build SDK/REST/agent-attach code from that schema. Do not invent inner parameters. Prefer the first-party aixplain Web Search tool for web-search use cases when it fits. +6. **Test correctly:** do not use MCP `run_*` actions; generate the SDK call and run it with realistic input instead. + +Use `search` plus `stats.total` for counts. `search_*` is for examples, not authoritative totals. There is no universal run action and no server-side sort parameter. diff --git a/skills/README.md b/skills/README.md index f7c9d02b..0443289d 100644 --- a/skills/README.md +++ b/skills/README.md @@ -32,6 +32,9 @@ The agent reads `SKILL.md` first and pulls in the reference files only when it n > More skills coming soon. See [Contributing](#contributing) to add one. +Looking for a one-command install that also wires up live MCP servers? See +[`plugins/`](../plugins/README.md) — a plugin bundles a skill together with the servers it needs. + --- ## Prerequisites diff --git a/skills/aixplain-agent-builder/SKILL.ar.md b/skills/aixplain-agent-builder/SKILL.ar.md deleted file mode 100644 index 5cd1d55d..00000000 --- a/skills/aixplain-agent-builder/SKILL.ar.md +++ /dev/null @@ -1,174 +0,0 @@ ---- -name: aixplain-agent-builder -description: A skill to build, deploy, and run production-grade AI agents on aiXplain. -metadata: {"requires": {"env": ["AIXPLAIN_API_KEY"], "bins": ["python3", "pip"]}} ---- - -# aiXplain Agent Builder - -بناء ونشر وتشغيل وإدارة وكلاء aiXplain — الوكلاء المنفردون، وكلاء الفريق، الأدوات، وتكاملات OAuth. - -## كيف يعمل - -تقبل هذه المهارة الأوامر التالية: - -- **بناء وكيل** — التخطيط، والبحث عن الأدوات، وإنشاء الأدوات، وتجميع الوكيل وحفظه -- **نشر وكيل** — الحفظ ومشاركة روابط Studio للتحرير المرئي والتحليلات -- **تشغيل وكيل** — التنفيذ المتزامن أو غير المتزامن وإرجاع المخرج -- **تصحيح أخطاء وكيل** — تشخيص مشاكل الأدوات أو الإجراءات أو OAuth أو وقت التشغيل -- **تصدير وكيل** — إنشاء سكريبت Python مستقل من وكيل منشور - -## 1. الإعداد - -قم دائمًا بتثبيت/ترقية أحدث إصدار من SDK قبل القيام بأي شيء: `pip install --upgrade aixplain`. - -هذه المهارة مُتحقق منها مقابل **SDK v0.2.44**. إذا اختلف الإصدار المُثبَّت، أبلغ المستخدم. - -```python -import os -from pathlib import Path -from dotenv import load_dotenv -from aixplain import Aixplain - -env_path = Path(".env") -if env_path.exists(): - load_dotenv(env_path) -else: - env_path.write_text("# Get your key from https://platform.aixplain.com → Settings → API Keys\nAIXPLAIN_API_KEY=\n") - print(f"Created {env_path.resolve()} — paste your API key, then re-run.") - raise SystemExit(1) - -api_key = os.getenv("AIXPLAIN_API_KEY") or os.getenv("AIXPLAIN_KEY_NUR") -if not api_key: - print(f"No API key found. Open {env_path.resolve()} and paste your key.") - raise SystemExit(1) - -os.environ["AIXPLAIN_API_KEY"] = api_key -aix = Aixplain(api_key=api_key) -``` - -## 2. التخطيط - -قبل البناء، قدِّم خطة للمستخدم تتضمن: اسم الوكيل، والوصف، والتعليمات، والأدوات/التكاملات المستخدمة، وما إذا كان وكيلًا منفردًا أو وكيل فريق. انتظر الموافقة قبل المتابعة. - -**ابحث دائمًا قبل إنشاء أو ترميز أداة أو تكامل بشكل ثابت.** كل من `Tool` و`Model` و`Integration` تدعم `.search(query=...).results`. لا تقل أبدًا "غير متاح" دون البحث أولًا. - -```python -results = aix.Tool.search(query="web search").results -# Names may be concatenated (e.g. "Googledrive"). Normalize before comparing. -``` - -إذا لم يتوفر أداة أو تكامل في السوق للقدرة المطلوبة، أعلن أنك ستبنيها كدالة **Python Sandbox** (انظر `references/integration-playbooks.md § 4`). - -## 3. إنشاء الأدوات - -ثلاثة مسارات، بترتيب الأفضلية: - -```python -# Path A: Marketplace tool by ID -tool = aix.Tool.get("698cda188bbb345db14ac13b") # Code Execution - -# Path B: Non-OAuth integration (KB, SQLite) -# READ references/integration-playbooks.md for config payloads. -# Ask the user for any missing inputs — never invent placeholders. -# Always create fresh; do not reuse by name unless explicitly asked. -tool = aix.Tool(name="KB Search", description="Search product docs", - integration="6904bcf672a6e36b68bb72fb", allowed_actions=["search", "get"]).save() - -# Path C: OAuth integration (Gmail, Slack, Jira, Google Drive) -# READ references/integration-playbooks.md § 5 for full workflow. -# (1) integration = aix.Integration.get("") -# (2) integration.list_actions() → discover action names -# (3) Create tool with allowed_actions in constructor -# (4) User completes OAuth via redirect URL emitted at .save() -# (5) Attach in-memory tool object to agent and save - -# Path D: Python Sandbox (last resort) -# READ references/integration-playbooks.md § 4 for config shape. -``` - -## 4. بناء الوكيل - -يستخدم الوكلاء نموذج LLM الافتراضي للمنصة — لا تحدد `llm` إلا إذا طلب المستخدم نموذجًا معينًا. - -```python -agent = aix.Agent( - name="My Agent", description="...", instructions="...", - tools=[tool], output_format="markdown", -).save() -``` - -إذا أثار `save()` خطأ `name_already_exists`، اسأل المستخدم: تحديث الوكيل الحالي أو الإنشاء باسم جديد. - -## 5. النشر والتشغيل - -بعد النشر، شارك هذه الروابط مع المستخدم: -- **المُنشئ المرئي:** `https://studio.aixplain.com/build//schema` -- **التحليلات:** `https://studio.aixplain.com/dashboard/analytics/?agent=` - -```python -# Sync -result = agent.run(query="...", executionParams={"maxTokens": 6000}, runResponseGeneration=True) -print(result.data.output) - -# Async polling -import requests, time -ar = agent.run_async(query="...") -for _ in range(30): - time.sleep(10) - raw = requests.get(ar.url, headers={"x-api-key": api_key}).json() - if raw.get("status", "").upper() in ("SUCCESS", "FAILED"): - print(raw["data"]["output"]); break -``` - -## 6. تصحيح الأخطاء - -### فحص الخطوات الوسيطة - -```python -result = agent.run(query="...") - -# Step-by-step trace: thought, action, tool used, input/output, tokens -for step in result.data.steps: - print(step['thought'], step['action'], step['unit']['name']) - -# Aggregate costs and timing -stats = result.data.execution_stats -print(stats['credits'], stats['runtime'], stats['api_calls']) -``` - -أو اعرض التتبعات بصريًا في Studio: `https://studio.aixplain.com/build//schema` - -## معرّفات الأصول السريعة - -| النوع | الاسم | المعرّف | -|------|------|---------| -| أداة | Tavily Web Search | `6931bdf462eb386b7158def3` | -| أداة | Code Execution | `698cda188bbb345db14ac13b` | -| أداة | Google Search API | `692f18557b2cc45d29150cb0` | -| أداة | Firecrawl API | `69442021f2e6cb73e286ff0f` | -| أداة | Docling Document Parser | `6944350ff2e6cb73e286ff20` | -| تكامل | Gmail | `6864328d1223092cb4294d30` | -| تكامل | Slack | `686432941223092cb4294d3f` | -| تكامل | Google Drive | `6864329b1223092cb4294d4e` | -| تكامل | Google Sheets | `686432931223092cb4294d3c` | -| تكامل | Google Docs | `6864329c1223092cb4294d51` | -| تكامل | Google Calendar | `686432901223092cb4294d36` | -| تكامل | aiR Knowledge Base | `6904bcf672a6e36b68bb72fb` | -| تكامل | PostgreSQL | `693ac6e8217c7b13b480970f` | -| تكامل | SQLite | `689e06ed3ce71f58d73cc999` | -| تكامل | Python Sandbox | `688779d8bfb8e46c273982ca` | - -## الملفات المرجعية - -- `references/integration-playbooks.md` — حمولات التهيئة، ورفع الملفات، وقيود التأليف، وسير عمل OAuth لجميع أنواع التكاملات -- `references/agent-patterns.md` — وكلاء الفريق، وتحديث الوكلاء المنشورين، والمراقبون، والتصدير إلى Python، وCode Execution مقابل Python Sandbox -- `references/inspector-analytics.md` — سياسات المراقب ومخطط التحليلات - -## الروابط الخارجية - -- **التوثيق:** https://docs.aixplain.com -- **الأسعار:** https://aixplain.com/pricing -- **Studio:** https://studio.aixplain.com - - diff --git a/skills/aixplain-agent-builder/SKILL.md b/skills/aixplain-agent-builder/SKILL.md index 8710eda3..30d4ff80 100644 --- a/skills/aixplain-agent-builder/SKILL.md +++ b/skills/aixplain-agent-builder/SKILL.md @@ -1,213 +1,273 @@ --- name: aixplain-agent-builder -description: A skill to design, deploy, and run production-grade AI agents on aiXplain. +description: Build, configure, deploy, run, debug, export, verify, and update aixplain agents from a user's problem or intent using the Python SDK v2. Use for single agents, teams, marketplace tools, integrations, custom Python tools, runtime Code Execution, knowledge, sessions, budgets, and inspectors. The skill works autonomously, asks only when an external integration or secret needs the user's decision, and returns a working app link. metadata: {"requires": {"env": ["AIXPLAIN_API_KEY"], "bins": ["python3", "pip"]}} --- -# aiXplain Agent Builder +# aixplain agent builder -Design, deploy, run, and manage aiXplain agents — single agents, team agents, tools, and OAuth integrations. +Turn a user's intent into a working, verified aixplain agent. Do the SDK work yourself; do not make the user design the schema, select routine defaults, or debug tracebacks. -> **Last updated:** 2026-05-31 · **Verified against aiXplain SDK:** v0.2.44 (inspector API verified live) +> **Current stable SDK:** `aixplain==0.2.47` (verified against the public release and repository on 2026-08-22). +> **SDK v2 only:** use `from aixplain import Aixplain`. Never use `AgentFactory`, `ModelFactory`, `TeamAgentFactory`, `aixplain.factories`, or other v1 APIs. If docs or an error lead to v1, translate the approach to v2 and follow `references/reliability-guidelines.md`. -## Audience & Self-Containment +## Modes -This skill is used on behalf of **both non-developers and developers**. They will not read the SDK source, API reference, or GitHub. Therefore: +Infer the mode from the request; do not make the user choose: -- **This skill is the single source of truth.** Every code block here is verified against the SDK version above — use it as written. Do not tell the user to consult the SDK docs, GitHub, or source code. -- **Do the technical work yourself.** Write and run the code; the user should not have to. Never hand a non-developer a traceback — diagnose it, fix it, and report the outcome in plain language (what happened, what you did, what's next). -- **Explain in plain terms for non-developers**, but keep full code available for developers who want it. Lead with the result and the Studio link, not the implementation. -- **If a snippet here ever fails against the installed SDK, the skill is wrong, not the user.** Fix the skill in place, then follow the "Report aiXplain-Caused Issues" habit (see Debugging) — never redirect the user to external docs to work around it. +- **Build:** turn an intent into a configured, deployed, verified agent. +- **Run/debug:** load an agent by ID or exact name, reproduce the problem, inspect steps/governance/tool behavior, fix in place, and re-verify. +- **Export:** load an existing agent and recursively generate portable SDK v2 code for its tools, skills, inspectors, and subagents; syntax- and constructor-validate it before presenting it. See `references/agent-lifecycle.md`. -## How It Works +## Operating contract -This skill accepts the following commands: +1. **Start from intent.** Infer the job, users, inputs, outputs, success criteria, and likely next request. Prefer one capable agent; use a team only when roles genuinely need different tools, context, or delegation. +2. **Do not interview the user about routine configuration.** Choose names, descriptions, instructions, output format, budgets, models, action scopes, and iteration limits using safe defaults. +3. **Search before building.** Use the bundled Marketplace Search MCP when available to discover assets, actions, and input schemas before declaring a capability missing. Prefer native first-party assets, then supported integrations, then custom code. +4. **Ask only at the connection boundary.** Pause only when the user must choose whether to connect an external integration, authorize OAuth, provide a provider credential, or approve a consequential/irreversible action. Never invent secrets. +5. **Build proactively.** Anticipate the next useful capability when it is low-risk and clearly implied. Do not silently add access to external systems, broad permissions, or expensive behavior. +6. **Show configuration when it is ready.** After resolving assets and defaults, present the compact final schema so the user can request changes; do not expose a stream of intermediate choices. +7. **Deploy and prove it.** `save()` is not completion. Run realistic checks, inspect tool usage and governance, fix failures, then provide the app link. +8. **Never overwrite by accident.** If the request clearly targets an existing agent, update it in place. For a new build whose exact name already exists, choose a descriptive unique name and report the collision; do not overwrite or create opaque `(1)` duplicates. +9. **Apply verified reliability guidance.** Follow `references/reliability-guidelines.md` for known-safe patterns; when behavior differs, state the observation and use only a confirmed workaround. -- **Build agent** — plan, search tools, create tools, assemble and save the agent -- **Deploy agent** — save and share Studio links for visual editing and analytics -- **Run agent** — execute sync or async and return the output -- **Debug agent** — diagnose tool, action, OAuth, or runtime issues -- **Export agent** — generate a standalone Python script from a deployed agent +## Standalone compatibility + +This skill must work when it is installed directly as a Claude Code skill or used by an agent host that does not support Claude Code plugins or MCP servers. The aixplain Python SDK and `AIXPLAIN_API_KEY` are the only runtime requirements for core build, run, debug, update, verify, and export workflows. + +- Treat the bundled Marketplace Search MCP as an optional discovery enhancement, never as a prerequisite. +- When that MCP is unavailable, incompatible, or unauthenticated, use the SDK searches in **Resolve capabilities** and continue with the best supported asset, integration, or custom Python Sandbox tool. +- Do not report a capability as unavailable merely because the plugin or MCP cannot load. State that Marketplace discovery was skipped only when it materially limits the answer. +- Keep connection consent, least-privilege scopes, SDK v2 validation, and realistic verification requirements unchanged in standalone mode. ## 1. Setup -Always install/upgrade to the latest SDK before doing anything: `pip install --upgrade aixplain`. +Install the exact tested stable release: + +```bash +python3 -m pip install --upgrade "aixplain==0.2.47" +``` -This skill is verified against **SDK v0.2.44**. If the installed version differs, tell the user. +Read the key from a secret source and pass it explicitly. Never print, commit, or embed it in generated code. ```python import os -from dotenv import load_dotenv from aixplain import Aixplain -load_dotenv() # loads AIXPLAIN_API_KEY from .env if present - -api_key = os.getenv("AIXPLAIN_API_KEY") -if not api_key: - raise ValueError("AIXPLAIN_API_KEY not set. Add it to your .env file or environment.") - +api_key = os.environ["AIXPLAIN_API_KEY"] aix = Aixplain(api_key=api_key) ``` -> **Passing local files to agents or aiXplain assets:** Use `FileUploader` to upload a file to aiXplain S3 and get a presigned URL. Pass that URL anywhere a URL input is accepted (tools, models, integrations). `Resource.save()` is internal and not usable standalone. -> ```python -> from aixplain.v2.upload_utils import FileUploader -> uploader = FileUploader(api_key=api_key) -> file_url = uploader.upload("/path/to/file.mp3", is_temp=True, return_download_link=True) -> ``` -> -> **Downloadable links:** If the agent needs to return files the user can click and download, always set `return_download_link=True`. Without it, `upload()` returns a raw `s3://` path that is not browser-accessible. -> -> **MIME type patch for `.html` and `.zip`:** The SDK's `MimeTypeDetector` does not include these extensions — both fall back to `text/csv`, causing browsers to save files with the wrong extension. Always patch before uploading when generating these file types: -> ```python -> from aixplain.v2.upload_utils import MimeTypeDetector -> MimeTypeDetector.EXTENSION_MAPPING['.html'] = 'text/html' -> MimeTypeDetector.EXTENSION_MAPPING['.zip'] = 'application/zip' -> ``` +Keys are workspace-specific. `Forbidden resource` across known-valid assets usually means the key belongs to another workspace. + +## 2. Derive the schema + +Silently draft this internal blueprint from the user's intent: + +```yaml +name: concise job-based name +description: who it helps and the outcome +instructions: goal, workflow, tool-use rules, constraints, completion criteria +architecture: single | team +inputs: what a run needs +output_format: markdown | text | json +tools: minimum required capabilities +integrations: external systems requiring connection +knowledge_or_memory: none | session | shared-memory | knowledge-base | skill +runtime_code: true only if the agent must write/run code during a run +governance: least privilege, budget, inspectors when justified +verification: one realistic test per capability +``` -## 2. Plan +Defaults: +- Use the platform default LLM unless the user requests a model or the default is incompatible with a tool. +- Use `markdown` for human deliverables and `json` only when another system consumes the output. +- Use a single agent unless specialist delegation improves reliability. +- Scope every tool to the smallest non-empty `allowed_actions` set. +- Give each tool a concise, action-oriented description that states when to use it and what it returns; distinct descriptions improve tool and subagent routing. +- Add runtime Code Execution only when the deployed agent needs calculations, transforms, plots, file processing, or dynamic code during a run. +- Use a Python Sandbox custom tool when the missing capability is deterministic code authored at build time. -Before building, present a plan to the user covering: agent name, description, instructions, which tools/integrations to use, and whether it's a single or team agent. Wait for approval before proceeding. +## 3. Resolve capabilities -**Always search before creating or hardcoding a tool or integration.** `Tool`, `Model`, and `Integration` all support `.search(query=...).results`. Never say "not available" without searching first. +Search first: ```python -results = aix.Tool.search(query="web search").results -# Names may be concatenated (e.g. "Googledrive"). Normalize before comparing. +tools = aix.Tool.search(query="web search").results +integrations = aix.Integration.search(query="calendar").results +models = aix.Model.search(query="reasoning").results ``` -If a needed capability has no marketplace tool or integration, announce you'll build it as a **Python Sandbox** function (see `references/integration-playbooks.md § 4`). - -## 3. Create Tools - -**MANDATORY: Scope `allowed_actions` on every tool.** Never attach a tool to an agent without first narrowing its actions to the minimum needed for the task. Tools loaded via `Tool.get()` come with all actions enabled by default — this is over-privileged and degrades agent reasoning. Inspect `tool.actions` to see the full set, then assign `tool.allowed_actions = [...]` (or pass `allowed_actions=[...]` in the constructor) before adding to the agent. If you genuinely need all actions, state that explicitly to the user and confirm. +For broad discovery, use the aixplain Marketplace Search tool: ```python -tool = aix.Tool.get("") -print(tool.actions) # discover available actions -tool.allowed_actions = ["search_models", "list_filters"] # scope to task +search = aix.Tool.get("6960f934f316da19e5f22494") +result = search.run(action="search", data={"query": "microsoft calendar"}) +for kind in ("agent", "model", "tool", "integration"): + for item in result.data.get(kind, {}).get("results", []): + print(kind, item["name"], item["path"], item["id"]) ``` -Three paths, in order of preference: +Selection order: +1. Existing first-party marketplace tool. +2. Existing native integration. +3. Supported connector integration. +4. Custom Python Sandbox tool with `config={"code": ..., "function_name": ...}`. + +If an existing integration can do the job, ask whether the user wants to connect it. If no suitable tool exists, develop the missing deterministic tool with `code=` rather than asking the user to invent the implementation. Read `references/integration-connections.md` before connecting, and `references/integration-build-and-data.md` before authoring code. + +### Build-time code vs runtime code + +- **Python Sandbox integration** (`688779d8bfb8e46c273982ca`): you write fixed code now. Use when a capability is missing and should become a deterministic tool. +- **Code Execution tool** (`698cda188bbb345db14ac13b`): the agent writes/runs code later. Add only when runtime work actually requires it. + +## 4. Build ```python -# Path A: Marketplace tool by ID — REMEMBER to scope allowed_actions after .get() -tool = aix.Tool.get("698cda188bbb345db14ac13b") # Code Execution -tool.allowed_actions = [...] # required - -# Path B: Non-OAuth integration (KB, SQLite) -# READ references/integration-playbooks.md for config payloads. -# Ask the user for any missing inputs — never invent placeholders. -# Always create fresh; do not reuse by name unless explicitly asked. -tool = aix.Tool(name="KB Search", description="Search product docs", - integration="6904bcf672a6e36b68bb72fb", allowed_actions=["search", "get"]).save() - -# Path C: OAuth integration (Gmail, Slack, Jira, Google Drive) -# READ references/integration-playbooks.md § 5 for full workflow. -# (1) integration = aix.Integration.get("") -# (2) integration.list_actions() → discover action names -# (3) Create tool with allowed_actions in constructor -# (4) User completes OAuth via redirect URL emitted at .save() -# (5) Attach in-memory tool object to agent and save - -# Path D: Python Sandbox (last resort) -# READ references/integration-playbooks.md § 4 for config shape. +agent = aix.Agent( + name="Customer Briefing Agent", + description="Produces an evidence-backed customer briefing.", + instructions=( + "Gather the required evidence with the attached tools, reconcile conflicts, " + "and return a concise briefing with sources. Do not claim completion until " + "all required sections are present." + ), + tools=[tool], + output_format="markdown", + max_tokens=6000, +) +agent.save() ``` -## 4. Build Agent - -Agents use the platform default LLM — do not specify `llm` unless the user requests a specific model. +For structured output, set it on the agent: ```python agent = aix.Agent( - name="My Agent", description="...", instructions="...", - tools=[tool], output_format="markdown", - max_tokens=6000, # set at creation, not per-run -).save() + name="Lead Qualifier", + description="Qualifies a lead against the supplied criteria.", + instructions="Evaluate every criterion and explain the result.", + output_format="json", + expected_output={ + "type": "object", + "properties": { + "qualified": {"type": "boolean"}, + "reasons": {"type": "array", "items": {"type": "string"}}, + }, + "required": ["qualified", "reasons"], + }, +) +agent.save() ``` -If `save()` raises `name_already_exists`, ask the user: update existing or create with a new name. +For teams, budgets, sessions, memory, and knowledge, read `references/agent-architecture.md`. For run controls and execution strategy, read `references/agent-runtime.md`. For updates or exports, read `references/agent-lifecycle.md`; read `references/inspectors.md` for runtime validation. -## 5. Deploy & Run +## 5. Integration checkpoint -After deploy, share these links with the user: -- **Visual builder:** `https://studio.aixplain.com/build//schema` -- **Analytics:** `https://studio.aixplain.com/dashboard/analytics/?agent=` +Do not ask for general approval of the inferred schema. Ask a focused question only when connection is required: -Default: leave `runResponseGeneration` unset. Only pass `runResponseGeneration=True` when you specifically need structured/JSON output. +- Name the integration and why it is needed. +- List the minimum actions requested. +- State whether OAuth or a provider key is required. +- Offer the no-connection alternative, if one exists. + +After consent, create the tool from the integration in the current workspace and return the generated connect URL. Never hardcode a workspace-bound integration tool instance. + +## 6. Verify before reporting success + +Run one realistic query per capability. For every run: ```python -# Sync — default text output -result = agent.run(query="...") -print(result.data.output) +result = agent.run(query="Create this week's customer briefing from the available sources.") -# Sync — structured/JSON output -result = agent.run(query="...", runResponseGeneration=True) +used_units = [ + step.get("unit", {}).get("name") + for step in (result.data.steps or []) + if isinstance(step, dict) +] -# Async — SDK handles polling; no manual HTTP needed -ar = agent.run_async(query="...") -result = agent.sync_poll(ar.url) # blocks until SUCCESS/FAILED, returns the same result shape as run() +governance = result.data.governance or {} +assert governance.get("status", "ALLOWED") == "ALLOWED", governance print(result.data.output) +print(used_units) ``` -## 6. Debugging +Completion gate: +- Every intended tool fired at least once when relevant. +- Output satisfies the user's requested result and format. +- Governance did not block the run; run `SUCCESS` alone is insufficient. +- Generated files have clickable HTTPS download links. +- Team output is synthesized, not an internal `delegate_task` trace. +- Integration permissions are the minimum required. +- Actual spend comes from `result.data.execution_stats["credits"]`. -### Inspect Intermediate Steps +If a tool works standalone but fails inside the agent, isolate the orchestration issue before changing the schema. Apply the closest safe practice in `references/reliability-guidelines.md` and report the observed behavior clearly. -```python -result = agent.run(query="...") +## 7. Final response + +Lead with the result, not the code. Include: + +1. What the agent does. +2. App link: `https://app.aixplain.com/agents/`. +3. Compact final configuration: architecture, tools/integrations, action scopes, output, memory/knowledge, model override if any, budget/guardrails. +4. Verification evidence: test prompts, tools observed, pass/fail, governance status. +5. Connection link or remaining user action, only if required. +6. One likely next improvement, phrased as optional rather than another setup questionnaire. + +Keep build code available on request. When the user asks for integration code, provide a complete v2 snippet based on the relevant `integration-connections.md` or `integration-build-and-data.md` reference. -# Step-by-step trace: thought, action, tool used, input/output, tokens -for step in result.data.steps: - print(step['thought'], step['action'], step['unit']['name']) +## Updating an existing agent -# Aggregate costs and timing -stats = result.data.execution_stats -print(stats['credits'], stats['runtime'], stats['api_calls']) +SDK `0.2.47` has a critical hydration bug: `Agent.get()` can populate each tool's `allowed_actions` from every returned parameter definition instead of the backend's persisted `actions` scope. Before any get→mutate→save cycle, fetch the raw agent and reapply its stored scopes: + +```python +agent_id = "" +raw = aix.Agent.context.client.get(f"v2/agents/{agent_id}") +agent = aix.Agent.get(agent_id) + +scopes_by_id = { + item.get("id"): list(item.get("actions") or []) + for item in (raw.get("tools") or []) + if isinstance(item, dict) and item.get("id") +} +for tool in agent.tools or []: + if getattr(tool, "id", None) in scopes_by_id: + tool.allowed_actions = scopes_by_id[tool.id] + +agent.instructions = "Updated instructions..." +agent.save() ``` -Or view traces visually in Studio: `https://studio.aixplain.com/build//schema` +This keeps the ID and external references stable without widening permissions. Verify behavior and raw persisted scopes again after every update. See `references/agent-lifecycle.md` and `references/reliability-guidelines.md`. -### Report aiXplain-Caused Issues +## Reliability follow-up -When you hit a quirk or error that is **aiXplain-caused** — not a mistake in the user's code, query, or config — make it a habit to propose filing a GitHub issue at https://github.com/aixplain/aiXplain. Examples: an action advertised by an integration that always fails, an SDK method that misbehaves, a documented parameter the backend ignores, a connector requesting insufficient OAuth scopes. +Use `references/reliability-guidelines.md` to choose a safe, verified path. If behavior differs, preserve a minimal redacted reproduction in local engineering notes and explain the confirmed workaround; do not invent a fix. Draft a public issue only after the user asks, targeting https://github.com/aixplain/aiXplain with SDK v2 examples only. -Workflow: -1. Confirm the cause is on aiXplain's side (reproduce it; rule out user input, missing scopes the user controls, rate limits, etc.). -2. Propose the issue to the user with a draft title and body. Do not post without confirmation — the repo is public. -3. **Redact personal data** from the public issue: no API keys, real email addresses, tool/agent IDs, message counts, or any account-specific data. Keep repro steps generic. -4. On approval, post with `gh issue create --repo aixplain/aiXplain` and share the issue link. +## Reference routing -Issue body should include: SDK version, integration/asset type, a minimal repro, the exact error, and expected vs. actual behavior. +- Marketplace discovery and exact MCP schemas: `../marketplace-search/references/effective-mcp-use.md` +- Connection consent, Slack, Web Search, OAuth, provider keys, MCP: `references/integration-connections.md` +- Custom Python tools, Code Execution, databases, knowledge, files: `references/integration-build-and-data.md` +- Agent shape, teams, budgets, sessions, memory, knowledge: `references/agent-architecture.md` +- Run controls, variables, execution strategy, reasoning models: `references/agent-runtime.md` +- Safe updates, debugging, portability, exports: `references/agent-lifecycle.md` +- Runtime inspectors and validation: `references/inspectors.md` +- Verified reliability practices and safe fallbacks: `references/reliability-guidelines.md` -## Quick Asset IDs +## Stable asset IDs | Type | Name | ID | -|------|------|----| -| Tool | Tavily Web Search | `6931bdf462eb386b7158def3` | +|---|---|---| | Tool | Code Execution | `698cda188bbb345db14ac13b` | -| Tool | Google Search API | `692f18557b2cc45d29150cb0` | -| Tool | Firecrawl API | `69442021f2e6cb73e286ff0f` | -| Tool | Docling Document Parser | `6944350ff2e6cb73e286ff20` | +| Tool | Marketplace Search | `6960f934f316da19e5f22494` | +| Tool | File Manager | `6a0216cffb2a801f1c41e32e` | +| Integration | Python Sandbox | `688779d8bfb8e46c273982ca` | | Integration | Gmail | `6864328d1223092cb4294d30` | | Integration | Slack | `686432941223092cb4294d3f` | | Integration | Google Drive | `6864329b1223092cb4294d4e` | -| Integration | Google Sheets | `686432931223092cb4294d3c` | -| Integration | Google Docs | `6864329c1223092cb4294d51` | | Integration | Google Calendar | `686432901223092cb4294d36` | | Integration | aiR Knowledge Base | `6904bcf672a6e36b68bb72fb` | | Integration | PostgreSQL | `693ac6e8217c7b13b480970f` | | Integration | SQLite | `689e06ed3ce71f58d73cc999` | -| Integration | Python Sandbox | `688779d8bfb8e46c273982ca` | - -## Reference Files - -- `references/integration-playbooks.md` — config payloads, file upload, authoring constraints, and OAuth workflow for all integration types -- `references/agent-patterns.md` — team agents, updating deployed agents, inspectors, export to Python, Code Execution vs Python Sandbox -- `references/inspectors.md` — inspector policies, action values, and post-change validation - -## External Links -- **Docs:** https://docs.aixplain.com -- **Pricing:** https://aixplain.com/pricing -- **Studio:** https://studio.aixplain.com +Docs: https://docs.aixplain.com · App: https://app.aixplain.com · SDK: https://github.com/aixplain/aiXplain diff --git a/skills/aixplain-agent-builder/references/agent-architecture.md b/skills/aixplain-agent-builder/references/agent-architecture.md new file mode 100644 index 00000000..76e057c2 --- /dev/null +++ b/skills/aixplain-agent-builder/references/agent-architecture.md @@ -0,0 +1,144 @@ +# Agent architecture (SDK v2) + +Read this for agent shape, teams, budgets, sessions, memory, knowledge, or choosing build-time versus runtime code. + +## Single agent or team + +Use one agent by default. Use a team when at least one is true: +- roles need different tools or permissions; +- specialist context would otherwise overload one prompt; +- work can be delegated with clear outputs; +- an independent reviewer materially improves reliability. + +Do not create a team merely because the workflow has multiple steps. + +## Team agent + +```python +researcher = aix.Agent( + name="Evidence Researcher", + description="Finds and summarizes evidence from approved sources.", + instructions="Use the search tool; return source-backed facts only.", + tools=[search_tool], +).save() +researcher.budget.max_iterations = 30 +researcher.save() + +writer = aix.Agent( + name="Briefing Writer", + description="Turns validated evidence into a concise briefing.", + instructions="Write only from supplied evidence; flag gaps.", +).save() + +team = aix.Agent( + name="Customer Briefing Team", + description="Researches and writes a verified customer briefing.", + instructions="Delegate evidence gathering, then synthesize the final answer.", + agents=[researcher, writer], +).save() +team.budget.max_iterations = 15 +team.save() +``` + +Subagent names and descriptions drive routing. Make them outcome-specific. Use `agents=`, not deprecated `subagents=`. + +Typical iteration defaults: +- team lead: `12–15`; +- tool-heavy worker: `30–40`; +- toolless writer/reviewer: backend default is usually enough. + +Exhaustion can return an internal `[Calling tool delegate_task ...]` trace while run status is `SUCCESS`; validate output shape and governance. + +## Budgets + +Every agent has `agent.budget`. Mutate it before `run()` for a one-off cap; save it to persist a default. + +```python +agent.budget.max_cost = 0.25 +agent.budget.max_duration_seconds = 120 +agent.budget.max_iterations = 12 +agent.save() +``` + +Do not pass `budget=` to `agent.run()` on `0.2.47`; it is silently ignored. `max_iterations=` as a standalone field/argument is deprecated. + +A budget is a circuit breaker, not a billing ceiling: in-flight and subagent work can overshoot. Treat `result.data.execution_stats["credits"]` as actual spend. A budget block can still return run status `SUCCESS`; inspect `result.data.governance`. + +## Sessions (one conversation) + +```python +session = aix.Session(agent=agent, name="customer-thread").save() +first = agent.run("My company is Acme.", session=session) +second = agent.run("Which company did I mention?", session=session) +``` + +Sessions are synchronous. `run_async(..., session=...)` is not supported. + +## Choose the right context mechanism + +| Need | Use | Do not use it for | +| --- | --- | --- | +| A self-contained request | no retained context | facts needed only for one run | +| Continuity in one active conversation | `Session` | durable preferences or cross-conversation recall | +| Confirmed user or account context across conversations | Shared Memory | a large document corpus, secrets, or unverified model inferences | +| Retrieval from many documents by relevance | aiR Knowledge Base | durable per-user preferences or conversation state | +| A stable policy, procedure, or playbook always needed by the agent | `aix.Skill` | mutable customer facts or personal history | + +## Shared memory (across conversations) + +Use Shared Memory only when the agent must recall **confirmed, useful context** for the same user, customer, or account in a later conversation—for example, a reporting preference, approved account facts, or an agreed working convention. + +Before enabling it: + +1. Confirm that cross-conversation retention is required by the request; do not persist context merely because it might be useful later. +2. Explain the retained scope when it includes personal or customer information, and ask before enabling retention when that choice is not already clear from the request. +3. Store the minimum durable fact. Never store API keys, credentials, access tokens, payment data, or unverified model conclusions. +4. Use a stable, explicit `identifier` for the user/customer/account. Never use a global/default identity in a multi-user agent. +5. In teams, verify that every memory read and write carries the same intended identity, then test that one identity cannot retrieve another identity's context. + +```python +memory = aix.Tool( + integration="aixplain/shared-memory/aixplain", + name="Account Memory", + description="Stores confirmed, durable account context for the current account only.", + config={ + "max_memory_size": 256, + "size_management_policy": "summarize", + }, + allowed_actions=["insert", "get"], +).save() + +account_id = "customer-123" +memory.run( + action="insert", + data={"identifier": account_id, "content": "Prefers weekly summaries."}, +) +``` + +Use a `Session` instead when the context belongs only to the current conversation. Use a Knowledge Base when the agent must search a larger source corpus rather than recall durable account-specific facts. + +## Knowledge base vs skill + +- Use an aiR knowledge-base tool for a large corpus queried by relevance. +- Use `aix.Skill` for a bounded playbook/reference that should always be in context. + +```python +skill = aix.Skill( + name="Support Playbook", + description="Approved support workflow and policies.", + file_path="/absolute/path/to/playbook.md", +).save() + +agent = aix.Agent( + name="Support Agent", + description="Answers support questions using the approved playbook.", + instructions="Follow the attached playbook.", + skills=[skill], +).save() +``` + +`Session`, `Budget`, and `Skill` require SDK `0.2.47` or later. + +## Runtime Code Execution vs build-time Python Sandbox + +Use Code Execution (`698cda188bbb345db14ac13b`) when the agent must write and execute arbitrary code at runtime. Use Python Sandbox (`688779d8bfb8e46c273982ca`) when you can define the deterministic function during the build. See `integration-build-and-data.md`. diff --git a/skills/aixplain-agent-builder/references/agent-lifecycle.md b/skills/aixplain-agent-builder/references/agent-lifecycle.md new file mode 100644 index 00000000..e1162b31 --- /dev/null +++ b/skills/aixplain-agent-builder/references/agent-lifecycle.md @@ -0,0 +1,117 @@ +# Agent lifecycle and portability (SDK v2) + +Read this to update, debug, export, or present an existing aixplain agent without changing its identity or widening access. + +## Update without changing the ID + +SDK `0.2.47` `Agent.get()` can widen hydrated tool scopes. Load the raw backend definition first and reapply its persisted `actions` values before mutating or exporting: + +```python +def load_agent_preserving_scopes(aix, agent_id: str): + raw = aix.Agent.context.client.get(f"v2/agents/{agent_id}") + agent = aix.Agent.get(agent_id) + scopes = { + item.get("id"): list(item.get("actions") or []) + for item in (raw.get("tools") or []) + if isinstance(item, dict) and item.get("id") + } + for tool in agent.tools or []: + tool_id = getattr(tool, "id", None) + if tool_id in scopes: + tool.allowed_actions = scopes[tool_id] + return agent, raw + +agent, raw = load_agent_preserving_scopes(aix, "") +agent.instructions = "Updated instructions..." +agent.description = "Updated description..." +agent.tools = updated_tools or agent.tools +agent.output_format = "markdown" +agent.save() +``` + +After saving, fetch `v2/agents/` again and assert each raw tool's `actions` equals the intended least-privilege scope. Do not trust hydrated `allowed_actions` until the bug is fixed. + +Do not recreate by default. Verify the same capabilities after every update. + +`0.2.47` fixed the earlier `Agent.get()`/`save()` default-LLM overwrite. If behavior suggests a regression, compare the backend model before/after and follow `reliability-guidelines.md` rather than adding a permanent workaround without evidence. + +## Portability + +A portable build must depend only on: +- the API key/environment; +- stable first-party marketplace asset IDs/paths; +- integration definitions from which workspace-local tools are created; +- inline custom code for agent-specific Python Sandbox tools. + +Never hardcode a workspace-bound OAuth, connector, MCP, or custom tool instance ID in reusable build code. + +## Final configuration summary + +After deployment, show only the resolved configuration: + +```yaml +architecture: single | team +model: platform default | explicit model +output: markdown | text | json +tools: + - name: ... + allowed_actions: [...] +integrations: + - name: ... + connection: connected | user action required +knowledge: none | session | shared memory | index | skill +runtime_code: enabled | disabled +budget: + max_cost: ... + max_duration_seconds: ... + max_iterations: ... +inspectors: [...] +``` + +Then provide `https://app.aixplain.com/agents/` and verification evidence. + +## Run or debug an existing agent + +Load by ID (preferred) or exact search match. Reproduce with the user's query before changing configuration. + +```python +agent = aix.Agent.get("") +result = agent.run(query="Reproduce the reported behavior.") + +for step in result.data.steps or []: + print(step.get("action"), step.get("unit", {}).get("name")) +print(result.data.governance) +print(result.data.output) +``` + +For long runs, use SDK polling rather than custom HTTP loops: + +```python +started = agent.run_async(query="Run the full analysis.") +result = agent.sync_poll(started.url) +``` + +Fix the smallest responsible layer—tool, action scope, description, instructions, budget, or model—save in place, and rerun the same reproduction plus regression checks. + +## Export an existing agent to portable SDK v2 code + +Use the SDK, not raw REST: + +```python +agent = aix.Agent.get("") +config = agent.to_dict() +members = agent.agents or [] +``` + +Recursively map the root and each member to `aix.Agent(...)` constructor arguments. Preserve name, description, instructions, output format, expected output, model configuration, budget, skills, inspectors, and action scopes. + +Portability rules for generated code: +- Read `AIXPLAIN_API_KEY` from the environment; never embed it. +- Fetch stable first-party marketplace assets by path/ID. +- Recreate OAuth, connector, MCP, database, and custom tools from their integration definitions; do not export workspace-bound tool-instance IDs as portable dependencies. +- Inline agent-specific Python Sandbox source using `config={"code": ..., "function_name": ...}`. +- Emit subagents before the root and attach them with `agents=[...]`. +- End with `.save()` and print `https://app.aixplain.com/agents/`. +- Generate `llm=...`, never unsupported `llm_id=...`; keep iteration limits in `agent.budget`, never top-level `max_iterations`. +- Before presenting an export, run `python -m py_compile `, inspect constructor arguments against SDK `0.2.47`, and ensure save/run side effects are guarded by `if __name__ == "__main__":`. +- Run the generated script in a clean workspace when practical and compare the resolved schema before calling the export portable. diff --git a/skills/aixplain-agent-builder/references/agent-patterns.ar.md b/skills/aixplain-agent-builder/references/agent-patterns.ar.md deleted file mode 100644 index 16ad9fdc..00000000 --- a/skills/aixplain-agent-builder/references/agent-patterns.ar.md +++ /dev/null @@ -1,84 +0,0 @@ -# أنماط الوكلاء - -أنماط متقدمة للوكلاء — وكلاء الفريق، تحديث الوكلاء المنشورين، المراقبون، التصدير، وتفاصيل الأدوات. -مرتبط من المهارة الرئيسية — يُرجع إليه عند الحاجة. - ---- - -## وكيل الفريق - -```python -sub1 = aix.Agent(name="Researcher", instructions="Search and summarize.", tools=[search_tool]).save() -sub2 = aix.Agent(name="Writer", instructions="Write the final report.").save() -team = aix.Agent(name="Team Lead", instructions="Route to specialists.", subagents=[sub1, sub2]).save() -``` - ---- - -## تحديث وكيل منشور - -الوكلاء المنشورون قابلون للتعديل. لا تُعِد إنشاء الوكيل لتغيير سلوكه — قم بتحميله، وعدّله، ثم استدعِ `save()`. يبقى معرّف الوكيل والسجل والمراجع الخارجية كما هي. - -الحقول القابلة للتحديث: -- `agent.instructions` — موجِّه النظام -- `agent.description` — ملخص عام -- `agent.tools` — إضافة أو إزالة أو استبدال الأدوات -- `agent.output_format` — `"markdown"` / `"text"` / `"json"` -- `agent.llm` — تبديل النموذج اللغوي الكبير الأساسي (اسم الخاصية هو `llm`، وليس `llm_id`) - -يمكنك أيضًا تحديث الأدوات المرفقة دون فصلها — غيّر `description` الخاصة بالأداة، أو أضف ملفات إلى أداة قاعدة المعرفة، أو عدّل `allowed_actions`. عدّل كائن الأداة، واستدعِ `tool.save()`، وسيلتقط الوكيل التغيير في التشغيل التالي. - -```python -agent = aix.Agent.get("") -agent.instructions = "New system prompt..." -agent.output_format = "json" -agent.save() - -# Update an attached tool in place (no detach/reattach): -kb_tool = next(t for t in agent.tools if t.name == "Product KB") -kb_tool.description = "Updated KB scope: includes 2026 docs." -kb_tool.save() -``` - ---- - -## إضافة مراقب - -```python -from aixplain.modules.team_agent import InspectorTarget -from aixplain.modules.team_agent.inspector import Inspector, InspectorPolicy, InspectorAuto - -inspector = Inspector( - name="Content Gate", - auto=InspectorAuto.ALIGNMENT, - model_params={"prompt": "Validate output meets policy. Fail if non-compliant."}, - policy=InspectorPolicy.ABORT, -) -team.inspectors = [inspector] -team.inspector_targets = [InspectorTarget.OUTPUT] -team.save() -``` - -بعد إضافة المراقبين، تحقق من الصحة باستخدام 3 موجِّهات: مسموح، ومرفوض، وغامض. راجع `references/inspector-analytics.md` لمصفوفة التحقق الكاملة. - ---- - -## تصدير الوكيل إلى Python - -1. `GET https://platform-api.aixplain.com/sdk/agents/{ID}` مع `x-api-key` -2. تتبع `agents[].assetId` بشكل تكراري للوكلاء الفرعيين -3. ربط حقول API بوسيطات مُنشئ SDK -4. إنشاء ملف `.py` مستقل مع تحميل المفتاح عبر متغيرات البيئة - ---- - -## تنفيذ الشيفرة مقابل صندوق حماية Python - -**تنفيذ الشيفرة** (أداة من السوق `698cda188bbb345db14ac13b`) — يكتب الوكيل شيفرة Python عشوائية وينفّذها أثناء التشغيل. صندوق حماية سحابي آمن مع اتصال بالإنترنت. يُستخدم للحسابات، وتحويلات البيانات، والتصورات البيانية، ومعالجة الملفات، وجلب البيانات من عناوين URL أو واجهات API. - -- استخدم `print()` لإرجاع النتائج النهائية. -- إذا أنشأت الشيفرة ملفات (رسوم بيانية، ملفات CSV)، اطبع قائمة بيانات وصفية بتنسيق JSON إلى stdout: `[{"name":"","file":""}]`. بدون ذلك، تُفقد الملفات المُنشأة بصمت. - -**صندوق حماية Python** (تكامل `688779d8bfb8e46c273982ca`) — نفس صندوق الحماية لكن مع سكربت مُعرَّف مسبقًا يُكتب وقت البناء، وليس وقت التشغيل. يُستخدم للأدوات الحتمية ذات المدخلات والمخرجات الثابتة عندما لا تغطي أي أداة في السوق القدرة المطلوبة. راجع `references/integration-playbooks.md § 4` لشكل التهيئة وقيود التأليف. - - diff --git a/skills/aixplain-agent-builder/references/agent-patterns.md b/skills/aixplain-agent-builder/references/agent-patterns.md deleted file mode 100644 index 601aba9a..00000000 --- a/skills/aixplain-agent-builder/references/agent-patterns.md +++ /dev/null @@ -1,104 +0,0 @@ -# Agent Patterns - -Advanced agent patterns — team agents, updating deployed agents, inspectors, export, and tool details. -Linked from the main skill — consult when needed. - ---- - -## Team Agent - -```python -sub1 = aix.Agent(name="Researcher", instructions="Search and summarize.", tools=[search_tool]).save() -sub2 = aix.Agent(name="Writer", instructions="Write the final report.").save() -team = aix.Agent(name="Team Lead", instructions="Route to specialists.", subagents=[sub1, sub2]).save() -``` - ---- - -## Update a Deployed Agent - -Deployed agents are mutable. Never recreate to change behavior — load, mutate, and `save()`. The agent ID, history, and external references stay intact. - -Updatable fields: -- `agent.instructions` — system prompt -- `agent.description` — public-facing summary -- `agent.tools` — add, remove, or replace tools -- `agent.output_format` — `"markdown"` / `"text"` / `"json"` -- `agent.llm` — swap the underlying LLM. **Always assign to `.llm` (accepts the model ID string or a Model object). Never use `.llm_id` — that attribute exists but assigning it does NOT propagate to the save payload, so `save()` silently keeps the old model.** - -You can also update attached tools without detaching them — change a tool's `description`, add files to a KB tool, or change `allowed_actions`. Mutate the tool object, call `tool.save()`, and the agent picks up the change on next run. - -```python -agent = aix.Agent.get("") -agent.instructions = "New system prompt..." -agent.output_format = "json" -agent.llm = "" # correct — use `.llm`, NOT `.llm_id` -agent.save() - -# Update an attached tool in place (no detach/reattach): -kb_tool = next(t for t in agent.tools if t.name == "Product KB") -kb_tool.description = "Updated KB scope: includes 2026 docs." -kb_tool.save() -``` - ---- - -## Add Inspector - -Inspectors attach to **team agents**. Use the **v2** inspector API (`aixplain.v2.inspector`) — it matches the v2 `Aixplain` client used everywhere in this skill. Do **not** import from `aixplain.modules.team_agent.inspector`; that path resolves to the v1 module and its `Inspector(auto=..., policy=...)` shape will fail with `ImportError`/`TypeError` under SDK v0.2.44. - -Config is plain data — no enums or config classes to import. Pass strings for `action` / `targets` / `severity`, and the universal judge (`aix.Metric`, an asset-id string, or a Python callable) for `metric`. - -```python -from aixplain.v2 import Inspector - -inspector = Inspector( - name="Content Gate", - action="abort", # "continue" | "rerun" | "abort" | "edit" - targets=["output"], # "input" | "steps" | "output" (or a sub-agent name) - severity="high", # "low" | "medium" | "high" | "critical" - metric=aix.Metric.create( # the universal judge - name="policy-check", - llm_path="", # your judge LLM; wraps the prompt below - prompt_template="Validate output meets policy. Fail if non-compliant.", - ), -) -team.inspectors = [inspector] -team.inspector_targets = ["output"] -team.save() -``` - -Notes: -- `action="rerun"` is the only type that accepts retry params. Pass them via a dict: `action={"type": "rerun", "max_retries": 2, "on_exhaust": "abort"}` (`on_exhaust` is `"continue"` | `"abort"`). Setting retry params on any other action raises. -- `action="edit"` requires an `editor=...` (same accepted types as `metric`). -- `metric` accepts an `aix.Metric`, a bare asset-id string, or a Python callable (a custom function judge). For an inline asset + prompt without creating a Metric, pass a dict: `metric={"asset_id": "", "prompt": "..."}`. - -After adding inspectors, validate with 3 prompts: allowed, denied, ambiguous. See `references/inspectors.md` for the full validation matrix. - ---- - -## Export Agent to Python - -Use the SDK — no raw REST. `Agent.get()` already returns the full config; serialize it and read `subagents` directly. - -```python -agent = aix.Agent.get("") -config = agent.to_dict() # full agent config as a dict -subs = agent.subagents # list of subagent objects (recurse the same way) -``` - -1. `aix.Agent.get(ID)` — load the agent (and read `.subagents` for team agents). -2. Read fields from the object or `agent.to_dict()` (name, description, instructions, output_format, llm, tools, max_tokens, inspectors). -3. Map those fields to SDK constructor args (`aix.Agent(...)`, `aix.Tool.get(...)`, inspector objects per § Add Inspector). -4. Generate a standalone `.py` that loads the key from env (`AIXPLAIN_API_KEY`) and rebuilds the agent with those args. - ---- - -## Code Execution vs Python Sandbox - -**Code Execution** (marketplace tool `698cda188bbb345db14ac13b`) — the agent writes and runs arbitrary Python at runtime. Secure cloud sandbox with internet access. Use for calculations, data transformations, visualizations, file processing, and fetching data from URLs/APIs. - -- Use `print()` to return final results. -- If code generates files (plots, CSVs), print a JSON metadata list to stdout: `[{"name":"","file":""}]`. Without this, generated files are silently lost. - -**Python Sandbox** (integration `688779d8bfb8e46c273982ca`) — same sandbox but with a pre-defined script authored at build time, not runtime. Use for deterministic tools with fixed inputs/outputs when no marketplace tool covers the capability. See `references/integration-playbooks.md § 4` for config shape and authoring constraints. diff --git a/skills/aixplain-agent-builder/references/agent-runtime.md b/skills/aixplain-agent-builder/references/agent-runtime.md new file mode 100644 index 00000000..dcc04289 --- /dev/null +++ b/skills/aixplain-agent-builder/references/agent-runtime.md @@ -0,0 +1,125 @@ +# Agent runtime (SDK v2) + +Read this for per-run controls, stateful or stateless execution, execution strategy, and model reasoning configuration. + +## Invocation and runtime controls + +Configure tools, model, `output_format`, `expected_output`, and the default `agent.budget` on the agent. `run()` controls one execution: + +```python +# Stateless reusable run: variables are currently unreliable; validate the result. +result = agent.run( + query="Create the customer briefing for Acme from the supplied context.", + variables={"account_name": "Acme"}, + identifier="customer-123", + attachments=["/absolute/path/to/brief.pdf"], + criteria="Include sources and flag unsupported claims.", +) + +# Stateful conversation: resolve non-secret values into the query; do not pass variables. +follow_up = agent.run( + query="For Acme, turn the prior briefing into an executive summary.", + session=session, +) +``` + +| Need | `run()` controls | Notes | +|---|---|---| +| Conversation | `query`, `session` | `session` accepts a `Session` or ID; do not combine it with `variables`. | +| Per-run input | `variables`, `identifier`, `attachments` | Variables are currently unreliable. For stateful runs, resolve non-secret values into `query`; use `attachments`, not deprecated `files`. | +| Quality and oversight | `criteria`, `inspectors` | Add inspectors only for consequential output checks. | +| Progress display | `progress_format`, `progress_verbosity`, `progress_truncate` | Use `status` or `logs`; leave unset in normal production runs. | +| Advanced execution | `tasks`, `prompt`, `history`, `execution_params`, `evolve` | Use only when the execution strategy requires them. `execution_params.max_iterations` is deprecated. | +| Transport | `timeout`, `wait_time`, `run_retries`, `run_retry_wait` | Client wait/retry controls, not agent behavior. | + +There is no `output_format=`, `expected_output=`, or `budget=` run override in SDK `0.2.47`. Set output contracts on `Agent`; set limits through `agent.budget` before running. `run_response_generation` is deprecated/ignored; do not use it in new builds. + +## Reusable run variables + +SDK `0.2.47` exposes `variables=`, but current platform substitution is unreliable and the SDK rejects `session=...` combined with `variables=...`. Until that is repaired, do not promise placeholder substitution. + +For a stateless run, use `variables=` only when a focused verification proves substitution occurred. For a stateful run—or whenever correctness matters—resolve non-secret values into the per-run query instead: + +```python +account_name = "Acme" # Never resolve secrets into prompts. +query = f"Create a customer briefing for {account_name} from the supplied context." +result = agent.run(query=query, session=session) +``` + +Pass credentials only through approved integration/auth flows. + +## Choose the execution strategy + +The schema selects the strategy; there is no separate `strategy=` flag: + +| Configuration | Use when | Behavior | +|---|---|---| +| neither `planner` nor `tasks` | the next step depends on observations | adaptive plan/act/observe loop | +| `tasks=[...]` only | the workflow and dependencies are known | deterministic task graph | +| `planner=` | the goal needs decomposition but execution should then be structured | planner creates the graph, then tasks execute | + +Static task example: + +```python +from aixplain.v2.agent import Task + +collect = Task( + name="collect", + instructions="Collect the required account evidence.", + expected_output="Evidence with sources", +) +summarize = Task( + name="summarize", + instructions="Create the final briefing from collected evidence.", + expected_output="A concise markdown briefing", + dependencies=[collect], +) +agent = aix.Agent( + name="Static Briefing Agent", + description="Runs a predictable evidence-to-briefing workflow.", + instructions="Complete each task and preserve source attribution.", + tasks=[collect, summarize], +).save() +``` + +Use a planner when task shape varies by request: + +```python +agent = aix.Agent( + name="Planning Analyst", + description="Plans and executes multi-step analyses.", + instructions="Plan the analysis, execute it, and verify the conclusion.", + planner="", +).save() +``` + +If both `planner` and `tasks` are present, treat the supplied tasks as seeds for the plan. Do not offer a “dynamic” strategy; it is not a stable v2 SDK mode. + +## Reasoning effort and per-role models + +Reasoning effort is model-specific. Check availability before setting it: + +```python +llm = aix.Model.get("") +if "reasoning_effort" in llm.inputs.keys(): + llm.inputs.reasoning_effort = "high" # model-supported values only + +agent = aix.Agent(name="Reasoning Agent", description="...", instructions="...", llm=llm) +``` + +For planned workflows, configure expensive reasoning only where it adds value: + +```python +planner = aix.Model.get("") +if "reasoning_effort" in planner.inputs.keys(): + planner.inputs.reasoning_effort = "high" + +agent = aix.Agent( + name="Planning Analyst", + description="Plans and executes variable multi-step analyses.", + instructions="Plan, execute, verify, then answer concisely.", + planner=planner, +).save() +``` + +SDK `0.2.47` accepts and persists `llm`, `planner`, `supervisor`, and `response_generator`, but that does not mean the runtime applies every role. Freshly verified against `aixplain-agents` `1.3.0` (`028a81ac`): only `llm` and `planner` model-parameter buckets are applied. `supervisor`, `responder`, and `inspector` buckets emit “not applied yet” warnings. The dedicated second-LLM response-generator pass and `run_response_generation` are deprecated and ignored. Do not configure `response_generator` in new builds; treat the field as compatibility-only until runtime support is restored and verified. diff --git a/skills/aixplain-agent-builder/references/inspector-analytics.ar.md b/skills/aixplain-agent-builder/references/inspector-analytics.ar.md deleted file mode 100644 index 1915f55b..00000000 --- a/skills/aixplain-agent-builder/references/inspector-analytics.ar.md +++ /dev/null @@ -1,134 +0,0 @@ -# عقد تحليلات المراقب - -أنماط سياسة المراقب، ومخطط التحليلات، ومتطلبات التحقق. -مرتبط من المهارة الرئيسية — يُرجع إليه عند إضافة مراقبين. - ---- - -## إرشادات السياسة - -- يُفضَّل استخدام `InspectorPolicy.ABORT` لانتهاكات السياسة الصارمة. -- يُفضَّل استخدام `InspectorPolicy.ADAPTIVE` لمشكلات الجودة القابلة للاسترداد. -- اضبط مراقبي `RERUN` مع تحديد `max_retries` و`on_exhaust` بشكل صريح. -- لعمليات النشر القائمة: استرجع `TeamAgent`، وعدّل `inspectors`/`inspector_targets`، ثم استدعِ `.save()`. - ---- - -## دلالات الحالة - -أبقِ حالة التشغيل في aiXplain كما هي: `IN_PROGRESS | SUCCESS | FAILED`. -لا تُحمِّل `FAILED` معنى حظر السياسة. - -### تعداد حالة الحوكمة (على مستوى التطبيق) - -| الحالة | المعنى | -|--------|--------| -| `ALLOWED` | نجح المراقب، تنفيذ طبيعي | -| `BLOCKED_BY_INSPECTOR` | أوقف المراقب التشغيل | -| `REQUIRES_HUMAN_REVIEW` | وضع المراقب علامة للمراجعة اليدوية | -| `INSUFFICIENT_AUTH_CONTEXT` | سياق الدور/النطاق مفقود | -| `RESTRICTED_SCOPE` | الطلب يتجاوز النطاق المسموح | - -### قيم إجراءات المراقب - -قيم SDK: `continue | rerun | abort`. - -إذا استخدمت واجهة المنتج `CONT|RERUN|ABORT|EDIT`، فقم بالربط صراحةً: -- `CONT` -> `continue` -- `RERUN` -> `rerun` -- `ABORT` -> `abort` -- `EDIT` -> امتداد مشتق من التطبيق (ليس إجراء مراقب أصلي) - ---- - -## حقول حدث المراقب لكل تشغيل - -الحد الأدنى من الحقول المطلوب التقاطها لكل تقييم مراقب: - -| الحقل | الوصف | -|-------|-------| -| `run_id` | معرّف التشغيل الأصلي | -| `inspector_event_id` | معرّف الحدث الفريد | -| `inspector_name` | اسم المراقب | -| `target` | `INPUT | STEPS | OUTPUT` | -| `decision` | `continue | rerun | abort` | -| `reason_code` | سبب قابل للقراءة الآلية | -| `severity` | `LOW | MEDIUM | HIGH | CRITICAL` | -| `final_effect` | `none | rerouted | blocked` | -| `timestamp_start_utc` | بداية التقييم | -| `timestamp_end_utc` | نهاية التقييم | -| `latency_ms` | مدة التقييم | -| `retries_used` | عدد المحاولات المستهلكة | -| `governance_status` | حالة الحوكمة على مستوى التطبيق | -| `access_policy` | كائن سياسة قابل للقيمة الفارغة | -| `approval_status` | `DRAFT | PENDING_REVIEW | APPROVED | REJECTED` (قابل للقيمة الفارغة) | -| `run_total_latency_ms` | المدة الإجمالية للتشغيل | -| `token_or_credit_usage` | قابل للقيمة الفارغة؛ يُرجع لمستوى التشغيل | - ---- - -## قواعد الربط المطلوبة - -| القرار | حالة الحوكمة | التأثير النهائي | -|--------|-------------|----------------| -| `abort` | `BLOCKED_BY_INSPECTOR` | `blocked` | -| `rerun` (محاولة ناجحة) | — | `rerouted` | -| `continue` (بدون تدخل) | — | `none` | - -يمكن لحظر السياسة أن يُعيد حالة تشغيل `SUCCESS` مع مخرج رفض آمن — يُعامَل كحظر حوكمة، وليس فشل وقت التشغيل. - ---- - -## مصفوفة التحقق بعد التغيير (إلزامية) - -بعد إضافة/تحديث المراقبين، شغّل 3 موجِّهات بالضبط: - -| الاختبار | السلوك المتوقع | -|----------|---------------| -| **موجِّه مسموح** | مسار المتابعة، إجابة طبيعية متوافقة | -| **موجِّه مرفوض** | حظر/رفض، بدون بيانات/إجراءات مقيدة | -| **موجِّه غامض** | معالجة تحفظية (رفض أو طلب توضيح) | - -لكل حالة التقط: -- `prompt` -- `expected_action` -- `observed_run_status` -- `observed_governance_status` -- `observed_output_summary` -- `pass_fail` - ---- - -## بطاقات مؤشرات الأداء الرئيسية لكل مراقب - -عند اختيار وكيل، اعرض هذه المقاييس لكل مراقب: - -| المقياس | الوصف | -|---------|-------| -| `inspector_id` | المعرّف الفريد | -| `inspector_name` | اسم العرض | -| `inspector_desc` | الوصف | -| `target` | مدخل/خطوات/مخرج | -| `policy/action_mode` | نوع السياسة | -| `severity_model` | تصنيف الخطورة | -| `evaluation_count` | تقييمات المراقب (وليس تشغيلات الوكيل) | -| `pass_rate_pct` | معدل النجاح (صيغة صريحة مطلوبة) | -| `block_rate_pct` | معدل نتائج الإيقاف | -| `rerun_rate_pct` | معدل إعادة التشغيل | -| `edit_rate_pct` | معدل التعديل (إن وُجد) | -| `avg_reruns_per_evaluation` | متوسط المحاولات | -| `retry_exhausted_count` | عدد المحاولات المستنفدة | -| `avg_latency_ms` | متوسط زمن التقييم | -| `p95_latency_ms` | زمن الاستجابة عند المئين 95 | -| `avg_credits_per_evaluation` | تكلفة الرصيد لكل تقييم | -| `last_config_change_at` | طابع زمني لآخر تعديل | -| `last_config_change_by` | آخر مُعدِّل | -| `config_version` | إصدار التهيئة | -| `drift_7d_vs_30d_pass_delta` | انحراف معدل النجاح: 7 أيام مقابل 30 يومًا | -| `drift_7d_vs_30d_block_delta` | انحراف معدل الحظر: 7 أيام مقابل 30 يومًا | - -### مخطط الاتجاه (يومي) - -تتبّع لكل مراقب: `pass_count`، `block_count`، `rerun_count`، `override_count`. - - diff --git a/skills/aixplain-agent-builder/references/inspectors.md b/skills/aixplain-agent-builder/references/inspectors.md index 8837b828..95e430db 100644 --- a/skills/aixplain-agent-builder/references/inspectors.md +++ b/skills/aixplain-agent-builder/references/inspectors.md @@ -1,44 +1,83 @@ -# Inspector Reference +# Inspectors and governance (SDK v2) -Inspector policy patterns and post-change validation for aiXplain agents. -Linked from the main skill — consult when adding inspectors. +Use inspectors only when runtime policy or quality enforcement is required. Least-privilege tool actions remain necessary; inspectors do not replace permissions. -See `agent-patterns.md § Add Inspector` for the full v2 construction code. Use the v2 API (`aixplain.v2.inspector`), not the v1 `aixplain.modules.team_agent.inspector` path. +## Stable 0.2.47 surface ---- +The published `0.2.47` wheel intentionally uses a small, plain-data API: strings for `action`, `targets`, and `severity`; a Metric, asset ID, callable, or judge dict for `metric`. There are no public `InspectorActionConfig` or `EvaluatorConfig` classes in this release. -## Policy Guidelines +```python +from aixplain.v2.inspector import Inspector, AUTO_DEFAULT_MODEL_ID -- Set the policy with a plain string: `action="..."`. Valid values: `"continue" | "rerun" | "abort" | "edit"`. -- Prefer `"abort"` for hard policy violations. -- Use `"rerun"` for recoverable quality issues — it is the only action that accepts retry params, passed as a dict: `action={"type": "rerun", "max_retries": 2, "on_exhaust": "abort"}` (`on_exhaust` is `"continue" | "abort"`). Setting retry params on any other action raises. -- `"edit"` requires an `editor=...` (a Metric, asset-id string, or callable — same as `metric`). -- The judge is `metric=` (the universal judge): an `aix.Metric`, a bare asset-id string, or a Python callable. No `EvaluatorConfig`/enum imports. -- For existing deployments: fetch the team agent, modify `inspectors`/`inspector_targets`, call `.save()`. +inspector = Inspector( + name="Content Gate", + description="Blocks output that violates the approved content policy.", + severity="high", # low | medium | high | critical + targets=["output"], # input | steps | output | subagent name + action="abort", # continue | rerun | abort | edit + metric={ + "asset_id": AUTO_DEFAULT_MODEL_ID, + "prompt": "Fail output that violates the approved content policy.", + }, +) ---- +team.inspectors = [inspector] +team.save() +``` -## Status Semantics +Do not import from `aixplain.modules.team_agent.inspector`; that is v1. Do not generate the older typed config-class form for SDK `0.2.47`. -Keep aiXplain run status as-is: `IN_PROGRESS | SUCCESS | FAILED`. -Do not overload `FAILED` for policy blocks — a policy block can still return run `SUCCESS` with a safe refusal output. Treat that as a governance block, not a runtime failure. +## Action choice ---- +- `abort`: hard policy violation. +- `rerun`: recoverable quality problem. Retry settings use a dict: -## Inspector Action Values +```python +action={"type": "rerun", "max_retries": 2, "on_exhaust": "abort"} +``` -`action` SDK values (plain strings): `continue | rerun | abort | edit`. +- `edit`: sanitize output and requires `editor=` using the same accepted forms as `metric`. +- `continue`: observe/log without intervention. ---- +A custom callable can act as the judge: -## Post-Change Validation (Mandatory) +```python +def contains_required_citation(text: str) -> bool: + return "http" in text -After adding/updating inspectors, run exactly 3 prompts: +inspector = Inspector( + name="Citation Check", + targets=["output"], + action="rerun", + metric=contains_required_citation, +) +``` -| Test | Expected Behavior | -|------|-------------------| -| **Allowed prompt** | Continue path, compliant normal answer | -| **Denied prompt** | Blocked/denial, no restricted data/action | -| **Ambiguous prompt** | Conservative handling (deny or ask clarification) | +For reusable/model-backed judges, prefer an onboarded `aix.Metric` object or an asset ID plus prompt. -For each case capture: `prompt`, `expected_action`, `observed_run_status`, `observed_output_summary`, `pass_fail`. +## Mandatory policy verification + +After every inspector change, run exactly three cases: + +| Case | Expected behavior | +|---|---| +| Allowed | normal compliant answer | +| Denied | blocked/refused; restricted action or data absent | +| Ambiguous | conservative refusal or clarification request | + +Capture: + +```yaml +prompt: ... +expected_action: ... +observed_run_status: ... +observed_governance: ... +observed_output_summary: ... +pass_fail: ... +``` + +A policy block can return run status `SUCCESS`. Always read `result.data.governance` and inspect output; never equate `SUCCESS` with permission or completion. + +## Product boundary + +Debugger/Evolver meta-agents and dynamic self-improvement are not part of this skill's GA build path. Do not fabricate APIs. Use traces, controlled A/B changes, and explicit user review instead. diff --git a/skills/aixplain-agent-builder/references/integration-build-and-data.md b/skills/aixplain-agent-builder/references/integration-build-and-data.md new file mode 100644 index 00000000..65ca1631 --- /dev/null +++ b/skills/aixplain-agent-builder/references/integration-build-and-data.md @@ -0,0 +1,180 @@ +# Integration build and data tools (SDK v2) + +Read this when creating deterministic tools, enabling runtime code, working with databases or knowledge, or handling files. + +## Missing capability: build a deterministic tool with `code=` + +Use the Python Sandbox integration when no existing tool/integration meets the need and the implementation should be fixed at build time. + +```python +code = """def normalize_account(name: str, domain: str): + clean_name = " ".join(name.split()).strip() + clean_domain = domain.lower().removeprefix("https://").rstrip("/") + return {"name": clean_name, "domain": clean_domain} +""" + +tool = aix.Tool( + name="Normalize Account", + description="Normalizes an account name and website domain.", + integration="688779d8bfb8e46c273982ca", + config={"code": code, "function_name": "normalize_account"}, + allowed_actions=["normalize_account"], +).save() +``` + +Authoring constraints verified on `0.2.47`: +- `function_name` must exactly match a function defined in `code`. +- Prefer `int` (`0`/`1`) over `bool` parameters because boolean serialization has produced invalid Python literals. +- Return `dict` or `list`, not tuples. +- Define custom code inline in portable build code; custom tool instance IDs are workspace-bound. +- Test the tool standalone before attaching it. + +### Python Sandbox authoring contract + +Use Python Sandbox for a small, deterministic function authored at build time—not for an agent to generate code during a run, and not as an implicit replacement for an approved external integration. + +- Define one clear entry function with JSON-compatible inputs and a `dict` or `list` result. +- Prefer the Python standard library. Do not assume third-party packages, network access, filesystem persistence, environment variables, or credentials are available unless the live integration schema and a standalone run prove the requirement works. +- Never embed API keys, tokens, customer data, or workspace-bound IDs in `code`. +- Keep side effects explicit and minimal. Use a supported integration with explicit user consent when the job needs an external system. +- Inspect the current integration actions and input schema before generating code that depends on a Sandbox capability. +- Run one representative success case and one expected failure case directly through the tool before attaching it to an agent; then verify the agent trace includes the tool when the capability is required. + +```python +result = tool.run( + action="normalize_account", + data={"name": " Acme Corp ", "domain": "https://ACME.EXAMPLE/"}, +) +assert result.data["domain"] == "acme.example" +``` + +## Runtime Code Execution + +Use Code Execution only when the deployed agent must decide and write code during a run (analysis, calculations, data transforms, plots, dynamic file processing, or API/URL processing). + +```python +code_execution = aix.Tool.get("698cda188bbb345db14ac13b") +code_execution.allowed_actions = ["run"] +``` + +Do not add it merely because the build itself uses Python. For deterministic logic, prefer a Python Sandbox tool. + +When generated files must be returned, instruct the runtime code to print metadata: + +```json +[{"name":"analysis.csv","file":"analysis.csv"}] +``` + +Without file metadata, generated files may be lost. + +## PostgreSQL + +Use read-only credentials whenever possible and reinforce read-only behavior in the agent instructions. + +```python +pg = aix.Tool( + name="Customer Database Reader", + description="Reads approved customer reporting tables.", + integration="693ac6e8217c7b13b480970f", + config={"url": database_url}, + allowed_actions=["schema", "query"], +).save() +``` + +Never enable write actions unless the user explicitly requires and approves them. + +## SQLite + +```python +from aixplain.v2.upload_utils import FileUploader + +url = FileUploader(api_key=api_key, backend_url=aix.backend_url).upload( + "/absolute/path/to/data.db", + is_temp=True, + return_download_link=True, +) + +sqlite = aix.Tool( + name="SQLite Reader", + description="Reads the supplied SQLite database.", + integration="689e06ed3ce71f58d73cc999", + config={"url": url}, + allowed_actions=["schema", "query"], +).save() +``` + +SQLite writes apply to an in-memory copy and are not a persistence strategy. + +## Knowledge base + +```python +index = aix.Tool( + name="Product Knowledge", + description="Searches approved product documentation.", + integration="6904bcf672a6e36b68bb72fb", +).save() + +index.run(action="upsert", data={"records": [ + {"id": "doc-1", "text": "Approved product facts.", "metadata": {"type": "product"}}, +]}) +index.allowed_actions = ["search", "get"] +index.save() +``` + +Tell the agent explicitly to search the index. Otherwise it may answer from the LLM while the knowledge tool remains unused. + +## Files in and out + +Local paths are not portable runtime inputs. Upload and use HTTPS URLs: + +```python +from aixplain.v2.upload_utils import FileUploader + +url = FileUploader(api_key=api_key).upload( + "/absolute/path/to/input.pdf", + is_temp=True, + return_download_link=True, +) +``` + +Always use `return_download_link=True` for a browser-accessible URL. + +### HTML and ZIP MIME safeguard + +Stable `0.2.47` omits `.html` and `.zip` from the fallback MIME map; unrecognized files fall back to `text/csv`. Patch before uploading these deliverables: + +```python +from aixplain.v2.upload_utils import MimeTypeDetector + +MimeTypeDetector.EXTENSION_MAPPING[".html"] = "text/html" +MimeTypeDetector.EXTENSION_MAPPING[".zip"] = "application/zip" +``` + +When a tool config needs an uploaded resource URL, construction alone does not upload. Call `.save()` and then read `.url`: + +```python +resource = aix.Resource(name="Input Database", file_path="/absolute/path/to/data.db") +resource.save() +assert resource.url +``` + +`aix.Resource.create_from_file(...)` only constructs the object in `0.2.47`; its URL remains empty until `.save()` uploads it. + +For deliverables, the File Manager tool is preferred: + +```python +import uuid + +file_manager = aix.Tool.get("6a0216cffb2a801f1c41e32e") +file_manager.allowed_actions = ["save_content"] +result = file_manager.run( + action="save_content", + data={ + "requestid": str(uuid.uuid4()), + "contents": ["final report"], + "names": ["report.md"], + }, +) +``` + +`requestid` is required. diff --git a/skills/aixplain-agent-builder/references/integration-connections.md b/skills/aixplain-agent-builder/references/integration-connections.md new file mode 100644 index 00000000..55767705 --- /dev/null +++ b/skills/aixplain-agent-builder/references/integration-connections.md @@ -0,0 +1,131 @@ +# Integration connections (SDK v2) + +Read only the section needed for the current agent. All examples assume: + +```python +from aixplain import Aixplain + +aix = Aixplain(api_key=api_key) +``` + +Never use v1 factories. Before connecting, call `integration.list_actions()` and `integration.list_inputs(action_name)`. Scope every attached tool to a non-empty least-privilege `allowed_actions` list. + +## Discover before connecting + +```python +matches = aix.Integration.search(query="slack").results +for item in matches: + print(item.name, item.id, item.path) + +integration = aix.Integration.get(matches[0].id) +print(integration.list_actions()) +``` + +## Slack: discover and scope the exact action + +Preserve the explicit connection checkpoint. After the user agrees to connect Slack, inspect the action list and use the exact platform action name—not a conceptual alias: + +```python +slack = aix.Integration.get("686432941223092cb4294d3f") +actions = list(slack.list_actions()) +assert "SLACK_SEND_MESSAGE" in actions + +slack_tool = aix.Tool( + name="Approved Slack Sender", + description="Sends an approved message to the selected Slack destination.", + integration=slack, + allowed_actions=["SLACK_SEND_MESSAGE"], +).save() +``` + +Request only the minimum `chat:write` permission needed to send the approved message. Do not use `send_message` as an action name. + +## Web Search: inspect before generating runnable code + +Prefer the first-party aixplain Web Search tool when it satisfies the request. With this unified plugin, use Marketplace Search to find the tool, then call `list_actions_tools` and `list_inputs_tools` for the selected asset before writing code. Emit only the returned action name and input fields; never invent nested parameters. Validate the resulting SDK snippet before presenting it. + +Do not choose solely by display name. Prefer `aixplain/...` first-party paths over brokered connectors when both meet the requirement. + +## Existing marketplace tool + +```python +tool = aix.Tool.get("") +print(list(tool.actions)) +tool.allowed_actions = ["required_action"] +``` + +`tool.actions` is an `Actions` collection. Use `list(tool.actions)`; `.keys()` and numeric indexing are not reliable. + +## OAuth integration + +Ask before initiating the external connection. State the minimum actions and why they are needed. + +```python +import warnings + +integration = aix.Integration.get("6864328d1223092cb4294d30") # Gmail +print(integration.list_actions()) + +with warnings.catch_warnings(record=True) as captured: + warnings.simplefilter("always") + tool = aix.Tool( + name="Customer Email Sender", + description="Sends the final approved customer email.", + integration=integration, + allowed_actions=["GMAIL_SEND_EMAIL"], + ).save() + +connect_url = next( + (str(item.message) for item in captured if "http" in str(item.message)), + None, +) +print(connect_url) +``` + +The connection is workspace-specific. Create the tool fresh from the integration in each workspace; do not ship a saved connector tool ID in portable build code. + +OAuth is a deliberate two-phase checkpoint: print/present the connect URL and stop that execution. Never call `input()` or wait interactively inside generated scripts. After the user confirms authorization, continue agent creation or verification in a new execution using the in-memory tool when available or its recorded workspace-local ID. + +## Provider API-key integration + +Ask for the provider key only after the user agrees to connect the provider. Keep it in memory/environment and never commit or print it. + +```python +integration = aix.Integration.get("") +connected = integration.run( + name="Provider Tool", + authScheme="API_KEY", + data={"generic_api_key": provider_api_key}, +) +tool = aix.Tool.get(connected.data.id) +tool.allowed_actions = ["MINIMUM_REQUIRED_ACTION"] +tool.save() +``` + +The authentication field is `generic_api_key`; `api_key` and `apiKey` can fail with an unhelpful generic error. + + +## Remote MCP server + +```python +mcp = aix.Tool( + name="Remote MCP Tool", + description="Uses the approved remote MCP capability.", + integration="aixplain/mcp-server", + config={"url": "https://example.com/mcp"}, +).save() +mcp.allowed_actions = ["required_action"] +mcp.save() +``` + +Keep the exposed action set small; large MCP surfaces degrade tool selection. + +## Verification sequence + +For every connected or custom tool: +1. Run it directly with realistic input. +2. Attach it to the agent. +3. Run an agent prompt that requires it. +4. Confirm its unit name appears in `result.data.steps`. +5. Confirm governance allowed the run. +6. If observed behavior differs, follow the safe fallback in `reliability-guidelines.md` and keep a redacted local reproduction. diff --git a/skills/aixplain-agent-builder/references/integration-playbooks.ar.md b/skills/aixplain-agent-builder/references/integration-playbooks.ar.md deleted file mode 100644 index 68f9b06f..00000000 --- a/skills/aixplain-agent-builder/references/integration-playbooks.ar.md +++ /dev/null @@ -1,141 +0,0 @@ -# أدلة التكامل العملية - -أنماط الاتصال حسب نوع التكامل. -مرتبط من المهارة الرئيسية — يُستشار عند توصيل الأدوات. - -nقطة الدخول الأساسية v2: -- استخدم `from aixplain import Aixplain`. -- استخدم `aix.Tool` و`aix.Integration` مباشرةً من واجهة عميل v2. - -استخدم `integration.list_actions()` و`integration.list_inputs(action_name)` قبل الاتصال في تدفقات SDK. احرص دائمًا على تحديد نطاق الإجراءات بمجموعة غير فارغة ذات أقل صلاحيات. - ---- - -## 1. قاعدة المعرفة aiR (`6904bcf672a6e36b68bb72fb`) - -اتصل بملفات مرفوعة (`.pdf`، `.docx`، `.txt`، `.md`، `.html`، `.csv`) أو ابدأ فارغًا واكتب باستخدام `upsert`. - -**الإجراءات النموذجية:** `search`، `get`، `count`، `metadata`، واختياريًا `upsert`. -**أفضل الممارسات:** حافظ على نطاق الاسترجاع أولًا (`search`، `get`، `metadata`) ما لم تكن الكتابة أثناء التشغيل مطلوبة صراحةً. - -```python -index_tool = aix.Tool( - name="My Knowledge Base", - description="Semantic search over uploaded documents", - integration="6904bcf672a6e36b68bb72fb", -).save() - -# Populate -records = [ - {"id": "doc1", "text": "Python is a programming language", "metadata": {"category": "tech"}}, - {"id": "doc2", "text": "Machine learning uses algorithms", "metadata": {"category": "ai"}}, -] -index_tool.run(action="upsert", data={"records": records}) - -# Query -index_tool.run(action="search", data={"query": "programming", "filters": []}) -index_tool.run(action="count", data={}) -``` - -يعمل البحث الدلالي فورًا بعد `upsert`؛ ويُعيد `count` إجمالي المستندات المفهرسة. - ---- - -## 2. PostgreSQL (`693ac6e8217c7b13b480970f`) - -اتصل باستخدام عنوان URL لقاعدة البيانات: `postgresql://username:password@host:port/database`. - -- يُفضَّل استخدام مستخدمي قاعدة بيانات للقراءة فقط لمساعدي التحليل. -- حدِّد نطاق إجراءات الاستعلام أولًا؛ وفعِّل إجراءات الكتابة فقط عند الحاجة الصريحة. - ---- - -## 3. قاعدة بيانات SQLite (`689e06ed3ce71f58d73cc999`) - -اتصل باستخدام عنوان URL لملف `.db` عبر `config={"url": ""}`. - -- الإجراءات الصالحة: `query`، `schema`، `commit`. -- النطاق الآمن الافتراضي: `["query", "schema"]`. -- يرفض اتصال SQLite أنواع الملفات غير `.db`. - -```python -from aixplain.v2.upload_utils import FileUploader - -integration = aix.Integration.get("689e06ed3ce71f58d73cc999") -uploader = FileUploader(api_key=api_key, backend_url=aix.backend_url) -db_url = uploader.upload("/absolute/path/to/data.db", is_temp=True, return_download_link=True) - -sqlite_tool = aix.Tool( - name="SQLite Reader", - description="Read-only DB access", - integration=integration, - config={"url": db_url}, - allowed_actions=["query", "schema"], -).save() -``` - ---- - -## 4. صندوق حماية Python (`688779d8bfb8e46c273982ca`) - -اتصل بملف `.py` أو شيفرة مصدرية مضمّنة وعيِّن `function_name` للدالة المكشوفة. - -### قيود التأليف - -تم التحقق منها مقابل aiXplain SDK v0.2.44. اتبع قيود التأليف التالية: - -1. **يجب أن يتطابق `function_name` مع دالة معرَّفة في `code`.** إذا ضمّنت عدة دوال في كتلة شيفرة واحدة، فإن الدالة التي يساوي اسمها `function_name` فقط هي التي تُسجَّل كأداة. -2. **استخدم `int` (0/1) بدلًا من معاملات `bool`.** يمرر مُسلسِل وقت التشغيل JSON `true`/`false` (بأحرف صغيرة)، والتي يفسرها Python كأسماء غير معرَّفة. استخدام `int` يتجنب ذلك. -3. **أعِد `dict` أو `list`، وليس tuples.** يُحوَّل `return a, b` إلى السلسلة النصية `"(a, b)"` عند النقل ذهابًا وإيابًا. للمخرجات المهيكلة، أعِد دائمًا `dict` أو `list`. - -### ملاحظات v2 - -- تُكتشف مواصفات الإجراءات/المدخلات الخاصة بالتكامل بعد الاتصال (`list_actions`/`list_inputs`). -- الشيفرة المساعدة المخصصة (`aix.Utility`) لها قواعد تحليل منفصلة وتتطلب `def main(...)`؛ لا تخلط بينها وبين أدوات تكامل صندوق حماية Python. - -```python -script_content = """def sum_then_square(first_number: int, second_number: int): - total = first_number + second_number - return total * total -""" -tool = aix.Tool( - name="Sum and Square", - description="Sums two numbers and squares the result.", - integration="688779d8bfb8e46c273982ca", - config={"code": script_content, "function_name": "sum_then_square"}, -).save() - -result = tool.run(action="sum_then_square", data={"first_number": 2, "second_number": 3}) -``` - ---- - -## 5. تكاملات OAuth (Gmail، Slack، Jira، Google Drive) - -سير العمل موصوف في SKILL.md § 3 (إنشاء الأدوات، المسار C). يوفر هذا القسم الشيفرة. - -```python -import warnings - -integration = aix.Integration.get("6864328d1223092cb4294d30") # Gmail -actions = integration.list_actions() - -with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - gmail_tool = aix.Tool( - name="My Gmail Tool", - description="Send emails", - integration=integration, - allowed_actions=["GMAIL_SEND_EMAIL"], - ).save() - oauth_url = next((str(x.message) for x in w if "http" in str(x.message)), None) - -print(f"Connect Gmail: {oauth_url}") -# User completes OAuth, then attach to agent: -agent = aix.Agent( - name="Gmail Agent", instructions="Use Gmail to send emails.", - tools=[gmail_tool], output_format="markdown", -).save() -``` - - diff --git a/skills/aixplain-agent-builder/references/integration-playbooks.md b/skills/aixplain-agent-builder/references/integration-playbooks.md deleted file mode 100644 index b254eed9..00000000 --- a/skills/aixplain-agent-builder/references/integration-playbooks.md +++ /dev/null @@ -1,139 +0,0 @@ -# Integration Playbooks - -Connection patterns per integration type. -Linked from the main skill — consult when wiring tools. - -Canonical v2 entry point: -- Use `from aixplain import Aixplain`. -- Use `aix.Tool`, `aix.Integration` directly from the v2 client surface. - -Use `integration.list_actions()` and `integration.list_inputs(action_name)` before connecting in SDK flows. Always scope actions to a non-empty least-privilege set. - ---- - -## 1. aiR Knowledge Base (`6904bcf672a6e36b68bb72fb`) - -Connect with uploaded files (`.pdf`, `.docx`, `.txt`, `.md`, `.html`, `.csv`) or start empty and write with `upsert`. - -**Typical actions:** `search`, `get`, `count`, `metadata`, optional `upsert`. -**Best practice:** Keep retrieval-first scope (`search`, `get`, `metadata`) unless runtime writing is explicitly required. - -```python -index_tool = aix.Tool( - name="My Knowledge Base", - description="Semantic search over uploaded documents", - integration="6904bcf672a6e36b68bb72fb", -).save() - -# Populate -records = [ - {"id": "doc1", "text": "Python is a programming language", "metadata": {"category": "tech"}}, - {"id": "doc2", "text": "Machine learning uses algorithms", "metadata": {"category": "ai"}}, -] -index_tool.run(action="upsert", data={"records": records}) - -# Query -index_tool.run(action="search", data={"query": "programming", "filters": []}) -index_tool.run(action="count", data={}) -``` - -Semantic search works immediately after `upsert`; `count` returns indexed document total. - ---- - -## 2. PostgreSQL (`693ac6e8217c7b13b480970f`) - -Connect with database URL: `postgresql://:@:/`. - -- Prefer read-only DB users for analysis assistants. -- Scope query actions first; enable write actions only when explicitly required. - ---- - -## 3. SQLite Database (`689e06ed3ce71f58d73cc999`) - -Connect using `.db` file URL via `config={"url": ""}`. - -- Valid actions: `query`, `schema`, `commit`. -- Default safe scope: `["query", "schema"]`. -- SQLite connect rejects non-`.db` file types. - -```python -from aixplain.v2.upload_utils import FileUploader - -integration = aix.Integration.get("689e06ed3ce71f58d73cc999") -uploader = FileUploader(api_key=api_key, backend_url=aix.backend_url) -db_url = uploader.upload("/absolute/path/to/data.db", is_temp=True, return_download_link=True) - -sqlite_tool = aix.Tool( - name="SQLite Reader", - description="Read-only DB access", - integration=integration, - config={"url": db_url}, - allowed_actions=["query", "schema"], -).save() -``` - ---- - -## 4. Python Sandbox (`688779d8bfb8e46c273982ca`) - -Connect with `.py` file or inline source code and set `function_name` for the exposed function. - -### Authoring Constraints - -Verified against aiXplain SDK v0.2.44. Follow these authoring constraints: - -1. **`function_name` must match a function defined in `code`.** If you include multiple functions in one code block, only the one whose name equals `function_name` is registered as the tool. -2. **Use `int` (0/1) instead of `bool` parameters.** The runtime serializer passes JSON `true`/`false` (lowercase), which Python interprets as undefined names. Using `int` avoids this. -3. **Return `dict` or `list`, not tuples.** A `return a, b` round-trips as the string `"(a, b)"`. For structured output, always return a `dict` or `list`. - -### v2 Notes - -- Integration action/input specs are discovered after connection (`list_actions`/`list_inputs`). -- Custom utility code (`aix.Utility`) has separate parser rules and requires `def main(...)`; do not confuse with Python Sandbox integration tools. - -```python -script_content = """def sum_then_square(first_number: int, second_number: int): - total = first_number + second_number - return total * total -""" -tool = aix.Tool( - name="Sum and Square", - description="Sums two numbers and squares the result.", - integration="688779d8bfb8e46c273982ca", - config={"code": script_content, "function_name": "sum_then_square"}, -).save() - -result = tool.run(action="sum_then_square", data={"first_number": 2, "second_number": 3}) -``` - ---- - -## 5. OAuth Integrations (Gmail, Slack, Jira, Google Drive) - -The workflow is described in SKILL.md § 3 (Create Tools, Path C). This section provides the code. - -```python -import warnings - -integration = aix.Integration.get("6864328d1223092cb4294d30") # Gmail -actions = integration.list_actions() - -with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - gmail_tool = aix.Tool( - name="My Gmail Tool", - description="Send emails", - integration=integration, - allowed_actions=["GMAIL_SEND_EMAIL"], - ).save() - oauth_url = next((str(x.message) for x in w if "http" in str(x.message)), None) - -print(f"Connect Gmail: {oauth_url}") -# User completes OAuth, then attach to agent: -agent = aix.Agent( - name="Gmail Agent", instructions="Use Gmail to send emails.", - tools=[gmail_tool], output_format="markdown", -).save() -``` diff --git a/skills/aixplain-agent-builder/references/reliability-guidelines.md b/skills/aixplain-agent-builder/references/reliability-guidelines.md new file mode 100644 index 00000000..e68a7757 --- /dev/null +++ b/skills/aixplain-agent-builder/references/reliability-guidelines.md @@ -0,0 +1,47 @@ +# Reliability Guidelines + +Use these proven practices to build and maintain aixplain agents safely with SDK v2. They capture behavior verified against `aixplain==0.2.47` and help the agent choose dependable paths without exposing an internal defect log. + +## Build against the released SDK + +- Use `from aixplain import Aixplain` and SDK v2 objects only. +- Validate generated Python with `py_compile` and inspect constructors from the installed package before presenting exports. +- Use `llm=`, never `llm_id=`, in exported agent constructors. Keep iteration limits in `agent.budget`; do not set a top-level `max_iterations` field. + +## Configure budgets as guardrails, not invoices + +- Set durable controls on `agent.budget`; `agent.run(budget=...)` is not a reliable one-off override. +- Treat `max_cost` as a circuit breaker rather than an exact billing ceiling, especially for teams. +- Report actual usage from `result.data.execution_stats["credits"]` after every consequential run. + +## Verify capabilities from live schemas + +- Inspect Marketplace Search results through each kind's `results` collection, not `items`. +- Before generating Web Search or integration code, inspect the exact actions and input schemas. +- If Marketplace Search is unavailable, continue with SDK discovery and state only material limitations. + +## Keep integrations safe in real agent runs + +- Test each integration directly and again inside the agent. A connector that works alone may need a compatible model or deterministic application code when its structured output cannot be consumed in an agent loop. +- Request explicit user consent before connecting an external service, and use the smallest supported action scope. +- Call `resource.save()` before relying on a file resource URL. Set explicit MIME types for `.html` and `.zip` uploads when needed. + +## Preserve least privilege during updates + +Before a get → mutate → save update, fetch the raw agent payload and restore each tool's stored `actions` onto the hydrated tool's `allowed_actions`. Then verify the raw persisted scopes after saving. This prevents an update from unintentionally broadening tool permissions. + +## Prove required tool use + +A successful result is not proof that a required tool ran. For every realistic verification: + +1. Check the output and governance status. +2. Inspect `result.data.steps` for the intended tool unit. +3. When tool invocation is mandatory, prefer adaptive or planner execution and assert the unit is present. + +## Keep final-answer behavior explicit + +Do not rely on `response_generator` or a deprecated separate response-generation pass. Put final-answer requirements in the primary agent instructions, or model a visible task/subagent when a separate synthesis step is required. + +## Escalate responsibly + +If observed behavior contradicts these guidelines, preserve a minimal, redacted reproduction in local engineering notes, use the safest confirmed workaround, and draft a public issue only when the user asks. Never include API keys, customer data, or workspace-bound IDs. diff --git a/skills/marketplace-search/SKILL.md b/skills/marketplace-search/SKILL.md new file mode 100644 index 00000000..2cc14bce --- /dev/null +++ b/skills/marketplace-search/SKILL.md @@ -0,0 +1,217 @@ +--- +name: marketplace-search +description: Search the aixplain marketplace for agents, models, tools, and integrations — check whether an asset exists, what it costs, who hosts it, how many of a kind there are — and turn any hit into working code (SDK, REST, MCP config, or an agent that attaches it). Use whenever the user asks what is on aixplain, asks for a model/tool by name or capability, asks the price or host of an aixplain asset, or asks how to call or attach one. +--- + +# aixplain Marketplace Search + +Searches the aixplain catalog through the `aixplain-marketplace-search` MCP server, then hands back +something runnable. One `search` covers all four asset types at once — you never need to know whether +the thing you want is a model, a tool, an agent, or an integration. + +Every fact you report must come from a tool result. Never invent an asset, price, host, or count. +If every type returns `total: 0`, say the asset is not on the marketplace. + +## Setup check + +The tools are named `mcp__aixplain-marketplace-search__*`. If they are not available, the plugin's MCP +server has not connected — almost always a missing key. Tell the user: + +```bash +export AIXPLAIN_API_KEY=your_aixplain_api_key +``` + +Keys come from https://studio.aixplain.com under account settings. The server is PROD. + +## Which tool to call + +| Question | Call | +|---|---| +| Does X exist? What is it? | `search` with `query` | +| Price, host, supplier, status | `get_asset_details` with `asset_id` | +| How many X are there? | `search` with `query: ""` plus filters, read `stats.total` | +| What inputs does it take? | `list_inputs_models` / `list_inputs_tools` / `list_inputs_agents` / `list_inputs_integrations` | +| What actions does it have? | `list_actions_models` / `list_actions_tools` / … | +| What filter values are valid? | `list_filters` (no arguments) | + +`search` parameters are all flat strings or numbers — **not** arrays: `query`, `asset_type`, +`categories`, `developers`, `suppliers`, `hosts`, `function`, `num_results`, `page_number`, `page_size`. +`get_asset_details` takes `asset_id` (snake_case) and optionally `asset_type`. + +`search` returns one block per asset type, each `{results: [...], stats: {total, pages_count, current_page}}`. + +### The surface is only universal at discovery + +Of the 19 actions, three are universal — `search`, `get_asset_details`, `list_filters` — and sixteen are +per-type variants of `search_*`, `list_actions_*`, `list_inputs_*`, `run_*`. **There is no universal +`run`.** Do not look for one. Resolve the asset's type from the `search` block it came back in (or from +`get_asset_details.asset_type`), then use the matching per-type action. + +`get_asset_details` is the exception worth knowing: it takes `asset_id` alone, with `asset_type` +optional, so you can read any asset's details without knowing its type first. + +### Counting + +Pass an empty `query` as a wildcard and read `stats.total`. Add `page_size: 1` so you are not paying for +rows you will not read: + +``` +search(query: "", asset_type: "model", categories: "LLM", page_size: 1) → stats.total = 177 +``` + +Filter by `hosts` or `developers` the same way to narrow a count ("how many hosted by OpenAI"). + +There is **no sort parameter** — `search` orders by relevance only. To answer "cheapest", "newest", or +"most expensive" you must page through the filtered set and sort locally, which for a big category +(LLM alone is 177) means several calls. Say that is what you are doing rather than presenting a +single page's minimum as the catalog's minimum. +Do not count with `search_models` / `search_tools` / `search_agents` / `search_integrations` — those cap +results and carry no reliable total. Use them only to list examples. + +Valid `categories` (from `list_filters`): LLM, Productivity, Marketing, Finance & Accounting, Utility, +Sales, Customer Support, Communication, Cybersecurity, Developer Tools, Search, Speech, +E-commerce & Payments, Analytics, Data & Storage, Language, Miscellaneous, Media & Creative, +Development, Image, Classification, Video, OCR, Guardrails. Call `list_filters` for the current +developer/host/supplier lists rather than guessing a name. + +### Matching behavior + +Keyword matching is literal. If a multi-word phrase returns nothing, retry with one distinctive token +before concluding the asset is absent — "speaker diarization whisper" may miss where "whisper" hits. + +### Pricing has two shapes + +Report whichever the tool returned; do not normalize one into the other. + +- Per-unit assets: `{price, unit_type, unit_type_scale}` → "0.0018 per MINUTE" +- Token-priced LLMs: `{input_price, output_price}` → "0.000003 in / 0.000015 out per token" +- `null` or absent → say pricing is not listed. + +For hosting, report the returned `hosted_by` value only. If it is absent or empty, say **“Hosting provider is not listed.”** Never infer a host from a model name, supplier, or price. + +## Always finish the lookup + +When the user asks about cost, host, supplier, function, or status, call `get_asset_details` on the +best match in the same response as the `search`. Do not stop at `search` and do not ask permission to +look up details. If several assets match strongly, detail the most relevant and name the others. + +## Turning a hit into code + +Resolve the asset's `id`, `path`, and `asset_type` first. For web search, prefer the first-party aixplain Web Search tool when it meets the request. Then **call the matching `list_actions_*` and `list_inputs_*` tools** and build the snippet from the real returned action and input names — `source_audio`, `text`, +`sourcelanguage`, whatever the asset actually declares. Only fall back to a `""` +placeholder if `list_inputs_*` returns nothing usable. Never invent a parameter name. + +Note which inputs are `required`, and which are `isFixed: true` with a single allowed value — a fixed +input should be emitted as that literal value, not as a choice for the user to fill in. Inputs with an +`availableOptions` list are enums: pick from it (language codes, for example) rather than free-texting. + +## Testing an asset before you wire it in + +**Do not use the `run_models` / `run_tools` / `run_agents` / `run_integrations` MCP actions.** They take +a single `input` string, which cannot express the multi-field input real assets declare, and they fail in +practice — verified 2026-08-18: an LLM returned `err.supplier_error` ("Input required: specify prompt or +messages") and a translation model returned HTTP 491, while the same asset ran fine through the SDK. + +To actually test an asset, write the SDK call from `list_inputs_*` and run it in the shell: + +```bash +python3 -c ' +from aixplain import Aixplain +aix = Aixplain(api_key="'"$AIXPLAIN_API_KEY"'") +r = aix.Model.get("").run() +print(r.status, r.data) +' +``` + +Report the real `status` and `data`. That closes the loop — found, verified running, then integrated — +and it is the only execution path that works today. + +### Python SDK + +`run()` takes the input names as **keyword arguments** — not a positional dict. Use the exact `name` +values from `list_inputs_*`: + +```python +from aixplain import Aixplain +aix = Aixplain(api_key="YOUR_API_KEY") + +# model — one kwarg per declared input (verified against Cloud Translation) +r = aix.Model.get("66aa869f6eb56342c26057e1").run( + text="Good morning", sourcelanguage="en", targetlanguage="ar") +print(r.status, r.data) # SUCCESS صباح الخير + +aix.Tool.get("").run(action="", data={...}) # tool +aix.Agent.get("").run(query="...") # agent +``` + +`Model.get("").run({...})` with a positional dict raises `TypeError` — always kwargs. +Read the result off `r.status` and `r.data`. + +`Model.get` / `Tool.get` also accept the supplier path (`"openai/whisper-large/groq"`), not just the id. +Integrations need a one-time connect (OAuth or API key) before they can run — point the user at the +integration's page rather than emitting a one-line call. + +### Attach it to an agent + +This is usually what the user actually wants after finding a tool: + +```python +from aixplain import Aixplain +aix = Aixplain(api_key="YOUR_API_KEY") + +agent = aix.Agent( + name="...", + description="...", + instructions="...", + tools=[aix.Tool.get("")], +).save() + +agent.run(query="...") +``` + +### REST + +Headers `x-api-key: YOUR_API_KEY` and `Content-Type: application/json`. + +- model: `POST https://models.aixplain.com/api/v2/execute/` — body from `list_inputs_models` +- tool: `POST https://models.aixplain.com/api/v2/execute/` — body `{"action": "", "data": {...}}` +- agent: `POST https://platform-api.aixplain.com/v2/agents//run` — body `{"query": "..."}` + +A response may return a `requestId` to poll: models and tools at +`GET https://models.aixplain.com/api/v2/data/`, agents at +`GET https://platform-api.aixplain.com/sdk/agents//result`. + +### MCP config for another asset + +Models and tools are each individually available over hosted MCP — agents and integrations are not. +Endpoint: `https://models-mcp.aixplain.com/mcp/` (encode `/` as `%2F`). + +Native streamable HTTP — Claude Code, VS Code, newer Cursor: + +```json +{"mcpServers": {"": { + "type": "http", + "url": "https://models-mcp.aixplain.com/mcp/", + "headers": {"Authorization": "Bearer YOUR_API_KEY", "Accept": "application/json, text/event-stream"} +}}} +``` + +stdio bridge — Claude Desktop and older clients without native remote MCP: + +```json +{"mcpServers": {"": { + "command": "npx", + "args": ["-y", "mcp-remote", "https://models-mcp.aixplain.com/mcp/", "--header", "Authorization:${AUTH_HEADER}"], + "env": {"AUTH_HEADER": "Bearer YOUR_API_KEY", "PATH": "/opt/homebrew/bin:/usr/bin:/bin"} +}}} +``` + +Pass the key via `env.AUTH_HEADER` and reference it as `Authorization:${AUTH_HEADER}` — a literal space +inside a single `mcp-remote` arg breaks it. On Apple Silicon `npx` is usually `/opt/homebrew/bin/npx`; +keep its directory on `PATH`. + +## Answering + +Lead with the direct answer — yes/no, or the number — then a short bulleted detail block: path, +function, price, host, supplier, status. When listing matches, give name plus path so the user can +identify the exact asset. Never paste raw tool JSON. diff --git a/skills/marketplace-search/references/effective-mcp-use.md b/skills/marketplace-search/references/effective-mcp-use.md new file mode 100644 index 00000000..2fa5e9a9 --- /dev/null +++ b/skills/marketplace-search/references/effective-mcp-use.md @@ -0,0 +1,12 @@ +# Effective Marketplace MCP use + +Use this sequence whenever an answer depends on marketplace truth or runnable asset code: + +1. **Discover:** call `search` with a focused query. Retry with one distinctive token if a phrase misses. +2. **Identify:** take `id`, `path`, and `asset_type` from the returned result block. +3. **Confirm facts:** call `get_asset_details` before reporting price, supplier, status, or hosting. If `hosted_by` is empty, say “Hosting provider is not listed.” +4. **Inspect the contract:** call the asset-type-specific `list_actions_*` and `list_inputs_*` actions. Use only returned action names and fields. +5. **Generate and validate:** build SDK/REST/agent-attach code from that schema. Do not invent inner parameters. Prefer the first-party aixplain Web Search tool for web-search use cases when it fits. +6. **Test correctly:** do not use MCP `run_*` actions; generate the SDK call and run it with realistic input instead. + +Use `search` plus `stats.total` for counts. `search_*` is for examples, not authoritative totals. There is no universal run action and no server-side sort parameter.