Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion internal/flow/processor/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ func (p *ContentRequestProcessor) ProcessRequest(
}
}
messages = p.getIncrementMessages(invocation, summaryUpdatedAt)
// When summary is present, ensure messages start with user role to comply with API requirements.
if !summaryUpdatedAt.IsZero() {
messages = p.trimLeadingNonUserMessages(messages)
Copy link
Contributor

@nomand-zc nomand-zc Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里可能会有问题。假如第一个agent 执行完后 触发了summary,第二个agent 执行时是不是大概率就都拿不到增量消息了,包括第二个agent在本轮迭代中自己生成的tool相关的消息

Copy link
Contributor

@nomand-zc nomand-zc Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感觉这里不应该是截断,而是在没有userMessage时 强制将 invocation.Message插入进来

Copy link
Contributor Author

@hyprh hyprh Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

欸,没太理解,现在summary应该是绑定的runner,runner执行执行结束的时候触发吧,应该不会在第一个agent执行之后触发?你说的是graph的场景吗

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

欸,没太理解,现在summary应该是绑定的runner,runner执行执行结束的时候触发吧,应该不会在第一个agent执行之后触发?你说的是graph的场景吗

并不是runner结束时触发哦, 执行过程中也可以触发的。 只是你这一版改成了单个agent执行结束后才触发。 multiagent模式下,就会出现上面提到的这个问题。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"假如第一个agent 执行完后 触发了summary,第二个agent 执行时拿不到增量消息",我理解还是能拿到的吧,如果是顺序执行的话,updatetime绑定的是用于summary的最后一个event,增量event理论上没啥影响

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

第一个agent触发summary后, 第二个agent拿到的增量里是不是就不会包含userMessage了,在经过trimLeadingNonUserMessages处理,返回的就是nil了。

}
req.Messages = append(req.Messages, messages...)
needToAddInvocationMessage = summaryUpdatedAt.IsZero() && len(messages) == 0
}
Expand Down Expand Up @@ -328,13 +332,33 @@ func (p *ContentRequestProcessor) mergeUserMessages(
return merged
}

// trimLeadingNonUserMessages removes leading non-user messages from the slice.
// This ensures that when summary is present, the incremental messages start with
// a user message to comply with API requirements (system messages must be followed by user).
//
// Note: Under normal operation, summary is only triggered after final assistant responses
// (not user messages, tool calls, or tool results), so the incremental messages should
// already start with a user message. This function serves as a defensive measure for
// edge cases or potential future changes.
func (p *ContentRequestProcessor) trimLeadingNonUserMessages(messages []model.Message) []model.Message {
for i, msg := range messages {
if msg.Role == model.RoleUser {
return messages[i:]
}
}
// No user message found, return empty slice.
return nil
}

func (p *ContentRequestProcessor) shouldIncludeEvent(evt event.Event, inv *agent.Invocation, filter string,
isZeroTime bool, since time.Time) bool {
if evt.Response == nil || evt.IsPartial || !evt.IsValidContent() {
return false
}

if !isZeroTime && evt.Timestamp.Before(since) {
// Use !After instead of Before to exclude events at exactly the summary boundary.
// This prevents the last summarized event from appearing in both the summary and incremental messages.
if !isZeroTime && !evt.Timestamp.After(since) {
return false
}

Expand Down
8 changes: 7 additions & 1 deletion runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,13 @@ func (r *runner) handleEventPersistence(
return
}

// Trigger summarization immediately after appending a qualifying event.
// Trigger summarization only after final assistant responses.
// Skip user messages, tool calls, and tool results to ensure summary
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里应该还有遗漏, !event.IsValidContent() 的也需要排除掉。有一种情况是event中仅带deltaState

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

诶,这里前面的shouldPersistEvent 判定应该能挡住

Copy link
Contributor

@nomand-zc nomand-zc Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

诶,这里前面的shouldPersistEvent 判定应该能挡住

仅带deltaState时 !event.IsValidContent() == true, 拦不住

// always contains complete Q&A pairs (including tool call round-trips).
if agentEvent.IsUserMessage() || agentEvent.IsToolCallResponse() || agentEvent.IsToolResultResponse() {
return
}

// Use EnqueueSummaryJob for true asynchronous processing.
// Prefer filter-specific summarization to avoid scanning all filters.
if err := r.sessionService.EnqueueSummaryJob(
Expand Down
Loading