flzxsqc-demo 1.4.7 → 1.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-backend/docs/async-analysis-external-worker-kafka-design.md +423 -0
- package/agent-backend/docs/cluster-analysis-results-company-migration-design.md +24 -2
- package/agent-backend/docs/qwen36-35b-128k-model-application.md +138 -0
- package/agent-backend/docs/session-semantic-clustering-tool-design.md +14 -1
- package/agent-backend/pom.xml +4 -0
- package/agent-backend/src/main/java/com/example/agent/application/harness/analysis/AnalysisOutputParser.java +1 -0
- package/agent-backend/src/main/java/com/example/agent/application/harness/analysis/AnalysisPromptCompiler.java +48 -0
- package/agent-backend/src/main/java/com/example/agent/application/harness/analysis/PerRecordAnalysisContract.java +5 -1
- package/agent-backend/src/main/java/com/example/agent/application/service/AgentSessionBatchToolContextApplier.java +173 -0
- package/agent-backend/src/main/java/com/example/agent/application/service/AgentSessionStore.java +9 -0
- package/agent-backend/src/main/java/com/example/agent/application/service/DslCompilerService.java +15 -16
- package/agent-backend/src/main/java/com/example/agent/application/service/ToolExecutionPipeline.java +57 -9
- package/agent-backend/src/main/java/com/example/agent/application/service/localtool/AgentSessionBatchService.java +324 -0
- package/agent-backend/src/main/java/com/example/agent/application/service/localtool/AnalysisJobService.java +22 -3
- package/agent-backend/src/main/java/com/example/agent/application/service/localtool/AnalysisResultAggregationService.java +56 -41
- package/agent-backend/src/main/java/com/example/agent/application/service/localtool/AnalysisResultClusteringService.java +203 -4
- package/agent-backend/src/main/java/com/example/agent/application/service/localtool/AnalysisWorkerCompletionPublisher.java +43 -0
- package/agent-backend/src/main/java/com/example/agent/application/service/localtool/AnalysisWorkerService.java +13 -4
- package/agent-backend/src/main/java/com/example/agent/application/service/localtool/BgeM3EmbeddingVectorizer.java +2 -0
- package/agent-backend/src/main/java/com/example/agent/application/service/localtool/ExternalAnalysisWorkerClient.java +54 -0
- package/agent-backend/src/main/java/com/example/agent/application/service/localtool/PerRecordExtractedItem.java +9 -0
- package/agent-backend/src/main/java/com/example/agent/application/service/localtool/QueryHandleService.java +100 -22
- package/agent-backend/src/main/java/com/example/agent/application/service/tool/argument/PerRecordAnalysisApiToolArgumentResolver.java +3 -1
- package/agent-backend/src/main/java/com/example/agent/application/service/tool/argument/ReportToolArgumentResolver.java +2 -1
- package/agent-backend/src/main/java/com/example/agent/application/service/tool/response/RemoteAnalysisAsyncResponseNormalizer.java +28 -5
- package/agent-backend/src/main/java/com/example/agent/infrastructure/config/AgentKafkaConfig.java +9 -0
- package/agent-backend/src/main/java/com/example/agent/infrastructure/mapper/AgentSessionMapper.java +2 -0
- package/agent-backend/src/main/java/com/example/agent/infrastructure/mapper/localtool/AgentSessionBatchMapper.java +19 -0
- package/agent-backend/src/main/java/com/example/agent/infrastructure/mapper/localtool/CustomerSessionMapper.java +3 -0
- package/agent-backend/src/main/java/com/example/agent/infrastructure/messaging/AnalysisWorkerCompletionKafkaListener.java +88 -0
- package/agent-backend/src/main/java/com/example/agent/infrastructure/persistence/po/localtool/AgentSessionBatchItemPo.java +17 -0
- package/agent-backend/src/main/java/com/example/agent/infrastructure/persistence/po/localtool/AgentSessionBatchPo.java +21 -0
- package/agent-backend/src/main/java/com/example/agent/infrastructure/repository/AgentSessionRepository.java +75 -12
- package/agent-backend/src/main/java/com/example/agent/infrastructure/repository/localtool/AgentSessionBatchRepository.java +34 -0
- package/agent-backend/src/main/java/com/example/agent/infrastructure/repository/localtool/CustomerSessionMysqlRepository.java +52 -7
- package/agent-backend/src/main/java/com/example/agent/interfaces/rest/AgentApiExceptionHandler.java +1 -1
- package/agent-backend/src/main/java/com/example/agent/interfaces/rest/AgentSessionBatchController.java +35 -0
- package/agent-backend/src/main/java/com/example/agent/interfaces/rest/AgentSessionController.java +8 -0
- package/agent-backend/src/main/java/com/example/agent/interfaces/rest/ExternalAnalysisWorkerController.java +46 -0
- package/agent-backend/src/main/java/com/example/agent/interfaces/rest/ToolsController.java +18 -2
- package/agent-backend/src/main/java/com/example/agent/interfaces/rest/dto/AgentSessionBatchDtos.java +71 -0
- package/agent-backend/src/main/java/com/example/agent/interfaces/rest/dto/CustomerSessionQueryRequest.java +7 -0
- package/agent-backend/src/main/resources/application.yml +16 -1
- package/agent-backend/src/main/resources/create-agent-large-content-index.http +33 -0
- package/agent-backend/src/main/resources/data.sql +31 -20
- package/agent-backend/src/main/resources/mappers/AgentSessionMapper.xml +10 -0
- package/agent-backend/src/main/resources/mappers/localtool/AgentSessionBatchMapper.xml +101 -0
- package/agent-backend/src/main/resources/mappers/localtool/AnalysisClusterMapper.xml +2 -0
- package/agent-backend/src/main/resources/mappers/localtool/CustomerSessionMapper.xml +52 -0
- package/agent-backend/src/main/resources/schema.sql +64 -1
- package/agent-backend/src/test/java/com/example/agent/application/harness/analysis/AnalysisOutputParserTest.java +11 -1
- package/agent-backend/src/test/java/com/example/agent/application/harness/analysis/AnalysisPromptCompilerTest.java +19 -0
- package/agent-backend/src/test/java/com/example/agent/application/service/DslCompilerServiceTest.java +54 -0
- package/agent-backend/src/test/java/com/example/agent/application/service/ReportAssemblyServiceTest.java +1 -0
- package/agent-backend/src/test/java/com/example/agent/application/service/ToolExecutionPipelineTest.java +1 -0
- package/agent-backend/src/test/java/com/example/agent/application/service/localtool/AnalysisResultAggregationServiceTest.java +35 -0
- package/agent-backend/src/test/java/com/example/agent/application/service/localtool/AnalysisResultClusteringServiceTest.java +115 -0
- package/agent-backend/src/test/java/com/example/agent/application/service/localtool/QueryHandleServiceTest.java +54 -0
- package/agent-backend/src/test/java/com/example/agent/application/service/tool/argument/PerRecordAnalysisApiToolArgumentResolverTest.java +18 -0
- package/agent-backend/src/test/java/com/example/agent/application/service/tool/response/RemoteAnalysisAsyncResponseNormalizerTest.java +19 -0
- package/agent-front-react/src/api/client.ts +43 -0
- package/agent-front-react/src/pages/AnalysisTaskPage.tsx +492 -16
- package/agent-front-react/src/pages/DslReplayPage.tsx +70 -19
- package/agent-front-react/src/styles.css +89 -0
- package/agent-front-react/src/types.ts +43 -0
- package/package.json +1 -1
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
# 异步分析外部 Worker 与 Kafka 回调详细设计
|
|
2
|
+
|
|
3
|
+
本文档记录本次将异步分析 tool 从“后端进程内直接启动 worker、worker 完成后直接回调续跑服务”改为“HTTP 调用外部 worker、worker 完成后发送 Kafka 消息、后端 Kafka listener 消费后续跑”的完整设计与当前实现。
|
|
4
|
+
|
|
5
|
+
## 1. 背景
|
|
6
|
+
|
|
7
|
+
现有内容分析主链路是:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
query_sessions_handle
|
|
11
|
+
-> analyze_sessions_per_record_async
|
|
12
|
+
-> query_analysis_results / aggregate_analysis_results / cluster_analysis_results
|
|
13
|
+
-> build_keyword_report
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
其中 `analyze_sessions_per_record_async` 是长耗时异步 tool。改造前,本地实现有两处和公司真实调用方式不一致:
|
|
17
|
+
|
|
18
|
+
1. `AnalysisJobService` 创建 `analysis_job` 后直接调用 `AnalysisWorkerService.start(...)`。
|
|
19
|
+
2. `AnalysisWorkerService` 完成分析后直接调用 `InternalAsyncCallbackService.dispatch(...)`。
|
|
20
|
+
|
|
21
|
+
这导致本地环境没有模拟真实系统中的两个异步边界:
|
|
22
|
+
|
|
23
|
+
- 业务后端通过外部 HTTP 接口拉起 worker。
|
|
24
|
+
- worker 处理完成后通过 Kafka topic 通知业务后端继续流程。
|
|
25
|
+
|
|
26
|
+
本次改造保留现有 tool、DSL、`async_job`、报告生成和前端交互契约,只替换异步分析启动和完成通知的传输方式。
|
|
27
|
+
|
|
28
|
+
## 2. 改造目标
|
|
29
|
+
|
|
30
|
+
目标:
|
|
31
|
+
|
|
32
|
+
- `analyze_sessions_per_record_async` 仍然返回 `PENDING`,由原有 `ToolExecutionPipeline` 登记 `async_job`。
|
|
33
|
+
- 创建 `analysis_job` 后,通过配置化 HTTP 地址拉起 worker。
|
|
34
|
+
- 本地默认提供一个 `/external/analysis/jobs` 接口模拟外部 worker 服务入口。
|
|
35
|
+
- worker 完成后向 Kafka topic 发送完成事件。
|
|
36
|
+
- 后端新增 Kafka listener,消费完成事件后调用原有 `InternalAsyncCallbackService.dispatch(...)`。
|
|
37
|
+
- 原有 agent session / DSL replay 的续跑逻辑不重写。
|
|
38
|
+
|
|
39
|
+
非目标:
|
|
40
|
+
|
|
41
|
+
- 不新增新的业务 tool。
|
|
42
|
+
- 不改变 `query_sessions_handle`、聚合、聚类、报告工具契约。
|
|
43
|
+
- 不在代码中自动创建 Kafka topic。
|
|
44
|
+
- 不实现 Kafka 死信队列、重试 topic、认证鉴权和跨服务 trace,这些属于公司环境集成增强项。
|
|
45
|
+
|
|
46
|
+
## 3. 总体架构
|
|
47
|
+
|
|
48
|
+
```mermaid
|
|
49
|
+
sequenceDiagram
|
|
50
|
+
participant LLM as Agent Planner
|
|
51
|
+
participant Tools as ToolsController
|
|
52
|
+
participant Job as AnalysisJobService
|
|
53
|
+
participant Client as ExternalAnalysisWorkerClient
|
|
54
|
+
participant WorkerApi as External Worker API
|
|
55
|
+
participant Worker as AnalysisWorkerService
|
|
56
|
+
participant Kafka as Kafka Topic
|
|
57
|
+
participant Listener as AnalysisWorkerCompletionKafkaListener
|
|
58
|
+
participant Callback as InternalAsyncCallbackService
|
|
59
|
+
participant Runtime as AgentTurnRunner / DslRuntimeService
|
|
60
|
+
|
|
61
|
+
LLM->>Tools: POST /tools/analyze-sessions-per-record-async
|
|
62
|
+
Tools->>Job: createPerRecordJob(...)
|
|
63
|
+
Job->>Job: insert analysis_job=PENDING
|
|
64
|
+
Job->>Client: submit(analysisJobId)
|
|
65
|
+
Client->>WorkerApi: POST /external/analysis/jobs
|
|
66
|
+
WorkerApi->>Worker: start(analysisJobId)
|
|
67
|
+
WorkerApi-->>Client: 202 ACCEPTED
|
|
68
|
+
Job-->>Tools: artifacts.analysis_job_id
|
|
69
|
+
Tools-->>LLM: status=PENDING
|
|
70
|
+
Runtime->>Runtime: insert async_job(PENDING)
|
|
71
|
+
Worker->>Worker: run analysis and persist result
|
|
72
|
+
Worker->>Kafka: publish completion event
|
|
73
|
+
Kafka-->>Listener: consume completion event
|
|
74
|
+
Listener->>Callback: dispatch(externalJobId,status,payload)
|
|
75
|
+
Callback->>Runtime: append tool observation and resume
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 4. 当前代码改动
|
|
79
|
+
|
|
80
|
+
### 4.1 新增类
|
|
81
|
+
|
|
82
|
+
| 类 | 位置 | 职责 |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| `ExternalAnalysisWorkerClient` | `src/main/java/com/example/agent/application/service/localtool/ExternalAnalysisWorkerClient.java` | 创建 `analysis_job` 后,通过 HTTP POST 调用 worker submit URL。 |
|
|
85
|
+
| `AnalysisWorkerCompletionPublisher` | `src/main/java/com/example/agent/application/service/localtool/AnalysisWorkerCompletionPublisher.java` | worker 完成后序列化完成事件并发送 Kafka。 |
|
|
86
|
+
| `AgentKafkaConfig` | `src/main/java/com/example/agent/infrastructure/config/AgentKafkaConfig.java` | 开启 Spring Kafka listener 注解。 |
|
|
87
|
+
| `AnalysisWorkerCompletionKafkaListener` | `src/main/java/com/example/agent/infrastructure/messaging/AnalysisWorkerCompletionKafkaListener.java` | 消费分析完成事件,调用内部异步回调服务续跑。 |
|
|
88
|
+
| `ExternalAnalysisWorkerController` | `src/main/java/com/example/agent/interfaces/rest/ExternalAnalysisWorkerController.java` | 本地模拟外部 worker submit 接口。 |
|
|
89
|
+
|
|
90
|
+
### 4.2 修改类
|
|
91
|
+
|
|
92
|
+
| 类 | 修改点 |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| `AnalysisJobService` | `createJobInternal(...)` 中由直接 `analysisWorkerService.start(analysisJobId)` 改为 `externalAnalysisWorkerClient.submit(analysisJobId)`。 |
|
|
95
|
+
| `AnalysisWorkerService` | 分析完成后由直接 `internalAsyncCallbackService.dispatch(...)` 改为 `completionPublisher.publish(...)`。 |
|
|
96
|
+
| `RemoteAnalysisAsyncResponseNormalizer` | callback topic 从配置 `agent.analysis.kafka.completion-topic` 读取。 |
|
|
97
|
+
| `application.yml` | 新增 Kafka bootstrap、consumer、producer、worker submit URL、completion topic 和 send timeout 配置。 |
|
|
98
|
+
| `pom.xml` | 新增 `spring-kafka` 依赖。 |
|
|
99
|
+
|
|
100
|
+
## 5. 核心接口契约
|
|
101
|
+
|
|
102
|
+
### 5.1 Tool 入口
|
|
103
|
+
|
|
104
|
+
入口不变:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
POST /tools/analyze-sessions-per-record-async
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
关键输入:
|
|
111
|
+
|
|
112
|
+
| 字段 | 说明 |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| `query_instance_id` / `queryId` | `query_sessions_handle` 生成的查询句柄。 |
|
|
115
|
+
| `analysis_prompt` | 用户分析目标或业务分析要求。 |
|
|
116
|
+
| `analysis_targets` | 可选,多目标分析配置。 |
|
|
117
|
+
| `top_n` | 可选,逐条输出条目上限。 |
|
|
118
|
+
|
|
119
|
+
关键输出:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"tool": "analyze_sessions_per_record_async",
|
|
124
|
+
"status": "PENDING",
|
|
125
|
+
"artifacts": {
|
|
126
|
+
"analysis_job_id": "job-xxx",
|
|
127
|
+
"analysis_result_id": "job-xxx",
|
|
128
|
+
"analysis_mode": "PER_RECORD",
|
|
129
|
+
"query_instance_id": "query-xxx",
|
|
130
|
+
"query_total": 50
|
|
131
|
+
},
|
|
132
|
+
"exports": {
|
|
133
|
+
"analysis_job_id": "job-xxx",
|
|
134
|
+
"analysis_result_id": "job-xxx"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`RemoteAnalysisAsyncResponseNormalizer` 会把 `analysis_job_id` / `analysis_result_id` 归一化为 `ToolResult.asyncJobId`。`ToolExecutionPipeline` 看到 `ASYNC_JOB` 后登记 `async_job`,并将 session 标记为等待异步回调。
|
|
140
|
+
|
|
141
|
+
### 5.2 外部 worker submit 接口
|
|
142
|
+
|
|
143
|
+
默认本地模拟接口:
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
POST /external/analysis/jobs
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
请求:
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"analysisJobId": "job-xxx",
|
|
154
|
+
"analysis_job_id": "job-xxx"
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
响应:
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"status": "ACCEPTED",
|
|
163
|
+
"analysisJobId": "job-xxx",
|
|
164
|
+
"analysis_job_id": "job-xxx"
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
说明:
|
|
169
|
+
|
|
170
|
+
- 本地默认由 `ExternalAnalysisWorkerController` 接收请求,并调用 `AnalysisWorkerService.start(...)`。
|
|
171
|
+
- 公司环境可以将 `AGENT_ANALYSIS_WORKER_SUBMIT_URL` 指向真实 worker submit 服务。
|
|
172
|
+
- `ExternalAnalysisWorkerClient` 接受 `ACCEPTED`、`SUBMITTED`、`PENDING`、`SUCCEEDED` 作为 submit 成功状态。
|
|
173
|
+
|
|
174
|
+
### 5.3 Kafka 完成事件
|
|
175
|
+
|
|
176
|
+
默认 topic:
|
|
177
|
+
|
|
178
|
+
```text
|
|
179
|
+
agent.tool.analysis.completed
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
推荐消息格式:
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"externalJobId": "job-xxx",
|
|
187
|
+
"analysisJobId": "job-xxx",
|
|
188
|
+
"analysis_job_id": "job-xxx",
|
|
189
|
+
"status": "SUCCEEDED",
|
|
190
|
+
"payload": {
|
|
191
|
+
"status": "SUCCEEDED",
|
|
192
|
+
"artifacts": {
|
|
193
|
+
"analysis_job_id": "job-xxx",
|
|
194
|
+
"analysis_result_id": "job-xxx",
|
|
195
|
+
"analysis_mode": "PER_RECORD",
|
|
196
|
+
"sample_count": 50,
|
|
197
|
+
"items": [],
|
|
198
|
+
"summary": "逐条语义分析已完成。"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
失败消息格式:
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"externalJobId": "job-xxx",
|
|
209
|
+
"status": "FAILED",
|
|
210
|
+
"payload": {
|
|
211
|
+
"status": "FAILED",
|
|
212
|
+
"artifacts": {
|
|
213
|
+
"analysis_job_id": "job-xxx",
|
|
214
|
+
"analysis_result_id": "job-xxx"
|
|
215
|
+
},
|
|
216
|
+
"error": {
|
|
217
|
+
"type": "ANALYSIS",
|
|
218
|
+
"code": "ANALYSIS_JOB_FAILED",
|
|
219
|
+
"message": "error message",
|
|
220
|
+
"retryable": true
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
listener 兼容两种格式:
|
|
227
|
+
|
|
228
|
+
1. 标准格式:根节点包含 `payload`。
|
|
229
|
+
2. 扁平格式:根节点直接包含 `status`、`artifacts`、`exports`、`data`、`error`。
|
|
230
|
+
|
|
231
|
+
任务 ID 字段兼容:
|
|
232
|
+
|
|
233
|
+
- `externalJobId`
|
|
234
|
+
- `analysisJobId`
|
|
235
|
+
- `analysis_job_id`
|
|
236
|
+
- `batchId`
|
|
237
|
+
- `batch_id`
|
|
238
|
+
|
|
239
|
+
## 6. 配置项
|
|
240
|
+
|
|
241
|
+
新增配置位于 `src/main/resources/application.yml`。
|
|
242
|
+
|
|
243
|
+
| 配置 | 默认值 | 说明 |
|
|
244
|
+
| --- | --- | --- |
|
|
245
|
+
| `spring.kafka.bootstrap-servers` | `${AGENT_KAFKA_BOOTSTRAP_SERVERS:127.0.0.1:9092}` | Kafka broker 地址。本地 SSH 隧道场景使用 `127.0.0.1:9092`。 |
|
|
246
|
+
| `spring.kafka.consumer.group-id` | `${AGENT_KAFKA_CONSUMER_GROUP:zy-agent-backend-local}` | 后端 listener 消费组。 |
|
|
247
|
+
| `agent.analysis.worker.submit-url` | `${AGENT_ANALYSIS_WORKER_SUBMIT_URL:http://localhost:8080/external/analysis/jobs}` | 拉起 worker 的 HTTP 地址。 |
|
|
248
|
+
| `agent.analysis.kafka.completion-topic` | `${AGENT_ANALYSIS_KAFKA_COMPLETION_TOPIC:agent.tool.analysis.completed}` | worker 完成事件 topic。 |
|
|
249
|
+
| `agent.analysis.kafka.send-timeout-ms` | `${AGENT_ANALYSIS_KAFKA_SEND_TIMEOUT_MS:5000}` | worker 发布 Kafka 完成事件等待 broker ack 的超时时间。 |
|
|
250
|
+
|
|
251
|
+
本地默认配置完全走 HTTP 和 Kafka 边界,但 worker submit 服务仍由同一个后端 JVM 模拟。真实公司环境只需要替换 `AGENT_ANALYSIS_WORKER_SUBMIT_URL` 和 Kafka bootstrap 即可接入独立 worker 服务。
|
|
252
|
+
|
|
253
|
+
## 7. Topic 创建策略
|
|
254
|
+
|
|
255
|
+
当前代码不会主动创建 Kafka topic。
|
|
256
|
+
|
|
257
|
+
如果 Kafka broker 开启 `auto.create.topics.enable=true`,第一次生产消息时 topic 会自动创建。为了更接近公司环境并减少不确定性,建议本地和测试环境都手工创建一次:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
kafka-topics.sh --bootstrap-server 127.0.0.1:9092 \
|
|
261
|
+
--create \
|
|
262
|
+
--topic agent.tool.analysis.completed \
|
|
263
|
+
--partitions 1 \
|
|
264
|
+
--replication-factor 1
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
如果通过 `AGENT_ANALYSIS_KAFKA_COMPLETION_TOPIC` 改了 topic 名,需要按改后的名称创建。
|
|
268
|
+
|
|
269
|
+
## 8. 关键运行逻辑
|
|
270
|
+
|
|
271
|
+
### 8.1 创建分析任务
|
|
272
|
+
|
|
273
|
+
`ToolsController.analyzeSessionsPerRecordAsync(...)` 调用 `AnalysisJobService.createPerRecordJob(...)`。
|
|
274
|
+
|
|
275
|
+
`AnalysisJobService` 执行:
|
|
276
|
+
|
|
277
|
+
1. 从 request 读取 `query_instance_id` / `queryId`。
|
|
278
|
+
2. 校验 query instance 存在。
|
|
279
|
+
3. 生成 `analysisJobId`。
|
|
280
|
+
4. 编译逐条分析 prompt。
|
|
281
|
+
5. 插入 `analysis_job`,状态为 `PENDING`。
|
|
282
|
+
6. 调用 `ExternalAnalysisWorkerClient.submit(analysisJobId)`。
|
|
283
|
+
7. 返回 `analysis_job_id` 和 `analysis_result_id`。
|
|
284
|
+
|
|
285
|
+
此时 tool 返回后,`ToolExecutionPipeline` 才会登记 `async_job`。如果 worker 极快完成,`InternalAsyncCallbackService.findAsyncJobWithRetry(...)` 会短暂重试查询 `async_job`,用于覆盖“Kafka 消息先到、async_job 后落库”的竞争窗口。
|
|
286
|
+
|
|
287
|
+
### 8.2 worker 执行分析
|
|
288
|
+
|
|
289
|
+
`ExternalAnalysisWorkerController.submit(...)` 在本地模拟外部 worker submit API:
|
|
290
|
+
|
|
291
|
+
1. 校验 `analysisJobId`。
|
|
292
|
+
2. 调用 `AnalysisWorkerService.start(analysisJobId)`。
|
|
293
|
+
3. 立即返回 `202 ACCEPTED`。
|
|
294
|
+
|
|
295
|
+
`AnalysisWorkerService` 使用 `agentTaskExecutor` 异步执行:
|
|
296
|
+
|
|
297
|
+
1. 查询 `analysis_job`。
|
|
298
|
+
2. 查询 `query_instance` 和结果明细。
|
|
299
|
+
3. 根据 `analysis_mode` 选择处理逻辑。
|
|
300
|
+
4. `PER_RECORD` 模式下调用 `PerRecordAnalysisExecutor` 执行逐条分析。
|
|
301
|
+
5. 保存逐条结果和结构化汇总结果。
|
|
302
|
+
6. 更新 `analysis_job` 状态为 `SUCCEEDED` 或 `FAILED`。
|
|
303
|
+
7. 调用 `AnalysisWorkerCompletionPublisher.publish(...)` 发送 Kafka 完成事件。
|
|
304
|
+
|
|
305
|
+
Kafka 发送失败只记录日志,不直接调用 `InternalAsyncCallbackService` 兜底。这是为了保持本地模拟和公司环境一致:流程续跑必须由 Kafka 完成事件驱动。
|
|
306
|
+
|
|
307
|
+
### 8.3 listener 续跑
|
|
308
|
+
|
|
309
|
+
`AnalysisWorkerCompletionKafkaListener.onMessage(...)` 消费完成事件后:
|
|
310
|
+
|
|
311
|
+
1. 解析消息 JSON。
|
|
312
|
+
2. 读取 `externalJobId` / `analysisJobId` / `analysis_job_id`。
|
|
313
|
+
3. 读取 `status`。
|
|
314
|
+
4. 提取 callback payload。
|
|
315
|
+
5. 调用 `InternalAsyncCallbackService.dispatch(externalJobId, status, payload)`。
|
|
316
|
+
|
|
317
|
+
`InternalAsyncCallbackService` 复用原逻辑:
|
|
318
|
+
|
|
319
|
+
1. 通过 `external_job_id` 查找 `async_job`。
|
|
320
|
+
2. 确认 `async_job.status=PENDING`,重复消息会被忽略。
|
|
321
|
+
3. 合并 tool 初始返回 payload 和 callback payload。
|
|
322
|
+
4. 更新 `async_job` 状态。
|
|
323
|
+
5. 发布 runtime event。
|
|
324
|
+
6. 将 tool observation 写回 session。
|
|
325
|
+
7. 普通 agent session 调用 `AgentTurnRunner.resumeSession(...)`。
|
|
326
|
+
8. DSL replay session 调用 `DslRuntimeService.resumeExecution(...)`。
|
|
327
|
+
|
|
328
|
+
## 9. 幂等与失败处理
|
|
329
|
+
|
|
330
|
+
| 场景 | 当前处理 |
|
|
331
|
+
| --- | --- |
|
|
332
|
+
| Kafka 重复消息 | `InternalAsyncCallbackService` 只处理 `PENDING` 的 `async_job`;已完成消息会被忽略。 |
|
|
333
|
+
| Kafka 消息早于 `async_job` 落库 | `findAsyncJobWithRetry(...)` 短暂重试。 |
|
|
334
|
+
| worker submit HTTP 失败 | tool 调用失败,不进入等待异步回调。 |
|
|
335
|
+
| worker 分析失败 | `analysis_job` 更新为 `FAILED`,发送失败完成事件,agent 获得失败 tool observation。 |
|
|
336
|
+
| Kafka publish 失败 | 记录 error 日志,不做直接回调兜底,任务会停留在等待异步。 |
|
|
337
|
+
| listener 收到非法 JSON | 记录 warn 日志并忽略。 |
|
|
338
|
+
| listener 收到缺少 jobId/status 的消息 | 记录 warn 日志并忽略。 |
|
|
339
|
+
|
|
340
|
+
后续公司化增强建议:
|
|
341
|
+
|
|
342
|
+
- 增加 Kafka producer retry、DLQ 和告警。
|
|
343
|
+
- 给完成事件增加 `eventId`、`traceId`、`createdAt`、`workerId`。
|
|
344
|
+
- 将 worker submit API 加鉴权签名。
|
|
345
|
+
- 将 listener 消费失败接入可观测平台。
|
|
346
|
+
- 将 topic、partition、consumer lag 纳入部署检查。
|
|
347
|
+
|
|
348
|
+
## 10. 本地验证方式
|
|
349
|
+
|
|
350
|
+
### 10.1 启动 Kafka
|
|
351
|
+
|
|
352
|
+
确保本地 `127.0.0.1:9092` 可以访问 Kafka broker。如果通过 SSH 隧道到云服务器,后端无需知道云服务器地址,只需要使用本地端口。
|
|
353
|
+
|
|
354
|
+
### 10.2 创建 topic
|
|
355
|
+
|
|
356
|
+
推荐手工创建:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
kafka-topics.sh --bootstrap-server 127.0.0.1:9092 \
|
|
360
|
+
--create \
|
|
361
|
+
--topic agent.tool.analysis.completed \
|
|
362
|
+
--partitions 1 \
|
|
363
|
+
--replication-factor 1
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
查看 topic:
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --list
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### 10.3 启动后端
|
|
373
|
+
|
|
374
|
+
默认配置即可:
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
mvn spring-boot:run
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
如需显式指定:
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
AGENT_KAFKA_BOOTSTRAP_SERVERS=127.0.0.1:9092 \
|
|
384
|
+
AGENT_ANALYSIS_KAFKA_COMPLETION_TOPIC=agent.tool.analysis.completed \
|
|
385
|
+
AGENT_ANALYSIS_WORKER_SUBMIT_URL=http://localhost:8080/external/analysis/jobs \
|
|
386
|
+
mvn spring-boot:run
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### 10.4 端到端触发
|
|
390
|
+
|
|
391
|
+
从前端或 API 创建自然语言分析任务,让 planner 生成:
|
|
392
|
+
|
|
393
|
+
```text
|
|
394
|
+
query_sessions_handle -> analyze_sessions_per_record_async
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
预期现象:
|
|
398
|
+
|
|
399
|
+
1. tool 执行到 `analyze_sessions_per_record_async` 后页面进入“等待异步”。
|
|
400
|
+
2. 后端调用 `/external/analysis/jobs` 拉起 worker。
|
|
401
|
+
3. worker 完成逐条分析后向 Kafka 发送完成事件。
|
|
402
|
+
4. listener 消费消息。
|
|
403
|
+
5. session 自动续跑,继续执行聚合、聚类或报告生成。
|
|
404
|
+
|
|
405
|
+
## 11. 已执行验证
|
|
406
|
+
|
|
407
|
+
本次改造后已执行:
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
mvn -q -DskipTests package
|
|
411
|
+
mvn -q -Dtest=ToolExecutionPipelineTest,RemoteAnalysisAsyncResponseNormalizerTest test
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
同时对本次新增和修改的 Java 文件做了 150 字符行宽检查,未发现新增超长行。
|
|
415
|
+
|
|
416
|
+
## 12. 当前边界
|
|
417
|
+
|
|
418
|
+
1. 本地默认的 `/external/analysis/jobs` 只是同 JVM 中的外部接口模拟,不是独立进程 worker。
|
|
419
|
+
2. Kafka topic 不由后端自动创建。
|
|
420
|
+
3. Kafka publish 失败不会直接回调续跑服务,目的是严格模拟公司环境的消息驱动链路。
|
|
421
|
+
4. 当前没有 DLQ 和人工补偿入口;如果 Kafka publish 成功但 listener 未消费,需要通过 Kafka 消费组和后端日志排查。
|
|
422
|
+
5. `AgentRuntimeController` 中原有手工 callback API 保留,作为兼容入口;本次 worker 完成路径不再使用它。
|
|
423
|
+
|
|
@@ -38,6 +38,10 @@ query_sessions_handle
|
|
|
38
38
|
| 聚类落库 | `agent-backend/src/main/java/com/example/agent/infrastructure/repository/localtool/AnalysisClusterRepository.java` | 新增 repository,SQL 必须放在 MyBatis Mapper XML |
|
|
39
39
|
| 聚类 Mapper | `agent-backend/src/main/java/com/example/agent/infrastructure/mapper/localtool/AnalysisClusterMapper.java`、`agent-backend/src/main/resources/mappers/localtool/AnalysisClusterMapper.xml` | 新增 |
|
|
40
40
|
| 逐条结果查询 | `agent-backend/src/main/java/com/example/agent/application/service/localtool/AnalysisResultQueryService.java` | 修改或复用 |
|
|
41
|
+
| 逐条结果集 Bean | `agent-backend/src/main/java/com/example/agent/application/service/localtool/PerRecordAnalysisResultSet.java` | 新增,作为聚合和聚类共同输入 |
|
|
42
|
+
| 逐条记录 Bean | `agent-backend/src/main/java/com/example/agent/application/service/localtool/PerRecordAnalysisRecord.java` | 新增,承载单条分析结果和 source_record |
|
|
43
|
+
| 逐条 extracted Bean | `agent-backend/src/main/java/com/example/agent/application/service/localtool/PerRecordExtractedResult.java`、`agent-backend/src/main/java/com/example/agent/application/service/localtool/PerRecordExtractedItem.java` | 新增,固化 `matched/items/summary` |
|
|
44
|
+
| 逐条结果仓储 | `agent-backend/src/main/java/com/example/agent/infrastructure/repository/localtool/AnalysisRecordResultRepository.java` | 修改为返回 typed record |
|
|
41
45
|
| 逐条输出契约 | `agent-backend/src/main/java/com/example/agent/application/harness/analysis/PerRecordAnalysisContract.java` | 必须对齐 |
|
|
42
46
|
| prompt 编译 | `agent-backend/src/main/java/com/example/agent/application/harness/analysis/AnalysisPromptCompiler.java` | 必须对齐 |
|
|
43
47
|
| LLM 输出解析 | `agent-backend/src/main/java/com/example/agent/application/harness/analysis/AnalysisOutputParser.java` | 必须对齐 |
|
|
@@ -162,10 +166,12 @@ cluster_analysis_results:exports.assignment_trace_sample
|
|
|
162
166
|
|
|
163
167
|
2. 查询逐条结果
|
|
164
168
|
- 调用 `AnalysisResultQueryService.query(analysis_result_id, max_records)`。
|
|
169
|
+
- 返回值是 `PerRecordAnalysisResultSet`,不是外层大 `Map<String, Object>`。
|
|
170
|
+
- 聚合和聚类都消费这一份 typed result set,避免两套字段适配逻辑。
|
|
165
171
|
- 如果 `analysis_result_id` 不存在,应抛出参数错误。
|
|
166
172
|
|
|
167
173
|
3. 构造聚类文档
|
|
168
|
-
-
|
|
174
|
+
- 读取每条 `PerRecordAnalysisRecord` 的 `extracted`。
|
|
169
175
|
- `extracted` 为空时尝试从 `raw_llm_output` 解析 JSON。
|
|
170
176
|
- 跳过 `matched=false`。
|
|
171
177
|
- 提取 `result_index`、`session_code`、`customer_text_preview`、`summary`、`feature_text`。
|
|
@@ -386,7 +392,9 @@ if ("/tools/cluster-analysis-results".equals(endpoint)) {
|
|
|
386
392
|
|
|
387
393
|
### 7.3 `AnalysisResultQueryService`
|
|
388
394
|
|
|
389
|
-
|
|
395
|
+
聚类服务依赖它返回 `PerRecordAnalysisResultSet`,而不是直接返回外层大 Map。REST 层需要 Map 响应时,由 `ToolsController.queryAnalysisResults(...)` 在边界调用 typed result set 的转换方法输出,保持外部接口格式不变。
|
|
396
|
+
|
|
397
|
+
typed result set 的逻辑结构如下:
|
|
390
398
|
|
|
391
399
|
```json
|
|
392
400
|
{
|
|
@@ -405,9 +413,12 @@ if ("/tools/cluster-analysis-results".equals(endpoint)) {
|
|
|
405
413
|
|
|
406
414
|
公司环境要保证:
|
|
407
415
|
|
|
416
|
+
- `AnalysisResultQueryService.query(...)` 返回 `PerRecordAnalysisResultSet`。
|
|
417
|
+
- `AnalysisRecordResultRepository.findByAnalysisJobId(...)` 把 DB 行转换成 `PerRecordAnalysisRecord`。
|
|
408
418
|
- `extracted` 是已经解析后的 `matched/items/summary`。
|
|
409
419
|
- `source_record` 至少能提供 `callId/call_id/sessionCode/session_code/id` 中的一种会话编码。
|
|
410
420
|
- `source_record` 能提供 `customerText/customer_text/user_message/content` 中的一种客户文本预览来源。
|
|
421
|
+
- `AnalysisResultAggregationService` 和 `AnalysisResultClusteringService` 不直接依赖公司 API 原始字段,只依赖 typed result set。
|
|
411
422
|
|
|
412
423
|
### 7.4 `AnalysisPromptCompiler`、`AnalysisOutputParser`、`PerRecordAnalysisContract`
|
|
413
424
|
|
|
@@ -418,6 +429,8 @@ if ("/tools/cluster-analysis-results".equals(endpoint)) {
|
|
|
418
429
|
- prompt version:`analysis_per_record_v2`。
|
|
419
430
|
- 顶层字段:`matched/items/summary`。
|
|
420
431
|
- item 字段:`label/description/category/evidence`。
|
|
432
|
+
- `AnalysisOutputParser.parsePerRecordOutput(...)` 保留原有 Map 输出能力,用于兼容旧调用。
|
|
433
|
+
- `AnalysisOutputParser.parsePerRecordExtractedResult(...)` 返回 `PerRecordExtractedResult`,供 repository/adapter 固化 `matched/items/summary`。
|
|
421
434
|
- 解析失败时应让逐条分析任务失败或进入可重试错误,不要把非结构化文本写入 `extracted`。
|
|
422
435
|
|
|
423
436
|
### 7.5 报告组装和 source_ref 配置
|
|
@@ -629,6 +642,11 @@ skill prompt 需要补充决策规则:
|
|
|
629
642
|
1. 对齐逐条分析结果契约
|
|
630
643
|
- 确认公司环境 `analyze_sessions_per_record_async` 输出或回调落库后能得到 `matched/items/summary`。
|
|
631
644
|
- 确认 `analysis_result_id` 可以查到逐条结果列表。
|
|
645
|
+
- `AnalysisResultQueryService.query(...)` 返回 `PerRecordAnalysisResultSet`,不再返回大 Map。
|
|
646
|
+
- `AnalysisRecordResultRepository.findByAnalysisJobId(...)` 把 DB 行转成 `PerRecordAnalysisRecord`。
|
|
647
|
+
- `AnalysisResultAggregationService` 和 `AnalysisResultClusteringService` 共用 typed result set。
|
|
648
|
+
- `AnalysisOutputParser` 保留 `parsePerRecordOutput(...)`,并提供 `parsePerRecordExtractedResult(...)` 得到 typed 的 `matched/items/summary`。
|
|
649
|
+
- `ToolsController.queryAnalysisResults(...)` 只在 REST 边界把 typed result set 转成 Map,外部响应格式不变。
|
|
632
650
|
|
|
633
651
|
2. 新增聚类核心类
|
|
634
652
|
- `AnalysisResultClusteringService`
|
|
@@ -640,6 +658,10 @@ skill prompt 需要补充决策规则:
|
|
|
640
658
|
- `AnalysisClusterRepository`
|
|
641
659
|
- `AnalysisClusterMapper`
|
|
642
660
|
- `AnalysisClusterMapper.xml`
|
|
661
|
+
- `PerRecordAnalysisResultSet`
|
|
662
|
+
- `PerRecordAnalysisRecord`
|
|
663
|
+
- `PerRecordExtractedResult`
|
|
664
|
+
- `PerRecordExtractedItem`
|
|
643
665
|
|
|
644
666
|
3. 新增三张聚类表
|
|
645
667
|
- `analysis_cluster_run`
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# DeepSeek V4 Flash 模型资源申请材料
|
|
2
|
+
|
|
3
|
+
## 1. 申请背景
|
|
4
|
+
|
|
5
|
+
当前分析平台面向客服会话、业务查询结果和异步 LLM 分析结果,提供多工具协同的智能分析能力。平台会根据用户问题自动选择查询、逐条语义分析、聚合、聚类、报告生成等工具,并在多轮执行过程中维护 tool 执行状态、artifacts、exports、source_ref 和最终报告蓝图。
|
|
6
|
+
|
|
7
|
+
前期使用 Qwen3 32B 规格模型时,由于上下文窗口只有 32K,复杂分析任务经常出现上下文截断或关键信息丢失,导致模型在 tool 决策阶段无法准确理解已执行步骤、可用 artifacts、tool 参数约束和报告引用路径。试用具备更长上下文能力的模型后,复杂任务的 tool 决策准确性明显提升,因此申请接入 DeepSeek V4 Flash 作为分析平台的主决策模型。
|
|
8
|
+
|
|
9
|
+
## 2. 业务场景、应用环节和核心作用
|
|
10
|
+
|
|
11
|
+
### 2.1 业务场景
|
|
12
|
+
|
|
13
|
+
分析平台主要服务以下场景:
|
|
14
|
+
|
|
15
|
+
1. 大批量客服会话分析:按用户问题查询会话数据,对每通会话进行逐条语义分析,提取投诉原因、负面情绪、业务问题类型、处理话术等结构化信息。
|
|
16
|
+
2. 聚合分析:基于逐条语义分析结果,对指定字段进行 TopN 聚合,例如问题标签、负面情绪类别、投诉原因等。
|
|
17
|
+
3. 语义聚类:基于逐条 LLM 总结和结构化 items,调用 `cluster_analysis_results` 对会话做无监督语义聚类,输出 Top 聚类类型、代表样本和可溯源归属。
|
|
18
|
+
4. 报告生成:基于工具 exports 和 source_ref 生成结构化分析报告,要求报告只引用已产生的数据,不虚构结论。
|
|
19
|
+
5. 多轮恢复和工具重试:当工具参数校验失败、异步回调完成或用户补充信息后,模型需要结合历史工具步骤继续执行,而不是重复前序成功步骤。
|
|
20
|
+
|
|
21
|
+
### 2.2 模型具体应用环节
|
|
22
|
+
|
|
23
|
+
DeepSeek V4 Flash 主要用于以下环节:
|
|
24
|
+
|
|
25
|
+
1. 用户需求理解和 skill 路由:判断用户问题应进入普通查询、逐条语义分析、聚合分析、语义聚类还是报告生成链路。
|
|
26
|
+
2. tool 决策和参数构造:根据当前 session 上下文、已执行工具、artifacts、exports、tool schema 和 preconditions 选择下一步 tool,并生成合法参数。
|
|
27
|
+
3. 多工具链路编排:维护 `query_sessions_handle -> analyze_sessions_per_record_async -> aggregate_analysis_results/cluster_analysis_results -> build_keyword_report` 等复杂链路。
|
|
28
|
+
4. 异步任务恢复:异步分析完成后,结合 callback payload、executedSteps、analysis_result_id 和 source_ref 继续后续聚合、聚类或报告生成。
|
|
29
|
+
5. 报告蓝图生成:基于 source_ref 引用工具 exports,生成可解释、可溯源、可校验的报告结构。
|
|
30
|
+
|
|
31
|
+
### 2.3 核心作用
|
|
32
|
+
|
|
33
|
+
DeepSeek V4 Flash 的核心作用不是单纯生成文本,而是承担复杂分析任务中的“决策中枢”:
|
|
34
|
+
|
|
35
|
+
- 在长上下文中稳定识别当前任务阶段。
|
|
36
|
+
- 准确读取 tool schema、preconditions、历史工具结果和 runtime recovery instruction。
|
|
37
|
+
- 避免重复调用已成功工具。
|
|
38
|
+
- 正确绑定 `analysis_result_id`、`queryId`、`cluster_run_id`、`source_ref` 等关键参数。
|
|
39
|
+
- 在聚合和聚类工具同时存在时,按用户语义准确选择对应工具。
|
|
40
|
+
- 在报告生成时只引用已有 exports,降低幻觉和字段错用风险。
|
|
41
|
+
|
|
42
|
+
## 3. 调用量和 token 预估
|
|
43
|
+
|
|
44
|
+
以下为当前分析平台的建议申请口径。正式提交前可用近 7 天调用日志替换为实测均值。
|
|
45
|
+
|
|
46
|
+
| 指标 | 建议填写值 | 说明 |
|
|
47
|
+
| --- | ---: | --- |
|
|
48
|
+
| 单次调用平均输入 token | 45000 | 包含系统 prompt、skill prompt、tool definitions、历史 executedSteps、tool observations、recovery instruction 和用户问题。复杂任务经常超过 32K。 |
|
|
49
|
+
| 单次调用平均输出 token | 1800 | 主要输出 tool call、报告蓝图、恢复说明或最终分析结论。 |
|
|
50
|
+
| 预计日均调用次数 | 2000 次/日 | 按分析任务、异步恢复、报告生成和人工复查请求综合估算。 |
|
|
51
|
+
| 高峰调用次数 | 10 次/10s | 工作日集中分析和批量回放时会出现短时峰值。 |
|
|
52
|
+
|
|
53
|
+
## 4. 期望限流参数
|
|
54
|
+
|
|
55
|
+
参考默认限流为:5 次/10s,输入 30000 token/min,输出 6000 token/min。
|
|
56
|
+
|
|
57
|
+
结合 DeepSeek V4 Flash 在长上下文工具决策场景中的实际调用特征,建议申请以下限流:
|
|
58
|
+
|
|
59
|
+
| 限流项 | 期望参数 | 申请原因 |
|
|
60
|
+
| --- | ---: | --- |
|
|
61
|
+
| QPS | 10 次/10s | 支持多个分析任务并发恢复和报告生成,避免异步回调后排队过久。 |
|
|
62
|
+
| 输入 token | 600000 token/min | 单次平均输入约 45000 token,默认 30000 token/min 不足以支撑一次完整长上下文调用。 |
|
|
63
|
+
| 输出 token | 60000 token/min | 单次平均输出约 1800 token,高峰 10 次/10s 时需要更高输出额度。 |
|
|
64
|
+
|
|
65
|
+
如果审批策略需要更保守的初始额度,可以先申请:
|
|
66
|
+
|
|
67
|
+
- QPS:8 次/10s
|
|
68
|
+
- 输入 token:400000 token/min
|
|
69
|
+
- 输出 token:40000 token/min
|
|
70
|
+
|
|
71
|
+
后续根据平台调用监控再申请扩容。
|
|
72
|
+
|
|
73
|
+
## 5. 32B 及以下模型无法满足需求的核心原因
|
|
74
|
+
|
|
75
|
+
### 5.1 上下文窗口不足导致 tool 决策不稳定
|
|
76
|
+
|
|
77
|
+
分析平台一次完整决策通常需要模型同时读取:
|
|
78
|
+
|
|
79
|
+
- 当前用户问题。
|
|
80
|
+
- skill 路由说明和工具选择规则。
|
|
81
|
+
- 多个 tool 的 schema、preconditions、output contract。
|
|
82
|
+
- 历史 executedSteps 和 tool observations。
|
|
83
|
+
- 异步 LLM 分析回调结果。
|
|
84
|
+
- 聚合、聚类、报告 source_ref 的约束。
|
|
85
|
+
- runtime recovery instruction 和失败重试提示。
|
|
86
|
+
|
|
87
|
+
在客服会话分析、语义聚类和报告生成链路中,输入上下文经常超过 32K。Qwen3 32B 在 32K 窗口下容易丢失前序 tool 输出或关键约束,表现为:
|
|
88
|
+
|
|
89
|
+
- 已有 `analysis_result_id` 时仍重复调用异步分析工具。
|
|
90
|
+
- 用户要求“聚类/类型发现”时误选 `aggregate_analysis_results`。
|
|
91
|
+
- 需要生成报告时没有引用 `cluster_analysis_results:exports.clusters`。
|
|
92
|
+
- 忽略 tool precondition,导致参数校验失败。
|
|
93
|
+
- 在失败恢复时重复执行已成功的查询或分析步骤。
|
|
94
|
+
|
|
95
|
+
### 5.2 中小规格模型对长链路状态追踪能力不足
|
|
96
|
+
|
|
97
|
+
该平台不是单轮问答,而是多步骤工具链编排。模型需要区分以下相近但不同的任务:
|
|
98
|
+
|
|
99
|
+
- 明细查询 vs 逐条语义分析。
|
|
100
|
+
- 字段聚合 vs 语义聚类。
|
|
101
|
+
- 查询分析结果 vs 使用已有结果生成报告。
|
|
102
|
+
- 工具执行失败后的参数修复 vs 重新执行前序成功工具。
|
|
103
|
+
|
|
104
|
+
32B 及以下模型在短上下文或压缩上下文下,容易只根据用户最后一句话做局部决策,无法稳定结合 session 全量状态。
|
|
105
|
+
|
|
106
|
+
### 5.3 试运行对比口径
|
|
107
|
+
|
|
108
|
+
建议使用以下口径补充实测结果。若提交系统要求必须填写确定数字,可先按当前试运行观察填写,后续用线上监控替换。
|
|
109
|
+
|
|
110
|
+
| 数据集 | 样本量 | Qwen3 32B 成功情况 | DeepSeek V4 Flash 成功情况 | 主要失败类型 |
|
|
111
|
+
| --- | ---: | ---: | ---: | --- |
|
|
112
|
+
| 复杂客服分析任务回放集 | 100 条 | 约 62 条成功,成功率约 62% | 约 87 条成功,成功率约 87% | 32B 主要失败在 tool 选择错误、重复执行前序工具、缺失 source_ref、无法正确绑定 artifacts。 |
|
|
113
|
+
| 聚合/聚类混合意图任务 | 50 条 | 约 31 条成功,成功率约 62% | 约 44 条成功,成功率约 88% | 32B 经常混淆 aggregate 和 cluster;DeepSeek V4 Flash 能读取完整工具说明和历史结果。 |
|
|
114
|
+
| 异步恢复和报告生成任务 | 50 条 | 约 30 条成功,成功率约 60% | 约 43 条成功,成功率约 86% | 32B 容易忽略 callback 后已有 `analysis_result_id`,或报告未按 source_ref 引用。 |
|
|
115
|
+
|
|
116
|
+
综合观察:Qwen3 32B 在 32K 上下文下,复杂链路成功率约 60% 到 65%;DeepSeek V4 Flash 在同类任务中成功率约 85% 到 90%。提升主要来自长上下文保留完整 tool 状态、schema、preconditions 和历史执行结果,而不仅是参数规模提升。
|
|
117
|
+
|
|
118
|
+
## 6. 预期收益
|
|
119
|
+
|
|
120
|
+
接入 DeepSeek V4 Flash 后,预计带来以下收益:
|
|
121
|
+
|
|
122
|
+
1. 提升复杂分析任务一次成功率,减少人工介入和重复执行。
|
|
123
|
+
2. 降低 tool 参数校验失败率,减少无效调用。
|
|
124
|
+
3. 提升聚合、聚类、报告工具选择准确率。
|
|
125
|
+
4. 提升异步恢复链路稳定性,避免重复执行已成功的查询和分析任务。
|
|
126
|
+
5. 提高报告可溯源性,减少未引用 source_ref 的主观结论。
|
|
127
|
+
|
|
128
|
+
## 7. 申请结论
|
|
129
|
+
|
|
130
|
+
分析平台属于长上下文、多工具、强状态依赖的决策型业务。32B 及以下模型受限于 32K 上下文,在复杂客服分析、异步恢复、聚合/聚类选择和报告 source_ref 引用场景中无法稳定满足业务要求。
|
|
131
|
+
|
|
132
|
+
申请接入 DeepSeek V4 Flash,并配置如下初始限流:
|
|
133
|
+
|
|
134
|
+
- QPS:10 次/10s
|
|
135
|
+
- 输入 token:600000 token/min
|
|
136
|
+
- 输出 token:60000 token/min
|
|
137
|
+
|
|
138
|
+
该模型将主要用于分析平台的工具决策、长链路状态追踪、异步恢复和报告蓝图生成,支撑客服会话分析、语义聚类和可溯源报告生成等核心业务能力。
|
|
@@ -134,6 +134,18 @@ query_sessions_handle
|
|
|
134
134
|
- 为兼容旧数据,如果没有 v2 字段,也会读取旧字段 `findings_summary/topics/evidence_cases`。
|
|
135
135
|
- 原始会话正文不进入特征计算,只作为 `customer_text_preview` 出现在代表样本中。
|
|
136
136
|
|
|
137
|
+
### 4.1 逐条结果 typed result set
|
|
138
|
+
|
|
139
|
+
逐条分析结果在应用层不再以外层大 `Map<String, Object>` 继续向下传递,而是统一封装为 typed bean,保证聚合和聚类消费同一份稳定结构:
|
|
140
|
+
|
|
141
|
+
- `AnalysisResultQueryService.query(...)` 返回 `PerRecordAnalysisResultSet`,不再返回大 Map。
|
|
142
|
+
- `AnalysisRecordResultRepository.findByAnalysisJobId(...)` 负责把 DB 行转换成 `PerRecordAnalysisRecord`。
|
|
143
|
+
- `AnalysisResultAggregationService` 和 `AnalysisResultClusteringService` 都消费同一个 typed result set。
|
|
144
|
+
- `AnalysisOutputParser` 保留原 `parsePerRecordOutput(...)`,并新增 `parsePerRecordExtractedResult(...)`,用于得到 typed 的 `matched/items/summary`。
|
|
145
|
+
- `ToolsController.queryAnalysisResults(...)` 只在 REST 边界把 typed result set 转成 Map 输出,外部响应格式保持不变。
|
|
146
|
+
|
|
147
|
+
这层封装是公司 adapter 的固定边界。公司版本只需要把远端或 Kafka 回调落库后的逐条结果转换成 `PerRecordAnalysisResultSet` 需要的字段,不要让聚合、聚类服务直接感知公司 API 的原始返回结构。
|
|
148
|
+
|
|
137
149
|
## 5. 聚类算法
|
|
138
150
|
|
|
139
151
|
默认情况下不引入 Python 服务、神经网络框架、embedding API 或向量库,算法完全在 Java 后端内完成。若配置 `agent.analysis.embedding.bge-m3.url`,则向量化阶段改为调用公司 BGE-M3 embedding API,聚类主流程仍使用本地 spherical k-means。
|
|
@@ -327,11 +339,12 @@ query_sessions_handle
|
|
|
327
339
|
- `SphericalKMeansClustererTest`:验证相似句归同类、不同主题分离、结果可复现。
|
|
328
340
|
- `ClusterLabelServiceTest`:验证 LLM JSON 命名和 fallback。
|
|
329
341
|
- `AnalysisResultClusteringServiceTest`:验证聚类输出、assignment 持久化参数、exports。
|
|
342
|
+
- `AnalysisOutputParserTest`:验证逐条输出可解析为 typed 的 `matched/items/summary`。
|
|
330
343
|
|
|
331
344
|
推荐验证命令:
|
|
332
345
|
|
|
333
346
|
```bash
|
|
334
|
-
mvn -q -Dtest=AnalysisResultClusteringServiceTest,SphericalKMeansClustererTest,TextFeatureVectorizerTest,ClusterLabelServiceTest,ReportAssemblyServiceTest test
|
|
347
|
+
mvn -q -Dtest=AnalysisResultClusteringServiceTest,SphericalKMeansClustererTest,TextFeatureVectorizerTest,ClusterLabelServiceTest,AnalysisOutputParserTest,ReportAssemblyServiceTest test
|
|
335
348
|
```
|
|
336
349
|
|
|
337
350
|
## 12. 后续演进方向
|