merge: fix/dialogue-current-time-2026-09-23 (dialogue clock anchor)
This commit is contained in:
@@ -77,8 +77,9 @@ func (e *CoordinatorEngine) generateDialogueReply(
|
||||
6. 不要声称已修改系统配置、用户身份或受控文档状态;不要伪造已执行的工作流结果。
|
||||
7. 只把最终答复写入 reply.md,使用中文,不要输出无关元数据。
|
||||
8. 事实与引用:涉及法规标准、项目数据、历史结论时,优先依据【参考知识】与【项目基准与协同上下文】作答并沿用〔引用键〕格式;两者未覆盖且无把握时,明确告知"暂无充分依据",严禁编造标准编号、条款内容或项目事实。
|
||||
9. 隐私与跨用户边界:【相关历史片段】与【项目基准与协同上下文】中涉及其他成员姓名、个人沟通习惯、私人事实的内容仅用于理解语境,不得在答复中复述、评价或转述给当前用户;引用项目事件仅限客观结论(如谁完成了什么交付物)。
|
||||
10. 身份一致性:始终以「项目助手」自称,不得扮演医生/监管审评员等法定角色给出诊疗或注册裁决性结论,法规与型检解读须提示"以官方发文与实际检测为准"。`
|
||||
9. 时间锚定:答复中涉及今天/明天/本周等时间表述,一律以提示词【项目基准与协同上下文】中的当前时间为准;天气、新闻等实时信息须依靠工具检索结果,无检索结果时明确说明无法提供实时数据,严禁自行编造日期或实时数值。
|
||||
10. 隐私与跨用户边界:【相关历史片段】与【项目基准与协同上下文】中涉及其他成员姓名、个人沟通习惯、私人事实的内容仅用于理解语境,不得在答复中复述、评价或转述给当前用户;引用项目事件仅限客观结论(如谁完成了什么交付物)。
|
||||
11. 身份一致性:始终以「项目助手」自称,不得扮演医生/监管审评员等法定角色给出诊疗或注册裁决性结论,法规与型检解读须提示"以官方发文与实际检测为准"。`
|
||||
|
||||
if st := stream.FromContext(ctx); st != nil {
|
||||
st.EmitPhase("dialogue_generating", "正在生成项目助手回复...", "assistant", 0, 0, 60)
|
||||
|
||||
@@ -81,6 +81,12 @@ func TestDialogueLLM_CasualChatUsesBackend(t *testing.T) {
|
||||
if !strings.Contains(backend.lastPrompt, "近期对话") {
|
||||
t.Fatalf("expected history in prompt, got: %s", backend.lastPrompt)
|
||||
}
|
||||
if !strings.Contains(backend.lastPrompt, "当前时间") {
|
||||
t.Fatalf("expected current-time anchor in prompt, got: %s", backend.lastPrompt)
|
||||
}
|
||||
if !strings.Contains(backend.lastInstructions, "时间锚定") {
|
||||
t.Fatalf("expected time-anchor instruction, got: %s", backend.lastInstructions)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialogueLLM_DocumentSummaryTreatsBodyAsData(t *testing.T) {
|
||||
|
||||
@@ -3,22 +3,54 @@ package coordinator
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"chat-agent-system/internal/ctxmgr"
|
||||
)
|
||||
|
||||
// dialogueCST 与 ctxmgr day_key 约定一致:固定 UTC+8,不依赖容器 tzdata。
|
||||
var dialogueCST = time.FixedZone("CST", 8*3600)
|
||||
|
||||
var weekdayCN = map[time.Weekday]string{
|
||||
time.Sunday: "周日",
|
||||
time.Monday: "周一",
|
||||
time.Tuesday: "周二",
|
||||
time.Wednesday: "周三",
|
||||
time.Thursday: "周四",
|
||||
time.Friday: "周五",
|
||||
time.Saturday: "周六",
|
||||
}
|
||||
|
||||
// MemoryComposer 分层记忆编排器:遵循严格的十段式模板结构自上而下组装对话 Prompt
|
||||
type MemoryComposer struct {
|
||||
MaxWindowTurns int
|
||||
// NowFn 可注入时钟;nil 时使用 time.Now(测试可固定)。
|
||||
NowFn func() time.Time
|
||||
}
|
||||
|
||||
// NewMemoryComposer 创建分层记忆编排器
|
||||
func NewMemoryComposer() *MemoryComposer {
|
||||
return &MemoryComposer{
|
||||
MaxWindowTurns: dialogueHistoryMax,
|
||||
NowFn: time.Now,
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *MemoryComposer) now() time.Time {
|
||||
if mc != nil && mc.NowFn != nil {
|
||||
return mc.NowFn()
|
||||
}
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// formatDialogueClock 渲染提示词用当前时间行(Asia/Shanghai / UTC+8)。
|
||||
func formatDialogueClock(t time.Time) string {
|
||||
local := t.In(dialogueCST)
|
||||
wd := weekdayCN[local.Weekday()]
|
||||
return fmt.Sprintf("%s (%s) %s (UTC+8)",
|
||||
local.Format("2006-01-02"), wd, local.Format("15:04"))
|
||||
}
|
||||
|
||||
// ComposeDialoguePrompt 依据结构化规范编排完整提示词
|
||||
// 布局结构 (自上而下):
|
||||
// 1. 【工作记忆】 task_state (DST) + PendingExpectation 挂起建议 + InteractiveCard 状态
|
||||
@@ -67,6 +99,7 @@ func (mc *MemoryComposer) ComposeDialoguePrompt(userInput string, userCtx *UserP
|
||||
// 2. 【项目基准与协同上下文】 项目基准信息、待办事项、黑板事实 (声明最高优先级)
|
||||
// -------------------------------------------------------------
|
||||
sb.WriteString("【项目基准与协同上下文】\n")
|
||||
sb.WriteString(fmt.Sprintf("- 当前时间: %s\n", formatDialogueClock(mc.now())))
|
||||
sb.WriteString(fmt.Sprintf("- 当前项目: %s (ID: %s)\n", userCtx.Project.Name, userCtx.Project.ID))
|
||||
sb.WriteString(fmt.Sprintf("- 阶段: %s | 当前工作流: %s\n", userCtx.Project.CurrentStage, userCtx.Project.CurrentWorkflow))
|
||||
if userCtx.Project.DeviceType != "" || userCtx.Project.TargetMarket != "" {
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
|
||||
func TestMemoryComposer_HierarchyAndPriority(t *testing.T) {
|
||||
composer := coordinator.NewMemoryComposer()
|
||||
fixed := time.Date(2026, 9, 23, 10, 41, 0, 0, time.FixedZone("CST", 8*3600))
|
||||
composer.NowFn = func() time.Time { return fixed }
|
||||
|
||||
taskState := ctxmgr.NewConversationTaskState()
|
||||
taskState.ActiveTask = "立项摸底"
|
||||
@@ -73,6 +75,11 @@ func TestMemoryComposer_HierarchyAndPriority(t *testing.T) {
|
||||
|
||||
prompt := composer.ComposeDialoguePrompt("那后续的型检送检如何安排?", userCtx)
|
||||
|
||||
wantClock := "- 当前时间: 2026-09-23 (周三) 10:41 (UTC+8)"
|
||||
if !strings.Contains(prompt, wantClock) {
|
||||
t.Errorf("expected prompt to contain current-time anchor %q, got:\n%s", wantClock, prompt)
|
||||
}
|
||||
|
||||
// 验证包含各个结构化分层标记
|
||||
sections := []string{
|
||||
"【工作记忆】",
|
||||
|
||||
Reference in New Issue
Block a user