1const e={frontmatter:{title:"Engineers Should Handle Prompting LLMs (and Prompts Should Live in Your Codebase)",description:"Many prompt engineering tools neither follow developer best practices, nor manage prompts with their LLM calls. We created Mirascope to address this.",date:"2024-03-29",readTime:"20 min read",author:"William Bakst"},tableOfContents:[{id:"our-frustrations-with-developer-tools-for-prompt-engineering",content:"Our Frustrations with Developer Tools for Prompt Engineering",level:2},{id:"how-mirascope-makes-prompt-engineering-intuitive-and-scalable",content:"How Mirascope Makes Prompt Engineering Intuitive and Scalable",level:2},{id:"code-like-you-already-code-with-pythonic-simplicity",content:"Code Like You Already Code, with Pythonic Simplicity",level:3},{id:"built-in-data-validation-for-error-free-prompting",content:"Built-in Data Validation for Error-Free Prompting",level:3},{id:"simplify-llm-interactions-with-decorators-and-integrations",content:"Simplify LLM Interactions with Decorators and Integrations",level:3},{id:"expand-llm-capabilities-with-tools",content:"Expand LLM Capabilities with Tools",level:3},{id:"extract-structured-data-from-llm-generated-text",content:"Extract Structured Data from LLM-Generated Text",level:3},{id:"facilitate-your-prompt-workflows-with-cli-and-ide-support",content:"Facilitate Your Prompt Workflows with CLI and IDE Support",level:3}],content:`Weâve seen many discussions around Large Language Model (LLM) software development allude to a workflow where prompts live apart from LLM calls and are managed by multiple stakeholders, including non-engineers. In fact, many popular LLM development frameworks and libraries are built in a way that _requires_ prompts to be managed separately from their calls. 2 3We think this is an unnecessarily cumbersome approach thatâs not scalable for complex, production-grade LLM software development. 4 5Hereâs why: for anyone developing production-grade LLM apps, prompts that include code will necessarily be a part of your engineering workflow. Therefore, separating prompts from the rest of your codebase, especially from their API calls, means youâre splitting that workflow into different, independent parts. 6 7Separating concerns and assigning different roles to manage each may _seem_ to bring certain efficiencies, for example, easing collaboration between tech and non-tech roles. But **it introduces fundamental complexity that can disrupt the engineering process.** For instance, introducing a change in one place-like adding a new key-value pair to an input for an LLM call-means hunting down that change manually. And then, you will likely _still_ not catch all the errors. 8 9Donât get us wrong. If your prompts are purely text or have very minimal code, then managing them separately from your calls may not have much of an impact. And there are legitimate examples of prompts with minimal or no code, like prompts for ChatGPT. In such cases, managing prompts separately from calls can make sense. 10 11But any enterprise-grade LLM apps require sophisticated prompts, **which means youâll end up writing code for such prompts anyway.** 12 13In fact, **trying to write out that logic in text would be even more complicated.** In our view, code makes prompting both efficient and manageable, as well as the purview of engineers. 14 15Below, we outline how we arrived at this truth, and how the solution weâve developed (a Python-based LLM development library) helps developers manage prompts in the codebase easily and efficiently, making, in our experience, LLM app development faster and more enjoyable. 16 17## Our Frustrations with Developer Tools for Prompt Engineering 18 19Our view on prompting started as we were using an early version of the OpenAI SDK to build out interpretable machine learning tools at a previous company. This was the standard OpenAI API for accessing GPT model functionalities. 20 21Back then we didnât have the benefit of any useful helper libraries, so we wrote all the API code ourselves. This amounted to writing lots of boilerplate to accomplish what seemed like simple tasks. For example, automatically extracting the model configuration (such as constraints) from just the name of the features in a given dataset. This required many prompt iterations and it was a pain to evaluate them. 22 23It was around that time that we began asking ourselves: why arenât there better [developer tools in the prompt engineering 24space](/blog/prompt-engineering-tools)? Is it because people are bringing experimental stuff into production too quickly? Or simply because the space is so new? 25 26The more we worked to develop our LLM applications, the more it was clear that from a software engineer's perspective, the **separation of prompt management from the calls was fundamentally flawed.** It made the actual engineering slow, cumbersome and arguably more error prone. It was almost as if current tools weren't built around developer best practices but rather around Jupyter notebook best practices (if there even is such a thing). 27 28Beyond that, we noticed some other issues: 29 30- Our prompts became unmanageable past two versions. We werenât using a prompt management workflow back then, so implementing changes was a manual process. We started telling colleagues not to touch the code because it might break a function somewhere else. 31- A lot of libraries tried to offer functionality for as many use cases as possible, sometimes making you feel dependent on them. They required y
31ou to do things their way, or youâd have to wait for them to catch up with new features from the LLMs. 32 33All this led us to rethink how prompts should be managed to make developersâ lives easier. In the end, these frustrations boiled over into us wanting to build our own library that approached LLM development in a developer-first way to make LLM app development faster and more enjoyable. This ultimately became [Mirascope](https://github.com/mirascope/mirascope). 34 35## How Mirascope Makes Prompt Engineering Intuitive and Scalable 36 37For us, prompt engineering boils down to the relationship between the prompt and the API call. Mirascope represents what we feel is a best-in-class approach for generating that prompt, taking the LLM response, and tracking all aspects of that flow. 38 39As developers, we want to focus on innovation and creativity, rather than on managing and troubleshooting underlying processes. 40 41To that end, we designed Mirascope with the following features and capabilities to make your prompting more efficient, simpler, and scalable. 42 43### Code Like You Already Code, with Pythonic Simplicity 44 45It was important to us to be able to just code in Python, without having to learn superfluous abstractions or extra, fancy structures that make development more cumbersome than it needs to be. So we designed Mirascope to do just that. 46 47For instance, we donât make you implement directed acyclic graphs in the context of sequencing function calls. We provide code thatâs eminently readable, lightweight, and maintainable. 48 49An example of this is our [\`prompt_template\`](/docs/v1/learn/prompts/#prompt-templates-messages) decorator, which encapsulates as much logic within the prompt as feasible. 50 51Within a decorated function, the return value can be used as the prompt template. The following example requests book recommendations based on particular genre: 52 53\`\`\`python 54from mirascope.core import prompt_template 55 56@prompt_template() 57def book_recommendation_prompt(genre: str) -> str: 58 return f"Recommend a {genre} book" 59 60prompt = book_recommendation_prompt("fantasy") 61print(prompt) 62 63# > [BaseMessageParam(role='user', content='Recommend a fantasy book')] 64 65\`\`\` 66 67You can also provide a string template directly as an argument of the decorator where the function arguments will be injected into the template automatically: 68 69\`\`\`python 70from mirascope.core import prompt_template 71 72@prompt_template("Recommend a {genre} book") 73def book_recommendation_prompt(genre: str): ... 74 75prompt = book_recommendation_prompt("fantasy") 76print(prompt) 77 78# > [BaseMessageParam(role='user', content='Recommend a fantasy book')] 79 80\`\`\` 81 82By default, Mirascope's \`prompt_template\` treats the prompt message as a single user message in order to simplify initial use and implementation for straightforward scenarios. 83 84But you may want to add more context to prompts in the form of different roles, such as system, user, or assistant roles to generate more nuanced responses, as shown here: 85 86\`\`\`python 87from mirascope.core import Messages, prompt_template 88 89@prompt_template() 90def book_recommendation_prompt(genre: str) -> Messages.Type: 91 return [ 92 Messages.System("You are a librarian"), 93 Messages.User(f"Recommend a {genre} book"), 94 ] 95 96prompt = book_recommendation_prompt("fantasy") 97print(prompt) 98 99# > [ 100 101# BaseMessageParam(role='system', content='You are a librarian'), 102 103# BaseMessageParam(role='user', content='Recommend a fantasy book'), 104 105# ] 106 107\`\`\` 108 109Now running this prompt template will automatically parse and structure the prompt as a list of \`BaseMessageParam\` message objects that work across all supported providers. 110 111You can also use all caps keywords in a string template for the same functionality: 112 113\`\`\`python 114from mirascope.core import prompt_template 115 116@prompt_template( 117 """ 118 SYSTEM: You are a librarian 119 USER: Recommend a {genre} book 120 """ 121) 122def book_recommendation_prompt(genre: str): ... 123 124prompt = book_recommendation_prompt("fantasy") 125print(prompt) 126 127# > [ 128 129# BaseMessageParam(role='system', content='You are a librarian'), 130 131# BaseMessageParam(role='user', content='Recommend a fantasy book'),
132 133# ] 134 135\`\`\` 136 137We also provide decorators for turning prompt templates into actual calls to an LLM API. This further reduces the boilerplate needed to write LLM-powered functions: 138 139\`\`\`python 140from mirascope.core import openai 141 142@openai.call("gpt-4o-mini") 143def recommend_book(genre: str) -> str: 144 return f"Recommend a {genre} book" 145 146response = recommend_book("fantasy") 147print(response.content) 148 149# > Sure! I'd be happy to recommend... 150 151\`\`\` 152 153In the above example, we've directly decorated the function, which means the function is now tied to a specific provider. As before, you can always instead use the \`prompt_template\` decorator to write provider-agnostic prompts, which you can then use with the decorator for any supported provider: 154 155\`\`\`python 156from mirascope.core import prompt_template 157 158@prompt_template( 159 """ 160 SYSTEM: You are a librarian 161 USER: Recommend a {genre} book 162 """ 163) 164def book_recommendation_prompt(genre: str): ... 165 166# OpenAI call and response 167 168openai_recommend_book = openai.call( 169 "gpt-4o-mini", 170)(book_recommendation_prompt) 171openai_response = openai_recommend_book("fantasy") 172print(openai_response.content) 173 174# Anthropic call and response 175 176anthropic_recommend_book = anthropic.call( 177 "claude-3-5-sonnet-20240620", 178)(book_recommendation_prompt) 179anthropic_response = anthropic_recommend_book("fantasy") 180print(anthropic_response.content) 181\`\`\` 182 183Writing prompts this way enables endless possibilities and customization, from few-shot prompting to chat interactions with an LLM and so much more. 184 185We also wanted to avoid introducing complexity where it's not absolutely necessary. For example, given a choice in how we would chain together components, we'd prefer relying on native Python rather than relying on something like the pipe moderator. 186 187The goal is to maintain an interface that remains as Pythonic as possible. 188 189**Note** : This isnât to say thereâs one âbestâ way to accomplish chaining, and you're certainly not required to do it exactly as we've shown. For instance, you can also have two separate calls where you pass the output of the first into the second as an argument (rather than calling it inside of the first call). It's not our recommendation since it breaks colocation, but you're free to do what you like. We just have opinionated guidelines, not requirements. 190 191Nevertheless, this approach to chaining encapsulates each step of the process within class methods, allowing for a clean and readable way to sequentially execute tasks that depend on the outcome of previous steps: 192 193\`\`\`python 194from mirascope.core import Messages, openai 195 196@openai.call("gpt-4o-mini") 197def select_chef(food_type: str) -> str: 198 return f"Name a chef who is good at cooking {food_type} food" 199 200@openai.call("gpt-4o-mini") 201def recommend_recipe(ingredient: str, food_type: str) -> str: 202 chef = select_chef(food_type) 203 return "Imagine you are {chef}. Recommend a {food_type} recipe using {ingredient}" 204 205response = recommend_recipe("apples", "japanese") 206print(response.content) 207 208# > Certainly! Here's a delightful Japanese-inspired recipe using apples: ... 209 210\`\`\` 211 212Finally, we show an example of how you can use Mirascope to do few-shot prompting, which provides the language model with a few examples (shots) to help it understand the task and generate better output. 213 214Below are three example sets of book recommendations for different topics to guide the model in understanding the format and type of response expected when asked to recommend books on a new topic, such as "coding." 215 216\`\`\`python 217from mirascope.core import prompt_template 218 219@prompt_template( 220 """ 221 I'm looking for book recommendations on various topics. Here are some examples: 222 223 1. For a topic on 'space exploration', you might recommend: 224 - 'The Right Stuff' by Tom Wolfe 225 - 'Cosmos' by Carl Sagan 226 227 2. For a topic on 'artificial intelligence', you might recommend: 228 - 'Life 3.0' by Max Tegmark 229 - 'Superintelligence' by Nick Bostrom 230 231 3. For a topic on 'historical fiction', you might recommend: 232 - 'The Pillars of the Earth' by Ken Follett 233 - 'Wolf Hall' by Hilary Mantel 234 235 Can you recommend some books on {topic}? 236 """ 237) 238def few_shot_book_recommendation_prompt(topic: str): ... 239\`\`\` 240 241Minimizing complexity lowers the learning curve. In Mirascopeâs case, beyond knowing our library and Python, the only framework to learn is Pydantic for structuring and validating outputs. 242 243### Built-in Data Validation for Error-Free Prompting 244 245We find that high-quality prompts-ones that are type and error checked-lead to more accurate and useful LLM responses, and so data validation is at the heart of what we do. 246 247Automatic validation against predefined schemas is built into the fabric of our framework, allowing you to be more productive rather than having to chase down bugs or code your own basic error handling logic. 248 249For starters, by writing prompts as typed functions, you can easily ensure val
249id and well-formed inputs for you prompts by using Pydantic's [\`validate_call\`](https://docs.pydantic.dev/latest/concepts/validation_decorator/) decorator. We've also written our \`BaseCallResponse\` class as an extension of Pydantic's [\`BaseModel\`](https://docs.pydantic.dev/latest/api/base_model/), and we've implemented first-class support for structuring LLM outputs as BaseModel instances as well. 250 251All of this means: 252 253- Mirascope prompt templates can easily ensure the data is correctly typed before it's processed and sent over to the API, leading to cleaner, more maintainable code. Developers can focus more on the business logic specific to prompting rather than on writing boilerplate. 254- Pydantic easily serializes data both to and from JSON format, which simplifies the process of dumping and saving LLM responses (without any additional tooling). 255- Everything is engineered with editor support in mind, offering autocomplete and proper type hints. 256- It also lets developers define custom validation methods if needed, allowing them to enforce complex rules that go beyond type checks and basic validations. 257 258An example of using Pydantic for enforcing type validation (with graceful error handling) is shown below: 259 260\`\`\`python 261from pydantic import BaseModel, ValidationError 262from mirascope.core import openai 263 264class Book(BaseModel): 265 title: str 266 author: str 267 268@openai.call("gpt-4o-mini", response_model=Book) 269def recommend_book(genre: str) -> str: 270 return f"Recommend a {genre} book" 271 272try: 273 book = recommend_book("fantasy") 274 assert isinstance(book, Book) 275 print(book) 276except ValidationError as e: 277 print(e) 278 # > 1 validation error for Book 279 280 # price 281 282 # Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='standard', input_type=str] 283 284 # For further information visit https://errors.pydantic.dev/2.6/v/float_parsing 285 286\`\`\` 287 288You can also validate data in ways that are difficult if not impossible to code successfully, but that LLMs excel at, such as analyzing sentiment. For instance, you can add Pydanticâs [\`AfterValidator\`](/docs/v1/learn/response_models/#validation-and-error-handling) annotation to Mirascopeâs extracted output as shown below: 289 290\`\`\`python 291from typing import Annotated, Literal 292 293from pydantic import AfterValidator, BaseModel, ValidationError 294from mirascope.core import openai 295 296@openai.call("gpt-4o-mini", response_model=Literal["happy", "sad"]) 297def classify_sentiment(text: str) -> str: 298 return f"Is the following text happy or sad? {text}" 299 300def validate_happy(story: str) -> str: 301 """Confirm that the story is happy.""" 302 assert classify_sentiment(story) == "happy", "Story wasn't happy" 303 return story 304
305@openai.call( 306 model="gpt-4o-mini", 307 response_model=Annotated[str, AfterValidator(validate_happy)], 308) 309def tell_sad_story() -> str: 310 return "Tell me a very sad story." 311 312try: 313 story = tell_sad_story() 314 print(story) 315except ValidationError as e: 316 print(e) 317 # > 1 validation error for Story 318 319 # story 320 321 # Assertion failed, Story wasn't happy. [type=assertion_error, input_value="Once upon a time, there ...er every waking moment.", input_type=str] 322 323 # For further information visit https://errors.pydantic.dev/2.6/v/assertion_error 324 325\`\`\` 326 327### Simplify LLM Interactions with Decorators and Integrations 328 329We believe in freeing you from writing boilerplate to interact with APIs, we we made available a number of arguments for easily modifying and customizing how you use the underlying APIs. 330 331For example, you can set \`stream=True\` in the function decorator, which will generate a stream of chunks (and tools, which will be \`None\` if not using tools): 332 333\`\`\`python 334from mirascope.core import openai 335 336@openai.call("gpt-4o-mini", stream=True) 337def recommend_book(genre: str) -> str: 338 return f"Recommend a {genre} book" 339 340for chunk, _ in recommend_book("fantasy"): 341 print(chunk.content, end="", flush=True) 342\`\`\` 343 344You can also further configure the API call by setting the \`call_params\` argument, meaning that any time the decorated function is called it will use the correct, colocated call parameters: 345 346\`\`\`python 347from mirascope.core import openai 348 349@openai.call("gpt-4o-mini", call_params={"temperature": 0.7}) 350def recommend_book(genre: str) -> str: 351 return f"Recommend a {genre} book" 352 353response = recommend_book("fantasy") 354print(response.content) 355\`\`\` 356 357If a provider has a custom endpoint you can call with their own API (for example, OpenAI compatibility), you can set the \`client\` argument in the decorator to use the custom client. This is true for providers such as [Ollama](https://ollama.com/), [Anyscale](https://www.anyscale.com/), [Together](https://www.together.ai/), [AzureOpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service), and others that support the OpenAI API through a proxy (as well as any other APIs such as Anthropic that have similar support). 358 359\`\`\`python 360from mirascope.core import openai 361from openai import OpenAI 362 363client = OpenAI( 364 base_url="http://localhost:11434/v1", 365 api_key="ollama", # required, but unused 366 367) 368 369@openai.call("gpt-4o-mini", client=client) 370def recommend_book(genre: str) -> str: 371 return f"Recommend a {genre} book" 372 373response = recommend_book("fantasy") 374print(response.content) 375\`\`\` 376 377We also made sure that Mirascope works well with or integrates directly with tools such as Logfire, OpenTelemetry, HyperDX, Langfuse, and more for easily tracking machine learning experiments and visualizing data as well as improving prompt effectiveness through automated refinement and testing. All together these tools work together with Mirascope as [an alternative to LangChain](/blog/langchain-alternatives). 378 379Beyond OpenAI, Mirascope supports currently these [other LLM providers](/docs/v1/learn/calls): 380 381- Anthropic 382- Mistral 383- Google Gemini 384- Groq 385- Cohere 386- LiteLLM 387- Azure AI 388- AWS Bedrock 389 390We've made sure that every example in our documentation shows how to use each provider for easy copy-paste to try it out yourself. 391 392We consistently add support for new providers, so if there is a provider you want to see supported that isn't yet, let us know! 393 394If you want to switch to another model provider (like [Anthropic](/docs/v1/api/core/anthropic/call/) or [Mistral](/docs/v1/api/core/mistral/call/), for instance), you just need to change the decorator and the corresponding call parameters: 395 396\`\`\`python 397from mirascope.core import anthropic 398 399@anthropic.call("claude-3-5-sonnet-20240620") 400def recommend_book(genre: str) -> str: 401 return f"Recommend a {genre} book" 402 403response = recommend_book("fantasy") 404print(response.content) 405\`\`\` 406 407### Expand LLM Capabilities with Tools 408 409Although LLMs are known mostly for text generation, you can provide them with specific tools (also known as [function calling](/docs/v1/learn/tools)) to extend their capabilities. 410 411Examples of what you can do with tools include: 412 413- Granting access to the Bing API or DuckDuckGo Search API for internet search to fetch the latest information on various topics. 414- Providing a secure sandbox environment like Repl.it for dynamically running code snippets provided by users in a coding tutorial platform. 415- Allowing access to the Google Cloud Natural Language API for evaluating customer feedback and reviews to determine sentiment and help businesses quickly identify areas for improvement. 416- Providing a Machine Learning (ML) recommendation engine API for giving personalized content or product recommendations for an e-commerce website, based on natural language interactions with users. 417 418Mirascope lets you easily [define tools](/docs/v1/learn/tools/#basic-usage-and-syntax) by documenting any function using a docstring as shown below. It automatically converts this into a tool, saving you additional work. 419 420\`\`\`python 421from typing import Literal 422 423from mirascope.core import openai, prompt_template 424 425def get_current_weather( 426 location: str, unit: Literal["celsius", "fahrenheit"] = "fahrenheit" 427): 428 """Get the current weather in a given location.""" 429 if "tokyo" in location.lower(): 430 print(f"It is 10 degrees {unit} in Tokyo, Japan") 431 elif "san francisco" in location.lower(): 432 print(f"It is 72 degrees {unit} in San Francisco, CA") 433 elif "paris" in location.lower(): 434 print(f"It is 22 degrees {unit} in Paris, France") 435 else: 436 print("I'm not sure what the weather is like in {location}") 437 438@openai.call(model="gpt-4o", tools=[get_current_weather]) 439def forecast(city: str) -> str: 440 return f"What's the weather in {city}?" 441 442response = forecast("Tokyo") 443if tool := response.tool: 444 tool.call() 445\`\`\` 446 447Mirascope supports Google, ReST, Numpydoc, and Epydoc style docstrings for creating tools. If a particular function doesnât have a docstring, you can define your own \`BaseTool\` class. You can then define the \`call()\` method to attach the docstring-less functionâs functionality to the tool, but use your own custom docstrings: 448 449\`\`\`python 450from mirascope.core import BaseTool 451 452# has no docstings 453 454def get_weather(city: str) -> str: ... 455 456class GetWeather(BaseTool): 457 """Gets the weather in a city.""" 458 459 city: str 460 461 def call(self) -> str: 462 return get_weather(self.city) 463\`\`\` 464 465Tools allow you to dynamically generate prompts based on current or user-specified data such as extracting current weather data in a given city before generating a prompt like, "Given the current weather conditions in Tokyo, what are fun outdoor activities?" 466 467See our [documentation](/docs/v1/learn/tools) for details on generating prompts in this way (for instance, by calling the \`call\` method). 468 469### Extract Structured Data from LLM-Generated Text 470 471LLMs are great at producing conversations in text, which is unstructured information. But many applications need structured data from LLM outputs. Scenarios include: 472 473- Extracting structured information from a PDF invoice (i.e., invoice number, vendor, total charges, taxes, etc.) so that you can automatically insert that information into another system like a CRM or tracking tool, a spreadsheet, etc. 474- Automatically extracting sentiment, feedback categories (product quality, service, delivery, etc.), and customer intentions from customer reviews or survey responses. 475- Pulling out specific medical data such as symptoms, diagnoses, medication names, dosages, and patient history from clinical notes. 476- Extracting financial metrics, stock data, company performance indicators, and market trends from financial reports and news articles. 477 478To handle such scenarios, we support extraction with the [\`response_model\`](/docs/v1/learn/response_models) argument in the decorator, which leverages tools (or optionally \`json_mode=True\`) to reliably extract structured data from the outputs of LLMs according to the schema defined in a Pydantic \`BaseModel\`. In the example below you can see how due dates, priorities, and descriptions are being extracted: 479 480\`\`\`python 481from typing import Literal 482 483from mirascope.core import openai, prompt_template 484from pydantic import BaseModel, Field 485
486class TaskDetails(BaseModel): 487 due_date: str = Field(...) 488 priority: Literal["low", "normal", "high"] = Field(...) 489 description: str = Field(...) 490 491@openai.call( 492 model="gpt-4o", 493 response_model=TaskDetails, 494 call_params={"tool_choice": "required"}, 495) 496def get_task_details(task: str) -> str: 497 return f"Extract the details from the following task: {task}" 498 499task = "Submit quarterly report by next Friday. Task is high priority." 500task_details = get_task_details(task) 501assert isinstance(task_details, TaskDetails) 502print(task_details) 503 504# > due_date='next Friday' priority='high' description='Submit quarterly report' 505 506\`\`\` 507 508You can define schema parameters against which to extract data in Pydanticâs \`BaseModel\` class, by setting certain attributes and fields in that class. Mirascope also lets you set the number of retries to extract data in case a failure occurs. But you also donât have to use a detailed schema like \`BaseModel\` if youâre [extracting built-in types](/docs/v1/learn/response_models/#built-in-types) like strings, integers, booleans, etc. The code sample below shows how extraction for a simple structure like a list of strings doesnât need a full-fledged schema definition. 509 510\`\`\`python 511from mirascope.core import openai 512 513@openai.call(model="gpt-4o", response_model=list[str]) 514def recommend_books(genre: str) -> str: 515 return f"Recommend 3 {genre} books" 516 517books = recommend_books(genre="fantasy") 518print(books) 519 520# > [ 521 522# 'The Name of the Wind by Patrick Rothfuss', 523 524# 'Mistborn: The Final Empire by Brandon Sanderson', 525 526# 'The Way of Kings by Brandon Sanderson' 527 528# ] 529 530\`\`\` 531 532Mirascope makes things as simple as feasible, requiring you to write less code in cases where more code isn't necessary. 533 534### Facilitate Your Prompt Workflows with CLI and IDE Support 535 536As mentioned earlier, our experience with prompts is that they generally become unmanageable after a certain
536number of iterations. Versioning is obviously a good idea, and we see some cloud prompting tools that offer this, but as they donât generally colocate prompts with LLM calls, not all the relevant information gets versioned, unfortunately. 537 538We believe itâs important to colocate as much information with the prompt as feasible, and that it should all be versioned together as a single unit. It's also extremely important that this versioning happens automatically and captures the entire lexical closure (so that changes to dependent functions can be easily identified through the automatic versioning). 539 540Our prompt engineering framework [Lilypad](/docs/lilypad) lets you: 541 542- Write prompts and LLM functions like you normally would 543- Automatically versions any change to the function 544- Automatically traces every call to the function against its version 545- Is fully open-source and can be used locally 546 547This ensures that you can engineer your prompts as part of your standard Git workflow without having to worry about manually tracking minor changes during development. Colleagues can also see everything that was tried as well as the differences between prompts. 548 549By versioning the entire lexical closure and tracing every call automatically, you can continue iterating on your prompts and any other code in your codebase without having to worry about manually tracking the potential impacts of such changes on the outputs of your functions. 550 551If you want to give Mirascope a try, we recommend taking a look at our [documentation](/docs/mirascope). You can also find our source code on [GitHub](https://github.com/mirascope/mirascope). Don't forget to give us a star! 552`};export{e as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.