fix(agent): inject Shanghai time into each reply message
103 goosed harness_remember returns remembered:false, so session memory never carried the time anchor. Prepend buildCurrentTimeAgentPrefix on every H5 /reply and WeChat agent prompt so the model sees Asia/Shanghai clock. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,16 +4,23 @@
|
|||||||
|
|
||||||
- 分支:`0630007-agent-time-fix`(从 `0630007-memind` 切出,**不含** miniapp / 0630006 改动)
|
- 分支:`0630007-agent-time-fix`(从 `0630007-memind` 切出,**不含** miniapp / 0630006 改动)
|
||||||
- 修复:向 Agent 注入 `TKMind 当前时间基准`(Asia/Shanghai),避免回复里报 UTC 时间
|
- 修复:向 Agent 注入 `TKMind 当前时间基准`(Asia/Shanghai),避免回复里报 UTC 时间
|
||||||
|
- **v2**:103 上 `harness_remember` 返回 `remembered:false`,仅靠 harness 不可靠;改为在每条 `/reply` 用户消息里直接注入时间前缀(不依赖 harness)
|
||||||
|
|
||||||
## 发布前 103 状态(可回退目标)
|
## 发布前 103 状态(可回退目标)
|
||||||
|
|
||||||
| 项 | 值 |
|
| 项 | 值 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| release_id | `20260701-075736-16828a5` |
|
| release_id | `20260701-083212-7e3c018` |
|
||||||
| git_head | `16828a58f7fc79d7b9a174ffa87f5579894798fc` |
|
| git_head | `7e3c018a5503ba691b0f7cc26d0df2e4276e9a9c` |
|
||||||
| git_branch | `0630007-memind` |
|
| 说明 | 仅有 harness 时间锚点,103 harness 不生效 |
|
||||||
|
|
||||||
回退到上述 release 发布前版本:
|
回退到 harness-only 版:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash scripts/rollback-portal-runtime-prod.sh 20260701-083212-7e3c018 --yes
|
||||||
|
```
|
||||||
|
|
||||||
|
更早(完全无时间锚点):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash scripts/rollback-portal-runtime-prod.sh 20260701-075736-16828a5 --yes
|
bash scripts/rollback-portal-runtime-prod.sh 20260701-075736-16828a5 --yes
|
||||||
|
|||||||
+22
-4
@@ -11,7 +11,7 @@ import {
|
|||||||
PUBLISH_ROOT_DIR,
|
PUBLISH_ROOT_DIR,
|
||||||
resolvePublicBaseUrl,
|
resolvePublicBaseUrl,
|
||||||
} from './user-publish.mjs';
|
} from './user-publish.mjs';
|
||||||
import { buildTaskRoutingAgentText } from './user-memory-profile.mjs';
|
import { buildCurrentTimeAgentPrefix, buildTaskRoutingAgentText } from './user-memory-profile.mjs';
|
||||||
import { reconcileAgentSession } from './session-reconcile.mjs';
|
import { reconcileAgentSession } from './session-reconcile.mjs';
|
||||||
import { createImgproxySigner } from './imgproxy-signer.mjs';
|
import { createImgproxySigner } from './imgproxy-signer.mjs';
|
||||||
|
|
||||||
@@ -343,11 +343,11 @@ function messageHasImages(userMessage) {
|
|||||||
return extractImageUrlsFromMessage(userMessage).length > 0;
|
return extractImageUrlsFromMessage(userMessage).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function injectTaskRoutingHint(body, sessionPolicy) {
|
function prependAgentTextToUserMessage(body, transformText) {
|
||||||
const originalText = firstUserText(body?.user_message);
|
const originalText = firstUserText(body?.user_message);
|
||||||
if (!originalText?.trim()) return body;
|
if (!originalText?.trim()) return body;
|
||||||
|
|
||||||
const agentText = buildTaskRoutingAgentText(originalText, sessionPolicy);
|
const agentText = transformText(originalText);
|
||||||
if (!agentText || agentText === originalText) return body;
|
if (!agentText || agentText === originalText) return body;
|
||||||
|
|
||||||
const userMessage = body.user_message ?? {};
|
const userMessage = body.user_message ?? {};
|
||||||
@@ -377,6 +377,20 @@ function injectTaskRoutingHint(body, sessionPolicy) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function injectCurrentTimeAnchor(body, timezone) {
|
||||||
|
const tz = String(timezone ?? process.env.H5_DEFAULT_TIMEZONE ?? 'Asia/Shanghai').trim() || 'Asia/Shanghai';
|
||||||
|
return prependAgentTextToUserMessage(body, (originalText) => {
|
||||||
|
if (originalText.includes('TKMind 当前时间基准')) return originalText;
|
||||||
|
return `${buildCurrentTimeAgentPrefix({ timezone: tz })}${originalText}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function injectTaskRoutingHint(body, sessionPolicy) {
|
||||||
|
return prependAgentTextToUserMessage(body, (originalText) =>
|
||||||
|
buildTaskRoutingAgentText(originalText, sessionPolicy),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export async function buildVisionPayload({
|
export async function buildVisionPayload({
|
||||||
userMessage,
|
userMessage,
|
||||||
userId,
|
userId,
|
||||||
@@ -1336,8 +1350,12 @@ export function createTkmindProxy({
|
|||||||
|
|
||||||
let baseBody = req.body;
|
let baseBody = req.body;
|
||||||
if (isReplyPath && !policyState.unrestricted) {
|
if (isReplyPath && !policyState.unrestricted) {
|
||||||
|
const publishLayout = await userAuth
|
||||||
|
.getUserPublishLayout(req.currentUser.id)
|
||||||
|
.catch(() => null);
|
||||||
|
baseBody = injectCurrentTimeAnchor(req.body, publishLayout?.timezone);
|
||||||
baseBody = injectTaskRoutingHint(
|
baseBody = injectTaskRoutingHint(
|
||||||
req.body,
|
baseBody,
|
||||||
await userAuth.getAgentSessionPolicy(req.currentUser.id),
|
await userAuth.getAgentSessionPolicy(req.currentUser.id),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,6 +192,14 @@ export function resolveTimeOfDayPeriod(hour) {
|
|||||||
return { period: '晚上', greeting: '晚上好' };
|
return { period: '晚上', greeting: '晚上好' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildCurrentTimeAgentPrefix({ now = Date.now(), timezone = DEFAULT_TIMEZONE } = {}) {
|
||||||
|
return [
|
||||||
|
'【TKMind 时间基准 - 仅供回答时间/日期/时段问候,不要向用户复述】',
|
||||||
|
renderCurrentTimeAnchor({ now, timezone }),
|
||||||
|
'',
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
export function renderCurrentTimeAnchor({ now = Date.now(), timezone = DEFAULT_TIMEZONE } = {}) {
|
export function renderCurrentTimeAnchor({ now = Date.now(), timezone = DEFAULT_TIMEZONE } = {}) {
|
||||||
const { year, month, day } = formatDateParts(now, timezone);
|
const { year, month, day } = formatDateParts(now, timezone);
|
||||||
const { hour, minute } = formatLocalClockParts(now, timezone);
|
const { hour, minute } = formatLocalClockParts(now, timezone);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
resolveTimeOfDayPeriod,
|
resolveTimeOfDayPeriod,
|
||||||
suggestCodeExecutorForTask,
|
suggestCodeExecutorForTask,
|
||||||
buildSessionMemoryEntries,
|
buildSessionMemoryEntries,
|
||||||
|
buildCurrentTimeAgentPrefix,
|
||||||
ensureUserMemoryProfile,
|
ensureUserMemoryProfile,
|
||||||
hasMemoryStore,
|
hasMemoryStore,
|
||||||
loadUserMemoryProfile,
|
loadUserMemoryProfile,
|
||||||
@@ -123,6 +124,16 @@ test('buildSessionMemoryEntries injects time anchor even without memory store',
|
|||||||
assert.match(entries[0].content, /13:00(中午)/);
|
assert.match(entries[0].content, /13:00(中午)/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('buildCurrentTimeAgentPrefix wraps Shanghai anchor for agent-only injection', () => {
|
||||||
|
const text = buildCurrentTimeAgentPrefix({
|
||||||
|
now: Date.UTC(2026, 5, 29, 5, 0, 0),
|
||||||
|
timezone: 'Asia/Shanghai',
|
||||||
|
});
|
||||||
|
assert.match(text, /不要向用户复述/);
|
||||||
|
assert.match(text, /2026-06-29(星期一)/);
|
||||||
|
assert.match(text, /13:00(中午)/);
|
||||||
|
});
|
||||||
|
|
||||||
test('renderCurrentTimeAnchor formats Shanghai weekday from exact date', () => {
|
test('renderCurrentTimeAnchor formats Shanghai weekday from exact date', () => {
|
||||||
const text = renderCurrentTimeAnchor({
|
const text = renderCurrentTimeAnchor({
|
||||||
now: Date.UTC(2026, 5, 29, 5, 0, 0),
|
now: Date.UTC(2026, 5, 29, 5, 0, 0),
|
||||||
|
|||||||
+7
-3
@@ -6,8 +6,8 @@ import { developerToolsFromPolicy } from './capabilities.mjs';
|
|||||||
import { mergeMessageContent } from './message-stream.mjs';
|
import { mergeMessageContent } from './message-stream.mjs';
|
||||||
import { reconcileAgentSession } from './session-reconcile.mjs';
|
import { reconcileAgentSession } from './session-reconcile.mjs';
|
||||||
import { isScheduleIntent, parseScheduleIntent, shouldUseScheduleAssistant } from './schedule-intent.mjs';
|
import { isScheduleIntent, parseScheduleIntent, shouldUseScheduleAssistant } from './schedule-intent.mjs';
|
||||||
import { renderCurrentTimeAnchor } from './user-memory-profile.mjs';
|
|
||||||
import { materializeMissingPublicHtmlWrites } from './mindspace-public-finish-sync.mjs';
|
import { materializeMissingPublicHtmlWrites } from './mindspace-public-finish-sync.mjs';
|
||||||
|
import { buildCurrentTimeAgentPrefix } from './user-memory-profile.mjs';
|
||||||
import { PUBLISH_ROOT_DIR } from './user-publish.mjs';
|
import { PUBLISH_ROOT_DIR } from './user-publish.mjs';
|
||||||
import { downloadTemporaryMedia, persistWechatImage } from './wechat-media.mjs';
|
import { downloadTemporaryMedia, persistWechatImage } from './wechat-media.mjs';
|
||||||
import { buildAckText } from './wechat/ack/ack-provider.mjs';
|
import { buildAckText } from './wechat/ack/ack-provider.mjs';
|
||||||
@@ -1098,12 +1098,12 @@ export function buildWechatAgentPrompt(intent) {
|
|||||||
].join('\n')
|
].join('\n')
|
||||||
: '';
|
: '';
|
||||||
const scheduleTimezone = process.env.H5_DEFAULT_TIMEZONE || 'Asia/Shanghai';
|
const scheduleTimezone = process.env.H5_DEFAULT_TIMEZONE || 'Asia/Shanghai';
|
||||||
|
const currentTimeHint = buildCurrentTimeAgentPrefix({ timezone: scheduleTimezone });
|
||||||
const scheduleAssistantHint = shouldUseScheduleAssistant(intent?.agentText ?? intent?.content)
|
const scheduleAssistantHint = shouldUseScheduleAssistant(intent?.agentText ?? intent?.content)
|
||||||
? [
|
? [
|
||||||
'【日程技能要求】这条消息涉及待办、提醒或日程。',
|
'【日程技能要求】这条消息涉及待办、提醒或日程。',
|
||||||
'开始前先加载 `schedule-assistant` skill,并严格按 skill 里的边界执行。',
|
'开始前先加载 `schedule-assistant` skill,并严格按 skill 里的边界执行。',
|
||||||
'写入工具时优先使用 startLocal / endLocal / remindLocal(YYYY-MM-DD HH:mm),不要自行估算 Unix 毫秒。',
|
'写入工具时优先使用 startLocal / endLocal / remindLocal(YYYY-MM-DD HH:mm),不要自行估算 Unix 毫秒。',
|
||||||
renderCurrentTimeAnchor({ timezone: scheduleTimezone }),
|
|
||||||
'只有在 `schedule_create_item` / `schedule_create_reminder` 等工具成功返回后,才能告诉用户“已经设置好了”。',
|
'只有在 `schedule_create_item` / `schedule_create_reminder` 等工具成功返回后,才能告诉用户“已经设置好了”。',
|
||||||
intent?.msgId ? `调用 schedule_create_item 时必须传入 sourceMessageId: ${intent.msgId}` : '',
|
intent?.msgId ? `调用 schedule_create_item 时必须传入 sourceMessageId: ${intent.msgId}` : '',
|
||||||
'',
|
'',
|
||||||
@@ -1112,6 +1112,7 @@ export function buildWechatAgentPrompt(intent) {
|
|||||||
if (msgType === 'voice') {
|
if (msgType === 'voice') {
|
||||||
return [
|
return [
|
||||||
docxDownloadHint,
|
docxDownloadHint,
|
||||||
|
currentTimeHint,
|
||||||
scheduleAssistantHint,
|
scheduleAssistantHint,
|
||||||
'【微信服务号语音消息】用户通过语音输入,以下是微信识别结果。',
|
'【微信服务号语音消息】用户通过语音输入,以下是微信识别结果。',
|
||||||
'',
|
'',
|
||||||
@@ -1123,6 +1124,7 @@ export function buildWechatAgentPrompt(intent) {
|
|||||||
if (msgType === 'image') {
|
if (msgType === 'image') {
|
||||||
return [
|
return [
|
||||||
docxDownloadHint,
|
docxDownloadHint,
|
||||||
|
currentTimeHint,
|
||||||
scheduleAssistantHint,
|
scheduleAssistantHint,
|
||||||
'【微信服务号图片消息】用户发送了图片。',
|
'【微信服务号图片消息】用户发送了图片。',
|
||||||
'如用户没有明确要求,请先根据图片内容给出简短理解,并询问下一步。',
|
'如用户没有明确要求,请先根据图片内容给出简短理解,并询问下一步。',
|
||||||
@@ -1135,6 +1137,7 @@ export function buildWechatAgentPrompt(intent) {
|
|||||||
if (msgType === 'location') {
|
if (msgType === 'location') {
|
||||||
const location = intent?.location ?? {};
|
const location = intent?.location ?? {};
|
||||||
return [
|
return [
|
||||||
|
currentTimeHint,
|
||||||
scheduleAssistantHint,
|
scheduleAssistantHint,
|
||||||
'【微信服务号位置消息】用户发送了当前位置。',
|
'【微信服务号位置消息】用户发送了当前位置。',
|
||||||
location.label ? `地址:${location.label}` : '',
|
location.label ? `地址:${location.label}` : '',
|
||||||
@@ -1152,6 +1155,7 @@ export function buildWechatAgentPrompt(intent) {
|
|||||||
if (msgType === 'link') {
|
if (msgType === 'link') {
|
||||||
const link = intent?.link ?? {};
|
const link = intent?.link ?? {};
|
||||||
return [
|
return [
|
||||||
|
currentTimeHint,
|
||||||
scheduleAssistantHint,
|
scheduleAssistantHint,
|
||||||
'【微信服务号链接消息】用户分享了链接。',
|
'【微信服务号链接消息】用户分享了链接。',
|
||||||
link.title ? `标题:${link.title}` : '',
|
link.title ? `标题:${link.title}` : '',
|
||||||
@@ -1162,7 +1166,7 @@ export function buildWechatAgentPrompt(intent) {
|
|||||||
.join('\n');
|
.join('\n');
|
||||||
}
|
}
|
||||||
const content = String(intent?.agentText ?? intent?.content ?? '').trim();
|
const content = String(intent?.agentText ?? intent?.content ?? '').trim();
|
||||||
const lines = [];
|
const lines = [currentTimeHint];
|
||||||
if (docxDownloadHint) lines.push(docxDownloadHint);
|
if (docxDownloadHint) lines.push(docxDownloadHint);
|
||||||
if (pagePublishHint) lines.push(pagePublishHint);
|
if (pagePublishHint) lines.push(pagePublishHint);
|
||||||
if (scheduleAssistantHint) lines.push(scheduleAssistantHint);
|
if (scheduleAssistantHint) lines.push(scheduleAssistantHint);
|
||||||
|
|||||||
Reference in New Issue
Block a user