Centralize active skill field extraction

This commit is contained in:
lky-spec
2026-04-26 20:44:09 +08:00
parent 903eb591eb
commit cfd91069d3
5 changed files with 7 additions and 121 deletions

View File

@@ -44,7 +44,7 @@ func TestHandleModelCreateSkillAsksProviderFirstWithClaw402Recommendation(t *tes
}
}
func TestHandleModelCreateSkillAcceptsBareClaw402PrivateKey(t *testing.T) {
func TestHandleModelCreateSkillUsesCollectedClaw402PrivateKey(t *testing.T) {
dbPath := filepath.Join(t.TempDir(), "agent-model-create-claw402.db")
st, err := store.New(dbPath)
if err != nil {
@@ -57,13 +57,14 @@ func TestHandleModelCreateSkillAcceptsBareClaw402PrivateKey(t *testing.T) {
Action: "create",
Phase: "collecting",
Fields: map[string]string{
"provider": "claw402",
"name": "Claw402 (Base USDC)",
"provider": "claw402",
"name": "Claw402 (Base USDC)",
"api_key": "0x205d759b80bae1afa31a36c4afaeec0b10378c1c55e3363bcde5a1db75c747ca",
"custom_model_name": "deepseek",
},
}
reply := a.handleModelCreateSkill("default", 42, "zh", "0x205d759b80bae1afa31a36c4afaeec0b10378c1c55e3363bcde5a1db75c747ca", session)
reply := a.handleModelCreateSkill("default", 42, "zh", "继续", session)
if strings.Contains(reply, "还缺这些字段:钱包私钥") {
t.Fatalf("expected bare private key to be accepted, got: %s", reply)

View File

@@ -935,7 +935,7 @@ func (a *Agent) tryStatePriorityPath(ctx context.Context, storeUserID string, us
if answer, ok := a.answerSkillSessionExplanation(storeUserID, lang, session, text); ok {
return answer, true, nil
}
decision, extraction := a.resolveSkillSessionTurn(ctx, userID, lang, text, session)
decision, _ := a.resolveSkillSessionTurn(ctx, userID, lang, text, session)
switch decision.Intent {
case "cancel":
a.clearSkillSession(userID)
@@ -947,10 +947,6 @@ func (a *Agent) tryStatePriorityPath(ctx context.Context, storeUserID string, us
answer, handled, err := a.handoffFromActiveFlow(ctx, storeUserID, userID, lang, text, decision.TargetSnapshotID, onEvent)
return answer, handled, err
default:
if extraction.Intent == "continue" {
a.applyLLMExtractionToSkillSession(storeUserID, &session, extraction, lang, text)
a.saveSkillSession(userID, session)
}
if answer, ok := a.dispatchBridgedSkillSession(storeUserID, userID, lang, text, session); ok {
if onEvent != nil && strings.TrimSpace(answer) != "" {
switch session.Name {
@@ -1234,12 +1230,6 @@ func (a *Agent) resolveSkillSessionTurn(ctx context.Context, userID int64, lang,
if isInstantDirectReplyText(text) {
return unifiedFlowDecision{Intent: "instant_reply"}, llmFlowExtractionResult{Intent: "instant_reply"}
}
if a.aiClient != nil {
result := a.extractSkillSessionFieldsWithLLM(ctx, userID, lang, text, session)
if decision := unifiedFlowDecisionFromIntent(result.Intent, result.TargetSnapshotID); decision.Intent != "" {
return decision, result
}
}
return a.classifySkillSessionDecision(ctx, userID, lang, session, text), llmFlowExtractionResult{}
}

View File

@@ -516,38 +516,6 @@ func (a *Agent) handleCreateTraderSkill(storeUserID string, userID int64, lang,
}
}
availableResources := a.buildTraderCreateConversationResources(storeUserID, session)
result := skillConversationResult{}
if a != nil && a.aiClient != nil {
result = a.llmSkillConversationDriver(context.Background(), storeUserID, userID, lang, text, session, availableResources)
} else {
result = a.fallbackTraderCreateConversation(storeUserID, lang, text, session, availableResources)
}
if !result.Cancel && !result.UserRejectedFlow && !result.Ready && result.Question == "" && a != nil && a.aiClient != nil {
if extraction := a.extractSkillSessionFieldsWithLLM(context.Background(), userID, lang, text, session); extraction.Intent == "continue" {
a.applyLLMExtractionToSkillSession(storeUserID, &session, extraction, lang, text)
}
}
if result.Cancel {
a.clearSkillSession(userID)
if lang == "zh" {
return "已取消当前创建交易员流程。", true
}
return "Cancelled the current trader creation flow.", true
}
if result.UserRejectedFlow {
return a.rerouteRejectedSkillFlow(context.Background(), storeUserID, userID, lang, text)
}
for k, v := range result.Extracted {
v = strings.TrimSpace(v)
if v == "" {
continue
}
setField(&session, k, v)
}
a.hydrateCreateTraderSlotReferences(storeUserID, &session)
if fieldValue(session, "exchange_id") != "" && fieldValue(session, "model_id") != "" && fieldValue(session, "strategy_id") != "" {
if err := a.validateTraderDraft(storeUserID, fieldValue(session, "model_id"), fieldValue(session, "exchange_id"), fieldValue(session, "strategy_id")); err != nil {
@@ -561,15 +529,6 @@ func (a *Agent) handleCreateTraderSkill(storeUserID string, userID int64, lang,
a.saveSkillSession(userID, session)
return a.buildTraderCreateMissingPrompt(storeUserID, lang, session, a.buildTraderCreateConversationResources(storeUserID, session)), true
}
if !result.Ready && result.Question == "" {
result.Ready = true
}
if !result.Ready {
session.Phase = "collecting"
a.saveSkillSession(userID, session)
return "", false
}
if stillMissing := missingFieldKeysForSkillSession(session); len(stillMissing) > 0 {
session.Phase = "collecting"

View File

@@ -1,7 +1,6 @@
package agent
import (
"context"
"encoding/json"
"fmt"
"regexp"
@@ -1459,12 +1458,6 @@ func (a *Agent) handleExchangeCreateSkill(storeUserID string, userID int64, lang
}
return "Cancelled the current exchange creation flow."
}
if result := a.extractSkillSessionFieldsWithLLM(context.Background(), userID, lang, text, session); result.Intent == "continue" {
a.applyLLMExtractionToSkillSession(storeUserID, &session, result, lang, text)
}
if v := inferCreateDisplayName(text); fieldValue(session, "account_name") == "" && v != "" {
setField(&session, "account_name", v)
}
exType := fieldValue(session, "exchange_type")
accountName := fieldValue(session, "account_name")
missing := make([]string, 0, 6)
@@ -1578,18 +1571,7 @@ func (a *Agent) handleModelCreateSkill(storeUserID string, userID int64, lang, t
}
return "Cancelled the current model creation flow."
}
if result := a.extractSkillSessionFieldsWithLLM(context.Background(), userID, lang, text, session); result.Intent == "continue" {
a.applyLLMExtractionToSkillSession(storeUserID, &session, result, lang, text)
}
if v := inferCreateDisplayName(text); fieldValue(session, "name") == "" && v != "" {
setField(&session, "name", v)
}
provider := fieldValue(session, "provider")
if provider != "" && fieldValue(session, "api_key") == "" {
if credential := inferModelCredentialFromText(provider, text); credential != "" {
setField(&session, "api_key", credential)
}
}
if provider != "" {
if fieldValue(session, "name") == "" {
setField(&session, "name", defaultModelConfigName(provider))
@@ -1726,9 +1708,6 @@ func (a *Agent) handleStrategyCreateSkill(storeUserID string, userID int64, lang
}
return "Cancelled the current strategy creation flow."
}
if result := a.extractSkillSessionFieldsWithLLM(context.Background(), userID, lang, text, session); result.Intent == "continue" {
a.applyLLMExtractionToSkillSession(storeUserID, &session, result, lang, text)
}
name := fieldValue(session, "name")
hasDescriptiveDraftIntent := session.Phase == "draft_create"
if hasDescriptiveDraftIntent {
@@ -1768,24 +1747,6 @@ func (a *Agent) handleSimpleEntitySkill(storeUserID string, userID int64, lang,
if session.Name != skillName || session.Action != action {
return "", false
}
if shouldUseLLMConversationForSimpleEntity(skillName, action) {
result := a.llmSkillConversationDriver(context.Background(), storeUserID, userID, lang, text, session, a.buildSimpleEntityConversationResources(storeUserID, session, options))
if result.Cancel {
a.clearSkillSession(userID)
if lang == "zh" {
return "已取消当前流程。", true
}
return "Cancelled the current flow.", true
}
if result.UserRejectedFlow {
return a.rerouteRejectedSkillFlow(context.Background(), storeUserID, userID, lang, text)
}
applySkillConversationResultToSession(&session, result)
if !result.Ready && result.Question != "" {
a.saveSkillSession(userID, session)
return result.Question, true
}
}
if supportsBulkTargetSelection(skillName, action) && textMeansAllTargets(text) {
setField(&session, "bulk_scope", "all")
session.TargetRef = nil
@@ -1886,26 +1847,6 @@ func (a *Agent) handleSimpleEntitySkill(storeUserID string, userID int64, lang,
}
func (a *Agent) askLLMAmbiguousTargetQuestion(storeUserID string, userID int64, lang, text string, session skillSession, skillName, action string, allOptions, ambiguous []traderSkillOption) string {
if a.aiClient != nil {
ambiguousSession := session
ambiguousSession.Name = skillName
ambiguousSession.Action = action
ambiguousSession.TargetRef = nil
setSkillDAGStep(&ambiguousSession, "resolve_target")
resources := a.buildSimpleEntityConversationResources(storeUserID, ambiguousSession, allOptions)
if resources == nil {
resources = map[string]any{}
}
resources["targets"] = ambiguous
resources["target_conflict"] = ambiguous
result := a.llmSkillConversationDriver(context.Background(), storeUserID, userID, lang, text, ambiguousSession, resources)
if question := strings.TrimSpace(result.Question); question != "" {
a.saveSkillSession(userID, ambiguousSession)
return question
}
}
return formatAmbiguousTargetPrompt(lang, ambiguous)
}

View File

@@ -214,7 +214,7 @@ func (a *Agent) handleWorkflowSession(ctx context.Context, storeUserID string, u
}
if activeSkill := a.getSkillSession(userID); strings.TrimSpace(activeSkill.Name) != "" {
decision, extraction := a.resolveSkillSessionTurn(ctx, userID, lang, text, activeSkill)
decision, _ := a.resolveSkillSessionTurn(ctx, userID, lang, text, activeSkill)
switch decision.Intent {
case "cancel":
a.clearSkillSession(userID)
@@ -230,11 +230,6 @@ func (a *Agent) handleWorkflowSession(ctx context.Context, storeUserID string, u
a.clearSkillSession(userID)
a.clearWorkflowSession(userID)
return "", false, nil
case "continue_active":
if extraction.Intent == "continue" {
a.applyLLMExtractionToSkillSession(storeUserID, &activeSkill, extraction, lang, text)
a.saveSkillSession(userID, activeSkill)
}
}
answer, handled := a.executeAtomicSkillTask(storeUserID, userID, lang, text, activeSkill.Name, activeSkill.Action, onEvent)
if !handled {