merge: fix/intent-bizdev-boundary

This commit is contained in:
weichen
2026-09-15 10:29:38 +00:00
12 changed files with 160 additions and 46 deletions
+3 -2
View File
@@ -5,7 +5,7 @@
## 焦点
1. **TRACK-S**: [D6 doccontent](./short-term/2026-09-15-doccontent-canonical-boundary.md)(Track E 复盘已归档;dispatcher 仍 off。下一波 D7a/D5/D4)
1. **TRACK-S**: [D7a intent 解绑](./short-term/2026-09-15-intent-bizdev-boundary.md)(D6 已合入。下一波 D5/D4/D7b)
2. **CHAT-INTENT**: [对话意图运行时状态](../test-reports/2026-09-13-conversation-intent-runtime-state-test-report.md)(双标签 busy 已签:项目 **6275** / `DOC_BUSY` 单 run;不删 `sess-p143-u9` 探针)
3. **CHAT**: [对话历史刷新活验已补](../test-reports/2026-09-13-chat-history-refresh-test-report.md)(项目 6252 闲聊刷新同集 + guest 403;编制任务链仍开放)
@@ -64,7 +64,8 @@
| Cursor | event-e1b-workflow-advance | fix/event-workflow-advance | - | events | done |
| Cursor | event-e1c-projection-cutover | fix/event-projection-cutover | - | events | done |
| Cursor | event-e2-hook-guard | fix/event-e2-hook-guard | - | events | done |
| Cursor | doccontent-canonical-boundary | fix/doccontent-canonical-boundary | - | docgov | in_progress |
| Cursor | doccontent-canonical-boundary | fix/doccontent-canonical-boundary | - | docgov | done |
| Cursor | intent-bizdev-boundary | fix/intent-bizdev-boundary | - | intent | in_progress |
> 新任务开始前登记。`branch` 必须为 `feature/<slug>` 或 `fix/<slug>`,**禁止** `dev` / `test` / `main`。同一 `mutex` 同时仅一个 `in_progress`。
@@ -0,0 +1,39 @@
---
title: "D7a intent 与 bizdev 解绑"
status: in_progress
created: 2026-09-15
updated: 2026-09-15
owner: Cursor
branch: fix/intent-bizdev-boundary
worktree: "-"
mutex: intent
---
# D7a intent 与 bizdev 解绑
## 目标
intent 只依赖成熟度词汇叶子包,不再 import bizdev。
## 范围
- `internal/maturity`
- intent 生产与表征测试
- bizdev 类型别名保持兼容
## 非目标
- 不改识别规则
- 不迁 coordinator / chatflow(D5/D7b)
## 步骤
1. [x] 叶子包与别名
2. [x] intent 切断 bizdev
3. [x] 表征测试与 arch-check
4. [x] 报告
## 验收标准
- [x] intent 生产 import 无 bizdev
- [x] [S4a 报告](../../test-reports/2026-09-15-intent-bizdev-boundary-test-report.md)
+3 -1
View File
@@ -19,6 +19,7 @@ imports:
- docrevision
- freedoc
- kb
- maturity
- projectcontext
- projectworkflow
- quote
@@ -129,9 +130,10 @@ imports:
- replyexport
fsutil: []
intent:
- bizdev
- maturity
kb:
- config
maturity: []
notification: []
observability: []
projectcontext:
@@ -0,0 +1,52 @@
# D7a intent 与 bizdev 解绑测试报告
## 一、 测试背景 (Background)
intent 只为读取三档成熟度常量而 import 整个 bizdev,把识别层绑在编排根上。D7a 把常量抽到叶子包 `internal/maturity`。
## 二、 测试目标 (Test Objectives)
- 阶段 token 不变:`V0.1_triage` / `V0.5_feasibility` / `V1.0_quoting`。
- intent 生产代码与测试不再 import bizdev。
- bizdev 仍以类型别名导出,既有调用方编译通过。
- arch-check 收紧 intent allowlist。
## 三、 测试方案与环境 (Test Plan & Environment)
| 项 | 值 |
|----|-----|
| OS | Linux 7.0.0-31-generic |
| Go | go1.27.1 linux/amd64 |
| 分支 | `fix/intent-bizdev-boundary` |
| 层级 | 表征/单元 + arch-check |
## 四、 预期结果 (Expected Results)
intent 既有 ADVANCE_STAGE 槽位测试仍过;token 黄金不变。
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
### 5.1 表征
- **测试输入 (Input)**:`go test -short -count=1 ./internal/intent ./internal/maturity ./internal/bizdev ./internal/archtest`
- **实际输出 (Actual Output)**:四包 ok(intent 0.010s,maturity 0.001s,bizdev 0.129s,archtest 0.117s)
- **评价判定 (Evaluation / Verdict)**:**PASS**
- **性能与耗时 (Duration & Latency)**:~0.26s
### 5.2 arch-check
- **测试输入 (Input)**:`./scripts/arch-check.sh --mode block`
- **实际输出 (Actual Output)**:`arch-check block: production gate passed`
- **评价判定 (Evaluation / Verdict)**:**PASS**
- **性能与耗时 (Duration & Latency)**:~0.11s
## 六、 自动化测试套件汇总 (Test Suite Execution)
| 命令 | 结果 |
|------|------|
| intent/maturity/bizdev/archtest | PASS |
| arch-check | PASS |
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
D7a **PASS**。coordinator 仍依赖 bizdev DTO(D5);chatflow 阶段常量可在 D7b 首批改为 maturity。
+5 -5
View File
@@ -7,6 +7,7 @@ import (
"chat-agent-system/internal/ctxmgr"
"chat-agent-system/internal/docgov"
"chat-agent-system/internal/kb"
"chat-agent-system/internal/maturity"
"chat-agent-system/internal/squad"
"chat-agent-system/internal/storage"
)
@@ -14,13 +15,12 @@ import (
type TaskMeta = squad.TaskMeta
type AdvisorBriefing = squad.AdvisorBriefing
// MaturityStage 定义商务对接成熟度螺旋收敛阶段
type MaturityStage string
type MaturityStage = maturity.MaturityStage
const (
StageV01_Triage MaturityStage = "V0.1_triage" // 闪电摸底与盲区探测
StageV05_Feasibility MaturityStage = "V0.5_feasibility" // 方案成型与法规对齐
StageV10_Quoting MaturityStage = "V1.0_quoting" // 工程拆解与商业锁定
StageV01_Triage = maturity.StageV01_Triage
StageV05_Feasibility = maturity.StageV05_Feasibility
StageV10_Quoting = maturity.StageV10_Quoting
)
// StageMeta 商务对接阶段元数据说明
+3 -3
View File
@@ -4,7 +4,7 @@ import (
"context"
"testing"
"chat-agent-system/internal/bizdev"
"chat-agent-system/internal/maturity"
)
func TestBuildIntentFrameV2_FromCandidate(t *testing.T) {
@@ -48,7 +48,7 @@ func TestBuildIntentFrameV2_FromCandidate(t *testing.T) {
func TestRecognize_DualWriteIntentFrameV2(t *testing.T) {
rec := NewRecognizer()
ctx := &ContextSnapshot{CurrentStage: string(bizdev.StageV01_Triage)}
ctx := &ContextSnapshot{CurrentStage: string(maturity.StageV01_Triage)}
res, err := rec.Recognize(context.Background(), "现在这个便携超声立项评估到哪一步了?", ctx)
if err != nil {
t.Fatalf("recognize: %v", err)
@@ -75,7 +75,7 @@ func TestBuildIntentFrameV2_ExpectationHitRelation(t *testing.T) {
Intent: IntentAdvanceStage,
Confidence: 1.0,
FilledSlots: map[string]string{
SlotTargetStage: string(bizdev.StageV05_Feasibility),
SlotTargetStage: string(maturity.StageV05_Feasibility),
},
},
}
+2 -2
View File
@@ -4,7 +4,7 @@ import (
"context"
"testing"
"chat-agent-system/internal/bizdev"
"chat-agent-system/internal/maturity"
"chat-agent-system/internal/intent"
)
@@ -14,7 +14,7 @@ func TestExpectationPlusNewPlan_AffirmAndQueryReg(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
CurrentStage: string(bizdev.StageV01_Triage),
CurrentStage: string(maturity.StageV01_Triage),
PendingExpectation: &intent.PendingExpectation{
ExpectedIntent: intent.IntentAdvanceStage,
AllowAffirmative: true,
+2 -2
View File
@@ -10,7 +10,7 @@ import (
"testing"
"time"
"chat-agent-system/internal/bizdev"
"chat-agent-system/internal/maturity"
"chat-agent-system/internal/intent"
)
@@ -204,7 +204,7 @@ func TestIntentGoldenV1_V2DualWrite(t *testing.T) {
// TestIntentGoldenV1_ShadowFields shadow 观测字段应被填充
func TestIntentGoldenV1_ShadowFields(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{CurrentStage: string(bizdev.StageV01_Triage)}
ctx := &intent.ContextSnapshot{CurrentStage: string(maturity.StageV01_Triage)}
res, err := rec.Recognize(context.Background(), "现在这个便携超声立项评估到哪一步了?", ctx)
if err != nil {
t.Fatalf("recognize failed: %v", err)
+13 -13
View File
@@ -7,7 +7,7 @@ import (
"strings"
"time"
"chat-agent-system/internal/bizdev"
"chat-agent-system/internal/maturity"
)
// Recognizer 意图识别器接口
@@ -43,13 +43,13 @@ func (r *RuleBasedRecognizer) Recognize(ctx context.Context, input string, userC
}
snapID := ""
currentStage := bizdev.StageV01_Triage
currentStage := maturity.StageV01_Triage
var pending *PendingExpectation
if userCtx != nil {
snapID = userCtx.SnapshotID
if userCtx.CurrentStage != "" {
currentStage = bizdev.MaturityStage(userCtx.CurrentStage)
currentStage = maturity.MaturityStage(userCtx.CurrentStage)
}
pending = userCtx.PendingExpectation
}
@@ -1037,7 +1037,7 @@ func extractFactSlots(input string) map[string]string {
return slots
}
func matchAdvanceStage(input string, currentStage bizdev.MaturityStage) *CandidateIntent {
func matchAdvanceStage(input string, currentStage maturity.MaturityStage) *CandidateIntent {
patterns := []string{
"进入下一阶段", "推进到下一阶段", "推进阶段", "进入方案", "进入论证",
"方案论证", "可行性论证", "开始方案", "进入报价", "开始报价", "商业锁定",
@@ -1049,20 +1049,20 @@ func matchAdvanceStage(input string, currentStage bizdev.MaturityStage) *Candida
for _, p := range patterns {
if matched, _ := regexp.MatchString("(?i)"+p, input); matched {
var target bizdev.MaturityStage
var target maturity.MaturityStage
if strings.Contains(input, "报价") || strings.Contains(input, "v1.0") || strings.Contains(input, "V1.0") || strings.Contains(input, "商业锁定") {
target = bizdev.StageV10_Quoting
target = maturity.StageV10_Quoting
} else if strings.Contains(input, "方案") || strings.Contains(input, "论证") || strings.Contains(input, "可行性") || strings.Contains(input, "v0.5") || strings.Contains(input, "V0.5") {
target = bizdev.StageV05_Feasibility
target = maturity.StageV05_Feasibility
} else if strings.Contains(input, "摸底") || strings.Contains(input, "初筛") || strings.Contains(input, "v0.1") || strings.Contains(input, "V0.1") {
target = bizdev.StageV01_Triage
target = maturity.StageV01_Triage
} else {
if currentStage == bizdev.StageV01_Triage {
target = bizdev.StageV05_Feasibility
} else if currentStage == bizdev.StageV05_Feasibility {
target = bizdev.StageV10_Quoting
if currentStage == maturity.StageV01_Triage {
target = maturity.StageV05_Feasibility
} else if currentStage == maturity.StageV05_Feasibility {
target = maturity.StageV10_Quoting
} else {
target = bizdev.StageV10_Quoting
target = maturity.StageV10_Quoting
}
}
+18 -18
View File
@@ -6,7 +6,7 @@ import (
"testing"
"time"
"chat-agent-system/internal/bizdev"
"chat-agent-system/internal/maturity"
"chat-agent-system/internal/intent"
)
@@ -14,7 +14,7 @@ func TestIntentRecognition_QueryProgress(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
SnapshotID: "ctx_test_1",
CurrentStage: string(bizdev.StageV01_Triage),
CurrentStage: string(maturity.StageV01_Triage),
}
res, err := rec.Recognize(context.Background(), "现在这个便携超声立项评估到哪一步了?有啥风险没?", ctx)
@@ -56,7 +56,7 @@ func TestIntentRecognition_AdvanceStage(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
SnapshotID: "ctx_test_3",
CurrentStage: string(bizdev.StageV01_Triage),
CurrentStage: string(maturity.StageV01_Triage),
}
res, err := rec.Recognize(context.Background(), "摸底表我看过了,没问题,可以进入方案论证阶段了", ctx)
@@ -67,7 +67,7 @@ func TestIntentRecognition_AdvanceStage(t *testing.T) {
if res.TopIntent.Intent != intent.IntentAdvanceStage {
t.Errorf("expected intent ADVANCE_STAGE, got %s", res.TopIntent.Intent)
}
if res.TopIntent.FilledSlots[intent.SlotTargetStage] != string(bizdev.StageV05_Feasibility) {
if res.TopIntent.FilledSlots[intent.SlotTargetStage] != string(maturity.StageV05_Feasibility) {
t.Errorf("expected target stage V0.5_feasibility, got %s", res.TopIntent.FilledSlots[intent.SlotTargetStage])
}
}
@@ -114,7 +114,7 @@ func samplePendingExpectation() *intent.PendingExpectation {
Description: "小队将自动开展技术路线比选与分类判定",
Intent: intent.IntentAdvanceStage,
Slots: map[string]string{
intent.SlotTargetStage: string(bizdev.StageV05_Feasibility),
intent.SlotTargetStage: string(maturity.StageV05_Feasibility),
},
}
optRefresh := intent.ExpectationOption{
@@ -141,7 +141,7 @@ func TestThreeBranch_Branch1_DirectAffirmative(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
SnapshotID: "ctx_pending_1",
CurrentStage: string(bizdev.StageV01_Triage),
CurrentStage: string(maturity.StageV01_Triage),
PendingExpectation: samplePendingExpectation(),
}
@@ -157,7 +157,7 @@ func TestThreeBranch_Branch1_DirectAffirmative(t *testing.T) {
if res.TopIntent.Intent != intent.IntentAdvanceStage {
t.Errorf("input '%s': expected intent ADVANCE_STAGE, got %s", tc, res.TopIntent.Intent)
}
if res.TopIntent.FilledSlots[intent.SlotTargetStage] != string(bizdev.StageV05_Feasibility) {
if res.TopIntent.FilledSlots[intent.SlotTargetStage] != string(maturity.StageV05_Feasibility) {
t.Errorf("input '%s': expected target stage V0.5, got %s", tc, res.TopIntent.FilledSlots[intent.SlotTargetStage])
}
if res.TopIntent.Confidence < 0.95 {
@@ -171,7 +171,7 @@ func TestThreeBranch_Branch1_AffirmativeWithPatch(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
SnapshotID: "ctx_pending_2",
CurrentStage: string(bizdev.StageV01_Triage),
CurrentStage: string(maturity.StageV01_Triage),
PendingExpectation: samplePendingExpectation(),
}
@@ -186,7 +186,7 @@ func TestThreeBranch_Branch1_AffirmativeWithPatch(t *testing.T) {
if res.TopIntent.Intent != intent.IntentAdvanceStage {
t.Errorf("expected intent ADVANCE_STAGE, got %s", res.TopIntent.Intent)
}
if res.TopIntent.FilledSlots[intent.SlotTargetStage] != string(bizdev.StageV05_Feasibility) {
if res.TopIntent.FilledSlots[intent.SlotTargetStage] != string(maturity.StageV05_Feasibility) {
t.Errorf("expected target stage V0.5, got %s", res.TopIntent.FilledSlots[intent.SlotTargetStage])
}
if res.TopIntent.FilledSlots["target_market"] != "欧洲CE" {
@@ -202,7 +202,7 @@ func TestThreeBranch_Branch1_Decline(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
SnapshotID: "ctx_pending_3",
CurrentStage: string(bizdev.StageV01_Triage),
CurrentStage: string(maturity.StageV01_Triage),
PendingExpectation: samplePendingExpectation(),
}
@@ -229,7 +229,7 @@ func TestThreeBranch_Branch2_NewIntentPreempt(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
SnapshotID: "ctx_pending_4",
CurrentStage: string(bizdev.StageV01_Triage),
CurrentStage: string(maturity.StageV01_Triage),
PendingExpectation: samplePendingExpectation(),
}
@@ -266,7 +266,7 @@ func TestThreeBranch_Branch3_AmbiguousConsult(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
SnapshotID: "ctx_pending_5",
CurrentStage: string(bizdev.StageV01_Triage),
CurrentStage: string(maturity.StageV01_Triage),
PendingExpectation: samplePendingExpectation(),
}
@@ -294,7 +294,7 @@ func TestIntentRecognition_ReviewReject(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
SnapshotID: "ctx_rej_1",
CurrentStage: string(bizdev.StageV05_Feasibility),
CurrentStage: string(maturity.StageV05_Feasibility),
ActiveDocID: "doc-002",
DocLifecycleState: "reviewing",
UserDocRole: "reviewer",
@@ -320,7 +320,7 @@ func TestIntentRecognition_ReviewReject_UnauthorizedRole(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
SnapshotID: "ctx_rej_2",
CurrentStage: string(bizdev.StageV05_Feasibility),
CurrentStage: string(maturity.StageV05_Feasibility),
ActiveDocID: "doc-002",
DocLifecycleState: "reviewing",
UserDocRole: "author", // 非审核人
@@ -343,7 +343,7 @@ func TestIntentRecognition_QueryRejectReason(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
SnapshotID: "ctx_rej_q",
CurrentStage: string(bizdev.StageV05_Feasibility),
CurrentStage: string(maturity.StageV05_Feasibility),
ActiveDocID: "doc-feas",
DocLifecycleState: "drafting",
UserDocRole: "author",
@@ -365,7 +365,7 @@ func TestIntentRecognition_ReviewReject_MissingReason(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
SnapshotID: "ctx_rej_3",
CurrentStage: string(bizdev.StageV05_Feasibility),
CurrentStage: string(maturity.StageV05_Feasibility),
ActiveDocID: "doc-002",
DocLifecycleState: "reviewing",
UserDocRole: "reviewer",
@@ -525,7 +525,7 @@ func TestIntentRecognition_FinalReject_MissingReason(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
SnapshotID: "ctx_final_rej_1",
CurrentStage: string(bizdev.StageV05_Feasibility),
CurrentStage: string(maturity.StageV05_Feasibility),
ActiveDocID: "doc-002",
DocLifecycleState: "approving",
UserDocRole: "approver",
@@ -595,7 +595,7 @@ func TestIntentRecognition_ExplicitDocName_OverActiveDocID(t *testing.T) {
rec := intent.NewRecognizer()
ctx := &intent.ContextSnapshot{
SnapshotID: "ctx_explicit_doc",
CurrentStage: string(bizdev.StageV05_Feasibility),
CurrentStage: string(maturity.StageV05_Feasibility),
ActiveDocID: "doc-001-intake", // 上下文中当前文档是需求收集表
DocLifecycleState: "reviewing",
UserDocRole: "reviewer",
+11
View File
@@ -0,0 +1,11 @@
package maturity
// MaturityStage is the vocabulary for the three official commercial-maturity stages.
// Intent, coordinator and bizdev share this leaf; it must not import those packages.
type MaturityStage string
const (
StageV01_Triage MaturityStage = "V0.1_triage"
StageV05_Feasibility MaturityStage = "V0.5_feasibility"
StageV10_Quoting MaturityStage = "V1.0_quoting"
)
+9
View File
@@ -0,0 +1,9 @@
package maturity
import "testing"
func TestOfficialStageTokens(t *testing.T) {
if StageV01_Triage != "V0.1_triage" || StageV05_Feasibility != "V0.5_feasibility" || StageV10_Quoting != "V1.0_quoting" {
t.Fatalf("stage tokens drifted: %s %s %s", StageV01_Triage, StageV05_Feasibility, StageV10_Quoting)
}
}