Files
Agent-System-Backend/internal/ctxasm/ctxasm.go
T
weichen 0d74ec94dd Implement the backend: store/bus/runmgr core
Details (original message):
Implement the backend: store/bus/runmgr core, mock and LLM runners (agent-native client), HITL three-branch routing, subagent runs, builtin and declarative HTTP tools, ingest parsers (md/txt/docx/pdf-text), knowledge fulltext, layered settings, profiles, plugin host, REST+SSE API, single-binary entrypoint; e2e and unit tests.
2026-09-29 17:53:24 +00:00

117 lines
3.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package ctxasm 实现上下文组装与预算管理(4.10):分层管线 + 可观测裁剪记录。
package ctxasm
import (
"fmt"
"strings"
agentllm "agent-native/runtime/llm"
core "agent-system-core"
)
// Report 记录一次组装的实际构成(可观测:模型看到了什么、裁了什么)。
type Report struct {
Items []string `json:"items"`
Truncations []string `json:"truncations,omitempty"`
EstChars int `json:"estChars"`
}
// AssembleInput 组装模型输入消息。
func AssembleInput(prof core.AgentProfile, eff core.EffectiveSettings, history []core.Message, currentUser core.Message, attachments []core.ParsedDocument, knowledge []core.RetrievalHit) ([]agentllm.Message, *Report) {
rep := &Report{}
msgs := []agentllm.Message{{Role: agentllm.RoleSystem, Content: prof.SystemPrompt}}
rep.Items = append(rep.Items, "system(画像)")
maxHist := prof.Context.MaxHistoryMessages
if maxHist <= 0 {
maxHist = int(eff.GetNumber("context.maxHistoryMessages", 40))
}
if maxHist <= 0 {
maxHist = 40
}
// 历史窗口(不含当前轮——当前轮单独注入附件与检索)。
start := 0
if len(history) > maxHist {
start = len(history) - maxHist
rep.Truncations = append(rep.Truncations, fmt.Sprintf("历史 %d 条超窗口 %d,仅保留最近部分", len(history), maxHist))
}
for _, m := range history[start:] {
if m.ID == currentUser.ID {
continue
}
content := renderMessage(m)
if content == "" {
continue
}
role := agentllm.RoleUser
if m.Role == core.RoleAssistant {
role = agentllm.RoleAssistant
}
msgs = append(msgs, agentllm.Message{Role: role, Content: content})
}
rep.Items = append(rep.Items, fmt.Sprintf("history(%d)", len(history)-start))
// 当前轮:文本 + 附件解析结果 + 知识检索注入。
var sb strings.Builder
for _, p := range currentUser.Parts {
if p.Type == core.PartText {
sb.WriteString(p.Text)
}
}
if len(attachments) > 0 {
sb.WriteString("\n\n[附件]\n")
for _, doc := range attachments {
text := doc.PlainText()
limit := 16000
if len(text) > limit {
text = text[:limit] + "…(附件超长截断)"
rep.Truncations = append(rep.Truncations, "附件 "+doc.Meta.Filename+" 超长截断")
}
fmt.Fprintf(&sb, "### %s\n%s\n", doc.Meta.Filename, text)
}
rep.Items = append(rep.Items, fmt.Sprintf("attachments(%d)", len(attachments)))
}
if len(knowledge) > 0 {
sb.WriteString("\n\n[相关知识]\n")
for i, h := range knowledge {
fmt.Fprintf(&sb, "[%d] %s(来源 %s)\n%s\n", i+1, h.Doc.Title, h.Doc.Source, h.Doc.Chunk)
}
rep.Items = append(rep.Items, fmt.Sprintf("knowledge(%d)", len(knowledge)))
}
msgs = append(msgs, agentllm.Message{Role: agentllm.RoleUser, Content: sb.String()})
for _, m := range msgs {
rep.EstChars += len(m.Content)
}
return msgs, rep
}
// renderMessage 把消息部件渲染为模型可读文本(思考部件不回灌;工具结果给摘要)。
func renderMessage(m core.Message) string {
var sb strings.Builder
for _, p := range m.Parts {
switch p.Type {
case core.PartText:
sb.WriteString(p.Text)
sb.WriteString("\n")
case core.PartToolCall:
if p.ToolCall != nil {
fmt.Fprintf(&sb, "[调用工具 %s(%s)]\n", p.ToolCall.Name, p.ToolCall.Arguments)
}
case core.PartToolResult:
if p.ToolResult != nil {
fmt.Fprintf(&sb, "[工具结果 %s %s]\n", p.ToolResult.Status, p.ToolResult.Summary)
}
case core.PartArtifactRef:
if p.Artifact != nil {
fmt.Fprintf(&sb, "[产出工件 %s]\n", p.Artifact.Name)
}
case core.PartHITL:
if p.Hitl != nil && p.Hitl.Resolved {
fmt.Fprintf(&sb, "[用户确认:%s → %s]\n", p.Hitl.Question, p.Hitl.Answer)
}
}
}
return strings.TrimSpace(sb.String())
}