Files
geo/docs/superpowers/plans/2026-04-23-deepseek-monitoring.md
root f6e553dcc3 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>
2026-04-23 22:54:56 +08:00

275 lines
8.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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"
```