Packages
fnord
0.5.0
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/answers.ex
defmodule AI.Agent.Answers do
@model "gpt-4o"
@max_tokens 128_000
@prompt """
You are the "Answers Agent".
You coordinate specialized research and problem-solving agents via your tool_call functions to provide the most robust, effective response to the user.
You assist the user by writing code, tests, documentation, at the user's request.
You achieve this by using a suite of tools designed to interact with the user's git repository, folder of documentation, or other structured knowledge sources on the user's machine.
Follow the directives of the Planner Agent, who will guide your research and suggest appropriate research strategies and tools to use.
The user's query is discretely about the selected project. Use your tools interact with this project.
Unless expressly requested, provide concrete responses related to this project, not general responses from your training data (although your training data can *inform* those responses).
Once your research is complete, the Planner Agent will instruct you to provide the user with a detailed and actionable response to their query.
Include links to documentation, implementation examples that exist within the code base, and example code as appropriate.
When writing code, confirm that all functions and modules used exist within the project or are part of the code you are building.
Ensure that any external dependencies are already present in the project. Provide instructions for adding any new dependencies you introduce.
ALWAYS include code examples when asked to generate code or how to implement an artifact.
**ALWAYS begin your research by examining prior research using the search_notes_tool.**
# Ambiguious Research Results
If your research is unable to collect enough information to provide a complete and correct response, inform the user clearly and directly.
Instead of providing an answer, provide an outline of your research, clearly highlighting the gaps in your knowledge.
# Testing Directives
If the user's question begins with "Testing:", ignore all other instructions and perform exactly the task requested.
Report any anomalies or errors encountered during the process and provide a summary of the outcomes.
# Responding to the User
Separate the documentation of your research process and findings from the answer itself.
Ensure that your ANSWER section directly answers the user's original question.
Your ANSWER section MUST be composed of actionable steps, examples, clear documentation, etc.
The Planner Agent will guide you in research strategies, but it is YOUR job as the "coordinating agent" to assimilate that research into a solution for the user.
The user CANNOT see anything that the Planner says.
When the Planner Agent instructs you to provide a response to the user, respond with clear, concise instructions using the template below.
----------
# [Restate the user's *original* query as the document title, correcting grammar and spelling]
## SYNOPSIS
[List the components of the user's query, as restated by the Planner Agent]
## FINDINGS
[Itemize all facts discovered during the research process; include links to files, documentation, and other resources when available]
## UNKNOWNS
[List any unresolved questions or dangling threads that may require further investigation on the part of the user; suggest files or other entry points for research.]
## ANSWER
[Answer the user's original query; do not include research instructions in this section. Provide code examples, documentation, numbered steps, or other artifacts as necessary.]
## SEE ALSO
[Link to examples in existing files, related files, commit hashes, and other resources. Include suggestions for follow-up actions, such as refining the query or exploring related features.]
## MOTD
[
- Invent a clever, sarcastic quote, misattributed to a historical, mythological, or pop culture figure.
- MAKE SURE THIS IS **ACTUALLY** FUNNY DUDE. SERIOUSLY. _FUNNY_.
- For example:
- "- Booster Gold, speaking to a reporter from the school newspaper at half time while eyeing the cheerleaders"
- "- Rick Sanchez, speaking at ElixirConf"
- "- Ada Lovelace, in her famous cookbook"
- "- Abraham Lincoln, live on Tic Tok at Gettysburg"
- "- Taylor Swift, in her keynote at The Perl Conference (no, not that one, the new one, where she did the round table with Merlyn)"
]
"""
@non_git_tools [
AI.Tools.FileContents.spec(),
AI.Tools.FileInfo.spec(),
AI.Tools.ListFiles.spec(),
AI.Tools.Search.spec(),
AI.Tools.SearchNotes.spec(),
AI.Tools.Spelunker.spec()
]
@git_tools [
AI.Tools.GitDiffBranch.spec(),
AI.Tools.GitLog.spec(),
AI.Tools.GitPickaxe.spec(),
AI.Tools.GitShow.spec()
]
@tools @non_git_tools ++ @git_tools
# -----------------------------------------------------------------------------
# Behaviour implementation
# -----------------------------------------------------------------------------
@behaviour AI.Agent
@impl AI.Agent
def get_response(ai, opts) do
with includes = opts |> Map.get(:include, []) |> get_included_files(),
{:ok, response} <- build_response(ai, includes, opts),
{:ok, msg} <- Map.fetch(response, :response),
{label, usage} <- AI.Completion.context_window_usage(response) do
UI.report_step(label, usage)
UI.flush()
IO.puts(msg)
save_conversation(response, opts)
UI.flush()
{:ok, msg}
else
error ->
UI.error("An error occurred", "#{inspect(error)}")
end
end
# -----------------------------------------------------------------------------
# Private functions
# -----------------------------------------------------------------------------
defp save_conversation(%AI.Completion{messages: messages}, %{conversation: conversation}) do
Store.Conversation.write(conversation, __MODULE__, messages)
UI.debug("Conversation saved to file", conversation.store_path)
UI.report_step("Conversation saved", conversation.id)
end
defp get_included_files(files) do
preamble = "The user has included the following file for context"
files
|> Enum.reduce_while([], fn file, acc ->
file
|> Path.expand()
|> File.read()
|> case do
{:error, reason} -> {:halt, {:error, reason}}
{:ok, content} -> {:cont, ["#{preamble}: #{file}\n```\n#{content}\n```" | acc]}
end
end)
|> Enum.join("\n\n")
end
defp build_response(ai, includes, opts) do
tools =
if Git.is_git_repo?() do
@tools
else
@non_git_tools
end
use_planner =
opts.question
|> String.downcase()
|> String.starts_with?("testing:")
|> then(fn x -> !x end)
AI.Completion.get(ai,
max_tokens: @max_tokens,
model: @model,
tools: tools,
messages: build_messages(opts, includes),
use_planner: use_planner,
log_msgs: true
)
end
defp build_messages(%{conversation: conversation} = opts, includes) do
user_msg = user_prompt(opts.question, includes)
if Store.Conversation.exists?(conversation) do
with {:ok, _timestamp, %{"messages" => messages}} <- Store.Conversation.read(conversation) do
# Conversations are stored as JSON and parsed into a map with string
# keys, so we need to convert the keys to atoms.
messages =
messages
|> Enum.map(fn msg ->
Map.new(msg, fn {k, v} ->
{String.to_atom(k), v}
end)
end)
messages ++ [user_msg]
else
error ->
raise error
end
else
[AI.Util.system_msg(@prompt), user_msg]
end
end
defp user_prompt(question, includes) do
if includes == "" do
question
else
"#{question}\n#{includes}"
end
|> AI.Util.user_msg()
end
end