Files
huihuiSquare/docs/superpowers/plans/2026-07-23-avatar-chat-knowledge-plan.md
2026-07-23 16:27:02 +08:00

17 KiB
Raw Blame History

数字分身聊天与知识库增强实施计划

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: 实现“标准问答对 > 文件知识库 > OpenAI 兼容 Qwen”的数字分身聊天链路并补齐 MD/TXT 文档、人格编辑配置和知识库双表界面。

Architecture: 后端新增聊天编排接口,统一负责用户/分身权限、标准问答匹配、向量召回和 Qwen 调用;前端只传递消息历史并展示答案来源。现有 SQLite/SQLAlchemy 数据模型继续复用聊天历史暂不持久化Qwen 只通过后端环境变量访问。

Tech Stack: FastAPI、SQLAlchemy、Python unittest/unittest.mock、Vue 3 Composition API、Vue Router、Pinia、Axios、Vite。

Global Constraints

  • 回复优先级固定为标准问答对 > 文件知识库 > Qwen。
  • Qwen 配置只读取后端 CHAT_API_URLCHAT_API_KEYCHAT_MODEL,前端不得接触密钥。
  • 所有聊天、知识库、问答操作必须校验 Bearer app token 对应的分身归属。
  • 文档支持 .md.txt.pdf.doc.docx.xlsx,上传文件名只展示,落盘使用随机名。
  • 不新增聊天历史表,不改会会登录和用户体系。
  • 任何生产代码变更前先写并运行对应失败测试;每项任务完成后运行该任务的完整测试。

Task 1: 建立后端测试夹具和文本抽取回归测试

Files:

  • Create: digital-avatar-app/backend/tests/__init__.py
  • Create: digital-avatar-app/backend/tests/test_embeddings.py
  • Create: digital-avatar-app/backend/tests/test_chat_orchestration.py

Interfaces:

  • Produces temporary SQLite database fixtures and deterministic fake embeddings/model clients for later backend tasks.

  • Tests import embeddings.extract_text, routers.knowledge._match_standard_qa, and routers.chat._build_prompt/routers.chat._resolve_reply once those functions exist.

  • Step 1: Add a failing MD/TXT extraction test

Create two temporary files with UTF-8 Chinese text and assert extract_text(path, '.md') and extract_text(path, '.txt') return the source text. Also assert an unsupported extension raises ValueError rather than silently returning an empty string.

  • Step 2: Run the focused test and verify the expected failure

Run:

cd /Users/yqq/Works/AiProject/huihuiSquare/digital-avatar-app/backend
python3 -m unittest tests.test_embeddings -v

Expected: the MD/TXT cases fail because extract_text currently only handles the existing binary formats.

  • Step 3: Add failing orchestration tests

Cover these exact behaviors with an in-memory Avatar/QAPair setup and mocked embedding/model calls:

def test_enabled_qa_wins_without_calling_model():
    result = _resolve_reply(db, avatar, "  公司地址? ", [], model_client=fake_model)
    self.assertEqual(result["source"], "qa")
    self.assertEqual(result["answer"], "标准地址")
    fake_model.assert_not_called()

def test_knowledge_context_is_sent_to_qwen_after_qa_miss():
    result = _resolve_reply(db, avatar, "退款规则", [], search_fn=lambda *_: [knowledge_hit], model_client=fake_model)
    self.assertEqual(result["source"], "knowledge")
    self.assertIn("知识库内容", fake_model.call_args.kwargs["messages"][-1]["content"])

def test_chat_rejects_avatar_owned_by_another_user():
    with self.assertRaises(HTTPException) as caught:
        _require_owned_avatar(db, avatar.id, other_user_token)
    self.assertEqual(caught.exception.status_code, 403)
  • Step 4: Run the focused test and verify it fails for missing behavior

Run:

python3 -m unittest tests.test_chat_orchestration -v

Expected: import/behavior failures identify the missing helper and endpoint orchestration, not test syntax errors.

  • Step 5: Commit the test scaffolding
git add digital-avatar-app/backend/tests
git commit -m "test: cover avatar chat priority and text extraction"

Task 2: Add MD/TXT extraction and secure knowledge operations

Files:

  • Modify: digital-avatar-app/backend/embeddings.py
  • Modify: digital-avatar-app/backend/routers/knowledge.py
  • Modify: digital-avatar-app/backend/routers/avatars.py
  • Test: digital-avatar-app/backend/tests/test_embeddings.py

Interfaces:

  • embeddings.extract_text(path, ext) accepts .md and .txt and decodes text with UTF-8 replacement handling.

  • knowledge._require_owned_avatar(db, avatar_id, authorization) returns the owned Avatar or raises 401/403.

  • Existing docs/QA/search handlers use the ownership helper before reading or mutating data.

  • Step 1: Implement the smallest extraction change

Add .md and .txt to the text branch of extract_text; use open(path, 'r', encoding='utf-8', errors='replace'). Update the allowed extension set and client-facing error text to list MD/TXT.

  • Step 2: Run the extraction test to green

Run python3 -m unittest tests.test_embeddings -v; all extraction tests must pass.

  • Step 3: Add ownership resolution without changing public response shapes

Reuse the existing token lookup pattern from avatars.py. For no token return HTTPException(status_code=401, detail='未登录'); for a valid token whose huihui_user_id does not equal avatar.owner_id, return 403. Keep the current seed/empty-owner behavior only for explicitly unowned setup records, not for authenticated user data.

  • Step 4: Apply the helper to documents, search, and QA routes

Every route under /avatar/{avatar_id}/knowledge/... must call the helper before querying. Upload must also enforce a 10 MB request file limit based on bytes read and return 文件不能超过 10MB before vectorization.

  • Step 5: Run backend compile and focused tests
cd /Users/yqq/Works/AiProject/huihuiSquare/digital-avatar-app/backend
python3 -m unittest tests.test_embeddings tests.test_chat_orchestration -v
python3 -m py_compile main.py database.py models.py embeddings.py routers/*.py
  • Step 6: Commit the backend document/security changes
git add digital-avatar-app/backend/embeddings.py digital-avatar-app/backend/routers/knowledge.py digital-avatar-app/backend/routers/avatars.py digital-avatar-app/backend/tests
git commit -m "feat: support text knowledge files and protect avatar knowledge"

Task 3: Implement QA-first Qwen chat orchestration

Files:

  • Create: digital-avatar-app/backend/routers/chat.py
  • Modify: digital-avatar-app/backend/main.py
  • Modify: digital-avatar-app/backend/requirements.txt
  • Modify: digital-avatar-app/backend/tests/test_chat_orchestration.py

Interfaces:

  • POST /api/avatar/{avatar_id}/chat accepts {message: string, history?: [{role, content}]}.

  • Success response is {answer, source, references} where source is qa, knowledge, or qwen.

  • routers.chat._match_standard_qa(question, qa_pairs) returns the winning enabled QA or None.

  • routers.chat._build_prompt(avatar, history, question, knowledge_hits) returns system/user messages with no secrets.

  • Step 1: Make the QA-first tests fail for the current backend

Run the orchestration tests from Task 1 and confirm no chat router/helper currently exists.

  • Step 2: Implement deterministic QA matching

Normalize Unicode whitespace, case, and Chinese/ASCII punctuation. Check exact normalized questions first; only then use difflib.SequenceMatcher against enabled questions and accept a similarity of at least 0.86. Disabled QAs never match.

  • Step 3: Implement knowledge context assembly

Call the existing vector search logic with top five hits. Include only non-empty snippets and filenames in the prompt. Preserve hit metadata as references and use source='knowledge' whenever at least one document hit was supplied to Qwen.

  • Step 4: Implement the OpenAI-compatible Qwen client

Read:

CHAT_API_URL = os.getenv("CHAT_API_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1")
CHAT_API_KEY = os.getenv("CHAT_API_KEY", "")
CHAT_MODEL = os.getenv("CHAT_MODEL", "qwen-plus")

POST to CHAT_API_URL.rstrip('/') + '/chat/completions' with Authorization: Bearer ..., timeout 30 seconds, the configured model, bounded recent history, and the generated system prompt. Handle non-2xx responses, malformed JSON, and empty choices with user-safe errors.

  • Step 5: Map Avatar.config into the prompt

Use defaults matching AvatarCreate.vue: professional, creativity 50, rigor 50, humor 30, medium, empty system prompt. Convert creativity to temperature = 0.2 + creativity / 100 * 0.6; describe the other fields in Chinese instructions. Clamp history to the last 10 messages and each content field to 4,000 characters.

  • Step 6: Register the router and run all backend tests

Include routers.chat.router in main.py, then run:

python3 -m unittest discover -s tests -v
python3 -m py_compile main.py database.py models.py embeddings.py routers/*.py
  • Step 7: Commit the chat endpoint
git add digital-avatar-app/backend/routers/chat.py digital-avatar-app/backend/main.py digital-avatar-app/backend/requirements.txt digital-avatar-app/backend/tests
git commit -m "feat: add QA-first avatar chat with Qwen fallback"

Task 4: Add frontend API, avatar editing controls, and chat route

Files:

  • Modify: digital-avatar-app/src/api/index.ts
  • Modify: digital-avatar-app/src/utils/avatar-page-data.js
  • Modify: digital-avatar-app/src/views/AvatarEdit.vue
  • Modify: digital-avatar-app/src/views/AvatarManage.vue
  • Create: digital-avatar-app/src/views/AvatarChat.vue
  • Modify: digital-avatar-app/src/router/index.ts
  • Test: digital-avatar-app/scripts/avatar-page-data.test.mjs

Interfaces:

  • sendAvatarChat(avatarId, payload) calls POST /avatar/${avatarId}/chat.

  • normalizeAvatarEditForm returns all six config values with defaults.

  • buildAvatarUpdatePayload includes the nested config object.

  • /avatar/chat/:id renders a usable conversation without requiring a second login flow.

  • Step 1: Extend the frontend helper test first

Add assertions for missing config defaults, payload preservation, chat request shape, and source labels qa, knowledge, qwen; run the test and verify the new assertions fail.

  • Step 2: Add chat API types and helper functions

Add ChatMessage, ChatResponse, and sendAvatarChat. Keep Axios response unwrapping consistent with the existing interceptor; do not read an extra .data layer in the view.

  • Step 3: Extend AvatarEdit controls and payload

Add the same style chips, range sliders, response length choices, and system prompt textarea used by AvatarCreate.vue. Initialize through normalizeAvatarEditForm; save through buildAvatarUpdatePayload so existing name/description/photo/status fields remain intact.

  • Step 4: Add the chat route and page

Create a mobile-first page with a top back button, avatar identity header, scrollable message list, source/reference labels, textarea input, send button, empty-state starter prompts, and error retry. On send, append the user message, send the last 10 messages, append the assistant response, and keep the input focused only when the request succeeds.

  • Step 5: Wire management entry points

Make the primary digital-avatar card/action open /avatar/chat/${avatar.id}; keep edit, knowledge, and delete actions separate so clicking chat does not trigger an unrelated action.

  • Step 6: Run frontend helper test and production build
cd /Users/yqq/Works/AiProject/huihuiSquare/digital-avatar-app
node scripts/avatar-page-data.test.mjs
./node_modules/.bin/vite build
  • Step 7: Commit the frontend chat/edit changes
git add digital-avatar-app/src/api/index.ts digital-avatar-app/src/utils/avatar-page-data.js digital-avatar-app/src/views/AvatarEdit.vue digital-avatar-app/src/views/AvatarManage.vue digital-avatar-app/src/views/AvatarChat.vue digital-avatar-app/src/router/index.ts digital-avatar-app/scripts/avatar-page-data.test.mjs
git commit -m "feat: add avatar chat and editable personality settings"

Task 5: Rebuild knowledge management as scrollable tabbed tables

Files:

  • Modify: digital-avatar-app/src/views/KnowledgeManage.vue
  • Modify: digital-avatar-app/src/views/QaPairEdit.vue
  • Modify: digital-avatar-app/src/api/index.ts

Interfaces:

  • Documents and QAs remain loaded through the existing APIs and are independently rendered.

  • The page exposes activeTab values docs and qa and keeps data loaded when switching tabs.

  • Upload accept string and displayed help text include .md and .txt.

  • Step 1: Add a failing UI contract smoke test

Add a small source-level Node assertion script or extend the existing helper test to assert the component contains the two tab labels, activeTab, .md, and .txt; run it before changing the component and confirm failure.

  • Step 2: Implement tab state and document panel

Keep the upload zone and search controls in the document tab. Replace document cards with a semantic table: filename/type, size, vectorization status/chunks, created time, and delete action. Use a .table-scroll wrapper with a bounded max-height and overflow-y: auto.

  • Step 3: Implement the QA table panel

Render question, answer summary, enabled switch/status, updated time, edit and delete actions. Keep the add button routing to QaPairEdit.vue. Do not fetch from the API again on every tab switch.

  • Step 4: Fix page scrolling and responsive layout

Set the page to min-height: 100dvh, overflow-x: hidden, and normal vertical flow. Make the tab/table wrappers responsive so desktop uses side-by-side table columns where space permits and mobile stacks the active table with horizontal table overflow only when required.

  • Step 5: Run frontend build and inspect the generated CSS/JS markers

Run ./node_modules/.bin/vite build and assert the generated bundle contains the new tab labels and MD/TXT accept values.

  • Step 6: Commit the knowledge UI changes
git add digital-avatar-app/src/views/KnowledgeManage.vue digital-avatar-app/src/views/QaPairEdit.vue digital-avatar-app/src/api/index.ts digital-avatar-app/scripts
git commit -m "feat: add text uploads and tabbed knowledge tables"

Task 6: Backend/API integration verification and test deployment

Files:

  • Modify: /Users/yqq/Works/AiProject/huihuiSquare/digital-avatar-app/.env only if local test defaults need documenting; never commit secrets.
  • Modify: /Users/yqq/Works/AiProject/huihuiSquare/digital-avatar-app/backend/.env only through the test server deployment environment, never commit secrets.
  • Test: digital-avatar-app/backend/tests/test_chat_orchestration.py
  • Test: digital-avatar-app/scripts/avatar-page-data.test.mjs

Interfaces:

  • Test server receives CHAT_API_URL, CHAT_API_KEY, and CHAT_MODEL in /root/avatar-test/.env without logging the key.

  • Frontend /api reaches the newly deployed backend router.

  • Step 1: Verify model configuration availability without printing secrets

On geo-server, inspect only whether CHAT_API_KEY is set and print CHAT_API_URL/CHAT_MODEL. If the key is absent, stop deployment and report that the user must provide/configure it; do not fall back to a fake answer.

  • Step 2: Rebuild the backend image and restart only avatar-test services

From /root/avatar-test, rebuild avatar-test-backend, recreate that container, and confirm /api/health plus an unauthenticated chat request returns 401/未登录. Preserve the existing SQLite file by taking a timestamped copy before recreation and restoring it only if the compose flow replaces the container without a volume.

  • Step 3: Build and deploy the frontend dist safely

Run vite build, upload dist to a timestamped staging directory, back up /root/avatar-test/run-dist, then copy only assets/. and index.html into avatar-test-frontend and restart it. Do not use a broad docker cp run-dist/. html/ replacement.

  • Step 4: Run live QA-first acceptance checks

Using a valid test app token, create one short MD or TXT document and one enabled QA pair. Send the exact QA question and verify response source=qa; send a non-QA question and verify the response is from knowledge or qwen with no secret leakage. Delete the temporary QA/document records after capturing results.

  • Step 5: Verify frontend and backend health
curl -fsS http://192.168.1.188:8099/api/health
curl -fsS http://192.168.1.188:8011/api/health

Inspect the deployed index.html asset names and confirm the chat route, MD/TXT accept values, and tab labels are in the new bundle. Record the deployed container status and backup path.

  • Step 6: Final verification and handoff

Run the backend unit suite, Python compile check, frontend smoke test, Vite build, git diff --check, and git status --short. Report any remaining type-check limitation separately from build status; do not claim Qwen live success unless the configured test key produced a real response.