618 lines
21 KiB
Markdown
618 lines
21 KiB
Markdown
# 07 - Memory Manager Agent (记忆管理) 设计详解
|
||
|
||
## 1. 核心职责
|
||
|
||
Memory Manager 是系统中的**用户信息管家**。它负责从对话中提取用户信息、组织存储到长期记忆、以及按需检索用户历史上下文。它替代了旧架构中 `StrategyEngineService.extractUserInfo()` 的手动提取逻辑。
|
||
|
||
核心能力:
|
||
1. **信息提取** -- 从对话文本中自动识别和提取用户的个人信息(年龄、学历、工作等)
|
||
2. **记忆存储** -- 将提取的信息分类标注后保存到 knowledge-service 的长期记忆
|
||
3. **上下文加载** -- 检索用户的历史记忆,为其他 Agent 提供用户背景
|
||
4. **摘要生成** -- 将散落在多次对话中的用户信息整合为结构化的用户画像
|
||
5. **去重合并** -- 避免重复存储相同信息,新信息覆盖旧信息
|
||
|
||
> 设计原则:**只提取用户明确表述的事实,不做推测。** "我大概三十多岁"提取为"30+"而非具体数字。
|
||
|
||
## 2. 模型与参数
|
||
|
||
```typescript
|
||
{
|
||
model: 'claude-haiku-3-5-20241022', // 结构化提取任务,Haiku 足矣
|
||
max_tokens: 1000,
|
||
temperature: 0, // 信息提取必须确定性
|
||
system: [
|
||
{
|
||
type: 'text',
|
||
text: memoryManagerPrompt,
|
||
cache_control: { type: 'ephemeral' }
|
||
}
|
||
],
|
||
}
|
||
```
|
||
|
||
选用 Haiku 的理由:
|
||
- 信息提取是相对简单的 NLP 任务(实体识别 + 分类)
|
||
- 需要高频调用(每轮对话后都可能触发),成本敏感
|
||
- 输入输出格式高度结构化,不需要 Sonnet 的推理能力
|
||
- 速度要求高 -- 信息保存不应阻塞主对话流
|
||
|
||
## 3. 可用工具 (Available Tools)
|
||
|
||
Memory Manager 有 **2 个工具**:
|
||
|
||
### 3.1 save_user_memory
|
||
|
||
```typescript
|
||
{
|
||
name: 'save_user_memory',
|
||
description: '将用户信息保存到长期记忆存储。每条记忆包含类型、内容和相关移民类别。',
|
||
input_schema: {
|
||
type: 'object',
|
||
properties: {
|
||
memoryType: {
|
||
type: 'string',
|
||
enum: ['FACT', 'PREFERENCE', 'INTENT'],
|
||
description: 'FACT=用户陈述的事实(年龄、学历等),PREFERENCE=用户偏好(倾向的移民方式),INTENT=用户意图(计划时间、预算等)'
|
||
},
|
||
content: {
|
||
type: 'string',
|
||
description: '要保存的记忆内容,用简洁的陈述句表达,如"用户32岁,清华大学计算机硕士"'
|
||
},
|
||
category: {
|
||
type: 'string',
|
||
enum: ['QMAS', 'GEP', 'IANG', 'TTPS', 'CIES', 'TECHTAS'],
|
||
description: '相关的移民类别(如果有明确关联)'
|
||
}
|
||
},
|
||
required: ['memoryType', 'content']
|
||
}
|
||
}
|
||
```
|
||
|
||
**底层实现**:调用 knowledge-service 的 `POST /api/v1/memory/user`
|
||
|
||
```typescript
|
||
const response = await axios.post('http://knowledge-service:3003/api/v1/memory/user', {
|
||
userId: context.userId,
|
||
tenantId: context.tenantId,
|
||
memoryType: input.memoryType,
|
||
content: input.content,
|
||
category: input.category,
|
||
importance: IMPORTANCE_MAP[input.memoryType], // FACT:70, INTENT:80, PREFERENCE:60
|
||
source: 'conversation',
|
||
conversationId: context.conversationId,
|
||
});
|
||
```
|
||
|
||
### 3.2 get_user_context
|
||
|
||
```typescript
|
||
{
|
||
name: 'get_user_context',
|
||
description: '获取用户的历史记忆和背景信息。返回按重要性和相关性排序的记忆列表。',
|
||
input_schema: {
|
||
type: 'object',
|
||
properties: {
|
||
query: {
|
||
type: 'string',
|
||
description: '检索查询,如"用户的基本信息和移民意向"'
|
||
}
|
||
},
|
||
required: ['query']
|
||
}
|
||
}
|
||
```
|
||
|
||
## 4. System Prompt 要点
|
||
|
||
```
|
||
# 身份
|
||
你是 iConsulting 的记忆管理专员。你负责从对话中提取、保存和检索用户信息。
|
||
|
||
# 核心原则
|
||
1. 只提取用户明确陈述的信息,不做推测
|
||
2. 模糊信息保留模糊性("三十多岁" → "30+岁",不要推测为35)
|
||
3. 同一信息不要重复保存,检查已有记忆后再决定是否保存
|
||
4. 每条记忆用简洁的陈述句表达
|
||
5. 正确分类 memoryType:
|
||
- FACT: 客观事实(年龄、学历、工作、收入等)
|
||
- PREFERENCE: 主观偏好(偏好的移民方式、时间偏好等)
|
||
- INTENT: 行动意图(打算何时申请、预算范围、已在准备等)
|
||
|
||
# 需要提取的核心信息字段
|
||
## 基础信息
|
||
- age: 年龄
|
||
- education: 最高学历(博士/硕士/学士/大专/高中)
|
||
- university: 毕业院校
|
||
- major: 专业方向
|
||
|
||
## 职业信息
|
||
- currentJobTitle: 当前职位
|
||
- currentIndustry: 所属行业
|
||
- currentCompany: 当前公司(如果提到)
|
||
- totalYearsOfExperience: 工作年限
|
||
- annualIncome: 年收入(注意币种)
|
||
|
||
## 移民相关
|
||
- targetCategory: 感兴趣的移民类别
|
||
- hasHongKongEmployer: 是否有香港雇主
|
||
- hasTechBackground: 是否有科技背景
|
||
- investmentAmount: 可投资金额
|
||
- immigrationTimeline: 计划移民时间
|
||
|
||
## 家庭信息
|
||
- maritalStatus: 婚姻状况
|
||
- hasChildren: 是否有子女
|
||
- familySupport: 家人支持程度
|
||
|
||
# 操作类型
|
||
根据 Coordinator 传入的 action 参数执行不同操作:
|
||
- load_context: 加载用户上下文(调用 get_user_context)
|
||
- save_info: 从对话中提取信息并保存(调用 save_user_memory)
|
||
- summarize: 加载所有记忆并生成结构化摘要
|
||
```
|
||
|
||
## 5. 输入/输出格式
|
||
|
||
Memory Manager 的输入/输出根据 `action` 不同而变化:
|
||
|
||
### Action: load_context
|
||
|
||
**输入**:
|
||
|
||
```typescript
|
||
interface MemoryManagerLoadInput {
|
||
action: 'load_context';
|
||
/** 当前用户问题(用于检索相关记忆) */
|
||
query: string;
|
||
}
|
||
```
|
||
|
||
**输出**:
|
||
|
||
```typescript
|
||
interface MemoryManagerLoadOutput {
|
||
action: 'load_context';
|
||
/** 检索到的用户记忆列表 */
|
||
memories: Array<{
|
||
type: 'FACT' | 'PREFERENCE' | 'INTENT';
|
||
content: string;
|
||
importance: number;
|
||
category?: string;
|
||
createdAt?: string;
|
||
}>;
|
||
/** 用户画像摘要(如果有足够信息) */
|
||
user_profile_summary?: string;
|
||
/** 记忆总数 */
|
||
total_memories: number;
|
||
}
|
||
```
|
||
|
||
### Action: save_info
|
||
|
||
**输入**:
|
||
|
||
```typescript
|
||
interface MemoryManagerSaveInput {
|
||
action: 'save_info';
|
||
/** 用户最新消息 */
|
||
userMessage: string;
|
||
/** 助手最新回复 */
|
||
assistantMessage: string;
|
||
/** 已有的用户信息(避免重复提取) */
|
||
existingInfo?: Record<string, unknown>;
|
||
}
|
||
```
|
||
|
||
**输出**:
|
||
|
||
```typescript
|
||
interface MemoryManagerSaveOutput {
|
||
action: 'save_info';
|
||
/** 本次提取并保存的信息 */
|
||
extracted_info: Record<string, unknown>;
|
||
/** 保存的记忆条数 */
|
||
saved_count: number;
|
||
/** 保存的记忆详情 */
|
||
saved_memories: Array<{
|
||
memoryType: 'FACT' | 'PREFERENCE' | 'INTENT';
|
||
content: string;
|
||
field: string; // 对应的字段名
|
||
}>;
|
||
/** 跳过的信息(已存在) */
|
||
skipped_fields: string[];
|
||
}
|
||
```
|
||
|
||
### Action: summarize
|
||
|
||
**输入**:
|
||
|
||
```typescript
|
||
interface MemoryManagerSummarizeInput {
|
||
action: 'summarize';
|
||
}
|
||
```
|
||
|
||
**输出**:
|
||
|
||
```typescript
|
||
interface MemoryManagerSummarizeOutput {
|
||
action: 'summarize';
|
||
/** 结构化用户画像 */
|
||
user_profile: {
|
||
// 基础信息
|
||
basic: {
|
||
age?: string;
|
||
education?: string;
|
||
university?: string;
|
||
major?: string;
|
||
nationality?: string;
|
||
location?: string;
|
||
};
|
||
// 职业信息
|
||
career: {
|
||
jobTitle?: string;
|
||
industry?: string;
|
||
company?: string;
|
||
yearsOfExperience?: string;
|
||
annualIncome?: string;
|
||
};
|
||
// 移民意向
|
||
immigration: {
|
||
targetCategories?: string[];
|
||
timeline?: string;
|
||
hasHKEmployer?: boolean;
|
||
investmentCapacity?: string;
|
||
};
|
||
// 家庭情况
|
||
family: {
|
||
maritalStatus?: string;
|
||
hasChildren?: boolean;
|
||
familySupport?: string;
|
||
};
|
||
};
|
||
/** 信息完整度 */
|
||
completeness: {
|
||
score: number; // 0-100
|
||
filled_fields: string[];
|
||
missing_fields: string[];
|
||
};
|
||
/** 一段话总结 */
|
||
narrative_summary: string;
|
||
}
|
||
```
|
||
|
||
## 6. 触发时机 (When to Trigger)
|
||
|
||
Coordinator 根据不同的 `action` 在不同时机调用 `invoke_memory_manager`:
|
||
|
||
### load_context 触发时机
|
||
|
||
| 场景 | 触发条件 | 目的 |
|
||
|------|----------|------|
|
||
| 对话开始 | 第 1 轮对话 | 加载用户历史信息,实现跨会话记忆 |
|
||
| 需要用户背景 | Coordinator 需要参考用户画像 | 为决策提供上下文 |
|
||
| 评估前准备 | 准备调 Assessment Expert 前 | 补充已有但本次对话未提及的信息 |
|
||
|
||
### save_info 触发时机
|
||
|
||
| 场景 | 触发条件 | 目的 |
|
||
|------|----------|------|
|
||
| 用户分享个人信息 | 对话中出现年龄、学历、工作等信息 | 实时保存用户数据 |
|
||
| 每轮对话后 | 助手回复生成后 | 确保不遗漏任何用户信息 |
|
||
| 用户表达偏好/意图 | "我比较想走高才通" / "打算明年申请" | 保存用户偏好和意图 |
|
||
|
||
### summarize 触发时机
|
||
|
||
| 场景 | 触发条件 | 目的 |
|
||
|------|----------|------|
|
||
| 评估前摘要 | 准备做全面评估时 | 生成完整的用户画像供 Assessment Expert 使用 |
|
||
| 对话结束时 | 对话即将结束 | 更新用户画像摘要 |
|
||
|
||
**调用频率建议**:
|
||
- `load_context`:每次新对话开始时调用 1 次
|
||
- `save_info`:不需要每轮都调用,Coordinator 判断用户提供了新信息时才调用
|
||
- `summarize`:整个对话过程中最多调用 1-2 次
|
||
|
||
## 7. 内部循环 (Internal Loop)
|
||
|
||
Memory Manager 的内部循环根据 action 不同而变化:
|
||
|
||
### load_context 流程
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ load_context Loop (max 1 turn) │
|
||
│ │
|
||
│ Turn 0: │
|
||
│ ├── get_user_context({query: input.query}) │
|
||
│ ├── 整理返回的记忆列表 │
|
||
│ ├── 如果记忆足够多,生成 user_profile_summary │
|
||
│ └── 返回结构化输出 │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### save_info 流程
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ save_info Loop (max 2 turns) │
|
||
│ │
|
||
│ Turn 0: 提取信息 │
|
||
│ ├── LLM 分析 userMessage + assistantMessage │
|
||
│ ├── 对比 existingInfo,筛除已有字段 │
|
||
│ ├── 对新信息逐条调用 save_user_memory │
|
||
│ │ (可能并行保存多条) │
|
||
│ │ │
|
||
│ Turn 1: 确认保存结果(如需) │
|
||
│ ├── 检查 save 操作是否全部成功 │
|
||
│ └── 返回保存结果摘要 │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### summarize 流程
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ summarize Loop (max 2 turns) │
|
||
│ │
|
||
│ Turn 0: 加载所有记忆 │
|
||
│ ├── get_user_context({query: "用户所有背景信息"}) │
|
||
│ │ │
|
||
│ Turn 1: 生成摘要 │
|
||
│ ├── 将散碎的记忆整合为结构化 user_profile │
|
||
│ ├── 计算 completeness score │
|
||
│ └── 生成 narrative_summary │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**save_info 的提取逻辑伪代码**:
|
||
|
||
```typescript
|
||
async function extractAndSave(input: MemoryManagerSaveInput): Promise<MemoryManagerSaveOutput> {
|
||
// Step 1: LLM 提取信息(Haiku 快速完成)
|
||
const extractionPrompt = `
|
||
从以下对话中提取用户信息。只提取能明确确定的信息,不要猜测。
|
||
已有信息(不要重复提取):${JSON.stringify(input.existingInfo)}
|
||
|
||
用户消息:${input.userMessage}
|
||
助手回复:${input.assistantMessage}
|
||
|
||
返回 JSON:{field: value} 格式。
|
||
`;
|
||
|
||
const extracted = await llm.extract(extractionPrompt);
|
||
const newFields = filterOutExisting(extracted, input.existingInfo);
|
||
|
||
// Step 2: 逐条保存(可并行)
|
||
const savePromises = Object.entries(newFields).map(([field, value]) => {
|
||
const memoryType = inferMemoryType(field); // FACT/PREFERENCE/INTENT
|
||
return saveUserMemory({
|
||
memoryType,
|
||
content: `${FIELD_LABELS[field]}:${value}`,
|
||
category: inferCategory(field, value),
|
||
});
|
||
});
|
||
|
||
const results = await Promise.all(savePromises);
|
||
|
||
return {
|
||
action: 'save_info',
|
||
extracted_info: newFields,
|
||
saved_count: results.filter(r => r.success).length,
|
||
saved_memories: results.map(r => r.detail),
|
||
skipped_fields: Object.keys(extracted).filter(k => input.existingInfo?.[k]),
|
||
};
|
||
}
|
||
```
|
||
|
||
## 8. 与其他 Agent 的关系
|
||
|
||
```
|
||
┌──────────────┐ invoke_memory_manager ┌──────────────┐
|
||
│ │ ─────────────────────────────→ │ │
|
||
│ Coordinator │ MemoryManagerOutput │ Memory │
|
||
│ │ ←───────────────────────────── │ Manager │
|
||
└──────┬───────┘ └──────┬───────┘
|
||
│ │
|
||
│ Memory Manager 是其他 Agent 的 ├── save_user_memory
|
||
│ 数据供应商 └── get_user_context
|
||
│ │
|
||
│ ▼
|
||
│ ┌──────────────┐
|
||
│ │ Knowledge │
|
||
│ │ Service │
|
||
│ │ (Memory API) │
|
||
│ └──────────────┘
|
||
│
|
||
│ 协作模式:
|
||
│
|
||
│ 1. 对话开始:
|
||
│ Coordinator → invoke_memory_manager({action: 'load_context'})
|
||
│ → 获取用户画像 → 注入到后续所有 Agent 调用的上下文中
|
||
│
|
||
│ 2. 信息收集轮:
|
||
│ 用户发消息 → Coordinator 回复 → Coordinator 调用
|
||
│ invoke_memory_manager({action: 'save_info', userMessage, assistantMessage})
|
||
│ → 信息自动归档(异步,不阻塞主流程)
|
||
│
|
||
│ 3. 评估准备:
|
||
│ Coordinator → invoke_memory_manager({action: 'summarize'})
|
||
│ → 获取完整用户画像 → 传入 Assessment Expert
|
||
│
|
||
│ 4. 跨会话衔接:
|
||
│ 新对话开始 → load_context → 上一次对话的信息仍然可用
|
||
```
|
||
|
||
**与旧架构的对应关系**:
|
||
|
||
| 旧架构 | 新架构 |
|
||
|--------|--------|
|
||
| `StrategyEngineService.extractUserInfo()` | Memory Manager 的 `save_info` action |
|
||
| `ConsultingState.collectedInfo` | Memory Manager 从 knowledge-service 加载 |
|
||
| 手动在 System Prompt 中拼接用户信息 | Memory Manager 的 `load_context` + `summarize` |
|
||
| 信息只在单次对话中有效 | 信息持久化到 knowledge-service,跨会话有效 |
|
||
|
||
**异步保存优化**:
|
||
`save_info` 可以**异步执行**,不阻塞 Coordinator 的主回复流:
|
||
|
||
```typescript
|
||
// Coordinator 主流程
|
||
const reply = yield* generateReply(userMessage); // 先回复用户
|
||
|
||
// 异步保存用户信息(不阻塞)
|
||
invokeMemoryManager({
|
||
action: 'save_info',
|
||
userMessage,
|
||
assistantMessage: reply,
|
||
existingInfo: currentCollectedInfo,
|
||
}).catch(err => logger.warn('Memory save failed:', err));
|
||
```
|
||
|
||
## 9. 示例场景
|
||
|
||
### 场景 1:从对话中提取信息 (save_info)
|
||
|
||
**Coordinator 调用**:
|
||
|
||
```json
|
||
{
|
||
"tool": "invoke_memory_manager",
|
||
"input": {
|
||
"action": "save_info",
|
||
"userMessage": "我今年32岁,浙大计算机硕士毕业的,现在在杭州做前端开发,工作8年了,年薪大概80万",
|
||
"assistantMessage": "感谢您分享这些信息!您的背景很不错...",
|
||
"existingInfo": {}
|
||
}
|
||
}
|
||
```
|
||
|
||
**内部执行**:
|
||
|
||
```
|
||
Turn 0: LLM 分析对话,提取信息
|
||
→ 识别到 5 个字段:age=32, university="浙江大学", education="硕士",
|
||
major="计算机", currentJobTitle="前端开发", totalYearsOfExperience=8,
|
||
annualIncome=800000, currentLocation="杭州"
|
||
|
||
Turn 1: 并行保存 8 条记忆
|
||
→ save_user_memory({type: "FACT", content: "用户32岁"})
|
||
→ save_user_memory({type: "FACT", content: "浙江大学计算机硕士"})
|
||
→ save_user_memory({type: "FACT", content: "前端开发工程师,工作8年"})
|
||
→ save_user_memory({type: "FACT", content: "年薪约80万人民币"})
|
||
→ save_user_memory({type: "FACT", content: "目前在杭州工作"})
|
||
→ ... (其他字段)
|
||
```
|
||
|
||
**返回结果**:
|
||
|
||
```json
|
||
{
|
||
"action": "save_info",
|
||
"extracted_info": {
|
||
"age": 32,
|
||
"education": "硕士",
|
||
"university": "浙江大学",
|
||
"major": "计算机",
|
||
"currentJobTitle": "前端开发",
|
||
"totalYearsOfExperience": 8,
|
||
"annualIncome": 800000,
|
||
"currentLocation": "杭州"
|
||
},
|
||
"saved_count": 5,
|
||
"saved_memories": [
|
||
{"memoryType": "FACT", "content": "用户32岁", "field": "age"},
|
||
{"memoryType": "FACT", "content": "浙江大学计算机硕士毕业", "field": "education+university+major"},
|
||
{"memoryType": "FACT", "content": "从事前端开发工作8年", "field": "currentJobTitle+totalYearsOfExperience"},
|
||
{"memoryType": "FACT", "content": "年薪约80万人民币", "field": "annualIncome"},
|
||
{"memoryType": "FACT", "content": "目前在杭州工作", "field": "currentLocation"}
|
||
],
|
||
"skipped_fields": []
|
||
}
|
||
```
|
||
|
||
### 场景 2:跨会话加载上下文 (load_context)
|
||
|
||
**Coordinator 调用**(新对话开始时):
|
||
|
||
```json
|
||
{
|
||
"tool": "invoke_memory_manager",
|
||
"input": {
|
||
"action": "load_context",
|
||
"query": "用户的基本背景和移民意向"
|
||
}
|
||
}
|
||
```
|
||
|
||
**返回结果**:
|
||
|
||
```json
|
||
{
|
||
"action": "load_context",
|
||
"memories": [
|
||
{"type": "FACT", "content": "用户32岁", "importance": 70, "createdAt": "2026-02-05"},
|
||
{"type": "FACT", "content": "浙江大学计算机硕士毕业", "importance": 70, "createdAt": "2026-02-05"},
|
||
{"type": "FACT", "content": "从事前端开发工作8年", "importance": 70, "createdAt": "2026-02-05"},
|
||
{"type": "FACT", "content": "年薪约80万人民币", "importance": 70, "createdAt": "2026-02-05"},
|
||
{"type": "INTENT", "content": "对高才通B类非常感兴趣", "importance": 80, "category": "TTPS", "createdAt": "2026-02-05"},
|
||
{"type": "PREFERENCE", "content": "希望尽快办理,时间越短越好", "importance": 60, "createdAt": "2026-02-05"}
|
||
],
|
||
"user_profile_summary": "32岁浙大计算机硕士,8年前端开发经验,年薪80万。上次对话中对高才通B类表现出浓厚兴趣,偏好快速通道。",
|
||
"total_memories": 6
|
||
}
|
||
```
|
||
|
||
### 场景 3:生成用户画像摘要 (summarize)
|
||
|
||
**Coordinator 调用**(准备做评估前):
|
||
|
||
```json
|
||
{
|
||
"tool": "invoke_memory_manager",
|
||
"input": {
|
||
"action": "summarize"
|
||
}
|
||
}
|
||
```
|
||
|
||
**返回结果**:
|
||
|
||
```json
|
||
{
|
||
"action": "summarize",
|
||
"user_profile": {
|
||
"basic": {
|
||
"age": "32岁",
|
||
"education": "硕士",
|
||
"university": "浙江大学",
|
||
"major": "计算机科学",
|
||
"location": "杭州"
|
||
},
|
||
"career": {
|
||
"jobTitle": "前端开发工程师",
|
||
"industry": "IT/互联网",
|
||
"yearsOfExperience": "8年",
|
||
"annualIncome": "约80万人民币"
|
||
},
|
||
"immigration": {
|
||
"targetCategories": ["TTPS"],
|
||
"timeline": "尽快",
|
||
"hasHKEmployer": false
|
||
},
|
||
"family": {
|
||
"maritalStatus": "未知",
|
||
"hasChildren": null,
|
||
"familySupport": "未知"
|
||
}
|
||
},
|
||
"completeness": {
|
||
"score": 65,
|
||
"filled_fields": ["age", "education", "university", "major", "location", "jobTitle", "industry", "yearsOfExperience", "annualIncome", "targetCategories", "timeline"],
|
||
"missing_fields": ["nationality", "maritalStatus", "hasChildren", "languageSkills", "hasHongKongEmployer", "hasTechBackground", "investmentAmount"]
|
||
},
|
||
"narrative_summary": "用户是一位32岁的前端开发工程师,浙江大学计算机硕士毕业,拥有8年IT行业经验,年薪约80万人民币,目前在杭州工作。用户对高才通B类最感兴趣,希望尽快办理。家庭情况和语言能力等信息尚未收集。"
|
||
}
|
||
```
|