- 重构所有 Agent System Prompt,参考 ChatGPT 风格:自然对话、匹配用户沟通风格、避免翻译腔 - Router 路由规则结构化,新增 Decision Rules 和分组示例,提升路由准确率 - 前端 P0:textarea 多行输入 + Shift+Enter 换行、流式打字光标动画、编辑已发送消息 - 新增 ToolCallStatus 组件、process-attachment 文件处理节点、retry 重试工具 - 工具调用错误响应友好化(formatToolError 中文提示) - Enterprise tool descriptions 增加使用场景说明 - 上下文截断提示改为中文 - 新增 soc-product-agent 产品经理 Agent 定义 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
88 lines
2.9 KiB
TypeScript
88 lines
2.9 KiB
TypeScript
import { BaseMessage, SystemMessage, HumanMessage } from "@langchain/core/messages";
|
|
|
|
const MAX_RECENT_MESSAGES = 20;
|
|
|
|
/**
|
|
* Estimate token count using a simple character-based heuristic.
|
|
* ~4 chars per token for English, ~2 chars per token for Chinese.
|
|
* We use ~3 as a rough average for mixed content.
|
|
*/
|
|
function estimateTokens(message: BaseMessage): number {
|
|
const content = typeof message.content === "string"
|
|
? message.content
|
|
: JSON.stringify(message.content);
|
|
return Math.ceil(content.length / 3);
|
|
}
|
|
|
|
/**
|
|
* Truncate messages to fit within a token budget.
|
|
* Strategy:
|
|
* - Always keep system messages (if any)
|
|
* - Keep the most recent N messages
|
|
* - If the history exceeds maxTokens, drop older messages and inject a summary note
|
|
*/
|
|
export function truncateMessages(
|
|
messages: BaseMessage[],
|
|
maxTokens = 60000,
|
|
): BaseMessage[] {
|
|
if (messages.length === 0) return messages;
|
|
|
|
// Separate system messages from the rest
|
|
const systemMessages = messages.filter((m) => m instanceof SystemMessage);
|
|
const nonSystemMessages = messages.filter((m) => !(m instanceof SystemMessage));
|
|
|
|
// Calculate total tokens
|
|
const totalTokens = messages.reduce((sum, m) => sum + estimateTokens(m), 0);
|
|
|
|
// If within budget, return as-is
|
|
if (totalTokens <= maxTokens) {
|
|
return messages;
|
|
}
|
|
|
|
// Keep only recent messages
|
|
const recentMessages = nonSystemMessages.slice(-MAX_RECENT_MESSAGES);
|
|
|
|
// Check if recent messages alone fit
|
|
const recentTokens =
|
|
systemMessages.reduce((sum, m) => sum + estimateTokens(m), 0) +
|
|
recentMessages.reduce((sum, m) => sum + estimateTokens(m), 0);
|
|
|
|
if (recentTokens <= maxTokens) {
|
|
const droppedCount = nonSystemMessages.length - recentMessages.length;
|
|
if (droppedCount > 0) {
|
|
const summaryNote = new HumanMessage(
|
|
`[系统提示:为保持对话质量,已省略前 ${droppedCount} 条较早的消息。如需回溯之前的内容,请告诉我。]`,
|
|
);
|
|
return [...systemMessages, summaryNote, ...recentMessages];
|
|
}
|
|
return [...systemMessages, ...recentMessages];
|
|
}
|
|
|
|
// Still too large — progressively trim from the front of recent messages
|
|
const result: BaseMessage[] = [...systemMessages];
|
|
let budget = maxTokens - systemMessages.reduce((sum, m) => sum + estimateTokens(m), 0);
|
|
|
|
const trimmed: BaseMessage[] = [];
|
|
for (let i = recentMessages.length - 1; i >= 0; i--) {
|
|
const tokens = estimateTokens(recentMessages[i]);
|
|
if (budget - tokens >= 0) {
|
|
trimmed.unshift(recentMessages[i]);
|
|
budget -= tokens;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
const droppedCount = nonSystemMessages.length - trimmed.length;
|
|
if (droppedCount > 0) {
|
|
result.push(
|
|
new HumanMessage(
|
|
`[系统提示:为保持对话质量,已省略前 ${droppedCount} 条较早的消息。如需回溯之前的内容,请告诉我。]`,
|
|
),
|
|
);
|
|
}
|
|
result.push(...trimmed);
|
|
|
|
return result;
|
|
}
|