Files
Agent-System/internal/squad/extract.go
T
agent-bot e47cfd8f4e
CI / go-vet (pull_request) Successful in 12s
CI / short-tests-report (pull_request) Failing after 36s
CI / backup-restore-drill (pull_request) Successful in 24s
CI / web (pull_request) Successful in 32s
refactor(extract): move text extraction to internal/textextract
KTD1 third step: material parsing returns to its own capability.
- ExtractTextWithAnchors + docx/anchor helpers move from squad/extract.go
  to internal/textextract (pure stdlib, no workflowdef dep)
- squad/extract.go keeps only ExtractAdvisorBriefing (squad domain logic)
- callers updated: squad context builders, bizdev materials, server
  chat_document_reader, materialindex pipeline
- reverse dependency materialindex -> squad eliminated (KTD1 explicit goal)
- module-boundaries.yaml: textextract declared + 4 edges
- verified: build/vet clean, arch green, textextract/squad/materialindex/
  bizdev tests green; server pre-existing env-dependent failure unchanged
  (issue #2, fails with and without this change)
2026-09-29 06:11:20 +00:00

178 lines
7.3 KiB
Go

package squad
import (
"fmt"
"strings"
"chat-agent-system/internal/workflowdef"
)
// ExtractAdvisorBriefing 从 output.md 与黑板事实中提取 B 轨业务参谋决策锦囊
func ExtractAdvisorBriefing(taskKey string, markdownBody string, fields map[string]string) *AdvisorBriefing {
if fields == nil {
fields = make(map[string]string)
}
projectName := fields["project_name"]
customerName := fields["customer_name"]
deviceType := fields["device_type"]
if projectName == "" {
projectName = "本项目"
}
if customerName == "" {
customerName = "客户"
}
if deviceType == "" {
deviceType = "医疗器械"
}
brief := &AdvisorBriefing{
MaturityScore: 60,
MaturityLevel: "V0.1",
MaturityHeadline: "已提取基础事实框架,关键技术与商务参数已按行业基准预设",
BaselineAssumptions: []string{},
PreemptiveRisks: []string{},
NextBestActions: []string{},
CommunicationKit: "",
}
// 1. 扫描文档提取待确认项与假定
lines := strings.Split(markdownBody, "\n")
inPendingSection := false
var pendingItems []string
for _, line := range lines {
trim := strings.TrimSpace(line)
if strings.HasPrefix(trim, "## 待确认清单") {
inPendingSection = true
continue
}
if inPendingSection && strings.HasPrefix(trim, "## ") {
inPendingSection = false
}
if inPendingSection {
if strings.HasPrefix(trim, "- ") || strings.HasPrefix(trim, "* ") {
item := strings.TrimLeft(trim, "-* ")
if len(item) > 3 && !strings.HasPrefix(item, "(逐条") {
pendingItems = append(pendingItems, item)
}
}
}
}
// 2. 根据任务类型与提取事实定制成熟度、风险与话术
taskKey = workflowdef.ImplementationTaskKey(taskKey)
switch taskKey {
case "intake", "user_requirements":
brief.MaturityScore = 65
brief.MaturityLevel = "V0.1"
brief.MaturityHeadline = fmt.Sprintf("已完成「%s」的初始事实沉淀,当前具备进入方案与法规初筛的基准条件", projectName)
brief.BaselineAssumptions = []string{
"硬件与模具研发暂按国内 ISO 13485 标准 CDMO 体系工程规范核算",
"临床评价路径暂按免临床评价/同品种对比做基准成本估算",
"软件若涉及 AI 或算法质控,暂假定数据不出域、私有化部署",
}
brief.PreemptiveRisks = []string{
"若客户宣称中包含实质性疗效或治疗作用,可能触发器械分类升档(如二类转三类),研发与注册周期将增加 6-12 个月",
"时间与预算尚缺客户书面口径,直接对外报价存在后续争议风险",
}
brief.NextBestActions = []string{
"使用下方微信话术与客户负责人核实 3 个关键事实口径",
"同意当前基准假设,直接触发 V0.5 阶段(方案成型与法规对齐)",
"上传新的客户沟通纪要或补充材料进行局部刷新",
}
topClarifications := "1. 目标注册区域是否锁定中国 NMPA;\n2. 期望的样机测试与立项交付节点;\n3. 是否有指定采购的元器件品牌或特定工艺要求。"
if len(pendingItems) > 0 {
var picked []string
for _, pi := range pendingItems {
if len(picked) >= 3 {
break
}
if len(pi) > 100 {
pi = pi[:100] + "..."
}
picked = append(picked, pi)
}
topClarifications = ""
for idx, p := range picked {
topClarifications += fmt.Sprintf("%d. %s\n", idx+1, p)
}
}
brief.CommunicationKit = fmt.Sprintf(
"【建议发送给 %s 负责人的微信沟通草稿】\n"+
"----------------------------------------\n"+
"您好!关于「%s」的立项需求,我们小队已基于您此前提供的资料做了梳理,搭建了一版立项需求与方案底表。\n\n"+
"为了方便我们后续为您更精准地测算开发周期和成本,想向您请教并确认几个核心细节:\n"+
"%s\n"+
"您可以随时语音或文字简单告知,我们随时跟进更新!\n"+
"----------------------------------------",
customerName, projectName, topClarifications,
)
case "research", "feasibility_reg", "feasibility_dev", "prelim_design":
brief.MaturityScore = 80
brief.MaturityLevel = "V0.5"
brief.MaturityHeadline = fmt.Sprintf("「%s」的设计方案框架与法规路径已成型,已具备进行详细 WBS 拆解与报价的条件", projectName)
brief.BaselineAssumptions = []string{
"系统架构采用模块化分层设计,软硬件接口保持独立兼容",
"电气安全与电磁兼容暂按 GB 9706.1-2020 / YY 0505 标准预留检测整改预算",
"关键物料与传感器优先考虑国内成熟供应链以确保交付周期可控",
}
brief.PreemptiveRisks = []string{
"关键元器件交付周期可能受供应链波动影响,建议首期备料周期预留 4 周冗余",
"新国标实施对型式检验提出更严格要求,需警惕实验室排队时间窗口",
}
brief.NextBestActions = []string{
"审阅初步系统架构图与法规分类论证说明",
"进入 V1.0 商业锁定阶段,生成明细 CDO 报价单与 Excel 核算模型",
}
brief.CommunicationKit = fmt.Sprintf(
"【建议发送给 %s 的方案汇报提纲】\n"+
"----------------------------------------\n"+
"您好!关于「%s」,我们已完成了技术路线比选与法规路径分析:\n"+
"1. 系统初步架构及子模块边界已清晰,推荐选用高成熟度国产供应链方案;\n"+
"2. 注册路径与检验标准已完成梳理,预计整体开发与型检周期可控;\n"+
"我们正在为您生成精细化的 CDO 工时与成本核算表,如有最新调整敬请告知。\n"+
"----------------------------------------",
customerName, projectName,
)
case "cdo_quote":
brief.MaturityScore = 95
brief.MaturityLevel = "V1.0"
brief.MaturityHeadline = fmt.Sprintf("「%s」已完成全模块 WBS 工时拆解与成本测算,具备合同签约与立项启动条件", projectName)
brief.BaselineAssumptions = []string{
"按水木东方标准 CDMO 研发工时单价模型测算,包含设计、工艺、质量与项目管理全环节",
"第三方注册检测规费按当前国家认可实验室公允价格预留",
}
brief.PreemptiveRisks = []string{
"客户中途若增加子功能或改动工业设计外壳,会直接影响模具及结构费用,合同需设变更保护条款",
}
brief.NextBestActions = []string{
"下载并审阅 CDO报价核算-V01.xlsx 明细表",
"根据谈判空间决策是否给予商务折扣优惠",
"确认并锁定项目立项任务书与商务合同草稿",
}
brief.CommunicationKit = fmt.Sprintf(
"【商务报价呈送微信/邮件草稿】\n"+
"----------------------------------------\n"+
"尊敬的 %s 领导:\n"+
"您好!关于「%s」的 CDMO 研发与制造服务方案,我们技术与商务团队已完成最终核算:\n"+
"附件为详细报价核算明细表,涵盖从设计输入、详细工程开发、样品试制到注册检验的全流程工时分解。\n"+
"期待与您进一步探讨商务细节,预祝合作顺利!\n"+
"----------------------------------------",
customerName, projectName,
)
default:
brief.MaturityScore = 70
brief.MaturityLevel = "V0.5"
brief.MaturityHeadline = fmt.Sprintf("已完成任务「%s」的阶段交付与事实沉淀", taskKey)
brief.NextBestActions = []string{"审阅产出正文", "推进到下一阶段"}
}
return brief
}