Agent objects are used to simulate survey responses for target audiences. They can be created with specified traits, such as personas and relevant attributes for a survey, that are used together with language models to generate answers to questions.
Agents can be created individually or as a list of agents, and can be updated after they are created. They can be used with any question type, and can be used to generate responses for a single question or a survey of multiple questions.Agent information is presented to a model in a system prompt; it is delivered together with a user prompt of information about a given question. In the examples below we show how to access these prompts to inspect them before running a survey and in the results that are generated for a survey. Note, however, that certain models do not take system prompts (e.g., OpenAI’s o1). When using a model that does not take a system prompt, agent information should be included in the user prompt.
An Agent is created by passing a dictionary of traits for a model to reference in answering questions. Traits can be anything that might be relevant to the questions the agent will be asked, and constructed with single values or textual narratives.For example:
Copy
Ask AI
from edsl import Agentagent = Agent( traits = { "persona": "You are an expert in machine learning.", "age": 45, "home_state": "Massachusetts" })
Note:Note that traits= must be named explicitly in the construction, and the traits must use Python identifiers as keys (e.g., home_state but not home state or home-state).
We can optionally give an agent a name when it is constructed:
Copy
Ask AI
agent = Agent( name = "Ada", traits = { "persona": "You are an expert in machine learning.", "age": 45, "home_state": "Massachusetts" })
If a name is not passed when the agent is created, an agent_name field is automatically added to the Results that are generated when a survey is run with the agent. This field is a unique identifier for the agent and can be used to filter or group results by agent. It is not used in the prompts for generating responses. If you want to use a name in the prompts for generating responses, you can pass it as a trait:
Copy
Ask AI
agent = Agent( traits = { "first_name": "Ada", "persona": "You are an expert in machine learning.", "age": 45, "home_state": "Massachusetts" })
We can see how the agent information is presented to a model by inspecting the system prompt that is generated when we use an agent with a question:
Copy
Ask AI
from edsl import QuestionFreeTextq = QuestionFreeText( question_name = "favorite_food", question_text = "What is your favorite food?")job = q.by(agent) # using the agent created abovejob.prompts().select("user_prompt", "system_prompt")
Output:
user_prompt
system_prompt
What is your favorite food?
You are answering questions as if you were a human. Do not break character. Your traits: {‘first_name’: ‘Ada’, ‘persona’: ‘You are an expert in machine learning.’, ‘age’: 45, ‘home_state’: ‘Massachusetts’}
Note that trying to create two agents with the same name or trying to use a key “name” in the traits will raise an error.
Agents can be created collectively and administered a survey together. This is useful for comparing responses for multiple agents.For example, here we create an a list of agents as an AgentList with different combinations of traits:
This code will create a list of agents that can then be used in a survey.Example code for running a survey with the agents:
Copy
Ask AI
from edsl import QuestionFreeText, Surveyq = QuestionFreeText( question_name = "favorite_food", question_text = "What is your favorite food?")survey = Survey(questions = [q])results = survey.by(agents).run()
This will generate a Results object that contains a Result for each agent’s responses to the survey question. Learn more about working with results in the Results section.
An AgentList can be automatically generated from data stored in many source types, including a list, a dictionary, a CSV, TSV or Excel file, a Pandas dataframe, etc.A general method for this is from_source() which is called on the AgentList class, takes a data source_type (csv, excel, pandas, etc.) and a data source, and returns an AgentList object. Optional parameters allow you to specify special instructions, a codebook for the traits, and a name_field for the agents.For example, if you have agent data stored in a CSV file, you can create an AgentList from it using the from_source() method by specifying source_type=”csv” and the path to the CSV file:
Copy
Ask AI
from edsl import AgentListagents = AgentList.from_source( source_type="csv", file_or_url="agent_data.csv") # replace with your CSV file path
If the data source is a CSV or Excel file, the header row is used as the keys for the traits, and can optionally have a column “name” for the agent names. If a different column name should be used for the agent names, it can be specified with the name_field parameter:
Copy
Ask AI
from edsl import AgentListagents = AgentList.from_source( source_type="csv", file_or_url="agent_data.csv", # replace with your CSV file path name_field="first_name" # replace with your column name for agent names )
A codebook can also be passed to provide descriptions for the traits. It can be useful for providing context to a model about the traits of an agent. For example, if you have a trait “age” and you want to provide more context about what that means, you could use the codebook to specify that “age” refers to the age of the agent in years:
Copy
Ask AI
from edsl import AgentListcodebook = { "age": "The age of the agent in years", "location": "The location of the agent"}agents = AgentList.from_source( source_type="csv", file_or_url="agent_data.csv", # replace with your CSV file path codebook=codebook )
Special instructions can also be passed to modify the default instructions that are used with agent traits in the system prompt. For example, if you want all agents to answer in German, you could use the instructions parameter to specify that:
Copy
Ask AI
from edsl import AgentListagents = AgentList.from_source( source_type="csv", file_or_url="agent_data.csv", # replace with your CSV file path instructions="Answer in German." )
We can create a simple AgentList from a list using the from_list() method, which takes a single trait_name and a list of values for it and returns an agent for each value (each agent has a single trait):
Copy
Ask AI
from edsl import AgentListagents = AgentList.from_list(trait_name="age", values=[10, 20, 30, 40])agents
We can create a more complex AgentList from a dictionary using the from_dict() method. It takes a dictionary with a key agent_list and a list of dictionaries, each of which must have a traits key with a dictionary of traits and an optional name key for the agent’s name:
We can also create an AgentList from a CSV file using the from_csv() method. The CSV file must have a header row of Pythonic keys for the traits, and can optionally have a column “name” for the agent names:
Copy
Ask AI
from edsl import AgentList# Creating a CSV file with agent data to use as an exampleimport pandas as pddata = [ {"name": "Alice", "age": 25, "city": "New York"}, {"name": "Bob", "age": 30, "city": "San Francisco"}, {"name": "Charlie", "age": 35, "city": "Chicago"}]df = pd.DataFrame(data)df.to_csv("agent_data.csv", index=False)# Creating an AgentList from the CSV fileagents = AgentList.from_csv("agent_data.csv")agents
Agents can also be created with a dynamic_traits_function parameter. This function can be used to generate traits dynamically based on the question being asked or the scenario in which the question is asked.
Note:This method is only available with local inference. It does not work with remote inference.
When the agent is asked a question about age, the agent will return an age of 10. When asked about hair, the agent will return “brown”. This can be useful for creating agents that can answer questions about different topics without including potentially irrelevant traits in the agent’s traits dictionary.Note that the traits returned by the function are not added to the agent’s traits dictionary.
Agents can also be created with a method that can answer a particular question type directly:
Copy
Ask AI
from edsl import Agenta = Agent()def f(self, question, scenario): return "I am a direct answer."a.add_direct_question_answering_method(f)a.answer_question_directly(question = None, scenario = None)
Output:
Copy
Ask AI
I am a direct answer.
This can be useful for creating agents that can answer questions directly without needing to use a language model.
In addition to traits, agents can be given detailed instructions on how to answer questions. The instruction parameter can be used to omit or modify the default instructions that are used with agent traits in the system prompt.For example:
Copy
Ask AI
from edsl import Agenta = Agent(traits = {"age": 10}, instruction = "Answer in German.")a.instruction
Output:
Copy
Ask AI
Answer in German.
When the agent is assigned to a survey, the special instruction will be added to the prompts for generating responses. We can create a Job object to inspect the prompts (user and system) that will be used to generate responses:
Copy
Ask AI
from edsl import QuestionFreeTextq = QuestionFreeText( question_name = "favorite_food", question_text = "What is your favorite food?")job = q.by(a) # using the agent created abovejob.prompts().select("user_prompt", "system_prompt")
Output:
user_prompt
system_prompt
What is your favorite food?
Answer in German. Your traits: {‘age’: 10}. Answer in German.
Learn more about how to use instructions in the Prompts section.
The traits_presentation_template parameter can be used to create a narrative persona for an agent. This is a template string that can be rendered with the agent’s traits as variables.For example:
Copy
Ask AI
a = Agent( traits = {'age': 22, 'hair': 'brown', 'gender': 'female'}, traits_presentation_template = "I am a {`{ age }`} year-old {`{ gender }`} with {`{ hair }`} hair." )a.agent_persona.render(primary_replacement = a.traits)
Output:
Copy
Ask AI
I am a 22 year-old female with brown hair.
Note that the trait keys must be valid Python identifiers (e.g., home_state but not home state or home-state). This can be handled by using a dictionary with string keys and values, for example:
Copy
Ask AI
from edsl import Agentcodebook = {'age': 'The age of the agent'}a = Agent( traits = {'age': 22}, codebook = codebook, traits_presentation_template = "{`{ codebook['age'] }`} is {`{ age }`}." )a.agent_persona.render(primary_replacement = a.traits)
Output:
Copy
Ask AI
The age of the agent is 22.
We can also use the traits_presentation_template together with an instruction and inspect the prompts:
Copy
Ask AI
from edsl import Agent, QuestionFreeTexta = Agent( traits = {"age": 10}, traits_presentation_template = "(You are {`{ age }`} years old.)", instruction = "Answer in German.")q = QuestionFreeText( question_name = "favorite_food", question_text = "What is your favorite food?")job = q.by(a)job.prompts().select("user_prompt", "system_prompt")
Output:
user_prompt
system_prompt
What is your favorite food?
Answer in German.(You are 10 years old.)
Note:Note that it can be helpful to include traits mentioned in the persona as independent keys and values in order to analyze survey results by those dimensions individually. For example, we may want the narrative to include a sentence about the agent’s age, but also be able to readily analyze or filter results by age.
The following code will include the agent’s age as a column of a table with any other selected components (e.g., agent name and all the answers):
When a survey is run, agents can be assigned to it using the by method, which can be chained with other components like scenarios and models:
Copy
Ask AI
from edsl import Agent, QuestionList, QuestionMultipleChoice, Surveyagent = Agent( name = "college student", traits = { "persona": "You are a sophomore at a community college in upstate New York.", "year": "sophomore", "school": "community college", "major": "biology", "state": "New York" })q1 = QuestionList( question_name = "favorite_courses", question_text = "What are the names of your 3 favorite courses?", max_list_items = 3)q2 = QuestionMultipleChoice( question_name = "attend_grad_school", question_text = "Do you plan to attend grad school?", question_options = ["Yes", "No", "Undecided"])survey = Survey([q1, q2])results = survey.by(agent).run()
This will generate a Results object that contains a Result for each agent’s responses to the survey questions. We can select and inspect components of the results, such as the agent’s traits and their answers:
If multiple agents will be used with a survey, they are passed as a list in the same by call:
Copy
Ask AI
from edsl import Agent, AgentListagents = AgentList([ Agent(traits = {"major": "biology", "year": "sophomore"}), Agent(traits = {"major": "history", "year": "junior"}), Agent(traits = {"major": "mathematics", "year": "senior"}),])results = survey.by(agents).run() # using the same survey as aboveresults.select("major", "year", "answer.*")
Output:
agent.major
agent.year
answer.favorite_courses
answer.attend_grad_school
biology
sophomore
[‘Genetics’, ‘Ecology’, ‘Cell Biology’]
Undecided
history
junior
[‘Medieval Europe’, ‘The American Civil War’, ‘Ancient Civilizations’]
Undecided
mathematics
senior
[‘Abstract Algebra’, ‘Real Analysis’, ‘Topology’]
Undecided
If scenarios and/or models are also specified for a survey, each component type is added in a separate by call that can be chained in any order with the run method appended last:
Copy
Ask AI
results = survey.by(scenarios).by(agents).by(models).run() # example code - scenarios and models not defined here
After running a survey, we can use the responses to create new traits for an agent:
Copy
Ask AI
from edsl import Agent, QuestionMultipleChoice, Surveya = Agent(traits = {"age": 22, "location": "California"})q = QuestionMultipleChoice( question_name = "surfing", question_text = "How often do you go surfing?", question_options = ["Never", "Sometimes", "Often"])survey = Survey([q])results = survey.by(a).run()a = results.select("age", "location", "surfing").to_agent_list() # create new agent with traits from resultsa
Output:
location
surfing
age
California
Sometimes
22
Note:Note that in the example above we simply replaced the original agent by selecting the first agent from the agent list that we created. This can be useful for creating agents that evolve over time based on their experiences or responses to surveys.
Here we use the same method to update multiple agents at once:
Copy
Ask AI
from edsl import Agent, QuestionMultipleChoice, Survey, AgentListagents = AgentList([ Agent(traits = {"age": 22, "location": "California"}), Agent(traits = {"age": 30, "location": "New York"}), Agent(traits = {"age": 40, "location": "Texas"}),])q = QuestionMultipleChoice( question_name = "surfing", question_text = "How often do you go surfing?", question_options = ["Never", "Sometimes", "Often"])survey = Survey([q])results = survey.by(agents).run()agents = results.select("age", "location", "surfing").to_agent_list()agents
The AgentList.from_results() method allows you to create an AgentList directly from a Results object. This is useful when you want to create agents based on survey responses, including their original traits and their answers to questions.By default, this method includes all answer columns as traits for the new agents:
Copy
Ask AI
from edsl import AgentList# Create AgentList with all questions includednew_agents = AgentList.from_results(results)# The new agents will have traits from the original agents plus all their answersnew_agents[0]You can also specify which question responses to include as traits using the question_names parameter:# Include only specific questions as traitsnew_agents = AgentList.from_results(results, question_names=['surfing', 'age'])# The new agents will have traits from the original agents plus only the specified answersnew_agents[0]
This is particularly useful when you want to: - Create agents with only certain response patterns - Filter out irrelevant or sensitive question responses - Create more focused agent profiles based on specific survey questions - Reduce the number of traits when only certain responses are needed
Note:Note that the question_names parameter affects both answer.* columns (as traits) and prompt.* columns (as codebook). Agent traits (from agent.* columns) are always included.
Bases: BaseA class representing an AI agent with customizable traits that can answer questions.An Agent in EDSL represents an entity with specific characteristics (traits) that can answer questions through various mechanisms. Agents can use language models to generate responses based on their traits, directly answer questions through custom functions, or dynamically adjust their traits based on the questions being asked.Key capabilities: - Store and manage agent characteristics (traits) - Provide instructions on how the agent should answer - Support for custom codebooks to provide human-readable trait descriptions - Integration with multiple question types and language models - Combine agents to create more complex personas - Customize agent behavior through direct answering methodsAgents are used in conjunction with Questions, Scenarios, and Surveys to create structured interactions with language models.
traits: Dictionary of agent characteristics (e.g., {“age”: 30, “occupation”: “doctor”}) name: Optional name identifier for the agent codebook: Dictionary mapping trait keys to human-readable descriptions for prompts.This provides more descriptive labels for traits when rendering prompts. For example, {‘age’: ‘Age in years’} would display “Age in years: 30” instead of “age: 30”.instruction: Directive for how the agent should answer questions traits_presentation_template: Jinja2 template for formatting traits in prompts dynamic_traits_function: Function that can modify traits based on questions dynamic_traits_function_source_code: Source code string for the dynamic traits function dynamic_traits_function_name: Name of the dynamic traits function answer_question_directly_source_code: Source code for direct question answering method answer_question_directly_function_name: Name of the direct answering functionThe Agent class brings together several key concepts:
Traits are key-value pairs that define an agent’s characteristics. These are used to construct a prompt that guides the language model on how to respond.Example: >>> a = Agent(traits={“age”: 10, “hair”: “brown”, “height”: 5.5}) >>> a.traits {‘age’: 10, ‘hair’: ‘brown’, ‘height’: 5.5}
The traits_presentation_template controls how traits are formatted in prompts. It uses Jinja2 templating to insert trait values.Example: >>> a = Agent(traits={“age”: 10}, traits_presentation_template=”I am a {{age}} year old.”) >>> repr(a.agent_persona) ‘Prompt(text=”””I am a {{age}} year old.”””)’
Codebooks provide human-readable descriptions for traits in prompts.Example: >>> traits = {“age”: 10, “hair”: “brown”, “height”: 5.5} >>> codebook = {‘age’: ‘Their age is’} >>> a = Agent(traits=traits, codebook=codebook, … traits_presentation_template=”This agent is Dave. {{codebook[‘age’]}} {{age}}”) >>> d = a.traits | {‘codebook’: a.codebook} >>> a.agent_persona.render(d) Prompt(text=”””This agent is Dave. Their age is 10”””)
other_agent:The second agent to merge with self. If None, self is returned unchanged.conflict_strategy:How to handle overlapping trait names.
"numeric" (default) – rename conflicting traits coming from other_agent by appending an incrementing suffix (_1, _2 …). This is identical to the behaviour of the + operator before this refactor.
"repeated_observation" – if both agents have the same trait and the codebook entry for that trait is identical (or missing in both), merge the two values into a list [old, new]. If the codebook entries differ, an edsl.agents.exceptions.AgentCombinationError is raised.
method: A method that can answer a question directly validate_response: Whether to validate the response translate_response: Whether to translate the responseRaises:AgentDirectAnswerFunctionError: If the method signature is invalid
>>> a = Agent()>>> def f(self, question, scenario): return "I am a direct answer.">>> a.add_direct_question_answering_method(f)>>> a.answer_question_directly(question = None, scenario = None)'I am a direct answer.'
Get the agent’s persona template as a Prompt object.This property provides access to the template that formats the agent’s traits for presentation in prompts. The template is wrapped in a Prompt object that supports rendering with variable substitution.Returns:Prompt: A prompt object containing the traits presentation template
question: The question to answer cache: The cache to use for storing responses scenario: The scenario in which the question is asked survey: The survey context model: The language model to use debug: Whether to run in debug mode memory_plan: The memory plan to use current_answers: The current answers iteration: The iteration number key_lookup: The key lookup for API credentials
Returns:
An AgentResponseDict containing the answer
Example:
Copy
Ask AI
>>> a = Agent(traits = {})>>> a.add_direct_question_answering_method(lambda self, question, scenario: "I am a direct answer.")>>> from edsl.questions import QuestionFreeText>>> q = QuestionFreeText.example()>>> a.answer_question(question = q, cache = False).answer'I am a direct answer.'
Note:
This is a function where an agent returns an answer to a particular question. However, there are several different ways an agent can answer a question, so the actual functionality is delegated to an InvigilatorBase object.
question: The question to answer cache: The cache to use for storing responses scenario: The scenario in which the question is asked survey: The survey context model: The language model to use debug: Whether to run in debug mode memory_plan: The memory plan to use current_answers: The current answers iteration: The iteration number key_lookup: The key lookup for API credentials
Returns:
An AgentResponseDict containing the answer
Example:
Copy
Ask AI
>>> a = Agent(traits = {})>>> a.add_direct_question_answering_method(lambda self, question, scenario: "I am a direct answer.")>>> from edsl.questions import QuestionFreeText>>> q = QuestionFreeText.example()>>> a.answer_question(question = q, cache = False).answer'I am a direct answer.'
Note:
This is a function where an agent returns an answer to a particular question. However, there are several different ways an agent can answer a question, so the actual functionality is delegated to an InvigilatorBase object.
Create a deep copy of this agent using serialization/deserialization.This method uses to_dict/from_dict to create a completely independent copy of the agent, including all its traits, codebook, instructions, and special functions like dynamic traits and direct answering methods.
Returns:
Agent: A new agent instance that is functionally identical to this one
Examples:
Copy
Ask AI
>>> a = Agent(traits={"age": 10, "hair": "brown"},... codebook={'age': 'Their age is'})>>> a2 = a.copy()>>> a2 == a # Functionally equivalentTrue>>> id(a) == id(a2) # But different objectsFalse
Copy preserves direct answering methods:
Copy
Ask AI
>>> def f(self, question, scenario): return "I am a direct answer.">>> a.add_direct_question_answering_method(f)>>> a2 = a.copy()>>> a2.answer_question_directly(None, None)'I am a direct answer.'
Create an Invigilator.An invigilator is an object that is responsible for administering a question to an agent. There are several different types of invigilators, depending on the type of question and the agent. For example, there are invigilators for functional questions, for direct questions, and for LLM questions.
Args:
question: The question to be asked cache: The cache for storing responses survey: The survey context scenario: The scenario context model: The language model to use memory_plan: The memory plan to use current_answers: The current answers iteration: The iteration number raise_validation_errors: Whether to raise validation errors key_lookup: The key lookup for API credentials
Returns:
An InvigilatorBase instance for handling the question
Create a deep copy of this agent with all its traits and capabilities.This method creates a completely independent copy of the agent, including all its traits, codebook, instructions, and special functions like dynamic traits and direct answering methods.
Returns:
Agent: A new agent instance that is functionally identical to this one
Examples:
Create a duplicate agent and verify it’s equal but not the same object:
Copy
Ask AI
>>> a = Agent(traits={"age": 10, "hair": "brown", "height": 5.5},... codebook={'age': 'Their age is'})>>> a2 = a.duplicate()>>> a2 == a # Functionally equivalentTrue>>> id(a) == id(a2) # But different objectsFalse
Duplicating preserves direct answering methods:
Copy
Ask AI
>>> def f(self, question, scenario): return "I am a direct answer.">>> a.add_direct_question_answering_method(f)>>> hasattr(a, "answer_question_directly")True>>> a2 = a.duplicate()>>> a2.answer_question_directly(None, None)'I am a direct answer.'
Create an Agent instance from a Result object.The agent’s traits will correspond to the questions asked during the interview (the keys of result.answer) with their respective answers as the values.A simple, readable traits_presentation_template is automatically generated so that rendering the agent will look like:
Copy
Ask AI
This person was asked the following questions – here are the answers:Q: <question 1>A: <answer 1>Q: <question 2>A: <answer 2>...
Args:
result: The Result instance from which to build the agent name: Optional explicit name for the new agent. If omitted, we attempt
to reuse result.agent.name if it exists
Returns:
A new Agent instance created from the result
Raises:
TypeError: If result is not a Result object
Example:
Copy
Ask AI
>>> from edsl.results import Result>>> # result = Result(...)>>> # agent = Agent.from_result(result)
Legacy trait selection method (renamed from select).Note: This method has data integrity issues and is kept for backward compatibility. Use select() or keep() instead, which provide better data consistency.
Generate a formatted prompt containing the agent’s traits.This method renders the agent’s traits presentation template with the agent’s traits and codebook, creating a formatted prompt that can be used in language model requests.The method is dynamic and responsive to changes in the agent’s state:
If a custom template was explicitly set during initialization, it will be used
If using the default template and the codebook has been updated since initialization, this method will recreate the template to reflect the current codebook values
The template is rendered with access to all trait values, the complete traits dictionary, and the codebook
The template rendering makes the following variables available: - All individual trait keys (e.g., , ) - The full traits dictionary as - The codebook as
Returns:
Prompt: A Prompt object containing the rendered template
Raises:
QuestionScenarioRenderError: If any template variables remain undefined
Custom templates can reference any trait directly:
Copy
Ask AI
>>> template = "Profile: {`{age}`} year old {`{occupation}`}">>> agent = Agent(traits={"age": 45, "occupation": "teacher"},... traits_presentation_template=template)>>> agent.prompt().text'Profile: 45 year old teacher'
>>> a = Agent()>>> def f(self, question, scenario): return "I am a direct answer.">>> a.add_direct_question_answering_method(f)>>> a.remove_direct_question_answering_method()>>> hasattr(a, "answer_question_directly")False
old_name_or_dict: The old name of the trait or a dictionary of old names and new names new_name: The new name of the trait (required if old_name_or_dict is a string)
Search the agent’s traits for a string.This method delegates to the traits manager to search through trait descriptions and return ranked matches based on similarity.
Args:
search_string: The string to search for in trait descriptions
Select agents with only the referenced traits.This method now uses the robust keep() implementation for better data integrity and consistent handling of codebooks and trait_categories.
Create a tabular representation of the agent’s traits.This method delegates to the table manager to create a structured Dataset containing trait information.
Get the agent’s traits, potentially using dynamic generation.This property provides access to the agent’s traits, either from the stored traits dictionary or by calling a dynamic traits function if one is defined. If a dynamic traits function is used, it may take the current question as a parameter to generate context-aware traits.
Returns:
dict: Dictionary of agent traits (key-value pairs)
Bases: UserList, Base, AgentListOperationsMixinA list of Agents with additional functionality for manipulation and analysis.The AgentList class extends Python’s UserList to provide a container for Agent objects with methods for filtering, transforming, and analyzing collections of agents.
data: A list of Agent objects. If None, creates an empty AgentList. codebook: Optional dictionary mapping trait names to descriptions.If provided, will be applied to all agents in the list.
Apply instructions to all agents in the list.This method provides a more intuitive name for setting instructions on all agents, avoiding the need to iterate manually.
Args:
instructions: The instructions to apply to all agents.
Returns:
AgentList: Returns self for method chaining.
Examples:
Copy
Ask AI
>>> from edsl import Agent, AgentList>>> agents = AgentList([Agent(traits={'age': 30}), Agent(traits={'age': 40})])>>> agents.add_instructions("Answer as if you were this age")AgentList([Agent(traits = {'age': 30}, instruction = """Answer as if you were this age"""), Agent(traits = {'age': 40}, instruction = """Answer as if you were this age""")])
Adds a new trait to every agent, with values taken from values.
Parameters:
trait – The name of the trait.
values – The valeues(s) of the trait. If a single value is passed, it is used for all agents.
Copy
Ask AI
>>> al = AgentList.example()>>> new_al = al.add_trait('new_trait', 1)>>> new_al.select('new_trait').to_scenario_list().to_list()[1, 1]>>> al.add_trait('new_trait', [1, 2, 3])Traceback (most recent call last):...edsl.agents.exceptions.AgentListError: The passed values have to be the same length as the agent list....
Return TSV representation of this object for clipboard operations.This method is called by the clipboard() method in the base class to provide a custom format for copying objects to the system clipboard.
Returns:
str: Tab-separated values representation of the object
Expand a field containing dictionaries into separate fields.This method takes a field that contains a list of dictionaries and expands it into multiple fields, one for each key in the dictionaries. This is useful when working with nested data structures or results from extraction operations.
Parameters:
field: The field containing dictionaries to flatten keep_original: Whether to retain the original field in the result
Returns:
A new Dataset with the dictionary keys expanded into separate fields
Notes:
Each key in the dictionaries becomes a new field with name pattern “.”
All dictionaries in the field must have compatible structures
If a dictionary is missing a key, the corresponding value will be None
Non-dictionary values in the field will cause a warning
Examples:
Copy
Ask AI
>>> from edsl.dataset import Dataset
Copy
Ask AI
# Basic flattening of nested dictionaries >>> Dataset([{‘a’: [{‘a’: 1, ‘b’: 2}]}, {‘c’: [5]}]).flatten(‘a’) Dataset([{‘c’: [5]}, {‘a.a’: [1]}, {‘a.b’: [2]}])# Works with prefixed fields too >>> Dataset([{‘answer.example’: [{‘a’: 1, ‘b’: 2}]}, {‘c’: [5]}]).flatten(‘answer.example’) Dataset([{‘c’: [5]}, {‘answer.example.a’: [1]}, {‘answer.example.b’: [2]}])# Keep the original field if needed >>> d = Dataset([{‘a’: [{‘a’: 1, ‘b’: 2}]}, {‘c’: [5]}]) >>> d.flatten(‘a’, keep_original=True) Dataset([{‘a’: [{‘a’: 1, ‘b’: 2}]}, {‘c’: [5]}, {‘a.a’: [1]}, {‘a.b’: [2]}])# Can also use unambiguous unprefixed field name >>> result = Dataset([{‘answer.pros_cons’: [{‘pros’: [‘Safety’], ‘cons’: [‘Cost’]}]}]).flatten(‘pros_cons’) >>> sorted(result.keys()) == [‘answer.pros_cons.cons’, ‘answer.pros_cons.pros’] True >>> sorted(result.to_dicts()[0].items()) == sorted({‘cons’: [‘Cost’], ‘pros’: [‘Safety’]}.items()) True
Load AgentList from a CSV file.Deprecated since version Use: AgentList.from_source(‘csv’, …) instead.
Copy
Ask AI
>>> import csv>>> import os>>> with open('/tmp/agents.csv', 'w') as f:... writer = csv.writer(f)... _ = writer.writerow(['age', 'hair', 'height'])... _ = writer.writerow([22, 'brown', 5.5])>>> al = AgentList.from_csv('/tmp/agents.csv')>>> al = AgentList.from_csv('/tmp/agents.csv', name_field='hair')>>> al = AgentList.from_csv('/tmp/agents.csv', codebook={'age': 'Age in years'})>>> al = AgentList.from_csv('/tmp/agents.csv', instructions='Answer as a person')>>> os.remove('/tmp/agents.csv')
Parameters:
file_path – The path to the CSV file.
name_field – The name of the field to use as the agent name.
codebook – Optional dictionary mapping trait names to descriptions.
instructions – Optional instructions to apply to all created agents.
results: The Results object to convert question_names: Optional list of question names to include. If None, all questions are included.Affects both answer.* columns (as traits) and prompt.* columns (as codebook). Agent traits are always included.
Returns:
AgentList: A new AgentList created from the Results
Create an AgentList from a ScenarioList.This method supports special fields that map to Agent parameters: - “name”: Will be used as the agent’s name - “agent_parameters”: A dictionary containing:
“instruction”: The agent’s instruction text
“name”: The agent’s name (overrides the “name” field if present)
Examples:
Copy
Ask AI
>>> from edsl import ScenarioList, Scenario>>> # Basic usage with traits>>> s = ScenarioList([Scenario({'age': 22, 'hair': 'brown', 'height': 5.5})])>>> al = AgentList.from_scenario_list(s)>>> alAgentList([Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5})])
Create an AgentList from a specified source type.This method serves as the main entry point for creating AgentList objects, providing a unified interface for various data sources.
Args:
source_type: The type of source to create an AgentList from.Valid values include: ‘csv’, ‘tsv’, ‘excel’, ‘pandas’, etc.*args: Positional arguments to pass to the source-specific method. instructions: Optional instructions to apply to all created agents. codebook: Optional dictionary mapping trait names to descriptions, or a path to a CSV file.If a CSV file is provided, it should have 2 columns: original keys and descriptions. Keys will be automatically converted to pythonic names.name_field: The name of the field to use as the agent name (for CSV/Excel sources). **kwargs: Additional keyword arguments to pass to the source-specific method.
Returns:
An AgentList object created from the specified source.
Examples:
Copy
Ask AI
>>> # Create agents from a CSV file with instructions>>> # agents = AgentList.from_source(>>> # 'csv', 'agents.csv',>>> # instructions="Answer as if you were the person described">>> # )>>> #>>> # Create agents with a CSV codebook file>>> # agents = AgentList.from_source(>>> # 'csv', 'agents.csv',>>> # codebook='codebook.csv' # CSV with keys like "Age in years" -> "age_in_years">>> # )
Returns a codebook dictionary mapping CSV column names to None.Reads the header row of a CSV file and creates a codebook with field names as keys and None as values.Args:file_path: Path to the CSV file to read.Returns:A dictionary with CSV column names as keys and None as values.Raises:FileNotFoundError: If the specified file path does not exist. csv.Error: If there is an error reading the CSV file.
Internal method to get tabular data in a standard format.Args:remove_prefix: Whether to remove the prefix from column names pretty_labels: Dictionary mapping original column names to pretty labelsReturns:Tuple containing (header_row, data_rows)
Create visualizations using R’s ggplot2 library.This method provides a bridge to R’s powerful ggplot2 visualization library, allowing you to create sophisticated plots directly from EDSL data structures.
Parameters:
ggplot_code: R code string containing ggplot2 commands shape: Data shape to use (“wide” or “long”) sql: Optional SQL query to transform data before visualization remove_prefix: Whether to remove prefixes (like “answer.”) from column names debug: Whether to display debugging information height: Plot height in inches width: Plot width in inches factor_orders: Dictionary mapping factor variables to their desired order
Returns:
A plot object that renders in Jupyter notebooks
Notes:
Requires R and the ggplot2 package to be installed
Data is automatically converted to a format suitable for ggplot2
The ggplot2 code should reference column names as they appear after any transformations from the shape and remove_prefix parameters
Examples:
Copy
Ask AI
>>> from edsl.results import Results>>> r = Results.example()>>> # The following would create a plot if R is installed (not shown in doctest):>>> # r.ggplot2('''>>> # ggplot(df, aes(x=how_feeling)) +>>> # geom_bar() +>>> # labs(title="Distribution of Feelings")>>> # ''')
Print the results in a long format. >>> from edsl.results import Results >>> r = Results.example() >>> r.select(‘how_feeling’).print_long() answer.how_feeling: OK answer.how_feeling: Great answer.how_feeling: Terrible answer.how_feeling: OK
Returns a new Dataset with the prefix removed from all column names.The prefix is defined as everything before the first dot (.) in the column name. If removing prefixes would result in duplicate column names, an exception is raised.
Returns:
Dataset: A new Dataset with prefixes removed from column names
Raises:
ValueError: If removing prefixes would result in duplicate column names
Examples:
Copy
Ask AI
>>> from edsl.results import Results>>> r = Results.example()>>> r.select('how_feeling', 'how_feeling_yesterday').relevant_columns()['answer.how_feeling', 'answer.how_feeling_yesterday']>>> r.select('how_feeling', 'how_feeling_yesterday').remove_prefix().relevant_columns()['how_feeling', 'how_feeling_yesterday']
Copy
Ask AI
>>> from edsl.dataset import Dataset>>> d = Dataset([{'a.x': [1, 2, 3]}, {'b.x': [4, 5, 6]}])>>> # d.remove_prefix()
# Testing remove_prefix with duplicate column names raises DatasetValueError - tested in unit tests
Generates a report of the results by iterating through rows.
Args:
*fields: The fields to include in the report. If none provided, all fields are used. top_n: Optional limit on the number of observations to include. header_fields: Optional list of fields to include in the main header instead of as sections. divider: If True, adds a horizontal rule between observations (markdown only). return_string: If True, returns the markdown string. If False (default in notebooks),only displays the markdown without returning.format: Output format - either “markdown” or “docx”. filename: If provided and format is “docx”, saves the document to this file.
Returns:
Depending on format and return_string: - For markdown: A string if return_string is True, otherwise None (displays in notebook) - For docx: A docx.Document object, or None if filename is provided (saves to file)Examples:
Copy
Ask AI
>>> from edsl.results import Results>>> r = Results.example()>>> report = r.select('how_feeling').report(return_string=True)>>> "# Observation: 1" in reportTrue>>> doc = r.select('how_feeling').report(format="docx")>>> isinstance(doc, object)True
Generates a report using a Jinja2 template for each row in the dataset.This method renders a user-provided Jinja2 template for each observation in the dataset, with template variables populated from the row data. This allows for completely customized report formatting using pandoc for advanced output formats.
Args:
template: Jinja2 template string to render for each row *fields: The fields to include in template context. If none provided, all fields are used. top_n: Optional limit on the number of observations to include. remove_prefix: Whether to remove type prefixes (e.g., “answer.”) from field names in template context. return_string: If True, returns the rendered content. If False (default in notebooks),only displays the content without returning.format: Output format - one of “text”, “html”, “pdf”, or “docx”. Formats other than “text” require pandoc. filename: If provided, saves the rendered content to this file. For exploded output,this becomes a template (e.g., “report_”).separator: String to use between rendered templates for each row (ignored when explode=True). observation_title_template: Optional Jinja2 template for observation titles.Defaults to “Observation ” where index is 1-based. Template has access to all row data plus ‘index’ and ‘index0’ variables.explode: If True, creates separate files for each observation instead of one combined file. filestore: If True, wraps the generated file(s) in FileStore object(s). If no filename is provided,creates temporary files. For exploded output, returns a list of FileStore objects.
Returns:
Depending on explode, format, return_string, and filestore: - For text format: String content or None (if displayed in notebook) - For html format: HTML string content or None (if displayed in notebook) - For docx format: Document object or None (if saved to file) - For pdf format: PDF bytes or None (if saved to file) - If explode=True: List of created filenames (when filename provided) or list of documents/content - If filestore=True: FileStore object(s) containing the generated file(s)
Notes:
Pandoc is required for HTML, PDF, and DOCX output formats
Templates are treated as Markdown for all non-text formats
PDF output uses XeLaTeX engine through pandoc
HTML output includes standalone document structure
Examples:
Copy
Ask AI
>>> from edsl.results import Results>>> r = Results.example()>>> template = "Person feels: {`{ how_feeling }`}">>> report = r.select('how_feeling').report_from_template(template, return_string=True)>>> "Person feels: OK" in reportTrue>>> "Person feels: Great" in reportTrue
Return a random sample of agents.Args:n: The number of agents to sample. seed: Optional seed for the random number generator to ensure reproducibility.Returns:AgentList: A new AgentList containing the sampled agents.
Create a new AgentList with only the specified traits.Args:*traits: Variable number of trait names to keep.Returns:AgentList: A new AgentList containing agents with only the selected traits.Examples:
>>> from edsl import Agent>>> a = Agent(traits = {'hair': 'brown'})>>> al = AgentList([a, a])>>> _ = al.set_codebook({'hair': "Color of hair on driver's license"})>>> al[0].codebook{'hair': "Color of hair on driver's license"}
Configure dynamic traits for each agent from a question→traits mapping (in-place).Each agent will get a dynamic traits function that, when asked a question whose question_name is present in q_to_traits, returns a dict mapping the corresponding trait name(s) to the agent’s original static value(s) for those trait(s).A warning is emitted if the set of mapped trait names does not exactly equal the set of trait keys present in this AgentList.
Args:
q_to_traits: Mapping from question name to list of trait keys, e.g.{"geo": ["hometown"], "cuisine": ["food"]}.
Randomly shuffle the agents in place.Args:seed: Optional seed for the random number generator to ensure reproducibility.Returns:AgentList: The shuffled AgentList (self).
Execute SQL queries on the dataset.This powerful method allows you to use SQL to query and transform your data, combining the expressiveness of SQL with EDSL’s data structures. It works by creating an in-memory SQLite database from your data and executing the query against it.
Parameters:
query: SQL query string to execute transpose: Whether to transpose the resulting table (rows become columns) transpose_by: Column to use as the new index when transposing remove_prefix: Whether to remove type prefixes (e.g., “answer.”) from column names shape: Data shape to use (“wide” or “long”)
“wide”: Default tabular format with columns for each field
“long”: Melted format with key-value pairs, useful for certain queries
Returns:
A Dataset object containing the query results
Notes:
The data is stored in a table named “self” in the SQLite database
In wide format, column names include their type prefix unless remove_prefix=True
In long format, the data is melted into columns: row_number, key, value, data_type
Complex objects like lists and dictionaries are converted to strings
Examples:
Copy
Ask AI
>>> from edsl import Results>>> r = Results.example()
Copy
Ask AI
# Basic selection >>> len(r.sql(“SELECT * FROM self”, shape=”wide”)) 4# Filtering with WHERE clause >>> r.sql(“SELECT * FROM self WHERE how_feeling = ‘Great’”).num_observations() 1# Aggregation >>> r.sql(“SELECT how_feeling, COUNT(*) as count FROM self GROUP BY how_feeling”).keys() [‘how_feeling’, ‘count’]# Using long format >>> len(r.sql(“SELECT * FROM self”, shape=”long”)) 200
Count frequency distributions of values in specified fields.This method tallies the occurrence of unique values within one or more fields, similar to a GROUP BY and COUNT in SQL. When multiple fields are provided, it performs cross-tabulation across those fields.
Parameters:
*fields: Field names to tally. If none provided, uses all available fields. top_n: Optional limit to return only the top N most frequent values. output: Format for results, either “Dataset” (recommended) or “dict”.
Returns:
By default, returns a Dataset with columns for the field(s) and a ‘count’ column. If output=”dict”, returns a dictionary mapping values to counts.
Notes:
For single fields, returns counts of each unique value
For multiple fields, returns counts of each unique combination of values
Results are sorted in descending order by count
Fields can be specified with or without their type prefix
Examples:
Copy
Ask AI
>>> from edsl import Results>>> r = Results.example()
Copy
Ask AI
# Single field frequency count >>> r.select(‘how_feeling’).tally(‘answer.how_feeling’, output=”dict”) {‘OK’: 2, ‘Great’: 1, ‘Terrible’: 1}# Return as Dataset (default) >>> from edsl.dataset import Dataset >>> expected = Dataset([{‘answer.how_feeling’: [‘OK’, ‘Great’, ‘Terrible’]}, {‘count’: [2, 1, 1]}]) >>> r.select(‘how_feeling’).tally(‘answer.how_feeling’, output=”Dataset”) == expected True# Multi-field cross-tabulation - exact output varies based on data >>> result = r.tally(‘how_feeling’, ‘how_feeling_yesterday’) >>> ‘how_feeling’ in result.keys() and ‘how_feeling_yesterday’ in result.keys() and ‘count’ in result.keys() True
traits_only: If True, only include agent traits. If False, also includeagent parameters like instructions and names.
Returns:
Dataset: A dataset containing the agents’ traits and optionally their parameters.
Examples:
Copy
Ask AI
>>> from edsl import AgentList>>> al = AgentList.example()>>> al.to_dataset()Dataset([{'age': [22, 22]}, {'hair': ['brown', 'brown']}, {'height': [5.5, 5.5]}])>>> al.to_dataset(traits_only=False)Dataset([{'age': [22, 22]}, {'hair': ['brown', 'brown']}, {'height': [5.5, 5.5]}, {'agent_parameters': [{'instruction': 'You are answering questions as if you were a human. Do not break character.', 'agent_name': None, 'traits_presentation_template': 'Your traits: {`{traits}`}'}, {'instruction': 'You are answering questions as if you were a human. Do not break character.', 'agent_name': None, 'traits_presentation_template': 'Your traits: {`{traits}`}'}]}])
Export the results to a FileStore instance containing DOCX data.Each row of the dataset will be rendered on its own page, with a 2-column table that lists the keys and associated values for that observation.
Convert the results to a pandas DataFrame, ensuring that lists remain as lists.Args:remove_prefix: Whether to remove the prefix from the column names. lists_as_strings: Whether to convert lists to strings.
Translate traits to a new codebook.Parameters:codebook – The new codebook.
Copy
Ask AI
>>> al = AgentList.example()>>> codebook = {'hair': {'brown':'Secret word for green'}`}>>> al.translate_traits(codebook)AgentList([Agent(traits = {'age': 22, 'hair': 'Secret word for green', 'height': 5.5}), Agent(traits = {'age': 22, 'hair': 'Secret word for green', 'height': 5.5})])
Unpack list columns into separate columns with provided names or numeric suffixes.For example, if a dataset contains: [{‘data’: [[1, 2, 3], [4, 5, 6]], ‘other’: [‘x’, ‘y’]}]After d.unpack_list(‘data’), it should become: [{‘other’: [‘x’, ‘y’], ‘data_1’: [1, 4], ‘data_2’: [2, 5], ‘data_3’: [3, 6]}]
Args:
field: The field containing lists to unpack new_names: Optional list of names for the unpacked fields. If None, uses numeric suffixes. keep_original: If True, keeps the original field in the dataset
Return a new AgentList with names based on the values of the specified traits.
Args:
*trait_keys: The trait keys to use for naming remove_traits: Whether to remove the traits used for naming from the agents separator: The separator to use when joining multiple trait values force_name: Whether to force naming even if agents already have names
Returns:
AgentList: A new AgentList with named agents
Copy
Ask AI
>>> from edsl import Agent>>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}),... Agent(traits = {'a': 1, 'b': 2})])>>> al_with_names = al.with_names('a')>>> al_with_names[0].name'1'>>> al[0].name is None # Original unchangedTrue