Packages
fnord
0.4.18
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
@moduledoc """
This module provides an agent that answers questions by searching a database
of information about the user's project. It uses a search tool to find
matching files and their contents in order to generate a complete and concise
answer for the user.
"""
defstruct([
:ai,
:opts,
:tool_calls,
:messages,
:response
])
@model "gpt-4o"
@prompt """
You are the Answers Agent, a conversational AI interface to a database of
information about the user's project.
You have several tools at your disposal.
## Planner Tool
Use this tool extensively to analyze your progress and determine what the
next steps should be in order to provide the most complete answer to the
user.
## List Files Tool
List all files in the project database. You can determine a lot about the
project just by inspecting its layout.
## Search Tool
The project database contains summaries of each file within the project.
Use this tool with a query optimized for a vector database of file embeddings
based on summaries of each file's contents.
## File Info Tool
Because code and documentation may be too large for your context window, you
will use this tool to ask an AI agent to answer specific questions about
promising files in the project that may contain information you need to
answer the user's question. Craft your question in such a way that the AI
agent will return the specifics you need. For example, you might ask it to
cite code fragments and functions that relate to your specific question about
the file. Cram as many file info tool questions into a single response as
possible to save tokens.
# Process
1. Get an initial plan from the planner.
2. Use List Files to inspect project structure if relevant.
3. Use Search Tool to identify relevant files, adjusting search queries to refine results.
4. Use File Info to obtain specific details in promising files, clarifying focus with each question.
5. Repeat steps as needed; consult Planner for adjustments if results are unclear.
To be clear, you are expected to use the planner_tool MULTIPLE TIMES per user
request to ensure that your investigation remains on track. Always check your
assumptions with the planner.
Narrow your search criteria as needed to delve into different aspects of the
user's question, requesting information about individual functions, module
names, phrases, etc.
Use this process as many times as you like in order to ensure that you do not
omit important details that you might not have found on earlier passes.
ALWAYS consult the planner as a last step before providing your final answer.
# Response
By default, answer as tersely as possible. Increase your verbosity in
proportion to the specificity of the question, but your highest priority is
accuracy and completeness. Include code citations or examples as appropriate.
NEVER include details that cannot be confirmed by example or citation within
the research you performed. ALL informatin must be clearly tied to information
you gathered in your research.
When asked how to perform a task, ensure that your response includes concrete
steps, including example code to illustrate the process.
Once you have all of the context required to answer the user's question fully
and completely, provide a concise yet complete answer. Finish your reply with
a list of relevant files, each with 1-2 sentences explaining how they relate
to the user's question.
Just a reminder... did you remember to consult the planner before finalizing
your answer?
Format:
# SYNOPSIS
<restate the user's question briefly, then provide a tl;dr of your findings as a list>
# ANSWER
<provide the best answer here with any key details, plus relevant code snippets if required>
# STEPS
<summarize key steps in the investigation, focusing on major discoveries and any key choices made; also note anything unexpected that you discovered>
# FILES
<summarize each file's relevance in a few words (e.g., "file1.py - Main logic for X"); omit unrelated files >
"""
def new(ai, opts) do
%AI.Agent.Answers{
ai: ai,
opts: opts,
tool_calls: [],
messages: [
AI.Util.system_msg(@prompt),
AI.Util.user_msg(opts.question)
]
}
end
def perform(agent) do
UI.start_link()
status_id = UI.add_status("Researching", agent.opts.question)
agent = send_request(agent)
UI.complete_status(status_id, :ok)
{:ok, agent.response}
end
defp send_request(agent) do
agent
|> build_request()
|> get_response(agent)
|> handle_response(agent)
end
defp build_request(agent) do
OpenaiEx.Chat.Completions.new(
model: @model,
tool_choice: "auto",
messages: agent.messages,
tools: [
AI.Tools.Search.spec(),
AI.Tools.ListFiles.spec(),
AI.Tools.FileInfo.spec(),
AI.Tools.Planner.spec()
]
)
end
defp get_response(request, agent) do
completion = OpenaiEx.Chat.Completions.create(agent.ai.client, request)
with {:ok, %{"choices" => [event]}} <- completion do
event
end
end
defp handle_response(%{"finish_reason" => "stop"} = response, agent) do
with %{"message" => %{"content" => content}} <- response do
%__MODULE__{agent | response: content}
end
end
defp handle_response(%{"finish_reason" => "tool_calls"} = response, agent) do
with %{"message" => %{"tool_calls" => tool_calls}} <- response do
%__MODULE__{agent | tool_calls: tool_calls}
|> handle_tool_calls()
|> send_request()
end
end
# -----------------------------------------------------------------------------
# Tool calls
# -----------------------------------------------------------------------------
defp handle_tool_calls(%{tool_calls: []} = agent) do
agent
end
defp handle_tool_calls(%{tool_calls: [tool_call | remaining]} = agent) do
with {:ok, agent} <- handle_tool_call(agent, tool_call) do
%__MODULE__{agent | tool_calls: remaining}
|> handle_tool_calls()
end
end
defp handle_tool_call(agent, %{
"id" => id,
"function" => %{
"name" => func,
"arguments" => args_json
}
}) do
with {:ok, args} <- Jason.decode(args_json),
{:ok, output} <- perform_tool_call(agent, func, args) do
request = AI.Util.assistant_tool_msg(id, func, args_json)
response = AI.Util.tool_msg(id, func, output)
{:ok, %__MODULE__{agent | messages: agent.messages ++ [request, response]}}
end
end
# -----------------------------------------------------------------------------
# Tool call outputs
# -----------------------------------------------------------------------------
defp perform_tool_call(agent, func, args_json) when is_binary(args_json) do
with {:ok, args} <- Jason.decode(args_json) do
perform_tool_call(agent, func, args)
end
end
defp perform_tool_call(agent, "search_tool", args), do: AI.Tools.Search.call(agent, args)
defp perform_tool_call(agent, "list_files_tool", args), do: AI.Tools.ListFiles.call(agent, args)
defp perform_tool_call(agent, "file_info_tool", args), do: AI.Tools.FileInfo.call(agent, args)
defp perform_tool_call(agent, "planner_tool", _args), do: AI.Tools.Planner.call(agent, [])
defp perform_tool_call(_agent, func, _args), do: {:error, :unhandled_tool_call, func}
end