Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 51 additions & 28 deletions docs/mkdocs/en/knowledge.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,34 +183,35 @@ kb := knowledge.New(
knowledge.WithEmbedder(embedder),
)

// Note: Metadata fields must use the metadata. prefix
filterCondition := &searchfilter.UniversalFilterCondition{
Operator: searchfilter.OperatorAnd,
Value: []*searchfilter.UniversalFilterCondition{
{
Field: "tag",
Field: "metadata.tag", // Metadata fields use metadata. prefix
Operator: searchfilter.OperatorEqual,
Value: "tag",
},
{
Field: "age",
Field: "metadata.age",
Operator: searchfilter.OperatorGreaterThanOrEqual,
Value: 18,
},
{
Field: "create_time",
Field: "metadata.create_time",
Operator: searchfilter.OperatorBetween,
Value: []string{"2024-10-11 12:11:00", "2025-10-11 12:11:00"},
},
{
Operator: searchfilter.OperatorOr,
Value: []*searchfilter.UniversalFilterCondition{
{
Field: "login_time",
Field: "metadata.login_time",
Operator: searchfilter.OperatorLessThanOrEqual,
Value: "2025-01-11 12:11:00",
},
{
Field: "status",
Field: "metadata.status",
Operator: searchfilter.OperatorEqual,
Value: "logout",
},
Expand Down Expand Up @@ -828,9 +829,9 @@ llmAgent := llmagent.New(
"source": "official", // Official source
"category": "documentation", // Documentation category
}),
// Agent-level complex condition filter
// Agent-level complex condition filter (metadata fields use metadata. prefix)
llmagent.WithKnowledgeConditionedFilter(
searchfilter.Equal("status", "published"), // Published status
searchfilter.Equal("metadata.status", "published"), // Published status
),
)

Expand All @@ -844,12 +845,12 @@ eventCh, err := runner.Run(
}),
// Runner-level complex condition filter
agent.WithKnowledgeConditionedFilter(
searchfilter.GreaterThan("priority", 5), // Priority greater than 5
searchfilter.GreaterThan("metadata.priority", 5), // Priority greater than 5
),
)

// 3. LLM intelligent filter (dynamically generated by LLM)
// Example: User asks "find API related docs", LLM might generate {"topic": "api"}
// Example: User asks "find API related docs", LLM might generate {"field": "metadata.topic", "value": "api"}

// Final effective filter conditions (all combined with AND):
// source = "official" AND
Expand All @@ -873,11 +874,11 @@ searchTool := tool.NewKnowledgeSearchTool(
tool.WithFilter(map[string]interface{}{
"source": "official",
}),
// Agent-level complex condition filter
// Agent-level complex condition filter (metadata fields use metadata. prefix)
tool.WithConditionedFilter(
searchfilter.Or(
searchfilter.Equal("topic", "programming"),
searchfilter.Equal("topic", "llm"),
searchfilter.Equal("metadata.topic", "programming"),
searchfilter.Equal("metadata.topic", "llm"),
),
),
)
Expand All @@ -889,35 +890,57 @@ llmAgent := llmagent.New(
)

// Final filter conditions:
// source = "official" AND (topic = "programming" OR topic = "llm")
// metadata.source = "official" AND (metadata.topic = "programming" OR metadata.topic = "llm")
// i.e., must be official source AND topic is either programming or LLM
```

##### Filter Field Naming Convention

When using `FilterCondition`, **metadata fields must use the `metadata.` prefix**:

```go
// ✅ Correct: Use metadata. prefix
searchfilter.Equal("metadata.topic", "programming")
searchfilter.Equal("metadata.category", "documentation")

// ❌ Wrong: Missing metadata. prefix
searchfilter.Equal("topic", "programming")
```

> **Notes**:
> - The `metadata.` prefix distinguishes metadata fields from system fields (e.g., `id`, `name`, `content`)
> - If you customized the metadata field name via `WithMetadataField()`, still use the `metadata.` prefix; the framework will automatically convert it to the actual field name
> - System fields (`id`, `name`, `content`, `created_at`, `updated_at`) use the field name directly without prefix

##### Common Filter Helper Functions

```go
// Comparison operators
searchfilter.Equal(field, value) // field = value
searchfilter.NotEqual(field, value) // field != value
searchfilter.GreaterThan(field, value) // field > value
searchfilter.GreaterThanOrEqual(field, value) // field >= value
searchfilter.LessThan(field, value) // field < value
searchfilter.LessThanOrEqual(field, value) // field <= value
searchfilter.In(field, values...) // field IN (...)
searchfilter.NotIn(field, values...) // field NOT IN (...)
searchfilter.Like(field, pattern) // field LIKE pattern
searchfilter.Between(field, min, max) // field BETWEEN min AND max
// Comparison operators (Note: metadata fields need metadata. prefix)
searchfilter.Equal("metadata.topic", value) // metadata.topic = value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里可以加一个searchfilter.GreaterThanOrEqual("updated_at", value),表示使用外层字段, 区别于metadata字段的过滤场景

searchfilter.NotEqual("metadata.status", value) // metadata.status != value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

也可以在加一个searchfilter.NotEqual("status", value),表示用户自定义表结构时使用的查询字段

searchfilter.GreaterThan("metadata.priority", value) // metadata.priority > value
searchfilter.GreaterThanOrEqual("metadata.score", value) // metadata.score >= value
searchfilter.LessThan("metadata.age", value) // metadata.age < value
searchfilter.LessThanOrEqual("metadata.level", value) // metadata.level <= value
searchfilter.In("metadata.category", values...) // metadata.category IN (...)
searchfilter.NotIn("metadata.type", values...) // metadata.type NOT IN (...)
searchfilter.Like("metadata.title", pattern) // metadata.title LIKE pattern
searchfilter.Between("metadata.date", min, max) // metadata.date BETWEEN min AND max

// System fields don't need prefix
searchfilter.Equal("id", "doc-123") // id = "doc-123"
searchfilter.In("name", "doc1", "doc2") // name IN ("doc1", "doc2")

// Logical operators
searchfilter.And(conditions...) // AND combination
searchfilter.Or(conditions...) // OR combination

// Nested example: (status = 'published') AND (category = 'doc' OR category = 'tutorial')
// Nested example: (metadata.status = 'published') AND (metadata.category = 'doc' OR metadata.category = 'tutorial')
searchfilter.And(
searchfilter.Equal("status", "published"),
searchfilter.Equal("metadata.status", "published"),
searchfilter.Or(
searchfilter.Equal("category", "documentation"),
searchfilter.Equal("category", "tutorial"),
searchfilter.Equal("metadata.category", "documentation"),
searchfilter.Equal("metadata.category", "tutorial"),
),
)
```
Expand Down
93 changes: 58 additions & 35 deletions docs/mkdocs/zh/knowledge.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,34 +180,35 @@ kb := knowledge.New(
knowledge.WithEmbedder(embedder),
)

// 注意:元数据字段需要使用 metadata. 前缀
filterCondition := &searchfilter.UniversalFilterCondition{
Operator: searchfilter.OperatorAnd,
Value: []*searchfilter.UniversalFilterCondition{
{
Field: "tag",
Field: "metadata.tag", // 元数据字段使用 metadata. 前缀
Operator: searchfilter.OperatorEqual,
Value: "tag",
},
{
Field: "age",
Field: "metadata.age",
Operator: searchfilter.OperatorGreaterThanOrEqual,
Value: 18,
},
{
Field: "create_time",
Field: "metadata.create_time",
Operator: searchfilter.OperatorBetween,
Value: []string{"2024-10-11 12:11:00", "2025-10-11 12:11:00"},
},
{
Operator: searchfilter.OperatorOr,
Value: []*searchfilter.UniversalFilterCondition{
{
Field: "login_time",
Field: "metadata.login_time",
Operator: searchfilter.OperatorLessThanOrEqual,
Value: "2025-01-11 12:11:00",
},
{
Field: "status",
Field: "metadata.status",
Operator: searchfilter.OperatorEqual,
Value: "logout",
},
Expand Down Expand Up @@ -826,9 +827,9 @@ llmAgent := llmagent.New(
"source": "official", // 官方来源
"category": "documentation", // 文档类别
}),
// Agent 级复杂条件过滤器
// Agent 级复杂条件过滤器(元数据字段使用 metadata. 前缀)
llmagent.WithKnowledgeConditionedFilter(
searchfilter.Equal("status", "published"), // 已发布状态
searchfilter.Equal("metadata.status", "published"), // 已发布状态
),
)

Expand All @@ -842,21 +843,21 @@ eventCh, err := runner.Run(
}),
// Runner 级复杂条件过滤器
agent.WithKnowledgeConditionedFilter(
searchfilter.GreaterThan("priority", 5), // 优先级大于 5
searchfilter.GreaterThan("metadata.priority", 5), // 优先级大于 5
),
)

// 3. LLM 智能过滤器(由 LLM 动态生成)
// 例如:用户问 "查找 API 相关文档",LLM 可能生成 {"topic": "api"}
// 例如:用户问 "查找 API 相关文档",LLM 可能生成 {"field": "metadata.topic", "value": "api"}

// 最终生效的过滤条件(所有条件通过 AND 组合):
// source = "official" AND
// category = "documentation" AND
// status = "published" AND
// region = "china" AND
// language = "zh" AND
// priority > 5 AND
// topic = "api"
// metadata.source = "official" AND
// metadata.category = "documentation" AND
// metadata.status = "published" AND
// metadata.region = "china" AND
// metadata.language = "zh" AND
// metadata.priority > 5 AND
// metadata.topic = "api"
//
// 即:必须同时满足所有层级的所有条件
```
Expand All @@ -871,11 +872,11 @@ searchTool := tool.NewKnowledgeSearchTool(
tool.WithFilter(map[string]any{
"source": "official",
}),
// Agent 级复杂条件过滤器
// Agent 级复杂条件过滤器(元数据字段使用 metadata. 前缀)
tool.WithConditionedFilter(
searchfilter.Or(
searchfilter.Equal("topic", "programming"),
searchfilter.Equal("topic", "llm"),
searchfilter.Equal("metadata.topic", "programming"),
searchfilter.Equal("metadata.topic", "llm"),
),
),
)
Expand All @@ -887,35 +888,57 @@ llmAgent := llmagent.New(
)

// 最终过滤条件:
// source = "official" AND (topic = "programming" OR topic = "llm")
// metadata.source = "official" AND (metadata.topic = "programming" OR metadata.topic = "llm")
// 即:必须是官方来源,且主题是编程或 LLM
```

##### 过滤器字段命名规范

使用 `FilterCondition` 时,**元数据字段必须使用 `metadata.` 前缀**:

```go
// ✅ 正确:使用 metadata. 前缀
searchfilter.Equal("metadata.topic", "programming")
searchfilter.Equal("metadata.category", "documentation")

// ❌ 错误:缺少 metadata. 前缀
searchfilter.Equal("topic", "programming")
```

> **说明**:
> - `metadata.` 前缀用于区分元数据字段和系统字段(如 `id`、`name`、`content` 等)
> - 如果通过 `WithMetadataField()` 自定义了元数据字段名,仍然使用 `metadata.` 前缀,框架会自动转换为实际的字段名
> - 系统字段(`id`、`name`、`content`、`created_at`、`updated_at`)直接使用字段名,无需前缀

##### 常用过滤器辅助函数

```go
// 比较操作符
searchfilter.Equal(field, value) // field = value
searchfilter.NotEqual(field, value) // field != value
searchfilter.GreaterThan(field, value) // field > value
searchfilter.GreaterThanOrEqual(field, value) // field >= value
searchfilter.LessThan(field, value) // field < value
searchfilter.LessThanOrEqual(field, value) // field <= value
searchfilter.In(field, values...) // field IN (...)
searchfilter.NotIn(field, values...) // field NOT IN (...)
searchfilter.Like(field, pattern) // field LIKE pattern
searchfilter.Between(field, min, max) // field BETWEEN min AND max
// 比较操作符(注意:元数据字段需要 metadata. 前缀)
searchfilter.Equal("metadata.topic", value) // metadata.topic = value
searchfilter.NotEqual("metadata.status", value) // metadata.status != value
searchfilter.GreaterThan("metadata.priority", value) // metadata.priority > value
searchfilter.GreaterThanOrEqual("metadata.score", value) // metadata.score >= value
searchfilter.LessThan("metadata.age", value) // metadata.age < value
searchfilter.LessThanOrEqual("metadata.level", value) // metadata.level <= value
searchfilter.In("metadata.category", values...) // metadata.category IN (...)
searchfilter.NotIn("metadata.type", values...) // metadata.type NOT IN (...)
searchfilter.Like("metadata.title", pattern) // metadata.title LIKE pattern
searchfilter.Between("metadata.date", min, max) // metadata.date BETWEEN min AND max

// 系统字段不需要前缀
searchfilter.Equal("id", "doc-123") // id = "doc-123"
searchfilter.In("name", "doc1", "doc2") // name IN ("doc1", "doc2")

// 逻辑操作符
searchfilter.And(conditions...) // AND 组合
searchfilter.Or(conditions...) // OR 组合

// 嵌套示例:(status = 'published') AND (category = 'doc' OR category = 'tutorial')
// 嵌套示例:(metadata.status = 'published') AND (metadata.category = 'doc' OR metadata.category = 'tutorial')
searchfilter.And(
searchfilter.Equal("status", "published"),
searchfilter.Equal("metadata.status", "published"),
searchfilter.Or(
searchfilter.Equal("category", "documentation"),
searchfilter.Equal("category", "tutorial"),
searchfilter.Equal("metadata.category", "documentation"),
searchfilter.Equal("metadata.category", "tutorial"),
),
)
```
Expand Down
3 changes: 3 additions & 0 deletions examples/knowledge/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export OPENAI_BASE_URL="http://wujipt.woa.com/api/v1"
export OPENAI_API_KEY="2fe60304d5192f22"
export MODEL_NAME="qwen3-omni-30b-a3b-thinking"
44 changes: 44 additions & 0 deletions examples/knowledge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Knowledge Examples

Knowledge-enhanced AI agents examples.

## Environment

```bash
export OPENAI_BASE_URL=xxx
export OPENAI_API_KEY=xxx
export MODEL_NAME=xxx
```

## Examples

### basic/
Basic example with file source and in-memory vector store.

### sources/
Different data source types:

| Case | Description |
|------|-------------|
| file-source/ | Load individual files (Markdown, PDF, DOCX, CSV, JSON) |
| directory-source/ | Recursively load entire directory |
| url-source/ | Fetch and parse web pages |
| auto-source/ | Auto-detect source type from path |

### vectorstores/
Persistent vector storage options:

| Case | Description | Extra Environment |
|------|-------------|-------------------|
| postgres/ | PostgreSQL with pgvector extension | `PGVECTOR_HOST`, `PGVECTOR_PORT`, `PGVECTOR_USER`, `PGVECTOR_PASSWORD`, `PGVECTOR_DATABASE` |
| elasticsearch/ | Elasticsearch (v7/v8/v9) | `ELASTICSEARCH_HOSTS`, `ELASTICSEARCH_USERNAME`, `ELASTICSEARCH_PASSWORD` |
| tcvector/ | Tencent VectorDB | `TCVECTOR_URL`, `TCVECTOR_USERNAME`, `TCVECTOR_PASSWORD` |

### features/
Advanced features:

| Case | Description |
|------|-------------|
| agentic-filter/ | LLM automatically generates metadata filter based on user query |
| metadata-filter/ | Programmatic metadata filtering with AND/OR/NOT operations |
| management/ | Dynamic source management: AddSource, RemoveSource, ReloadSource |
Loading
Loading