docs(design): 补全中文设计文档体系
This commit is contained in:
106
docs/设计文档/模块设计/1.配置模块详细设计.md
Normal file
106
docs/设计文档/模块设计/1.配置模块详细设计.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# 配置模块详细设计
|
||||
|
||||
## 1. 模块目标
|
||||
|
||||
Config 模块负责 Django 项目的启动配置和总装配。它不承载业务逻辑,只为其他模块提供稳定运行环境。
|
||||
|
||||
目标:
|
||||
|
||||
- 项目本地和 Docker 均可启动。
|
||||
- 环境变量可覆盖关键配置。
|
||||
- App、模板、静态资源、上传文件和数据库路径统一配置。
|
||||
- URL 总入口清晰,模块路由各自维护。
|
||||
|
||||
## 2. 职责边界
|
||||
|
||||
负责:
|
||||
|
||||
- `settings.py`、`urls.py`、`wsgi.py`、`asgi.py`。
|
||||
- 环境变量读取和默认值。
|
||||
- SQLite、静态文件、媒体文件、Chroma、场景配置目录。
|
||||
- Django Admin 和模块 URL 装配。
|
||||
|
||||
不负责:
|
||||
|
||||
- 不读取场景 YAML 业务内容。
|
||||
- 不调用 Agent Core。
|
||||
- 不处理上传文件文本抽取。
|
||||
- 不写审计日志。
|
||||
|
||||
## 3. 配置项设计
|
||||
|
||||
| 配置 | Django setting | 默认值 |
|
||||
|---|---|---|
|
||||
| `DJANGO_SECRET_KEY` | `SECRET_KEY` | `dev-secret-key` |
|
||||
| `DJANGO_DEBUG` | `DEBUG` | `true` |
|
||||
| `DJANGO_ALLOWED_HOSTS` | `ALLOWED_HOSTS` | `["*"]` |
|
||||
| `UPLOAD_ROOT` | `MEDIA_ROOT` | `BASE_DIR / "data" / "uploads"` |
|
||||
| `SCENARIO_CONFIG_DIR` | `SCENARIO_CONFIG_DIR` | `BASE_DIR / "configs"` |
|
||||
| `CHROMA_PATH` | `CHROMA_PATH` | `BASE_DIR / "data" / "chroma"` |
|
||||
| `LLM_API_KEY` | `LLM_API_KEY` | 空 |
|
||||
| `LLM_BASE_URL` | `LLM_BASE_URL` | `https://api.openai.com/v1` |
|
||||
| `LLM_MODEL` | `LLM_MODEL` | `gpt-4.1-mini` |
|
||||
| `EMBEDDING_API_KEY` | `EMBEDDING_API_KEY` | 空,默认可复用 `LLM_API_KEY` |
|
||||
| `EMBEDDING_BASE_URL` | `EMBEDDING_BASE_URL` | 空,默认可复用 `LLM_BASE_URL` |
|
||||
| `EMBEDDING_MODEL` | `EMBEDDING_MODEL` | `text-embedding-3-small` |
|
||||
|
||||
## 4. 目录路径设计
|
||||
|
||||
启动前或初始化时应确保:
|
||||
|
||||
```text
|
||||
data/
|
||||
uploads/
|
||||
chroma/
|
||||
configs/
|
||||
static/
|
||||
templates/
|
||||
```
|
||||
|
||||
V1 可以在 `settings.py` 中定义路径,在 management command 或启动脚本中创建目录。生产代码不应在每次请求中反复创建目录。
|
||||
|
||||
## 5. URL 总路由设计
|
||||
|
||||
`config.urls`:
|
||||
|
||||
```python
|
||||
urlpatterns = [
|
||||
path("admin/", admin.site.urls),
|
||||
path("", include("apps.scenarios.urls")),
|
||||
path("chat/", include("apps.chat.urls")),
|
||||
path("documents/", include("apps.documents.urls")),
|
||||
path("audit/", include("apps.audit.urls")),
|
||||
]
|
||||
```
|
||||
|
||||
开发模式下追加 `static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)`,用于访问上传文件。
|
||||
|
||||
## 6. 静态资源与上传文件设计
|
||||
|
||||
- `STATIC_URL = "static/"`
|
||||
- `STATICFILES_DIRS = [BASE_DIR / "static"]`
|
||||
- `MEDIA_URL = "media/"`
|
||||
- `MEDIA_ROOT = UPLOAD_ROOT`
|
||||
|
||||
上传文件路径由 Documents 模块按场景组织,Config 只提供根目录。
|
||||
|
||||
## 7. 环境变量读取设计
|
||||
|
||||
V1 可使用标准库 `os.environ.get()`,不强制引入复杂配置库。
|
||||
|
||||
布尔值规则:
|
||||
|
||||
```text
|
||||
"1", "true", "yes", "on" -> True
|
||||
其他 -> False
|
||||
```
|
||||
|
||||
`DJANGO_ALLOWED_HOSTS` 使用逗号分隔,空值时默认 `["*"]`。
|
||||
|
||||
## 8. 验收标准
|
||||
|
||||
- `python manage.py check` 通过。
|
||||
- `python manage.py migrate` 可执行。
|
||||
- `/`、`/admin/` 路由可访问。
|
||||
- `MEDIA_ROOT`、`CHROMA_PATH`、`SCENARIO_CONFIG_DIR` 在 settings 中可被其他模块引用。
|
||||
- LLM 与 Embedding 配置只从 settings 或环境变量读取,不散落在业务代码中。
|
||||
134
docs/设计文档/模块设计/2.场景模块详细设计.md
Normal file
134
docs/设计文档/模块设计/2.场景模块详细设计.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# 场景模块详细设计
|
||||
|
||||
## 1. 模块目标
|
||||
|
||||
Scenarios 模块是业务 Agent 的入口,负责读取和展示场景配置,并向 Chat、Documents、Agent Core 提供场景上下文。
|
||||
|
||||
## 2. 职责边界
|
||||
|
||||
负责:
|
||||
|
||||
- 从 `configs/*.yaml` 读取场景。
|
||||
- 校验场景必填字段。
|
||||
- 展示场景列表和场景摘要。
|
||||
- 提供 `list_scenarios()`、`get_scenario()` 等服务。
|
||||
|
||||
不负责:
|
||||
|
||||
- 不执行 Agent。
|
||||
- 不做 RAG 检索。
|
||||
- 不调用工具和大模型。
|
||||
- 不保存审计日志。
|
||||
|
||||
## 3. 场景配置结构
|
||||
|
||||
必填结构:
|
||||
|
||||
```yaml
|
||||
id: knowledge_qa
|
||||
name: 知识库问答助手
|
||||
description: 用于 SOP、制度和内部知识库问答
|
||||
applicable_questions:
|
||||
- SOP 问答
|
||||
- 制度问答
|
||||
|
||||
agent:
|
||||
role: 知识库问答专家
|
||||
goal: 基于知识库回答用户问题
|
||||
system_prompt: ""
|
||||
instructions:
|
||||
- 回答必须基于检索内容
|
||||
|
||||
rag:
|
||||
enabled: true
|
||||
collection: knowledge_qa
|
||||
top_k: 5
|
||||
|
||||
tools:
|
||||
- generate_action_items
|
||||
|
||||
output:
|
||||
type: general_answer
|
||||
|
||||
audit:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
`agent.system_prompt` 为可选字段。配置了非空值时,Agent Core 优先使用该字段作为系统提示词;为空或缺失时,由 `role`、`goal` 和 `instructions` 组合生成系统提示词。
|
||||
|
||||
`applicable_questions` 作为页面展示字段,若缺失可显示为空列表。
|
||||
|
||||
## 4. 场景加载流程
|
||||
|
||||
1. 读取 `settings.SCENARIO_CONFIG_DIR`。
|
||||
2. 遍历 `.yaml` 和 `.yml` 文件。
|
||||
3. 使用 YAML parser 转为 dict。
|
||||
4. 调用 `validate_scenario()`。
|
||||
5. 转换为 `ScenarioConfig` dataclass 或普通 dict。
|
||||
6. 按文件名或配置顺序返回。
|
||||
|
||||
为了便于复试修改,V1 不需要强缓存;若加缓存,应提供清理方式或在 DEBUG 下禁用缓存。
|
||||
|
||||
## 5. 场景校验规则
|
||||
|
||||
必填字段:
|
||||
|
||||
- `id`
|
||||
- `name`
|
||||
- `description`
|
||||
- `agent.role`
|
||||
- `agent.goal`
|
||||
- `agent.instructions`
|
||||
- `rag.enabled`
|
||||
- `tools`
|
||||
- `output.type`
|
||||
- `audit.enabled`
|
||||
|
||||
校验失败时返回包含文件名、字段路径、错误原因的结果。列表页可以跳过非法场景并展示错误摘要。
|
||||
|
||||
## 6. 页面设计
|
||||
|
||||
首页路径:`/`
|
||||
|
||||
展示:
|
||||
|
||||
- 场景名称。
|
||||
- 场景描述。
|
||||
- 适用题型。
|
||||
- RAG 是否启用。
|
||||
- 工具数量。
|
||||
- 进入对话按钮。
|
||||
|
||||
可选详情页:`/scenarios/<scenario_id>/`。V1 可以把详情合并到 Chat 页面。
|
||||
|
||||
## 7. 服务函数设计
|
||||
|
||||
```python
|
||||
def list_scenarios() -> list[ScenarioConfig]:
|
||||
"""读取配置目录中的合法场景,非法场景以错误摘要返回给页面。"""
|
||||
|
||||
def get_scenario(scenario_id: str) -> ScenarioConfig:
|
||||
"""按场景 ID 返回完整配置,找不到时抛出 ScenarioNotFound。"""
|
||||
|
||||
def validate_scenario(config: dict) -> ValidationResult:
|
||||
"""校验必填字段、字段类型、工具名称和输出类型。"""
|
||||
```
|
||||
|
||||
`get_scenario()` 找不到时抛出业务异常,例如 `ScenarioNotFound`,由 View 转成中文错误提示。
|
||||
|
||||
## 8. 异常处理
|
||||
|
||||
| 异常 | 处理 |
|
||||
|---|---|
|
||||
| 配置目录不存在 | 返回空列表和错误提示 |
|
||||
| YAML 语法错误 | 标记该文件无效 |
|
||||
| ID 重复 | 保留第一个,报告重复错误 |
|
||||
| 必填字段缺失 | 标记该场景无效 |
|
||||
| 工具不存在 | 场景仍可展示,但 Chat 执行时记录工具错误 |
|
||||
|
||||
## 9. 验收标准
|
||||
|
||||
- 首页至少展示 5 个场景。
|
||||
- 场景配置来自 `configs/` 文件。
|
||||
- 非法配置有明确错误,不导致首页 500。
|
||||
- Chat 可通过 `scenario_id` 获取完整配置。
|
||||
127
docs/设计文档/模块设计/3.文档模块详细设计.md
Normal file
127
docs/设计文档/模块设计/3.文档模块详细设计.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# 文档模块详细设计
|
||||
|
||||
## 1. 模块目标
|
||||
|
||||
Documents 模块让用户把复试题材料快速变成 Agent 可检索的知识库。V1 必须支持 `.txt`、`.md`、`.pdf` 和 `.docx`,保证常见复试材料可以进入 RAG。
|
||||
|
||||
## 2. 职责边界
|
||||
|
||||
负责:
|
||||
|
||||
- 文件上传表单和页面。
|
||||
- 文件保存与元数据记录。
|
||||
- 读取文本内容。
|
||||
- 调用 Agent Core RAG 入库。
|
||||
- 更新入库状态。
|
||||
|
||||
不负责:
|
||||
|
||||
- 不实现向量检索算法。
|
||||
- 不生成模型回答。
|
||||
- 不直接写审计日志。
|
||||
|
||||
## 3. 数据模型设计
|
||||
|
||||
模型:`UploadedDocument`
|
||||
|
||||
字段见 `docs/设计文档/3.数据库设计.md`。
|
||||
|
||||
常量:
|
||||
|
||||
```python
|
||||
STATUS_UPLOADED = "uploaded"
|
||||
STATUS_INDEXED = "indexed"
|
||||
STATUS_FAILED = "failed"
|
||||
SUPPORTED_EXTENSIONS = {".txt", ".md", ".pdf", ".docx"}
|
||||
```
|
||||
|
||||
文件保存路径建议:
|
||||
|
||||
```text
|
||||
uploads/<scenario_id>/<YYYYMMDD>/<uuid>_<original_name>
|
||||
```
|
||||
|
||||
## 4. 文件上传流程
|
||||
|
||||
1. GET `/documents/upload/` 渲染上传表单。
|
||||
2. POST 校验 `scenario_id` 和文件。
|
||||
3. 调用 Scenarios 服务确认场景存在。
|
||||
4. 校验扩展名和文件大小。
|
||||
5. 保存文件。
|
||||
6. 创建 `UploadedDocument(status="uploaded")`。
|
||||
7. 跳转文件列表页并展示成功提示。
|
||||
|
||||
## 5. 文本抽取流程
|
||||
|
||||
抽取函数:
|
||||
|
||||
```python
|
||||
def extract_text(document: UploadedDocument) -> str:
|
||||
"""按文件类型抽取可入库纯文本,失败时抛出可展示的业务异常。"""
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- `.txt`:优先 UTF-8,失败时尝试系统默认编码。
|
||||
- `.md`:UTF-8 读取,保留标题、列表和正文。
|
||||
- `.pdf`:抽取纯文本,不要求 OCR、表格还原和复杂版式理解。
|
||||
- `.docx`:抽取段落、标题和普通表格文本,不要求完整保留 Word 样式。
|
||||
- 空文本视为失败。
|
||||
- 文件不存在视为失败。
|
||||
|
||||
XLSX 暂不作为 V1 必须项,可作为后续结构化业务数据导入能力。
|
||||
|
||||
## 6. RAG 入库触发流程
|
||||
|
||||
POST `/documents/<id>/index/`
|
||||
|
||||
1. 获取 `UploadedDocument`。
|
||||
2. 调用 `extract_text()`。
|
||||
3. 调用 `agent_core.rag.ingest.ingest_document()`,传入 `document_id`、`scenario_id`、文件名和抽取文本。
|
||||
4. 成功后更新 `status="indexed"`,清空 `error_message`。
|
||||
5. 失败后更新 `status="failed"`,写入 `error_message`。
|
||||
6. 重定向回文件列表页。
|
||||
|
||||
入库动作必须使用 POST,避免 GET 触发写操作。
|
||||
|
||||
已入库或失败文档允许重新入库。重新入库前需要按 `document_id` 清理或覆盖旧 chunk,避免重复检索。
|
||||
|
||||
## 7. 页面设计
|
||||
|
||||
文件列表页展示:
|
||||
|
||||
- 文件名。
|
||||
- 场景 ID。
|
||||
- 文件类型。
|
||||
- 文件大小。
|
||||
- 状态。
|
||||
- 上传时间。
|
||||
- 入库按钮。
|
||||
- 错误信息。
|
||||
|
||||
上传页展示:
|
||||
|
||||
- 场景下拉框。
|
||||
- 文件控件。
|
||||
- 支持类型提示。
|
||||
- 表单错误。
|
||||
|
||||
## 8. 异常处理
|
||||
|
||||
| 异常 | 处理 |
|
||||
|---|---|
|
||||
| 场景不存在 | 表单错误 |
|
||||
| 文件为空 | 表单错误 |
|
||||
| 扩展名不支持 | 表单错误 |
|
||||
| 文件保存失败 | 页面提示失败 |
|
||||
| 文本为空 | 状态 failed |
|
||||
| RAG 入库失败 | 状态 failed 并保存原因 |
|
||||
|
||||
## 9. 验收标准
|
||||
|
||||
- 可以上传 `.txt`、`.md`、`.pdf` 和 `.docx`。
|
||||
- 文件列表可看到记录。
|
||||
- 文件可按场景关联。
|
||||
- 入库成功状态变为 `indexed`。
|
||||
- 入库失败状态变为 `failed` 且可查看原因。
|
||||
- 入库失败或已入库文档可重新入库。
|
||||
118
docs/设计文档/模块设计/4.对话模块详细设计.md
Normal file
118
docs/设计文档/模块设计/4.对话模块详细设计.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# 对话模块详细设计
|
||||
|
||||
## 1. 模块目标
|
||||
|
||||
Chat 模块负责复试演示中的主交互:用户选择场景后提交问题,系统展示 Agent 输出、引用、工具调用和审计入口。
|
||||
|
||||
## 2. 职责边界
|
||||
|
||||
负责:
|
||||
|
||||
- 对话页 GET/POST。
|
||||
- 用户输入表单校验。
|
||||
- 获取场景配置。
|
||||
- 调用 Agent Core。
|
||||
- 调用 Audit 服务写日志。
|
||||
- 渲染 AgentResult。
|
||||
|
||||
不负责:
|
||||
|
||||
- 不直接读取 YAML。
|
||||
- 不直接调用 LLM。
|
||||
- 不直接执行 RAG 和工具。
|
||||
- 不实现复杂多轮会话状态。
|
||||
|
||||
## 3. 页面设计
|
||||
|
||||
路径:`/chat/<scenario_id>/`
|
||||
|
||||
GET:
|
||||
|
||||
- 加载场景配置。
|
||||
- 展示场景摘要。
|
||||
- 加载当前场景下状态为 `indexed` 的文档列表。
|
||||
- 展示空表单。
|
||||
|
||||
POST:
|
||||
|
||||
- 校验输入。
|
||||
- 执行 Agent。
|
||||
- 写审计。
|
||||
- 展示结果和审计链接。
|
||||
|
||||
## 4. 表单设计
|
||||
|
||||
字段:
|
||||
|
||||
| 字段 | 类型 | 规则 |
|
||||
|---|---|---|
|
||||
| `message` | textarea | 必填,最大 4000 字 |
|
||||
| `document_ids` | 多选 | 可选,只能选择当前场景下已入库文档 |
|
||||
|
||||
错误提示:
|
||||
|
||||
- 空输入:`请输入要咨询的问题。`
|
||||
- 超长输入:`问题过长,请控制在 4000 字以内。`
|
||||
- 文档不属于当前场景或未入库:`请选择当前场景下已入库的文档。`
|
||||
|
||||
## 5. Agent Core 调用流程
|
||||
|
||||
```python
|
||||
scenario = get_scenario(scenario_id)
|
||||
result = run_agent(
|
||||
scenario_config=scenario,
|
||||
user_input=form.cleaned_data["message"],
|
||||
options={"document_ids": form.cleaned_data.get("document_ids", [])}
|
||||
)
|
||||
```
|
||||
|
||||
Chat 只依赖 Agent Core 的统一返回对象,不关心内部是否使用 RAG、工具或真实模型。
|
||||
|
||||
未选择文档时,`document_ids` 传空列表或不传,由 Agent Core 默认使用当前场景全部已入库文档。
|
||||
|
||||
## 6. 结果展示设计
|
||||
|
||||
优先级:
|
||||
|
||||
1. 如果 `structured_output` 不为空,展示结构化 JSON 或字段化结果。
|
||||
2. 展示 `answer`。
|
||||
3. 展示 `references`。
|
||||
4. 展示 `tool_calls`。
|
||||
5. 展示 `latency_ms`、`model_name`、`status`。
|
||||
6. 如果有 `error`,展示中文错误提示。
|
||||
|
||||
结构化解析失败时,页面仍展示 `raw_output` 或 `answer`。
|
||||
|
||||
## 7. 审计日志写入流程
|
||||
|
||||
Agent Core 返回后调用:
|
||||
|
||||
```python
|
||||
audit_log = create_audit_log(
|
||||
scenario_id=scenario.id,
|
||||
scenario_name=scenario.name,
|
||||
user_input=message,
|
||||
agent_result=result,
|
||||
)
|
||||
```
|
||||
|
||||
如果 Agent Core 抛异常,Chat 应构造失败结果并继续写失败审计。
|
||||
|
||||
## 8. 异常处理
|
||||
|
||||
| 异常 | 处理 |
|
||||
|---|---|
|
||||
| 场景不存在 | 显示错误并返回首页入口 |
|
||||
| 表单无效 | 留在页面并显示表单错误 |
|
||||
| Agent Core 抛异常 | 构造 failed AgentResult,写审计 |
|
||||
| 审计写入失败 | 页面提示审计失败,但展示 Agent 输出 |
|
||||
| LLM 配置缺失 | 展示模型配置缺失 |
|
||||
|
||||
## 9. 验收标准
|
||||
|
||||
- 从首页可进入对话页。
|
||||
- 可提交问题并渲染 AgentResult。
|
||||
- 可选择本次对话使用的文档范围;未选择时默认使用当前场景全部已入库文档。
|
||||
- 失败时有中文提示。
|
||||
- 成功和失败都尽量写入审计。
|
||||
- View 中没有 RAG、工具、LLM 的细节实现。
|
||||
121
docs/设计文档/模块设计/5.审计模块详细设计.md
Normal file
121
docs/设计文档/模块设计/5.审计模块详细设计.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# 审计模块详细设计
|
||||
|
||||
## 1. 模块目标
|
||||
|
||||
Audit 模块记录 Agent 执行过程,使演示者能够解释一次输出的来源、工具调用和模型结果。它是系统从“普通问答页面”变成“可追踪业务 Agent”的关键。
|
||||
|
||||
## 2. 职责边界
|
||||
|
||||
负责:
|
||||
|
||||
- `AgentAuditLog` 模型。
|
||||
- 审计写入服务。
|
||||
- 审计列表页。
|
||||
- 审计详情页。
|
||||
- 敏感信息过滤。
|
||||
|
||||
不负责:
|
||||
|
||||
- 不执行 Agent。
|
||||
- 不执行 RAG。
|
||||
- 不执行工具。
|
||||
- 不调用模型。
|
||||
|
||||
## 3. 数据模型设计
|
||||
|
||||
模型:`AgentAuditLog`
|
||||
|
||||
字段见 `docs/设计文档/3.数据库设计.md`。
|
||||
|
||||
JSON 字段默认值必须使用函数,例如 `default=list`、`default=dict`,避免多实例共享同一对象。
|
||||
|
||||
## 4. 日志写入流程
|
||||
|
||||
服务函数:
|
||||
|
||||
```python
|
||||
def create_audit_log(
|
||||
scenario_id: str,
|
||||
scenario_name: str,
|
||||
user_input: str,
|
||||
agent_result: AgentResult,
|
||||
) -> AgentAuditLog:
|
||||
"""将 AgentResult 映射为 AgentAuditLog,并在保存前做敏感信息脱敏。"""
|
||||
```
|
||||
|
||||
写入映射:
|
||||
|
||||
- `agent_result.references` -> `retrieved_chunks`
|
||||
- `agent_result.tool_calls` -> `tool_calls`
|
||||
- `agent_result.structured_output` -> `structured_output`
|
||||
- `agent_result.answer` -> `final_answer`
|
||||
- `agent_result.raw_output` -> `raw_output`
|
||||
- `agent_result.model_name` -> `model_name`
|
||||
- `agent_result.latency_ms` -> `latency_ms`
|
||||
- `agent_result.status` -> `status`
|
||||
- `agent_result.error` -> `error_message`
|
||||
|
||||
## 5. 日志列表页设计
|
||||
|
||||
路径:`/audit/`
|
||||
|
||||
查询:
|
||||
|
||||
- 默认按创建时间倒序。
|
||||
- V1 可不做分页,若日志较多再加 Django Paginator。
|
||||
|
||||
展示:
|
||||
|
||||
- ID。
|
||||
- 场景名称。
|
||||
- 用户输入前 80 字。
|
||||
- 状态。
|
||||
- 模型名。
|
||||
- 耗时。
|
||||
- 创建时间。
|
||||
- 详情链接。
|
||||
|
||||
## 6. 日志详情页设计
|
||||
|
||||
路径:`/audit/<log_id>/`
|
||||
|
||||
展示:
|
||||
|
||||
- 基础信息。
|
||||
- 用户输入。
|
||||
- 最终回答。
|
||||
- 结构化输出。
|
||||
- RAG 检索片段。
|
||||
- 工具调用。
|
||||
- 原始输出。
|
||||
- 错误信息。
|
||||
|
||||
JSON 可用格式化后的 `<pre>` 展示。
|
||||
|
||||
## 7. 敏感信息处理
|
||||
|
||||
不得保存:
|
||||
|
||||
- `LLM_API_KEY`
|
||||
- 完整环境变量 dump
|
||||
- 用户机器上的敏感绝对路径
|
||||
- Docker secret 或 token
|
||||
|
||||
如错误信息来自异常对象,应在保存前做简单脱敏,至少替换 API Key 值。
|
||||
|
||||
## 8. 异常处理
|
||||
|
||||
| 异常 | 处理 |
|
||||
|---|---|
|
||||
| AgentResult 字段缺失 | 使用默认空值 |
|
||||
| JSON 不可序列化 | 转为字符串或空对象 |
|
||||
| 日志不存在 | 返回 404 |
|
||||
| 写入失败 | 抛给 Chat,由 Chat 展示审计失败提示 |
|
||||
|
||||
## 9. 验收标准
|
||||
|
||||
- 每次对话成功后有审计日志。
|
||||
- Agent 失败也有失败日志。
|
||||
- 列表页可查看日志摘要。
|
||||
- 详情页可查看输入、输出、引用和工具调用。
|
||||
- 日志不包含 API Key。
|
||||
259
docs/设计文档/模块设计/6.智能核心模块详细设计.md
Normal file
259
docs/设计文档/模块设计/6.智能核心模块详细设计.md
Normal file
@@ -0,0 +1,259 @@
|
||||
# 智能核心模块详细设计
|
||||
|
||||
## 1. 模块目标
|
||||
|
||||
Agent Core 提供独立于 Django View 的智能编排能力。它消费场景配置,执行 RAG、工具、模型调用和结构化解析,最终返回统一 AgentResult。
|
||||
|
||||
## 2. 职责边界
|
||||
|
||||
负责:
|
||||
|
||||
- Agent 编排。
|
||||
- 场景配置对象消费。
|
||||
- RAG 入库和检索。
|
||||
- 工具注册与执行。
|
||||
- LLM Provider 与 Embedding Provider。
|
||||
- 结构化输出解析。
|
||||
- AgentResult 定义。
|
||||
|
||||
不负责:
|
||||
|
||||
- 不渲染页面。
|
||||
- 不处理 Django 表单。
|
||||
- 不保存 Django Model。
|
||||
- 不管理登录权限。
|
||||
|
||||
## 3. 子模块划分
|
||||
|
||||
```text
|
||||
agent_core/
|
||||
orchestrator.py
|
||||
scenario_loader.py
|
||||
llm_provider.py
|
||||
tool_registry.py
|
||||
structured_output.py
|
||||
rag/
|
||||
ingest.py
|
||||
retriever.py
|
||||
tools/
|
||||
builtin_tools.py
|
||||
schemas/
|
||||
outputs.py
|
||||
```
|
||||
|
||||
`scenario_loader.py` 可作为非 Django 环境下加载配置的工具;Django 场景展示仍由 `apps.scenarios` 负责。
|
||||
|
||||
## 4. Orchestrator 设计
|
||||
|
||||
入口:
|
||||
|
||||
```python
|
||||
def run_agent(scenario_config, user_input: str, options: dict | None = None) -> AgentResult:
|
||||
"""执行一次 Agent 编排,options 可包含 document_ids 等运行期约束。"""
|
||||
```
|
||||
|
||||
流程:
|
||||
|
||||
1. 记录开始时间。
|
||||
2. 根据 `rag.enabled`、`scenario_id` 和可选 `document_ids` 检索引用。
|
||||
3. 根据 `tools` 执行或准备工具结果。
|
||||
4. 构造 messages。
|
||||
5. 调用 LLM Provider。
|
||||
6. 解析结构化输出。
|
||||
7. 计算耗时。
|
||||
8. 返回 `AgentResult(status="success")`。
|
||||
9. 捕获可恢复异常并返回 `status="failed"`。
|
||||
|
||||
V1 在缺少 LLM 或 Embedding 配置时必须返回清晰失败结果。测试代码可以使用 mock provider,但 V1 验收链路必须通过真实 OpenAI 兼容 LLM、Embedding 和 Chroma。
|
||||
|
||||
## 5. Scenario Loader 设计
|
||||
|
||||
Agent Core 的 Scenario Loader 用于脚本、测试或后续独立服务场景。它不依赖 Django View,可以复用 Scenarios 模块的字段规范。
|
||||
|
||||
接口:
|
||||
|
||||
```python
|
||||
load_scenario(path: str) -> dict
|
||||
load_scenarios(directory: str) -> list[dict]
|
||||
```
|
||||
|
||||
## 6. RAG 设计
|
||||
|
||||
入库接口:
|
||||
|
||||
```python
|
||||
def ingest_document(
|
||||
document_id: int,
|
||||
scenario_id: str,
|
||||
source_file: str,
|
||||
text: str,
|
||||
collection: str,
|
||||
) -> IngestResult:
|
||||
"""切分文档、生成 embedding,并写入 Chroma。重新入库时覆盖同一 document_id 的旧 chunk。"""
|
||||
```
|
||||
|
||||
检索接口:
|
||||
|
||||
```python
|
||||
def retrieve(
|
||||
scenario_id: str,
|
||||
query: str,
|
||||
collection: str,
|
||||
top_k: int = 5,
|
||||
document_ids: list[int] | None = None,
|
||||
) -> list[ReferenceChunk]:
|
||||
"""按场景和可选文档范围执行向量检索,返回可审计引用片段。"""
|
||||
```
|
||||
|
||||
切分策略:
|
||||
|
||||
- 默认 chunk size 800 到 1000 字。
|
||||
- overlap 100 到 150 字。
|
||||
- metadata 包含 `scenario_id`、`document_id`、`source_file`、`chunk_id`。
|
||||
|
||||
RAG 入库和检索必须使用 Embedding Provider 与 Chroma。单元测试桩或开发阶段临时验证方案不属于 V1 验收设计。
|
||||
|
||||
## 7. Tool Registry 设计
|
||||
|
||||
工具注册:
|
||||
|
||||
```python
|
||||
registry.register("calculate_rate", calculate_rate)
|
||||
registry.get("calculate_rate")
|
||||
registry.run("calculate_rate", **kwargs)
|
||||
```
|
||||
|
||||
工具结果统一:
|
||||
|
||||
```json
|
||||
{
|
||||
"tool_name": "calculate_rate",
|
||||
"success": true,
|
||||
"arguments": {},
|
||||
"result": {},
|
||||
"error": ""
|
||||
}
|
||||
```
|
||||
|
||||
内置工具:
|
||||
|
||||
- `calculate_rate`
|
||||
- `query_demo_records`
|
||||
- `check_required_fields`
|
||||
- `generate_action_items`
|
||||
|
||||
工具函数不得直接读取 API Key 或执行无审计的外部副作用。
|
||||
|
||||
## 8. LLM Provider 设计
|
||||
|
||||
接口:
|
||||
|
||||
```python
|
||||
class LLMProvider:
|
||||
def generate(self, messages: list[dict], response_format: dict | None = None) -> LLMResponse:
|
||||
"""调用 OpenAI 兼容 Chat Completions 接口并返回统一响应对象。"""
|
||||
```
|
||||
|
||||
配置来源:
|
||||
|
||||
- `LLM_API_KEY`
|
||||
- `LLM_BASE_URL`
|
||||
- `LLM_MODEL`
|
||||
|
||||
Provider 对外隐藏供应商差异,Orchestrator 只处理 `LLMResponse.content`、`LLMResponse.model_name` 和错误信息。供应商可自主选择 OpenAI、硅基流动等 OpenAI 兼容服务。
|
||||
|
||||
Embedding Provider 接口:
|
||||
|
||||
```python
|
||||
class EmbeddingProvider:
|
||||
def embed_texts(self, texts: list[str]) -> list[list[float]]:
|
||||
"""调用 OpenAI 兼容 Embeddings 接口,返回与输入文本一一对应的向量。"""
|
||||
```
|
||||
|
||||
配置来源:
|
||||
|
||||
- `EMBEDDING_API_KEY`
|
||||
- `EMBEDDING_BASE_URL`
|
||||
- `EMBEDDING_MODEL`
|
||||
|
||||
当 `EMBEDDING_API_KEY` 或 `EMBEDDING_BASE_URL` 为空时,可以复用 `LLM_API_KEY` 和 `LLM_BASE_URL`。
|
||||
|
||||
## 9. Structured Output 设计
|
||||
|
||||
接口:
|
||||
|
||||
```python
|
||||
def parse_structured_output(raw_output: str, output_type: str) -> ParseResult:
|
||||
"""优先解析 JSON,并根据输出类型返回结构化结果或解析错误。"""
|
||||
```
|
||||
|
||||
策略:
|
||||
|
||||
- 优先解析 JSON。
|
||||
- 根据 `output_type` 做字段补齐或轻校验。
|
||||
- 失败时返回 `success=False`,保留 `raw_output`。
|
||||
- 不因结构化解析失败导致整个 Agent 流程崩溃。
|
||||
|
||||
## 10. AgentResult 设计
|
||||
|
||||
建议 dataclass:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class AgentResult:
|
||||
answer: str
|
||||
structured_output: dict
|
||||
references: list
|
||||
tool_calls: list
|
||||
raw_output: str
|
||||
model_name: str
|
||||
latency_ms: int
|
||||
status: str
|
||||
error: str = ""
|
||||
```
|
||||
|
||||
所有字段必须有默认值或构造时明确传入,保证 Audit 模块写入稳定。
|
||||
|
||||
## 11. Adapter 扩展设计
|
||||
|
||||
统一接口:
|
||||
|
||||
```python
|
||||
class AgentEngine:
|
||||
def run_agent(self, scenario_config, user_input: str, options: dict | None = None) -> AgentResult:
|
||||
"""保持与顶层 run_agent 函数一致的输入输出合约。"""
|
||||
```
|
||||
|
||||
V1 实现:
|
||||
|
||||
- `LightweightOrchestrator`
|
||||
|
||||
后续扩展:
|
||||
|
||||
- `DifyAdapter`
|
||||
- `OpenAIAgentsAdapter`
|
||||
- `LangGraphAdapter`
|
||||
|
||||
Adapter 只能替换编排实现,不能改变 Django 层依赖的 AgentResult 合约。
|
||||
|
||||
## 12. 异常处理
|
||||
|
||||
| 异常 | 处理 |
|
||||
|---|---|
|
||||
| RAG 检索失败 | 记录错误,允许继续或返回 failed |
|
||||
| 工具不存在 | 记录失败工具调用 |
|
||||
| 工具执行异常 | 捕获并返回失败工具结果 |
|
||||
| LLM 配置缺失 | 返回 failed AgentResult |
|
||||
| LLM 调用失败 | 返回 failed AgentResult |
|
||||
| JSON 解析失败 | 返回 success 但带解析错误,展示 raw output |
|
||||
|
||||
## 13. 验收标准
|
||||
|
||||
- Chat 可以调用 `run_agent()`。
|
||||
- 返回对象字段稳定完整。
|
||||
- RAG 按 `scenario_id` 隔离。
|
||||
- RAG 支持按 `document_ids` 限定本次对话的文档范围。
|
||||
- 工具调用结果格式统一。
|
||||
- LLM 与 Embedding 配置从环境变量读取。
|
||||
- 结构化解析失败不导致页面崩溃。
|
||||
- Agent Core 不依赖 Django View。
|
||||
Reference in New Issue
Block a user