Major Changes: - Database: Install pg_bigm/pgvector plugins, create test database - Python service: v1.0 -> v1.1, add pymupdf4llm/openpyxl/pypandoc - Node.js backend: v1.3 -> v1.7, fix pino-pretty and ES Module imports - Frontend: v1.2 -> v1.3, skip TypeScript check for deployment - Code recovery: Restore empty files from local backup Technical Fixes: - Fix pino-pretty error in production (conditional loading) - Fix ES Module import paths (add .js extensions) - Fix OSSAdapter TypeScript errors - Update Prisma Schema (63 models, 16 schemas) - Update environment variables (DATABASE_URL, EXTRACTION_SERVICE_URL, OSS) - Remove deprecated variables (REDIS_URL, DIFY_API_URL, DIFY_API_KEY) Documentation: - Create 0126 deployment folder with 8 documents - Update database development standards v2.0 - Update SAE deployment status records Deployment Status: - PostgreSQL: ai_clinical_research_test with plugins - Python: v1.1 @ 172.17.173.84:8000 - Backend: v1.7 @ 172.17.173.89:3001 - Frontend: v1.3 @ 172.17.173.90:80 Tested: All services running successfully on SAE
1133 lines
29 KiB
Markdown
1133 lines
29 KiB
Markdown
# Protocol Agent 代码结构设计
|
||
|
||
> 版本:v1.0
|
||
> 创建日期:2026-01-24
|
||
|
||
---
|
||
|
||
## 一、目录结构
|
||
|
||
```
|
||
apps/api/src/
|
||
├── modules/
|
||
│ │
|
||
│ ├── agent-framework/ # 【新增】通用Agent框架
|
||
│ │ ├── core/
|
||
│ │ │ ├── BaseAgentOrchestrator.ts # 抽象基类
|
||
│ │ │ ├── QueryAnalyzer.ts # Query层-意图分析
|
||
│ │ │ ├── Planner.ts # Planner层-执行规划
|
||
│ │ │ ├── Executor.ts # Executor层-任务执行
|
||
│ │ │ ├── ToolInvoker.ts # 工具调用器
|
||
│ │ │ └── ReflexionEngine.ts # Reflection层-质量反思
|
||
│ │ │
|
||
│ │ ├── memory/
|
||
│ │ │ ├── MemoryManager.ts # 记忆管理器
|
||
│ │ │ └── ContextManager.ts # 上下文管理器
|
||
│ │ │
|
||
│ │ ├── config/
|
||
│ │ │ ├── ConfigLoader.ts # 配置加载器
|
||
│ │ │ └── StageManager.ts # 阶段状态机
|
||
│ │ │
|
||
│ │ ├── prompt/
|
||
│ │ │ ├── PromptBuilder.ts # Prompt构建器
|
||
│ │ │ └── PromptTemplates.ts # Prompt模板常量
|
||
│ │ │
|
||
│ │ ├── trace/
|
||
│ │ │ ├── TraceLogger.ts # 追踪日志记录
|
||
│ │ │ └── TraceAnalyzer.ts # 追踪分析(调试)
|
||
│ │ │
|
||
│ │ ├── tools/
|
||
│ │ │ ├── ToolRegistry.ts # 工具注册表
|
||
│ │ │ ├── DeepLinkGenerator.ts # Deep Link生成器
|
||
│ │ │ └── BaseToolHandler.ts # 工具处理基类
|
||
│ │ │
|
||
│ │ ├── types/
|
||
│ │ │ ├── agent.types.ts # Agent核心类型
|
||
│ │ │ ├── query.types.ts # Query相关类型
|
||
│ │ │ ├── plan.types.ts # Plan相关类型
|
||
│ │ │ ├── execution.types.ts # 执行相关类型
|
||
│ │ │ ├── reflexion.types.ts # 反思相关类型
|
||
│ │ │ └── tool.types.ts # 工具相关类型
|
||
│ │ │
|
||
│ │ └── index.ts # 模块导出
|
||
│ │
|
||
│ ├── aia/
|
||
│ │ ├── agents/
|
||
│ │ │ │
|
||
│ │ │ ├── protocol/ # 【新增】Protocol Agent
|
||
│ │ │ │ ├── ProtocolOrchestrator.ts # Protocol编排器
|
||
│ │ │ │ ├── ProtocolContextService.ts # Context服务
|
||
│ │ │ │ ├── ProtocolStageConfig.ts # 阶段配置
|
||
│ │ │ │ ├── ProtocolPrompts.ts # 专用Prompt
|
||
│ │ │ │ ├── ProtocolExtractor.ts # 数据提取器
|
||
│ │ │ │ ├── tools/
|
||
│ │ │ │ │ ├── SampleCalculatorTool.ts # 样本量计算工具
|
||
│ │ │ │ │ ├── DeepLinkTools.ts # Deep Link工具
|
||
│ │ │ │ │ └── index.ts
|
||
│ │ │ │ └── index.ts
|
||
│ │ │ │
|
||
│ │ │ └── (future agents)/
|
||
│ │ │ ├── statistics/ # 未来:统计分析Agent
|
||
│ │ │ └── data-cleaning/ # 未来:数据清洗Agent
|
||
│ │ │
|
||
│ │ ├── routes/
|
||
│ │ │ ├── protocolAgent.routes.ts # 【新增】Protocol Agent路由
|
||
│ │ │ ├── chat.routes.ts # 现有聊天路由
|
||
│ │ │ └── index.ts
|
||
│ │ │
|
||
│ │ ├── services/
|
||
│ │ │ ├── chat.service.ts # 现有聊天服务
|
||
│ │ │ ├── conversation.service.ts # 现有对话服务
|
||
│ │ │ └── protocolAgent.service.ts # 【新增】Protocol Agent服务
|
||
│ │ │
|
||
│ │ └── (existing files)
|
||
│ │
|
||
│ └── (other modules)
|
||
│
|
||
├── shared/
|
||
│ ├── llm/ # 现有LLM Gateway
|
||
│ └── (other shared)
|
||
│
|
||
└── (other folders)
|
||
```
|
||
|
||
---
|
||
|
||
## 二、核心类型定义
|
||
|
||
### 2.1 agent.types.ts - Agent核心类型
|
||
|
||
```typescript
|
||
// ============================================================
|
||
// Agent核心类型定义
|
||
// ============================================================
|
||
|
||
/**
|
||
* Agent配置(从数据库加载)
|
||
*/
|
||
export interface AgentConfig {
|
||
id: string;
|
||
agentId: string;
|
||
agentName: string;
|
||
baseSystemPrompt: string;
|
||
toneOfVoice?: string;
|
||
globalKnowledge: string[];
|
||
memoryStrategy: 'full' | 'sliding_window' | 'summary';
|
||
maxHistoryTurns: number;
|
||
defaultModel: string;
|
||
temperature: number;
|
||
maxTokens: number;
|
||
stages: StageConfig[];
|
||
tools: ToolConfig[];
|
||
reflexionRules: ReflexionRuleConfig[];
|
||
}
|
||
|
||
/**
|
||
* 阶段配置
|
||
*/
|
||
export interface StageConfig {
|
||
id: string;
|
||
stageId: string;
|
||
stageName: string;
|
||
stageOrder: number;
|
||
instruction: string;
|
||
extractionSchema: JSONSchema;
|
||
completionCriteria?: string;
|
||
nextStageId: string | null;
|
||
transitionMode: 'user_confirm' | 'auto' | 'condition';
|
||
transitionCondition?: Record<string, any>;
|
||
availableToolIds: string[];
|
||
actionCards: ActionCardConfig[];
|
||
}
|
||
|
||
/**
|
||
* Action Card配置
|
||
*/
|
||
export interface ActionCardConfig {
|
||
cardId: string;
|
||
title: string;
|
||
description?: string;
|
||
iconType?: string;
|
||
triggerType: 'stage_enter' | 'condition' | 'manual';
|
||
triggerCondition?: Record<string, any>;
|
||
actionType: 'deep_link' | 'api_call' | 'modal';
|
||
actionPayload: Record<string, any>;
|
||
onCompleteAction?: string;
|
||
resultMapping?: Record<string, string>;
|
||
}
|
||
|
||
/**
|
||
* Agent会话状态
|
||
*/
|
||
export interface AgentSessionState {
|
||
sessionId: string;
|
||
conversationId: string;
|
||
agentId: string;
|
||
userId: string;
|
||
currentStageId: string;
|
||
stageStatus: 'in_progress' | 'completed' | 'paused';
|
||
lastActiveAt: Date;
|
||
}
|
||
|
||
/**
|
||
* Agent响应
|
||
*/
|
||
export interface AgentResponse {
|
||
traceId: string;
|
||
message: string;
|
||
thinkingContent?: string;
|
||
actionCards: ActionCardPayload[];
|
||
contextUpdate?: Record<string, any>;
|
||
currentStage: string;
|
||
stageStatus: string;
|
||
suggestions?: string[];
|
||
}
|
||
```
|
||
|
||
### 2.2 query.types.ts - Query相关类型
|
||
|
||
```typescript
|
||
// ============================================================
|
||
// Query Analysis 类型定义
|
||
// ============================================================
|
||
|
||
/**
|
||
* 意图类型
|
||
*/
|
||
export type IntentType =
|
||
| 'provide_info' // 提供信息
|
||
| 'ask_question' // 询问问题
|
||
| 'request_change' // 请求修改
|
||
| 'confirm_complete' // 确认完成
|
||
| 'navigate_stage' // 跳转阶段
|
||
| 'request_tool' // 请求工具
|
||
| 'chitchat' // 闲聊
|
||
| 'unclear'; // 不明确
|
||
|
||
/**
|
||
* 提取的实体
|
||
*/
|
||
export interface ExtractedEntity {
|
||
type: string; // 实体类型,如 'population', 'intervention'
|
||
value: string; // 实体值
|
||
confidence: number; // 置信度 0-1
|
||
span?: { // 在原文中的位置
|
||
start: number;
|
||
end: number;
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Query分析结果
|
||
*/
|
||
export interface QueryAnalysisResult {
|
||
intent: {
|
||
primary: IntentType;
|
||
confidence: number;
|
||
subIntent?: string;
|
||
};
|
||
entities: ExtractedEntity[];
|
||
resolvedQuery: string;
|
||
sentiment?: 'positive' | 'neutral' | 'confused' | 'frustrated';
|
||
requiresClarification: boolean;
|
||
clarificationQuestion?: string;
|
||
}
|
||
|
||
/**
|
||
* Query分析器配置
|
||
*/
|
||
export interface QueryAnalyzerConfig {
|
||
enableEntityExtraction: boolean;
|
||
enableSentimentAnalysis: boolean;
|
||
enableContextResolution: boolean;
|
||
confidenceThreshold: number;
|
||
}
|
||
```
|
||
|
||
### 2.3 plan.types.ts - Plan相关类型
|
||
|
||
```typescript
|
||
// ============================================================
|
||
// Planner 类型定义
|
||
// ============================================================
|
||
|
||
/**
|
||
* 执行策略
|
||
*/
|
||
export type ExecutionStrategy =
|
||
| 'guide_and_extract' // 引导对话并提取数据
|
||
| 'answer_question' // 回答用户问题
|
||
| 'update_context' // 更新已有数据
|
||
| 'trigger_tool' // 触发工具调用
|
||
| 'transition_stage' // 执行阶段流转
|
||
| 'request_clarification' // 请求澄清
|
||
| 'handle_chitchat'; // 处理闲聊
|
||
|
||
/**
|
||
* 执行任务
|
||
*/
|
||
export interface ExecutionTask {
|
||
taskId: string;
|
||
taskType: 'llm_generate' | 'tool_call' | 'data_extract' | 'context_update';
|
||
priority: number;
|
||
params: Record<string, any>;
|
||
dependsOn?: string[]; // 依赖的其他任务ID
|
||
}
|
||
|
||
/**
|
||
* 执行计划
|
||
*/
|
||
export interface ExecutionPlan {
|
||
strategy: ExecutionStrategy;
|
||
tasks: ExecutionTask[];
|
||
toolsNeeded: string[];
|
||
promptOverrides?: {
|
||
systemPrompt?: string;
|
||
userPromptPrefix?: string;
|
||
};
|
||
shouldExtract: boolean;
|
||
shouldReflect: boolean;
|
||
metadata: {
|
||
planReason: string;
|
||
estimatedTokens?: number;
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 阶段流转结果
|
||
*/
|
||
export interface StageTransitionResult {
|
||
success: boolean;
|
||
previousStage: string;
|
||
nextStage?: string;
|
||
issues?: string[];
|
||
suggestions?: string[];
|
||
summary?: string;
|
||
}
|
||
```
|
||
|
||
### 2.4 execution.types.ts - 执行相关类型
|
||
|
||
```typescript
|
||
// ============================================================
|
||
// Executor 类型定义
|
||
// ============================================================
|
||
|
||
/**
|
||
* LLM响应
|
||
*/
|
||
export interface LLMResponse {
|
||
content: string;
|
||
thinkingContent?: string;
|
||
finishReason: 'stop' | 'length' | 'tool_calls';
|
||
usage: {
|
||
promptTokens: number;
|
||
completionTokens: number;
|
||
totalTokens: number;
|
||
};
|
||
model: string;
|
||
}
|
||
|
||
/**
|
||
* 提取的数据
|
||
*/
|
||
export interface ExtractedData {
|
||
hasNewData: boolean;
|
||
data: Record<string, any>;
|
||
confidence: number;
|
||
sourceMessageId: string;
|
||
extractionMethod: 'inline' | 'post_process';
|
||
}
|
||
|
||
/**
|
||
* 工具调用结果
|
||
*/
|
||
export interface ToolCallResult {
|
||
toolId: string;
|
||
success: boolean;
|
||
result?: any;
|
||
error?: string;
|
||
durationMs: number;
|
||
}
|
||
|
||
/**
|
||
* 执行结果
|
||
*/
|
||
export interface ExecutionResult {
|
||
response: LLMResponse;
|
||
extracted?: ExtractedData;
|
||
toolResults?: ToolCallResult[];
|
||
actionCards: ActionCardPayload[];
|
||
}
|
||
|
||
/**
|
||
* Action Card载荷(返回给前端)
|
||
*/
|
||
export interface ActionCardPayload {
|
||
cardId: string;
|
||
title: string;
|
||
description?: string;
|
||
iconType?: string;
|
||
actionType: 'deep_link' | 'api_call' | 'modal';
|
||
actionUrl?: string;
|
||
actionParams?: Record<string, any>;
|
||
priority: number;
|
||
}
|
||
```
|
||
|
||
### 2.5 reflexion.types.ts - 反思相关类型
|
||
|
||
```typescript
|
||
// ============================================================
|
||
// Reflexion 类型定义
|
||
// ============================================================
|
||
|
||
/**
|
||
* 反思规则配置
|
||
*/
|
||
export interface ReflexionRuleConfig {
|
||
ruleId: string;
|
||
ruleName: string;
|
||
triggerStageId?: string;
|
||
triggerTiming: 'on_extract' | 'on_complete' | 'on_transition';
|
||
ruleType: 'prompt_based' | 'rule_based' | 'hybrid';
|
||
promptTemplate?: string;
|
||
ruleLogic?: RuleLogic;
|
||
severity: 'error' | 'warning' | 'info';
|
||
failureAction: 'block' | 'warn' | 'log';
|
||
priority: number;
|
||
}
|
||
|
||
/**
|
||
* 规则逻辑(rule_based类型)
|
||
*/
|
||
export interface RuleLogic {
|
||
field: string;
|
||
condition: 'required' | 'min_length' | 'max_length' | 'pattern' | 'custom';
|
||
value?: any;
|
||
customValidator?: string; // 自定义验证函数名
|
||
}
|
||
|
||
/**
|
||
* 反思检查项
|
||
*/
|
||
export interface ReflexionCheck {
|
||
ruleId: string;
|
||
ruleName: string;
|
||
passed: boolean;
|
||
severity: 'error' | 'warning' | 'info';
|
||
message?: string;
|
||
suggestion?: string;
|
||
}
|
||
|
||
/**
|
||
* 反思结果
|
||
*/
|
||
export interface ReflexionResult {
|
||
passed: boolean;
|
||
checks: ReflexionCheck[];
|
||
issues: string[];
|
||
suggestions: string[];
|
||
blockTransition: boolean;
|
||
}
|
||
```
|
||
|
||
### 2.6 tool.types.ts - 工具相关类型
|
||
|
||
```typescript
|
||
// ============================================================
|
||
// Tool 类型定义
|
||
// ============================================================
|
||
|
||
/**
|
||
* 工具类型
|
||
*/
|
||
export type ToolType = 'internal' | 'external' | 'deep_link' | 'rag';
|
||
|
||
/**
|
||
* 工具配置
|
||
*/
|
||
export interface ToolConfig {
|
||
toolId: string;
|
||
toolName: string;
|
||
toolDescription: string;
|
||
toolType: ToolType;
|
||
functionSchema?: FunctionSchema;
|
||
handlerType: 'code' | 'api' | 'deep_link';
|
||
handlerConfig: Record<string, any>;
|
||
requiresAuth: boolean;
|
||
timeout: number;
|
||
}
|
||
|
||
/**
|
||
* Function Schema (OpenAI格式)
|
||
*/
|
||
export interface FunctionSchema {
|
||
name: string;
|
||
description: string;
|
||
parameters: {
|
||
type: 'object';
|
||
properties: Record<string, JSONSchemaProperty>;
|
||
required?: string[];
|
||
};
|
||
}
|
||
|
||
/**
|
||
* JSON Schema属性
|
||
*/
|
||
export interface JSONSchemaProperty {
|
||
type: 'string' | 'number' | 'boolean' | 'array' | 'object';
|
||
description?: string;
|
||
enum?: string[];
|
||
items?: JSONSchemaProperty;
|
||
properties?: Record<string, JSONSchemaProperty>;
|
||
}
|
||
|
||
/**
|
||
* 工具处理器接口
|
||
*/
|
||
export interface IToolHandler {
|
||
toolId: string;
|
||
execute(params: Record<string, any>, context: ToolContext): Promise<any>;
|
||
validate?(params: Record<string, any>): boolean;
|
||
}
|
||
|
||
/**
|
||
* 工具执行上下文
|
||
*/
|
||
export interface ToolContext {
|
||
conversationId: string;
|
||
userId: string;
|
||
agentId: string;
|
||
currentStage: string;
|
||
protocolContext?: Record<string, any>;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 三、核心类实现
|
||
|
||
### 3.1 BaseAgentOrchestrator.ts
|
||
|
||
```typescript
|
||
/**
|
||
* Agent编排器基类
|
||
* 所有Agent都继承此类,实现标准的Query→Plan→Execute→Reflect流程
|
||
*/
|
||
export abstract class BaseAgentOrchestrator<TContext> {
|
||
|
||
protected config: AgentConfig;
|
||
protected queryAnalyzer: QueryAnalyzer;
|
||
protected planner: Planner;
|
||
protected executor: Executor;
|
||
protected reflexionEngine: ReflexionEngine;
|
||
protected memoryManager: MemoryManager;
|
||
protected configLoader: ConfigLoader;
|
||
protected traceLogger: TraceLogger;
|
||
|
||
constructor(dependencies: AgentDependencies) {
|
||
// 注入依赖
|
||
}
|
||
|
||
/**
|
||
* 子类必须实现的抽象方法
|
||
*/
|
||
abstract getAgentId(): string;
|
||
abstract getContextSchema(): TContext;
|
||
abstract getContext(conversationId: string): Promise<TContext>;
|
||
abstract updateContext(conversationId: string, data: Partial<TContext>): Promise<void>;
|
||
|
||
/**
|
||
* 主处理入口
|
||
*/
|
||
async handleMessage(
|
||
conversationId: string,
|
||
userMessage: string,
|
||
messageId: string
|
||
): Promise<AgentResponse> {
|
||
|
||
const traceId = this.traceLogger.startTrace(conversationId);
|
||
|
||
try {
|
||
// Step 1: 加载配置和上下文
|
||
const config = await this.configLoader.getAgentConfig(this.getAgentId());
|
||
const context = await this.getContext(conversationId);
|
||
const session = await this.getSession(conversationId);
|
||
const stageConfig = this.getStageConfig(config, session.currentStageId);
|
||
|
||
// Step 2: Query Analysis
|
||
const queryResult = await this.queryAnalyzer.analyze(
|
||
userMessage,
|
||
context,
|
||
session
|
||
);
|
||
this.traceLogger.logStep(traceId, 'query_analysis', { input: userMessage, output: queryResult });
|
||
|
||
// Step 3: Planning
|
||
const plan = await this.planner.createPlan(
|
||
queryResult,
|
||
stageConfig,
|
||
context
|
||
);
|
||
this.traceLogger.logStep(traceId, 'planning', { input: queryResult, output: plan });
|
||
|
||
// Step 4: Execution
|
||
const execResult = await this.executor.execute(
|
||
plan,
|
||
config,
|
||
stageConfig,
|
||
context,
|
||
conversationId
|
||
);
|
||
this.traceLogger.logStep(traceId, 'execution', { input: plan, output: execResult });
|
||
|
||
// Step 5: Data Extraction
|
||
if (plan.shouldExtract && execResult.extracted?.hasNewData) {
|
||
await this.updateContext(conversationId, execResult.extracted.data);
|
||
}
|
||
|
||
// Step 6: Reflection (可选)
|
||
let reflexionResult: ReflexionResult | undefined;
|
||
if (plan.shouldReflect) {
|
||
reflexionResult = await this.reflexionEngine.reflect(
|
||
execResult,
|
||
context,
|
||
stageConfig
|
||
);
|
||
this.traceLogger.logStep(traceId, 'reflexion', { output: reflexionResult });
|
||
}
|
||
|
||
// Step 7: 构建响应
|
||
const response = this.buildResponse(
|
||
execResult,
|
||
reflexionResult,
|
||
session,
|
||
stageConfig
|
||
);
|
||
|
||
this.traceLogger.endTrace(traceId, 'success');
|
||
return response;
|
||
|
||
} catch (error) {
|
||
this.traceLogger.endTrace(traceId, 'error', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理阶段完成
|
||
*/
|
||
async handleStageComplete(conversationId: string): Promise<StageTransitionResult> {
|
||
// ... 阶段流转逻辑
|
||
}
|
||
|
||
/**
|
||
* 处理Action Card回调
|
||
*/
|
||
async handleActionCallback(
|
||
conversationId: string,
|
||
cardId: string,
|
||
result: any
|
||
): Promise<void> {
|
||
// ... Action回调处理
|
||
}
|
||
|
||
// ... 其他protected方法
|
||
}
|
||
```
|
||
|
||
### 3.2 ProtocolOrchestrator.ts
|
||
|
||
```typescript
|
||
/**
|
||
* Protocol Agent 编排器
|
||
* 继承BaseAgentOrchestrator,实现研究方案特定逻辑
|
||
*/
|
||
export class ProtocolOrchestrator extends BaseAgentOrchestrator<ProtocolContext> {
|
||
|
||
private contextService: ProtocolContextService;
|
||
private protocolExtractor: ProtocolExtractor;
|
||
|
||
constructor(dependencies: ProtocolAgentDependencies) {
|
||
super(dependencies);
|
||
this.contextService = dependencies.contextService;
|
||
this.protocolExtractor = dependencies.protocolExtractor;
|
||
}
|
||
|
||
getAgentId(): string {
|
||
return 'protocol_agent';
|
||
}
|
||
|
||
getContextSchema(): ProtocolContext {
|
||
return ProtocolContextSchema;
|
||
}
|
||
|
||
async getContext(conversationId: string): Promise<ProtocolContext> {
|
||
return this.contextService.getOrCreate(conversationId);
|
||
}
|
||
|
||
async updateContext(conversationId: string, data: Partial<ProtocolContext>): Promise<void> {
|
||
await this.contextService.update(conversationId, data);
|
||
}
|
||
|
||
/**
|
||
* 重写:Protocol特定的提取逻辑
|
||
*/
|
||
protected async extractData(
|
||
llmResponse: LLMResponse,
|
||
stageConfig: StageConfig
|
||
): Promise<ExtractedData> {
|
||
return this.protocolExtractor.extract(llmResponse, stageConfig);
|
||
}
|
||
|
||
/**
|
||
* 重写:Protocol特定的阶段完成检查
|
||
*/
|
||
protected async validateStageCompletion(
|
||
context: ProtocolContext,
|
||
stageConfig: StageConfig
|
||
): Promise<ReflexionResult> {
|
||
// Protocol特定的验证逻辑
|
||
const stageData = context[stageConfig.stageId as keyof ProtocolContext];
|
||
|
||
// 检查必填字段
|
||
// 检查数据完整性
|
||
// 调用Reflexion引擎
|
||
|
||
return this.reflexionEngine.reflectOnStageComplete(stageData, stageConfig);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.3 PromptBuilder.ts
|
||
|
||
```typescript
|
||
/**
|
||
* Prompt构建器
|
||
* 负责构建各阶段的System Prompt
|
||
*/
|
||
export class PromptBuilder {
|
||
|
||
/**
|
||
* 构建系统Prompt
|
||
*/
|
||
buildSystemPrompt(params: {
|
||
basePrompt: string;
|
||
stageConfig: StageConfig;
|
||
context: Record<string, any>;
|
||
toneOfVoice?: string;
|
||
}): string {
|
||
|
||
const { basePrompt, stageConfig, context, toneOfVoice } = params;
|
||
|
||
return `
|
||
# 角色设定
|
||
${basePrompt}
|
||
|
||
${toneOfVoice ? `# 语气风格\n${toneOfVoice}\n` : ''}
|
||
|
||
# 当前阶段: ${stageConfig.stageName}
|
||
${stageConfig.instruction}
|
||
|
||
# 已收集的研究方案信息
|
||
<protocol_context>
|
||
${this.formatContext(context)}
|
||
</protocol_context>
|
||
|
||
# 数据提取要求
|
||
在回复的最后,如果用户提供了新的信息,请以XML格式输出提取到的结构化数据:
|
||
<extracted_data>
|
||
${JSON.stringify(stageConfig.extractionSchema.example || {}, null, 2)}
|
||
</extracted_data>
|
||
|
||
# 注意事项
|
||
- 如果用户信息不完整,温和地追问补充
|
||
- 如果用户表述有歧义,先确认再记录
|
||
- 保持专业但不失亲和力的语气
|
||
- 不要编造或假设用户未提供的信息
|
||
`.trim();
|
||
}
|
||
|
||
/**
|
||
* 构建意图识别Prompt
|
||
*/
|
||
buildQueryAnalysisPrompt(params: {
|
||
userMessage: string;
|
||
currentStage: string;
|
||
contextSummary: string;
|
||
}): string {
|
||
return `
|
||
分析用户消息的意图。
|
||
|
||
当前阶段: ${params.currentStage}
|
||
上下文摘要: ${params.contextSummary}
|
||
|
||
用户消息: "${params.userMessage}"
|
||
|
||
请分析并以JSON格式返回:
|
||
{
|
||
"intent": {
|
||
"primary": "provide_info|ask_question|request_change|confirm_complete|navigate_stage|request_tool|chitchat|unclear",
|
||
"confidence": 0.0-1.0,
|
||
"subIntent": "可选的子意图"
|
||
},
|
||
"entities": [
|
||
{"type": "entity_type", "value": "entity_value", "confidence": 0.0-1.0}
|
||
],
|
||
"requiresClarification": true|false,
|
||
"clarificationQuestion": "如需澄清,提供问题"
|
||
}
|
||
`.trim();
|
||
}
|
||
|
||
/**
|
||
* 构建Reflexion检查Prompt
|
||
*/
|
||
buildReflexionPrompt(params: {
|
||
stageData: Record<string, any>;
|
||
stageConfig: StageConfig;
|
||
rule: ReflexionRuleConfig;
|
||
}): string {
|
||
if (params.rule.promptTemplate) {
|
||
return this.interpolateTemplate(params.rule.promptTemplate, {
|
||
stageData: params.stageData,
|
||
stageName: params.stageConfig.stageName
|
||
});
|
||
}
|
||
|
||
return `
|
||
检查${params.stageConfig.stageName}阶段的数据质量。
|
||
|
||
当前数据:
|
||
${JSON.stringify(params.stageData, null, 2)}
|
||
|
||
完成标准:
|
||
${params.stageConfig.completionCriteria || '无特定标准'}
|
||
|
||
请检查并以JSON格式返回:
|
||
{
|
||
"passed": true|false,
|
||
"issues": ["问题1", "问题2"],
|
||
"suggestions": ["建议1", "建议2"]
|
||
}
|
||
`.trim();
|
||
}
|
||
|
||
private formatContext(context: Record<string, any>): string {
|
||
// 格式化上下文,过滤空值,美化输出
|
||
const filtered = Object.entries(context)
|
||
.filter(([_, v]) => v != null && Object.keys(v).length > 0)
|
||
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
|
||
|
||
return JSON.stringify(filtered, null, 2);
|
||
}
|
||
|
||
private interpolateTemplate(template: string, vars: Record<string, any>): string {
|
||
return template.replace(/\{\{(\w+(?:\.\w+)*)\}\}/g, (_, path) => {
|
||
const value = path.split('.').reduce((obj: any, key: string) => obj?.[key], vars);
|
||
return typeof value === 'object' ? JSON.stringify(value) : String(value ?? '');
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 四、API设计
|
||
|
||
### 4.1 路由定义
|
||
|
||
```typescript
|
||
// protocolAgent.routes.ts
|
||
|
||
export async function protocolAgentRoutes(fastify: FastifyInstance) {
|
||
|
||
// 发送消息(核心对话API)
|
||
fastify.post('/api/aia/protocol-agent/chat', {
|
||
schema: {
|
||
body: {
|
||
type: 'object',
|
||
required: ['conversationId', 'message'],
|
||
properties: {
|
||
conversationId: { type: 'string' },
|
||
message: { type: 'string' },
|
||
attachments: { type: 'array', items: { type: 'string' } }
|
||
}
|
||
}
|
||
},
|
||
handler: protocolAgentController.chat
|
||
});
|
||
|
||
// 确认阶段完成
|
||
fastify.post('/api/aia/protocol-agent/stage/complete', {
|
||
schema: {
|
||
body: {
|
||
type: 'object',
|
||
required: ['conversationId'],
|
||
properties: {
|
||
conversationId: { type: 'string' }
|
||
}
|
||
}
|
||
},
|
||
handler: protocolAgentController.completeStage
|
||
});
|
||
|
||
// 获取当前Context
|
||
fastify.get('/api/aia/protocol-agent/context/:conversationId', {
|
||
handler: protocolAgentController.getContext
|
||
});
|
||
|
||
// 更新Context(State Panel编辑)
|
||
fastify.patch('/api/aia/protocol-agent/context/:conversationId', {
|
||
schema: {
|
||
body: {
|
||
type: 'object',
|
||
required: ['field', 'value'],
|
||
properties: {
|
||
field: { type: 'string' },
|
||
value: { type: 'object' }
|
||
}
|
||
}
|
||
},
|
||
handler: protocolAgentController.updateContext
|
||
});
|
||
|
||
// Action Card回调
|
||
fastify.post('/api/aia/protocol-agent/action-callback', {
|
||
schema: {
|
||
body: {
|
||
type: 'object',
|
||
required: ['conversationId', 'cardId', 'result'],
|
||
properties: {
|
||
conversationId: { type: 'string' },
|
||
cardId: { type: 'string' },
|
||
result: { type: 'object' }
|
||
}
|
||
}
|
||
},
|
||
handler: protocolAgentController.handleActionCallback
|
||
});
|
||
|
||
// 获取追踪日志(调试用)
|
||
fastify.get('/api/aia/protocol-agent/trace/:traceId', {
|
||
handler: protocolAgentController.getTrace
|
||
});
|
||
}
|
||
```
|
||
|
||
### 4.2 响应格式
|
||
|
||
```typescript
|
||
// Chat响应
|
||
interface ChatResponse {
|
||
traceId: string;
|
||
message: string;
|
||
thinkingContent?: string;
|
||
actionCards: ActionCardPayload[];
|
||
contextUpdate: {
|
||
field: string;
|
||
data: any;
|
||
confidence: number;
|
||
} | null;
|
||
currentStage: string;
|
||
stageStatus: 'in_progress' | 'completed';
|
||
stageProgress: {
|
||
current: number;
|
||
total: number;
|
||
completedStages: string[];
|
||
};
|
||
}
|
||
|
||
// Stage Complete响应
|
||
interface StageCompleteResponse {
|
||
success: boolean;
|
||
issues?: string[];
|
||
suggestions?: string[];
|
||
previousStage: string;
|
||
nextStage?: string;
|
||
summary?: string;
|
||
nextStageActionCards?: ActionCardPayload[];
|
||
}
|
||
|
||
// Context响应
|
||
interface ContextResponse {
|
||
id: string;
|
||
conversationId: string;
|
||
currentStage: string;
|
||
overallProgress: number;
|
||
completedStages: string[];
|
||
scientificQuestion: any;
|
||
pico: any;
|
||
studyDesign: any;
|
||
sampleSize: any;
|
||
endpoints: any;
|
||
updatedAt: string;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 五、前端组件设计
|
||
|
||
### 5.1 组件结构
|
||
|
||
```
|
||
apps/web/src/modules/aia/
|
||
├── components/
|
||
│ ├── ProtocolAgent/
|
||
│ │ ├── ProtocolAgentPage.tsx # 主页面
|
||
│ │ ├── ProtocolChat.tsx # 聊天区域
|
||
│ │ ├── StatePanel/
|
||
│ │ │ ├── StatePanel.tsx # 状态面板容器
|
||
│ │ │ ├── StageProgress.tsx # 阶段进度
|
||
│ │ │ ├── ContextDisplay.tsx # Context展示
|
||
│ │ │ └── ContextEditor.tsx # Context编辑
|
||
│ │ ├── ActionCard/
|
||
│ │ │ ├── ActionCardList.tsx # Action Card列表
|
||
│ │ │ ├── ActionCard.tsx # 单个Card
|
||
│ │ │ └── DeepLinkModal.tsx # Deep Link弹窗
|
||
│ │ └── index.ts
|
||
│ └── (existing components)
|
||
│
|
||
├── hooks/
|
||
│ ├── useProtocolAgent.ts # Protocol Agent Hook
|
||
│ ├── useProtocolContext.ts # Context管理Hook
|
||
│ └── useActionCard.ts # Action Card Hook
|
||
│
|
||
├── stores/
|
||
│ └── protocolAgentStore.ts # Zustand Store
|
||
│
|
||
└── services/
|
||
└── protocolAgentApi.ts # API调用
|
||
```
|
||
|
||
### 5.2 核心Hook
|
||
|
||
```typescript
|
||
// useProtocolAgent.ts
|
||
|
||
export function useProtocolAgent(conversationId: string) {
|
||
const [context, setContext] = useState<ProtocolContext | null>(null);
|
||
const [currentStage, setCurrentStage] = useState<string>('scientific_question');
|
||
const [actionCards, setActionCards] = useState<ActionCardPayload[]>([]);
|
||
const [isLoading, setIsLoading] = useState(false);
|
||
|
||
// 发送消息
|
||
const sendMessage = async (message: string) => {
|
||
setIsLoading(true);
|
||
try {
|
||
const response = await protocolAgentApi.chat(conversationId, message);
|
||
|
||
// 更新状态
|
||
if (response.contextUpdate) {
|
||
setContext(prev => ({
|
||
...prev,
|
||
[response.contextUpdate.field]: response.contextUpdate.data
|
||
}));
|
||
}
|
||
setCurrentStage(response.currentStage);
|
||
setActionCards(response.actionCards);
|
||
|
||
return response;
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
// 确认阶段完成
|
||
const completeStage = async () => {
|
||
const result = await protocolAgentApi.completeStage(conversationId);
|
||
if (result.success && result.nextStage) {
|
||
setCurrentStage(result.nextStage);
|
||
setActionCards(result.nextStageActionCards || []);
|
||
}
|
||
return result;
|
||
};
|
||
|
||
// 更新Context
|
||
const updateContext = async (field: string, value: any) => {
|
||
await protocolAgentApi.updateContext(conversationId, field, value);
|
||
setContext(prev => ({ ...prev, [field]: value }));
|
||
};
|
||
|
||
// 处理Action Card点击
|
||
const handleActionCard = async (card: ActionCardPayload) => {
|
||
if (card.actionType === 'deep_link') {
|
||
// 打开Deep Link
|
||
window.open(card.actionUrl, '_blank');
|
||
} else if (card.actionType === 'modal') {
|
||
// 打开弹窗
|
||
}
|
||
};
|
||
|
||
return {
|
||
context,
|
||
currentStage,
|
||
actionCards,
|
||
isLoading,
|
||
sendMessage,
|
||
completeStage,
|
||
updateContext,
|
||
handleActionCard
|
||
};
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 六、依赖注入设计
|
||
|
||
```typescript
|
||
// 依赖注入容器配置
|
||
|
||
export function createProtocolAgentDependencies(
|
||
prisma: PrismaClient,
|
||
llmGateway: LLMGateway
|
||
): ProtocolAgentDependencies {
|
||
|
||
// 基础服务
|
||
const traceLogger = new TraceLogger(prisma);
|
||
const configLoader = new ConfigLoader(prisma);
|
||
const memoryManager = new MemoryManager(prisma);
|
||
const promptBuilder = new PromptBuilder();
|
||
|
||
// Query层
|
||
const queryAnalyzer = new QueryAnalyzer(llmGateway, promptBuilder);
|
||
|
||
// Planner层
|
||
const stageManager = new StageManager(configLoader);
|
||
const planner = new Planner(stageManager);
|
||
|
||
// Executor层
|
||
const toolRegistry = new ToolRegistry();
|
||
const executor = new Executor(llmGateway, toolRegistry, promptBuilder);
|
||
|
||
// Reflexion层
|
||
const reflexionEngine = new ReflexionEngine(llmGateway, promptBuilder);
|
||
|
||
// Protocol专用
|
||
const contextService = new ProtocolContextService(prisma);
|
||
const protocolExtractor = new ProtocolExtractor();
|
||
|
||
return {
|
||
traceLogger,
|
||
configLoader,
|
||
memoryManager,
|
||
queryAnalyzer,
|
||
planner,
|
||
executor,
|
||
reflexionEngine,
|
||
contextService,
|
||
protocolExtractor
|
||
};
|
||
}
|
||
```
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|