diff --git a/docs/superpowers/plans/2026-07-24-digital-master-agent.md b/docs/superpowers/plans/2026-07-24-digital-master-agent.md new file mode 100644 index 00000000..c7750505 --- /dev/null +++ b/docs/superpowers/plans/2026-07-24-digital-master-agent.md @@ -0,0 +1,408 @@ +# 数字师傅工作 Agent 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:** 将移动端“问”从默认 RAG 查询改为受身份、权限、确认和审计约束的数字师傅工作 Agent,并保持现有知识、数据工具、记忆、全网和媒体链路兼容。 + +**Architecture:** 新增 `org.dromara.aihr.agent` 编排边界,Planner 只产生受校验的意图和工具计划,Policy 固定角色与工具许可,Orchestrator 复用现有领域服务。旧 `/api/knowledge/**` 与 `/api/aihr/web-ai/**` 保留;移动端改用 `/api/aihr/agent/**` 的统一响应,不从模型自由文本决定按钮或业务状态。 + +**Tech Stack:** Java 17、Spring Boot、Sa-Token、JdbcTemplate、Jackson、JUnit 5/Mockito、uni-app Vue 3/TypeScript、Node test、MySQL 8。 + +--- + +## Scope split + +本计划完整实现已确认的 Agent 设计。课程播放器/防快进和个人文件/网页知识空间有独立数据生命周期,不塞入 Agent 编排改动;Agent 只读取当前已经正式存在的学习、训练、工作成果、记忆和知识能力。它们分别形成后续独立计划,不影响本计划验收。 + +### Task 1: Agent contract and conservative planner + +**Files:** +- Create: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent/AihrAgentDto.java` +- Create: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent/AihrAgentPlanner.java` +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent/AihrAgentPlannerTest.java` + +- [ ] **Step 1: Write failing planner tests** + +Cover exact routes: + +```java +assertEquals(Intent.LIVE_MY_WORK, planner.plan("我今天有什么待办", false).intent()); +assertEquals(Tool.MY_CURRENT_TASKS, planner.plan("我今天有什么待办", false).tool()); +assertEquals(Intent.CAPTURE_FACT, planner.plan("记一下,3栋1201要回访", false).intent()); +assertEquals(Intent.SOCIAL, planner.plan("你好", false).intent()); +assertEquals(Intent.CLARIFY, planner.plan("帮我处理一下", false).intent()); +assertEquals(Intent.MEDIA_UNDERSTANDING, planner.plan("图中是什么宠物", true).intent()); +assertEquals(Intent.KNOWLEDGE_QA, planner.plan("装修人员怎么进场", false).intent()); +``` + +- [ ] **Step 2: Run the focused test and verify RED** + +Run: + +```bash +timeout 120s mvn -pl ruoyi-modules/ruoyi-aihr -am \ + -DskipTests=false -Dsurefire.failIfNoSpecifiedTests=false \ + -Dtest=AihrAgentPlannerTest test +``` + +Expected: compilation failure because Agent contract and planner do not exist. + +- [ ] **Step 3: Add the minimal typed contract** + +`AihrAgentDto` defines: + +```java +enum Intent { + KNOWLEDGE_QA, RESOURCE_DELIVERY, LIVE_MY_WORK, LIVE_TEAM_WORK, + PRACTICE_COACHING, CAPTURE_FACT, DRAFT_ACTION, + MEDIA_UNDERSTANDING, WEB_RESEARCH, CLARIFY, SOCIAL +} + +enum Tool { + NONE, KNOWLEDGE_SEARCH, KNOWLEDGE_RESOURCE, MY_CURRENT_TASKS, + MY_PRACTICE_SUMMARY, TEAM_PRACTICE_SUMMARY, + CAPTURE_MEMORY_DRAFT, MEDIA_ANALYZE, WEB_RESEARCH +} + +record AgentPlan( + Intent intent, + String rewrittenRequest, + Tool tool, + boolean requiresConfirmation, + boolean requiresExternalConsent, + ResponseStyle responseStyle +) {} +``` + +Add request/response records matching the approved design. Requests accept question, conversation/version, project, broadcast ID and explicit external consent; they do not accept identity, role or tool name. + +- [ ] **Step 4: Implement deterministic safe routing, then optional model refinement** + +Known high-confidence intents route deterministically. Model output is parsed only when it matches known enums, contains at most one tool and does not supply identity/project IDs. Invalid or unavailable model output returns deterministic `CLARIFY`, never default RAG. + +- [ ] **Step 5: Run planner tests and commit** + +Expected: all `AihrAgentPlannerTest` tests pass. + +```bash +git add backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent \ + backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent/AihrAgentPlannerTest.java +git commit -m "feat(agent): add conservative intent planner" +``` + +### Task 2: Policy gate and registered tool metadata + +**Files:** +- Create: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent/AihrAgentPolicy.java` +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent/AihrAgentPolicyTest.java` + +- [ ] **Step 1: Write failing policy tests** + +Required assertions: + +```java +assertDoesNotThrow(() -> policy.authorize(employee, plan(MY_CURRENT_TASKS))); +assertThrows(ServiceException.class, () -> policy.authorize(employee, plan(TEAM_PRACTICE_SUMMARY))); +assertDoesNotThrow(() -> policy.authorize(supervisor, plan(TEAM_PRACTICE_SUMMARY))); +assertThrows(ServiceException.class, () -> policy.authorize(candidate, plan(MY_CURRENT_TASKS))); +assertThrows(ServiceException.class, () -> policy.authorize(employee, forgedUnknownToolPlan)); +``` + +- [ ] **Step 2: Verify RED** + +Run `AihrAgentPolicyTest` with the same Maven command pattern. + +- [ ] **Step 3: Implement one explicit switch** + +`AihrAgentPolicy` declares risk and allowed roles for each `Tool`; it rejects unsupported role/tool pairs, write-like plans without confirmation, external plans without consent and multiple calls. Do not introduce an interface/factory/registry framework. + +- [ ] **Step 4: Verify GREEN and commit** + +```bash +git add backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent/AihrAgentPolicy.java \ + backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent/AihrAgentPolicyTest.java +git commit -m "feat(agent): enforce tool policy" +``` + +### Task 3: Read-only orchestrator and unified API + +**Files:** +- Create: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent/AihrAgentOrchestrator.java` +- Create: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent/AihrAgentController.java` +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent/AihrAgentOrchestratorTest.java` +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent/AihrAgentControllerContractTest.java` + +- [ ] **Step 1: Write failing orchestrator tests** + +Verify: + +- `LIVE_MY_WORK` sends only `MY_CURRENT_TASKS` to the existing query/data-tool chain and never invokes RAG. +- `KNOWLEDGE_QA` delegates to `AihrKnowledgeQueryService`. +- `SOCIAL` and `CLARIFY` do not invoke knowledge or data services. +- unavailable data maps to `UNAVAILABLE`; no task maps to `COMPLETED` plus `data.state=NO_TASKS`. +- the controller has login protection and does not expose identity/tool parameters. + +- [ ] **Step 2: Verify RED** + +Run: + +```bash +timeout 120s mvn -pl ruoyi-modules/ruoyi-aihr -am \ + -DskipTests=false -Dsurefire.failIfNoSpecifiedTests=false \ + -Dtest=AihrAgentOrchestratorTest,AihrAgentControllerContractTest test +``` + +- [ ] **Step 3: Implement the unified text endpoint** + +Add: + +```text +POST /api/aihr/agent/messages +``` + +The orchestrator resolves the current principal server-side, calls Planner, then Policy, and delegates to existing services. It maps citations, resources, conversation/version and data into `AgentResponse`. + +- [ ] **Step 4: Verify GREEN and commit** + +```bash +git add backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent \ + backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent +git commit -m "feat(agent): orchestrate read-only work tools" +``` + +### Task 4: Correct media routing and conversation context + +**Files:** +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/knowledge/service/AihrKnowledgeQueryService.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/knowledge/AihrKnowledgeQueryServiceTest.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent/AihrAgentOrchestrator.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent/AihrAgentController.java` +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent/AihrAgentMediaTest.java` + +- [ ] **Step 1: Write failing tests for both media modes** + +Verify: + +- “图中是什么宠物” returns the vision observation, labels it `MEDIA`, does not call SOP search and appends that answer to the short conversation. +- “图中电梯门打不开怎么办” may combine vision context with authorized SOP search. +- no-evidence work media still returns the vision observation and explicitly marks missing SOP evidence. + +- [ ] **Step 2: Verify RED** + +Run focused knowledge and Agent media tests. + +- [ ] **Step 3: Expose one internal media mode** + +Change the knowledge media method to accept an internal enum `MEDIA_ONLY|MEDIA_WITH_KNOWLEDGE`; the client cannot submit it. Reuse current extraction, authorization, size checks and conversation append. `MEDIA_ONLY` skips `queryDocuments`. + +- [ ] **Step 4: Add the multipart Agent endpoint** + +Add: + +```text +POST /api/aihr/agent/messages/media +``` + +Request parameters mirror text conversation/project fields and explicit consent only. + +- [ ] **Step 5: Verify GREEN and commit** + +```bash +git add backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent \ + backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/knowledge/service/AihrKnowledgeQueryService.java \ + backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr +git commit -m "fix(agent): route media by intent" +``` + +### Task 5: Confirmation actions by reference, not duplicate business logic + +**Files:** +- Create: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent/AihrAgentActionService.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent/AihrAgentController.java` +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent/AihrAgentActionServiceTest.java` + +- [ ] **Step 1: Write failing capture-action tests** + +Verify: + +- capture intent creates/reuses the existing memory candidate and returns `NEEDS_CONFIRMATION`; +- confirmation requires candidate version, idempotency key, edited draft and `PRIVATE|COMPANY`; +- duplicate identical confirmation returns the same target; +- changed payload with the same key, expired candidate and cross-user candidate are rejected; +- dismiss delegates to the existing candidate state machine. + +- [ ] **Step 2: Verify RED** + +Run `AihrAgentActionServiceTest`. + +- [ ] **Step 3: Implement reference-backed actions** + +Agent `draftId` is opaque and resolves server-side to an existing domain draft. Confirmation delegates to `AihrMemoryService.confirm`; dismissal delegates to `AihrMemoryService.dismiss`. Do not duplicate memory normalization, authorization, persistence or idempotency logic. + +- [ ] **Step 4: Add endpoints and verify GREEN** + +```text +POST /api/aihr/agent/actions/{draftId}/confirm +POST /api/aihr/agent/actions/{draftId}/dismiss +``` + +- [ ] **Step 5: Commit** + +```bash +git add backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent \ + backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent +git commit -m "feat(agent): confirm existing domain drafts" +``` + +### Task 6: Explicit web consent and source separation + +**Files:** +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent/AihrAgentOrchestrator.java` +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent/AihrAgentWebResearchTest.java` + +- [ ] **Step 1: Write failing consent tests** + +Without consent, `WEB_RESEARCH` returns a clarification/consent card and never calls `AihrWebAiService`. With consent, it delegates to the existing web service and maps only web sources; enterprise citations stay empty. + +- [ ] **Step 2: Verify RED, implement minimal delegation, verify GREEN** + +Keep existing provider enablement, HTTPS validation, privacy redaction and rate limits inside `AihrWebAiService`. + +- [ ] **Step 3: Commit** + +```bash +git add backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent/AihrAgentOrchestrator.java \ + backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent/AihrAgentWebResearchTest.java +git commit -m "feat(agent): require web research consent" +``` + +### Task 7: Minimal Agent audit and migration + +**Files:** +- Create: `backend/script/sql/update/aihr_20260724_agent_orchestration_mysql8.sql` +- Modify: `backend/script/sql/aihr_knowledge_mysql8.sql` +- Create: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent/AihrAgentAuditService.java` +- Modify: `scripts/reset-dev-db.sh` +- Modify: `scripts/release-preflight.sh` +- Modify: `scripts/tests/aihr-schema-migrations.test.sh` +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent/AihrAgentAuditServiceTest.java` + +- [ ] **Step 1: Write failing schema and audit tests** + +Assert the migration provides `aihr_agent_run` with run, tenant/app/user, conversation/version, intent, tool, status, source type, duration and sanitized error code. Action details remain in existing domain draft tables and are linked by `result_ref`; do not duplicate sensitive request bodies. + +- [ ] **Step 2: Verify RED** + +Run Java audit test plus: + +```bash +bash scripts/tests/aihr-schema-migrations.test.sh +``` + +- [ ] **Step 3: Add idempotent MySQL migration and audit writer** + +The service records final state in a `finally`-safe path. Audit failure logs a sanitized warning and does not turn a valid user response into failure. + +- [ ] **Step 4: Extend reset and read-only preflight checks** + +Production request code must not run DDL. Preflight checks the table and required indexes. + +- [ ] **Step 5: Verify GREEN and commit** + +```bash +git add backend/script/sql backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/agent \ + backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/agent \ + scripts/reset-dev-db.sh scripts/release-preflight.sh scripts/tests/aihr-schema-migrations.test.sh +git commit -m "feat(agent): audit orchestrated runs" +``` + +### Task 8: Move mobile “问” to the Agent response contract + +**Files:** +- Create: `mobile-uni/src/services/agent.ts` +- Modify: `mobile-uni/src/types/api.ts` +- Modify: `mobile-uni/src/pages/user/sop/index.vue` +- Test: `mobile-uni/tests/agent.test.mjs` +- Test: `mobile-uni/tests/mobile-user-pages.test.mjs` + +- [ ] **Step 1: Write failing contract tests** + +Verify source chips, `NO_EVIDENCE/UNAVAILABLE/FORBIDDEN`, clarification cards, memory confirmation cards, external-consent prompts, citations/resources and media uploads all use the new Agent endpoints. Verify the UI never derives an action button from answer text. + +- [ ] **Step 2: Verify RED** + +Run: + +```bash +node --test mobile-uni/tests/agent.test.mjs mobile-uni/tests/mobile-user-pages.test.mjs +``` + +- [ ] **Step 3: Add typed Agent service and map the existing chat page** + +Reuse existing audio recording, upload, citation, resource, memory-card and conversation-version UI. Add only source chips, clarification/consent card and status-specific copy. Keep legacy knowledge service for compatibility outside this page. + +- [ ] **Step 4: Verify tests and H5 build** + +```bash +node --test mobile-uni/tests/agent.test.mjs mobile-uni/tests/mobile-user-pages.test.mjs +npm --prefix mobile-uni run build:h5 +``` + +- [ ] **Step 5: Commit** + +```bash +git add mobile-uni/src/services/agent.ts mobile-uni/src/types/api.ts \ + mobile-uni/src/pages/user/sop/index.vue mobile-uni/tests +git commit -m "feat(mobile): use digital master agent" +``` + +### Task 9: Regression, docs, browser and release handoff + +**Files:** +- Modify: `docs/API_INTEGRATION.md` +- Modify: `docs/BRD_IMPLEMENTATION_AUDIT.md` +- Modify: `docs/20260708/数字师傅学练问报整合方案.md` +- Modify: `docs/superpowers/specs/2026-07-24-digital-master-agent-design.md` + +- [ ] **Step 1: Run backend regression** + +```bash +timeout 120s mvn -pl ruoyi-modules/ruoyi-aihr -am \ + -DskipTests=false -Dsurefire.failIfNoSpecifiedTests=false \ + -Dtest='AihrAgent*Test,AihrKnowledgeQueryServiceTest,AihrKnowledgeDataToolServiceTest,AihrMemoryServiceTest' test +``` + +- [ ] **Step 2: Run mobile regression and build** + +```bash +node --test mobile-uni/tests/*.test.mjs +npm --prefix mobile-uni run build:h5 +git diff --check +``` + +- [ ] **Step 3: Update current-truth documentation** + +Document exact API contract, migration order, implemented/verified/deployed distinction and remaining courseware/personal-resource subprojects. Remove “待评审” from the approved design. + +- [ ] **Step 4: Run authenticated browser acceptance** + +At 390×844 and desktop widths, exercise: + +1. “你好” — no RAG/source card. +2. “我今天有什么待办” — live-data source and truthful `NO_TASKS|HAS_TASKS`. +3. “记一下,3栋1201要回访” — confirmation before persistence. +4. pet image — media source, no SOP lookup. +5. work-risk image — media + authorized SOP or explicit no evidence. +6. web question — consent before external call. +7. old file follow-up — protected resource delivery and revoked-access rejection. + +Capture screenshots and inspect layout, overflow, keyboard, focus and action-state feedback. + +- [ ] **Step 5: Final commit and release gate** + +```bash +git add docs +git commit -m "docs(agent): record implementation and acceptance" +``` + +Do not deploy until the complete remote static/backend/schema preflight can be run from a clean committed tree. Deployment and formal production account/real-device acceptance remain separate explicit actions. diff --git a/docs/superpowers/specs/2026-07-24-digital-master-agent-design.md b/docs/superpowers/specs/2026-07-24-digital-master-agent-design.md index 66feb773..cf77634f 100644 --- a/docs/superpowers/specs/2026-07-24-digital-master-agent-design.md +++ b/docs/superpowers/specs/2026-07-24-digital-master-agent-design.md @@ -1,6 +1,6 @@ # 数字师傅工作 Agent 总体设计 -> 状态:待评审 +> 状态:已确认(2026-07-24) > > 日期:2026-07-24 >