feat: add Graph Tool code and handoff reason to agent run log model and related components

This commit is contained in:
mlogclub
2026-04-10 16:23:25 +08:00
parent c1d42be1fc
commit 023ba802e4
7 changed files with 64 additions and 3 deletions
+19 -1
View File
@@ -309,6 +309,8 @@ func (s *aiReplyService) writeRunLog(startedAt time.Time, message models.Message
SkillRouteTrace: strings.TrimSpace(summarySkillRouteTrace(summary)), SkillRouteTrace: strings.TrimSpace(summarySkillRouteTrace(summary)),
ToolSearchTrace: extractToolSearchTrace(summary), ToolSearchTrace: extractToolSearchTrace(summary),
GraphToolTrace: extractGraphToolTrace(summary), GraphToolTrace: extractGraphToolTrace(summary),
GraphToolCode: firstGraphToolCode(summary),
HandoffReason: extractHandoffReason(summary),
PlannedToolCode: plannedToolCode, PlannedToolCode: plannedToolCode,
PlanReason: planReason, PlanReason: planReason,
InterruptType: firstInterruptType(summary), InterruptType: firstInterruptType(summary),
@@ -597,6 +599,21 @@ func firstGraphToolCode(summary *Summary) string {
return "" return ""
} }
func extractHandoffReason(summary *Summary) string {
trace := parseRuntimeTraceData(summary.TraceData)
for _, item := range trace.GraphTools.Items {
if strings.TrimSpace(item.ToolCode) != toolx.GraphHandoffConversationToolCode {
continue
}
if len(item.Arguments) == 0 {
return ""
}
reason, _ := item.Arguments["reason"].(string)
return strings.TrimSpace(reason)
}
return ""
}
type runtimeTraceProjection struct { type runtimeTraceProjection struct {
ToolSearch struct { ToolSearch struct {
Raw json.RawMessage `json:"-"` Raw json.RawMessage `json:"-"`
@@ -608,7 +625,8 @@ type runtimeTraceProjection struct {
GraphTools struct { GraphTools struct {
Raw json.RawMessage `json:"-"` Raw json.RawMessage `json:"-"`
Items []struct { Items []struct {
ToolCode string `json:"toolCode"` ToolCode string `json:"toolCode"`
Arguments map[string]any `json:"arguments"`
} `json:"items"` } `json:"items"`
} `json:"graphTools"` } `json:"graphTools"`
} }
@@ -22,6 +22,8 @@ func BuildAgentRunLog(item *models.AgentRunLog) response.AgentRunLogResponse {
SkillRouteTrace: item.SkillRouteTrace, SkillRouteTrace: item.SkillRouteTrace,
ToolSearchTrace: item.ToolSearchTrace, ToolSearchTrace: item.ToolSearchTrace,
GraphToolTrace: item.GraphToolTrace, GraphToolTrace: item.GraphToolTrace,
GraphToolCode: item.GraphToolCode,
HandoffReason: item.HandoffReason,
PlannedToolCode: item.PlannedToolCode, PlannedToolCode: item.PlannedToolCode,
PlanReason: item.PlanReason, PlanReason: item.PlanReason,
InterruptType: item.InterruptType, InterruptType: item.InterruptType,
@@ -26,6 +26,11 @@ func (c *AgentRunLogController) AnyList() *web.JsonResult {
params.QueryFilter{ParamName: "aiAgentId"}, params.QueryFilter{ParamName: "aiAgentId"},
params.QueryFilter{ParamName: "plannedAction"}, params.QueryFilter{ParamName: "plannedAction"},
params.QueryFilter{ParamName: "plannedSkillCode", Op: params.Like}, params.QueryFilter{ParamName: "plannedSkillCode", Op: params.Like},
params.QueryFilter{ParamName: "graphToolCode"},
params.QueryFilter{ParamName: "interruptType"},
params.QueryFilter{ParamName: "resumeSource"},
params.QueryFilter{ParamName: "finalStatus"},
params.QueryFilter{ParamName: "handoffReason", Op: params.Like},
params.QueryFilter{ParamName: "finalAction"}, params.QueryFilter{ParamName: "finalAction"},
params.QueryFilter{ParamName: "userMessage", Op: params.Like}, params.QueryFilter{ParamName: "userMessage", Op: params.Like},
).Desc("id") ).Desc("id")
+2
View File
@@ -952,6 +952,8 @@ type AgentRunLog struct {
SkillRouteTrace string `gorm:"type:text"` SkillRouteTrace string `gorm:"type:text"`
ToolSearchTrace string `gorm:"type:text"` ToolSearchTrace string `gorm:"type:text"`
GraphToolTrace string `gorm:"type:text"` GraphToolTrace string `gorm:"type:text"`
GraphToolCode string `gorm:"type:varchar(200);not null;default:'';index"`
HandoffReason string `gorm:"type:varchar(500);not null;default:''"`
PlannedToolCode string `gorm:"type:varchar(200);not null;default:'';index"` PlannedToolCode string `gorm:"type:varchar(200);not null;default:'';index"`
PlanReason string `gorm:"type:varchar(500);not null;default:''"` PlanReason string `gorm:"type:varchar(500);not null;default:''"`
InterruptType string `gorm:"type:varchar(50);not null;default:'';index"` InterruptType string `gorm:"type:varchar(50);not null;default:'';index"`
@@ -50,6 +50,8 @@ type AgentRunLogResponse struct {
SkillRouteTrace string `json:"skillRouteTrace"` SkillRouteTrace string `json:"skillRouteTrace"`
ToolSearchTrace string `json:"toolSearchTrace"` ToolSearchTrace string `json:"toolSearchTrace"`
GraphToolTrace string `json:"graphToolTrace"` GraphToolTrace string `json:"graphToolTrace"`
GraphToolCode string `json:"graphToolCode"`
HandoffReason string `json:"handoffReason"`
PlannedToolCode string `json:"plannedToolCode"` PlannedToolCode string `json:"plannedToolCode"`
PlanReason string `json:"planReason"` PlanReason string `json:"planReason"`
InterruptType string `json:"interruptType"` InterruptType string `json:"interruptType"`
+32 -2
View File
@@ -51,6 +51,15 @@ const actionOptions = [
{ value: "fallback", label: "兜底" }, { value: "fallback", label: "兜底" },
] ]
const finalStatusOptions = [
{ value: "all", label: "全部状态" },
{ value: "completed", label: "completed" },
{ value: "interrupted", label: "interrupted" },
{ value: "expired", label: "expired" },
{ value: "error", label: "error" },
{ value: "fallback", label: "fallback" },
]
function actionBadgeVariant(action: string) { function actionBadgeVariant(action: string) {
switch (action) { switch (action) {
case "handoff": case "handoff":
@@ -74,10 +83,12 @@ export default function DashboardAgentRunLogsPage() {
const [keywordInput, setKeywordInput] = useState("") const [keywordInput, setKeywordInput] = useState("")
const [plannedActionInput, setPlannedActionInput] = useState("all") const [plannedActionInput, setPlannedActionInput] = useState("all")
const [finalActionInput, setFinalActionInput] = useState("all") const [finalActionInput, setFinalActionInput] = useState("all")
const [finalStatusInput, setFinalStatusInput] = useState("all")
const [aiAgentIdInput, setAiAgentIdInput] = useState("all") const [aiAgentIdInput, setAiAgentIdInput] = useState("all")
const [keyword, setKeyword] = useState("") const [keyword, setKeyword] = useState("")
const [plannedAction, setPlannedAction] = useState("all") const [plannedAction, setPlannedAction] = useState("all")
const [finalAction, setFinalAction] = useState("all") const [finalAction, setFinalAction] = useState("all")
const [finalStatus, setFinalStatus] = useState("all")
const [aiAgentId, setAiAgentId] = useState("all") const [aiAgentId, setAiAgentId] = useState("all")
const [page, setPage] = useState(1) const [page, setPage] = useState(1)
const [limit, setLimit] = useState(20) const [limit, setLimit] = useState(20)
@@ -121,6 +132,7 @@ export default function DashboardAgentRunLogsPage() {
userMessage: keyword.trim() || undefined, userMessage: keyword.trim() || undefined,
plannedAction: plannedAction === "all" ? undefined : plannedAction, plannedAction: plannedAction === "all" ? undefined : plannedAction,
finalAction: finalAction === "all" ? undefined : finalAction, finalAction: finalAction === "all" ? undefined : finalAction,
finalStatus: finalStatus === "all" ? undefined : finalStatus,
aiAgentId: aiAgentId === "all" ? undefined : aiAgentId, aiAgentId: aiAgentId === "all" ? undefined : aiAgentId,
page, page,
limit, limit,
@@ -131,7 +143,7 @@ export default function DashboardAgentRunLogsPage() {
} finally { } finally {
setLoading(false) setLoading(false)
} }
}, [aiAgentId, finalAction, keyword, limit, page, plannedAction]) }, [aiAgentId, finalAction, finalStatus, keyword, limit, page, plannedAction])
useEffect(() => { useEffect(() => {
void loadData() void loadData()
@@ -153,6 +165,7 @@ export default function DashboardAgentRunLogsPage() {
setKeyword(keywordInput) setKeyword(keywordInput)
setPlannedAction(plannedActionInput) setPlannedAction(plannedActionInput)
setFinalAction(finalActionInput) setFinalAction(finalActionInput)
setFinalStatus(finalStatusInput)
setAiAgentId(aiAgentIdInput) setAiAgentId(aiAgentIdInput)
setPage(1) setPage(1)
} }
@@ -213,6 +226,16 @@ export default function DashboardAgentRunLogsPage() {
onChange={(value) => setFinalActionInput(value || "all")} onChange={(value) => setFinalActionInput(value || "all")}
/> />
</div> </div>
<div className="w-full xl:w-40">
<OptionCombobox
value={finalStatusInput}
options={finalStatusOptions}
placeholder="最终状态"
searchPlaceholder="搜索状态"
emptyText="未找到状态"
onChange={(value) => setFinalStatusInput(value || "all")}
/>
</div>
<div className="w-full xl:w-52"> <div className="w-full xl:w-52">
<OptionCombobox <OptionCombobox
value={aiAgentIdInput} value={aiAgentIdInput}
@@ -278,13 +301,18 @@ export default function DashboardAgentRunLogsPage() {
{item.plannedSkillCode || item.plannedToolCode ? ( {item.plannedSkillCode || item.plannedToolCode ? (
<div className="space-y-1"> <div className="space-y-1">
<Badge variant="outline"> <Badge variant="outline">
{item.plannedSkillCode || item.plannedToolCode} {item.plannedSkillCode || item.graphToolCode || item.plannedToolCode}
</Badge> </Badge>
{item.plannedSkillName ? ( {item.plannedSkillName ? (
<div className="line-clamp-1 text-xs text-muted-foreground"> <div className="line-clamp-1 text-xs text-muted-foreground">
{item.plannedSkillName} {item.plannedSkillName}
</div> </div>
) : null} ) : null}
{item.handoffReason ? (
<div className="line-clamp-1 text-xs text-muted-foreground">
{item.handoffReason}
</div>
) : null}
</div> </div>
) : ( ) : (
"-" "-"
@@ -366,8 +394,10 @@ export default function DashboardAgentRunLogsPage() {
`plannedAction: ${activeLog.plannedAction || "-"}`, `plannedAction: ${activeLog.plannedAction || "-"}`,
`plannedSkillCode: ${activeLog.plannedSkillCode || "-"}`, `plannedSkillCode: ${activeLog.plannedSkillCode || "-"}`,
`plannedSkillName: ${activeLog.plannedSkillName || "-"}`, `plannedSkillName: ${activeLog.plannedSkillName || "-"}`,
`graphToolCode: ${activeLog.graphToolCode || "-"}`,
`plannedToolCode: ${activeLog.plannedToolCode || "-"}`, `plannedToolCode: ${activeLog.plannedToolCode || "-"}`,
`planReason: ${activeLog.planReason || "-"}`, `planReason: ${activeLog.planReason || "-"}`,
`handoffReason: ${activeLog.handoffReason || "-"}`,
`skillRouteTrace: ${activeLog.skillRouteTrace || "-"}`, `skillRouteTrace: ${activeLog.skillRouteTrace || "-"}`,
]} ]}
/> />
+2
View File
@@ -363,6 +363,8 @@ export type AgentRunLog = {
skillRouteTrace: string skillRouteTrace: string
toolSearchTrace: string toolSearchTrace: string
graphToolTrace: string graphToolTrace: string
graphToolCode: string
handoffReason: string
plannedToolCode: string plannedToolCode: string
planReason: string planReason: string
interruptType: string interruptType: string