Files
prop-ai-hr/docs/superpowers/plans/2026-07-24-digital-master-agent.md
T

407 lines
18 KiB
Markdown
Raw 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.
# 数字师傅工作 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。
**实施状态(2026-07-24):** Tasks 1–8 及 Task 9 的自动化、H5 构建和当前事实文档已完成;后端相关回归 91/91、移动端 Agent 契约 3/3。390×844 浏览器已验证寒暄、训练实时来源、全网同意和普通媒体分析;确认式写入、现场风险图 + SOP、旧资源撤权,以及主管/候选人入口仍未完成正式账号/真机浏览器验收。本分支尚未部署或生产验证。
---
## 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`
- [x] **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());
```
- [x] **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.
- [x] **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.
- [x] **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.
- [x] **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`
- [x] **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));
```
- [x] **Step 2: Verify RED**
Run `AihrAgentPolicyTest` with the same Maven command pattern.
- [x] **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.
- [x] **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`
- [x] **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.
- [x] **Step 2: Verify RED**
Run:
```bash
timeout 120s mvn -pl ruoyi-modules/ruoyi-aihr -am \
-DskipTests=false -Dsurefire.failIfNoSpecifiedTests=false \
-Dtest=AihrAgentOrchestratorTest,AihrAgentControllerContractTest test
```
- [x] **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`.
- [x] **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`
- [x] **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.
- [x] **Step 2: Verify RED**
Run focused knowledge and Agent media tests.
- [x] **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`.
- [x] **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.
- [x] **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`
- [x] **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.
- [x] **Step 2: Verify RED**
Run `AihrAgentActionServiceTest`.
- [x] **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.
- [x] **Step 4: Add endpoints and verify GREEN**
```text
POST /api/aihr/agent/actions/{draftId}/confirm
POST /api/aihr/agent/actions/{draftId}/dismiss
```
- [x] **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`
- [x] **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.
- [x] **Step 2: Verify RED, implement minimal delegation, verify GREEN**
Keep existing provider enablement, HTTPS validation, privacy redaction and rate limits inside `AihrWebAiService`.
- [x] **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`
- [x] **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.
- [x] **Step 2: Verify RED**
Run Java audit test plus:
```bash
bash scripts/tests/aihr-schema-migrations.test.sh
```
- [x] **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.
- [x] **Step 4: Extend reset and read-only preflight checks**
Production request code must not run DDL. Preflight checks the table and required indexes.
- [x] **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`
- [x] **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.
- [x] **Step 2: Verify RED**
Run:
```bash
node --test mobile-uni/tests/agent.test.mjs mobile-uni/tests/mobile-user-pages.test.mjs
```
- [x] **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.
- [x] **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
```
- [x] **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`
- [x] **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
```
- [x] **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
```
- [x] **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: Complete authenticated browser and real-device acceptance**
已在 390×844 完成寒暄、训练实时来源、全网同意和普通媒体分析。发布前仍须以正式账号/真机补齐:
1. “记一下,3栋1201要回访” — confirmation before persistence.
2. work-risk image — media + authorized SOP or explicit no evidence.
3. old file follow-up — protected resource delivery and revoked-access rejection.
同时补齐桌面宽度、键盘、焦点、确认/撤销反馈和主管/候选人角色正反例;截图不能替代实际交互。
- [x] **Step 5: Prepare the final documentation commit and preserve the release gate**
```bash
git add docs
git commit -m "docs(agent): record implementation and acceptance"
```
本次只提交本地实现和事实文档。未执行发布;只有在干净已提交树上完成远端静态、后端、schema 三项预检并补齐上面的正式验收,才可更新为 deployed / production-verified。