Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
Back to tool/Back to issues
#608·unopim

[Feature]: Attribute-scoped AI prompts with declarative constraints, and structured-output schema generation

Author: ozarteCreated Aug 3, 2026Updated Aug 3, 2026

Is your feature request related to a problem?

magic_ai_prompts has no link to an attribute. The table carries title, prompt, type (product|category), purpose and tone — nothing that says which field this prompt fills. A prompt is a free-floating library entry.

To drive per-field generation on a 1589-product catalogue with 74 attributes, we ended up binding prompts to attributes by naming convention, writing the title as family:attribute_code (affiche:point_1, affiche:meta_title, …). 34 prompts today. Nothing validates that convention: rename an attribute and the link breaks silently; there is no way to ask "which attributes have a prompt?" other than parsing titles.

Two consequences that hurt more than the naming itself:

1. Constraints can only be expressed in prose, and prose is not enforced. Our bullet fields must stay under 80 characters. Written as a sentence in the prompt, that constraint is respected sometimes. Written as an enum in a JSON Schema, a constraint is respected always — that difference is very visible in practice. We had to build a post-write verifier whose only job is to re-read what was generated and flag bullets that came back at 95 characters. That check should never have needed to exist: the model should have been told in the schema, not asked politely in the prompt.

2. Structured output exists in 3.0, but only for translation. Webkul\MagicAI\Agents\TranslationAgent already implements schema(JsonSchema $schema) and returns a real structured-output schema — but a hardcoded, single-field one (translated_html). The machinery is in place; generation simply cannot reach it. So we reimplemented outside UnoPim a generator that walks a family, reads each attribute's type, and reproduces UnoPim's own type→JSON-Schema mapping (boolean → string enum ["true","false"], multiselect → array of enum, datetime → string with a YYYY-MM-DD HH:MM:SS pattern and no T). That mapping is UnoPim's business, not ours — we are guessing at rules the product already knows.

Describe the solution you'd like

A. An attribute-scoped prompt kind.

Extend magic_ai_prompts.type beyond product|category with an attribute kind, and add a real foreign key to attributes (plus, ideally, an optional attribute-family scope, so the same attribute can be prompted differently in two families). The naming convention then disappears, and the admin can show the prompt next to the attribute it fills.

B. Declarative constraints on the attribute itself.

attributes already carries validation, regex_pattern, is_required, is_unique, max_file_size, allowed_extensions — but nothing bounding a text length or a collection size. We would like:

property applies to feeds
min_length / max_length text, textarea schema + form validation
min_items / max_items multiselect, checkbox schema + form validation
min_value / max_value integer, decimal, price schema + form validation

The important part is that these live on the attribute, not in the prompt text. They are already validation concerns; a storefront and an AI generation should not disagree about how long a field may be, and nobody should have to restate the same bound in two places and keep them in sync by hand.

C. Schema generation for a family, reusing the existing Agent pattern.

Given a family (and a set of attributes that have an attribute-prompt), UnoPim already has everything needed to emit the JSON Schema:

  • the type mapping it already applies elsewhere;
  • the option list of every select/multiselect, which becomes an enum — this is the single highest-value piece. Our family has 188 options across its select attributes. Restating them in prose is unmaintainable and goes stale the day someone adds an option; read from the attribute, they cannot;
  • the bounds from (B), as maxLength / minItems / maximum;
  • is_required → required;
  • value_per_locale / value_per_channel, so a translation run can emit a schema limited to localisable attributes while a generation run emits the full one.

Concretely: generalise TranslationAgent into an agent whose schema() is built from the family rather than hardcoded.

D. Let the schema be read from outside.

Please expose the generated schema — via the REST API, and/or an MCP tool, and/or a download in the admin — not only as an internal input to UnoPim's own LLM call.

Not every team can route generation through MagicAI. We can't: we have no LLM API budget at all, and work through an assistant subscription instead. The schema is useful to us even when the inference happens elsewhere; it is the part that encodes UnoPim's knowledge of the catalogue. Keeping it internal-only means anyone in that position has to reimplement the mapping, which is exactly what we did and exactly what goes stale.

Describe alternatives you've considered

Naming convention on title — what we do today. Works, but the link is unverifiable and breaks on rename.

Constraints in the prompt prose — what we do today for lengths. Measurably unreliable: enums declared in a schema are respected, numeric bounds asked for in prose are not. It also duplicates a rule that belongs to the attribute.

Regenerating our schema from attributes on every run — what we do today, and it works precisely because it derives everything from the family rather than from a frozen list. But it means we maintain a second implementation of UnoPim's own type mapping, and we discover changes to it by having output break.

A separate attribute_prompts table instead of extending magic_ai_prompts — reasonable too; we have no preference, as long as the link is a real one.

Additional Context

Running UnoPim 3.0.0 (Laravel 13.23, PHP 8.4) on a 1589-product catalogue, 74 attributes, 188 select options, 34 prompts.

One observation from building this by hand, in case it is useful: what made the generated schema trustworthy was deriving the field list from the family rather than declaring it. Our first version carried a hardcoded list of attributes; it was already stale four hours later, missing four attributes added that same afternoon, and nothing signalled it. Deriving removed the problem instead of managing it. If this feature ships, the same principle would be worth keeping: an attribute added today should appear in tomorrow's schema without anyone editing anything.

Happy to test against a real catalogue, and to share the standalone generator we wrote if it would save time.

Source: unopim/unopim

View original on GitHubView discussion on GitHub