feat(agent): use deepseek flash for complete SCADA investigations
This commit is contained in:
@@ -32,7 +32,8 @@ metadata:
|
|||||||
先限定对象、来源、字段和时间窗,再按当前问题所需证据扩大范围。
|
先限定对象、来源、字段和时间窗,再按当前问题所需证据扩大范围。
|
||||||
|
|
||||||
- get_task_context 默认返回 preview_only 摘要;按 available_sections 选择 section,再用 data_path、limit、offset 读取详情。指定报告保留 report_revision。
|
- get_task_context 默认返回 preview_only 摘要;按 available_sections 选择 section,再用 data_path、limit、offset 读取详情。指定报告保留 report_revision。
|
||||||
- 首次集合查询用 limit=20、offset=0,优先少量对象和短窗。has_more=true 且后页必要时,offset 按本页 limit 推进;超过10000时缩小对象或时间窗。
|
- 普通集合按schema默认值分页。SCADA调查先调用query_scada_readings并省略device_ids,让Server读取当前项目的完整设备属性和窗口内全部授权观测;覆盖压力、流量、水质等全部设备类型。显式device_ids仅用于全量读取后的局部复核。单独读取设备目录时,按每页100个持续翻页到has_more=false,不能自行以代表设备或已标记设备缩小调查输入。
|
||||||
|
- 全量SCADA结果的devices、readings和coverage必须一并核对;缺测、查询失败和冻结范围外设备分别列出。query_complete表示查询已读完,不表示所有设备都有有效观测。当前窗和正常对照保持任务给定的时空边界,最终SCADA报告引用本轮全量读取的evidence_id。
|
||||||
- 已有证据按 evidence_read 调用 get_task_evidence;嵌套集合和长文本选择更深 data_path 和小页。RESULT_TOO_LARGE 时调整页或路径,继续遵守数据源整份校验条件。
|
- 已有证据按 evidence_read 调用 get_task_evidence;嵌套集合和长文本选择更深 data_path 和小页。RESULT_TOO_LARGE 时调整页或路径,继续遵守数据源整份校验条件。
|
||||||
- 用户指定的窗口保持原范围;按真实观测说明覆盖与缺口。模型值另列为模型结果。
|
- 用户指定的窗口保持原范围;按真实观测说明覆盖与缺口。模型值另列为模型结果。
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,9 @@ synthetic、回放等来源标记本身不构成停止条件。核对工具返
|
|||||||
|
|
||||||
同阶段按对象集合发布;数据或方案变化时更新,同内容复用已有请求。人工提问或计算等待前,先发布已取得的关键步骤展示。map_targets和报告链接提供手动入口,过程展示使用上述发布工具。
|
同阶段按对象集合发布;数据或方案变化时更新,同内容复用已有请求。人工提问或计算等待前,先发布已取得的关键步骤展示。map_targets和报告链接提供手动入口,过程展示使用上述发布工具。
|
||||||
|
|
||||||
SCADA地图定位、等级标记、表格与图表采用同一目标设备集合。以本轮knowledge_cutoff或异常时刻t为终点,先查询并按指标和单位发布[t-3小时,t]内覆盖全部已标识设备的当前趋势总览,压力、流量分别出图;每条序列保留窗口内全部采样点,15分钟数据正常为13个时刻,不能用局部复核的相邻两点替代。系列名包含device_id;超过8组时分图,各组使用不同且稳定的chart_id。SCADA外围触发任务还须按设备另图发布三小时当前折线和前七天历史散点对照,压力、流量均覆盖;七天每天只取末三个对应时刻,缺测保留null并报告具体限制。图表数据视图沿用完整序列,报告按指标列出测点、单位、窗口、有效点数和缺测情况。不能以代表测点替代其他设备的当前趋势。缺测保留null,无读数设备明确列入缺测清单并说明窗口;final前核对目标设备、查询覆盖和已发布图表。分页按page.has_more接续,大目录可用limit=100;缩略内容按evidence_read和data_path读取已保存证据。
|
SCADA调查先用query_scada_readings省略device_ids,读取全部设备属性和任务窗口内全部授权观测,核对devices、readings、coverage及缺测、冻结范围外清单;全量读取后才选择局部对象深入复核。设备目录单独读取时持续翻页到has_more=false。最终SCADA报告必须引用本轮全量证据,Server会拒绝仅有局部设备证据的final。
|
||||||
|
|
||||||
|
SCADA地图定位、等级标记、表格与图表采用同一目标设备集合;展示重点可以选择,但调查输入仍须全量。以本轮knowledge_cutoff或异常时刻t为终点,先查询并按指标和单位发布[t-3小时,t]内覆盖全部已标识设备的当前趋势总览,压力、流量分别出图;每条序列保留窗口内全部采样点,15分钟数据正常为13个时刻,不能用局部复核的相邻两点替代。系列名包含device_id;超过8组时分图,各组使用不同且稳定的chart_id。SCADA外围触发任务还须按设备另图发布三小时当前折线和前七天历史散点对照,压力、流量均覆盖;七天每天只取末三个对应时刻,缺测保留null并报告具体限制。图表数据视图沿用完整序列,报告按指标列出测点、单位、窗口、有效点数和缺测情况。不能以代表测点替代其他已标识设备的当前趋势。缺测保留null,无读数设备明确列入缺测清单并说明窗口;final前核对完整读取、缺测清单和已发布图表。
|
||||||
|
|
||||||
两个及以上有效时刻用折线图,同指标对象比较用柱状图,单值或缺少可比数据时用文字或指标。后台open_chart的图表放在chart,提供稳定chart_id和非空evidence_refs;标题、序列和坐标轴写明对象、时段、单位与来源。变化量采用同口径基准计算。
|
两个及以上有效时刻用折线图,同指标对象比较用柱状图,单值或缺少可比数据时用文字或指标。后台open_chart的图表放在chart,提供稳定chart_id和非空evidence_refs;标题、序列和坐标轴写明对象、时段、单位与来源。变化量采用同口径基准计算。
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"chat": "waternetwork/gpt-5.6-terra",
|
"chat": "waternetwork/deepseek-flash",
|
||||||
"task": "waternetwork/gpt-5.6-terra",
|
"task": "waternetwork/deepseek-flash",
|
||||||
"learning_gate": "waternetwork/gpt-5.6-terra"
|
"learning_gate": "waternetwork/deepseek-flash"
|
||||||
},
|
},
|
||||||
"providers": {
|
"providers": {
|
||||||
"waternetwork": {
|
"waternetwork": {
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
"api_url": "https://llm.waternetwork.cn/v1",
|
"api_url": "https://llm.waternetwork.cn/v1",
|
||||||
"api_key": "{env:WATERNETWORK_API_KEY}",
|
"api_key": "{env:WATERNETWORK_API_KEY}",
|
||||||
"models": {
|
"models": {
|
||||||
"gpt-5.6-terra": {
|
"deepseek-flash": {
|
||||||
"name": "快速",
|
"name": "快速",
|
||||||
"description": "快速回答和日常任务处理",
|
"description": "快速回答和日常任务处理",
|
||||||
"icon": "bolt",
|
"icon": "bolt",
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"context": 272000
|
"context": 272000
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gpt-5.6-sol": {
|
"gpt-6.1-sol": {
|
||||||
"name": "专家",
|
"name": "专家",
|
||||||
"description": "深入分析和复杂任务处理",
|
"description": "深入分析和复杂任务处理",
|
||||||
"icon": "sparkle",
|
"icon": "sparkle",
|
||||||
|
|||||||
@@ -156,19 +156,21 @@ describe("agent model config", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("exposes only the two configured gateway models with their roles, icons and context limits", () => {
|
it("exposes only the two configured gateway models with their roles, icons and context limits", () => {
|
||||||
expect(supportedModels).toEqual(["waternetwork/gpt-5.6-terra", "waternetwork/gpt-5.6-sol"]);
|
expect(supportedModels).toEqual(["waternetwork/deepseek-flash", "waternetwork/gpt-6.1-sol"]);
|
||||||
expect(agentModelOptions.map(({ id, label, icon }) => ({ id, label, icon }))).toEqual([
|
expect(agentModelOptions.map(({ id, label, icon }) => ({ id, label, icon }))).toEqual([
|
||||||
{ id: "waternetwork/gpt-5.6-terra", label: "快速", icon: "bolt" },
|
{ id: "waternetwork/deepseek-flash", label: "快速", icon: "bolt" },
|
||||||
{ id: "waternetwork/gpt-5.6-sol", label: "专家", icon: "sparkle" },
|
{ id: "waternetwork/gpt-6.1-sol", label: "专家", icon: "sparkle" },
|
||||||
]);
|
]);
|
||||||
expect(isSupportedModel("deepseek/deepseek-flash")).toBe(false);
|
expect(isSupportedModel("deepseek/deepseek-flash")).toBe(false);
|
||||||
expect(isSupportedModel("waternetwork/deepseek-flash")).toBe(false);
|
expect(isSupportedModel("waternetwork/deepseek-flash")).toBe(true);
|
||||||
|
expect(isSupportedModel("waternetwork/gpt-5.6-terra")).toBe(false);
|
||||||
|
expect(isSupportedModel("waternetwork/gpt-5.6-sol")).toBe(false);
|
||||||
expect(isSupportedModel("waternetwork/gpt-image-2.5")).toBe(false);
|
expect(isSupportedModel("waternetwork/gpt-image-2.5")).toBe(false);
|
||||||
expect(isSupportedModel("waternetwork/codex-auto-review")).toBe(false);
|
expect(isSupportedModel("waternetwork/codex-auto-review")).toBe(false);
|
||||||
const native = serviceOpencodeConfig({});
|
const native = serviceOpencodeConfig({});
|
||||||
expect(Object.keys(native.providers)).toEqual(["waternetwork"]);
|
expect(Object.keys(native.providers)).toEqual(["waternetwork"]);
|
||||||
expect(native.model).toBe("waternetwork/gpt-5.6-terra");
|
expect(native.model).toBe("waternetwork/deepseek-flash");
|
||||||
expect(native.providers.waternetwork.models["gpt-5.6-terra"].limit?.context).toBe(272000);
|
expect(native.providers.waternetwork.models["deepseek-flash"].limit?.context).toBe(272000);
|
||||||
expect(native.providers.waternetwork.models["gpt-5.6-sol"].limit?.context).toBe(475000);
|
expect(native.providers.waternetwork.models["gpt-6.1-sol"].limit?.context).toBe(475000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,6 +17,30 @@ afterEach(async () => {
|
|||||||
});
|
});
|
||||||
const productTool: Tool = { name: "get_task_context", description: "Bound task", inputSchema: { type: "object", properties: { report_revision: { type: "integer", minimum: 1 } }, additionalProperties: false } };
|
const productTool: Tool = { name: "get_task_context", description: "Bound task", inputSchema: { type: "object", properties: { report_revision: { type: "integer", minimum: 1 } }, additionalProperties: false } };
|
||||||
|
|
||||||
|
it("delivers complete SCADA metadata and observations through native MCP without narrowing device scope", async () => {
|
||||||
|
const data = {
|
||||||
|
devices: Array.from({ length: 168 }, (_, index) => ({ device_id: `D${index}`, node_id: `J${index}`, description: "x".repeat(100) })),
|
||||||
|
readings: Array.from({ length: 2184 }, (_, index) => ({ device_id: `D${index % 168}`, monitored_value: index, cleaned_value: null })),
|
||||||
|
coverage: { mode: "all_scada", query_complete: true, queried_device_count: 168, reading_count: 2184 },
|
||||||
|
};
|
||||||
|
const upstream = { content: [{ type: "text" as const, text: JSON.stringify(data) }], structuredContent: {
|
||||||
|
source: "query_scada_readings", evidence_id: "mcp:all-scada", data, truncated: false,
|
||||||
|
} };
|
||||||
|
const query = mock(async () => upstream);
|
||||||
|
const tool: Tool = { name: "query_scada_readings", inputSchema: { type: "object", properties: {
|
||||||
|
start: { type: "string" }, end: { type: "string" },
|
||||||
|
device_ids: { type: "array", items: { type: "string" }, maxItems: 20 },
|
||||||
|
}, required: ["start", "end"], additionalProperties: false } };
|
||||||
|
const { call } = await setup({ productTools: [tool], productClient: { query } });
|
||||||
|
setRuntimeSessionContext({ ...owner, purpose: "task_execution", taskId: "scada-task" });
|
||||||
|
const args = { start: "2026-07-16T05:00:00+08:00", end: "2026-07-16T08:00:00+08:00" };
|
||||||
|
const result = await call("query_scada_readings", args);
|
||||||
|
expect(result.isError).not.toBeTrue();
|
||||||
|
expect((query.mock.calls[0] as unknown as unknown[])[3]).toEqual(args);
|
||||||
|
expect(result.structuredContent).toEqual(upstream.structuredContent);
|
||||||
|
expect(JSON.stringify(result.structuredContent).length).toBeGreaterThan(12000);
|
||||||
|
});
|
||||||
|
|
||||||
it("hides HTTP budgets from models and fixes the native connection ceiling", async () => {
|
it("hides HTTP budgets from models and fixes the native connection ceiling", async () => {
|
||||||
const simulation: Tool = { name: "run_simulation", inputSchema: { type: "object", properties: {
|
const simulation: Tool = { name: "run_simulation", inputSchema: { type: "object", properties: {
|
||||||
timeout_seconds: { type: "integer", minimum: 1, maximum: 240, default: 120 },
|
timeout_seconds: { type: "integer", minimum: 1, maximum: 240, default: 120 },
|
||||||
|
|||||||
@@ -313,9 +313,9 @@ describe("OpencodeRuntimeAdapter.warmup", () => {
|
|||||||
} finally { await cleanup(); }
|
} finally { await cleanup(); }
|
||||||
});
|
});
|
||||||
it("rejects an invalid non-default model before readiness and cleans up the session", async () => {
|
it("rejects an invalid non-default model before readiness and cleans up the session", async () => {
|
||||||
const { runtime, calls, cleanup } = await createWarmupRuntime([], "waternetwork/gpt-5.6-sol");
|
const { runtime, calls, cleanup } = await createWarmupRuntime([], "waternetwork/gpt-6.1-sol");
|
||||||
try {
|
try {
|
||||||
await expect(runtime.warmup()).rejects.toThrow("waternetwork/gpt-5.6-sol: not_advertised");
|
await expect(runtime.warmup()).rejects.toThrow("waternetwork/gpt-6.1-sol: not_advertised");
|
||||||
expect(calls).not.toContain("model.list");
|
expect(calls).not.toContain("model.list");
|
||||||
expect(calls.at(-1)).toBe("session.remove");
|
expect(calls.at(-1)).toBe("session.remove");
|
||||||
} finally { await cleanup(); }
|
} finally { await cleanup(); }
|
||||||
|
|||||||
Reference in New Issue
Block a user