feat(monitoring): add DeepSeek monitoring end-to-end

- desktop: new DeepSeek page adapter and monitor registry
- desktop: extract generic AI auth helpers into shared module and
  skip binding when challenge signals are present
- admin-web: render DeepSeek HTML answers with inline citation anchors
  and surface reference-number badges on source cards
- server: fall back to inline_links/source_panel_links and read
  text/siteName fields when extracting DeepSeek citations

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 22:54:56 +08:00
parent f6889ecdee
commit f6e553dcc3
12 changed files with 2971 additions and 65 deletions
@@ -0,0 +1,274 @@
# DeepSeek Monitoring Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add production-usable `deepseek` desktop monitoring support with answer capture, citation extraction, runtime routing, and regression coverage.
**Architecture:** Reuse the existing generic AI auth flow, add a new Playwright-based desktop monitor adapter for DeepSeek, register it in the desktop runtime, and keep server-side result ingestion unchanged. The adapter should favor structured page observation plus network-response capture so answer capture and citation extraction can degrade independently.
**Tech Stack:** TypeScript + Vitest in `apps/desktop-client`, Go + `testing` in `server/internal/tenant/app`, Playwright hidden-page execution, pnpm workspace, existing shared monitoring payload contracts.
---
### Task 1: Lock In Failing Tests First
**Files:**
- Create: `apps/desktop-client/src/main/adapters/deepseek.test.ts`
- Modify: `server/internal/tenant/app/monitoring_callback_service_test.go`
- [ ] **Step 1: Write the failing desktop adapter helper tests**
```ts
import { describe, expect, it } from "vitest";
import { __deepseekTestUtils } from "./deepseek";
const {
extractQuestionText,
dedupeSourceItems,
classifyDeepseekSources,
isObservationComplete,
} = __deepseekTestUtils;
describe("deepseek adapter helpers", () => {
it("extracts question text from monitoring payload", () => {
expect(extractQuestionText({ question_text: "DeepSeek 怎么看 GEO" })).toBe("DeepSeek 怎么看 GEO");
});
it("dedupes sources by normalized url", () => {
const items = dedupeSourceItems([
{ url: "https://example.com/a#top", normalized_url: "https://example.com/a#top" },
{ url: "https://example.com/a#bottom", normalized_url: "https://example.com/a#bottom" },
]);
expect(items).toHaveLength(1);
expect(items[0]?.normalized_url).toBe("https://example.com/a");
});
it("separates citations from search-style sources", () => {
const classified = classifyDeepseekSources({
inlineLinks: [{ url: "https://example.com/citation", normalized_url: "https://example.com/citation" }],
sourcePanelLinks: [],
searchPanelLinks: [{ url: "https://example.com/search", normalized_url: "https://example.com/search" }],
});
expect(classified.citations).toHaveLength(1);
expect(classified.searchResults).toHaveLength(1);
});
it("treats a stable answer as complete even without citations", () => {
expect(isObservationComplete({
answer: "这是最终答案",
busy: false,
loginRequired: false,
challengeRequired: false,
signature: "sig-1",
})).toBe(true);
});
});
```
- [ ] **Step 2: Run the new desktop test to verify it fails**
Run:
```bash
pnpm --filter @geo/desktop-client test apps/desktop-client/src/main/adapters/deepseek.test.ts
```
Expected:
- FAIL because `./deepseek` does not exist yet
- [ ] **Step 3: Write the failing server regression test**
```go
func TestBuildMonitoringRawPayloadDeepSeekIncludesSearchResultsInCitationInputs(t *testing.T) {
searchTitle := "搜索网页结果"
req := MonitoringTaskResultRequest{
Status: "succeeded",
SearchResults: []MonitoringSourceItem{
{
URL: "https://example.com/search",
Title: &searchTitle,
},
},
}
_, sourceInputs := buildMonitoringRawPayload("deepseek", req)
if len(sourceInputs) != 1 {
t.Fatalf("buildMonitoringRawPayload() sourceInputs len = %d, want 1", len(sourceInputs))
}
if sourceInputs[0].URL != "https://example.com/search" {
t.Fatalf("buildMonitoringRawPayload() source url = %q, want search url", sourceInputs[0].URL)
}
}
```
- [ ] **Step 4: Run the targeted Go test to verify the new assertion state**
Run:
```bash
cd server && go test ./internal/tenant/app -run TestBuildMonitoringRawPayloadDeepSeekIncludesSearchResultsInCitationInputs -count=1
```
Expected:
- PASS or FAIL is acceptable here, but the test must compile and pin the intended DeepSeek ingestion behavior before adapter code lands
### Task 2: Implement the DeepSeek Desktop Adapter
**Files:**
- Create: `apps/desktop-client/src/main/adapters/deepseek.ts`
- Modify: `apps/desktop-client/src/main/adapters/index.ts`
- Modify: `apps/desktop-client/src/main/runtime-controller.ts`
- [ ] **Step 1: Add the adapter skeleton and helper exports**
```ts
export const deepseekAdapter: MonitorAdapter = {
provider: "deepseek",
executionMode: "playwright",
async query(context, payload) {
// implement after tests are in place
},
};
export const __deepseekTestUtils = {
extractQuestionText,
dedupeSourceItems,
classifyDeepseekSources,
isObservationComplete,
};
```
- [ ] **Step 2: Implement page bootstrap and observation helpers**
```ts
async function ensurePageOnDeepSeekChat(page: PlaywrightPage, signal: AbortSignal): Promise<void> {
if (signal.aborted) {
throw new Error("adapter_aborted");
}
if (!page.url() || !page.url().startsWith("https://chat.deepseek.com/")) {
await page.goto("https://chat.deepseek.com/", {
waitUntil: "domcontentloaded",
timeout: 20_000,
});
}
await page.waitForLoadState("domcontentloaded", { timeout: 20_000 }).catch(() => undefined);
await page.waitForLoadState("networkidle", { timeout: 3_000 }).catch(() => undefined);
}
```
- [ ] **Step 3: Implement answer submission, wait loop, and citation extraction**
```ts
const result = await waitForDeepSeekAnswer(page, questionText, context.signal);
const classified = classifyDeepseekSources({
inlineLinks: result.inlineLinks,
sourcePanelLinks: result.sourcePanelLinks,
searchPanelLinks: result.searchPanelLinks,
});
return {
status: "succeeded",
summary: "DeepSeek 监控任务执行成功。",
payload: {
platform: "deepseek",
provider_model: result.providerModel ?? "deepseek-chat",
provider_request_id: result.providerRequestID,
request_id: result.requestID,
answer: result.answer,
citations: toJsonSources(classified.citations),
search_results: toJsonSources(classified.searchResults),
raw_response_json: {
platform: "deepseek",
page_url: result.url,
answer: result.answer,
citations: toJsonSources(classified.citations),
search_results: toJsonSources(classified.searchResults),
},
},
};
```
- [ ] **Step 4: Register the adapter in exports and runtime routing**
```ts
export * from "./deepseek";
```
```ts
import { deepseekAdapter } from "./adapters";
if (platform === deepseekAdapter.provider) {
return deepseekAdapter;
}
```
- [ ] **Step 5: Run the desktop DeepSeek tests to verify they now pass**
Run:
```bash
pnpm --filter @geo/desktop-client test apps/desktop-client/src/main/adapters/deepseek.test.ts
```
Expected:
- PASS
### Task 3: Verify Integration Boundaries
**Files:**
- Modify: `apps/desktop-client/src/main/platform-auth-adapters.ts` only if DeepSeek-specific auth/risk strings are discovered during implementation
- Modify: `server/internal/tenant/app/monitoring_callback_service_test.go`
- [ ] **Step 1: Keep server ingestion behavior explicit for DeepSeek**
```go
func TestBuildMonitoringRawPayloadDeepSeekIncludesSearchResultsInCitationInputs(t *testing.T) {
// keep DeepSeek aligned with the default non-Kimi ingestion path
}
```
- [ ] **Step 2: Run the combined targeted verification suite**
Run:
```bash
pnpm --filter @geo/desktop-client test apps/desktop-client/src/main/adapters/deepseek.test.ts apps/desktop-client/src/main/platform-auth-adapters.test.ts
cd server && go test ./internal/tenant/app -run 'TestBuildMonitoringRawPayload(DeepSeek|Qwen|Kimi)|TestExtractMonitoringInlineCitationsFromRawResponseKimi' -count=1
```
Expected:
- all targeted tests pass
- [ ] **Step 3: Run desktop typecheck**
Run:
```bash
pnpm typecheck:desktop
```
Expected:
- PASS with no new type errors from the DeepSeek adapter wiring
- [ ] **Step 4: Commit the implementation**
```bash
git add apps/desktop-client/src/main/adapters/deepseek.ts \
apps/desktop-client/src/main/adapters/deepseek.test.ts \
apps/desktop-client/src/main/adapters/index.ts \
apps/desktop-client/src/main/runtime-controller.ts \
server/internal/tenant/app/monitoring_callback_service_test.go \
docs/superpowers/plans/2026-04-23-deepseek-monitoring.md
git commit -m "feat(desktop): add DeepSeek monitoring adapter"
```