一键导入
structured-data
Build typed AI agents with InstructorLite and Ecto embedded schemas. Use when LLM output must be validated and returned as structured Elixir data.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Build typed AI agents with InstructorLite and Ecto embedded schemas. Use when LLM output must be validated and returned as structured Elixir data.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when the app needs branded 404 and 500 error pages and a way to raise not-found from LiveViews. Covers install, the Fallback exception, and tweaking the templates.
Use when the app needs an admin-only dashboard at `/admin` for managing users, teams, and admins, with charts, impersonation, and the LiveDashboard. Covers install, configuration, and tweaks.
Use when authenticated routes must be gated behind email confirmation, redirecting users whose `confirmed_at` is nil to a confirmation page. Covers install, configuration, and tweaks for the confirmation gate.
Use when the app needs user accounts, sign-in, and roles. Covers installing phx.gen.auth-based auth with role-based access, profiles, and admin bootstrapping, plus how to tweak it.
Use when the app needs a Cmd+K/Ctrl+K command palette for quick navigation and search. Covers installing the LiveView palette, its protocol-based result formatting, and how to make resources searchable.
Use when the app needs database-backed error tracking without an external SaaS. Covers installing ErrorTracker with automatic Phoenix/LiveView/Oban reporting, migrations, pruning, and a dashboard.
| name | structured-data |
| description | Build typed AI agents with InstructorLite and Ecto embedded schemas. Use when LLM output must be validated and returned as structured Elixir data. |
| user-invocable | true |
Use this skill when an AI feature should return validated Elixir data instead of freeform text.
MyApp.Agents.*.InstructorLite.def deps do
[
{:instructor_lite, "~> 1.2.0"}
]
end
Create one shared module for API keys and model names:
defmodule MyApp.Agents do
@moduledoc """
Central configuration for AI agents.
Import this module in agent implementations to access API keys and models.
"""
def anthropic_key, do: Application.get_env(:my_app, :anthropic)[:api_key]
def openai_key, do: Application.get_env(:my_app, :openai)[:api_key]
def gemini_key, do: Application.get_env(:my_app, :gemini)[:api_key]
def model(:haiku), do: "claude-haiku-4-5"
def model(:sonnet), do: "claude-sonnet-4-5"
def model(:opus), do: "claude-opus-4-5"
def model(:gpt_5_nano), do: "gpt-5-nano"
def model(:gpt_5_mini), do: "gpt-5-mini"
def model(:gpt_5), do: "gpt-5.2"
def model(:gpt_4_1_mini), do: "gpt-4.1-mini"
def model(:gemini_flash), do: "gemini-3-flash-preview"
def model(:gemini_pro), do: "gemini-3-pro-preview"
end
Do not put provider credentials in shared config/config.exs.
Keep local credentials in config/dev.exs:
config :my_app, :anthropic, api_key: System.get_env("ANTHROPIC_API_KEY")
config :my_app, :openai, api_key: System.get_env("OPENAI_API_KEY")
config :my_app, :gemini, api_key: System.get_env("GEMINI_API_KEY")
lib/my_app/agents/.MyApp.Agents.TopicSomeAction style names.MyApp.Agents.PostsWritePost.Example:
lib/my_app/agents/posts_write_post.ex
defmodule MyApp.Agents.PostsWritePost do
end
Use one task-specific module with a nested Response schema:
defmodule MyApp.Agents.PostsWritePost do
@moduledoc """
Generates a structured draft for a post.
"""
import MyApp.Agents
defmodule Response do
use Ecto.Schema
use InstructorLite.Instruction
@notes """
## Field Descriptions:
- title: Final title for the post
- summary: Short summary of the post
- tags: List of topic tags
- sections: Ordered sections in the post
- heading: Section heading
- body: Section content
"""
@primary_key false
embedded_schema do
field :title, :string
field :summary, :string
field :tags, {:array, :string}
embeds_many :sections, Section, primary_key: false do
field :heading, :string
field :body, :string
end
end
@impl InstructorLite.Instruction
def validate_changeset(changeset, _opts) do
changeset
|> Ecto.Changeset.validate_required([:title, :summary])
|> Ecto.Changeset.validate_length(:title, min: 5, max: 120)
end
end
@system_prompt """
You write structured post drafts.
Return complete, practical content that fits the requested topic and audience.
"""
def call(topic) do
InstructorLite.instruct(
%{
model: model(:sonnet),
messages: [
%{role: "system", content: @system_prompt},
%{role: "user", content: build_user_prompt(topic)}
]
},
response_model: Response,
adapter: InstructorLite.Adapters.Anthropic,
adapter_context: [api_key: anthropic_key(), http_options: [receive_timeout: 90_000]]
)
end
defp build_user_prompt(topic) do
"Write a post draft about: #{topic}"
end
end
InstructorLite.instruct(
%{
model: model(:sonnet),
messages: [
%{role: "system", content: "System prompt"},
%{role: "user", content: "User prompt"}
]
},
response_model: Response,
adapter: InstructorLite.Adapters.Anthropic,
adapter_context: [api_key: anthropic_key(), http_options: [receive_timeout: 90_000]]
)
InstructorLite.instruct(
%{
model: model(:gpt_5),
input: [
%{role: "system", content: "System prompt"},
%{role: "user", content: "User prompt"}
]
},
response_model: Response,
adapter_context: [api_key: openai_key(), http_options: [receive_timeout: 90_000]]
)
embedded_schema for the response model.@notes with field meanings, allowed values, and nested structure.embeds_many when each list item has multiple fields.{:array, :string} for simple string lists.validate_changeset/2 for business rules the model must satisfy.Test structured AI integrations with Mimic so tests never hit real providers. Mock the boundary you call directly:
# test/test_helper.exs
Mimic.copy(InstructorLite)
ExUnit.start()
defmodule MyApp.Agents.PostsWritePostTest do
use MyApp.DataCase, async: true
use Mimic
test "returns a validated response" do
InstructorLite
|> expect(:instruct, fn _request, opts ->
{:ok, struct(opts[:response_model], %{title: "Hello", summary: "World", tags: [], sections: []})}
end)
assert {:ok, response} = MyApp.Agents.PostsWritePost.call("Phoenix")
assert response.title == "Hello"
end
end
MyApp.Agents as the single source of truth for model strings and API keys.MyApp.Agents.PostsWritePost over a generic MyApp.Agents.Posts.