{"slug": "aichain-skill-a-prompt-as-a-reusable-object", "title": "AIchain Skill: A Prompt as a Reusable Object", "summary": "The AIchain library introduces \"Skill,\" a Python object that treats prompts as reusable, parameterizable components rather than hard-coded strings. By separating prompt templates from runtime data and model execution, Skill allows developers to version-control, test, and swap underlying AI models without altering business logic. The approach mirrors parameterized SQL queries, enabling prompt engineers to review and modify prompts independently of application code.", "body_md": "A prompt buried in an f-string is technical debt. You can't test it. You can't save it to a file. You can't hand it to a colleague and say \"here's the extraction logic.\" The moment you hard-code a prompt into a string, you've welded your application logic to a single, frozen instruction — one that will rot as models improve and requirements shift.\n\n`Skill`\n\nfixes that. It turns a prompt into a first-class object: something you can construct, parameterize, persist, reload, version-control, and swap models underneath — all without changing a single line of business logic.\n\nConsider the typical approach:\n\n```\nresponse = client.chat(f\"What is {topic} in one sentence?\")\n```\n\nThis line mixes three concerns: the prompt template, the runtime data, and the model that executes it. Change any one of those, and you're editing the same line. Want to test the prompt with different inputs? You rewrite the call. Want to try a cheaper model? You rewire the client. Want a non-developer on your team to review the prompt? You point them at application code.\n\nSQL solved this exact problem decades ago. Write a parameterized query once, bind values at execution time, and the database engine is a separate concern entirely. Skill applies the same separation to prompts.\n\n[Install the library](https://github.com/yaitio/aichain) and its serialization dependency:\n\n```\npip install yait-aichain pyyaml\n```\n\nHere's the simplest Skill — a one-sentence explainer with a `{topic}`\n\nvariable:\n\n``` python\nimport os\nfrom yait_aichain.models import Model\nfrom yait_aichain.skills import Skill\n\nskill = Skill(\n    model=Model(\"claude-sonnet-4-6\", api_key=os.getenv(\"ANTHROPIC_API_KEY\")),\n    input={\n        \"messages\": [{\n            \"role\": \"user\",\n            \"parts\": [\"What is {topic} in one sentence?\"],\n        }]\n    },\n)\n\nresult = skill.run(variables={\"topic\": \"machine learning\"})\nprint(result)\n```\n\nTwo things to notice. First, `{topic}`\n\nis a placeholder inside the prompt template — not an f-string resolved at definition time. The value gets substituted only when you call `.run(variables={...})`\n\n. Second, the model is declared at construction, not at call time. The Skill *knows* which model it targets.\n\nThat separation matters. The same Skill instance can run with `{\"topic\": \"quantum computing\"}`\n\n, then `{\"topic\": \"gradient descent\"}`\n\n, without rebuilding anything. The template stays fixed; the data varies. Exactly like a prepared statement.\n\nThe key differentiator of a Skill: it's built around the model most effective for a specific task — balancing quality, speed, and cost. What one model handled well six months ago, another can do dramatically better today. Skill lets you act on that without rewriting your logic.\n\nHere's what that looks like in code:\n\n``` python\nimport os\nfrom yait_aichain.models import Model\nfrom yait_aichain.skills import Skill\n\nPROMPT = {\n    \"messages\": [{\n        \"role\": \"user\",\n        \"parts\": [\"What is {topic} in one sentence?\"],\n    }]\n}\n\nmodels = [\n    Model(\"claude-sonnet-4-6\", api_key=os.getenv(\"ANTHROPIC_API_KEY\")),\n    Model(\"gpt-4o-mini\",       api_key=os.getenv(\"OPENAI_API_KEY\")),\n    Model(\"gemini-2.5-flash\",  api_key=os.getenv(\"GOOGLE_AI_API_KEY\")),\n]\n\nfor model in models:\n    skill  = Skill(model=model, input=PROMPT)\n    result = skill.run(variables={\"topic\": \"machine learning\"})\n    print(f\"[{model.name}]\\n{result}\\n\")\n```\n\nThe prompt template is defined once. The loop swaps only the `Model`\n\n. You get three answers from three providers — Anthropic, OpenAI, Google — with zero changes to the prompt logic. (`model.name`\n\nis a string attribute on `Model`\n\nthat returns the identifier you passed at construction, used here purely for labeling output.) When a newer model returns a better answer at a fraction of the cost, you change one string and move on.\n\nThe moment a prompt lives in a Python file, it's coupled to that file's deployment cycle. Edit the prompt, redeploy the app. That's fine until you want a prompt engineer — or your future self at 11pm — to iterate on prompts independently.\n\n`Skill`\n\nsupports serialization to YAML. Same idea as versioning a migration script: track every change in Git, roll back when new wording degrades output quality.\n\n``` python\nimport os\nfrom yait_aichain.models import Model\nfrom yait_aichain.skills import Skill\n\nYAML_PATH = \"validator_skill.yaml\"\n\n# Build and save\nskill = Skill(\n    model=Model(\"claude-sonnet-4-6\", api_key=os.getenv(\"ANTHROPIC_API_KEY\")),\n    input={\n        \"messages\": [{\n            \"role\": \"user\",\n            \"parts\": [\"Review the following text and return only corrected version: {text}\"],\n        }]\n    },\n)\nskill.save(YAML_PATH)\n\n# Later — reload and run\nloaded_skill = Skill.load(YAML_PATH, api_key=os.getenv(\"ANTHROPIC_API_KEY\"))\nresult = loaded_skill.run(variables={\"text\": \"The quick brown fox jump over the lazy dog.\"})\nprint(result)\n```\n\nA few details worth highlighting:\n\n`skill.save()`\n\nserializes the model name, prompt template, and configuration — but intentionally strips the API key. You pass it from the environment when you call `Skill.load()`\n\n. No secrets in your repo.`validator_skill.yaml`\n\n, tweak the prompt wording, and commit it — without touching Python.`prompts/`\n\ndirectory, tag it `v1.2`\n\n, run automated tests against it in CI. If new wording degrades output quality, `git revert`\n\nand you're done.When a prompt becomes an object, workflows that strings simply can't support become straightforward:\n\n`prompts/`\n\ndirectory. Git tracks every change, who made it, and when — just as it tracks schema migrations.\n\n```\n# Construct\nSkill(model: Model, input: dict)\n\n# Run with variable substitution\nskill.run(variables: dict = {}) -> str\n\n# Persist\nskill.save(path: str) -> None\n\n# Restore (api_key is a keyword argument)\nSkill.load(path: str, *, api_key: str) -> Skill\n```\n\nFour operations. That's the entire surface area. A Skill does exactly one thing — execute a parameterized prompt against a specific model — and exposes exactly the controls you need to manage it over time.\n\nThe shift from f-string to Skill is small in code and large in practice. You go from a prompt that's invisible, untestable, and welded to one model — to one that's named, versioned, portable, and model-aware.\n\nNext in this series: `Chain`\n\n— what happens when one Skill's output feeds into another. But it all starts here, with one prompt treated as a real artifact, not a throwaway string.", "url": "https://wpnews.pro/news/aichain-skill-a-prompt-as-a-reusable-object", "canonical_source": "https://dev.to/yait/aichain-skill-a-prompt-as-a-reusable-object-1n4p", "published_at": "2026-06-04 08:29:00+00:00", "updated_at": "2026-06-04 08:42:00.698276+00:00", "lang": "en", "topics": ["large-language-models", "ai-tools", "ai-products", "ai-research", "ai-infrastructure"], "entities": ["AIchain", "Skill", "yaitio", "aichain", "SQL", "pyyaml"], "alternates": {"html": "https://wpnews.pro/news/aichain-skill-a-prompt-as-a-reusable-object", "markdown": "https://wpnews.pro/news/aichain-skill-a-prompt-as-a-reusable-object.md", "text": "https://wpnews.pro/news/aichain-skill-a-prompt-as-a-reusable-object.txt", "jsonld": "https://wpnews.pro/news/aichain-skill-a-prompt-as-a-reusable-object.jsonld"}}