Files
Agent-System/internal/coordinator/intent_reply_llm.go
T
weichen d55488ae37 feat(coordinator): 用智能体叙述查询、帮助与操作回执
确定性事实仍由代码生成;PI Agent 只组织语言,失败或关闭开关时回退原模板。安全拦截与确认提问保持模板。
2026-09-28 03:25:06 +00:00

74 lines
3.2 KiB
Go

package coordinator
import (
"context"
"log/slog"
"strings"
"chat-agent-system/internal/intent"
)
// FactBlock 是本轮答复允许引用的确定性事实。模型只组织语言,不补充事实。
type FactBlock struct {
Title string
Body string
}
const agentNarrativeInstruction = `只把【本轮事实】中的「系统确定性底稿」改写成项目助手的自然中文,并写入 reply.md。
不得新增底稿没有的标准编号、条款、数值、文档状态或执行结果;不得删除底稿中的文档名、路径、数字与〔引用键〕。
若底稿写明尚未执行、需要确认或请到项目设置界面操作,答复必须保留该边界,不得宣称已经完成。`
const intentReplyInstructions = `你是「水木东方」医疗器械「项目助手」。只以项目助手自称。
本轮任务是把系统已经确定的事实组织成自然答复,不是重新检索或执行操作。
1. 【本轮事实】是唯一事实来源。底稿未写出的标准编号、条款、项目数据、文档状态与执行结果一律不得编造。
2. 保留底稿中的文档名、路径、数字、角色与〔引用键〕。
3. 底稿若说明需要确认、权限不足、或请到项目设置界面操作,必须原意保留,不得改成已经执行。
4. 使用中文,只把最终答复写入 reply.md。`
// narrateReply 优先用智能体改写确定性底稿;失败或关闭时原样返回模板。
func (e *CoordinatorEngine) narrateReply(
ctx context.Context,
userCtx *UserProjectContext,
handler string,
it intent.IntentType,
rawInput, template, instruction string,
) (string, string) {
template = strings.TrimSpace(template)
if template == "" || !agentReplyLLMEnabled() || e == nil || e.dialogueBackend == nil {
return template, ReplySourceTemplate
}
if strings.TrimSpace(instruction) == "" {
instruction = agentNarrativeInstruction
}
e.ensureProjectDialogueSnippet(ctx, userCtx)
prompt := NewMemoryComposer().ComposeIntentReplyPrompt(rawInput, userCtx, string(it), []FactBlock{{
Title: "系统确定性底稿",
Body: template,
}}, instruction)
content, reason := e.runDialogueBackend(ctx, userCtx, prompt, intentReplyInstructions, "intent_reply_generating", "正在组织项目助手回复...")
if reason != "" || strings.TrimSpace(content) == "" {
if reason != "" && reason != "backend_nil" && reason != "no_data_dir" {
slog.InfoContext(ctx, "agent_reply_fallback", "handler", handler, "intent", string(it), "reason", reason)
}
return template, ReplySourceTemplate
}
return strings.TrimSpace(content), ReplySourcePiAgent
}
// applyNarrative 用智能体叙述替换已生成结果的 NaturalReply,并写入 ReplySource。
// 调用方只应对知识叙述、帮助导航和已完成/已受理通报使用;确认提问与权限拒绝不要调用。
func (e *CoordinatorEngine) applyNarrative(
ctx context.Context,
userCtx *UserProjectContext,
handler, rawInput, instruction string,
res *CoordinationResult,
) *CoordinationResult {
if res == nil {
return nil
}
reply, source := e.narrateReply(ctx, userCtx, handler, res.Intent, rawInput, res.NaturalReply, instruction)
res.NaturalReply = reply
res.ReplySource = source
return res
}