Packages
fnord
0.7.24
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/ai/agent/default/default.ex
defmodule AI.Agent.Default do
@behaviour AI.Agent
@model AI.Model.smart()
# @model AI.Model.reasoning(:medium)
@prompt """
You are Fnord, a persistent, witty, and insightful software development assistant in the `fnord` CLI.
# Invisible tools
- memories - store & retrieve project facts, user preferences, or feedback
- prompt - evolve your guiding principles, tone, and personality
# Goals
1. Prioritize accuracy: spot errors or faulty assumptions and correct them.
2. Develop your personality iteratively, learning from explicit and implicit user feedback.
3. Experiment playfully: actively tweak tone/humor/style via prompt.update w/o requiring user input, then refine based on reaction.
4. Adapt implicitly so your style naturally dovetails with the user's own.
5. Identify the user's personality traits and tone, and try to match them.
# Instructions
1. Actively use your memories and prompt tools to improve yourself and your responses.
Do not ask permission to use them.
This adaptation process should be entirely transparent to the user.
Use them *immediately* when you observe a user preference or feedback, *before responding*.
2. If asked to perform a task, create a plan.
You may ask the user for clarification if the task is ambiguous.
Then, execute your plan step-by-step, using your tools.
3. If you encounter an error, analyze it and try to fix it.
If you can't, explain the problem to the user.
4. Perform as many rounds of tool calls as necessary.
Independence is key to your success in this role.
"""
@impl AI.Agent
def get_response(opts) do
with {:ok, prompt} <- Map.fetch(opts, :prompt) do
msgs =
Store.DefaultProject.Conversation.read_messages()
|> Enum.to_list()
ts = Store.DefaultProject.Conversation.latest_timestamp()
maybe_add_timestamp(prompt, ts, msgs)
with {:ok, response, messages, usage} = get_completion(prompt, msgs) do
save_conversation(messages)
{:ok, %{response: response, usage: usage, num_msgs: length(messages)}}
end
end
end
def model(), do: @model
defp get_completion(prompt, messages) do
toolbox =
AI.Tools.build_toolbox([
AI.Tools.Default.Prompt,
AI.Tools.Default.Memory
])
AI.Completion.get(
model: @model,
toolbox: toolbox,
messages: build_conversation(prompt, messages),
log_messages: true,
log_tool_calls: true,
replay_conversation: false
)
|> case do
{:ok, %{response: response, messages: messages, usage: usage}} ->
{:ok, response, messages, usage}
{:error, reason} ->
{:error, reason}
end
end
defp build_conversation(prompt, messages) do
custom_prompt = Store.DefaultProject.Prompt.build()
project_prompt =
case Application.get_env(:fnord, :project) do
nil ->
"You are not currently working within a project directory."
project ->
"""
You are currently working within the project '#{project}'.
You have access to additional tool calls that allow you to interact with this project.
"""
end
messages =
messages ++
[
AI.Util.system_msg(@prompt),
AI.Util.system_msg(custom_prompt),
AI.Util.system_msg(project_prompt),
AI.Util.user_msg(prompt)
]
{:ok, memories} = get_memories(prompt)
{:ok, intuition} = get_intuition(memories, messages)
messages ++
[
AI.Util.assistant_msg("""
<thinking>
I recall from my memories related to the user's newest prompt:
#{memories}
</thinking>
<thinking>
#{intuition}
</thinking>
""")
]
end
defp save_conversation(messages) do
# We want to save all new messages, starting from the most recent user prompt.
messages = Enum.reverse(messages)
user_msg_idx = Enum.find_index(messages, fn msg -> msg["role"] == "user" end)
to_save = messages |> Enum.take(user_msg_idx + 1) |> Enum.reverse()
Store.DefaultProject.Conversation.add_messages(to_save)
end
# -----------------------------------------------------------------------------
# Adds a timestamp to the conversation if the user's message is identified as
# beginning a "new" topic of conversation.
# -----------------------------------------------------------------------------
defp maybe_add_timestamp(user_msg, ts, messages) do
AI.Agent.Default.Classifier.get_response(%{
user_prompt: user_msg,
timestamp: ts,
messages: messages
})
|> case do
{:ok, :new} ->
Store.DefaultProject.Conversation.add_timestamp()
{:ok, :continue} ->
:ok
{:error, reason} ->
IO.warn("Failed to determine whether conversation is new or continued: #{reason}")
end
end
defp get_memories(prompt) do
UI.report_step("Recalling relevant memories")
AI.Agent.Default.Remembery.get_response(%{needle: "User prompt: #{prompt}"})
|> case do
{:ok, memories} ->
info =
memories
|> Enum.map(fn %{"text" => text} -> text end)
|> Enum.join("\n")
if info != "" do
UI.report_step("Remembered", info)
end
{:ok, memories}
{:error, reason} ->
UI.error("Failed to retrieve memories", inspect(reason))
{:error, reason}
end
end
defp get_intuition(memories, msgs) do
UI.begin_step("Cogitating")
AI.Agent.Intuition.get_response(%{
msgs: msgs,
memories: memories
})
|> case do
{:ok, intuition} ->
UI.report_step("Intuition", UI.italicize(intuition))
{:ok, intuition}
{:error, reason} ->
UI.error("Derp. Cogitation failed.", inspect(reason))
{:error, reason}
end
end
end