311 lines
10 KiB
Go
311 lines
10 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"chat-agent-system/internal/bizdev"
|
|
"chat-agent-system/internal/config"
|
|
"chat-agent-system/internal/squad"
|
|
)
|
|
|
|
func main() {
|
|
stageFlag := flag.String("stage", "", "执行成熟度阶段 (可选: V0.1_triage, V0.5_feasibility, V1.0_quoting)")
|
|
taskKey := flag.String("task", "intake", "任务标识 (intake, research, cdo_quote 等)")
|
|
backend := flag.String("backend", "", "智能体后端 (留空则读取 .env 配置,可选: stub, pi-agent, cursor-cli)")
|
|
project := flag.String("project", "医疗器械研发演示项目", "项目名称")
|
|
customer := flag.String("customer", "北京水木东方", "客户名称")
|
|
device := flag.String("device", "医疗器械", "设备类型")
|
|
use := flag.String("use", "临床诊疗", "预期用途")
|
|
market := flag.String("market", "国内", "目标市场")
|
|
materials := flag.String("materials", "", "输入补充材料内容")
|
|
materialsDir := flag.String("materials-dir", "", "输入材料文件夹路径 (批量载入目录下所有文件)")
|
|
envPath := flag.String("env", ".env", "环境配置文件路径")
|
|
inspectDir := flag.String("inspect", "", "审查并查看指定工作区状态目录")
|
|
autoRepair := flag.Bool("auto-repair", true, "启用质量门禁自动闭环修正")
|
|
listTasks := flag.Bool("list-tasks", false, "列出所有受支持的商务对接任务清单")
|
|
listStages := flag.Bool("list-stages", false, "列出所有受支持的商务成熟度阶段清单")
|
|
addStep := flag.String("add-step", "", "动态向工作区追加任务步骤 (格式: role:title,配合 -inspect)")
|
|
flag.Parse()
|
|
|
|
cfg, err := config.Load(*envPath)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "加载配置失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
svc := bizdev.NewService(cfg)
|
|
|
|
// 0. 列出支持的阶段
|
|
if *listStages {
|
|
fmt.Println("=== 商务对接成熟度螺旋收敛阶段清单 ===")
|
|
for _, st := range svc.ListStages() {
|
|
fmt.Printf("- %-18s: %s\n", st.Stage, st.Title)
|
|
fmt.Printf(" 说明: %s\n", st.Description)
|
|
fmt.Printf(" 包含任务: %v\n", st.Tasks)
|
|
}
|
|
return
|
|
}
|
|
|
|
// 1. 列出支持的任务
|
|
if *listTasks {
|
|
fmt.Println("=== 商务对接小队支持的独立任务清单 ===")
|
|
for _, t := range svc.ListTasks() {
|
|
fmt.Printf("- %-18s: %s (交付: %s)\n", t.Key, t.Title, t.ArtifactTitle)
|
|
if t.Description != "" {
|
|
fmt.Printf(" 说明: %s\n", t.Description)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// 2. 审查已有工作区
|
|
if *inspectDir != "" {
|
|
handleInspect(context.Background(), svc, *inspectDir, *addStep, *backend)
|
|
return
|
|
}
|
|
|
|
// 3. 执行阶段或常规任务
|
|
activeBackend := cfg.AgentBackend
|
|
if *backend != "" {
|
|
activeBackend = *backend
|
|
cfg.AgentBackend = *backend
|
|
}
|
|
|
|
var materialsList []bizdev.MaterialInput
|
|
if *materialsDir != "" {
|
|
entries, err := os.ReadDir(*materialsDir)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "读取材料目录失败 %s: %v\n", *materialsDir, err)
|
|
os.Exit(1)
|
|
}
|
|
for _, entry := range entries {
|
|
if entry.IsDir() || strings.HasPrefix(entry.Name(), ".") {
|
|
continue
|
|
}
|
|
fPath := filepath.Join(*materialsDir, entry.Name())
|
|
content, err := os.ReadFile(fPath)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "读取材料文件失败 %s: %v\n", fPath, err)
|
|
continue
|
|
}
|
|
materialsList = append(materialsList, bizdev.MaterialInput{
|
|
Filename: entry.Name(),
|
|
Content: content,
|
|
})
|
|
fmt.Printf("已载入输入材料: %s (%d 字节)\n", entry.Name(), len(content))
|
|
}
|
|
}
|
|
if *materials != "" {
|
|
materialsList = append(materialsList, bizdev.MaterialInput{
|
|
Filename: "user_input.md",
|
|
Content: []byte(*materials),
|
|
})
|
|
}
|
|
|
|
// 如果指定了 -stage,执行阶段编排
|
|
if *stageFlag != "" {
|
|
handleStageRun(context.Background(), svc, *stageFlag, *project, *customer, *device, *use, *market, activeBackend, *autoRepair, materialsList)
|
|
return
|
|
}
|
|
|
|
res, err := svc.ExecuteTask(context.Background(), bizdev.TaskRunRequest{
|
|
TaskKey: *taskKey,
|
|
ProjectName: *project,
|
|
CustomerName: *customer,
|
|
DeviceType: *device,
|
|
IntendedUse: *use,
|
|
TargetMarket: *market,
|
|
Materials: materialsList,
|
|
Backend: activeBackend,
|
|
AutoRepair: *autoRepair,
|
|
})
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "任务执行失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if !res.Success {
|
|
fmt.Fprintf(os.Stderr, "警告: 后端执行未完全成功 [%s]: %s\n", res.TaskKey, res.ErrorReason)
|
|
}
|
|
|
|
fmt.Printf("=== 任务执行成功 [%s] ===\n", res.TaskKey)
|
|
fmt.Printf("耗时: %v\n", res.Duration)
|
|
fmt.Printf("工作区沙箱: %s\n", res.WorkDir)
|
|
fmt.Printf("交付文档产物: %s\n", filepath.Join(res.WorkDir, "output.md"))
|
|
if _, err := os.Stat(filepath.Join(res.WorkDir, "execution.log")); err == nil {
|
|
fmt.Printf("执行审计日志: %s\n", filepath.Join(res.WorkDir, "execution.log"))
|
|
}
|
|
fmt.Printf("使用的后端: %s\n", res.BackendUsed)
|
|
|
|
// 打印质量门禁与指纹报告
|
|
if res.QualityReport != nil {
|
|
fmt.Printf("质量门禁评分: %.1f / 100 (达标: %v)\n", res.QualityReport.Score, res.QualityReport.Passed)
|
|
if len(res.QualityReport.Violations) > 0 {
|
|
fmt.Printf("门禁检测项:\n")
|
|
for _, v := range res.QualityReport.Violations {
|
|
fmt.Printf(" - %s\n", v)
|
|
}
|
|
}
|
|
}
|
|
if res.Manifest != nil {
|
|
fmt.Printf("审计指纹清单: %s (追踪文件数: %d)\n", filepath.Join(res.WorkDir, "manifest.json"), len(res.Manifest.Files))
|
|
}
|
|
|
|
if len(res.ExtractedData) > 0 {
|
|
fmt.Printf("黑板抽取事实字段:\n")
|
|
for k, v := range res.ExtractedData {
|
|
fmt.Printf(" - %s: %s\n", k, v)
|
|
}
|
|
}
|
|
|
|
if len(res.Attachments) > 0 {
|
|
fmt.Printf("附加产物:\n")
|
|
for _, a := range res.Attachments {
|
|
p := filepath.Join(res.WorkDir, a.Filename)
|
|
fmt.Printf(" - %s (%d bytes) -> %s\n", a.Filename, len(a.Data), p)
|
|
}
|
|
}
|
|
|
|
// 打印 B 轨业务参谋锦囊
|
|
if res.AdvisorBrief != nil {
|
|
printAdvisorBrief(res.AdvisorBrief)
|
|
}
|
|
}
|
|
|
|
func handleStageRun(
|
|
ctx context.Context,
|
|
svc bizdev.BizdevService,
|
|
stageName string,
|
|
project, customer, device, use, market, backend string,
|
|
autoRepair bool,
|
|
materials []bizdev.MaterialInput,
|
|
) {
|
|
fmt.Printf("\n>>> 启动商务对接成熟度阶段: [%s] <<<\n", stageName)
|
|
stageRes, err := svc.ExecuteStage(ctx, bizdev.StageRunRequest{
|
|
Stage: bizdev.MaturityStage(stageName),
|
|
ProjectName: project,
|
|
CustomerName: customer,
|
|
DeviceType: device,
|
|
IntendedUse: use,
|
|
TargetMarket: market,
|
|
Backend: backend,
|
|
AutoRepair: autoRepair,
|
|
Materials: materials,
|
|
})
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "阶段执行失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Printf("\n=== 阶段执行成功: %s ===\n", stageRes.StageTitle)
|
|
fmt.Printf("总耗时: %v\n", stageRes.Duration)
|
|
fmt.Printf("阶段工作区根目录: %s\n", stageRes.WorkDir)
|
|
fmt.Printf("包含交付任务数: %d\n", len(stageRes.SubArtifacts))
|
|
for idx, sub := range stageRes.SubArtifacts {
|
|
fmt.Printf(" [%d] %-18s: %s -> %s\n", idx+1, sub.TaskKey, sub.ArtifactTitle, filepath.Join(sub.WorkDir, "output.md"))
|
|
}
|
|
|
|
// 优先呈现 B 轨:贴身业务参谋锦囊
|
|
if stageRes.AdvisorBrief != nil {
|
|
printAdvisorBrief(stageRes.AdvisorBrief)
|
|
}
|
|
}
|
|
|
|
func printAdvisorBrief(brief *bizdev.AdvisorBriefing) {
|
|
fmt.Println("\n==================================================================")
|
|
fmt.Println(" 💡【B轨:贴身业务参谋决策锦囊】 ")
|
|
fmt.Println("==================================================================")
|
|
fmt.Printf("【成熟度诊断】 等级: %s | 完备度评分: %d / 100\n", brief.MaturityLevel, brief.MaturityScore)
|
|
fmt.Printf("【进展研判】 %s\n", brief.MaturityHeadline)
|
|
|
|
if len(brief.BaselineAssumptions) > 0 {
|
|
fmt.Println("\n【基准预设假设(替代填空,为您先行兜底)】:")
|
|
for _, a := range brief.BaselineAssumptions {
|
|
fmt.Printf(" • %s\n", a)
|
|
}
|
|
}
|
|
|
|
if len(brief.PreemptiveRisks) > 0 {
|
|
fmt.Println("\n【预见性风险雷达(提前预警后续法规/商务卡点)】:")
|
|
for _, r := range brief.PreemptiveRisks {
|
|
fmt.Printf(" ⚠️ %s\n", r)
|
|
}
|
|
}
|
|
|
|
if len(brief.NextBestActions) > 0 {
|
|
fmt.Println("\n【建议下一步行动(提供选项供您掌舵决策)】:")
|
|
for idx, act := range brief.NextBestActions {
|
|
fmt.Printf(" [%d] %s\n", idx+1, act)
|
|
}
|
|
}
|
|
|
|
if brief.CommunicationKit != "" {
|
|
fmt.Println("\n【对外赋能:建议沟通话术(可直接复制使用)】:")
|
|
fmt.Println(brief.CommunicationKit)
|
|
}
|
|
fmt.Println("==================================================================")
|
|
}
|
|
|
|
func handleInspect(ctx context.Context, svc bizdev.BizdevService, workDir string, addStepFlag string, backend string) {
|
|
state, err := svc.InspectWorkspace(ctx, workDir)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "查看工作区失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Printf("=== 工作区状态审查 [%s] ===\n", workDir)
|
|
fmt.Printf("项目: %s | 客户: %s | 阶段: %s | 状态: %s\n",
|
|
state.Metadata.ProjectName, state.Metadata.CustomerName, state.Metadata.TaskKey, state.Metadata.Status)
|
|
|
|
fmt.Printf("\n小队角色编制 (%d 位):\n", len(state.Squad.Agents))
|
|
for _, a := range state.Squad.Agents {
|
|
fmt.Printf(" - %-12s: %s (模型: %s, 工具: %v)\n", a.Role, a.Name, a.Model, a.Tools)
|
|
}
|
|
|
|
fmt.Printf("\n工作流步骤清单 (%d 步):\n", len(state.Workflow.Steps))
|
|
for _, s := range state.Workflow.Steps {
|
|
fmt.Printf(" - [%s] %-10s (%-8s) : %s -> %s\n", s.Status, s.ID, s.Role, s.Title, s.SubWorkspace)
|
|
}
|
|
|
|
if len(state.Blackboard.Fields) > 0 {
|
|
fmt.Printf("\n黑板抽取字段:\n")
|
|
for k, v := range state.Blackboard.Fields {
|
|
fmt.Printf(" - %s: %s\n", k, v)
|
|
}
|
|
}
|
|
|
|
// 若用户指定了动态追加步骤
|
|
if addStepFlag != "" {
|
|
parts := strings.SplitN(addStepFlag, ":", 2)
|
|
if len(parts) == 2 {
|
|
role := strings.TrimSpace(parts[0])
|
|
title := strings.TrimSpace(parts[1])
|
|
fmt.Printf("\n动态追加步骤: [%s] %s\n", role, title)
|
|
modifyReq := squad.ModifyWorkflowRequest{
|
|
WorkDir: workDir,
|
|
Backend: backend,
|
|
AddSteps: []squad.StepSpec{
|
|
{
|
|
ID: fmt.Sprintf("step_dyn_%d", len(state.Workflow.Steps)+1),
|
|
Role: role,
|
|
Title: title,
|
|
SubWorkspace: fmt.Sprintf("tasks/dyn_%s", role),
|
|
Mode: "execute",
|
|
},
|
|
},
|
|
}
|
|
runRes, err := svc.ResumeOrModifyWorkspace(ctx, modifyReq)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "执行动态追加步骤失败: %v\n", err)
|
|
} else {
|
|
fmt.Printf("动态步骤执行成功! 耗时: %v\n", runRes.Duration)
|
|
}
|
|
}
|
|
}
|
|
}
|