fix(provider): optimize ToolCallIDTemplate generation while product starting

- Modified prompt templates to enforce completion requirements for function calls.
This commit is contained in:
Dmitry Ng
2026-03-22 01:25:28 +03:00
parent 47ae6fe39e
commit 55e0ac5c2f
15 changed files with 131 additions and 17 deletions
+3 -1
View File
@@ -20,6 +20,8 @@ var configFS embed.FS
const AnthropicAgentModel = "claude-sonnet-4-20250514"
const AnthropicToolCallIDTemplate = "toolu_{r:24:b}"
func BuildProviderConfig(configData []byte) (*pconfig.ProviderConfig, error) {
defaultOptions := []llms.CallOption{
llms.WithModel(AnthropicAgentModel),
@@ -177,5 +179,5 @@ func (p *anthropicProvider) GetUsage(info map[string]any) pconfig.CallUsage {
}
func (p *anthropicProvider) GetToolCallIDTemplate(ctx context.Context, prompter templates.Prompter) (string, error) {
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter)
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter, AnthropicToolCallIDTemplate)
}
+3 -1
View File
@@ -30,6 +30,8 @@ var configFS embed.FS
const BedrockAgentModel = bedrock.ModelAnthropicClaudeSonnet4
const BedrockToolCallIDTemplate = "tooluse_{r:22:x}"
func BuildProviderConfig(configData []byte) (*pconfig.ProviderConfig, error) {
defaultOptions := []llms.CallOption{
llms.WithModel(BedrockAgentModel),
@@ -248,7 +250,7 @@ func (p *bedrockProvider) GetUsage(info map[string]any) pconfig.CallUsage {
}
func (p *bedrockProvider) GetToolCallIDTemplate(ctx context.Context, prompter templates.Prompter) (string, error) {
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter)
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter, BedrockToolCallIDTemplate)
}
func extractToolsFromOptions(options []llms.CallOption) []llms.Tool {
+1 -1
View File
@@ -183,5 +183,5 @@ func (p *customProvider) GetUsage(info map[string]any) pconfig.CallUsage {
}
func (p *customProvider) GetToolCallIDTemplate(ctx context.Context, prompter templates.Prompter) (string, error) {
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter)
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter, "")
}
+3 -1
View File
@@ -21,6 +21,8 @@ var configFS embed.FS
const DeepSeekAgentModel = "deepseek-chat"
const DeepSeekToolCallIDTemplate = "call_{r:2:d}_{r:24:b}"
func BuildProviderConfig(configData []byte) (*pconfig.ProviderConfig, error) {
defaultOptions := []llms.CallOption{
llms.WithModel(DeepSeekAgentModel),
@@ -175,5 +177,5 @@ func (p *deepseekProvider) GetUsage(info map[string]any) pconfig.CallUsage {
}
func (p *deepseekProvider) GetToolCallIDTemplate(ctx context.Context, prompter templates.Prompter) (string, error) {
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter)
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter, DeepSeekToolCallIDTemplate)
}
+3 -1
View File
@@ -25,6 +25,8 @@ const GeminiAgentModel = "gemini-2.5-flash"
const defaultGeminiHost = "generativelanguage.googleapis.com"
const GeminiToolCallIDTemplate = "{r:8:x}"
func BuildProviderConfig(configData []byte) (*pconfig.ProviderConfig, error) {
defaultOptions := []llms.CallOption{
llms.WithModel(GeminiAgentModel),
@@ -186,5 +188,5 @@ func (p *geminiProvider) GetUsage(info map[string]any) pconfig.CallUsage {
}
func (p *geminiProvider) GetToolCallIDTemplate(ctx context.Context, prompter templates.Prompter) (string, error) {
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter)
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter, GeminiToolCallIDTemplate)
}
+3 -1
View File
@@ -21,6 +21,8 @@ var configFS embed.FS
const GLMAgentModel = "glm-4.7-flashx"
const GLMToolCallIDTemplate = "call_-{r:19:d}"
func BuildProviderConfig(configData []byte) (*pconfig.ProviderConfig, error) {
defaultOptions := []llms.CallOption{
llms.WithModel(GLMAgentModel),
@@ -173,5 +175,5 @@ func (p *glmProvider) GetUsage(info map[string]any) pconfig.CallUsage {
}
func (p *glmProvider) GetToolCallIDTemplate(ctx context.Context, prompter templates.Prompter) (string, error) {
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter)
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter, GLMToolCallIDTemplate)
}
+24 -2
View File
@@ -1,15 +1,17 @@
simple:
model: "kimi-k2-turbo-preview"
temperature: 0.6
temperature: 0.3
n: 1
max_tokens: 8192
extra_body:
tool_choice: "auto"
price:
input: 1.15
output: 8.0
simple_json:
model: "kimi-k2-turbo-preview"
temperature: 0.6
temperature: 0.3
n: 1
max_tokens: 4096
json: true
@@ -22,6 +24,8 @@ primary_agent:
temperature: 1.0
n: 1
max_tokens: 16384
extra_body:
tool_choice: "auto"
price:
input: 0.6
output: 3.0
@@ -31,6 +35,8 @@ assistant:
temperature: 1.0
n: 1
max_tokens: 16384
extra_body:
tool_choice: "auto"
price:
input: 0.6
output: 3.0
@@ -40,6 +46,8 @@ generator:
temperature: 1.0
n: 1
max_tokens: 32768
extra_body:
tool_choice: "auto"
price:
input: 0.6
output: 3.0
@@ -49,6 +57,8 @@ refiner:
temperature: 1.0
n: 1
max_tokens: 20480
extra_body:
tool_choice: "auto"
price:
input: 0.6
output: 3.0
@@ -67,6 +77,8 @@ reflector:
temperature: 0.7
n: 1
max_tokens: 4096
extra_body:
tool_choice: "auto"
price:
input: 0.6
output: 2.5
@@ -76,6 +88,8 @@ searcher:
temperature: 0.7
n: 1
max_tokens: 4096
extra_body:
tool_choice: "auto"
price:
input: 0.6
output: 2.5
@@ -85,6 +99,8 @@ enricher:
temperature: 0.7
n: 1
max_tokens: 4096
extra_body:
tool_choice: "auto"
price:
input: 0.6
output: 2.5
@@ -94,6 +110,8 @@ coder:
temperature: 1.0
n: 1
max_tokens: 20480
extra_body:
tool_choice: "auto"
price:
input: 0.6
output: 3.0
@@ -103,6 +121,8 @@ installer:
temperature: 0.7
n: 1
max_tokens: 16384
extra_body:
tool_choice: "auto"
price:
input: 1.15
output: 8.0
@@ -112,6 +132,8 @@ pentester:
temperature: 0.8
n: 1
max_tokens: 16384
extra_body:
tool_choice: "auto"
price:
input: 1.15
output: 8.0
+3 -1
View File
@@ -21,6 +21,8 @@ var configFS embed.FS
const KimiAgentModel = "kimi-k2-turbo-preview"
const KimiToolCallIDTemplate = "{f}:{r:1:d}"
func BuildProviderConfig(configData []byte) (*pconfig.ProviderConfig, error) {
defaultOptions := []llms.CallOption{
llms.WithModel(KimiAgentModel),
@@ -175,5 +177,5 @@ func (p *kimiProvider) GetUsage(info map[string]any) pconfig.CallUsage {
}
func (p *kimiProvider) GetToolCallIDTemplate(ctx context.Context, prompter templates.Prompter) (string, error) {
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter)
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter, KimiToolCallIDTemplate)
}
+1 -1
View File
@@ -295,5 +295,5 @@ func (p *ollamaProvider) GetUsage(info map[string]any) pconfig.CallUsage {
}
func (p *ollamaProvider) GetToolCallIDTemplate(ctx context.Context, prompter templates.Prompter) (string, error) {
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter)
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter, "")
}
+3 -1
View File
@@ -20,6 +20,8 @@ var configFS embed.FS
const OpenAIAgentModel = "o4-mini"
const OpenAIToolCallIDTemplate = "call_{r:24:b}"
func BuildProviderConfig(configData []byte) (*pconfig.ProviderConfig, error) {
defaultOptions := []llms.CallOption{
llms.WithModel(OpenAIAgentModel),
@@ -169,5 +171,5 @@ func (p *openaiProvider) GetUsage(info map[string]any) pconfig.CallUsage {
}
func (p *openaiProvider) GetToolCallIDTemplate(ctx context.Context, prompter templates.Prompter) (string, error) {
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter)
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter, OpenAIToolCallIDTemplate)
}
+59 -2
View File
@@ -46,6 +46,44 @@ func storeInCache(provider Provider, template string) {
cacheTemplates.Store(provider.Type(), template)
}
// testTemplate validates a template by collecting a single sample from the LLM
// and checking if it matches the provided template pattern.
// Returns true if the template is valid, false otherwise (including any errors).
// This function makes a real LLM call to collect samples for validation.
func testTemplate(
ctx context.Context,
provider Provider,
opt pconfig.ProviderOptionsType,
prompter templates.Prompter,
template string,
) bool {
// If no template provided, skip validation
if template == "" {
return false
}
// Collect one sample to validate the template
samples, err := runToolCallIDCollector(ctx, provider, opt, prompter)
if err != nil {
// Any error means validation failed
return false
}
// If no samples collected, validation failed
if len(samples) == 0 {
return false
}
// Validate the template against collected samples
if err := templates.ValidatePattern(template, samples); err != nil {
// Template doesn't match
return false
}
// Template validated successfully
return true
}
// DetermineToolCallIDTemplate analyzes tool call ID format by collecting samples
// and using AI to detect the pattern, with fallback to heuristic analysis
func DetermineToolCallIDTemplate(
@@ -53,6 +91,7 @@ func DetermineToolCallIDTemplate(
provider Provider,
opt pconfig.ProviderOptionsType,
prompter templates.Prompter,
defaultTemplate string,
) (string, error) {
ctx, observation := obs.Observer.NewObservation(ctx)
agent := observation.Agent(
@@ -84,6 +123,12 @@ func DetermineToolCallIDTemplate(
return wrapEndAgentSpan(template, "found in cache", nil)
}
// Step 0.5: Test default template if provided (makes one LLM call for validation)
if defaultTemplate != "" && testTemplate(ctx, provider, opt, prompter, defaultTemplate) {
storeInCache(provider, defaultTemplate)
return wrapEndAgentSpan(defaultTemplate, "validated default template", nil)
}
// Step 1: Collect 5 sample tool call IDs in parallel
samples, err := collectToolCallIDSamples(ctx, provider, opt, prompter)
if err != nil {
@@ -244,9 +289,15 @@ func runToolCallIDCollector(
// Call LLM with tool
messages := []llms.MessageContent{
{
Role: llms.ChatMessageTypeHuman,
Role: llms.ChatMessageTypeSystem,
Parts: []llms.ContentPart{llms.TextContent{Text: prompt}},
},
{
Role: llms.ChatMessageTypeHuman,
Parts: []llms.ContentPart{llms.TextContent{
Text: fmt.Sprintf("Call the %s function", testFunctionName),
}},
},
}
response, err := provider.CallWithTools(ctx, opt, messages, []llms.Tool{testTool}, nil)
@@ -332,9 +383,15 @@ func detectPatternWithAI(
// Call LLM with tool
messages := []llms.MessageContent{
{
Role: llms.ChatMessageTypeHuman,
Role: llms.ChatMessageTypeSystem,
Parts: []llms.ContentPart{llms.TextContent{Text: prompt}},
},
{
Role: llms.ChatMessageTypeHuman,
Parts: []llms.ContentPart{llms.TextContent{
Text: fmt.Sprintf("Submit the detected pattern template for the function %s", patternFunctionName),
}},
},
}
response, err := provider.CallWithTools(ctx, opt, messages, []llms.Tool{patternTool}, nil)
+3 -1
View File
@@ -21,6 +21,8 @@ var configFS embed.FS
const QwenAgentModel = "qwen-plus"
const QwenToolCallIDTemplate = "call_{r:24:h}"
func BuildProviderConfig(configData []byte) (*pconfig.ProviderConfig, error) {
defaultOptions := []llms.CallOption{
llms.WithModel(QwenAgentModel),
@@ -174,5 +176,5 @@ func (p *qwenProvider) GetUsage(info map[string]any) pconfig.CallUsage {
}
func (p *qwenProvider) GetToolCallIDTemplate(ctx context.Context, prompter templates.Prompter) (string, error) {
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter)
return provider.DetermineToolCallIDTemplate(ctx, p, pconfig.OptionsTypeSimple, prompter, QwenToolCallIDTemplate)
}
@@ -14,9 +14,14 @@ DO NOT:
- Describe what you will do instead of doing it
- Explain your reasoning before calling the function
- Ask questions or request clarification
- End your turn without making function calls
- Provide a text response as a substitute for function calls
REQUIRED ACTION: Call {{.FunctionName}} function exactly three times in parallel with value=42.
COMPLETION REQUIREMENT: Your work is NOT complete until you have successfully invoked the {{.FunctionName}} function exactly three times.
DO NOT terminate, finish, or end your response without making these function calls.
Any response that does not include exactly three function calls will be treated as a CRITICAL ERROR.
</mandatory_protocol>
@@ -14,9 +14,14 @@ DO NOT:
- Describe your analysis in plain text
- Explain your reasoning before calling the function
- Ask questions or request clarification
- End your turn without making a function call
- Provide a text response as a substitute for a function call
REQUIRED ACTION: Call {{.FunctionName}} function immediately with the detected pattern template.
COMPLETION REQUIREMENT: Your work is NOT complete until you have successfully invoked the {{.FunctionName}} function.
DO NOT terminate, finish, or end your response without making this function call.
Any response that does not include a function call will be treated as a CRITICAL ERROR and will force a retry.
</mandatory_protocol>
+12 -3
View File
@@ -19,7 +19,7 @@ const ASSISTANT_LOG_TYPENAME = 'AssistantLog';
const MAX_RETRY_DELAY_MS = 30_000;
const STREAMING_CACHE_MAX_ENTRIES = 500;
const STREAMING_CACHE_TTL_MS = 1000 * 60 * 5;
const STREAMING_THROTTLE_MS = 100;
const STREAMING_THROTTLE_MS = 50;
// --- Types ---
@@ -35,8 +35,17 @@ type SubscriptionAction = 'add' | 'create' | 'delete' | 'update';
const EMPTY_LOG_ENTRY: StreamingLogEntry = { message: null, result: null, thinking: null };
const concatStrings = (existing: null | string | undefined, incoming: null | string | undefined): null | string =>
existing && incoming ? `${existing}${incoming}` : (existing ?? incoming ?? null);
const concatStrings = (existing: null | string | undefined, incoming: null | string | undefined): null | string => {
if (existing === null || existing === undefined) {
return incoming ?? null;
}
if (incoming === null || incoming === undefined) {
return existing;
}
return `${existing}${incoming}`;
};
const resolveSubscriptionAction = (name: string): SubscriptionAction => {
if (name.endsWith('Deleted')) {