@gt-fe/eap-cli 0.2.17 → 0.2.19

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.
Files changed (41) hide show
  1. package/README.md +304 -304
  2. package/dist/commands/create.d.ts.map +1 -1
  3. package/dist/commands/create.js +31 -2
  4. package/dist/commands/create.js.map +1 -1
  5. package/dist/index.js +31 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/templates/README.md +51 -51
  8. package/dist/templates/agent-api-orchestration/README.md +326 -326
  9. package/dist/templates/agent-api-orchestration/eap.config.json +1 -1
  10. package/dist/templates/agent-api-orchestration/graph.json +35 -35
  11. package/dist/templates/agent-api-orchestration/package.json +2 -2
  12. package/dist/templates/dag-workflow/README.md +102 -102
  13. package/dist/templates/dag-workflow/agent.manifest.json +19 -19
  14. package/dist/templates/dag-workflow/eap.config.json +1 -1
  15. package/dist/templates/dag-workflow/gitignore +51 -51
  16. package/dist/templates/dag-workflow/graph.json +51 -51
  17. package/dist/templates/dag-workflow/skills/README.md +116 -116
  18. package/dist/templates/plan-and-execute/README.md +109 -109
  19. package/dist/templates/plan-and-execute/agent.manifest.json +19 -19
  20. package/dist/templates/plan-and-execute/eap.config.json +1 -1
  21. package/dist/templates/plan-and-execute/eap.lock.yaml +5 -5
  22. package/dist/templates/plan-and-execute/gitignore +51 -51
  23. package/dist/templates/plan-and-execute/graph.json +49 -49
  24. package/dist/templates/plan-and-execute/skills/README.md +120 -120
  25. package/dist/templates/simple-flow/README.md +87 -87
  26. package/dist/templates/simple-flow/agent.manifest.json +19 -19
  27. package/dist/templates/simple-flow/eap.config.json +1 -1
  28. package/dist/templates/simple-flow/gitignore +51 -51
  29. package/dist/templates/simple-flow/graph.json +32 -32
  30. package/dist/templates/simple-flow/package.json +10 -10
  31. package/dist/templates/simple-flow/skills/README.md +113 -113
  32. package/dist/templates/single-agent-with-tools/README.md +138 -138
  33. package/dist/templates/single-agent-with-tools/agent.manifest.json +19 -19
  34. package/dist/templates/single-agent-with-tools/eap.config.json +1 -1
  35. package/dist/templates/single-agent-with-tools/eap.lock.yaml +5 -5
  36. package/dist/templates/single-agent-with-tools/gitignore +51 -51
  37. package/dist/templates/single-agent-with-tools/graph.json +41 -41
  38. package/dist/templates/single-agent-with-tools/skills/README.md +119 -119
  39. package/dist/templates/single-agent-with-tools/tools/http-weather/toolset.manifest.json +73 -73
  40. package/dist/templates/single-agent-with-tools/tools/web-search/toolset.manifest.json +41 -41
  41. package/package.json +2 -2
@@ -1,326 +1,326 @@
1
- # Agent API 服务模板
2
-
3
- 这个模板模拟一个真实的前端智能体页面后端:Node.js 进程内嵌 `@gt-fe/eap-sdk`,启动后直接提供 Agent HTTP API。前端只需要知道 Agent code、服务地址和 SSE 事件协议,就可以完成能力发现、会话管理、历史消息、同步调用和流式对话。
4
-
5
- SDK 仍然是执行核心,模板里的 HTTP 层只是宿主适配器。它不重新实现 Graph、Tool 路由或 Runtime 事件;流式接口原样转发 Runtime `SSEEvent`。整图超时:图上未写 `config.timeout` 时为 20 分钟。
6
-
7
- ## 启动
8
-
9
- ```bash
10
- npm install
11
- npm run dev
12
- ```
13
-
14
- 开发启动会先把当前项目物化到 `.eap/runtime`,然后启动 HTTP 服务。默认地址是 `http://127.0.0.1:3000`。启动日志会打印 Agent 能力描述地址:
15
-
16
- ```text
17
- GET http://127.0.0.1:3000/api/v1/agents/my-agent
18
- ```
19
-
20
- 生产构建使用 `npm run build`。构建会通过 SDK 生成包含项目配置和锁文件的
21
- `dist/.eap/runtime`。正式宿主应从 `dist/embedded-agent.js` 调用
22
- `createEmbeddedAgent({ credentialProvider })`,显式注入短期凭证,再把 Agent 挂到自己的 HTTP
23
- 服务;不要把 `npm start` 的演示入口直接当成生产凭证接入方案。
24
-
25
- 模板里的 Express HTTP 层是可直接运行的服务骨架。它不实现登录、Bearer 鉴权、租户绑定或业务授权;这些职责由使用方在反向代理或自己的路由层集成。
26
- 模板随附的 `eap.config.json` 将 `runtime.storage.profile` 设为 `dev`,由 Runtime 提供本地文件
27
- 存储来演示会话和消息能力;状态位于 `.eap/`(生产入口位于 `dist/.eap/`)。模板不实现数据库。
28
-
29
- ## 项目配置
30
-
31
- Portal 和 Foundation 地址只配置在 `eap.config.json`:
32
-
33
- ```json
34
- {
35
- "platform": {
36
- "portalUrl": "https://developer-portal.example",
37
- "foundationServiceUrl": "https://foundation.example"
38
- }
39
- }
40
- ```
41
-
42
- 开发和生产运行都从 SDK 整理后的 Runtime Agent 目录读取该文件。生产构建不会再由模板脚本
43
- 单独复制配置,因此模板不再设置 `EAP_PLATFORM_ENDPOINT` 或
44
- `FOUNDATION_SERVICE_URL`;`.env` 文件只放 Token 回退、模型密钥和 HTTP 服务配置。
45
- 配置文件会随构建产物发布,请只写平台地址和其他非敏感配置。本地模型写在
46
- `runtime.llm.models.<模型名>`(`baseUrl` + `apiKey`),与另外四个执行模式模板相同。
47
- `apiKey` 使用 `${OPENAI_API_KEY}` 这类环境变量引用,不要写入明文密钥。不要只配顶层
48
- `runtime.llm.baseUrl`,否则可能落到 OpenAI 默认地址。
49
-
50
- ## 前端接口
51
-
52
- 所有接口都支持跨域(由 `EAP_HTTP_CORS_ORIGIN` 控制),JSON 请求使用 `Content-Type: application/json`。Agent code 来自 `agent.manifest.json` 的 `code` 字段。
53
-
54
- | 方法 | 路径 | 用途 |
55
- | --- | --- | --- |
56
- | `GET` | `/health` | 存活检查 |
57
- | `GET` | `/api/v1/agents/:agentCode` | Agent 名称、版本、模型、Tool/Skill、Graph 节点和可用能力 |
58
- | `POST` | `/api/v1/agents/:agentCode/sessions` | 创建会话,返回完整 Session |
59
- | `GET` | `/api/v1/agents/:agentCode/sessions` | 分页查询会话 |
60
- | `GET` | `/api/v1/agents/:agentCode/sessions/:sessionId` | 查询会话详情 |
61
- | `POST` | `/api/v1/agents/:agentCode/sessions/:sessionId/archive` | 归档会话 |
62
- | `POST` | `/api/v1/agents/:agentCode/sessions/:sessionId/restore` | 尝试恢复会话(当前 Runtime 返回 501) |
63
- | `GET` | `/api/v1/agents/:agentCode/sessions/:sessionId/messages` | 分页查询历史消息 |
64
- | `POST` | `/api/v1/agents/:agentCode/sessions/:sessionId/messages` | 发送一条消息并等待完整回复 |
65
- | `POST` | `/api/v1/agents/:agentCode/invoke` | 无路径会话的同步调用 |
66
- | `POST` | `/api/v1/agents/:agentCode/sessions/:sessionId/stream` | 在会话中流式对话 |
67
- | `POST` | `/api/v1/agents/:agentCode/stream` | 无路径会话的流式调用 |
68
-
69
- `restore` 接口保留在 SDK 能力边界中,但当前 Runtime 不支持恢复,会返回 `501 not_supported`,能力描述里的 `sessions.restore` 也会标为 `false`。
70
-
71
- ### 能力发现
72
-
73
- 前端页面加载时先请求能力描述,不必把模型或 Graph 配置硬编码到页面:
74
-
75
- ```ts
76
- const capability = await fetch(
77
- 'http://127.0.0.1:3000/api/v1/agents/my-agent',
78
- ).then((response) => response.json());
79
-
80
- console.log(capability.agent.name, capability.capabilities.stream);
81
- ```
82
-
83
- ### 创建会话并发送消息
84
-
85
- ```ts
86
- const session = await fetch(
87
- 'http://127.0.0.1:3000/api/v1/agents/my-agent/sessions',
88
- {
89
- method: 'POST',
90
- headers: { 'Content-Type': 'application/json' },
91
- body: JSON.stringify({ title: '产品咨询' }),
92
- },
93
- ).then((response) => response.json());
94
-
95
- const result = await fetch(
96
- `http://127.0.0.1:3000/api/v1/agents/my-agent/sessions/${session.id}/messages`,
97
- {
98
- method: 'POST',
99
- headers: { 'Content-Type': 'application/json' },
100
- body: JSON.stringify({ content: '帮我总结本周的产品反馈' }),
101
- },
102
- ).then((response) => response.json());
103
-
104
- console.log(result.data.output, result.data.tokenUsage);
105
- ```
106
-
107
- 同步调用返回 Runtime 的完整响应:`{ code, data, traceId }`。`data` 包含 `taskId`、`sessionId`、`output`、`status`、`tokenUsage` 和 `duration`。
108
-
109
- ### 流式对话
110
-
111
- 流式端点返回标准 `text/event-stream`。每一帧的 `event` 是 Runtime 事件类型,`data` 是事件负载加上 `correlationId`、`taskId` 和 `timestamp`:
112
-
113
- ```ts
114
- const response = await fetch(
115
- `http://127.0.0.1:3000/api/v1/agents/my-agent/sessions/${session.id}/stream`,
116
- {
117
- method: 'POST',
118
- headers: { 'Content-Type': 'application/json' },
119
- body: JSON.stringify({ content: '继续给出三个可执行建议' }),
120
- },
121
- );
122
-
123
- const reader = response.body!.getReader();
124
- const decoder = new TextDecoder();
125
- let buffer = '';
126
- while (true) {
127
- const { done, value } = await reader.read();
128
- if (done) break;
129
- buffer += decoder.decode(value, { stream: true });
130
- const frames = buffer.split('\n\n');
131
- buffer = frames.pop() ?? '';
132
- for (const frame of frames) {
133
- const dataLine = frame.split('\n').find((line) => line.startsWith('data:'));
134
- if (dataLine) console.log(JSON.parse(dataLine.slice(5)));
135
- }
136
- }
137
- ```
138
-
139
- 常见事件包括 `task.started`、`response.chunk`、`response.final`、`tool.called`、`tool.result`、`stats.token_usage` 和 `task.completed`。前端应以 `response.final` 或 `task.completed` 作为本轮结束信号,并处理 `task.error` 与 `error`。
140
-
141
- ## SDK Token 与用户信息示例
142
-
143
- `EAP_SDK_TOKEN` 只用于开发入口的演示回退,构建产物的生产运行不读取它。模板通过
144
- `AgentCredentialProvider` 把凭证交给 Runtime,不请求凭证 sidecar,也不负责刷新 Token。
145
-
146
- 生产宿主显式注入 Provider,显式注入优先于 `EAP_SDK_TOKEN`:
147
-
148
- ```ts
149
- import { createEmbeddedAgent, createAgentApp } from './dist/embedded-agent.js';
150
-
151
- const agent = await createEmbeddedAgent({
152
- credentialProvider: hostCredentialProvider,
153
- });
154
- ```
155
-
156
- 开发模式未传 `credentialProvider` 时,`createEmbeddedAgent()` 才读取 `EAP_SDK_TOKEN`;生产模式
157
- 缺少显式 Provider 会直接失败。宿主可以把返回的 Agent 传给 `createAgentApp(agent)` 或自己的
158
- HTTP 路由。
159
-
160
- 如果宿主已经拿到前端用户 Token,需要查询用户信息时,可以调用独立的 `src/platform-user.ts` 示例(构建后为 `dist/platform-user.js`):
161
-
162
- ```ts
163
- import { resolveTokenUser } from './dist/platform-user.js';
164
-
165
- const user = await resolveTokenUser(externalToken);
166
- console.log({ userId: user.userId, roles: user.roles, permissions: user.permissions });
167
- ```
168
-
169
- `resolveTokenUser()` 内部调用 `createPlatformClient()` 和 `client.resolveTokenUser(token)`,只返回 SDK 的 `PlatformIdentity`。宿主仍需自行处理 401、租户绑定、会话归属和业务权限;模板 HTTP 接口不会自动读取 `Authorization` 请求头。
170
-
171
- ## 资源管理服务模块
172
-
173
- 模板提供 `src/http/services/resource-service.ts`,并从 `src/index.ts` 导出
174
- `resourceService`。它是宿主对 SDK `AgentResourceManager` 的流程封装,包含:
175
-
176
- - `install()`:安装指定版本,写入 desired 状态。
177
- - `update()`:调用 manager 的 `refresh()`,检查并更新远程资源元数据或缓存;要切换到新版本,使用 `install()` 传入新的 `ref`。
178
- - `enable()` / `disable()`:修改资源 desired 状态。
179
- - `apply()`:按 desired 集物化资源;可以传 `reload: false` 只物化不切换 Runtime。
180
- - `reload()`:显式重新创建 Runtime。
181
- - `applyAndReload()`:先以 `reload: false` 应用资源,再根据 `requiresReload` 显式重载 Agent。
182
-
183
- 模板不实现资源状态存储。使用该模块前,宿主必须实现或接入自己的
184
- `AgentResourceManager`,并在 `createEmbeddedAgent()` 中注入:
185
-
186
- ```ts
187
- import { createEmbeddedAgent, createAgentResourceService } from './dist/embedded-agent.js';
188
- import type { AgentResourceManager } from '@gt-fe/eap-sdk';
189
-
190
- const resourceManager: AgentResourceManager = createHostResourceManager({
191
- client,
192
- runtimeRoot,
193
- store: resourceStore,
194
- });
195
-
196
- const agent = await createEmbeddedAgent({
197
- credentialProvider,
198
- resourceManager,
199
- });
200
- const resourceService = createAgentResourceService(agent);
201
- ```
202
-
203
- `createHostResourceManager()`、`resourceStore` 是宿主实现,不是模板或 SDK 导出的函数。模板只
204
- 演示 SDK manager 接口和调用顺序,不负责状态存储实现。未注入 manager 时,调用资源
205
- 方法会返回 `resource_manager_unavailable`(501)。
206
-
207
- 典型的更新和启用流程:
208
-
209
- ```ts
210
- import { resourceService } from './src/index.js';
211
-
212
- const ref = { kind: 'tool', code: 'search-tool', version: '1.2.0' } as const;
213
-
214
- await resourceService.install({ ref, enabled: false }); // 安装但不启用
215
- await resourceService.update({ ref }); // 检查远程资源
216
- await resourceService.enable(ref); // 设置 desired=enabled
217
-
218
- const applied = await resourceService.applyAndReload([ref]);
219
- if (applied.reload?.status === 'failed') {
220
- throw new Error(applied.reload.error?.message ?? 'Agent reload failed');
221
- }
222
-
223
- await resourceService.disable(ref); // 设置 desired=disabled
224
- await resourceService.applyAndReload([ref]);
225
- ```
226
-
227
- `enable()`/`disable()` 只更新 desired 状态;`applyAndReload()` 完成物化并在必要时调用
228
- `agent.reload()`,`effectiveState` 才表示资源已经进入当前 Runtime。生产服务应在请求排空或维护
229
- 窗口中执行重载。构建期固定资源版本时,仍应在 Agent 源项目中使用 `eap install` 或
230
- `installAgentResource()`,再重新生成 standalone Runtime;不要在每个用户请求中修改
231
- `eap.lock.yaml`。
232
-
233
- ## RAG 检索模块
234
-
235
- `graph.json` 在作答节点之前放了一个 `type: "rag"` 节点,路径是 `__start__ → rag → respond → __end__`。这是节点形态示例:未配置 `retrievalSources` 时检索面为空,rag 节点结束后仍由 `respond` 作答。要做多源检索,按下面补 `retrievalSources`(以及会话勾选 `ragSources`)。
236
-
237
- ### 节点配置
238
-
239
- rag 节点的 config 字段均可省略,各有缺省:
240
-
241
- | 字段 | 默认 | 说明 |
242
- | --- | --- | --- |
243
- | `model` | `default` | 决策/降噪判分/空结果重写共用的主力模型 |
244
- | `totalK` | `10` | 融合降噪后最终进上下文的总条数 |
245
- | `maxIterations` | `5` | 本节点检索轮次上限(同轮并发 tool_call 只计 1)。到上限该节点结束,后续图节点继续 |
246
- | `quota` | — | 融合主辅配额:源桶 → 占比(合计 1) |
247
- | `nearDupThreshold` | `0.8` | 近重去重阈值(字符 3-gram Jaccard) |
248
- | `retrievalSources.sources` | — | 检索面数据源目录(会话勾选 `ragSources` 在此基础上过滤) |
249
- | `toolSources` | — | 工具名 → 融合源桶映射(缺省全归 `kb` 桶) |
250
-
251
- `retrievalSources.sources` 里,工具型外部源的 `type` 是开放集合(kb/dw/api/maxkb/milvus/…),约定工具名为 `${type}_search`;`id` 是会话勾选键,`source` 是工具内资源定位,`ref` 在注册工具名与约定不符时显式指定:
252
-
253
- ```json
254
- {
255
- "id": "rag",
256
- "type": "rag",
257
- "config": {
258
- "retrievalSources": {
259
- "sources": [
260
- { "type": "maxkb", "id": "运行分析", "source": "【正式】运营分析报告智能体v1.0" },
261
- { "type": "dw", "id": "城市目标销售月报", "source": "sa_region_city_monthly_target_sales" },
262
- { "type": "local-file", "id": "共享资料", "source": "./docs" }
263
- ]
264
- },
265
- "quota": { "kb": 0.7, "dw": 0.2, "local": 0.1 },
266
- "toolSources": { "dw_search": "dw", "maxkb_search": "kb" }
267
- }
268
- }
269
- ```
270
-
271
- 模板默认只声明一个 `local-file` 源(`source` 指向 `./docs`,请改成你自己的目录);工具型源需要先把对应工具注册进 `agent.manifest.json` 的 `toolRefs`。
272
-
273
- ### 请求级检索范围
274
-
275
- 每次对话可通过请求体带上 `ragSources`(会话勾选的数据源 id)和 `ragScope`(本地目录范围),覆盖图里的默认检索面:
276
-
277
- ```ts
278
- const result = await fetch(
279
- `http://127.0.0.1:3000/api/v1/agents/my-agent/sessions/${session.id}/messages`,
280
- {
281
- method: 'POST',
282
- headers: { 'Content-Type': 'application/json' },
283
- body: JSON.stringify({
284
- content: '帮我总结本周的产品反馈',
285
- ragSources: ['运行分析', '城市目标销售月报'],
286
- ragScope: { dirPaths: ['D:/workpalce/共享资料'] },
287
- }),
288
- },
289
- ).then((response) => response.json());
290
- ```
291
-
292
- - `ragSources`:`undefined` = 目录全部启用(缺省);`[]` = 全不勾(外部源全拒,声明过 local-file 未勾则本地关闭)。
293
- - `ragScope`:只作用本地源;`dirPaths` 按目录整棵圈定(含子目录),`fileIds` 与 `dirPaths` 取并集,`fileIds: []` 显式关本地。
294
-
295
- ## 环境变量
296
-
297
- | 变量 | 默认值 | 说明 |
298
- | --- | --- | --- |
299
- | `EAP_HTTP_HOST` | `127.0.0.1` | HTTP 监听地址 |
300
- | `EAP_HTTP_PORT` | `3000` | HTTP 监听端口 |
301
- | `EAP_HTTP_CORS_ORIGIN` | `*` | 允许的前端 Origin |
302
- | `EAP_HTTP_MAX_BODY_BYTES` | `1048576` | JSON 请求体上限 |
303
- | `EAP_SDK_TOKEN` | — | 仅开发/演示用 Token 回退;生产必须显式注入 `credentialProvider` |
304
-
305
- 生产部署仍应由反向代理负责 TLS、鉴权、限流、访问日志脱敏和更严格的 Origin 白名单。模板不替宿主实现租户绑定、审计存储或业务级授权策略。
306
-
307
- 如果需要把模板路由嵌入已有 Express 应用,请复用 `src/http/routes` 中的路由工厂,在宿主完成鉴权后再挂载;`startAgentHttpServer()` 适合直接启动独立服务。
308
-
309
- ## 作为模块嵌入
310
-
311
- 需要自行接入 Fastify、Express 或其他 HTTP 框架时,可以只使用 SDK 入口:
312
-
313
- ```ts
314
- import { agent, invoke, stream, startAgentHttpServer } from './src/index.js';
315
-
316
- await invoke('你好');
317
- for await (const event of stream('给我一句摘要')) console.log(event);
318
-
319
- // 或把模板适配器挂到一个独立端口,作为最小服务实现。
320
- const service = await startAgentHttpServer(agent, { port: 3001 });
321
- ```
322
-
323
- 生产宿主要显式注入凭证时,使用上面的 `createEmbeddedAgent()` 工厂创建 Agent,再把实例传给
324
- `createAgentApp()` 或 `startAgentHttpServer()`;不要把生产 Token 写入项目文件。
325
-
326
- 会话和消息函数也从 `src/index.ts` 导出,适合在宿主路由中按自己的鉴权和审计规范重新映射;资源安装、启用和 Runtime 切换使用上面的 `resourceService`,图配置更新仍由宿主直接使用 SDK 管理。
1
+ # Agent API 服务模板
2
+
3
+ 这个模板模拟一个真实的前端智能体页面后端:Node.js 进程内嵌 `@gt-fe/eap-sdk`,启动后直接提供 Agent HTTP API。前端只需要知道 Agent code、服务地址和 SSE 事件协议,就可以完成能力发现、会话管理、历史消息、同步调用和流式对话。
4
+
5
+ SDK 仍然是执行核心,模板里的 HTTP 层只是宿主适配器。它不重新实现 Graph、Tool 路由或 Runtime 事件;流式接口原样转发 Runtime `SSEEvent`。整图超时:图上未写 `config.timeout` 时为 20 分钟。
6
+
7
+ ## 启动
8
+
9
+ ```bash
10
+ npm install
11
+ npm run dev
12
+ ```
13
+
14
+ 开发启动会先把当前项目物化到 `.eap/runtime`,然后启动 HTTP 服务。默认地址是 `http://127.0.0.1:3000`。启动日志会打印 Agent 能力描述地址:
15
+
16
+ ```text
17
+ GET http://127.0.0.1:3000/api/v1/agents/my-agent
18
+ ```
19
+
20
+ 生产构建使用 `npm run build`。构建会通过 SDK 生成包含项目配置和锁文件的
21
+ `dist/.eap/runtime`。正式宿主应从 `dist/embedded-agent.js` 调用
22
+ `createEmbeddedAgent({ credentialProvider })`,显式注入短期凭证,再把 Agent 挂到自己的 HTTP
23
+ 服务;不要把 `npm start` 的演示入口直接当成生产凭证接入方案。
24
+
25
+ 模板里的 Express HTTP 层是可直接运行的服务骨架。它不实现登录、Bearer 鉴权、租户绑定或业务授权;这些职责由使用方在反向代理或自己的路由层集成。
26
+ 模板随附的 `eap.config.json` 将 `runtime.storage.profile` 设为 `dev`,由 Runtime 提供本地文件
27
+ 存储来演示会话和消息能力;状态位于 `.eap/`(生产入口位于 `dist/.eap/`)。模板不实现数据库。
28
+
29
+ ## 项目配置
30
+
31
+ Portal 和 Foundation 地址只配置在 `eap.config.json`:
32
+
33
+ ```json
34
+ {
35
+ "platform": {
36
+ "portalUrl": "https://developer-portal.example",
37
+ "foundationServiceUrl": "https://foundation.example"
38
+ }
39
+ }
40
+ ```
41
+
42
+ 开发和生产运行都从 SDK 整理后的 Runtime Agent 目录读取该文件。生产构建不会再由模板脚本
43
+ 单独复制配置,因此模板不再设置 `EAP_PLATFORM_ENDPOINT` 或
44
+ `FOUNDATION_SERVICE_URL`;`.env` 文件只放 Token 回退、模型密钥和 HTTP 服务配置。
45
+ 配置文件会随构建产物发布,请只写平台地址和其他非敏感配置。本地模型写在
46
+ `runtime.llm.models.<模型名>`(`baseUrl` + `apiKey`),与另外四个执行模式模板相同。
47
+ `apiKey` 使用 `${OPENAI_API_KEY}` 这类环境变量引用,不要写入明文密钥。不要只配顶层
48
+ `runtime.llm.baseUrl`,否则可能落到 OpenAI 默认地址。
49
+
50
+ ## 前端接口
51
+
52
+ 所有接口都支持跨域(由 `EAP_HTTP_CORS_ORIGIN` 控制),JSON 请求使用 `Content-Type: application/json`。Agent code 来自 `agent.manifest.json` 的 `code` 字段。
53
+
54
+ | 方法 | 路径 | 用途 |
55
+ | --- | --- | --- |
56
+ | `GET` | `/health` | 存活检查 |
57
+ | `GET` | `/api/v1/agents/:agentCode` | Agent 名称、版本、模型、Tool/Skill、Graph 节点和可用能力 |
58
+ | `POST` | `/api/v1/agents/:agentCode/sessions` | 创建会话,返回完整 Session |
59
+ | `GET` | `/api/v1/agents/:agentCode/sessions` | 分页查询会话 |
60
+ | `GET` | `/api/v1/agents/:agentCode/sessions/:sessionId` | 查询会话详情 |
61
+ | `POST` | `/api/v1/agents/:agentCode/sessions/:sessionId/archive` | 归档会话 |
62
+ | `POST` | `/api/v1/agents/:agentCode/sessions/:sessionId/restore` | 尝试恢复会话(当前 Runtime 返回 501) |
63
+ | `GET` | `/api/v1/agents/:agentCode/sessions/:sessionId/messages` | 分页查询历史消息 |
64
+ | `POST` | `/api/v1/agents/:agentCode/sessions/:sessionId/messages` | 发送一条消息并等待完整回复 |
65
+ | `POST` | `/api/v1/agents/:agentCode/invoke` | 无路径会话的同步调用 |
66
+ | `POST` | `/api/v1/agents/:agentCode/sessions/:sessionId/stream` | 在会话中流式对话 |
67
+ | `POST` | `/api/v1/agents/:agentCode/stream` | 无路径会话的流式调用 |
68
+
69
+ `restore` 接口保留在 SDK 能力边界中,但当前 Runtime 不支持恢复,会返回 `501 not_supported`,能力描述里的 `sessions.restore` 也会标为 `false`。
70
+
71
+ ### 能力发现
72
+
73
+ 前端页面加载时先请求能力描述,不必把模型或 Graph 配置硬编码到页面:
74
+
75
+ ```ts
76
+ const capability = await fetch(
77
+ 'http://127.0.0.1:3000/api/v1/agents/my-agent',
78
+ ).then((response) => response.json());
79
+
80
+ console.log(capability.agent.name, capability.capabilities.stream);
81
+ ```
82
+
83
+ ### 创建会话并发送消息
84
+
85
+ ```ts
86
+ const session = await fetch(
87
+ 'http://127.0.0.1:3000/api/v1/agents/my-agent/sessions',
88
+ {
89
+ method: 'POST',
90
+ headers: { 'Content-Type': 'application/json' },
91
+ body: JSON.stringify({ title: '产品咨询' }),
92
+ },
93
+ ).then((response) => response.json());
94
+
95
+ const result = await fetch(
96
+ `http://127.0.0.1:3000/api/v1/agents/my-agent/sessions/${session.id}/messages`,
97
+ {
98
+ method: 'POST',
99
+ headers: { 'Content-Type': 'application/json' },
100
+ body: JSON.stringify({ content: '帮我总结本周的产品反馈' }),
101
+ },
102
+ ).then((response) => response.json());
103
+
104
+ console.log(result.data.output, result.data.tokenUsage);
105
+ ```
106
+
107
+ 同步调用返回 Runtime 的完整响应:`{ code, data, traceId }`。`data` 包含 `taskId`、`sessionId`、`output`、`status`、`tokenUsage` 和 `duration`。
108
+
109
+ ### 流式对话
110
+
111
+ 流式端点返回标准 `text/event-stream`。每一帧的 `event` 是 Runtime 事件类型,`data` 是事件负载加上 `correlationId`、`taskId` 和 `timestamp`:
112
+
113
+ ```ts
114
+ const response = await fetch(
115
+ `http://127.0.0.1:3000/api/v1/agents/my-agent/sessions/${session.id}/stream`,
116
+ {
117
+ method: 'POST',
118
+ headers: { 'Content-Type': 'application/json' },
119
+ body: JSON.stringify({ content: '继续给出三个可执行建议' }),
120
+ },
121
+ );
122
+
123
+ const reader = response.body!.getReader();
124
+ const decoder = new TextDecoder();
125
+ let buffer = '';
126
+ while (true) {
127
+ const { done, value } = await reader.read();
128
+ if (done) break;
129
+ buffer += decoder.decode(value, { stream: true });
130
+ const frames = buffer.split('\n\n');
131
+ buffer = frames.pop() ?? '';
132
+ for (const frame of frames) {
133
+ const dataLine = frame.split('\n').find((line) => line.startsWith('data:'));
134
+ if (dataLine) console.log(JSON.parse(dataLine.slice(5)));
135
+ }
136
+ }
137
+ ```
138
+
139
+ 常见事件包括 `task.started`、`response.chunk`、`response.final`、`tool.called`、`tool.result`、`stats.token_usage` 和 `task.completed`。前端应以 `response.final` 或 `task.completed` 作为本轮结束信号,并处理 `task.error` 与 `error`。
140
+
141
+ ## SDK Token 与用户信息示例
142
+
143
+ `EAP_SDK_TOKEN` 只用于开发入口的演示回退,构建产物的生产运行不读取它。模板通过
144
+ `AgentCredentialProvider` 把凭证交给 Runtime,不请求凭证 sidecar,也不负责刷新 Token。
145
+
146
+ 生产宿主显式注入 Provider,显式注入优先于 `EAP_SDK_TOKEN`:
147
+
148
+ ```ts
149
+ import { createEmbeddedAgent, createAgentApp } from './dist/embedded-agent.js';
150
+
151
+ const agent = await createEmbeddedAgent({
152
+ credentialProvider: hostCredentialProvider,
153
+ });
154
+ ```
155
+
156
+ 开发模式未传 `credentialProvider` 时,`createEmbeddedAgent()` 才读取 `EAP_SDK_TOKEN`;生产模式
157
+ 缺少显式 Provider 会直接失败。宿主可以把返回的 Agent 传给 `createAgentApp(agent)` 或自己的
158
+ HTTP 路由。
159
+
160
+ 如果宿主已经拿到前端用户 Token,需要查询用户信息时,可以调用独立的 `src/platform-user.ts` 示例(构建后为 `dist/platform-user.js`):
161
+
162
+ ```ts
163
+ import { resolveTokenUser } from './dist/platform-user.js';
164
+
165
+ const user = await resolveTokenUser(externalToken);
166
+ console.log({ userId: user.userId, roles: user.roles, permissions: user.permissions });
167
+ ```
168
+
169
+ `resolveTokenUser()` 内部调用 `createPlatformClient()` 和 `client.resolveTokenUser(token)`,只返回 SDK 的 `PlatformIdentity`。宿主仍需自行处理 401、租户绑定、会话归属和业务权限;模板 HTTP 接口不会自动读取 `Authorization` 请求头。
170
+
171
+ ## 资源管理服务模块
172
+
173
+ 模板提供 `src/http/services/resource-service.ts`,并从 `src/index.ts` 导出
174
+ `resourceService`。它是宿主对 SDK `AgentResourceManager` 的流程封装,包含:
175
+
176
+ - `install()`:安装指定版本,写入 desired 状态。
177
+ - `update()`:调用 manager 的 `refresh()`,检查并更新远程资源元数据或缓存;要切换到新版本,使用 `install()` 传入新的 `ref`。
178
+ - `enable()` / `disable()`:修改资源 desired 状态。
179
+ - `apply()`:按 desired 集物化资源;可以传 `reload: false` 只物化不切换 Runtime。
180
+ - `reload()`:显式重新创建 Runtime。
181
+ - `applyAndReload()`:先以 `reload: false` 应用资源,再根据 `requiresReload` 显式重载 Agent。
182
+
183
+ 模板不实现资源状态存储。使用该模块前,宿主必须实现或接入自己的
184
+ `AgentResourceManager`,并在 `createEmbeddedAgent()` 中注入:
185
+
186
+ ```ts
187
+ import { createEmbeddedAgent, createAgentResourceService } from './dist/embedded-agent.js';
188
+ import type { AgentResourceManager } from '@gt-fe/eap-sdk';
189
+
190
+ const resourceManager: AgentResourceManager = createHostResourceManager({
191
+ client,
192
+ runtimeRoot,
193
+ store: resourceStore,
194
+ });
195
+
196
+ const agent = await createEmbeddedAgent({
197
+ credentialProvider,
198
+ resourceManager,
199
+ });
200
+ const resourceService = createAgentResourceService(agent);
201
+ ```
202
+
203
+ `createHostResourceManager()`、`resourceStore` 是宿主实现,不是模板或 SDK 导出的函数。模板只
204
+ 演示 SDK manager 接口和调用顺序,不负责状态存储实现。未注入 manager 时,调用资源
205
+ 方法会返回 `resource_manager_unavailable`(501)。
206
+
207
+ 典型的更新和启用流程:
208
+
209
+ ```ts
210
+ import { resourceService } from './src/index.js';
211
+
212
+ const ref = { kind: 'tool', code: 'search-tool', version: '1.2.0' } as const;
213
+
214
+ await resourceService.install({ ref, enabled: false }); // 安装但不启用
215
+ await resourceService.update({ ref }); // 检查远程资源
216
+ await resourceService.enable(ref); // 设置 desired=enabled
217
+
218
+ const applied = await resourceService.applyAndReload([ref]);
219
+ if (applied.reload?.status === 'failed') {
220
+ throw new Error(applied.reload.error?.message ?? 'Agent reload failed');
221
+ }
222
+
223
+ await resourceService.disable(ref); // 设置 desired=disabled
224
+ await resourceService.applyAndReload([ref]);
225
+ ```
226
+
227
+ `enable()`/`disable()` 只更新 desired 状态;`applyAndReload()` 完成物化并在必要时调用
228
+ `agent.reload()`,`effectiveState` 才表示资源已经进入当前 Runtime。生产服务应在请求排空或维护
229
+ 窗口中执行重载。构建期固定资源版本时,仍应在 Agent 源项目中使用 `eap install` 或
230
+ `installAgentResource()`,再重新生成 standalone Runtime;不要在每个用户请求中修改
231
+ `eap.lock.yaml`。
232
+
233
+ ## RAG 检索模块
234
+
235
+ `graph.json` 在作答节点之前放了一个 `type: "rag"` 节点,路径是 `__start__ → rag → respond → __end__`。这是节点形态示例:未配置 `retrievalSources` 时检索面为空,rag 节点结束后仍由 `respond` 作答。要做多源检索,按下面补 `retrievalSources`(以及会话勾选 `ragSources`)。
236
+
237
+ ### 节点配置
238
+
239
+ rag 节点的 config 字段均可省略,各有缺省:
240
+
241
+ | 字段 | 默认 | 说明 |
242
+ | --- | --- | --- |
243
+ | `model` | `default` | 决策/降噪判分/空结果重写共用的主力模型 |
244
+ | `totalK` | `10` | 融合降噪后最终进上下文的总条数 |
245
+ | `maxIterations` | `5` | 本节点检索轮次上限(同轮并发 tool_call 只计 1)。到上限该节点结束,后续图节点继续 |
246
+ | `quota` | — | 融合主辅配额:源桶 → 占比(合计 1) |
247
+ | `nearDupThreshold` | `0.8` | 近重去重阈值(字符 3-gram Jaccard) |
248
+ | `retrievalSources.sources` | — | 检索面数据源目录(会话勾选 `ragSources` 在此基础上过滤) |
249
+ | `toolSources` | — | 工具名 → 融合源桶映射(缺省全归 `kb` 桶) |
250
+
251
+ `retrievalSources.sources` 里,工具型外部源的 `type` 是开放集合(kb/dw/api/maxkb/milvus/…),约定工具名为 `${type}_search`;`id` 是会话勾选键,`source` 是工具内资源定位,`ref` 在注册工具名与约定不符时显式指定:
252
+
253
+ ```json
254
+ {
255
+ "id": "rag",
256
+ "type": "rag",
257
+ "config": {
258
+ "retrievalSources": {
259
+ "sources": [
260
+ { "type": "maxkb", "id": "运行分析", "source": "【正式】运营分析报告智能体v1.0" },
261
+ { "type": "dw", "id": "城市目标销售月报", "source": "sa_region_city_monthly_target_sales" },
262
+ { "type": "local-file", "id": "共享资料", "source": "./docs" }
263
+ ]
264
+ },
265
+ "quota": { "kb": 0.7, "dw": 0.2, "local": 0.1 },
266
+ "toolSources": { "dw_search": "dw", "maxkb_search": "kb" }
267
+ }
268
+ }
269
+ ```
270
+
271
+ 模板默认只声明一个 `local-file` 源(`source` 指向 `./docs`,请改成你自己的目录);工具型源需要先把对应工具注册进 `agent.manifest.json` 的 `toolRefs`。
272
+
273
+ ### 请求级检索范围
274
+
275
+ 每次对话可通过请求体带上 `ragSources`(会话勾选的数据源 id)和 `ragScope`(本地目录范围),覆盖图里的默认检索面:
276
+
277
+ ```ts
278
+ const result = await fetch(
279
+ `http://127.0.0.1:3000/api/v1/agents/my-agent/sessions/${session.id}/messages`,
280
+ {
281
+ method: 'POST',
282
+ headers: { 'Content-Type': 'application/json' },
283
+ body: JSON.stringify({
284
+ content: '帮我总结本周的产品反馈',
285
+ ragSources: ['运行分析', '城市目标销售月报'],
286
+ ragScope: { dirPaths: ['D:/workpalce/共享资料'] },
287
+ }),
288
+ },
289
+ ).then((response) => response.json());
290
+ ```
291
+
292
+ - `ragSources`:`undefined` = 目录全部启用(缺省);`[]` = 全不勾(外部源全拒,声明过 local-file 未勾则本地关闭)。
293
+ - `ragScope`:只作用本地源;`dirPaths` 按目录整棵圈定(含子目录),`fileIds` 与 `dirPaths` 取并集,`fileIds: []` 显式关本地。
294
+
295
+ ## 环境变量
296
+
297
+ | 变量 | 默认值 | 说明 |
298
+ | --- | --- | --- |
299
+ | `EAP_HTTP_HOST` | `127.0.0.1` | HTTP 监听地址 |
300
+ | `EAP_HTTP_PORT` | `3000` | HTTP 监听端口 |
301
+ | `EAP_HTTP_CORS_ORIGIN` | `*` | 允许的前端 Origin |
302
+ | `EAP_HTTP_MAX_BODY_BYTES` | `1048576` | JSON 请求体上限 |
303
+ | `EAP_SDK_TOKEN` | — | 仅开发/演示用 Token 回退;生产必须显式注入 `credentialProvider` |
304
+
305
+ 生产部署仍应由反向代理负责 TLS、鉴权、限流、访问日志脱敏和更严格的 Origin 白名单。模板不替宿主实现租户绑定、审计存储或业务级授权策略。
306
+
307
+ 如果需要把模板路由嵌入已有 Express 应用,请复用 `src/http/routes` 中的路由工厂,在宿主完成鉴权后再挂载;`startAgentHttpServer()` 适合直接启动独立服务。
308
+
309
+ ## 作为模块嵌入
310
+
311
+ 需要自行接入 Fastify、Express 或其他 HTTP 框架时,可以只使用 SDK 入口:
312
+
313
+ ```ts
314
+ import { agent, invoke, stream, startAgentHttpServer } from './src/index.js';
315
+
316
+ await invoke('你好');
317
+ for await (const event of stream('给我一句摘要')) console.log(event);
318
+
319
+ // 或把模板适配器挂到一个独立端口,作为最小服务实现。
320
+ const service = await startAgentHttpServer(agent, { port: 3001 });
321
+ ```
322
+
323
+ 生产宿主要显式注入凭证时,使用上面的 `createEmbeddedAgent()` 工厂创建 Agent,再把实例传给
324
+ `createAgentApp()` 或 `startAgentHttpServer()`;不要把生产 Token 写入项目文件。
325
+
326
+ 会话和消息函数也从 `src/index.ts` 导出,适合在宿主路由中按自己的鉴权和审计规范重新映射;资源安装、启用和 Runtime 切换使用上面的 `resourceService`,图配置更新仍由宿主直接使用 SDK 管理。