Move bizdev/freedoc/intent/projectworkflow/squad giant files into ≤800-line use-case siblings without behavior change; short tests green.
206 lines
11 KiB
Go
206 lines
11 KiB
Go
package squad
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// buildContextSystem 构建具有权威度说明的 context/ 体系
|
|
func (e *WorkspaceEngine) buildContextSystem(workDir string, req WorkspacePrepareRequest) error {
|
|
ctxDir := filepath.Join(workDir, "context")
|
|
_ = os.MkdirAll(filepath.Join(ctxDir, "materials_text"), 0o755)
|
|
_ = os.MkdirAll(filepath.Join(ctxDir, "knowledge"), 0o755)
|
|
_ = os.MkdirAll(filepath.Join(ctxDir, "prior_docs"), 0o755)
|
|
_ = os.MkdirAll(filepath.Join(ctxDir, "memory"), 0o755)
|
|
|
|
var contextItems []ContextItem
|
|
|
|
// 1. user.md
|
|
userContent := req.UserContext
|
|
if strings.TrimSpace(userContent) == "" {
|
|
userContent = fmt.Sprintf("# 用户角色与偏好\n\n- 用户角色: 商务与技术负责人\n- 决策风格: 严谨可追溯,注重四段式论据\n- 授权边界: 预算与商业承诺必须经由用户拍板\n")
|
|
}
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "user.md"), []byte(userContent), 0o644)
|
|
contextItems = append(contextItems, ContextItem{
|
|
File: "context/user.md",
|
|
Description: "操作用户的业务角色、决策授权边界与协同偏好",
|
|
AuthorityLevel: "authoritative",
|
|
Guidance: "必须尊重用户设定的目标市场与边界,用户未决事项一律列入待确认清单,严禁越权替用户拍板",
|
|
})
|
|
|
|
// 2. project.md
|
|
projContent := req.ProjectContext
|
|
if strings.TrimSpace(projContent) == "" {
|
|
projContent = fmt.Sprintf("# 项目全局基线\n\n- 项目名称: %s\n- 客户名称: %s\n- 产品类型: %s\n- 预期用途: %s\n- 目标市场: %s\n",
|
|
req.ProjectName, req.CustomerName, req.DeviceType, req.IntendedUse, req.TargetMarket)
|
|
}
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "project.md"), []byte(projContent), 0o644)
|
|
contextItems = append(contextItems, ContextItem{
|
|
File: "context/project.md",
|
|
Description: "项目立项章程与全局事实基线",
|
|
AuthorityLevel: "authoritative",
|
|
Guidance: "本项目核心前提。所有后续分析论证不得推翻已锁定的项目名称与核心预期用途",
|
|
})
|
|
|
|
// 3. agent.md
|
|
agentContent := req.AgentContext
|
|
if strings.TrimSpace(agentContent) == "" {
|
|
agentContent = "# 智能体小队协同通用规范\n\n- 事实闭环: 严禁凭空捏造未提供之参数或法规\n- 引用规范: 引用必须标注出处〔材料:...〕或〔法规:...〕\n- 负向红线: 禁止敷衍写作;各章节须实质展开,不得以篇幅或省略写法跳过应写内容\n"
|
|
}
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "agent.md"), []byte(agentContent), 0o644)
|
|
contextItems = append(contextItems, ContextItem{
|
|
File: "context/agent.md",
|
|
Description: "智能体通用协同纪律与引用要求",
|
|
AuthorityLevel: "authoritative",
|
|
Guidance: "所有小队角色必须严格执行的专业守则与写作禁令",
|
|
})
|
|
|
|
// 4. materials_text/
|
|
if len(req.Materials) > 0 {
|
|
var allBuilder strings.Builder
|
|
for _, m := range req.Materials {
|
|
name := filepath.Base(m.Filename)
|
|
if name == "" || name == "." {
|
|
name = "material.txt"
|
|
}
|
|
|
|
// 自动提取纯文本并注入页码锚点 <!-- page N -->
|
|
textContent, err := ExtractTextWithAnchors(name, m.Content)
|
|
if err != nil || strings.TrimSpace(textContent) == "" {
|
|
textContent = string(m.Content)
|
|
}
|
|
textBytes := []byte(textContent)
|
|
|
|
p := filepath.Join(ctxDir, "materials_text", name)
|
|
_ = os.WriteFile(p, textBytes, 0o644)
|
|
// 若原文件扩展名不是 .txt / .md,同时生成一份 .txt 方便智能体直接阅读
|
|
if !strings.HasSuffix(name, ".txt") && !strings.HasSuffix(name, ".md") {
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "materials_text", name+".txt"), textBytes, 0o644)
|
|
}
|
|
|
|
// Also write to root materials_text/ for backward compatibility
|
|
rootMatDir := filepath.Join(workDir, "materials_text")
|
|
_ = os.MkdirAll(rootMatDir, 0o755)
|
|
_ = os.WriteFile(filepath.Join(rootMatDir, name), textBytes, 0o644)
|
|
if !strings.HasSuffix(name, ".txt") && !strings.HasSuffix(name, ".md") {
|
|
_ = os.WriteFile(filepath.Join(rootMatDir, name+".txt"), textBytes, 0o644)
|
|
}
|
|
|
|
allBuilder.WriteString(fmt.Sprintf("\n\n=== 文件: %s ===\n\n", name))
|
|
allBuilder.WriteString(textContent)
|
|
|
|
contextItems = append(contextItems, ContextItem{
|
|
File: filepath.ToSlash(filepath.Join("context", "materials_text", name)),
|
|
Description: fmt.Sprintf("用户上传的输入材料文本: %s", name),
|
|
AuthorityLevel: "primary_evidence",
|
|
Guidance: "核心原始证据。提取参数需标明出处;材料内部矛盾需在矛盾表中并列呈现",
|
|
})
|
|
}
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "materials_text", "_all.txt"), []byte(allBuilder.String()), 0o644)
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "materials_text", "README.md"), []byte("# 原始材料清单\n\n已汇总在 `context/materials_text/_all.txt`,亦可单独查阅各文件。\n"), 0o644)
|
|
} else {
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "materials_text", "README.md"), []byte("# 原始材料说明\n\n本项目当前未提供独立的附加文本材料,所有事实依据以 `context/project.md`(项目全局基线)、`context/user.md`(用户要求与偏好)以及 `context/knowledge/hits.md`(法规标准知识库)为准。\n"), 0o644)
|
|
}
|
|
|
|
// 5. knowledge/
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "knowledge", "WEB_CROSSCHECK.md"), []byte("# 法规检索与交叉核对原则\n\n1. 知识库条目(hits.md)为首要法规事实依据。\n2. 如需查询外部信息,以国家药监局官方分类目录为主。\n"), 0o644)
|
|
if len(req.KnowledgeHits) > 0 {
|
|
var kbBuilder strings.Builder
|
|
kbBuilder.WriteString("# 权威法规与技术标准引用库 (Regulatory Knowledge Hits)\n\n")
|
|
kbBuilder.WriteString("以下为针对本项目研发与注册需求命中的权威法规、国家标准(GB)与行业标准(YY)条款。\n")
|
|
kbBuilder.WriteString("在输出交付物时,请结合上下文使用,并显式标注【引用标记 (CiteKey)】标明出处。\n\n")
|
|
for _, hit := range req.KnowledgeHits {
|
|
kbBuilder.WriteString(fmt.Sprintf("### [%s] %s %s\n\n", strings.ToUpper(hit.Category), hit.DocCode, hit.Title))
|
|
if hit.HeadingPath != "" {
|
|
kbBuilder.WriteString(fmt.Sprintf("- **章节路径**: %s\n", hit.HeadingPath))
|
|
}
|
|
if hit.CiteKey != "" {
|
|
kbBuilder.WriteString(fmt.Sprintf("- **引用标记 (CiteKey)**: `%s`\n", hit.CiteKey))
|
|
}
|
|
kbBuilder.WriteString(fmt.Sprintf("- **来源**: %s\n\n", hit.Source))
|
|
kbBuilder.WriteString(hit.Content + "\n\n---\n\n")
|
|
}
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "knowledge", "hits.md"), []byte(kbBuilder.String()), 0o644)
|
|
contextItems = append(contextItems, ContextItem{
|
|
File: "context/knowledge/hits.md",
|
|
Description: "法规知识库中命中的医疗器械分类、国家标准与指导原则",
|
|
AuthorityLevel: "reference_standard",
|
|
Guidance: "合规与技术论据源。引用必须标注标准号与引用标记 (CiteKey);涉及审批结论需标注'需向药监局审评中心确认'",
|
|
})
|
|
}
|
|
|
|
// 6. prior_docs/
|
|
if len(req.PriorDocs) > 0 {
|
|
var indexBuilder strings.Builder
|
|
indexBuilder.WriteString("# 前序阶段交付物索引\n\n")
|
|
for _, doc := range req.PriorDocs {
|
|
filename := doc.Filename
|
|
if filename == "" {
|
|
filename = doc.DocKey + ".md"
|
|
}
|
|
p := filepath.Join(ctxDir, "prior_docs", filename)
|
|
_ = os.WriteFile(p, []byte(doc.Content), 0o644)
|
|
indexBuilder.WriteString(fmt.Sprintf("- [%s] `%s`: %s (阶段: %s)\n", doc.DocKey, filename, doc.Title, doc.Stage))
|
|
|
|
contextItems = append(contextItems, ContextItem{
|
|
File: filepath.ToSlash(filepath.Join("context", "prior_docs", filename)),
|
|
Description: fmt.Sprintf("前序已归档文档: %s (%s)", doc.Title, doc.DocKey),
|
|
AuthorityLevel: "primary_evidence",
|
|
Guidance: "前序阶段已批准的结论基准。本阶段内容应与前序结论保持自洽",
|
|
})
|
|
}
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "prior_docs", "index.md"), []byte(indexBuilder.String()), 0o644)
|
|
} else {
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "prior_docs", "README.md"), []byte("# 前序交付物说明\n\n本项目当前处于启动阶段,暂无已归档的前序阶段交付物。\n"), 0o644)
|
|
}
|
|
|
|
if req.TemplateContent != "" {
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "template.md"), []byte(req.TemplateContent), 0o644)
|
|
}
|
|
|
|
// 7. memory/
|
|
if len(req.MemoryBankFiles) > 0 {
|
|
for k, v := range req.MemoryBankFiles {
|
|
p := filepath.Join(ctxDir, "memory", k)
|
|
_ = os.WriteFile(p, []byte(v), 0o644)
|
|
}
|
|
} else {
|
|
// 默认四大核心资产骨架
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "memory", "projectbrief.md"), []byte(fmt.Sprintf("# 项目章程\n- 项目: %s\n- 客户: %s\n", req.ProjectName, req.CustomerName)), 0o644)
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "memory", "activeContext.md"), []byte(fmt.Sprintf("# 当前焦点\n- 阶段: %s\n- 时间: %s\n", req.TaskKey, time.Now().Format("2006-01-02 15:04"))), 0o644)
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "memory", "systemPatterns.md"), []byte("# 规范与模式\n- 遵循四段式结构与严格角色边界\n- 单一真实源原则\n"), 0o644)
|
|
_ = os.WriteFile(filepath.Join(ctxDir, "memory", "progress.md"), []byte(fmt.Sprintf("# 进度\n- 当前正在进行: %s\n", req.StageTitle)), 0o644)
|
|
}
|
|
contextItems = append(contextItems, ContextItem{
|
|
File: "context/memory/",
|
|
Description: "长期项目记忆银行 (ActiveContext, Progress, ProjectBrief, SystemPatterns)",
|
|
AuthorityLevel: "supplementary",
|
|
Guidance: "协同上下文背景参考,防止上下文遗忘",
|
|
})
|
|
|
|
// 8. 写入 context/index.json
|
|
ctxIndex := ContextIndex{Items: contextItems}
|
|
if err := writeJSON(filepath.Join(ctxDir, "index.json"), ctxIndex); err != nil {
|
|
return err
|
|
}
|
|
|
|
// 9. 写入 context/README.md 指引
|
|
readmeContent := `# 上下文使用指引 (Context Reading Guide)
|
|
|
|
本目录为当前智能体任务的自包含上下文沙箱。所有智能体专员在执行任务时,必须按照以下权威度等级(Authority Level)采纳信息:
|
|
|
|
| 权威度级别 | 适用文件类别 | 采信与处理规则 |
|
|
|---|---|---|
|
|
| **authoritative** | ` + "`user.md`" + `, ` + "`project.md`" + `, ` + "`agent.md`" + ` | **最高权威**。必须绝对遵循;严禁越权替用户拍板,未决项列入待确认清单 |
|
|
| **primary_evidence** | ` + "`materials_text/*`" + `, ` + "`prior_docs/*`" + ` | **原始事实证据**。引用必须标明出处条文;若材料间存在矛盾,并列呈现 |
|
|
| **reference_standard** | ` + "`knowledge/hits.md`" + ` | **标准与法规依据**。必须指明法规/标准代码;涉及监管裁定标注需窗口核实 |
|
|
| **supplementary** | ` + "`memory/*`" + ` | **补充记忆背景**。仅供背景了解,不作为唯一支撑证据 |
|
|
|
|
---
|
|
`
|
|
return os.WriteFile(filepath.Join(ctxDir, "README.md"), []byte(readmeContent), 0o644)
|
|
}
|