本文引用的文件
本文档面向调用方和前端开发者,系统化说明后端 Agent 对话、RAG、模型管理、专家目录、工具包、能力清单、Artifacts、Python 文件服务以及 Insight 研判相关接口的 URL、方法、参数、响应格式、错误语义与排查方法。重点覆盖 /chat/stream SSE 接口:连接建立方式、x-token 认证、请求体字段、事件类型、增量数据、结束帧、心跳保活、中断重连策略,并给出 curl 与前端 fetch/SSE 示例。
后端主要 API 集中在 ai-server 模块的 com.zsjz.ai.module.agent 包下,按控制器分层:
graph TB
subgraph "Agent 控制器"
Chat["AgentChatController<br/>/chat"]
Model["AgentModelController<br/>/models"]
Expert["AgentExpertController<br/>/agents"]
ToolPack["AgentToolPackController<br/>/tool-packs"]
Capability["AgentCapabilityController<br/>/capability"]
Artifact["AgentArtifactController<br/>/agent/artifact"]
PyFiles["AgentPythonFileController<br/>/py/files"]
InsightCtrl["InsightController<br/>/insight"]
end
subgraph "核心服务"
ChatSvc["AgentChatServiceImpl"]
Llm["LlmService"]
Rag["RagSchemaService"]
Python["PythonExecutor"]
end
Chat --> ChatSvc
InsightCtrl --> ChatSvc
ChatSvc --> Llm
ChatSvc --> Rag
ChatSvc --> Python
图示来源
章节来源
章节来源
Agent 对话链路以 AgentScope 的 HarnessAgent 为核心,结合工具、意图识别、Followup 中间件、RAG 与 Python 执行器完成多轮 ReAct 推理;LLM 服务则用于无需 Agent 链路的纯文本任务。
sequenceDiagram
participant Client as "客户端"
participant Controller as "AgentChatController"
participant Service as "AgentChatServiceImpl"
participant Agent as "HarnessAgent"
participant Tools as "工具集合"
participant LLM as "LLM Provider"
participant DB as "持久层"
Client->>Controller: POST /chat/stream
Controller->>Service: stream(ChatRequestDTO)
Service->>Service: 校验 sessionKey/案件/附件
Service->>Agent: streamEvents([USER], RuntimeContext)
Agent->>Tools: 工具调用
Tools->>DB: 查库/SQL/图谱/RAG
Tools-->>Agent: 工具结果
Agent->>LLM: 生成回复
LLM-->>Agent: token/思考/工具指令
Agent-->>Service: event 流
Service-->>Client: SSE: tool_call/token/thinking/done/error
Service->>DB: 异步落库 assistant 消息
图示来源
Result<T>,包含 code、message、data。/chat/** 与 /insight/** 采用裸响应(不走 Result<T>),便于前端复用原始流或对象。Result,并映射 HTTP 状态码。章节来源
/auth/login,返回包含 token 的 LoginVO。x-token 传递。body.code=401,由前端触发重新登录逻辑。| 接口 | 方法 | 说明 |
|---|---|---|
/auth/login |
POST |
用户名+密码登录,返回 token |
/auth/logout |
POST |
登出并释放数据源 |
/auth/info |
GET |
当前用户信息 |
/auth/changePwd |
POST |
修改密码 |
/auth/verifyPwd |
POST |
验证密码 |
章节来源
/models| 接口 | 方法 | 参数 | 响应 | 说明 |
|---|---|---|---|---|
/models/providers |
GET |
无 | Result<List<ModelProviderVO>> |
模型厂商列表 |
/models |
GET |
无 | Result<List<ModelVO>> |
启用模型列表 |
/models |
POST |
CreateModelDTO |
Result<ModelVO> |
创建模型 |
/models/{id} |
PUT |
path:id, body:UpdateModelDTO |
Result<Void> |
更新模型 |
/models/{id} |
GET |
path:id |
Result<Void> |
删除模型 |
/models/{id}/default |
GET |
path:id |
Result<Void> |
设置默认模型 |
/models/{id}/test |
GET |
path:id |
Result<ModelTestResultVO> |
连通性测试,失败也返回 200 + success=false |
章节来源
/agents| 接口 | 方法 | 参数 | 响应 | 说明 |
|---|---|---|---|---|
/agents |
GET |
无 | Result<List<ExpertVO>> |
内置 + 当前用户自建专家 |
/agents |
POST |
SaveExpertDTO |
Result<ExpertVO> |
新建自建专家 |
/agents/{id} |
PUT |
path:id, body:SaveExpertDTO |
Result<ExpertVO> |
编辑专家(内置拒绝) |
/agents/{id} |
DELETE |
path:id |
Result<Void> |
删除专家(内置拒绝) |
章节来源
/tool-packs| 接口 | 方法 | 参数 | 响应 | 说明 |
|---|---|---|---|---|
/tool-packs |
GET |
无 | Result<List<ToolPackVO>> |
当前用户工具包列表 |
/tool-packs |
POST |
SaveToolPackDTO |
Result<ToolPackVO> |
新建工具包 |
/tool-packs/{id} |
PUT |
path:id, body:SaveToolPackDTO |
Result<ToolPackVO> |
编辑工具包 |
/tool-packs/{id} |
DELETE |
path:id |
Result<Void> |
删除工具包,并移除方案绑定 |
章节来源
/capability| 接口 | 方法 | 参数 | 响应 | 说明 |
|---|---|---|---|---|
/capability/tools |
GET |
无 | Result<List<AgentToolVO>> |
运行时工具清单 |
/capability/skills |
GET |
可选 caseId |
Result<List<SkillVO>> |
技能清单 |
/capability/skills/{name} |
GET |
path:name, 可选 caseId |
Result<SkillDetailVO> |
技能详情 |
/capability/skills/{name}/file |
GET |
path:name, query:path, 可选 caseId |
Result<SkillFileContentVO> |
技能内单个文件内容 |
/capability/skills |
POST |
SaveSkillDTO |
Result<SkillVO> |
新建技能 |
/capability/skills/{name} |
PUT |
path:name, body:SaveSkillDTO |
Result<SkillVO> |
修改技能 |
/capability/skills/install |
POST |
file:file, query:overwrite |
Result<SkillVO> |
上传 zip 安装技能 |
/capability/skills/{name}/delete |
GET |
path:name |
Result<Void> |
卸载技能 |
/capability/skills/{name}/toggle |
GET |
path:name, query:enabled |
Result<Void> |
启停技能 |
章节来源
/agent/artifact| 接口 | 方法 | 参数 | 响应 | 说明 |
|---|---|---|---|---|
/agent/artifact/preview |
GET |
query:id, 可选 limit |
裸对象 {columns, rows} |
预览 CSV 前 N 行 |
/agent/artifact/download |
GET |
query:id |
二进制 CSV 流 | 下载完整产物 |
注意事项:
x-token,不进入 sa-token 白名单。CaseContextHolder 保证,跨案件访问返回 404/400。fetch 携带 x-token 后保存 blob,不能直接用 <a href>。章节来源
/py/files| 接口 | 方法 | 参数 | 响应 | 说明 |
|---|---|---|---|---|
/py/files/{workspaceId}/{execId}/{filename} |
GET |
path:workspaceId,execId,filename |
图片二进制流 | 读取 Python 沙箱产出图片 |
约束:
workspaceId 必须等于当前打开案件 ID,否则返回 403。workspace/{workspaceId}/py-out/,否则返回 400。章节来源
/insight| 接口 | 方法 | 参数 | 响应 | 说明 |
|---|---|---|---|---|
/insight/sessions |
POST |
InsightCreateDTO |
InsightSessionVO |
创建研判会话 |
/insight/stream |
POST |
InsightStreamDTO |
SSE 流 | 流式分析 |
/insight/sessions |
GET |
query:caseId, 可选 scope |
List<InsightSessionVO> |
历史会话 |
/insight/sessions/{id} |
GET |
path:id, query:caseId |
InsightSessionVO |
会话详情 |
/insight/sessions/{id}/messages |
GET |
path:id, query:caseId, 分页参数 |
IPage<InsightMessageVO> |
分页消息 |
/insight/messages/{messageId}/trace |
GET |
path:messageId, query:caseId |
InsightMessageTraceVO |
单条消息执行明细 |
/insight/sessions/{id}/delete |
GET |
path:id, query:caseId |
裸空响应 | 删除会话 |
/insight/clues |
GET |
query:caseId, 可选 level,limit |
List<InsightClueVO> |
线索聚合 |
注意:
caseId 双条件校验。Result<T>。章节来源
POST /chat/streamapplication/jsontext/event-streamx-tokenFlux<ServerSentEvent<String>>,每个事件为一个 event: + data: JSON 帧curl 示例:
curl -N -X POST http://host/chat/stream \
-H "Content-Type: application/json" \
-H "x-token: YOUR_TOKEN" \
-d '{"message":"分析本案件资金流向","sessionKey":"SESSION_ID"}'
前端 fetch + ReadableStream 示例:
const res = await fetch("/chat/stream", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-token": token,
},
body: JSON.stringify({ message, sessionKey }),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value, { stream: true });
// 按行解析 data: 和 event: 字段
}
章节来源
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
message |
String |
是 | 用户提问文本 |
sessionKey |
String |
是 | 当前会话的业务标识,服务端解析为 sessionId |
agentId |
Long |
否 | 可覆盖会话绑定的专家,内部用于构建 Agent |
modelId |
Long |
否 | 动态切换模型;与会话绑定模型不同时回写会话 |
attachmentIds |
List<Long> |
否 | 附件 ID,发送前通过 /chat/attachments 上传 |
章节来源
| 事件名 | 含义 | 关键字段 | 发生时机 |
|---|---|---|---|
tool_call |
工具调用开始 | type=tool_call、toolCallId、toolName |
AgentScope 发起工具调用 |
tool_input |
工具输入累积完成 | type=tool_input、toolCallId、toolInput |
工具参数收集完毕 |
tool_result |
工具结果累积完成 | type=tool_result、toolCallId、toolName、toolResult |
工具执行结束 |
token |
文本增量 | type=token、data |
模型逐字输出 |
thinking |
模型思考过程 | type=thinking、phase=start/delta/end、data |
开启、增量、结束 |
intent |
意图识别结果 | 由中间件注入 | 意图识别完成 |
suggestions |
下一步建议 | 由 Followup 中间件注入 | 对话结束后追加 |
graph_data |
全量图谱数据 | type=graph_data、toolCallId、graph |
调用 get_case_graph 工具后补发 |
done |
本轮对话结束 | type=done、sessionKey |
Agent 结束事件 |
error |
运行期错误 | type=error、error |
参数/会话/系统异常 |
heartbeat |
心跳保活 | type=heartbeat、ts |
每 10 秒一次 |
增量字段说明:
token.data 是增量片段,不是完整文本,需要前端拼接。tool_input.toolInput 和 tool_result.toolResult 在对应工具结束时一次性返回完整值。thinking.data 同样增量拼接,phase=start 表示一段新思考开始,phase=end 表示该段结束。done.sessionKey 可用于前端刷新后恢复会话上下文。章节来源
done:正常结束,附带 sessionKey。error:参数为空、缺少 sessionKey、会话不存在、会话不属于当前案件、模型未配置等错误都会转为 error 帧。章节来源
服务端同时发射两条 Flux:
heartbeat 事件。心跳帧不参与正文、思考、工具结果累积,仅用于让前端区分“模型仍在处理”和“连接已断”。
章节来源
error 帧:“该会话正在回复中,请等待当前回复完成后再发送”。AgentResultEvent 尚未到达,服务端会把已输出的部分文本、工具事件和思考过程异步落库,标记为中断。done。done,可用相同 sessionKey 和上一次 message 重试;如果已经收到 done,不应重复发送同一条消息。章节来源
flowchart TD
Start(["POST /chat/stream"]) --> Validate["校验 message 与 sessionKey"]
Validate --> Valid{"参数有效?"}
Valid -->|否| ErrorFrame["发送 error 帧"]
Valid -->|是| AcquireLock["获取会话级流锁"]
AcquireLock --> Locked{"锁可用?"}
Locked -->|否| BusyError["发送 '正在回复中' 错误帧"]
Locked -->|是| BuildAgent["加载会话/模型/Agent/上下文"]
BuildAgent --> StreamEvents["订阅 AgentScope 事件流"]
StreamEvents --> ToolCall["tool_call/tool_input/tool_result"]
StreamEvents --> Thinking["thinking start/delta/end"]
StreamEvents --> Token["token 增量"]
StreamEvents --> Done["done + sessionKey"]
StreamEvents --> Heartbeat["每 10 秒 heartbeat"]
Done --> End(["结束"])
Heartbeat --> End
ErrorFrame --> End
BusyError --> End
图示来源
| 组件 | 职责 | 在对话中的作用 |
|---|---|---|
| AgentScope | 多轮 Agent 编排、工具调度、记忆、技能过滤、Transcript | 承载 ReAct 循环,驱动工具调用与模型交互 |
| LLM Provider | 实际大模型连接层 | 被 AgentScope 与 LlmService 调用,提供文本生成 |
| RAG Schema | 表结构元数据向量化检索 | 通过 search_table_schema 工具帮助模型理解数据库结构 |
| Python 执行器 | 子进程执行 Python 代码 | 提供数据分析、绘图、DuckDB 查询能力,输出图片与结果 |
graph LR
Chat["AgentChatController"] --> Svc["AgentChatServiceImpl"]
Svc --> AgentScope["HarnessAgent"]
AgentScope --> Tools["工具集合"]
Tools --> Rag["RagSchemaService"]
Tools --> Python["PythonExecutor"]
AgentScope --> LLM["LLM Provider"]
图示来源
章节来源
Semaphore 非阻塞抢占,避免记忆污染。SUCCESS 与 IDLE,空索引抛可操作错误而非静默空结果。章节来源
现象:
/models/{id}/test 返回 success=false。error 帧。排查步骤:
/models 确认存在启用模型。/models/{id}/test 查看连通性失败原因。LlmService 日志中的模型名称与错误信息。可能错误:
章节来源
现象:
search_table_schema 返回空。排查步骤:
/chat/reindexWorkspace 重建索引。SUCCESS。可能错误:
章节来源
现象:
done。排查步骤:
heartbeat;若无心跳,可能是连接层问题。error,根据 error.error 字段定位。done,可视为中断,使用相同 sessionKey 重连重试。重连建议:
error、连接关闭、超时。done 时重试;收到 done 后不再重复发送。章节来源
现象:
body.code=401。/agent/artifact 下载失败。排查步骤:
x-token 是否携带。/auth/info 确认当前登录态。章节来源
现象:
排查步骤:
章节来源
Agent 对话接口以 /chat/stream 为核心,基于 AgentScope 的流式事件模型对外暴露 tool_call、token、thinking、done、error、heartbeat 等事件;配套提供模型、专家、工具包、能力、Artifacts、Python 文件与 Insight 研判接口。调用方应正确携带 x-token,按事件类型增量渲染,实现心跳检测、中断重连与错误降级,并在 LLM 不可用、RAG 无结果、SSE 断流、Token 过期等场景下依据本文排查路径快速定位问题。