Расширенная структура JSON
Instructions, Choice options, Score levels, and Noul criteria all accept JSON structure.
Инструкции, варианты Choice, уровни Score и критерии Noul поддерживают JSON-структуру.
export function TypesafeExample({example, display, title}) {
const keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$";
function compressToEncodedURIComponent(input) {
if (input == null) return "";
return _compress(input, 6, function (a) {
return keyStrUriSafe.charAt(a);
});
}
function _compress(uncompressed, bitsPerChar, getCharFromInt) {
var i, value, context_dictionary = {}, context_dictionaryToCreate = {}, context_c = "", context_wc = "", context_w = "", context_enlargeIn = 2, context_dictSize = 3, context_numBits = 2, context_data = [], context_data_val = 0, context_data_position = 0, ii;
for (ii = 0; ii < uncompressed.length; ii += 1) {
context_c = uncompressed.charAt(ii);
if (!Object.prototype.hasOwnProperty.call(context_dictionary, context_c)) {
context_dictionary[context_c] = context_dictSize++;
context_dictionaryToCreate[context_c] = true;
}
context_wc = context_w + context_c;
if (Object.prototype.hasOwnProperty.call(context_dictionary, context_wc)) {
context_w = context_wc;
} else {
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate, context_w)) {
if (context_w.charCodeAt(0) < 256) {
for (i = 0; i < context_numBits; i++) {
context_data_val = context_data_val << 1;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
}
value = context_w.charCodeAt(0);
for (i = 0; i < 8; i++) {
context_data_val = context_data_val << 1 | value & 1;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
} else {
value = 1;
for (i = 0; i < context_numBits; i++) {
context_data_val = context_data_val << 1 | value;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = 0;
}
value = context_w.charCodeAt(0);
for (i = 0; i < 16; i++) {
context_data_val = context_data_val << 1 | value & 1;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
delete context_dictionaryToCreate[context_w];
} else {
value = context_dictionary[context_w];
for (i = 0; i < context_numBits; i++) {
context_data_val = context_data_val << 1 | value & 1;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
context_dictionary[context_wc] = context_dictSize++;
context_w = String(context_c);
}
}
if (context_w !== "") {
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate, context_w)) {
if (context_w.charCodeAt(0) < 256) {
for (i = 0; i < context_numBits; i++) {
context_data_val = context_data_val << 1;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
}
value = context_w.charCodeAt(0);
for (i = 0; i < 8; i++) {
context_data_val = context_data_val << 1 | value & 1;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
} else {
value = 1;
for (i = 0; i < context_numBits; i++) {
context_data_val = context_data_val << 1 | value;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = 0;
}
value = context_w.charCodeAt(0);
for (i = 0; i < 16; i++) {
context_data_val = context_data_val << 1 | value & 1;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
delete context_dictionaryToCreate[context_w];
} else {
value = context_dictionary[context_w];
for (i = 0; i < context_numBits; i++) {
context_data_val = context_data_val << 1 | value & 1;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
}
value = 2;
for (i = 0; i < context_numBits; i++) {
context_data_val = context_data_val << 1 | value & 1;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
while (true) {
context_data_val = context_data_val << 1;
if (context_data_position == bitsPerChar - 1) {
context_data.push(getCharFromInt(context_data_val));
break;
} else context_data_position++;
}
return context_data.join("");
}
function buildHref(ex) {
const documentText = ex.state === undefined ? "" : typeof ex.state === "string" ? ex.state : JSON.stringify(ex.state, null, 2);
return "https://console.typesafe.ai/decode#share/" + compressToEncodedURIComponent(JSON.stringify({
apiVersion: "v1",
documentText,
promptsText: JSON.stringify(ex.questions, null, 2),
selectedModels: ex.selectedModels
}));
}
const displayedExample = display === "questions" ? example.questions : example.state === undefined ? {
questions: example.questions
} : {
state: example.state,
questions: example.questions
};
const code = JSON.stringify(displayedExample, null, 2);
const href = buildHref(example);
return <div style={{
margin: "1.25rem 0"
}}>
<CodeBlock language="json" filename={title ?? "request"}>
{code}
}
Модели System One обучены понимать структуру.
Где разрешена структура
Каждое из этих полей имеет тип EntryType.
| Поле | Применяется к | Допустимая форма |
|---|---|---|
instructions |
Choice, Score, Noul | string, object, array или null |
значения criteria (описания вариантов) |
Choice | string, object, array или null |
элементы criteria (описания уровней) |
Score | string, object, array или null |
criteria.true и criteria.false |
Noul | string, object, array или null |
Когда структурировать вопрос
- Когда это помогает достичь ясности. Когда вопрос состоит из нескольких частей, оформление их в виде JSON повышает ясность, поскольку каждый элемент снабжен понятным ключом.
- Когда вопросу требуются вспомогательные данные. Схема, таксономия или строка базы данных уже представлены в формате JSON. Используйте этот JSON целиком или передавайте соответствующие подполя вместо сериализации их в строковый шаблон.
Структурированные инструкции
Один объект field описывает проверяемое поле, и каждый вопрос ссылается на него по ключу. Одна и та же структура обеспечивает работу вопроса Noul (проверяющего значение), Choice (выбирающего значение из кандидатов) и двух вопросов Score (размещающих значение на шкале).
<TypesafeExample
display="request"
example={{
state: {
source_text:
'Invoice #4471 issued March 3, 2026 to Beaver Dam Logistics for $12,840.00, net 30.',
},
selectedModels: ['jev-latest'],
questions: {
invoice_number_is_correct: {
type: 'noul',
instructions: {
field: {
name: 'invoice_number',
type: 'string',
description: 'The identifier printed on the invoice.',
},
extracted_value: '4471',
question: 'Does extracted_value match the field as it appears in source_text?',
},
},
customer_name: {
type: 'choice',
instructions: {
field: {
name: 'customer_name',
type: 'string',
description: 'The organization the invoice was issued to.',
},
question: 'Which option is the value of field in source_text?',
},
criteria: {
'Beaver Logistics': null,
'Dam Logistics': null,
'Beaver Dam Logistics': null,
'Beaver': null,
'Dam': null,
},
},
amount_due: {
type: 'score',
instructions: {
field: {
name: 'amount_due',
type: 'number',
unit: 'USD',
description: 'The total the invoice asks to be paid.',
},
question: 'How large is the field value in source_text?',
},
criteria: [
'Under $1,000',
'$1,000 to $10,000',
'$10,000 to $100,000',
'$100,000 to $1,000,000',
'Over $1,000,000',
],
},
payment_terms: {
type: 'score',
instructions: {
field: {
name: 'payment_terms',
type: 'integer',
unit: 'days',
description: 'Days allowed for payment, from terms such as "net 30".',
},
question: 'How many days does the field in source_text allow for payment?',
},
criteria: [
'Due on receipt',
'Net 10',
'Net 30',
'Net 60',
'Net 90',
],
},
},
}}
/>
В коде вы можете обойти в цикле потенциальные записи и сформировать по одному такому вопросу на каждое поле, отправив их все в едином вызове. В кукбуке по каскадному извлечению данных (SDE cascade) применяется аналогичный подход.
Массивы также поддерживаются. Используйте массив, когда инструкция представляет собой список объектов для проверки или сравнения:
"instructions": {
"question": "Does the claimed sender identity conflict with the sending domain?",
"compare": ["ticket.sender.display_name", "ticket.sender.email"],
"focus": "Compare the named organization with the email domain."
}
Структурированные варианты Choice
Описание варианта Choice также может быть структурированным объектом.
Рубрика JSON для уточнения границ
<TypesafeExample
display="request"
example={{
state:
'I ordered the standing desk two weeks ago and tracking still says label created. Was I even charged?',
selectedModels: ['jev-latest'],
questions: {
department: {
type: 'choice',
instructions: {
question: 'Which team should handle this message?',
focus: "Classify the customer's primary request, not every topic mentioned.",
},
criteria: {
billing: {
what: 'Charges, invoices, refunds, or subscriptions',
not_for: 'Order tracking or account access',
examples: ['I was charged twice', 'Where is my refund?'],
},
orders: {
what: 'Order status, delivery, cancellation, or returns',
not_for: 'Charges or account access',
examples: ['Where is my package?', 'Cancel my order'],
},
account: {
what: 'Login, password, profile, or security',
not_for: 'Charges or delivery',
examples: ["I can't log in", 'Change my email'],
},
},
},
},
}}
/>
Пример сообщает модели, что охватывает каждый вариант, и что к нему не относится. Это делает границу между вариантами предельно четкой.
Обход таксономии
Для классификации по глубокой таксономии задавайте по одному вопросу Choice на каждый уровень и обходите дерево в коде. На каждом шаге вариантами являются дочерние узлы текущего узла, а значениями каждого варианта — поддерево этого потомка. Это позволяет модели видеть содержимое ветки до принятия окончательного решения, что особенно важно, когда товар относится к листовой категории, чье назначение неочевидно из одного лишь названия ветки.
Здесь состояние — это карточка товара, а первый вопрос выбирает отдел верхнего уровня.
<TypesafeExample
display="request"
example={{
state:
"32oz plastic bottle with a flip straw lid. Fits most bike cages.",
selectedModels: ['jev-latest'],
questions: {
department: {
type: 'choice',
instructions: 'Which top-level department does this product belong to?',
criteria: {
'Sporting Goods': {
Cycling: ['Bike Bottles & Cages', 'Bike Lights', 'Helmets'],
Fitness: ['Yoga Mats', 'Resistance Bands'],
Outdoor: ['Tents', 'Sleeping Bags', 'Hydration Packs'],
},
'Home & Kitchen': {
Drinkware: ['Water Bottles', 'Travel Mugs', 'Tumblers'],
Cookware: ['Pots & Pans', 'Bakeware'],
},
'Baby & Toddler': ['Sippy Cups', 'Bottle Warmers', 'Bibs'],
},
},
},
}}
/>
Бутылка правдоподобно подходит под два отдела. Демонстрация поддеревьев позволяет модели увидеть, что существуют и Sporting Goods > Cycling > Bike Bottles & Cages, и Home & Kitchen > Drinkware > Water Bottles, и сопоставить акцент в описании на велосипедных креплениях с обычной посудой для питья. Значения probabilities в ответе подскажут, насколько близки вероятности и стоит ли исследовать обе ветки.
После выбора отдела задайте следующий вопрос Choice с дочерними элементами этого отдела в качестве вариантов и их поддеревьями в качестве значений, повторяя процесс до достижения листа. В коде это может быть цикл по вложенному словарю, где criteria каждого вопроса — это просто текущий узел. В кукбуке по иерархической классификации показан пример аналогичного обхода дерева, включая лучевой поиск (beam search), сохраняющий несколько перспективных путей-кандидатов при близких вероятностях.
Структурированные уровни Score
Каждый элемент массива criteria для Score может быть объектом.
<TypesafeExample
display="request"
example={{
state:
'Fixed the null check in the payment handler. Also refactored the retry loop while I was in there, and bumped the SDK version since the old one had that timeout bug.',
selectedModels: ['jev-latest'],
questions: {
pr_scope: {
type: 'score',
instructions: {
question: 'How focused is this pull request description on a single change?',
note: 'Judge the number of independent changes, not the size of any one change.',
},
criteria: [
{
summary: 'One change, clearly stated',
signals: ['A single fix or feature', 'Nothing described as "also" or "while I was in there"'],
},
{
summary: 'One main change plus a small related tweak',
signals: ['A primary change and one minor adjacent edit', 'The tweak supports the main change'],
},
{
summary: 'Several independent changes bundled together',
signals: ['Two or more unrelated fixes or features', 'Changes that could each be their own PR'],
},
],
},
},
}}
/>
Структурированные критерии Noul
Поле criteria для Noul опционально. Когда граница между «да» и «нет» тонка, структурированные описания true и false позволяют четко зафиксировать ее с помощью определений и примеров для каждого исхода.
<TypesafeExample
display="request"
example={{
state: {
sender: { display_name: 'Beaver Dam Builders Ltd.', email: 'donotreply@payroll.example' },
message:
'Your Q3 bonus is ready. Reply with your login password so we can verify your identity and release the funds.',
},
selectedModels: ['jev-latest'],
questions: {
requests_credentials: {
type: 'noul',
instructions: {
question: 'Does the message ask the recipient to disclose a sensitive credential?',
inspect: 'message',
focus: 'Look for a request to send the credential itself, not a request to change or reset it.',
},
criteria: {
true: {
what: 'Asks the recipient to reply with, type, or send a password, PIN, one-time code, or other security sensitive answer',
examples: ['Reply with your password', 'Send us the 6-digit code you just received'],
},
false: {
what: 'No sensitive credential is requested',
examples: ['Reset your password from the settings page', 'Your statement is ready'],
},
},
},
},
}}
/>