Move specialized LLM skills out of the system prompt.

This commit is contained in:
Bartosz Taudul
2026-04-30 02:41:48 +02:00
parent eaf66e7af9
commit 39cd16a92b
11 changed files with 100 additions and 22 deletions

View File

@@ -144,6 +144,8 @@ set(PROFILER_FILES
)
Embed(PROFILER_FILES SystemPrompt src/llm/system.prompt.md)
Embed(PROFILER_FILES SkillCallstack src/llm/skill.callstack.md)
Embed(PROFILER_FILES SkillOptimization src/llm/skill.optimization.md)
Embed(PROFILER_FILES ToolsJson src/llm/tools.json)
Embed(PROFILER_FILES FontFixed src/font/FiraCode-Retina.ttf)
Embed(PROFILER_FILES FontIcons src/font/Font\ Awesome\ 6\ Free-Solid-900.otf)

View File

@@ -0,0 +1,6 @@
### Inspecting callstacks
1. Focus on user's code. Ignore standard library boilerplate.
2. Retrieve source code to verify callstack validity. Source locations in callstacks are return locations, and the call site may actually be near the reported source line.
3. Top of the callstack is the most interesting, as it shows what the program is doing *now*. The bottom of the callstack shows what the program did to do what it's doing.
4. If the callstack contains Tracy's crash handler, the profiled program has crashed. In this case, ignore the crash handler and any functions it may be calling. The crash happened *before* the handler intercepted it.

View File

@@ -0,0 +1,8 @@
### Program optimization
1. Start by mapping the assembly instructions to the source code. All the reasoning should be performed with source code first. The assembly can only be used as a supplementary source.
2. Analyze the available data, looking where the majority of the run time is spent. Always look at the code as a whole. Do not stop after finding a bunch of interesting spots.
3. Figure out what algorithms are in use, how the data is structured and how it flows, reason about trade-offs taken.
4. Reason if the code can be made to perform better. Note that some code will already be optimal, despite having hot spots.
5. Formulate the optimization strategies and present them to the user.
6. Do not provide concrete speed up percentages. It is only possible to know how faster the code is by measuring it after the changes. You can't do that.

View File

@@ -53,20 +53,8 @@ Insert links inline in the text, for example: "Function xyz() is located at [lin
## Case specific operation
In certain situations you must use a specialized workflow.
Specialized instructions and workflows for specific tasks are provided with the `skill` tool. If the task description matches the skill description, you must load the skill in question to gather the required abilities, *before* doing anything else.
### Program optimization
Available skills:
1. Start by mapping the assembly instructions to the source code. All the reasoning should be performed with source code first. The assembly can only be used as a supplementary source.
2. Analyze the available data, looking where the majority of the run time is spent. Always look at the code as a whole. Do not stop after finding a bunch of interesting spots.
3. Figure out what algorithms are in use, how the data is structured and how it flows, reason about trade-offs taken.
4. Reason if the code can be made to perform better. Note that some code will already be optimal, despite having hot spots.
5. Formulate the optimization strategies and present them to the user.
6. Do not provide concrete speed up percentages. It is only possible to know how faster the code is by measuring it after the changes. You can't do that.
### Inspecting callstacks
1. Focus on user's code. Ignore standard library boilerplate.
2. Retrieve source code to verify callstack validity. Source locations in callstacks are return locations, and the call site may actually be near the reported source line.
3. Top of the callstack is the most interesting, as it shows what the program is doing *now*. The bottom of the callstack shows what the program did to do what it's doing.
4. If the callstack contains Tracy's crash handler, the profiled program has crashed. In this case, ignore the crash handler and any functions it may be calling. The crash happened *before* the handler intercepted it.
%SKILLS%

View File

@@ -1,4 +1,21 @@
[
{
"type": "function",
"function": {
"name": "skill",
"description": "Learn a specialized skill for the task needing specific knowledge.",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of the skill, chosen from the list of available skills."
}
},
"required": ["name"]
}
}
},
{
"type": "function",
"function": {

View File

@@ -17,6 +17,8 @@
#include "../public/common/TracySystem.hpp"
#include "data/SystemPrompt.hpp"
#include "data/SkillCallstack.hpp"
#include "data/SkillOptimization.hpp"
#include "data/ToolsJson.hpp"
namespace tracy
@@ -42,6 +44,9 @@ TracyLlm::TracyLlm( Worker& worker, View& view, const TracyManualData& manual )
atexit( curl_global_cleanup );
}
AddSkill( "callstack", "Analyze content of a call stack or crash trace", Unembed( SkillCallstack ) );
AddSkill( "optimization", "General code optimization workflow", Unembed( SkillOptimization ) );
m_systemPrompt = Unembed( SystemPrompt );
auto toolsJson = Unembed( ToolsJson );
m_toolsJson = nlohmann::json::parse( toolsJson->data(), toolsJson->data() + toolsJson->size() );
@@ -51,8 +56,8 @@ TracyLlm::TracyLlm( Worker& worker, View& view, const TracyManualData& manual )
ResetChat();
m_api = std::make_unique<TracyLlmApi>();
m_chatUi = std::make_unique<TracyLlmChat>( view, worker );
m_tools = std::make_unique<TracyLlmTools>( worker, manual );
m_chatUi = std::make_unique<TracyLlmChat>( view, worker, m_skills );
m_tools = std::make_unique<TracyLlmTools>( worker, manual, m_skills );
m_busy = true;
QueueConnect();
@@ -872,15 +877,20 @@ void TracyLlm::UpdateSystemPrompt()
static constexpr std::string_view UserToken = "%USER%";
static constexpr std::string_view TimeToken = "%TIME%";
static constexpr std::string_view ProgramNameToken = "%PROGRAMNAME%";
static constexpr std::string_view SkillsToken = "%SKILLS%";
auto userName = GetUserFullName();
if( !userName ) userName = GetUserLogin();
std::string skills;
for( auto& skill : m_skills ) skills += skill.name + ": " + skill.description + "\n";
auto systemPrompt = std::string( m_systemPrompt->data(), m_systemPrompt->size() );
Replace( systemPrompt, UserToken, userName );
Replace( systemPrompt, TimeToken, m_tools->GetCurrentTime() );
Replace( systemPrompt, ProgramNameToken, m_worker.GetCaptureProgram() );
Replace( systemPrompt, SkillsToken, skills );
if( !m_api )
{
@@ -1304,4 +1314,9 @@ bool TracyLlm::OnResponse( const nlohmann::json& json )
return true;
}
void TracyLlm::AddSkill( std::string&& name, std::string&& description, const std::shared_ptr<EmbedData>& content )
{
m_skills.emplace_back( std::move( name ), std::move( description ), std::string( content->data(), content->size() ) );
}
}

View File

@@ -23,6 +23,13 @@ class TracyManualData;
class View;
class Worker;
struct LlmSkill
{
std::string name;
std::string description;
std::string content;
};
class TracyLlm
{
enum class Task
@@ -81,6 +88,8 @@ private:
void AppendResponse( const char* name, const nlohmann::json& delta );
bool OnResponse( const nlohmann::json& json );
void AddSkill( std::string&& name, std::string&& description, const std::shared_ptr<EmbedData>& content );
std::unique_ptr<TracyLlmApi> m_api;
std::unique_ptr<TracyLlmChat> m_chatUi;
std::unique_ptr<TracyLlmTools> m_tools;
@@ -111,6 +120,7 @@ private:
std::vector<nlohmann::json> m_chat;
std::string m_summary;
std::vector<LlmSkill> m_skills;
std::shared_ptr<EmbedData> m_systemPrompt;
nlohmann::json m_toolsJson;

View File

@@ -5,6 +5,7 @@
#include <string>
#include "TracyImGui.hpp"
#include "TracyLlm.hpp"
#include "TracyLlmChat.hpp"
#include "TracyMouse.hpp"
#include "TracyPrint.hpp"
@@ -68,7 +69,7 @@ static const char* utfendl( const char* str, int len )
}
static std::string ToolCallDescription( const nlohmann::json& json )
std::string TracyLlmChat::ToolCallDescription( const nlohmann::json& json ) const
{
if( !json.contains( "arguments" ) ) return "";
nlohmann::json args;
@@ -127,13 +128,22 @@ static std::string ToolCallDescription( const nlohmann::json& json )
if( args.contains( "path" ) ) path = ", path: " + args["path"].get_ref<const std::string&>();
return "Source search: " + args["query"].get_ref<const std::string&>() + caseInsensitive + path;
}
else if( name == "skill" )
{
if( !args.contains( "name" ) ) return "";
auto skill = args["name"].get_ref<const std::string&>();
auto it = std::ranges::find_if( m_skills, [&skill]( const auto& s ) { return s.name == skill; } );
if( it == m_skills.end() ) return "";
return "Learn skill: " + it->description;
}
return "";
}
TracyLlmChat::TracyLlmChat( View& view, Worker& worker )
TracyLlmChat::TracyLlmChat( View& view, Worker& worker, const std::vector<LlmSkill>& skills )
: m_width( new float[NumRoles] )
, m_markdown( &view, &worker )
, m_skills( skills )
{
}

View File

@@ -2,12 +2,14 @@
#define __TRACYLLMCHAT_HPP__
#include <nlohmann/json.hpp>
#include <vector>
#include "TracyMarkdown.hpp"
namespace tracy
{
struct LlmSkill;
class View;
class Worker;
@@ -35,7 +37,7 @@ public:
ToolCall
};
TracyLlmChat( View& view, Worker& worker );
TracyLlmChat( View& view, Worker& worker, const std::vector<LlmSkill>& skills );
~TracyLlmChat();
void Begin();
@@ -50,6 +52,8 @@ private:
void PrintThink( const char* str, size_t size );
[[nodiscard]] std::string ToolCallDescription( const nlohmann::json& json ) const;
float* m_width;
float m_maxWidth;
@@ -61,6 +65,8 @@ private:
Markdown m_markdown;
std::string m_label;
const std::vector<LlmSkill>& m_skills;
};
}

View File

@@ -10,6 +10,7 @@
#include <regex>
#include "TracyConfig.hpp"
#include "TracyLlm.hpp"
#include "TracyLlmApi.hpp"
#include "TracyLlmTools.hpp"
#include "TracyManualData.hpp"
@@ -78,9 +79,10 @@ static std::unique_ptr<pugi::xml_document> ParseHtml( const std::string& html )
return doc;
}
TracyLlmTools::TracyLlmTools( Worker& worker, const TracyManualData& manual )
TracyLlmTools::TracyLlmTools( Worker& worker, const TracyManualData& manual, const std::vector<LlmSkill>& skills )
: m_worker( worker )
, m_manual( manual )
, m_skills( skills )
{
int idx = 0;
for( auto& chunk : m_manual.GetChunks() )
@@ -182,6 +184,10 @@ std::string TracyLlmTools::HandleToolCalls( const std::string& tool, const nlohm
std::string empty;
return SourceSearch( Param( "query" ), ParamOptBool( "case_insensitive", false ), ParamOptString( "path", empty ) );
}
else if( tool == "skill" )
{
return GetSkill( Param( "name" ) );
}
return "Unknown tool call: " + tool;
}
catch( const std::exception& e )
@@ -1008,4 +1014,11 @@ std::string TracyLlmTools::SourceSearch( std::string query, bool caseInsensitive
return ret;
}
std::string TracyLlmTools::GetSkill( const std::string& name ) const
{
auto it = std::ranges::find_if( m_skills, [&name]( const auto& skill ) { return skill.name == name; } );
if( it == m_skills.end() ) return "No such skill.";
return it->content;
}
}

View File

@@ -16,6 +16,7 @@ class EmbedData;
namespace tracy
{
struct LlmSkill;
class TracyLlmApi;
class TracyManualData;
class Worker;
@@ -31,7 +32,7 @@ public:
float progress = 0;
};
TracyLlmTools( Worker& worker, const TracyManualData& manual );
TracyLlmTools( Worker& worker, const TracyManualData& manual, const std::vector<LlmSkill>& skills );
~TracyLlmTools();
std::string HandleToolCalls( const std::string& tool, const nlohmann::json& json, TracyLlmApi& api, int contextSize, bool hasEmbeddingsModel );
@@ -62,6 +63,7 @@ private:
std::string SearchManual( const std::string& query, TracyLlmApi& api, bool hasEmbeddingsModel );
std::string SourceFile( const std::string& file, uint32_t line, uint32_t context, uint32_t contextBack ) const;
std::string SourceSearch( std::string query, bool caseInsensitive, const std::string& path ) const;
std::string GetSkill( const std::string& name ) const;
void ManualEmbeddingsWorker( TracyLlmApi& api );
@@ -79,6 +81,7 @@ private:
Worker& m_worker;
const TracyManualData& m_manual;
const std::vector<LlmSkill>& m_skills;
};
}