260 lines
6.3 KiB
Markdown
260 lines
6.3 KiB
Markdown
# 智能核心模块详细设计
|
||
|
||
## 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。
|