Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🎓 AI 学习助手(Learning Assistant Agent)

一个面向个性化学习的智能助手:循环 ReAct agent(RAG 问答 + 自主规划)+ 系统级学习闭环(掌握度追踪 + SM-2 间隔重复),并配套完整的评估管线与工程化脚手架(并发/重试/限流/可观测)。

后端 FastAPI + LangGraph + Chroma;前端 Vue3 + Pinia + Vite;大模型与向量/重排使用阿里云 DashScope(通义千问)。


✨ 核心能力

模块 说明
循环 ReAct agent LangGraph agent↔tools 循环图 + should_continue 条件路由 + 原生函数调用(bind_tools)+ recursion_limit 优雅收尾(已流出的末轮内容不丢、通常为空时给明确兜底文案,收尾照常持久化)
RAG 对话 BM25+向量混合检索(RRF 融合)+ cross-encoder 重排 + 查询改写(多查询/HyDE)+ 回答引用溯源;分层贡献经五臂消融量化(见 eval)
工具错误隔离 工具失败 / 模型幻觉出不存在的工具名,都回 error ToolMessage 让 agent 观察后自纠,而非击垮整轮
真流式 LangGraph astream 逐 token 推送 SSE,支持中止;客户端断开时部分回答仍持久化(记忆不断档)
对话记忆 会话级滑动窗口 + 超阈值自动摘要(失败推进水位防级联),持久化
自适应学习计划 结构化 JSON 计划;对话中可让 AI 自主调用规划(react→plan 工具);更新进度时漂移/过期门控条件重规划(在轨不 churn);端点与工具共用同一编排(无漂移)
系统级学习闭环 计划 → 出题 → 自动批改 → 掌握度更新 → SM-2 间隔重复 → 重规划。对话 agent 可自主调工具参与其中:检索 / 规划 / 出题 / 批改 / 复习提醒 / 掌握度查询(闭环也经 REST 端点编排)
可评估 golden 集 + LLM-as-judge 双跑双判 + 工具路由行为用例 + 多轮指代用例 + 五臂检索消融 + 两臂成本(token/调用/延迟),可回归

🏗️ 架构

前端(Vue3 + Pinia + Vite)
  ChatWindow(真流式打字机 + 引用 chips)· UploadPanel · PlanView(计划/掌握度/测验)
        │
        │  fetch + SSE(text/event-stream,可中止)
        ▼
后端(FastAPI)
  对话编排 chat_service
    load_memory(窗口 + 摘要)→ build_agent → graph.astream → SSE
  Agent = LangGraph 循环 ReAct(agent ↔ tools,原生函数调用)
    agent 节点 ──tool_calls──▶ tools
       ▲                       · knowledge_search(BM25+向量+RRF+重排)
       └────观察回灌────       · make_learning_plan / get_due_reviews
                               · get_mastery / make_quiz / grade_quiz
    should_continue:无 tool_calls → END;工具失败/幻觉工具名 → error 观察 → 自纠
    recursion_limit 优雅收尾(兜底文案 + 持久化)
  学习闭环(REST 编排):/plan → /quiz → /quiz/grade → 掌握度 → SM-2 → /update_progress
  评估 eval:golden + LLM-as-judge 双跑双判(baseline 裸 RAG vs 真实 agent)
  持久化:SQLite(SQLAlchemy async)· 配置:pydantic-settings
        │
        ▼
DashScope(Qwen Chat + Embeddings + Rerank)

注:检索的「改写 / BM25+向量 / RRF / 重排」全部发生在 knowledge_search 工具内部,不是图里的独立节点;记忆加载与流式推送由 chat_service 编排,不在 agent 图内。agent 共 6 个工具:knowledge_search / make_learning_plan / get_due_reviews / get_mastery / make_quiz / grade_quiz(后四个让 agent 在对话里自主驱动学习闭环)。

Agent 工程(围绕模型的脚手架)

层 实现 代码
循环与兜底 agent↔tools 循环 + should_continue 条件路由 + recursion_limit 触发时优雅收尾并持久化(如末轮已有累积内容则连同说明收尾;真实拓扑下通常为空,走明确兜底文案) agents/react_agent.py · services/chat_service.py
工具错误隔离 工具失败 / 幻觉工具名(查表未命中)都回 error ToolMessage,agent 观察→再决策 react_agent.py tool_node
流式 graph.astream(stream_mode="messages") → SSE;客户端断开时把问题+部分回答落库(记忆不断档) services/chat_service.py
记忆 会话级滑动窗口 + 超阈值 LLM 摘要(带超时与水位推进),持久化(带多轮回归测试) memory/conversation_memory.py
可观测 RequestID 结构化日志;EvalTracer(token/LLM 调用/工具调用/延迟)经 SSE meta 透出 core/middleware.py · eval/tracer.py
韧性 统一 LLM 调用层:并发闸(同步+异步双 Semaphore)+ 按错误分类的退避重试(429/5xx 重试、401/400 不重试)+ 单调用超时,覆盖 agent 主路径/结构化输出/摘要/裁判/漂移判分全部调用点(查询改写亦有超时,失败降级为原 query 检索);工具调用超时与内部各段预算的算术自洽(外层 ≥ 改写+抽取+生成之和) models/llm.py · react_agent.py
护栏 身份网关(X-API-Key → user_id 映射,不信任请求体)+ 令牌桶限流(前置到鉴权之前,401 爆破同样限速)+ 上传安全(分块读+预检)+ 检索片段「不可信资料」前置框定 + 统一异常 core/security.py · core/rate_limit.py · core/errors.py
可评估 双跑双判(baseline vs 真实 agent)+ LLM-as-judge + 工具路由行为用例 + 多轮指代用例 + 检索消融 + 两臂成本追踪 eval/run.py · eval/golden.py

📁 目录结构

backend/
  app/
    main.py              # FastAPI 入口(路由/中间件/异常)
    core/                # settings / logging / middleware / errors / security / rate_limit / structured
    models/              # LLM 单例与统一调用层 / Embedding 单例
    schemas/             # Pydantic 请求/响应与结构化输出模型
    db/                  # SQLAlchemy async 模型与会话
    repositories/        # 仓库模式(替换内存 dict)
    memory/              # 对话记忆(窗口+摘要)
    rag/                 # document_loader / vector_store / hybrid_retriever / reranker / query_transform
    agents/              # react_agent(LangGraph 循环 ReAct)
    learning/            # srs(SM-2 间隔重复 + 统一 UTC 时间基准)
    services/            # 业务编排(chat / learning / planner 规划服务)
    eval/                # golden 集 + LLM-judge 评估管线 + 消融 + tracer
  eval_corpus/           # 评测语料(虚构简历,见 eval 段)
  data/                  # uploads / vector_db / app.db(运行时,不入库)
frontend/
  src/  api.js · App.vue · stores/ · components/

🚀 快速开始

0) 准备密钥

cd backend
cp .env.example .env
# 编辑 .env 填入你的 DASHSCOPE_API_KEY(阿里云 DashScope 控制台获取)

1) 后端

cd backend
python -m venv venv && venv\Scripts\activate     # Windows
pip install -r requirements.txt                  # 或 requirements-dev.txt(含测试)
uvicorn app.main:app --reload
# 访问 http://127.0.0.1:8000/docs

2) 前端

cd frontend
npm install
npm run dev      # http://localhost:3000

🔌 主要 API

方法 路径 说明
GET /health 健康检查
POST /upload 上传文档建库(uuid 重命名+白名单+分块读大小预检)
GET /documents 列出知识库文档
POST /chat 流式 RAG 对话(SSE:session / token / citations / meta / done / error)
POST /plan 生成结构化学习计划(与 make_learning_plan 工具共用编排)
POST /update_progress 更新进度并自适应重规划(漂移/过期门控)
POST /quiz 出题并保存为服务端「活动测验」,响应不含答案
POST /quiz/grade 批改:请求只带作答,题取服务端(答案不可偷看/伪造);一次性消费——成功后活动测验删除,重复/并发提交只有一个生效(404 no_active_quiz),防掌握度/SM-2 重放污染
GET /mastery 学习看板(掌握度/到期复习/薄弱知识点)
GET /reviews 今日到期复习队列

错误统一返回 {code, message, request_id}(含 X-Request-ID 头),不泄露堆栈。

✅ 测试与评估

pytest                                   # 单测 194 例(分片/RRF/掌握度/SM-2/校验/消毒/记忆/限流/漂移门控/工具隔离/信任边界/失败路径/批改消费…)
python -m app.eval.run                   # RAG 评估(双跑双判 + 行为/多轮用例,需先建库,见 eval 段)
python -m app.eval.run --ablation        # 五臂检索消融(hit@k + 空结果计数 + 延迟 + LLM 调用成本)

🛡️ 安全要点

  • 密钥仅存 .env(已 gitignore),源码零硬编码
  • 上传:uuid 重命名防路径穿越 + 扩展名白名单 + Content-Length 预检 + 分块读上限(超限 413 早于全量读内存)
  • 身份网关(调用方 + 身份合一):REQUIRE_API_KEY=true 时,X-API-Key 经 API_KEY_USERS 映射绑定 user_id(不信任请求体,映射即唯一白名单)——数据端点统一 current_user_id 依赖,防越权访问 agent 的按用户分域工具。dev(默认关闭)回落请求体 user_id,便于本地联调。鉴权按项目规模选用 API-key 网关而非 JWT/RBAC,避免半接线的 token 鉴权制造假安全面
  • 测验信任边界:标准答案只存服务端(active_quiz),/quiz 响应剔除 answer/explanation、/quiz/grade 只收作答——答案不可提前偷看、不可由客户端伪造刷掌握度;批改为一次性消费(原子删除活动测验,重复提交 404)
  • 生产 fail-closed:APP_ENV 非 dev 时,启动期断言鉴权映射与 DASHSCOPE_API_KEY 就绪,否则拒绝启动——避免默认部署被打穿或带病上线
  • 限流前置:令牌桶挂在鉴权之前且只在 key 为已映射合法 key 时按 key 聚合,其余按 IP——401 爆破路径同样限速,伪造/轮换 X-API-Key 头无法绕过(默认内存桶,RATE_LIMIT_* 可调;超限 429)
  • 检索注入框定:检索片段以「不可信资料」前置声明传入模型(明确资料内指令性内容一律视为数据),配轻量注入启发式,降低间接提示注入面
  • CORS 收敛为白名单;全局异常处理不向客户端泄露内部堆栈
  • 依赖 CVE 审计进 CI(pip-audit,失败即阻塞);唯一豁免项留档:chromadb PYSEC-2026-311(自托管 chroma server 未认证端点的 RCE,上游暂无修复版)——本项目默认嵌入式 PersistentClient 不暴露该面,server 模式为可选内网部署

📈 扩展性(可插拔后端:默认零依赖 / 配 env 即横向扩展)

横向扩展能力落地为可插拔后端:默认走本地零依赖后端(开箱即跑),配置对应环境变量即切换分布式后端,支持 uvicorn --workers N / 多副本。每个有状态组件都有明确的单进程→分布式切换点:

组件 默认(零依赖,单进程) 横向扩展(配 env 启用)
令牌桶限流 进程内 dict(asyncio.Lock,带桶数上限与淘汰) REDIS_URL → Redis Lua 原子令牌桶(register_script 自愈 NoScript,跨 worker/副本一致)
向量库 Chroma PersistentClient,data/vector_db/{user}/{kb}/ 目录隔离(client 按目标缓存复用) CHROMA_URL → Chroma Server HttpClient,按 collection=sha1(user/kb) 隔离(多 worker 共享写安全)
BM25 索引缓存 进程内 LRU(BM25_CACHE_MAX_SIZE,命中率先注入 /health) BM25_CACHE_TTL_SEC>0 → TTL 过期重建,多 worker 在窗口内最终一致(零依赖,无需广播)
元数据库 SQLite + WAL + busy_timeout(单写者) SQLITE_URL=postgresql+asyncpg://... → Postgres(多 worker 并发写安全)
LLM 并发足迹 不限 LLM_CONCURRENCY>0 → 同步/异步双 Semaphore 限 in-flight,防 DashScope 429 雪崩

横向扩展部署示例(多 worker):

REDIS_URL=redis://redis:6379/0 \
CHROMA_URL=http://chroma:8000 \
SQLITE_URL=postgresql+asyncpg://user:pass@postgres:5432/learning \
LLM_CONCURRENCY=8 \
uvicorn app.main:app --workers 4

可选依赖(仅启用对应后端时安装):redis(REDIS_URL 限流)、asyncpg(Postgres)。Chroma Server 用 chromadb 自带 HttpClient,无需额外依赖。

边界说明:分布式后端(Redis / Chroma Server / Postgres 适配器)已实现并由单测覆盖契约(后端路由 + Lua + TTL + driver 检测 + Semaphore);多 worker 端到端实测需对应中间件环境。默认单进程开箱即跑,横向扩展能力配 env 即生效。

完整部署步骤(docker-compose + 数据迁移须知 + 分布式限流验证)见 docs/scaling.md。

📈 进度(Roadmap)

  • 工程地基:配置外置(pydantic-settings)· 中间件 / 统一异常 / 结构化日志 / 健康检查
  • 对话主干:循环 ReAct agent · 真流式 SSE · SQLite 持久化 · 会话记忆(窗口 + 摘要)
  • 顶配召回:BM25+向量混合(RRF)+ 重排 + 查询改写,回答带来源引用;评估管线就位
  • 学习闭环:计划 / 出题 / 批改(一次性消费)/ 掌握度 / SM-2 / 门控重规划 + 看板
  • 前端工作台:三栏布局 · 流式打字机 · 明暗双主题 · 键盘可访问性
  • 测试与文档:离线单测 + HTTP 集成测 + 端到端验证
  • 加固轮:工具错误隔离(含幻觉工具名)· 失败路径闭环 · 限流与批改的信任模型 · eval 方法论(有效 N / 配对 Δ / 五臂消融 / 多轮用例)

🏆 技术要点

  • 循环 ReAct agent:ChatTongyi.bind_tools + LangGraph agent↔tools 循环图(should_continue 条件路由 + tools→agent 回边)+ recursion_limit 兜底;循环结构支持多步推理轨迹,行为证据见行为用例与多轮用例。
  • 规划即工具:tutor agent 自主调用规划工具 make_learning_plan;planner 为结构化输出服务,带「漂移/过期门控」(在轨不重规划);端点与工具共用 generate_grounded_plan 单一编排,两个入口行为一致。
  • 工具错误隔离(含幻觉工具名):工具失败与模型幻觉出不存在的工具名都回 error ToolMessage,agent 观察→再决策(换查询/换工具/致歉),两类失败的自愈均有离线测试覆盖。
  • 失败路径工程:recursion_limit 触发优雅收尾并持久化(末轮已有内容连同说明收尾,通常为空时给明确兜底文案);客户端断开把问题+半截回答落库(记忆不断档);统一 LLM 调用层把并发闸/分类重试/超时覆盖到全部调用点(agent 主路径/结构化输出/摘要/裁判/漂移判分),各层失败路径均有实现与测试覆盖。
  • agent 驱动学习闭环:除检索/规划外,agent 还能自主调 get_due_reviews / get_mastery / make_quiz / grade_quiz——在对话里观察掌握度、提醒到期复习、出题、批改并写回 SM-2 排期。
  • 真 token 流式:graph.astream(stream_mode="messages") → SSE,前端 fetch + ReadableStream 增量渲染打字机,可中止;同 chunk 双载荷(过渡语+工具调用)也能正确分桶。
  • 顶配召回(消融量化):BM25(jieba)+向量 → RRF 融合 → DashScope gte-rerank-v2 重排 → 多查询+HyDE 改写,回答带来源引用;分层贡献由五臂消融量化(hit@k + 空结果计数 + 延迟 + LLM 成本,python -m app.eval.run --ablation 可复跑)。
  • 结构化输出容错:with_structured_output 对大 schema 会因函数调用 JSON 偶发损坏(系统性 0/4 失败),改用自研 invoke_structured(JSON 提示 + json_repair + Pydantic 校验 + 把校验错误回灌 prompt 的 self-refine 重试)解决。
  • 评估管线(双跑双判 + 行为/多轮/消融):golden 集 + LLM-as-judge,每题同时跑 baseline 与真实 agent;两臂同温度、各臂对自己实际使用的 context 判分、拒答感知判分(正确拒答≠幻觉)、裁判失败剔除不混 0 分、有效 N<2 不判显著;行为用例断言工具路由,多轮用例断言指代接力。
  • 系统级学习闭环:结构化计划 → 出题 → 自动批改(客观精确匹配/数值等价/主观 LLM 判分)→ 掌握度 EMA(首评直接初始化)→ SM-2 间隔重复(半对=勉强通过的显式通过线)→ 自适应重规划;全程 UTC 统一时间基准。
  • 工程规范:Settings(.env) · 结构化日志+RequestID · 统一异常(不泄露堆栈) · 上传安全 · SQLite 持久化(CAS 乐观锁 + 并发首评竞态恢复 + 批改一次性消费) · 单测 194 例 · conftest 全局态复位 · 依赖锁定。

📊 RAG 评估(baseline vs 真实 agent)

每题双跑双判 ×N 跑(EVAL_RUNS,默认 3):baseline(裸 RAG:hybrid_search → 单次生成)对照 agent(/chat 同款 ReAct 循环),LLM-as-judge 打分报 mean±std + 显著性(逐题:|Δ|>两臂 pooled std,按失败剔除后的有效 run 数判定,有效 N<2 标 n/a;聚合级:逐题配对 Δ 检验 |mean Δ|>2×SE(Δ),题间共同方差经配对抵消)+ 两臂成本(token / LLM 调用 / 工具调用 / 延迟)。被测臂终态异常(超时/401)与裁判失败同样剔除计 fail_rate,不击穿整轮、不混 0 分(题层整臂全失败亦按臂剔除);结果逐题增量落盘。

golden 集 4 类:fact(单跳)/ multihop(多跳综合)/ unanswerable(资料外,测幻觉与拒答)/ adversarial(诱导确认伪事实)。语料为 backend/eval_corpus/fictional_resume.md(虚构人物,无真实 PII;unanswerable 题逐题核对过语料确无该信息)。行为用例覆盖全部 6 工具路由(含出题→批改两步链)+ 注入鲁棒;多轮用例验证指代接力与「指代+资料外」的正确拒答;另有五臂检索消融。

评估产物(report.md / report.json / ablation.md)为本地运行时生成,不入库;聚合与显著性逻辑由 tests/test_eval_scoring.py 离线回归覆盖。

运行前需先构建评测知识库(默认 probe/probe):

cd backend && python -c "from app.rag.document_loader import load_document; from app.rag.vector_store import build_vector_store; build_vector_store(load_document('eval_corpus/fictional_resume.md'), user_id='probe', kb_id='probe')"

About

ReAct agent × RAG 个性化学习助手:混合检索+重排+引用溯源,计划→出题→批改→SM-2 学习闭环,LLM-as-judge 评估管线 · FastAPI / LangGraph / Vue3

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages