fix(knowledge): 修复 readDocument 文件路径拼接问题

## 问题

L1 语义检索返回文档后,尝试读取文档内容时报错:

```
读取文档失败: api/payment-errors.md
java.nio.file.NoSuchFileException: api\payment-errors.md
```

**原因**:
- `KnowledgeEntry.filePath` 存储的是相对路径(如 `api/payment-errors.md`)
- `readDocument()` 方法直接使用相对路径读取,未拼接 `knowledge_base` 前缀
- 导致找不到文件

## 修复内容

### 1. 添加 knowledge.base-path 配置

```java
@Value("${knowledge.base-path:knowledge_base}")
private String knowledgeBasePath;
```

**默认值**:`knowledge_base`(当前工作目录下)

### 2. 修复 readDocument 方法

```java
public String readDocument(String filePath, int maxChars) {
    // 拼接完整路径:knowledge_base + 相对路径
    Path fullPath = Paths.get(knowledgeBasePath, filePath);
    String content = Files.readString(fullPath);
    // ...
}
```

**修复前**:
```
读取: api/payment-errors.md
实际路径: <当前目录>/api/payment-errors.md ❌
```

**修复后**:
```
读取: api/payment-errors.md
实际路径: knowledge_base/api/payment-errors.md ✅
```

## 数据流

```
L1 语义检索
    ↓
返回 KnowledgeEntry
filePath = "api/payment-errors.md"
    ↓
readDocument("api/payment-errors.md", 2000)
    ↓
拼接路径: Paths.get("knowledge_base", "api/payment-errors.md")
    ↓
完整路径: "knowledge_base/api/payment-errors.md"
    ↓
Files.readString(fullPath)
    ↓
返回文档内容 ✅
```

## 配置

在 `application.yml` 中可以自定义路径:

```yaml
knowledge:
  base-path: ./knowledge_base  # 默认值
```

或者绝对路径:

```yaml
knowledge:
  base-path: /data/knowledge_base
```

## 验证

```bash
# 1. 启动应用
mvn spring-boot:run

# 2. 提问触发 L1
你:支付为什么会失败?

# 3. Agent 应该:
# - lookup_knowledge("支付为什么会失败")
# - L0 失败 → L1 语义检索
# - 找到 api/payment-errors.md
# - 读取文件内容成功 ✅
# - 返回文档内容给用户
```

## 相关代码路径

- `KnowledgeIndexService.readDocument()` - 文件读取
- `KnowledgeEntry.filePath` - 存储相对路径
- `LookupKnowledgeTool` - 调用 readDocument
This commit is contained in:
zhuyongxin
2026-06-25 15:56:49 +08:00
parent f02a1389c8
commit f4f0c63325
4 changed files with 11 additions and 6 deletions
View File
@@ -29,6 +29,9 @@ import java.util.stream.Stream;
@Service @Service
public class KnowledgeIndexService { public class KnowledgeIndexService {
@Value("${knowledge.base-path:knowledge_base}")
private String knowledgeBasePath;
@Autowired @Autowired
private ApiDocumentRepository apiDocumentRepository; private ApiDocumentRepository apiDocumentRepository;
@@ -195,13 +198,15 @@ public class KnowledgeIndexService {
/** /**
* 读取文档内容 * 读取文档内容
* *
* @param filePath 文件路径 * @param filePath 文件相对路径(如 api/payment-errors.md)
* @param maxChars 最大字符数 * @param maxChars 最大字符数
* @return 文档内容(前 maxChars 字符),失败返回 null * @return 文档内容(前 maxChars 字符),失败返回 null
*/ */
public String readDocument(String filePath, int maxChars) { public String readDocument(String filePath, int maxChars) {
try { try {
String content = Files.readString(Paths.get(filePath)); // 拼接完整路径:knowledge_base + 相对路径
Path fullPath = Paths.get(knowledgeBasePath, filePath);
String content = Files.readString(fullPath);
if (content.length() > maxChars) { if (content.length() > maxChars) {
return content.substring(0, maxChars) + "..."; return content.substring(0, maxChars) + "...";
@@ -210,7 +215,7 @@ public class KnowledgeIndexService {
return content; return content;
} catch (IOException e) { } catch (IOException e) {
log.error("读取文档失败: {}", filePath, e); log.error("读取文档失败: {}/{}", knowledgeBasePath, filePath, e);
return null; return null;
} }
} }
@@ -40,7 +40,7 @@ class ApiDocumentRepositoryTest {
.filePath("/uploads/api-spec.md") .filePath("/uploads/api-spec.md")
.fileHash("abc123hash") .fileHash("abc123hash")
.fileSize(1024L) .fileSize(1024L)
.faultCategory(FaultCategory.EXTERNAL_API) .faultCategory(FaultCategory.API)
.faultSource("广东") .faultSource("广东")
.apiName("查询接口") .apiName("查询接口")
.status("PENDING") .status("PENDING")
@@ -167,7 +167,7 @@ class ApiDocumentRepositoryTest {
.docId(UUID.randomUUID().toString()) .docId(UUID.randomUUID().toString())
.fileName("guangdong-api.md") .fileName("guangdong-api.md")
.faultSource("广东") .faultSource("广东")
.faultCategory(FaultCategory.EXTERNAL_API) .faultCategory(FaultCategory.API)
.build(); .build();
repository.save(doc); repository.save(doc);
@@ -38,7 +38,7 @@ class DiagnosisRecordRepositoryTest {
.sessionId("session-001") .sessionId("session-001")
.businessId("order-12345") .businessId("order-12345")
.traceId("trace-abc123") .traceId("trace-abc123")
.faultCategory(FaultCategory.EXTERNAL_API) .faultCategory(FaultCategory.API)
.faultSource("广东") .faultSource("广东")
.faultTarget("http://api.example.com/query") .faultTarget("http://api.example.com/query")
.errorCode("40003") .errorCode("40003")