Features: - Add V2.9 enhancements: Cron Skill, User Profiling, Feedback Loop, Multi-Intent Handling - Create modular development plan documents (database, engines, services, memory, tasks) - Add V2.5/V2.6/V2.8/V2.9 design documents for architecture evolution - Add system design white papers and implementation guides Architecture: - Dual-Brain Architecture (SOP + ReAct engines) - Three-layer memory system (Flow Log, Hot Memory, History Book) - ProfilerService for personalized responses - SchedulerService with Cron Skill support Also includes: - Frontend nginx config updates - Backend test scripts for WeChat signature - Database backup files Co-authored-by: Cursor <cursoragent@cursor.com>
732 lines
20 KiB
Markdown
732 lines
20 KiB
Markdown
# IIT Manager Agent 核心引擎实现指南
|
||
|
||
> **版本:** V2.9
|
||
> **更新日期:** 2026-02-05
|
||
> **关联文档:** [IIT Manager Agent V2.6 综合开发计划](./IIT%20Manager%20Agent%20V2.6%20综合开发计划.md)
|
||
>
|
||
> **V2.9 更新**:ReActEngine Prompt 优化,支持多意图处理(Chain of Thought)
|
||
|
||
---
|
||
|
||
## 1. 引擎总览
|
||
|
||
| 引擎 | 职责 | 执行方式 | Phase |
|
||
|------|------|----------|-------|
|
||
| `HardRuleEngine` | 执行硬规则(JSON Logic) | CPU 执行,无 LLM | 1 |
|
||
| `SoftRuleEngine` | 执行软指令(LLM 推理) | LLM + 工具调用 | 2 |
|
||
| `SopEngine` | SOP 状态机调度 | 编排 Hard/Soft 引擎 | 2 |
|
||
| `ReActEngine` | 多步推理(思考-行动-观察) | LLM + 循环推理 | 5 |
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ SopEngine (状态机) │
|
||
│ ┌────────────────────────────────────────────────────────────────┐ │
|
||
│ │ 节点A: HardRuleEngine → 节点B: SoftRuleEngine → 节点C: ... │ │
|
||
│ └────────────────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ ToolsService (工具层) │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 2. HardRuleEngine - 硬规则引擎
|
||
|
||
> **文件路径**: `backend/src/modules/iit-manager/engines/HardRuleEngine.ts`
|
||
|
||
### 2.1 核心职责
|
||
|
||
- 执行 JSON Logic 规则,无需 LLM
|
||
- 返回结构化的违规列表
|
||
- 执行时间 < 100ms
|
||
|
||
### 2.2 完整实现
|
||
|
||
```typescript
|
||
import jsonLogic from 'json-logic-js';
|
||
|
||
export interface HardRule {
|
||
field: string;
|
||
logic: object; // JSON Logic 表达式
|
||
message: string;
|
||
severity?: 'error' | 'warning' | 'info';
|
||
}
|
||
|
||
export interface RuleResult {
|
||
field: string;
|
||
message: string;
|
||
severity: string;
|
||
value?: any;
|
||
}
|
||
|
||
export class HardRuleEngine {
|
||
/**
|
||
* 执行硬规则检查
|
||
* @param rules 规则数组
|
||
* @param data 待检查的数据
|
||
* @returns 违规列表(空数组表示全部通过)
|
||
*/
|
||
run(rules: HardRule[], data: Record<string, any>): RuleResult[] {
|
||
const errors: RuleResult[] = [];
|
||
|
||
for (const rule of rules) {
|
||
try {
|
||
const passed = jsonLogic.apply(rule.logic, data);
|
||
|
||
if (!passed) {
|
||
errors.push({
|
||
field: rule.field,
|
||
message: rule.message,
|
||
severity: rule.severity || 'error',
|
||
value: data[rule.field]
|
||
});
|
||
}
|
||
} catch (error) {
|
||
// 规则执行异常,记录但不中断
|
||
errors.push({
|
||
field: rule.field,
|
||
message: `规则执行异常: ${error.message}`,
|
||
severity: 'error'
|
||
});
|
||
}
|
||
}
|
||
|
||
return errors;
|
||
}
|
||
|
||
/**
|
||
* 检查是否全部通过
|
||
*/
|
||
isAllPassed(rules: HardRule[], data: Record<string, any>): boolean {
|
||
return this.run(rules, data).length === 0;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2.3 JSON Logic 常用表达式
|
||
|
||
```javascript
|
||
// 范围检查
|
||
{ "and": [
|
||
{ ">=": [{ "var": "age" }, 18] },
|
||
{ "<=": [{ "var": "age" }, 75] }
|
||
]}
|
||
|
||
// 非空检查
|
||
{ "!!": { "var": "informed_consent_date" } }
|
||
|
||
// 日期比较(需自定义操作符)
|
||
{ "<=": [{ "var": "icf_date" }, { "var": "enrollment_date" }] }
|
||
|
||
// 枚举值检查
|
||
{ "in": [{ "var": "ecog" }, [0, 1, 2]] }
|
||
|
||
// 字符串包含
|
||
{ "in": ["肺炎", { "var": "medical_history" }] }
|
||
```
|
||
|
||
---
|
||
|
||
## 3. SoftRuleEngine - 软规则引擎
|
||
|
||
> **文件路径**: `backend/src/modules/iit-manager/engines/SoftRuleEngine.ts`
|
||
|
||
### 3.1 核心职责
|
||
|
||
- 执行需要 LLM 推理的软指令
|
||
- 支持工具调用
|
||
- 实现自我修正回路(最多 3 次重试)
|
||
|
||
### 3.2 完整实现
|
||
|
||
```typescript
|
||
import { LLMFactory } from '../../common/llm/adapters/LLMFactory';
|
||
import { ToolsService } from '../services/ToolsService';
|
||
|
||
export interface SoftRuleResult {
|
||
passed: boolean;
|
||
reason: string;
|
||
confidence?: number;
|
||
evidence?: any;
|
||
}
|
||
|
||
export class SoftRuleEngine {
|
||
private llm = LLMFactory.create('qwen');
|
||
private tools: ToolsService;
|
||
|
||
constructor(tools: ToolsService) {
|
||
this.tools = tools;
|
||
}
|
||
|
||
/**
|
||
* 执行软指令,带自我修正回路
|
||
*/
|
||
async runWithRetry(
|
||
instruction: string,
|
||
context: any,
|
||
maxRetries: number = 3
|
||
): Promise<SoftRuleResult> {
|
||
const systemPrompt = `你是一个临床研究质控专家。请根据以下指令和数据进行判断。
|
||
|
||
指令: ${instruction}
|
||
|
||
请返回 JSON 格式:
|
||
{
|
||
"passed": true/false,
|
||
"reason": "判断理由",
|
||
"confidence": 0.0-1.0,
|
||
"evidence": "支持判断的证据"
|
||
}`;
|
||
|
||
let history: Message[] = [
|
||
{ role: 'system', content: systemPrompt },
|
||
{ role: 'user', content: `数据: ${JSON.stringify(context)}` }
|
||
];
|
||
|
||
for (let i = 0; i < maxRetries; i++) {
|
||
const response = await this.llm.chat(history, {
|
||
tools: this.tools.getToolDefinitions()
|
||
});
|
||
|
||
// AI 决定调用工具
|
||
if (response.toolCalls && response.toolCalls.length > 0) {
|
||
for (const toolCall of response.toolCalls) {
|
||
try {
|
||
const result = await this.tools.executeTool(
|
||
toolCall.name,
|
||
toolCall.args
|
||
);
|
||
history.push({
|
||
role: 'tool',
|
||
toolCallId: toolCall.id,
|
||
content: JSON.stringify(result)
|
||
});
|
||
} catch (error) {
|
||
// ⚠️ 自我修正:告诉 LLM 它错了
|
||
history.push({
|
||
role: 'user',
|
||
content: `工具调用失败: ${error.message}。请检查参数并重试,或直接根据已有信息判断。`
|
||
});
|
||
continue;
|
||
}
|
||
}
|
||
} else {
|
||
// AI 返回最终答案
|
||
return this.parseResult(response.content);
|
||
}
|
||
}
|
||
|
||
return {
|
||
passed: false,
|
||
reason: '多次重试后仍失败,需人工复核',
|
||
confidence: 0
|
||
};
|
||
}
|
||
|
||
private parseResult(content: string): SoftRuleResult {
|
||
try {
|
||
// 提取 JSON(可能被 Markdown 包裹)
|
||
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
||
if (jsonMatch) {
|
||
return JSON.parse(jsonMatch[0]);
|
||
}
|
||
} catch (e) {
|
||
// 解析失败
|
||
}
|
||
|
||
// 回退:尝试从文本推断
|
||
const passed = content.includes('通过') || content.includes('符合');
|
||
return {
|
||
passed,
|
||
reason: content.slice(0, 200),
|
||
confidence: 0.5
|
||
};
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. SopEngine - SOP 状态机引擎
|
||
|
||
> **文件路径**: `backend/src/modules/iit-manager/engines/SopEngine.ts`
|
||
|
||
### 4.1 核心职责
|
||
|
||
- 解析 Skill 配置中的 SOP 流程图
|
||
- 按顺序执行节点(Hard → Soft → Human Review)
|
||
- 实现状态持久化(防止服务重启丢失进度)
|
||
- 实现 SUSPENDED 挂起机制(解决"死锁"风险)
|
||
|
||
### 4.2 类型定义
|
||
|
||
```typescript
|
||
interface SopConfig {
|
||
name: string;
|
||
start_node: string;
|
||
nodes: {
|
||
[nodeId: string]: SopNode;
|
||
};
|
||
}
|
||
|
||
interface SopNode {
|
||
type: 'hard_rule' | 'soft_instruction' | 'human_review';
|
||
// hard_rule 类型
|
||
rules?: HardRule[];
|
||
// soft_instruction 类型
|
||
instruction?: string;
|
||
tools?: string[];
|
||
// human_review 类型
|
||
description?: string;
|
||
// 流转
|
||
on_pass: string;
|
||
on_fail: string;
|
||
on_approve?: string; // human_review 专用
|
||
on_reject?: string; // human_review 专用
|
||
on_error?: string;
|
||
}
|
||
|
||
interface SopResult {
|
||
status: 'COMPLETED' | 'SUSPENDED' | 'FAILED';
|
||
finalState: string;
|
||
trace: TraceItem[];
|
||
taskRunId?: string;
|
||
message?: string;
|
||
}
|
||
|
||
interface TraceItem {
|
||
node: string;
|
||
timestamp: Date;
|
||
result?: any;
|
||
}
|
||
```
|
||
|
||
### 4.3 完整实现
|
||
|
||
```typescript
|
||
import PgBoss from 'pg-boss';
|
||
import { prisma } from '../../common/prisma';
|
||
import { HardRuleEngine } from './HardRuleEngine';
|
||
import { SoftRuleEngine } from './SoftRuleEngine';
|
||
|
||
export class SopEngine {
|
||
private hardEngine: HardRuleEngine;
|
||
private softEngine: SoftRuleEngine;
|
||
private pgBoss: PgBoss;
|
||
|
||
constructor(
|
||
hardEngine: HardRuleEngine,
|
||
softEngine: SoftRuleEngine,
|
||
pgBoss: PgBoss
|
||
) {
|
||
this.hardEngine = hardEngine;
|
||
this.softEngine = softEngine;
|
||
this.pgBoss = pgBoss;
|
||
}
|
||
|
||
/**
|
||
* 执行 SOP 流程
|
||
*/
|
||
async run(
|
||
skillConfig: SopConfig,
|
||
data: any,
|
||
taskRunId?: string
|
||
): Promise<SopResult> {
|
||
// 创建或恢复任务记录
|
||
const task = taskRunId
|
||
? await this.resumeTask(taskRunId)
|
||
: await this.createTask(skillConfig, data);
|
||
|
||
let currentNodeId = task.currentNode || skillConfig.start_node;
|
||
let context = { ...data };
|
||
const trace: TraceItem[] = task.trace ? JSON.parse(task.trace) : [];
|
||
|
||
while (currentNodeId && !currentNodeId.startsWith('end')) {
|
||
const node = skillConfig.nodes[currentNodeId];
|
||
trace.push({ node: currentNodeId, timestamp: new Date() });
|
||
|
||
// ⚠️ 每步持久化,防止服务重启丢失进度
|
||
await prisma.iitTaskRun.update({
|
||
where: { id: task.id },
|
||
data: {
|
||
currentNode: currentNodeId,
|
||
trace: JSON.stringify(trace),
|
||
updatedAt: new Date()
|
||
}
|
||
});
|
||
|
||
// 检查是否需要人工介入
|
||
if (node.type === 'human_review') {
|
||
return await this.handleHumanReview(task.id, node, data, trace);
|
||
}
|
||
|
||
// 执行节点
|
||
let result: NodeResult;
|
||
try {
|
||
if (node.type === 'hard_rule') {
|
||
const errors = this.hardEngine.run(node.rules || [], context);
|
||
result = { passed: errors.length === 0, errors };
|
||
} else {
|
||
result = await this.softEngine.runWithRetry(
|
||
node.instruction || '',
|
||
context
|
||
);
|
||
}
|
||
} catch (error) {
|
||
// 执行异常,走 on_error 分支
|
||
currentNodeId = node.on_error || 'end_error';
|
||
continue;
|
||
}
|
||
|
||
// 记录违规
|
||
if (!result.passed) {
|
||
await this.savePendingAction(task.id, data.recordId, result);
|
||
}
|
||
|
||
// 状态流转
|
||
currentNodeId = result.passed ? node.on_pass : node.on_fail;
|
||
}
|
||
|
||
// 完成任务
|
||
await prisma.iitTaskRun.update({
|
||
where: { id: task.id },
|
||
data: {
|
||
status: 'COMPLETED',
|
||
currentNode: currentNodeId,
|
||
completedAt: new Date()
|
||
}
|
||
});
|
||
|
||
return {
|
||
status: 'COMPLETED',
|
||
finalState: currentNodeId,
|
||
trace
|
||
};
|
||
}
|
||
|
||
/**
|
||
* ⚠️ 处理人工复核节点 - SUSPENDED 挂起机制
|
||
*/
|
||
private async handleHumanReview(
|
||
taskRunId: string,
|
||
node: SopNode,
|
||
data: any,
|
||
trace: TraceItem[]
|
||
): Promise<SopResult> {
|
||
// 挂起任务,不再占用 Worker
|
||
await prisma.iitTaskRun.update({
|
||
where: { id: taskRunId },
|
||
data: {
|
||
status: 'SUSPENDED',
|
||
suspendedAt: new Date(),
|
||
resumeCallback: node.on_approve,
|
||
rejectCallback: node.on_reject || 'end_rejected'
|
||
}
|
||
});
|
||
|
||
// 通知相关人员
|
||
await this.notifyReviewRequired(data, node);
|
||
|
||
return {
|
||
status: 'SUSPENDED',
|
||
finalState: 'human_review',
|
||
trace,
|
||
taskRunId,
|
||
message: '任务已挂起,等待人工复核'
|
||
};
|
||
}
|
||
|
||
/**
|
||
* ⚠️ 唤醒机制 - CRC 确认后继续执行
|
||
*/
|
||
async resumeFromSuspended(
|
||
taskRunId: string,
|
||
decision: 'approve' | 'reject'
|
||
): Promise<void> {
|
||
const task = await prisma.iitTaskRun.findUnique({
|
||
where: { id: taskRunId }
|
||
});
|
||
|
||
if (!task || task.status !== 'SUSPENDED') {
|
||
throw new Error('任务不在挂起状态');
|
||
}
|
||
|
||
const nextNode = decision === 'approve'
|
||
? task.resumeCallback
|
||
: task.rejectCallback || 'end_rejected';
|
||
|
||
// 更新状态
|
||
await prisma.iitTaskRun.update({
|
||
where: { id: taskRunId },
|
||
data: {
|
||
status: 'RUNNING',
|
||
currentNode: nextNode,
|
||
resumedAt: new Date()
|
||
}
|
||
});
|
||
|
||
// 创建新的 Job 继续执行
|
||
await this.pgBoss.send('sop-continue', { taskRunId });
|
||
}
|
||
|
||
// ... 辅助方法省略
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. ReActEngine - 多步推理引擎
|
||
|
||
> **文件路径**: `backend/src/modules/iit-manager/engines/ReActEngine.ts`
|
||
|
||
### 5.1 核心职责
|
||
|
||
- 处理模糊查询,支持多步推理
|
||
- 循环执行"思考 → 行动 → 观察"
|
||
- **只允许调用只读工具**(安全约束)
|
||
- **只返回 Final Answer**(解决"多嘴"问题)
|
||
- **V2.9 新增**:支持多意图处理(用户一句话包含多个任务)
|
||
|
||
### 5.2 安全约束
|
||
|
||
```typescript
|
||
// 工具白名单:ReAct 只能调用只读工具
|
||
private readonly READONLY_TOOLS = [
|
||
'read_clinical_data',
|
||
'search_protocol',
|
||
'get_project_stats',
|
||
'check_visit_window'
|
||
];
|
||
|
||
// 资源限制
|
||
private maxIterations = 5; // 最大迭代次数
|
||
private maxTokens = 4000; // Token 预算
|
||
```
|
||
|
||
### 5.3 完整实现
|
||
|
||
```typescript
|
||
import { LLMFactory } from '../../common/llm/adapters/LLMFactory';
|
||
import { ToolsService } from '../services/ToolsService';
|
||
import { prisma } from '../../common/prisma';
|
||
|
||
export interface ReActResult {
|
||
success: boolean;
|
||
content: string;
|
||
trace: TraceItem[];
|
||
}
|
||
|
||
interface TraceItem {
|
||
iteration: number;
|
||
thought?: string;
|
||
action?: string;
|
||
observation?: string;
|
||
}
|
||
|
||
export class ReActEngine {
|
||
private llm = LLMFactory.create('qwen');
|
||
private tools: ToolsService;
|
||
|
||
private readonly READONLY_TOOLS = [
|
||
'read_clinical_data',
|
||
'search_protocol',
|
||
'get_project_stats',
|
||
'check_visit_window'
|
||
];
|
||
|
||
private maxIterations = 5;
|
||
private maxTokens = 4000;
|
||
private tokenCounter = 0;
|
||
|
||
constructor(tools: ToolsService) {
|
||
this.tools = tools;
|
||
}
|
||
|
||
async run(query: string, context: AgentContext): Promise<ReActResult> {
|
||
this.tokenCounter = 0;
|
||
|
||
// V2.9 优化:支持多意图处理的 Prompt
|
||
const systemPrompt = `你是一个临床研究智能助手。你可以使用以下工具回答问题:
|
||
${this.formatToolDescriptions(this.READONLY_TOOLS)}
|
||
|
||
请按照 ReAct 模式思考:
|
||
1. 思考:分析问题,决定下一步
|
||
2. 行动:调用工具获取信息
|
||
3. 观察:查看工具返回结果
|
||
4. 重复以上步骤,直到能回答用户问题
|
||
|
||
## 多任务处理原则 (V2.9)
|
||
|
||
当用户的消息包含多个任务时,请遵循以下步骤:
|
||
|
||
**Step 1: 意图拆解**
|
||
- 识别用户消息中的所有独立意图
|
||
- 按依赖关系排序:先处理前置任务
|
||
- 示例:"帮我查一下 P003 的 ECOG,然后录入今天的访视数据"
|
||
→ 意图1: 查询 P003 ECOG(只读)
|
||
→ 意图2: 录入访视数据(需要引导)
|
||
|
||
**Step 2: 顺序执行**
|
||
- 每次只聚焦处理一个意图
|
||
- 完成后明确告知用户,并继续下一个
|
||
- 遇到写操作,暂停并引导用户确认
|
||
|
||
**Step 3: 统一回复**
|
||
- 在最终回复中按顺序总结所有任务结果
|
||
- 格式:
|
||
1. [任务1] 已完成 / 结果:...
|
||
2. [任务2] 需要确认 / 引导操作
|
||
|
||
注意:你只能查询数据,不能修改数据。如果用户需要修改操作,请引导他们使用正式的质控流程。`;
|
||
|
||
let messages: Message[] = [
|
||
{ role: 'system', content: systemPrompt },
|
||
{ role: 'user', content: query }
|
||
];
|
||
|
||
const trace: TraceItem[] = [];
|
||
|
||
for (let i = 0; i < this.maxIterations; i++) {
|
||
// Token 预算检查
|
||
if (this.tokenCounter > this.maxTokens) {
|
||
return {
|
||
success: false,
|
||
content: '抱歉,这个问题比较复杂,请尝试更具体的描述。',
|
||
trace
|
||
};
|
||
}
|
||
|
||
const response = await this.llm.chat(messages, {
|
||
tools: this.getReadonlyToolDefinitions()
|
||
});
|
||
|
||
this.tokenCounter += response.usage?.total_tokens || 0;
|
||
trace.push({ iteration: i, thought: response.content });
|
||
|
||
// AI 决定结束,返回 Final Answer
|
||
if (!response.toolCalls || response.toolCalls.length === 0) {
|
||
return { success: true, content: response.content, trace };
|
||
}
|
||
|
||
// 执行工具调用
|
||
let errorCount = 0;
|
||
for (const toolCall of response.toolCalls) {
|
||
// ⚠️ 安全检查:只允许只读工具
|
||
if (!this.READONLY_TOOLS.includes(toolCall.name)) {
|
||
messages.push({
|
||
role: 'tool',
|
||
toolCallId: toolCall.id,
|
||
content: `错误:工具 ${toolCall.name} 不在允许列表中。你只能使用只读工具查询数据。如果需要修改数据,请引导用户使用"质控"命令。`
|
||
});
|
||
errorCount++;
|
||
continue;
|
||
}
|
||
|
||
try {
|
||
const result = await this.tools.executeTool(toolCall.name, toolCall.args);
|
||
messages.push({
|
||
role: 'tool',
|
||
toolCallId: toolCall.id,
|
||
content: JSON.stringify(result)
|
||
});
|
||
trace[i].observation = JSON.stringify(result).slice(0, 200);
|
||
} catch (error) {
|
||
errorCount++;
|
||
messages.push({
|
||
role: 'tool',
|
||
toolCallId: toolCall.id,
|
||
content: `工具调用失败: ${error.message}`
|
||
});
|
||
}
|
||
}
|
||
|
||
// ⚠️ 幻觉熔断:连续 2 次工具调用失败
|
||
if (errorCount >= 2) {
|
||
return {
|
||
success: false,
|
||
content: '抱歉,我遇到了一些技术问题,无法获取相关信息。请稍后重试或联系管理员。',
|
||
trace
|
||
};
|
||
}
|
||
}
|
||
|
||
return {
|
||
success: false,
|
||
content: '抱歉,我无法在有限步骤内完成这个查询。请尝试更具体的问题。',
|
||
trace
|
||
};
|
||
}
|
||
|
||
/**
|
||
* ⚠️ 保存 Trace 到数据库(仅供 Admin 调试)
|
||
*/
|
||
async saveTrace(
|
||
projectId: string,
|
||
userId: string,
|
||
query: string,
|
||
result: ReActResult
|
||
): Promise<void> {
|
||
await prisma.iitAgentTrace.create({
|
||
data: {
|
||
projectId,
|
||
userId,
|
||
query,
|
||
trace: result.trace,
|
||
tokenUsage: this.tokenCounter,
|
||
success: result.success,
|
||
createdAt: new Date()
|
||
}
|
||
});
|
||
}
|
||
|
||
private getReadonlyToolDefinitions() {
|
||
return this.tools.getToolDefinitions()
|
||
.filter(t => this.READONLY_TOOLS.includes(t.name));
|
||
}
|
||
|
||
private formatToolDescriptions(toolNames: string[]): string {
|
||
return toolNames.map(name => {
|
||
const def = this.tools.getToolDefinition(name);
|
||
return `- ${name}: ${def?.description || ''}`;
|
||
}).join('\n');
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 引擎集成示意
|
||
|
||
```typescript
|
||
// 初始化
|
||
const hardEngine = new HardRuleEngine();
|
||
const toolsService = new ToolsService(redcapAdapter, difyClient);
|
||
const softEngine = new SoftRuleEngine(toolsService);
|
||
const sopEngine = new SopEngine(hardEngine, softEngine, pgBoss);
|
||
const reactEngine = new ReActEngine(toolsService);
|
||
|
||
// 在 ChatService 中使用
|
||
async handleMessage(userId: string, message: string) {
|
||
const intent = await this.intentService.detect(message);
|
||
|
||
if (intent.type === 'QC_TASK') {
|
||
// 走 SOP 引擎
|
||
return await this.sopEngine.run(skillConfig, data);
|
||
} else if (intent.type === 'QA_QUERY') {
|
||
// 走 ReAct 引擎
|
||
const result = await this.reactEngine.run(message, context);
|
||
// ⚠️ 只返回 Final Answer,不返回中间思考
|
||
await this.reactEngine.saveTrace(projectId, userId, message, result);
|
||
return result.content;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
**文档维护人**:AI Agent
|
||
**最后更新**:2026-02-05
|