tmp
This commit is contained in:
+54
-21
@@ -15,6 +15,9 @@ import {
|
||||
} from "./tools/index.js";
|
||||
|
||||
export default class EscortAgent {
|
||||
// 历史消息字符数上限,超出时从头部丢弃完整轮次
|
||||
static MAX_HISTORY_CHARS = config.agent.maxHistoryChars || 40000;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
@@ -22,13 +25,39 @@ export default class EscortAgent {
|
||||
this.messages = [];
|
||||
}
|
||||
|
||||
// 按字符数裁剪历史:只以 HumanMessage 为界从头部删除完整轮次,
|
||||
// 避免切断 AI(tool_calls) -> Tool 的链式结构;永远保留最新一轮
|
||||
_trimHistory() {
|
||||
const size = (msgs) => msgs.reduce((sum, m) => sum + (m.text?.length ?? 0), 0);
|
||||
if (size(this.messages) <= EscortAgent.MAX_HISTORY_CHARS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const starts = [];
|
||||
this.messages.forEach((m, i) => {
|
||||
if (HumanMessage.isInstance(m)) starts.push(i);
|
||||
});
|
||||
if (starts.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cut = 0;
|
||||
for (let i = 0; i < starts.length - 1; i++) {
|
||||
cut = starts[i + 1];
|
||||
if (size(this.messages.slice(cut)) <= EscortAgent.MAX_HISTORY_CHARS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.messages = this.messages.slice(cut);
|
||||
logger.info(`History trimmed to ${this.messages.length} messages, ${size(this.messages)} chars`);
|
||||
}
|
||||
|
||||
// msg: { ts: "2023-08-01 10:00:00", content: "你好" }
|
||||
async streamChat(userInfo, msgs, callback) {
|
||||
if (!msgs.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const agent = this._genAgent(userInfo);
|
||||
msgs.forEach(msg => {
|
||||
if (msg.type === "clear") {
|
||||
this.messages = [];
|
||||
@@ -42,6 +71,10 @@ export default class EscortAgent {
|
||||
return;
|
||||
}
|
||||
|
||||
this._trimHistory();
|
||||
|
||||
// 在处理完 clear 消息后再生成 agent,确保清空后使用新实例
|
||||
const agent = this._genAgent(userInfo);
|
||||
const INTERESTING_NODES = new Set(["model_request", "tools"]);
|
||||
for await (const [namespace, mode, data] of await agent.stream(
|
||||
{ messages: this.messages },
|
||||
@@ -55,27 +88,27 @@ export default class EscortAgent {
|
||||
const isSubagent = namespace.some(s => s.startsWith("tools:"));
|
||||
const source = isSubagent ? "subagent" : "main";
|
||||
if (mode === "updates") {
|
||||
for (const nodeName of Object.keys(data)) {
|
||||
if (!INTERESTING_NODES.has(nodeName)) continue;
|
||||
// Main agent updates (empty namespace)
|
||||
if (namespace.length === 0) {
|
||||
for (const [nodeName, data_] of Object.entries(data)) {
|
||||
if (nodeName === "tools") {
|
||||
// Subagent results returned to main agent
|
||||
for (const msg of data_.messages ?? []) {
|
||||
if (msg.type === "tool") {
|
||||
logger.info(`Subagent complete: ${msg.name}, Result: ${String(msg.content).slice(0, 200)}`);
|
||||
}
|
||||
// Main agent updates (empty namespace)
|
||||
if (namespace.length === 0) {
|
||||
for (const [nodeName, data_] of Object.entries(data)) {
|
||||
if (!INTERESTING_NODES.has(nodeName)) continue;
|
||||
if (nodeName === "tools") {
|
||||
// 工具结果必须并入历史,否则下一轮会出现 tool_calls 缺少对应 tool 结果的断链
|
||||
for (const msg of data_.messages ?? []) {
|
||||
if (msg.type === "tool") {
|
||||
this.messages.push(msg);
|
||||
logger.info(`Tool complete: ${msg.name}, Result: ${String(msg.content).slice(0, 200)}`);
|
||||
}
|
||||
} else if (nodeName === "model_request") {
|
||||
this.messages.push(...data_.messages);
|
||||
}
|
||||
} else if (nodeName === "model_request") {
|
||||
this.messages.push(...data_.messages);
|
||||
}
|
||||
} else {
|
||||
// Subagent updates (non-empty namespace)
|
||||
for (const [nodeName, data_] of Object.entries(data)) {
|
||||
logger.info(`[${namespace[0]}] step: ${nodeName}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Subagent updates (non-empty namespace)
|
||||
for (const nodeName of Object.keys(data)) {
|
||||
if (!INTERESTING_NODES.has(nodeName)) continue;
|
||||
logger.info(`[${namespace[0]}] step: ${nodeName}`);
|
||||
}
|
||||
}
|
||||
} else if (mode === "messages") {
|
||||
@@ -87,10 +120,10 @@ export default class EscortAgent {
|
||||
continue;
|
||||
}
|
||||
if (AIMessageChunk.isInstance(message)) {
|
||||
if (message.text && !message.tool_call_chunks?.length) {
|
||||
if (message.text) {
|
||||
callback(source, "ai", message.text, message.id);
|
||||
}
|
||||
if (message.additional_kwargs.reasoning_content && !message.tool_call_chunks?.length) {
|
||||
if (message.additional_kwargs.reasoning_content) {
|
||||
callback(source, "reasoning", message.additional_kwargs.reasoning_content, message.id);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user