feat: use message tool for anthropic

This commit is contained in:
2024-11-24 11:11:26 +01:00
parent 13b5beeb7e
commit d5fd74b0c5
9 changed files with 479 additions and 105 deletions

4
.gitignore vendored
View File

@@ -131,4 +131,6 @@ dist
.editorconfig
personas/
personas/
.DS_Store

View File

@@ -1,11 +1,17 @@
import { makeRequest } from './utils/anthropicClient';
import { savePersonaJson } from './utils/savePersonaJson';
import { savePersonaDb } from './utils/savePersonaDb';
import { generatePromptWithMBTI } from './utils/personalityTrait';
const personaPromise = makeRequest();
const prompt =
'Generate a detailed, realistic persona with specific real-world values including store names, brands and locations. Use frequencies per week, ISO timestamps relative to the current week. Personality trait: <MBTI_AND_TRAITS_HERE>';
const fullPrompt = generatePromptWithMBTI(prompt);
const personaPromise = makeRequest(fullPrompt);
personaPromise.then(persona => {
savePersonaDb(persona.text).then(id => savePersonaJson(persona.text, id));
savePersonaDb(persona).then(id => savePersonaJson(persona, id));
console.log('New persona:', persona.text);
console.log('New persona:', persona);
});

View File

@@ -1,82 +1,9 @@
import 'dotenv/config';
import Anthropic from '@anthropic-ai/sdk';
import { Persona } from './types';
import { PersonaTool } from './personaSchema';
/*
THIS IS THE PROMPT IN HUMAN-READABLE FORMAT:
Generate a JSON object for a persona with the following schema:
{
"core": {
"age": <number>,
"name": <string>,
"occupation": {
"title": <string>,
"level": <string>,
"income": <number>,
"location": <string>,
"schedule": <array of strings>
},
"home": {
"type": <string>,
"ownership": <string>,
"location": <string>,
"commute_distance_km": <number>
},
"household": {
"status": <string>,
"members": <array of strings>,
"pets": <array of objects>
}
},
"routines": {
"weekday": <object with 24 hour timeline>,
"weekend": <array of regular activities>,
"commute": {
"method": <string>,
"route": <array of locations>,
"regular_stops": <array of objects>
}
},
"preferences": {
"diet": <array of strings>,
"brands": <array of objects with loyalty scores>,
"price_sensitivity": <number 1-10>,
"payment_methods": <array of strings>,
"shopping": {
"grocery_stores": <array of objects with frequency>,
"coffee_shops": <array of objects with frequency>,
"restaurants": <array of objects with frequency>,
"retail": <array of objects with frequency>
}
},
"finances": {
"subscriptions": <array of objects with amounts and dates>,
"regular_bills": <array of objects with amounts and dates>,
"spending_patterns": {
"impulsive_score": <number 1-10>,
"categories": <object with category preferences>
}
},
"habits": {
"exercise": <array of objects with schedule>,
"social": <array of objects with frequency>,
"entertainment": <array of objects with frequency>,
"vices": <array of objects with frequency>
},
"context": {
"stress_triggers": <array of strings>,
"reward_behaviors": <array of strings>,
"upcoming_events": <array of objects with dates>,
"recent_changes": <array of strings>
}
}
Generate realistic values for all fields, including specific store names, brands, and locations. All temporal data should be relative to the current week. Frequencies should be specified as times per week. Include coordinates for locations. Use ISO timestamps for dates.
*/
const personaCreationPrompt =
'Generate a detailed JSON object for a realistic persona following this schema, populating all fields with specific real-world values including store names, brands, coordinates, and locations. Use frequencies per week, ISO timestamps relative to the current week, and include coordinates for all locations. The output should be a complete JSON object with core demographic details (age, job, housing, household), daily routines, shopping preferences with actual store names and visit frequencies, brand loyalties with scores, diet, payment preferences, exercise habits, social patterns, entertainment choices, upcoming events, and stress factors. The data should reflect the current week for all temporal values. Schema: {"core":{age:<number>,"name": <string>,occupation:{title:<string>,level:<string>,income:<number>,location:<string>,schedule:<array of strings>},home:{type:<string>,ownership:<string>,location:<string>,commute_distance_km:<number>},household:{status:<string>,members:<array of strings>,pets:<array of objects>}},"routines":{weekday:<object with 24 hour timeline>,weekend:<array of regular activities>,commute:{method:<string>,route:<array of locations>,regular_stops:<array of objects>}},"preferences":{diet:<array of strings>,brands:<array of objects with loyalty scores>,price_sensitivity:<number 1-10>,payment_methods:<array of strings>,shopping:{grocery_stores:<array of objects with frequency>,coffee_shops:<array of objects with frequency>,restaurants:<array of objects with frequency>,retail:<array of objects with frequency>}},"finances":{subscriptions:<array of objects with amounts and dates>,regular_bills:<array of objects with amounts and dates>,spending_patterns:{impulsive_score:<number 1-10>,categories:<object with category preferences>}},"habits":{exercise:<array of objects with schedule>,social:<array of objects with frequency>,entertainment:<array of objects with frequency>,vices:<array of objects with frequency>},"context":{stress_triggers:<array of strings>,reward_behaviors:<array of strings>,upcoming_events:<array of objects with dates>,recent_changes:<array of strings>}}';
export async function makeRequest() {
export async function makeRequest(prompt: string) {
if (!process.env.ANTHROPIC_API_KEY) {
throw Error('Anthropic API key missing.');
}
@@ -88,18 +15,23 @@ export async function makeRequest() {
const response = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 2000,
messages: [{ role: 'user', content: personaCreationPrompt }]
temperature: 1,
tools: [PersonaTool],
messages: [{ role: 'user', content: prompt }]
});
if (
response.stop_reason &&
response.stop_reason !== 'end_turn' &&
response.stop_reason !== 'stop_sequence'
) {
throw Error(response.stop_reason);
if (response.stop_reason && response.stop_reason !== 'tool_use') {
throw Error(JSON.stringify(response));
}
console.log('Message ID:', response.id);
if (response.content.length != 2) {
throw Error(JSON.stringify(response));
}
return response.content[0] as { text: string };
const content = response.content as [
{ type: string; text: string },
{ type: string; input: object }
];
return content[1].input as Persona;
}

View File

@@ -1,5 +0,0 @@
import { Persona } from './types';
export function parsePersona(persona: string) {
return JSON.parse(persona) as Persona;
}

376
src/utils/personaSchema.ts Normal file
View File

@@ -0,0 +1,376 @@
export const PersonaTool = {
name: 'PersonaSchema' as const,
input_schema: {
type: 'object' as const,
description: 'User persona',
properties: {
core: {
type: 'object' as const,
description: 'Core user information and demographics',
properties: {
age: {
type: 'number' as const,
description: "User's age in years"
},
name: {
type: 'string' as const,
description: "User's full name"
},
occupation: {
type: 'object' as const,
description: 'Employment details',
properties: {
title: {
type: 'string' as const,
description: 'Job title'
},
level: {
type: 'string' as const,
description: 'Career level (e.g., entry, senior, manager)'
},
income: {
type: 'number' as const,
description: 'Annual income'
},
location: {
type: 'string' as const,
description: 'Work location'
},
schedule: {
type: 'array' as const,
description: 'Working days/hours',
items: {
type: 'string' as const
}
}
}
},
home: {
type: 'object' as const,
description: 'Housing information',
properties: {
type: {
type: 'string' as const,
description: 'Type of residence (e.g., apartment, house)'
},
ownership: {
type: 'string' as const,
description: 'Ownership status (e.g., owned, rented)'
},
location: {
type: 'string' as const,
description: 'Home address or area'
},
commute_distance_km: {
type: 'number' as const,
description: 'Distance to work in kilometers'
}
}
},
household: {
type: 'object' as const,
description: 'Household composition',
properties: {
status: {
type: 'string' as const,
description: 'Marital/living status'
},
members: {
type: 'array' as const,
description: 'Other household members',
items: {
type: 'string' as const
}
},
pets: {
type: 'array' as const,
description: 'Household pets',
items: {
type: 'object' as const,
properties: {
type: {
type: 'string' as const,
description: 'Type of pet'
},
name: {
type: 'string' as const,
description: "Pet's name"
}
}
}
}
}
}
}
},
routines: {
type: 'object' as const,
description: 'Daily and weekly routines',
properties: {
weekday: {
type: 'object' as const,
description: 'Typical weekday schedule',
additionalProperties: {
type: 'object' as const,
properties: {
activity: {
type: 'string' as const,
description: 'Activity description'
},
location: {
type: 'string' as const,
description: 'Location of activity'
},
duration_minutes: {
type: 'number' as const,
description: 'Duration in minutes'
}
}
}
},
weekend: {
type: 'array' as const,
description: 'Regular weekend activities',
items: {
type: 'string' as const
}
},
commute: {
type: 'object' as const,
description: 'Commute details',
properties: {
method: {
type: 'string' as const,
description: 'Primary mode of transportation'
},
route: {
type: 'array' as const,
description: 'Regular route points',
items: {
type: 'string' as const
}
},
regular_stops: {
type: 'array' as const,
description: 'Regular stops during commute',
items: {
type: 'object' as const,
properties: {
location: {
type: 'string' as const,
description: 'Stop location'
},
purpose: {
type: 'string' as const,
description: 'Purpose of stop'
},
frequency: {
type: 'string' as const,
description: 'How often this stop is made'
}
}
}
}
}
}
}
},
preferences: {
type: 'object' as const,
description: 'User preferences and habits',
properties: {
diet: {
type: 'array' as const,
description: 'Dietary preferences and restrictions',
items: {
type: 'string' as const
}
},
brands: {
type: 'array' as const,
description: 'Brand preferences',
items: {
type: 'object' as const,
properties: {
name: {
type: 'string' as const,
description: 'Brand name'
},
loyalty_score: {
type: 'number' as const,
description: 'Brand loyalty score (1-10)',
minimum: 1,
maximum: 10
}
}
}
},
price_sensitivity: {
type: 'number' as const,
description: 'Price sensitivity score (1-10)',
minimum: 1,
maximum: 10
},
payment_methods: {
type: 'array' as const,
description: 'Preferred payment methods',
items: {
type: 'string' as const
}
}
}
},
finances: {
type: 'object' as const,
description: 'Financial information',
properties: {
subscriptions: {
type: 'array' as const,
description: 'Regular subscriptions',
items: {
type: 'object' as const,
properties: {
name: {
type: 'string' as const,
description: 'Subscription name'
},
amount: {
type: 'number' as const,
description: 'Monthly cost'
},
frequency: {
type: 'string' as const,
description: 'Billing frequency'
},
next_due_date: {
type: 'string' as const,
description: 'Next payment date',
format: 'date'
}
}
}
},
spending_patterns: {
type: 'object' as const,
description: 'Spending behavior',
properties: {
impulsive_score: {
type: 'number' as const,
description: 'Impulse buying tendency (1-10)',
minimum: 1,
maximum: 10
},
categories: {
type: 'object' as const,
description: 'Spending categories',
additionalProperties: {
type: 'object' as const,
properties: {
preference_score: {
type: 'number' as const,
description: 'Category preference (1-10)',
minimum: 1,
maximum: 10
},
monthly_budget: {
type: 'number' as const,
description: 'Typical monthly spend'
}
}
}
}
}
}
}
},
habits: {
type: 'object' as const,
description: 'Regular activities and habits',
properties: {
exercise: {
type: 'array' as const,
description: 'Exercise routines',
items: {
type: 'object' as const,
properties: {
activity: {
type: 'string' as const,
description: 'Type of exercise'
},
frequency: {
type: 'string' as const,
description: 'How often performed'
},
duration_minutes: {
type: 'number' as const,
description: 'Typical duration'
}
}
}
},
social: {
type: 'array' as const,
description: 'Social activities',
items: {
type: 'object' as const,
properties: {
activity: {
type: 'string' as const,
description: 'Type of social activity'
},
frequency: {
type: 'string' as const,
description: 'How often performed'
}
}
}
}
}
},
context: {
type: 'object' as const,
description: 'Contextual information',
properties: {
stress_triggers: {
type: 'array' as const,
description: 'Known stress factors',
items: {
type: 'string' as const
}
},
reward_behaviors: {
type: 'array' as const,
description: 'Activities used as rewards',
items: {
type: 'string' as const
}
},
upcoming_events: {
type: 'array' as const,
description: 'Planned future events',
items: {
type: 'object' as const,
properties: {
name: {
type: 'string' as const,
description: 'Event name'
},
date: {
type: 'string' as const,
description: 'Event date',
format: 'date'
},
importance: {
type: 'number' as const,
description: 'Event importance (1-10)',
minimum: 1,
maximum: 10
}
}
}
}
}
}
}
}
} as const;

View File

@@ -0,0 +1,61 @@
import { MBTIType } from './types';
const mbtiTypes: MBTIType[] = [
{
type: 'INTJ',
traits: ['analytical', 'planning-focused', 'independent', 'private']
},
{
type: 'ENTJ',
traits: ['strategic', 'leadership-oriented', 'decisive', 'organized']
},
{
type: 'ISFP',
traits: ['artistic', 'spontaneous', 'nature-loving', 'adaptable']
},
{ type: 'ESFJ', traits: ['caring', 'social', 'traditional', 'organized'] },
{ type: 'INTP', traits: ['logical', 'abstract', 'adaptable', 'private'] },
{
type: 'ENFP',
traits: ['enthusiastic', 'creative', 'spontaneous', 'people-oriented']
},
{ type: 'ISTJ', traits: ['practical', 'factual', 'organized', 'reliable'] },
{
type: 'ENFJ',
traits: ['charismatic', 'inspiring', 'idealistic', 'people-focused']
},
{
type: 'ISTP',
traits: ['practical', 'adaptable', 'experiential', 'logical']
},
{ type: 'ESFP', traits: ['spontaneous', 'energetic', 'social', 'practical'] },
{
type: 'INFJ',
traits: ['idealistic', 'organized', 'insightful', 'private']
},
{
type: 'ESTP',
traits: ['energetic', 'practical', 'spontaneous', 'experiential']
},
{ type: 'INFP', traits: ['idealistic', 'creative', 'authentic', 'adaptive'] },
{
type: 'ENTP',
traits: ['innovative', 'adaptable', 'analytical', 'outgoing']
},
{ type: 'ISFJ', traits: ['practical', 'caring', 'organized', 'traditional'] },
{
type: 'ESTJ',
traits: ['practical', 'organized', 'leadership-oriented', 'traditional']
}
];
export function generatePromptWithMBTI(prompt: string): string {
const selectedType = mbtiTypes[Math.floor(Math.random() * mbtiTypes.length)];
const mbtiJson = JSON.stringify({
type: selectedType.type,
traits: selectedType.traits
});
return prompt.replace('<MBTI_AND_TRAITS_HERE>', mbtiJson);
}

View File

@@ -1,12 +1,11 @@
import { prisma } from './prismaClient';
import { Persona } from './types';
export async function savePersonaDb(persona: string) {
const personaObject = JSON.parse(persona);
export async function savePersonaDb(persona: Persona) {
const result = await prisma.user.create({
data: {
name: personaObject['core']['name'],
age: personaObject['core']['age'],
name: persona.core.name,
age: persona.core.age,
persona: JSON.stringify(persona)
}
});

View File

@@ -1,10 +1,8 @@
import fs from 'fs';
import { parsePersona } from './parsePersona';
import { Persona } from './types';
export function savePersonaJson(persona: string, id: number) {
fs.promises.writeFile(`personas/${id}.json`, persona, 'utf8');
export function savePersonaJson(persona: Persona, id: number) {
fs.promises.writeFile(`personas/${id}.json`, JSON.stringify(persona), 'utf8');
const personaObject = parsePersona(persona);
console.log(`Persona ${personaObject.core.name} saved as persona/${id}.json`);
console.log(`Persona ${persona.core.name} saved as persona/${id}.json`);
}

View File

@@ -116,3 +116,8 @@ export type Persona = {
recent_changes: string[];
};
};
export interface MBTIType {
type: string;
traits: string[];
}