922 lines
30 KiB
Markdown
922 lines
30 KiB
Markdown
# 10 - 实施计划详解 (Implementation Plan)
|
||
|
||
## 1. 总体时间线
|
||
|
||
```
|
||
Week 1 Week 2 Week 2-3 Week 3 Week 4 Week 5
|
||
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ Phase 1 │ │ Phase 2 │ │ Phase 3 │ │ Phase 4 │ │ Phase 5+6│ │ Phase 7 │
|
||
│ │ │ │ │ │ │ │ │ │ │ │
|
||
│ 架构文档 │──→│ 基础设施 │──→│ 专家Agent│──→│ Coordinator│──→│ 集成+前端│──→│ 测试+优化│
|
||
│ + 类型 │ │ │ │ │ │ │ │ │ │ │
|
||
│ 定义 │ │ Queue │ │ 6个Agent │ │ 核心Loop │ │ Module │ │ 场景测试 │
|
||
│ │ │ Context │ │ 6份Prompt│ │ 主Prompt │ │ Gateway │ │ 性能优化 │
|
||
│ │ │ Base │ │ │ │ │ │ Frontend │ │ 旧代码清理│
|
||
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
|
||
```
|
||
|
||
## 2. Phase 1: 架构文档 + 类型定义 (Week 1)
|
||
|
||
### 2.1 架构文档(12份)
|
||
|
||
| 序号 | 文件 | 内容 | 状态 |
|
||
|------|------|------|------|
|
||
| 00 | `00-overview.md` | 总览 + 架构图 | ✅ 已完成 |
|
||
| 01 | `01-coordinator-agent.md` | Coordinator 设计 | ✅ 已完成 |
|
||
| 02 | `02-specialist-agents.md` | 6 个专家 Agent 设计 | 待完成 |
|
||
| 03 | `03-agent-communication.md` | Agent 间通信协议 | 待完成 |
|
||
| 04 | `04-streaming-protocol.md` | 流式传输协议 | 待完成 |
|
||
| 05 | `05-frontend-integration.md` | 前端集成方案 | 待完成 |
|
||
| 06 | `06-token-cost-management.md` | Token 与成本管理 | 待完成 |
|
||
| 07 | `07-testing-strategy.md` | 测试策略 | 待完成 |
|
||
| 08 | `08-context-injection.md` | 动态上下文注入 | ✅ 已完成 |
|
||
| 09 | `09-tool-execution.md` | 工具并发执行 | ✅ 已完成 |
|
||
| 10 | `10-implementation-plan.md` | 实施计划(本文档) | ✅ 已完成 |
|
||
| 11 | `11-prompt-templates.md` | 所有 Prompt 模板 | ✅ 已完成 |
|
||
|
||
### 2.2 类型定义文件
|
||
|
||
需要创建以下类型定义文件:
|
||
|
||
```
|
||
packages/services/conversation-service/src/infrastructure/agents/
|
||
├── types/
|
||
│ ├── agent.types.ts # Agent 相关类型
|
||
│ ├── stream.types.ts # 流式事件类型
|
||
│ └── context.types.ts # 上下文注入类型
|
||
```
|
||
|
||
**agent.types.ts**:
|
||
```typescript
|
||
// 核心 Agent 类型定义
|
||
|
||
/** Agent 名称枚举 */
|
||
export enum AgentName {
|
||
COORDINATOR = 'coordinator',
|
||
POLICY_EXPERT = 'policy_expert',
|
||
ASSESSMENT_EXPERT = 'assessment_expert',
|
||
STRATEGIST = 'strategist',
|
||
OBJECTION_HANDLER = 'objection_handler',
|
||
CASE_ANALYST = 'case_analyst',
|
||
MEMORY_MANAGER = 'memory_manager',
|
||
}
|
||
|
||
/** Agent 使用的模型 */
|
||
export enum AgentModel {
|
||
SONNET = 'claude-sonnet-4-20250514',
|
||
HAIKU = 'claude-haiku-4-20250514',
|
||
}
|
||
|
||
/** Agent 配置 */
|
||
export interface AgentConfig {
|
||
name: AgentName;
|
||
model: AgentModel;
|
||
maxTokens: number;
|
||
maxTurns: number; // Agent mini-loop 最大轮次
|
||
timeoutMs: number; // 超时时间
|
||
tools: string[]; // 可用工具名列表
|
||
systemPromptFile: string; // Prompt 文件路径
|
||
}
|
||
|
||
/** Agent 执行上下文 */
|
||
export interface AgentExecutionContext {
|
||
userId: string;
|
||
conversationId: string;
|
||
coordinatorTurn: number; // Coordinator 当前轮次
|
||
parentToolUseId: string; // 触发此 Agent 的 tool_use ID
|
||
}
|
||
|
||
/** Agent Loop 参数 */
|
||
export interface AgentLoopParams {
|
||
messages: Anthropic.MessageParam[];
|
||
systemPrompt: string[] | Anthropic.TextBlockParam[];
|
||
tools: Anthropic.Tool[];
|
||
maxTurns: number;
|
||
maxBudgetUsd: number;
|
||
conversationId: string;
|
||
userId: string;
|
||
abortSignal?: AbortSignal;
|
||
}
|
||
|
||
/** 咨询状态(从 consulting_state XML tag 提取) */
|
||
export interface ConsultingStateReport {
|
||
currentStage: string;
|
||
collectedFields: string[];
|
||
nextAction: string;
|
||
confidenceLevel: 'low' | 'medium' | 'high';
|
||
recommendedPrograms?: string[];
|
||
}
|
||
```
|
||
|
||
**stream.types.ts**:
|
||
```typescript
|
||
// 流式事件类型
|
||
|
||
/** 所有可能的 Stream 事件 */
|
||
export type StreamEvent =
|
||
| TextStreamEvent
|
||
| AgentStartEvent
|
||
| AgentProgressEvent
|
||
| AgentCompleteEvent
|
||
| ToolStartEvent
|
||
| ToolCompleteEvent
|
||
| ToolErrorEvent
|
||
| StateUpdateEvent
|
||
| UsageEvent
|
||
| ErrorEvent
|
||
| EndEvent;
|
||
|
||
export interface TextStreamEvent {
|
||
type: 'text';
|
||
content: string;
|
||
}
|
||
|
||
export interface AgentStartEvent {
|
||
type: 'agent_start';
|
||
agentName: string;
|
||
toolUseId: string;
|
||
}
|
||
|
||
export interface AgentProgressEvent {
|
||
type: 'agent_progress';
|
||
agentName: string;
|
||
turn: number;
|
||
maxTurns: number;
|
||
}
|
||
|
||
export interface AgentCompleteEvent {
|
||
type: 'agent_complete';
|
||
agentName: string;
|
||
durationMs: number;
|
||
tokensUsed?: { input: number; output: number };
|
||
}
|
||
|
||
export interface StateUpdateEvent {
|
||
type: 'state_update';
|
||
state: ConsultingStateReport;
|
||
}
|
||
|
||
export interface UsageEvent {
|
||
type: 'usage';
|
||
usage: { inputTokens: number; outputTokens: number };
|
||
totalCostUsd: number;
|
||
}
|
||
|
||
export interface ErrorEvent {
|
||
type: 'error';
|
||
code: string;
|
||
message: string;
|
||
}
|
||
|
||
export interface EndEvent {
|
||
type: 'end';
|
||
totalTokens: { input: number; output: number };
|
||
totalCostUsd: number;
|
||
turnsUsed: number;
|
||
agentsCalled: string[];
|
||
}
|
||
```
|
||
|
||
**context.types.ts**:
|
||
- 详见 [08-context-injection.md](./08-context-injection.md) 第 3 节
|
||
|
||
**交付标准**:
|
||
- 所有类型文件通过 `tsc --noEmit` 类型检查
|
||
- 每个 interface 有 JSDoc 注释
|
||
- 导出为 barrel file (`types/index.ts`)
|
||
|
||
## 3. Phase 2: 基础设施 (Week 2)
|
||
|
||
### 3.1 文件结构
|
||
|
||
```
|
||
packages/services/conversation-service/src/infrastructure/agents/
|
||
├── types/ # Phase 1 产物
|
||
│ ├── agent.types.ts
|
||
│ ├── stream.types.ts
|
||
│ ├── context.types.ts
|
||
│ └── index.ts
|
||
├── core/ # Phase 2 产物
|
||
│ ├── tool-execution-queue.ts # 工具并发执行队列
|
||
│ ├── context-injector.service.ts # 上下文注入器
|
||
│ ├── base-specialist.service.ts # 专家 Agent 基类
|
||
│ ├── agent-executor.ts # Agent 执行器工厂
|
||
│ └── token-tracker.ts # Token 追踪器
|
||
```
|
||
|
||
### 3.2 tool-execution-queue.ts
|
||
|
||
- 完整实现详见 [09-tool-execution.md](./09-tool-execution.md) 第 4 节
|
||
- **关键依赖**: `tool-execution.types.ts`
|
||
- **单测覆盖**:
|
||
- 纯并发批次(所有 safe 工具)
|
||
- 纯串行批次(所有 unsafe 工具)
|
||
- 混合批次(safe + unsafe 交替)
|
||
- 单个工具超时
|
||
- 批次中一个工具失败
|
||
- 空请求
|
||
- 结果排序正确性
|
||
|
||
```typescript
|
||
// 单测示例
|
||
describe('ToolExecutionQueue', () => {
|
||
it('should execute concurrent-safe tools in parallel', async () => {
|
||
const queue = new ToolExecutionQueue();
|
||
const executionOrder: string[] = [];
|
||
|
||
queue.registerTool('tool_a', {
|
||
isConcurrencySafe: true,
|
||
executor: async () => {
|
||
executionOrder.push('a_start');
|
||
await sleep(100);
|
||
executionOrder.push('a_end');
|
||
return { content: 'a', success: true };
|
||
},
|
||
// ...
|
||
});
|
||
|
||
queue.registerTool('tool_b', {
|
||
isConcurrencySafe: true,
|
||
executor: async () => {
|
||
executionOrder.push('b_start');
|
||
await sleep(50);
|
||
executionOrder.push('b_end');
|
||
return { content: 'b', success: true };
|
||
},
|
||
// ...
|
||
});
|
||
|
||
const results = await queue.executeAll([
|
||
{ toolUseId: '1', toolName: 'tool_a', input: {}, originalIndex: 0 },
|
||
{ toolUseId: '2', toolName: 'tool_b', input: {}, originalIndex: 1 },
|
||
]);
|
||
|
||
// 两个工具应该几乎同时开始
|
||
expect(executionOrder[0]).toBe('a_start');
|
||
expect(executionOrder[1]).toBe('b_start');
|
||
// b 先完成(50ms vs 100ms)
|
||
expect(executionOrder[2]).toBe('b_end');
|
||
expect(executionOrder[3]).toBe('a_end');
|
||
// 结果按 originalIndex 排序
|
||
expect(results[0].toolName).toBe('tool_a');
|
||
expect(results[1].toolName).toBe('tool_b');
|
||
});
|
||
});
|
||
```
|
||
|
||
### 3.3 context-injector.service.ts
|
||
|
||
- 完整实现详见 [08-context-injection.md](./08-context-injection.md) 第 5 节
|
||
- **关键依赖**: `KnowledgeClientService`, `context.types.ts`
|
||
- **单测覆盖**:
|
||
- 所有 8 种上下文的独立构建
|
||
- 缓存命中/未命中
|
||
- Token 预算裁剪
|
||
- P0 上下文不被裁剪
|
||
- 缓存失效
|
||
- auto-compaction 触发条件
|
||
|
||
### 3.4 base-specialist.service.ts
|
||
|
||
- 完整实现详见 [09-tool-execution.md](./09-tool-execution.md) 第 6 节
|
||
- **关键依赖**: `@anthropic-ai/sdk`
|
||
- **单测覆盖**:
|
||
- Mini-loop 正常完成(1轮无工具调用)
|
||
- Mini-loop 有工具调用(2轮)
|
||
- 达到 maxTurns 强制终止
|
||
- API 错误处理
|
||
- Token 追踪回调
|
||
|
||
### 3.5 交付标准
|
||
|
||
- 所有基础设施组件可以独立运行单测
|
||
- 不依赖具体的 Specialist Agent 实现
|
||
- 使用 mock 的 Claude API 进行测试
|
||
|
||
## 4. Phase 3: 专家 Agent (Week 2-3)
|
||
|
||
### 4.1 文件结构
|
||
|
||
```
|
||
packages/services/conversation-service/src/infrastructure/agents/
|
||
├── specialists/
|
||
│ ├── policy-expert.service.ts # 政策专家
|
||
│ ├── assessment-expert.service.ts # 评估专家
|
||
│ ├── strategist.service.ts # 策略顾问
|
||
│ ├── objection-handler.service.ts # 异议处理
|
||
│ ├── case-analyst.service.ts # 案例分析
|
||
│ └── memory-manager.service.ts # 记忆管理
|
||
├── prompts/
|
||
│ ├── coordinator-system-prompt.ts # Phase 4
|
||
│ ├── policy-expert-prompt.ts
|
||
│ ├── assessment-expert-prompt.ts
|
||
│ ├── strategist-prompt.ts
|
||
│ ├── objection-handler-prompt.ts
|
||
│ ├── case-analyst-prompt.ts
|
||
│ └── memory-manager-prompt.ts
|
||
```
|
||
|
||
### 4.2 各 Agent 实现计划
|
||
|
||
| Agent | 继承 | Model | 工具 | 预估代码量 | 优先级 |
|
||
|-------|------|-------|------|-----------|--------|
|
||
| `PolicyExpertService` | `BaseSpecialistService` | Sonnet | `search_knowledge` | ~120 行 | P0 |
|
||
| `AssessmentExpertService` | `BaseSpecialistService` | Sonnet | `search_knowledge`, `get_user_context` | ~200 行 | P0 |
|
||
| `MemoryManagerService` | `BaseSpecialistService` | Haiku | `save_user_memory`, `get_user_context` | ~150 行 | P0 |
|
||
| `StrategistService` | `BaseSpecialistService` | Sonnet | `get_user_context` | ~100 行 | P1 |
|
||
| `ObjectionHandlerService` | `BaseSpecialistService` | Sonnet | `search_knowledge`, `get_user_context` | ~120 行 | P1 |
|
||
| `CaseAnalystService` | `BaseSpecialistService` | Haiku | `search_knowledge`, `get_user_context` | ~100 行 | P2 |
|
||
|
||
### 4.3 开发顺序
|
||
|
||
建议按以下顺序开发(优先级 + 依赖关系):
|
||
|
||
```
|
||
1. MemoryManagerService → 最简单,验证 BaseSpecialist + 工具执行
|
||
↓
|
||
2. PolicyExpertService → 最常用,验证 knowledge-service 集成
|
||
↓
|
||
3. AssessmentExpertService → 核心业务,需要 knowledge + 用户上下文
|
||
↓
|
||
4. StrategistService → 辅助 Coordinator 决策
|
||
↓
|
||
5. ObjectionHandlerService → 异议处理,依赖 knowledge
|
||
↓
|
||
6. CaseAnalystService → 最后实现,依赖度最低
|
||
```
|
||
|
||
### 4.4 每个 Agent 的独立测试策略
|
||
|
||
```typescript
|
||
// 以 PolicyExpertService 为例的独立测试
|
||
|
||
describe('PolicyExpertService', () => {
|
||
let service: PolicyExpertService;
|
||
let mockAnthropicClient: jest.Mocked<Anthropic>;
|
||
let mockKnowledgeClient: jest.Mocked<KnowledgeClientService>;
|
||
|
||
beforeEach(() => {
|
||
mockAnthropicClient = createMockAnthropicClient();
|
||
mockKnowledgeClient = createMockKnowledgeClient();
|
||
service = new PolicyExpertService(mockAnthropicClient, mockKnowledgeClient);
|
||
});
|
||
|
||
it('should answer policy questions without tool calls', async () => {
|
||
// Mock: Claude 直接回答,不调用工具
|
||
mockAnthropicClient.messages.create.mockResolvedValue({
|
||
content: [{ type: 'text', text: '高才通B类要求...' }],
|
||
usage: { input_tokens: 500, output_tokens: 200 },
|
||
stop_reason: 'end_turn',
|
||
});
|
||
|
||
const result = await service.execute({
|
||
input: { query: '高才通B类条件是什么' },
|
||
maxTurns: 3,
|
||
});
|
||
|
||
expect(result.output).toContain('高才通');
|
||
expect(result.turnsUsed).toBe(1);
|
||
});
|
||
|
||
it('should use search_knowledge tool when needed', async () => {
|
||
// Mock: Claude 调用 search_knowledge,再用结果生成回答
|
||
mockAnthropicClient.messages.create
|
||
.mockResolvedValueOnce({
|
||
content: [
|
||
{ type: 'tool_use', id: 't1', name: 'search_knowledge', input: { query: 'TTPS B类 条件' } },
|
||
],
|
||
usage: { input_tokens: 500, output_tokens: 100 },
|
||
stop_reason: 'tool_use',
|
||
})
|
||
.mockResolvedValueOnce({
|
||
content: [{ type: 'text', text: '根据查询结果,高才通B类...' }],
|
||
usage: { input_tokens: 800, output_tokens: 300 },
|
||
stop_reason: 'end_turn',
|
||
});
|
||
|
||
mockKnowledgeClient.retrieveKnowledge.mockResolvedValue({
|
||
content: '高才通B类要求全球百强大学学士学位...',
|
||
sources: [{ articleId: '1', title: '高才通指南', similarity: 0.92 }],
|
||
});
|
||
|
||
const result = await service.execute({
|
||
input: { query: '高才通B类条件' },
|
||
maxTurns: 3,
|
||
onToolCall: jest.fn(),
|
||
});
|
||
|
||
expect(result.output).toContain('根据查询结果');
|
||
expect(result.turnsUsed).toBe(2);
|
||
expect(mockKnowledgeClient.retrieveKnowledge).toHaveBeenCalled();
|
||
});
|
||
});
|
||
```
|
||
|
||
### 4.5 交付标准
|
||
|
||
- 每个 Agent 有独立的单元测试(mock Claude API + mock 外部服务)
|
||
- 每个 Agent 的 Prompt 是独立文件,可以独立调整
|
||
- 所有 Agent 通过 `BaseSpecialistService.execute()` 统一调用
|
||
|
||
## 5. Phase 4: Coordinator (Week 3)
|
||
|
||
### 5.1 文件结构
|
||
|
||
```
|
||
packages/services/conversation-service/src/infrastructure/agents/
|
||
├── coordinator/
|
||
│ ├── coordinator-agent.service.ts # 主服务
|
||
│ ├── agent-loop.ts # 核心递归循环
|
||
│ └── state-extractor.ts # 从回复中提取 consulting_state
|
||
├── prompts/
|
||
│ └── coordinator-system-prompt.ts # 500+ 行 Prompt
|
||
```
|
||
|
||
### 5.2 coordinator-system-prompt.ts
|
||
|
||
这是整个系统最重要的文件,详见 [11-prompt-templates.md](./11-prompt-templates.md)。
|
||
|
||
关键结构:
|
||
```typescript
|
||
export function buildCoordinatorSystemPrompt(config: {
|
||
expertContact: { wechat: string; phone: string; workingHours: string };
|
||
paidServices: { assessmentPrice: number; description: string };
|
||
currentDate: string;
|
||
}): string {
|
||
return `
|
||
# 身份定义
|
||
...(约 30 行)
|
||
|
||
# 你的专家团队
|
||
...(约 80 行)
|
||
|
||
# 六大移民类别
|
||
...(约 200 行)
|
||
|
||
# 对话策略
|
||
...(约 100 行)
|
||
|
||
# 回复规范
|
||
...(约 40 行)
|
||
|
||
# 状态报告格式
|
||
...(约 30 行)
|
||
|
||
# 业务规则
|
||
...(约 20 行)
|
||
`;
|
||
}
|
||
```
|
||
|
||
### 5.3 agent-loop.ts
|
||
|
||
核心递归循环,详见 [01-coordinator-agent.md](./01-coordinator-agent.md) 第 4 节。
|
||
|
||
关键特性:
|
||
- `async function* agentLoop()`: AsyncGenerator 模式
|
||
- 递归调用自身(tool_results → 再次调用 Claude)
|
||
- 集成 ContextInjector
|
||
- 集成 ToolExecutionQueue
|
||
- 集成 state extractor
|
||
- abort 信号支持
|
||
|
||
### 5.4 coordinator-agent.service.ts
|
||
|
||
NestJS 服务,封装 agent-loop,对外暴露与旧 `ClaudeAgentServiceV2` 相同的接口:
|
||
|
||
```typescript
|
||
@Injectable()
|
||
export class CoordinatorAgentService {
|
||
constructor(
|
||
private contextInjector: ContextInjectorService,
|
||
private toolExecutionQueue: ToolExecutionQueue,
|
||
// ... 各 Specialist Agent
|
||
) {}
|
||
|
||
async *sendMessage(params: {
|
||
conversationContext: ConversationContext;
|
||
userMessage: string;
|
||
attachments?: FileAttachment[];
|
||
userId: string;
|
||
conversationId: string;
|
||
deviceInfo?: DeviceInfo;
|
||
}): AsyncGenerator<StreamEvent> {
|
||
// 委托给 agentLoop
|
||
yield* agentLoop({
|
||
messages: this.buildMessages(params),
|
||
systemPrompt: this.buildSystemPrompt(),
|
||
tools: this.toolExecutionQueue.getClaudeTools(),
|
||
maxTurns: 15,
|
||
maxBudgetUsd: 0.50,
|
||
conversationId: params.conversationId,
|
||
userId: params.userId,
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.5 交付标准
|
||
|
||
- Coordinator 可以独立运行集成测试(使用真实 Claude API)
|
||
- agent-loop 有完整的单元测试(mock API)
|
||
- System Prompt 经过人工审查
|
||
- 通过 5 个核心对话场景的 E2E 测试
|
||
|
||
## 6. Phase 5: 集成 (Week 4 前半)
|
||
|
||
### 6.1 NestJS Module 集成
|
||
|
||
**新增 agents.module.ts**:
|
||
```typescript
|
||
// packages/services/conversation-service/src/infrastructure/agents/agents.module.ts
|
||
|
||
@Module({
|
||
imports: [KnowledgeModule], // 依赖 KnowledgeClientService
|
||
providers: [
|
||
// Core
|
||
ToolExecutionQueue,
|
||
ContextInjectorService,
|
||
// Specialists
|
||
PolicyExpertService,
|
||
AssessmentExpertService,
|
||
StrategistService,
|
||
ObjectionHandlerService,
|
||
CaseAnalystService,
|
||
MemoryManagerService,
|
||
// Coordinator
|
||
CoordinatorAgentService,
|
||
],
|
||
exports: [CoordinatorAgentService],
|
||
})
|
||
export class AgentsModule {}
|
||
```
|
||
|
||
**修改 claude.module.ts**:
|
||
```typescript
|
||
// 在 claude.module.ts 中导入 AgentsModule
|
||
@Module({
|
||
imports: [AgentsModule], // 新增
|
||
providers: [
|
||
ClaudeAgentServiceV2, // 保留旧服务(过渡期)
|
||
// ...
|
||
],
|
||
exports: [
|
||
ClaudeAgentServiceV2, // 保留旧导出
|
||
CoordinatorAgentService, // 新增导出
|
||
],
|
||
})
|
||
export class ClaudeModule {}
|
||
```
|
||
|
||
**修改 conversation.service.ts**:
|
||
```typescript
|
||
// 切换注入(使用 feature flag)
|
||
@Injectable()
|
||
export class ConversationService {
|
||
constructor(
|
||
@Inject(CONVERSATION_REPOSITORY)
|
||
private readonly conversationRepo: IConversationRepository,
|
||
@Inject(MESSAGE_REPOSITORY)
|
||
private readonly messageRepo: IMessageRepository,
|
||
// 旧服务(过渡期保留)
|
||
private readonly claudeAgentService: ClaudeAgentServiceV2,
|
||
// 新服务
|
||
private readonly coordinatorAgentService: CoordinatorAgentService,
|
||
private readonly configService: ConfigService,
|
||
) {}
|
||
|
||
private get useNewArchitecture(): boolean {
|
||
return this.configService.get<boolean>('USE_MULTI_AGENT', false);
|
||
}
|
||
|
||
async *sendMessage(params: SendMessageParams): AsyncGenerator<StreamChunk> {
|
||
if (this.useNewArchitecture) {
|
||
yield* this.sendMessageV3(params); // 新架构
|
||
} else {
|
||
yield* this.sendMessageV2(params); // 旧架构
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**更新 conversation.gateway.ts**:
|
||
```typescript
|
||
// 新增 Agent 相关的 WebSocket 事件
|
||
// stream_chunk 保持不变
|
||
// 新增:
|
||
// agent_start { agentName, conversationId }
|
||
// agent_progress { agentName, turn, maxTurns, conversationId }
|
||
// agent_complete { agentName, durationMs, conversationId }
|
||
```
|
||
|
||
### 6.2 Feature Flag 策略
|
||
|
||
```
|
||
环境变量: USE_MULTI_AGENT=true/false
|
||
|
||
开发环境: true (直接使用新架构)
|
||
测试环境: true (测试新架构)
|
||
生产环境: false (等全部测试通过后切换)
|
||
```
|
||
|
||
### 6.3 共享类型更新
|
||
|
||
```typescript
|
||
// packages/shared/types/src/conversation.ts
|
||
// 新增 StreamEvent 类型供前端使用
|
||
|
||
export interface AgentStatusEvent {
|
||
type: 'agent_start' | 'agent_progress' | 'agent_complete';
|
||
agentName: string;
|
||
conversationId: string;
|
||
// agent_progress only
|
||
turn?: number;
|
||
maxTurns?: number;
|
||
// agent_complete only
|
||
durationMs?: number;
|
||
}
|
||
```
|
||
|
||
### 6.4 交付标准
|
||
|
||
- Feature flag 可以在不重启服务的情况下切换
|
||
- 旧架构在 `USE_MULTI_AGENT=false` 时完全不受影响
|
||
- WebSocket 事件向后兼容(新事件是追加的,不修改已有事件)
|
||
|
||
## 7. Phase 6: 前端 (Week 4 后半)
|
||
|
||
### 7.1 前端文件修改
|
||
|
||
```
|
||
packages/clients/web-client/src/
|
||
├── stores/
|
||
│ └── chatStore.ts # 新增 agentStatus 状态
|
||
├── components/
|
||
│ ├── chat/
|
||
│ │ ├── ChatWindow.tsx # 处理新的 WebSocket 事件
|
||
│ │ ├── MessageBubble.tsx # 可能需要微调
|
||
│ │ └── AgentStatus.tsx # 新增:Agent 工作状态组件
|
||
│ └── shared/
|
||
│ └── AgentBadge.tsx # 新增:Agent 名称+状态徽章
|
||
```
|
||
|
||
### 7.2 chatStore 更新
|
||
|
||
```typescript
|
||
// chatStore.ts (Zustand)
|
||
|
||
interface AgentStatus {
|
||
name: string;
|
||
displayName: string; // 中文显示名
|
||
status: 'idle' | 'working' | 'completed';
|
||
startedAt?: number;
|
||
completedAt?: number;
|
||
durationMs?: number;
|
||
}
|
||
|
||
interface ChatState {
|
||
// 已有状态...
|
||
messages: Message[];
|
||
isStreaming: boolean;
|
||
|
||
// 新增:Agent 状态
|
||
activeAgents: AgentStatus[];
|
||
agentHistory: AgentStatus[]; // 本次对话中所有 Agent 调用记录
|
||
}
|
||
|
||
// Agent 名称映射
|
||
const AGENT_DISPLAY_NAMES: Record<string, string> = {
|
||
policy_expert: '政策专家',
|
||
assessment_expert: '评估专家',
|
||
strategist: '策略顾问',
|
||
objection_handler: '异议处理',
|
||
case_analyst: '案例分析',
|
||
memory_manager: '记忆管理',
|
||
};
|
||
```
|
||
|
||
### 7.3 AgentStatus 组件
|
||
|
||
```tsx
|
||
// AgentStatus.tsx
|
||
// 显示在聊天窗口中,消息气泡和输入框之间
|
||
|
||
function AgentStatus({ agents }: { agents: AgentStatus[] }) {
|
||
const workingAgents = agents.filter(a => a.status === 'working');
|
||
|
||
if (workingAgents.length === 0) return null;
|
||
|
||
return (
|
||
<div className="agent-status-bar">
|
||
{workingAgents.map(agent => (
|
||
<div key={agent.name} className="agent-badge working">
|
||
<span className="agent-icon">🔄</span>
|
||
<span className="agent-name">{agent.displayName}</span>
|
||
<span className="agent-time">
|
||
{formatElapsedTime(Date.now() - (agent.startedAt || 0))}
|
||
</span>
|
||
</div>
|
||
))}
|
||
<span className="agent-hint">正在分析中...</span>
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
### 7.4 ChatWindow 事件处理
|
||
|
||
```typescript
|
||
// ChatWindow.tsx - WebSocket 事件处理更新
|
||
|
||
socket.on('agent_start', (data: { agentName: string; conversationId: string }) => {
|
||
chatStore.addActiveAgent({
|
||
name: data.agentName,
|
||
displayName: AGENT_DISPLAY_NAMES[data.agentName] || data.agentName,
|
||
status: 'working',
|
||
startedAt: Date.now(),
|
||
});
|
||
});
|
||
|
||
socket.on('agent_complete', (data: { agentName: string; durationMs: number }) => {
|
||
chatStore.completeAgent(data.agentName, data.durationMs);
|
||
});
|
||
|
||
socket.on('stream_end', () => {
|
||
chatStore.clearActiveAgents(); // 清除所有工作中的 Agent 状态
|
||
});
|
||
```
|
||
|
||
### 7.5 交付标准
|
||
|
||
- Agent 状态在 UI 中实时显示
|
||
- Agent 完成后自动消失(带淡出动画)
|
||
- 在弱网环境下不会出现 Agent 状态 "卡住" 的问题(超时自动清除)
|
||
- 旧架构模式下不显示 Agent 状态
|
||
|
||
## 8. Phase 7: 测试 + 优化 + 清理 (Week 5)
|
||
|
||
### 8.1 测试场景矩阵
|
||
|
||
| 场景编号 | 场景描述 | 涉及 Agent | 预期行为 |
|
||
|----------|---------|-----------|---------|
|
||
| S01 | 新用户首次咨询 | Coordinator + Memory | 破冰 → 收集信息 → 保存记忆 |
|
||
| S02 | 询问高才通条件 | Coordinator + Policy | 检索知识库 → 准确回答 |
|
||
| S03 | 信息收集后评估 | Coordinator + Assessment + Memory | 评估资格 → 推荐方案 |
|
||
| S04 | 用户表示犹豫 | Coordinator + Objection + Strategist | 识别异议 → 策略回应 |
|
||
| S05 | 用户要求付费评估 | Coordinator + (Payment tool) | 生成支付链接 |
|
||
| S06 | 老用户回访 | Coordinator + Memory | 加载历史记忆 → 个性化问候 |
|
||
| S07 | 并发工具调用 | Coordinator + Policy + Case | 并行执行两个 Agent |
|
||
| S08 | 超长对话 (30+ 轮) | All | Auto-compaction 触发 |
|
||
| S09 | API 错误恢复 | Coordinator | 降级到 Haiku / 友好错误 |
|
||
| S10 | 用户中途取消 | Coordinator | AbortSignal 生效 |
|
||
|
||
### 8.2 性能优化清单
|
||
|
||
| 优化项 | 目标 | 方法 |
|
||
|--------|------|------|
|
||
| 首次回复延迟 | < 3s | Prompt Caching + 预热 |
|
||
| 并发 Agent 延迟 | < 5s (3 个 Agent 并行) | ToolExecutionQueue 并行 |
|
||
| Context Injection 延迟 | < 200ms | 缓存 + 并行获取 |
|
||
| Auto-compaction 延迟 | < 2s | Haiku 摘要 |
|
||
| Token 成本 (每对话) | < $0.30 | Haiku for 辅助 Agent + Caching |
|
||
| 内存使用 | < 100MB per 1000 并发对话 | 对话结束清理缓存 |
|
||
|
||
### 8.3 旧代码清理计划
|
||
|
||
**阶段 A: 标记废弃 (Week 5)**
|
||
```typescript
|
||
// 不删除代码,只标记 @deprecated
|
||
/** @deprecated Use CoordinatorAgentService instead. Will be removed in v4. */
|
||
export class ClaudeAgentServiceV2 { ... }
|
||
|
||
/** @deprecated Replaced by Coordinator Prompt. */
|
||
export class StrategyEngineService { ... }
|
||
|
||
/** @deprecated Intent classification now handled by LLM. */
|
||
export const intentClassifier = { ... };
|
||
|
||
/** @deprecated Response quality now handled by Prompt. */
|
||
export const responseGate = { ... };
|
||
```
|
||
|
||
**阶段 B: 删除代码 (Week 6+, after 1 week in production)**
|
||
- 删除 `strategy-engine.service.ts`
|
||
- 删除 `intent-classifier.ts`
|
||
- 删除 `response-gate.ts`
|
||
- 删除 `default-strategy.ts`
|
||
- 删除 `claude-agent-v2.service.ts`
|
||
- 移除 feature flag
|
||
|
||
### 8.4 Rollback 计划
|
||
|
||
```
|
||
如果新架构在生产中出现严重问题:
|
||
|
||
1. 即时回滚 (< 1 分钟):
|
||
设置环境变量 USE_MULTI_AGENT=false
|
||
→ 自动切回 ClaudeAgentServiceV2
|
||
→ 不需要重新部署
|
||
|
||
2. 部署回滚 (< 5 分钟):
|
||
git revert 到 Phase 5 之前的 commit
|
||
→ 重新部署 conversation-service
|
||
|
||
3. 数据恢复:
|
||
新架构不修改数据库 schema
|
||
ConsultingState 格式向后兼容
|
||
→ 无需数据迁移
|
||
```
|
||
|
||
## 9. 组件依赖图
|
||
|
||
```
|
||
┌─────────────────┐
|
||
│ agents.module │
|
||
└────────┬────────┘
|
||
│ exports
|
||
┌────────────▼────────────┐
|
||
│ CoordinatorAgentService │
|
||
└────────────┬────────────┘
|
||
│ depends on
|
||
┌──────────────────┼──────────────────┐
|
||
│ │ │
|
||
┌────────▼───────┐ ┌────────▼────────┐ ┌───────▼────────┐
|
||
│ ContextInjector │ │ ToolExecution │ │ agentLoop() │
|
||
│ Service │ │ Queue │ │ (function) │
|
||
└────────┬───────┘ └────────┬────────┘ └───────┬────────┘
|
||
│ │ │
|
||
┌────────▼───────┐ ┌────────▼────────┐ │
|
||
│ Knowledge │ │ Agent Executors │ │
|
||
│ ClientService │ │ (factory) │ │
|
||
└────────────────┘ └────────┬────────┘ │
|
||
│ │
|
||
┌───────▼───────────────────┘
|
||
│
|
||
┌─────────▼─────────┐
|
||
│ BaseSpecialist │
|
||
│ Service (abstract) │
|
||
└─────────┬─────────┘
|
||
│ extends
|
||
┌───────┬───────┬───┴───┬───────┬───────┐
|
||
│ │ │ │ │ │
|
||
Policy Assess Strat Object Case Memory
|
||
Expert Expert gist Handler Analyst Manager
|
||
```
|
||
|
||
## 10. 风险与缓解
|
||
|
||
| 风险 | 概率 | 影响 | 缓解措施 |
|
||
|------|------|------|---------|
|
||
| Coordinator Prompt 效果不佳 | 高 | 高 | Phase 4 投入大量时间测试 Prompt,准备多个版本 A/B 测试 |
|
||
| Token 成本超预期 | 中 | 中 | 监控每次对话成本,设置 maxBudgetUsd 硬上限 |
|
||
| 并发 Agent 导致 Rate Limit | 中 | 中 | 实现指数退避重试,降级到 Haiku |
|
||
| Context Window 不够用 | 低 | 高 | Auto-compaction + 严格的 token 预算 |
|
||
| 前端 Agent 状态同步问题 | 中 | 低 | WebSocket 事件超时自动清理 |
|
||
| 旧代码删除导致回归 | 低 | 高 | Feature flag 过渡期,完善的回滚计划 |
|
||
|
||
## 11. 交付 Checklist
|
||
|
||
### Phase 1 Checklist
|
||
- [ ] 12 份架构文档全部完成
|
||
- [ ] `agent.types.ts` 通过类型检查
|
||
- [ ] `stream.types.ts` 通过类型检查
|
||
- [ ] `context.types.ts` 通过类型检查
|
||
- [ ] 所有类型有 JSDoc 注释
|
||
|
||
### Phase 2 Checklist
|
||
- [ ] `ToolExecutionQueue` 通过所有单元测试
|
||
- [ ] `ContextInjectorService` 通过所有单元测试
|
||
- [ ] `BaseSpecialistService` 通过所有单元测试
|
||
- [ ] 所有组件可以独立测试(不依赖 Claude API)
|
||
|
||
### Phase 3 Checklist
|
||
- [ ] 6 个 Specialist Agent 全部实现
|
||
- [ ] 6 份 Prompt 文件全部编写
|
||
- [ ] 每个 Agent 有独立的单元测试
|
||
- [ ] 每个 Agent 使用真实 API 的集成测试(可选,按需运行)
|
||
|
||
### Phase 4 Checklist
|
||
- [ ] Coordinator System Prompt 完成(500+ 行)
|
||
- [ ] agent-loop 通过所有单元测试
|
||
- [ ] CoordinatorAgentService 通过集成测试
|
||
- [ ] 5 个核心场景 E2E 测试通过
|
||
|
||
### Phase 5 Checklist
|
||
- [ ] `agents.module.ts` 编写完成
|
||
- [ ] Feature flag 切换工作正常
|
||
- [ ] 旧架构在 flag=false 时不受影响
|
||
- [ ] WebSocket 事件向后兼容
|
||
|
||
### Phase 6 Checklist
|
||
- [ ] chatStore 更新完成
|
||
- [ ] AgentStatus 组件实现
|
||
- [ ] ChatWindow 处理新事件
|
||
- [ ] 前端无 regression
|
||
|
||
### Phase 7 Checklist
|
||
- [ ] 10 个测试场景全部通过
|
||
- [ ] 性能指标达标
|
||
- [ ] 旧代码标记 @deprecated
|
||
- [ ] 回滚计划验证通过
|
||
- [ ] 文档更新完成
|