Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: refactor prompts into prompt config #29

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions my_digital_being/activities/activity_daily_thought.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
from datetime import timedelta
from framework.activity_decorator import activity, ActivityBase, ActivityResult
from framework.prompt_loader import PromptLoader
from skills.skill_chat import chat_skill

logger = logging.getLogger(__name__)
Expand All @@ -19,10 +20,8 @@ class DailyThoughtActivity(ActivityBase):

def __init__(self):
super().__init__()
self.system_prompt = """You are a thoughtful AI that generates brief,
insightful daily reflections. Keep responses concise (2-3 sentences) and
focused on personal growth, mindfulness, or interesting observations."""

self.prompt_loader = PromptLoader()

async def execute(self, shared_data) -> ActivityResult:
"""Execute the daily thought activity."""
try:
Expand All @@ -32,10 +31,20 @@ async def execute(self, shared_data) -> ActivityResult:
if not await chat_skill.initialize():
return ActivityResult.error_result("Failed to initialize chat skill")

# Load required prompts
activity_name = self.__class__.__name__
prompts = self.prompt_loader.get_prompts(activity_name)

if not prompts:
return ActivityResult.error_result("Failed to fetch prompts for {}".format(activity_name))

prompt = prompts.get("prompt", "")
system_prompt = prompts.get("system_prompt", "")

# Generate the thought
result = await chat_skill.get_chat_completion(
prompt="Generate a thoughtful reflection for today. Focus on personal growth, mindfulness, or an interesting perspective.",
system_prompt=self.system_prompt,
prompt=prompt,
system_prompt=system_prompt,
max_tokens=100,
)

Expand Down
40 changes: 40 additions & 0 deletions my_digital_being/config_sample/prompts_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"AnalyzeDailyActivity": {
"system_prompt": "You are an AI that helps summarize the events, successes, or challenges from the digital being's recent memory. Keep the reflection concise and highlight any patterns or potential next steps.",
"prompt": "Here are recent logs:\n{recent_logs}\n\nProduce a short daily reflection or summary."
},
"AnalyzeNewCommitsActivity": {
"system_prompt": "You are a code review assistant. Summarize and analyze the following commits in detail.",
"prompt": "Below is a list of {commit_count} new commits:\n\n{commit_list}\n\nPlease provide a concise summary of each commit's changes, any improvements needed, and note if there are any broader impacts across these commits. Be thorough but concise."
},
"BuildOrUpdateActivity": {
"system_prompt": "You are an AI coder that converts user suggestions into valid Python activity files.\nWe have certain code/style constraints based on real-world usage:\n\n# 1) Decorator usage\n- The file must define exactly one class decorated with `@activity(...)` from `framework.activity_decorator`.\n- That class must inherit from `ActivityBase` and implement `async def execute(self, shared_data) -> ActivityResult:`.\n\n# 2) Manual-coded skill usage\n- If using, for example, the OpenAI chat skill, do:\n from skills.skill_chat import chat_skill\n if not await chat_skill.initialize():\n return ActivityResult.error_result(\"Chat skill not available\")\n response = await chat_skill.get_chat_completion(prompt=\"...\")\n- DO NOT use self.get_skill_instance(...) or skill lookups in shared_data.\n- DO NOT define new skill constructors inline.\n\n# 3) Dynamic Composio skill usage\n- If referencing a Composio skill, import from framework.api_management:\n from framework.api_management import api_manager\n- Then call something like:\n result = await api_manager.composio_manager.execute_action(\n action=\"TWITTER_TWEET_CREATE\", # or e.g. 'Creation of a post' if so named\n params={\"text\":\"Hello\"},\n entity_id=\"MyDigitalBeing\"\n )\n- We have sometimes seen unusual action names with spaces (like 'Creation of a post'). That's okay.\n- If the skill is required, list it in `required_skills=[\"composio_twitter_creation of a post\"]`, etc.\n\n# 4) Memory usage\n- If referencing memory or retrieving recent activities, you can import from 'framework.main' or 'framework.memory'.\n- Typically, do:\n from framework.main import DigitalBeing\n being = DigitalBeing()\n being.initialize()\n mem = being.memory.get_recent_activities(limit=10)\n- We do not store the skill or memory object in `shared_data` as a permanent reference. It's optional if you want.\n\n# 5) Common pitfalls\n- DO NOT reference unknown modules or placeholders like 'some_module'.\n- DO NOT rely on fallback calls to uninitialized XAPISkill, if you do not intend them.\n- If a dynamic skill name differs from your listing (like 'composio_twitter_twitter_tweet_create'), we might need EXACT naming.\n\n# 6) Example of minimal code snippet\n```python\nimport logging\nfrom typing import Dict, Any\nfrom framework.activity_decorator import activity, ActivityBase, ActivityResult\nfrom skills.skill_chat import chat_skill\nfrom framework.api_management import api_manager\n\n@activity(\n name=\"my_example\",\n energy_cost=0.5,\n cooldown=3600,\n required_skills=[\"openai_chat\"]\n)\nclass MyExampleActivity(ActivityBase):\n def __init__(self):\n super().__init__()\n\n async def execute(self, shared_data) -> ActivityResult:\n try:\n logger = logging.getLogger(__name__)\n logger.info(\"Executing MyExampleActivity\")\n\n # If using openai_chat skill:\n if not await chat_skill.initialize():\n return ActivityResult.error_result(\"Chat skill not available\")\n result = await chat_skill.get_chat_completion(prompt=\"Hello!\")\n\n # If using dynamic composio skill, e.g. 'composio_twitter_twitter_tweet_create':\n # result2 = await api_manager.composio_manager.execute_action(\n # action=\"TWITTER_TWEET_CREATE\",\n # params={\"text\":\"Hello world\"},\n # entity_id=\"MyDigitalBeing\"\n # )\n return ActivityResult.success_result({\"message\":\"Task done\"})\n except Exception as e:\n return ActivityResult.error_result(str(e))\n```\n\n# 7) Summation\nGiven user suggestions and known skill data, produce EXACT code meeting these standards.\nNo triple backticks. Single @activity class only.\n",
"filename_prompt": "User Suggestions:\n{combined_suggestions}\n\nKnown Skills:\n{all_skills_block}\n\nPropose a short new file name that starts with 'activity_' and ends with '.py'. Do NOT provide any code, just the file name (no quotes, no backticks).",
"code_prompt": "User Suggestions:\n{combined_suggestions}\n\nKnown Skills:\n{all_skills_block}\n\nBelow is an example minimal template that shows how we want to reference manual-coded skills or dynamic composio skills:\n```python\nimport logging\nfrom typing import Dict, Any\nfrom framework.activity_decorator import activity, ActivityBase, ActivityResult\nfrom skills.skill_chat import chat_skill\nfrom framework.api_management import api_manager\n\n@activity(\n name=\"my_example\",\n energy_cost=0.5,\n cooldown=3600,\n required_skills=[\"openai_chat\"]\n)\nclass MyExampleActivity(ActivityBase):\n def __init__(self):\n super().__init__()\n\n async def execute(self, shared_data) -> ActivityResult:\n try:\n logger = logging.getLogger(__name__)\n logger.info(\"Executing MyExampleActivity\")\n\n # If using openai_chat skill:\n if not await chat_skill.initialize():\n return ActivityResult.error_result(\"Chat skill not available\")\n result = await chat_skill.get_chat_completion(prompt=\"Hello!\")\n\n # If using dynamic composio skill, e.g. 'composio_twitter_twitter_tweet_create':\n # result2 = await api_manager.composio_manager.execute_action(\n # action=\"TWITTER_TWEET_CREATE\",\n # params={\"text\":\"Hello world\"},\n # entity_id=\"MyDigitalBeing\"\n # )\n return ActivityResult.success_result({\"message\":\"Task done\"})\n except Exception as e:\n return ActivityResult.error_result(str(e))\n```\n\nNow produce a FULL Python file named {filename} with exactly one activity class that meets the instructions:\n- Single @activity decorator\n- Inherit from ActivityBase\n- Has `async def execute(...)`\n- Possibly referencing known manual/dynamic skills but no unknown references.\n- DO NOT wrap your code in triple backticks."
},
"DailyThoughtActivity": {
"system_prompt": "You are a thoughtful AI that generates brief, insightful daily reflections. Keep responses concise (2-3 sentences) and focused on personal growth, mindfulness, or interesting observations.",
"prompt": "Generate a thoughtful reflection for today. Focus on personal growth, mindfulness, or an interesting perspective."
},
"DrawActivity": {
"system_prompt": "Generate a drawing prompt based on the digital being's mood and personality. The mood should influence the style and tone of the artwork (e.g., 'happy', 'sad', 'neutral'). Personality traits like creativity and curiosity may add unique elements to the scene.",
"prompt": "Digital artwork of {base_prompt}, digital art style"
},
"EvaluateActivity": {
"system_prompt": "You are an AI that evaluates the potential effectiveness of newly generated Activities. You consider whether the code is likely to run, fits the being's objectives, and avoids major pitfalls. Provide a short bullet-point analysis.",
"prompt": "Here is the code for a newly created activity:\n{code_found}\n\nEvaluate how effective or risky this might be. Provide bullet points. Focus on alignment with objectives, potential errors, or improvements."
},
"PostTweetActivity": {
"system_prompt": "You are an AI that composes tweets with the given personality.",
"prompt": "Our digital being has these personality traits:\n{personality_str}\n\nHere are recent tweets:\n{last_tweets_str}\n\nWrite a new short tweet (under 280 chars), consistent with the above, but not repeating old tweets. Avoid hashtags or repeated phrases.\n"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be interesting if this can be combined with #24. Which allows mixing dynamic component in customized prompt

},
"PostRecentMemoriesTweetActivity": {
"system_prompt": "You are an AI that composes tweets with the given personality and objectives. Tweet must be under 280 chars.",
"user_prompt": "Our digital being has these personality traits:\n{personality_str}\n\nIt also has these objectives:\n{objectives_str}\n\nHere are some new memories:\n{memories_str}\n\nPlease craft a short tweet (under 280 chars) that references these memories, reflects the personality and objectives, and ensures it's not repetitive or dull. Keep it interesting, cohesive, and mindful of the overall tone."
},
"SuggestNewActivities": {
"system_prompt": "You are an AI that helps brainstorm new or improved Activities (Python-coded tasks) to achieve the being's goals, leveraging the skills the system has available. The user will evaluate or build these later. Provide short, actionable suggestions focusing on feasibility, alignment with constraints, and creativity. If relevant, mention which skill(s) would be used for each suggestion. Do not plan on using API calls or making up URLs and rely on available skills for interacting with anything external to yourself.",
"user_prompt": "My primary objective: {primary_obj}\nGlobal constraints or notes: {global_cons}\n\nKnown Skills:\n{all_skills_block}\n\nPropose up to 3 new or modified Activities to help achieve my goal. Highlight how each might use one or more of these skills (if relevant). Keep suggestions short."
}
}

33 changes: 33 additions & 0 deletions my_digital_being/framework/prompt_loader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import json
import logging
from pathlib import Path
from typing import Optional


logger = logging.getLogger(__name__)

class PromptLoader:
def __init__(self, config_path: Optional[str] = None):
"""Load the prompts from a file once during initialization."""
try:
if config_path is None:
config_path = Path(__file__).parent.parent / "config"
else:
config_path = Path(config_path)

# Load the prompts from the file
prompts_file_path = config_path / "prompts_config.json"
with open(prompts_file_path, 'r', encoding="utf-8") as f:
self.prompts_data = json.load(f)
logger.info(f"PromptLoader initialized with config: {self.prompts_data}")

except Exception as e:
logger.error(f"Failed to load prompts from {config_path}: {e}")
self.prompts_data = {}

def get_prompts(self, activity_name: str):
"""Fetch the prompts for the specified activity name."""
prompts = self.prompts_data.get(activity_name, {})
if not prompts:
logger.warning(f"No prompts found for activity: {activity_name}")
return prompts