fix: 增强思考区多 agent 分组并补齐 rollout 前缀
This commit is contained in:
1 parent
614f174c4a
commit
5c0bb6fb4e
2 files changed
+98
-14
No files matched your search
@@ -33,7 +33,7 @@ _CHAT_SEMAPHORE = None
|
||||
_CHAT_SEM_VALUE = None
|
||||
|
||||
|
||||
def extract_tool_text(raw: str) -> str:
|
||||
def extract_tool_text(raw: str, rollout_id: str = "") -> str:
|
||||
if not raw:
|
||||
return ""
|
||||
name_match = re.search(
|
||||
@@ -60,12 +60,13 @@ def extract_tool_text(raw: str) -> str:
|
||||
|
||||
label = name
|
||||
text = args
|
||||
prefix = f"[{rollout_id}]" if rollout_id else ""
|
||||
if name == "web_search":
|
||||
label = "[WebSearch]"
|
||||
label = f"{prefix}[WebSearch]"
|
||||
if isinstance(payload, dict):
|
||||
text = payload.get("query") or payload.get("q") or ""
|
||||
elif name == "search_images":
|
||||
label = "[SearchImage]"
|
||||
label = f"{prefix}[SearchImage]"
|
||||
if isinstance(payload, dict):
|
||||
text = (
|
||||
payload.get("image_description")
|
||||
@@ -74,7 +75,7 @@ def extract_tool_text(raw: str) -> str:
|
||||
or ""
|
||||
)
|
||||
elif name == "chatroom_send":
|
||||
label = "[AgentThink]"
|
||||
label = f"{prefix}[AgentThink]"
|
||||
if isinstance(payload, dict):
|
||||
text = payload.get("message") or ""
|
||||
|
||||
@@ -415,6 +416,7 @@ class StreamProcessor(proc_base.BaseProcessor):
|
||||
def __init__(self, model: str, token: str = "", show_think: bool = None):
|
||||
super().__init__(model, token)
|
||||
self.response_id: str = None
|
||||
self.rollout_id: str = ""
|
||||
self.fingerprint: str = ""
|
||||
self.think_opened: bool = False
|
||||
self.role_sent: bool = False
|
||||
@@ -444,7 +446,7 @@ class StreamProcessor(proc_base.BaseProcessor):
|
||||
return "".join(output_parts)
|
||||
end_pos = end_idx + len(end_tag)
|
||||
self._tool_usage_buffer += rest[:end_pos]
|
||||
line = extract_tool_text(self._tool_usage_buffer)
|
||||
line = extract_tool_text(self._tool_usage_buffer, self.rollout_id)
|
||||
if line:
|
||||
if output_parts and not output_parts[-1].endswith("\n"):
|
||||
output_parts[-1] += "\n"
|
||||
@@ -470,7 +472,7 @@ class StreamProcessor(proc_base.BaseProcessor):
|
||||
|
||||
end_pos = end_idx + len(end_tag)
|
||||
raw_card = rest[start_idx:end_pos]
|
||||
line = extract_tool_text(raw_card)
|
||||
line = extract_tool_text(raw_card, self.rollout_id)
|
||||
if line:
|
||||
if output_parts and not output_parts[-1].endswith("\n"):
|
||||
output_parts[-1] += "\n"
|
||||
@@ -554,6 +556,8 @@ class StreamProcessor(proc_base.BaseProcessor):
|
||||
self.fingerprint = llm.get("modelHash", "")
|
||||
if rid := resp.get("responseId"):
|
||||
self.response_id = rid
|
||||
if rid := resp.get("rolloutId"):
|
||||
self.rollout_id = str(rid)
|
||||
|
||||
if not self.role_sent:
|
||||
yield self._sse(role="assistant")
|
||||
@@ -697,11 +701,17 @@ class CollectProcessor(proc_base.BaseProcessor):
|
||||
|
||||
result = content
|
||||
if "xai:tool_usage_card" in self.filter_tags:
|
||||
rollout_id = ""
|
||||
rollout_match = re.search(
|
||||
r"<rolloutId>(.*?)</rolloutId>", result, flags=re.DOTALL
|
||||
)
|
||||
if rollout_match:
|
||||
rollout_id = rollout_match.group(1).strip()
|
||||
result = re.sub(
|
||||
r"<xai:tool_usage_card[^>]*>.*?</xai:tool_usage_card>",
|
||||
lambda match: (
|
||||
f"{extract_tool_text(match.group(0))}\n"
|
||||
if extract_tool_text(match.group(0))
|
||||
f"{extract_tool_text(match.group(0), rollout_id)}\n"
|
||||
if extract_tool_text(match.group(0), rollout_id)
|
||||
else ""
|
||||
),
|
||||
result,
|
||||
|
||||
@@ -728,16 +728,29 @@
|
||||
return parts;
|
||||
}
|
||||
|
||||
function parseRolloutBlocks(text) {
|
||||
function parseRolloutBlocks(text, defaultId = 'General') {
|
||||
const lines = (text || '').split(/\r?\n/);
|
||||
const blocks = [];
|
||||
let current = null;
|
||||
for (const line of lines) {
|
||||
const match = line.match(/^\s*\[([^\]]+)\]\[([^\]]+)\]\s*(.*)$/);
|
||||
if (match) {
|
||||
const matchDouble = line.match(/^\s*\[([^\]]+)\]\[([^\]]+)\]\s*(.*)$/);
|
||||
if (matchDouble) {
|
||||
if (current) blocks.push(current);
|
||||
current = { id: match[1], type: match[2], lines: [] };
|
||||
if (match[3]) current.lines.push(match[3]);
|
||||
current = { id: matchDouble[1], type: matchDouble[2], lines: [] };
|
||||
if (matchDouble[3]) current.lines.push(matchDouble[3]);
|
||||
continue;
|
||||
}
|
||||
const matchSingle = line.match(/^\s*\[([^\]]+)\]\s*(.*)$/);
|
||||
if (matchSingle) {
|
||||
const maybeType = String(matchSingle[1] || '').trim();
|
||||
if (/^(WebSearch|SearchImage|AgentThink)$/i.test(maybeType)) {
|
||||
if (current) blocks.push(current);
|
||||
current = { id: defaultId || 'General', type: maybeType, lines: [] };
|
||||
if (matchSingle[2]) current.lines.push(matchSingle[2]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (current && /^\s*\[[^\]]+\]\s*$/.test(line)) {
|
||||
continue;
|
||||
}
|
||||
if (current) {
|
||||
@@ -779,6 +792,56 @@
|
||||
return sections;
|
||||
}
|
||||
|
||||
function splitBlocksIntoSyntheticAgents(blocks) {
|
||||
const list = Array.isArray(blocks) ? blocks : [];
|
||||
if (!list.length) return [];
|
||||
|
||||
const ids = Array.from(new Set(list.map((b) => String(b.id || '').trim()).filter(Boolean)));
|
||||
const nonGeneralIds = ids.filter((id) => !/^general$/i.test(id));
|
||||
|
||||
// 优先按 rollout id 分组,能更稳定地映射多 agent。
|
||||
if (nonGeneralIds.length >= 2) {
|
||||
const groups = [];
|
||||
const map = new Map();
|
||||
for (const block of list) {
|
||||
const key = String(block.id || 'General');
|
||||
let group = map.get(key);
|
||||
if (!group) {
|
||||
group = { key, blocks: [] };
|
||||
map.set(key, group);
|
||||
groups.push(group);
|
||||
}
|
||||
group.blocks.push(block);
|
||||
}
|
||||
return groups.map((group, idx) => ({
|
||||
title: idx === 0 ? 'Grok Leader' : `Agent ${idx}`,
|
||||
blocks: group.blocks
|
||||
}));
|
||||
}
|
||||
|
||||
// 兜底:按 AgentThink 作为阶段收束点切分。
|
||||
const sections = [];
|
||||
let current = [];
|
||||
for (const block of list) {
|
||||
current.push(block);
|
||||
if (/^agentthink$/i.test(String(block.type || '').trim())) {
|
||||
sections.push(current);
|
||||
current = [];
|
||||
}
|
||||
}
|
||||
if (current.length) {
|
||||
sections.push(current);
|
||||
}
|
||||
|
||||
if (sections.length <= 1) {
|
||||
return [{ title: 'Grok Leader', blocks: list }];
|
||||
}
|
||||
return sections.map((part, idx) => ({
|
||||
title: idx === 0 ? 'Grok Leader' : `Agent ${idx}`,
|
||||
blocks: part
|
||||
}));
|
||||
}
|
||||
|
||||
function renderThinkContent(text, openAll) {
|
||||
const sections = parseAgentSections(text);
|
||||
if (!sections.length) {
|
||||
@@ -812,7 +875,18 @@
|
||||
};
|
||||
|
||||
const agentBlocks = sections.map((section, idx) => {
|
||||
const blocks = parseRolloutBlocks(section.lines.join('\n'));
|
||||
const blocks = parseRolloutBlocks(section.lines.join('\n'), section.title || 'General');
|
||||
if (!section.title && blocks.length) {
|
||||
const synthetic = splitBlocksIntoSyntheticAgents(blocks);
|
||||
if (synthetic.length > 1) {
|
||||
return synthetic.map((agent, agentIdx) => {
|
||||
const inner = renderGroups(agent.blocks, openAll);
|
||||
const title = escapeHtml(agent.title);
|
||||
const openAttr = openAll ? ' open' : (idx === 0 && agentIdx === 0 ? ' open' : '');
|
||||
return `<details class="think-agent"${openAttr}><summary>${title}</summary><div class="think-agent-items">${inner}</div></details>`;
|
||||
}).join('');
|
||||
}
|
||||
}
|
||||
const inner = blocks.length
|
||||
? renderGroups(blocks, openAll)
|
||||
: `<div class="think-rollout-body">${renderBasicMarkdown(section.lines.join('\n').trim())}</div>`;
|
||||
|
||||
Reference in new issue
Block a user