Files
socaichat/langgraph/src/agent/utils/truncate-messages.ts
T
gongzhiyongandClaude Opus 4.6 86cfa317b6 feat: ChatGPT Enterprise 对标优化 — Prompt 重构 + 前端 P0 交互 + 错误处理
- 重构所有 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>
2026-04-11 14:35:45 +08:00

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;
}