Token导航 LogoToken导航

Agent Skill · 开发

simpleagents-builder(简单 Agent 构建器)

simpleagents-builder 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

simpleagents-builder 是开发类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为调用外部服务;许可证信息还不明确,第三方扫描已通过,按安装量在开发类的 21,746 个 Skill 里排第 20,462。

总安装 220周安装 9GitHub Stars 31下载 71核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:simpleagents-builder(简单 Agent 构建器)
来源仓库:https://github.com/craftsman-labs/simpleagents
仓库路径:skills/simpleagents-builder
安装命令:
npx skills add https://github.com/craftsman-labs/simpleagents --skill simpleagents-builder
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/craftsman-labs/simpleagents --skill simpleagents-builder

这个 Skill 是做什么的

开发类 Skill 面向写代码、改代码和排查问题,一般会读写仓库文件,部分还会执行构建或测试命令。

simpleagents-builder 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中整理仓库状态和协作事项。适用于围绕代码变更、Issue 跟踪和协作流程的信息组织与查询。

通过 npx skills add 命令从 GitHub 仓库安装并使用。安装前需确认权限范围、维护状态及是否触发联网或文件操作。建议结合原始 README 核验具体用法和功能边界。

来源仓库内路径:skills/simpleagents-builder

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/craftsman-labs/simpleagents --skill simpleagen…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要调用外部服务,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

simpleagents-builder 怎么安装?

在终端执行 npx skills add https://github.com/craftsman-labs/simpleagents --skill simpleagents-builder。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

simpleagents-builder 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

simpleagents-builder 需要什么权限?

权限级别是调用外部服务。该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

simpleagents-builder 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

simpleagents-builder 用的人多吗?

公开数据是累计安装 220 次,近一周安装 9 次,下载 71 次,GitHub 31 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

simpleagents-builder 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理开发类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

开发类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
skill-vetter

技能审查员 · OpenClaw

8,779,018986操作浏览器MIT-0通过
ontology

本体论 · OpenClaw

6,636,960576只读MIT-0通过
entra-app-registration

内部应用程序注册 · Codex、Claude

6,631,872764接触敏感数据未明确通过
entra-app-registration

内部应用程序注册 · Codex、Claude

3,757,680201接触敏感数据未明确通过
nano-banana-pro

纳米香蕉专业版 · OpenClaw

3,526,898364接触敏感数据MIT-0有提醒
simpleagents-builder

简单 Agent 构建器 · 本页

22031调用外部服务未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

SimpleAgentsBuilder

Every agentic SaaS is a config. Turn any AI product idea into a YAML workflow + runner code. When a user describes their problem, gather requirements through targeted follow-up questions, then generate the YAML workflow, handler file, and runner script that makes their agentic SaaS real.

When to Use This Skill

  • User asks to create, design, or improve a YAML agent workflow
  • User describes a problem that can be solved with an LLM workflow (classification, extraction, routing, generation, etc.)
  • User wants to add routing, guardrails, custom workers, or structured output to a workflow
  • User wants a complete runnable solution (YAML + handler + runner code)

Step 1: Gather Requirements (Ask These Questions)

When a user describes their problem, ask these follow-up questions before generating anything:

  1. Language: "Which language will you use to run this? (Python / TypeScript / both)"
  2. Streaming: "Do you need streaming output? (yes / no)"
  3. Observability: "Do you need Langfuse or Jaeger tracing? (langfuse / jaeger / none)"
  4. Custom logic: "Does any step need to call your own code (database lookup, API call, business rules)? If so, describe what it does."
  5. Model: "Which model do you want? (e.g. gpt-4.1-mini, azure/gpt-4.1-mini, claude-sonnet-4-20250514, or any OpenAI-compatible model)"

Then generate:

  • The YAML workflow file
  • A handlers.py / handlers.ts if custom workers are needed (assign handler_file path in YAML)
  • The runner script in the chosen language

Step 2: Generate the YAML Workflow

Core Rules

  1. Model the workflow as a graph, not a linear prompt.
  2. Define config.output_schema for every llm_call node.
  3. Keep switch routing deterministic -- simple == / != conditions.
  4. Each node prompt is single-responsibility.
  5. Instruct every LLM node: Return JSON only.
  6. Set additionalProperties: false on routing-critical schemas.

Required YAML Skeleton

id: workflow-id
version: 1.0.0
entry_node: first_node

nodes:
  - id: first_node
    node_type:
      llm_call:
        model: gpt-4.1-mini
        messages_path: input.messages
        append_prompt_as_user: true
        stream: true
        heal: true
    config:
      output_schema:
        type: object
        properties:
          field:
            type: string
        required: [field]
        additionalProperties: false
      prompt: |
        Your instruction here.
        Return JSON only.

edges:
  - from: first_node
    to: second_node

Node Types

TypeWhen to useExample
llm_callClassify, extract, generate, summarizeDetect intent, draft response, extract entities
switchRoute based on a previous node's outputIf category == "billing" go to billing handler
custom_workerRun deterministic code (DB, API, business logic)Look up customer, check inventory, call webhook

LLM Node Options

FieldTypeDefaultPurpose
modelstringrequiredLLM model identifier
temperaturefloatprovider defaultSampling temperature
max_tokensintprovider defaultMax response tokens
streamboolfalseEnable streaming for this node
stream_json_as_textboolfalseStream structured JSON as raw text deltas
healboolfalseAuto-fix truncated/malformed JSON
send_schemaboolfalseSend output_schema to the model as response format
messages_pathstring-Path to input messages (usually input.messages)
append_prompt_as_userboolfalseAppend config.prompt as a user message

Switch Node Pattern

- id: route_category
  node_type:
    switch:
      branches:
        - condition: '$.nodes.classify.output.category == "billing"'
          target: handle_billing
        - condition: '$.nodes.classify.output.category == "support"'
          target: handle_support
      default: handle_general

Custom Worker Pattern

YAML:

- id: lookup_customer
  node_type:
    custom_worker:
      handler: lookup_customer
      handler_file: handlers.py
  config:
    payload:
      customer_id: "{{ nodes.extract_info.output.customer_id }}"

Python handler (handlers.py):

def lookup_customer(context, payload):
    customer_id = payload.get("customer_id", "")
    return {"name": "John Doe", "plan": "enterprise"}

TypeScript handler (pass as customWorkerDispatch):

export function customWorkerDispatch(req: { handler: string; payload: unknown; context: unknown }): string {
  if (req.handler === "lookup_customer") {
    const p = req.payload as Record<string, unknown>;
    return JSON.stringify({ name: "John Doe", plan: "enterprise" });
  }
  throw new Error(`unknown handler: ${req.handler}`);
}

Templating -- Reference Previous Outputs

In prompts:

prompt: |
  The category is: {{ nodes.classify.output.category }}
  Reason: {{ nodes.classify.output.reason }}

In custom worker payloads:

config:
  payload:
    name: "{{ nodes.extract_name.output.name }}"

Globals (Run-Level Memory)

  • There is no top-level YAML globals: block.
  • Globals are created/updated from node config and are scoped to a single workflow run.
  • Read globals in templates with {{globals.<key>}}.
  • In set_globals and update_globals.from, use direct paths (for example nodes.classify.output.category), not {{...}}.
- id: classify
  node_type:
    llm_call:
      model: gpt-4.1-mini
  config:
    output_schema:
      type: object
      properties:
        category: { type: string }
      required: [category]
      additionalProperties: false
    set_globals:
      email_category: nodes.classify.output.category

- id: finalize
  node_type:
    llm_call:
      model: gpt-4.1-mini
  config:
    prompt: "Category is {{ globals.email_category }}"

Step 3: Generate the Runner Script

Python -- Normal Run

import json, os
from pathlib import Path
from dotenv import load_dotenv
from simple_agents_py import Client
from simple_agents_py.workflow_payload import workflow_execution_request_to_mapping
from simple_agents_py.workflow_request import (
    WorkflowExecutionRequest, WorkflowMessage, WorkflowRole,
)

load_dotenv()

client = Client(
    os.environ["WORKFLOW_PROVIDER"],
    api_base=os.environ["WORKFLOW_API_BASE"],
    api_key=os.environ["WORKFLOW_API_KEY"],
)

req = WorkflowExecutionRequest(
    workflow_path=str(Path("workflow.yaml").resolve()),
    messages=[WorkflowMessage(role=WorkflowRole.USER, content="your input here")],
)

result = client.run_workflow(workflow_execution_request_to_mapping(req))
print(json.dumps(result, indent=2))

Python -- Streaming

import json, os
from pathlib import Path
from dotenv import load_dotenv
from simple_agents_py import Client
from simple_agents_py.workflow_payload import workflow_execution_request_to_mapping
from simple_agents_py.workflow_request import (
    WorkflowExecutionFlags, WorkflowExecutionRequest, WorkflowMessage, WorkflowRole,
)

load_dotenv()

client = Client(
    os.environ["WORKFLOW_PROVIDER"],
    api_base=os.environ["WORKFLOW_API_BASE"],
    api_key=os.environ["WORKFLOW_API_KEY"],
)

req = WorkflowExecutionRequest(
    workflow_path=str(Path("workflow.yaml").resolve()),
    messages=[WorkflowMessage(role=WorkflowRole.USER, content="your input here")],
    execution=WorkflowExecutionFlags(
        node_llm_streaming=True,
        split_stream_deltas=False,
    ),
)

result = client.stream_workflow(
    workflow_execution_request_to_mapping(req),
    on_event=lambda event: print(event),
)
print(json.dumps(result, indent=2))

Python -- With Image

import json, os, base64
from pathlib import Path
from dotenv import load_dotenv
from simple_agents_py import Client
from simple_agents_py.workflow_payload import workflow_execution_request_to_mapping
from simple_agents_py.workflow_request import (
    WorkflowExecutionRequest, WorkflowMessage, WorkflowRole,
)

load_dotenv()

client = Client(
    os.environ["WORKFLOW_PROVIDER"],
    api_base=os.environ["WORKFLOW_API_BASE"],
    api_key=os.environ["WORKFLOW_API_KEY"],
)

b64 = base64.b64encode(Path("image.jpeg").read_bytes()).decode("ascii")

req = WorkflowExecutionRequest(
    workflow_path=str(Path("workflow.yaml").resolve()),
    messages=[
        WorkflowMessage(
            role=WorkflowRole.USER,
            content=[
                {"type": "text", "text": "Describe this image."},
                {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}},
            ],
        ),
    ],
)

result = client.run_workflow(workflow_execution_request_to_mapping(req))
print(json.dumps(result, indent=2))

Python -- With Langfuse

import json, os, base64
from pathlib import Path
from dotenv import load_dotenv
from simple_agents_py import Client
from simple_agents_py.workflow_payload import workflow_execution_request_to_mapping
from simple_agents_py.workflow_request import (
    WorkflowExecutionFlags, WorkflowExecutionRequest, WorkflowMessage, WorkflowRole,
    WorkflowRunOptions, WorkflowTelemetryConfig,
)

load_dotenv()

# Langfuse OTLP setup
public = os.environ["LANGFUSE_PUBLIC_KEY"]
secret = os.environ["LANGFUSE_SECRET_KEY"]
base = os.environ["LANGFUSE_BASE_URL"]
token = base64.b64encode(f"{public}:{secret}".encode()).decode("ascii")
os.environ["SIMPLE_AGENTS_TRACING_ENABLED"] = "true"
os.environ["OTEL_EXPORTER_OTLP_PROTOCOL"] = "http/protobuf"
os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = base.rstrip("/") + "/api/public/otel"
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"Authorization=Basic {token},x-langfuse-ingestion-version=4"

client = Client(
    os.environ["WORKFLOW_PROVIDER"],
    api_base=os.environ["WORKFLOW_API_BASE"],
    api_key=os.environ["WORKFLOW_API_KEY"],
)

req = WorkflowExecutionRequest(
    workflow_path=str(Path("workflow.yaml").resolve()),
    messages=[WorkflowMessage(role=WorkflowRole.USER, content="your input here")],
    execution=WorkflowExecutionFlags(node_llm_streaming=True, split_stream_deltas=False),
    workflow_options=WorkflowRunOptions(
        telemetry=WorkflowTelemetryConfig(enabled=True, nerdstats=True),
    ),
)

result = client.stream_workflow(
    workflow_execution_request_to_mapping(req),
    on_event=lambda event: print(event),
)
print(json.dumps(result, indent=2))

Python -- With Jaeger

import json, os
from pathlib import Path
from dotenv import load_dotenv
from simple_agents_py import Client
from simple_agents_py.workflow_payload import workflow_execution_request_to_mapping
from simple_agents_py.workflow_request import (
    WorkflowExecutionRequest, WorkflowMessage, WorkflowRole,
    WorkflowRunOptions, WorkflowTelemetryConfig,
)

load_dotenv()

os.environ["SIMPLE_AGENTS_TRACING_ENABLED"] = "true"
os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "http://localhost:4317"
os.environ["OTEL_EXPORTER_OTLP_PROTOCOL"] = "grpc"
os.environ["OTEL_SERVICE_NAME"] = "my-workflow"

client = Client(
    os.environ["WORKFLOW_PROVIDER"],
    api_base=os.environ["WORKFLOW_API_BASE"],
    api_key=os.environ["WORKFLOW_API_KEY"],
)

req = WorkflowExecutionRequest(
    workflow_path=str(Path("workflow.yaml").resolve()),
    messages=[WorkflowMessage(role=WorkflowRole.USER, content="your input here")],
    workflow_options=WorkflowRunOptions(
        telemetry=WorkflowTelemetryConfig(enabled=True, nerdstats=True),
    ),
)

result = client.run_workflow(workflow_execution_request_to_mapping(req))
print(json.dumps(result, indent=2))

TypeScript -- Normal Run

import { Client } from "simple-agents-node";
import { config as loadEnv } from "dotenv";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
// import { customWorkerDispatch } from "./handlers.js";  // uncomment if using custom workers

const __dirname = dirname(fileURLToPath(import.meta.url));
loadEnv({ path: join(__dirname, ".env") });

const client = new Client(process.env.WORKFLOW_API_KEY!, process.env.WORKFLOW_API_BASE);

const result = await client.runWorkflow(
  join(__dirname, "workflow.yaml"),
  { messages: [{ role: "user", content: "your input here" }] },
);
console.log(JSON.stringify(result, null, 2));

TypeScript -- Streaming

import { Client } from "simple-agents-node";
import { parseWorkflowEvent } from "simple-agents-node/workflow_event";
import { config as loadEnv } from "dotenv";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
loadEnv({ path: join(__dirname, ".env") });

const client = new Client(process.env.WORKFLOW_API_KEY!, process.env.WORKFLOW_API_BASE);

function onEvent(err: unknown, eventJson: string): void {
  if (err) { console.error(err); return; }
  const event = parseWorkflowEvent(eventJson) as any;
  if (event.event_type === "node_stream_delta" && event.delta) {
    process.stdout.write(event.delta);
  }
}

const result = await client.streamWorkflow(
  join(__dirname, "workflow.yaml"),
  { messages: [{ role: "user", content: "your input here" }] },
  onEvent,
  undefined,
  { nodeLlmStreaming: true, splitStreamDeltas: false },
);
console.log("\n" + JSON.stringify(result, null, 2));

TypeScript -- With Image

import { readFileSync } from "node:fs";
import { Client } from "simple-agents-node";
import type { MessageInput } from "simple-agents-node";
import { config as loadEnv } from "dotenv";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
loadEnv({ path: join(__dirname, ".env") });

const client = new Client(process.env.WORKFLOW_API_KEY!, process.env.WORKFLOW_API_BASE);
const b64 = readFileSync(join(__dirname, "image.jpeg")).toString("base64");

const messages: MessageInput[] = [
  {
    role: "user",
    content: [
      { type: "text", text: "Describe this image." },
      { type: "image", mediaType: "image/jpeg", data: b64 },
    ],
  },
];

const result = await client.runWorkflow(
  join(__dirname, "workflow.yaml"),
  { messages },
);
console.log(JSON.stringify(result, null, 2));

TypeScript -- With Langfuse

import { Client, syncOtelEnvFromProcess } from "simple-agents-node";
import { parseWorkflowEvent } from "simple-agents-node/workflow_event";
import { config as loadEnv } from "dotenv";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
loadEnv({ path: join(__dirname, ".env") });

const token = Buffer.from(
  `${process.env.LANGFUSE_PUBLIC_KEY}:${process.env.LANGFUSE_SECRET_KEY}`
).toString("base64");
const endpoint = `${process.env.LANGFUSE_BASE_URL!.replace(/\/$/, "")}/api/public/otel`;

process.env.SIMPLE_AGENTS_TRACING_ENABLED = "true";
process.env.OTEL_EXPORTER_OTLP_PROTOCOL = "http/protobuf";
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = endpoint;
process.env.OTEL_EXPORTER_OTLP_HEADERS = `Authorization=Basic ${token},x-langfuse-ingestion-version=4`;

syncOtelEnvFromProcess(
  process.env.SIMPLE_AGENTS_TRACING_ENABLED,
  process.env.OTEL_EXPORTER_OTLP_PROTOCOL,
  process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
  process.env.OTEL_EXPORTER_OTLP_HEADERS,
  process.env.OTEL_SERVICE_NAME || undefined,
);

const client = new Client(process.env.WORKFLOW_API_KEY!, process.env.WORKFLOW_API_BASE);

function onEvent(err: unknown, eventJson: string): void {
  if (err) { console.error(err); return; }
  const event = parseWorkflowEvent(eventJson) as any;
  if (event.event_type === "node_stream_delta" && event.delta) {
    process.stdout.write(event.delta);
  }
}

const result = await client.streamWorkflow(
  join(__dirname, "workflow.yaml"),
  { messages: [{ role: "user", content: "your input here" }] },
  onEvent,
  { telemetry: { enabled: true, nerdstats: true } },
  { nodeLlmStreaming: true, splitStreamDeltas: false },
);
console.log("\n" + JSON.stringify(result, null, 2));

TypeScript -- With Jaeger

import { Client, syncOtelEnvFromProcess } from "simple-agents-node";
import { config as loadEnv } from "dotenv";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
loadEnv({ path: join(__dirname, ".env") });

process.env.SIMPLE_AGENTS_TRACING_ENABLED = "true";
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = "http://localhost:4317";
process.env.OTEL_EXPORTER_OTLP_PROTOCOL = "grpc";
process.env.OTEL_SERVICE_NAME = "my-workflow";

syncOtelEnvFromProcess(
  process.env.SIMPLE_AGENTS_TRACING_ENABLED,
  process.env.OTEL_EXPORTER_OTLP_PROTOCOL,
  process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
  process.env.OTEL_EXPORTER_OTLP_HEADERS ?? "",
  process.env.OTEL_SERVICE_NAME,
);

const client = new Client(process.env.WORKFLOW_API_KEY!, process.env.WORKFLOW_API_BASE);

const result = await client.runWorkflow(
  join(__dirname, "workflow.yaml"),
  { messages: [{ role: "user", content: "your input here" }] },
  { telemetry: { enabled: true, nerdstats: true } },
);
console.log(JSON.stringify(result, null, 2));

Execution Flags Reference

Pass at runtime to override/combine with per-node YAML settings:

FlagTypeDefaultPurpose
node_llm_streamingbooltrueMaster switch for node streaming. stream = yaml.stream AND this flag
split_stream_deltasboolfalseEmit separate thinking vs output delta events
healingboolfalseGlobal healing. heal = yaml.heal OR this flag
workflow_streamingboolfalseForward token deltas to event sink

Validation Checklist

Before outputting any YAML:

  • id, version, entry_node present
  • entry_node exists in nodes
  • Every node has a unique id
  • All switch targets and edge targets exist as node IDs
  • Every llm_call has config.output_schema
  • output_schema has required and additionalProperties: false
  • Switch conditions reference real output paths ($.nodes.<id>.output.<field>)
  • Each prompt says Return JSON only
  • edges cover all intended flow transitions
  • For custom_worker, handler matches a function in the handler file
  • No ambiguous multi-question prompts in interview/chat flows

Complete Example: Email Classification Workflow

This real-world example classifies emails into categories with hierarchical routing and custom worker enrichment.

workflow.yaml

id: email-classifier
version: 1.0.0
entry_node: classify_email

nodes:
  - id: classify_email
    node_type:
      llm_call:
        model: gpt-4.1-mini
        messages_path: input.messages
        append_prompt_as_user: true
        stream: true
        heal: true
    config:
      output_schema:
        type: object
        properties:
          category:
            type: string
            enum: [hr, finance, education]
          reason:
            type: string
        required: [category, reason]
        additionalProperties: false
      prompt: |
        Classify this email into exactly one category.
        Return JSON only: {"category": "hr" | "finance" | "education", "reason": "..."}

  - id: route_category
    node_type:
      switch:
        branches:
          - condition: '$.nodes.classify_email.output.category == "finance"'
            target: detect_finance_subtype
        default: finalize

  - id: detect_finance_subtype
    node_type:
      llm_call:
        model: gpt-4.1-mini
        messages_path: input.messages
        append_prompt_as_user: true
        stream: true
        heal: true
    config:
      output_schema:
        type: object
        properties:
          subtype:
            type: string
            enum: [invoice, reimbursement, tax]
          reason:
            type: string
        required: [subtype, reason]
        additionalProperties: false
      prompt: |
        This email is classified as finance. Determine the subtype.
        Return JSON only: {"subtype": "invoice" | "reimbursement" | "tax", "reason": "..."}

  - id: route_finance
    node_type:
      switch:
        branches:
          - condition: '$.nodes.detect_finance_subtype.output.subtype == "invoice"'
            target: extract_company
        default: finalize

  - id: extract_company
    node_type:
      llm_call:
        model: gpt-4.1-mini
        messages_path: input.messages
        append_prompt_as_user: true
        heal: true
    config:
      output_schema:
        type: object
        properties:
          company_name:
            type: string
        required: [company_name]
        additionalProperties: false
      prompt: |
        Extract the seller/vendor company name from this invoice email.
        Return JSON only: {"company_name": "..."}

  - id: lookup_stakeholder
    node_type:
      custom_worker:
        handler: get_seller_name
    config:
      payload:
        company_name: "{{ nodes.extract_company.output.company_name }}"

  - id: finalize
    node_type:
      llm_call:
        model: gpt-4.1-mini
        messages_path: input.messages
        append_prompt_as_user: true
    config:
      output_schema:
        type: object
        properties:
          summary:
            type: string
        required: [summary]
        additionalProperties: false
      prompt: |
        Summarize the classification result.
        Category: {{ nodes.classify_email.output.category }}
        Return JSON only: {"summary": "..."}

edges:
  - from: classify_email
    to: route_category
  - from: detect_finance_subtype
    to: route_finance
  - from: extract_company
    to: lookup_stakeholder
  - from: lookup_stakeholder
    to: finalize

handlers.py

def get_seller_name(context, payload):
    company_name = str(payload.get("company_name", "")).strip().lower()
    stakeholder_map = {
        "google": "Sundar Pichai",
        "microsoft": "Satya Nadella",
        "apple": "Tim Cook",
        "amazon": "Andy Jassy",
    }
    return stakeholder_map.get(company_name, "unknown")

run.py

import json, os
from pathlib import Path
from dotenv import load_dotenv
from simple_agents_py import Client
from simple_agents_py.workflow_payload import workflow_execution_request_to_mapping
from simple_agents_py.workflow_request import (
    WorkflowExecutionRequest, WorkflowMessage, WorkflowRole,
)

load_dotenv()

client = Client(
    os.environ["WORKFLOW_PROVIDER"],
    api_base=os.environ["WORKFLOW_API_BASE"],
    api_key=os.environ["WORKFLOW_API_KEY"],
)

req = WorkflowExecutionRequest(
    workflow_path=str(Path("workflow.yaml").resolve()),
    messages=[
        WorkflowMessage(
            role=WorkflowRole.USER,
            content="We received an invoice from Google for $50,000 for cloud services.",
        ),
    ],
)

result = client.run_workflow(workflow_execution_request_to_mapping(req))
print(json.dumps(result, indent=2))

References

Read these files for reusable patterns and a pre-flight checklist:

  • references/patterns.md -- Detect->Route->Act, LLM best practices, custom workers, templating, multi-level routing, image input, execution flags
  • references/checklist.md -- QA checklist to validate YAML before outputting

Runnable skill examples (self-contained YAML + handler + runner):

  • examples/minimal-chat.yaml -- simplest single-node workflow
  • examples/email-classification.yaml -- hierarchical classification with custom worker enrichment
  • examples/handlers.py -- Python custom worker handler
  • examples/run.py -- Python normal run
  • examples/run_streaming.py -- Python streaming run
  • examples/run.ts -- TypeScript run with custom worker dispatch

Bundled full examples in this skill:

  • examples/python-test-simpleAgents/test.yaml -- full email classification with finance enrichment
  • examples/python-test-simpleAgents/friendly.yaml -- minimal single-node chat bot
  • examples/python-test-simpleAgents/handlers.py -- Python custom worker handler
  • examples/python-test-simpleAgents/test-py-simple-agents.py -- normal Python run
  • examples/python-test-simpleAgents/test-py-simple-agents-streaming.py -- streaming Python run
  • examples/python-test-simpleAgents/test-py-simple-agents-streaming-langfuse.py -- streaming with Langfuse
  • examples/python-test-simpleAgents/test-py-simple-agents-invoice-image.py -- image input (normal)
  • examples/python-test-simpleAgents/test-py-simple-agents-invoice-image-streaming.py -- image input (streaming)
  • examples/python-test-simpleAgents/test-py-simple-agents-invoice-image-jaegar.py -- image input with Jaeger
  • examples/python-test-simpleAgents/fastapi_workflow_stream.py -- FastAPI streaming endpoint example
  • examples/python-test-simpleAgents/README.md -- setup and run instructions for Python examples
  • examples/napi-test-simpleAgents/test.yaml -- NAPI workflow YAML
  • examples/napi-test-simpleAgents/handlers.ts -- TypeScript custom worker dispatch
  • examples/napi-test-simpleAgents/package.json -- NAPI scripts and dependencies

平台分布

Codex

34.62%
按下载量换算25

Claude

31.56%
按下载量换算22

Cursor

18.97%
按下载量换算13

Gemini CLI

10.81%
按下载量换算8

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

调用外部服务

该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills