Developing a Fixie Agent
The Fixie platform enables you to build agents in any language by writing a service that conforms to the protocol described in Agent Protocol. For Python developers, we provide a Python library for implementing agents using the API described below.
For a quick start on building your own Fixie agent, review the Quick Start guide.
See Fixie Agent Python API for the full API reference.
CodeShotAgent
The base class for agents in Fixie is CodeShotAgent
. This class handles communication with the Fixie platform via the Agent Protocol and provides a simple API for registering functions that can be invoked by the few-shot examples used by the agent.
A typical CodeShotAgent
structure looks like this:
import fixieai
BASE_PROMPT = "Base prompt for the agent."
FEW_SHOTS = """
Q: Example query for the agent
Ask Func[my_func]: query to the func
Func[my_func] says: response to the query
A: Response generated by the agent
Q: Second example query to the agent
A: Second response generated by the agent
"""
agent = fixieai.CodeShotAgent(BASE_PROMPT, FEW_SHOTS)
@agent.register_func()
def my_func(query: fixieai.Message) -> str:
return "Response to the query"
The BASE_PROMPT
and FEW_SHOTS
strings provide examples for the underlying Large Language Model (LLM), such as GPT-4, as well as supply the Fixie Platform with information about the types of queries this agent can support.
FEW_SHOTS
must be a string composed of one or more stanzas, where each stanza consists of a question, one or more rounds of internal actions taken by the agent, and a final answer. Stanzas must be separated by a blank line. The query line in the stanza must start with Q:
, and the answer line must start with A:
.
Internal actions taken by the agent can be one of two forms:
Ask Func[<func_name>]: <query_text>
: This indicates that the function<func_name>
should be invoked when the output of the underlying LLM starts with this string. The string followingAsk Func[<func_name>]:
is passed to the function as thequery.text
parameter.Ask Agent[<agent_name>]: <query_text>
: This indicates that the agent<agent_name>
should be invoked when the output of the underlying LLM starts with this string. The string followingAsk Agent[<agent_name>]:
is passed to the agent as thequery.text
parameter.
Agent Funcs
A Func is a Python function that the agent can invoke. (To implement Funcs in languages other than Python, create a service that implements the Fixie Agent Protocol.) When the language model for an agent emits the token Ask Func[<func_name>]
, the function <func_name>
will be invoked.
The register_func
decorator is used to register a function for invocation by the agent.
A function registered with the register_func
decorator has the signature:
@agent.register_func()
def my_func(query, user_storage=None, oauth_handler=None):
...
The query
parameter is either a str
or a Message
object. If the query parameter is a string, this parameter contains the text of the agent query. If the query parameter is a Message
object, this parameter contains the text of the agent along with zero or more Embed
objects, as described in the Embeds section below.
The optional user_storage
parameter provides the Func an interface to the Fixie User Storage service, as described below.
The optional oauth_handler
parameter provides the Func an interface for performing OAuth authentication with external services, as described in the OAuth section below.
The function must return either a str
or a Message
object. Returning a string is equivalent to returning a Message
object with the string as its text
field and no embeds
.
Embeds
Embeds enable the association of arbitrary binary data with a query or response Message in Fixie, similar to email attachments. Embeds can be used to store images, videos, text, or any other binary data.
Embeds are represented by the Embed
class. Agents can access the Embeds associated with a Message as follows:
@agent.register_func()
def my_func(query: fixieai.Message) -> str:
for key, embed in query.embeds.items():
print(f"Embed key: {key}")
print(f"Embed content-type: {embed.content_type}")
embed_value_as_text = embed.text
embed_value_as_bytes = embed.content
An agent function can also add an Embed to its response Message by adding it to the embeds
dictionary of the Message
object:
@agent.register_func()
def my_func(query: fixieai.Message) -> fixieai.Message:
reply = fixieai.Message("Response to the query")
reply.embeds["my_embed"] = fixieai.Embed(content_type="text/plain")
reply.embeds["my_embed"].text = "Hello, world!"
Embeds can be queried by leveraging the built-in fixie_query_embed
func. e.g., Ask Func[fixie_query_embed]
. See example of how this works.
External Knowledge Base Support
Fixie agents come with built-in support for indexing and querying external knowledge in the form of URLs. See the support agent for an example of how to use this.
URLS = [
"https://docs.fixie.ai/*",
]
DOCS = [fixieai.DocumentCorpus(urls=URLS)]
agent = fixieai.CodeShotAgent(BASE_PROMPT, FEW_SHOTS, DOCS)
Appending *
to the end of the URL will tell Fixie to automatically index all children of the root URL.
After indexing, you can query the corpus using the built-in Ask Func[fixie_query_corpus]
function.
Note: It can take up to 10 minutes to index content, depending on the number of URLs and content size. We're working on a mechanism to alert you to indexing progress. Stay tuned!
User Storage
Fixie agents can store and retrieve arbitrary data associated with a user using the UserStorage
class. This class provides a simple interface to a persistent key/value storage service, with a separate key/value store for each Fixie user. This can be used to maintain state about a particular user that persists across agent invocations.
The UserStorage
instance for a given query can be obtained by providing a user_storage
parameter to an agent function. The UserStorage
object acts as a Python dict
that stores state associated with an arbitrary string key. UserStorage
values may consist of Python primitive types, such as str
, int
, float
, bool
, None
, or bytes
, as well as lists of these types, or a dict
mapping a str
to one of these types.
User Storage Example
@agent.register_func()
def my_func(query: fixieai.Message, user_storage: fixieai.UserStorage) -> str:
user_storage["my_key"] = "my_value"
return user_storage["my_key"]
Agent OAuth Support
Fixie Agents can authenticate to third-party services to perform actions on behalf of the user. This is done using OAuth 2.0, a standard protocol for authorization. OAuth 2.0 allows users to grant limited access to their accounts on one service, to another service, without having to share their password.
Fixie provides a simple interface for agents to perform OAuth authentication, using the OAuthParams
class. Using this class, an agent function can use the OAuthHandler
class, passed to the function as the oauth_handler
parameter, to obtain an access token for the user.
OAuth Example
import fixieai
oauth_params = fixieai.OAuthParams(
client_id="XXXXX.apps.googleusercontent.com",
auth_uri="https://accounts.google.com/o/oauth2/auth",
token_uri="https://oauth2.googleapis.com/token",
client_secret="XXXXXXXXX",
scopes=["https://www.googleapis.com/auth/calendar.events"]
)
agent = fixieai.CodeShotAgent(BASE_PROMPT, FEW_SHOTS, oauth_params=oauth_params)
@agent.register_func
def my_func(query, oauth_handler: fixieai.OAuthHandler):
user_token = oauth_handler.user_token()
if user_token is None:
# Return the URL that the user should click on to authorize the agent.
return oauth_handler.get_authorization_url()
# Do something with the user_token returned by the OAuth handler.
client = gcalendar_client.GcalendarClient(user_token)
# ...
Default Agent Model and Model Parameters
By default, agents use the text-davinci-003
model from OpenAI with a default temperature of 0
and a maximum_tokens
size of 1,000. We've found this to be the right default for many use cases, but it's easy to change by passing llm_settings
when initializing your CodeShotAgent
.
Example:
agent = fixieai.CodeShotAgent(
BASE_PROMPT,
[],
conversational=True,
llm_settings=fixieai.LlmSettings(temperature=1.0, model="openai/gpt-4", maximum_tokens=500),
)
Supported Models:
Model Name | Identifier String |
---|---|
GPT-3 | openai/text-davinci-003 (default) |
GPT-3.5 (ChatGPT) | openai/gpt-3.5-turbo |
GPT-4 | openai/gpt-4 |
AI21 J2 Grande | ai21/j2-grande |
AI21 J2 Jumbo | ai21/j2-jumbo |
GooseAI GPTJ 6B | gooseai/gpt-j-6b |
GooseAI GPT NEO 20B | gooseai/gpt-neo-20b |
Built-In Functions
All Fixie agents have access to the following built-in functions that they can invoke:
Ask Func[fixie_base_prompt]
: Returns the base prompt for the agent.Ask Func[fixie_local_datetime]
: Returns the current date and time in the user's local timezone.Ask Func[fixie_utc_datetime]
: Returns the current date and time in the UTC timezone.Ask Func[fixie_query_embed]
: Executes the prompt in the query against the contents of the embed.Ask Func[fixie_query_corpus]
: Executes the prompt in the query against the contents of the agent-defined corpus.