feat: enhance external tool routing with domains, intents, slots and examples

main
sp mac bookpro 2605 2026-07-27 22:28:18 +08:00
parent a4f5859c81
commit 67585d17ce
3 changed files with 171 additions and 1 deletions

View File

@ -37,6 +37,11 @@ export interface ExternalToolApiRouting {
summary: string; summary: string;
useWhen: string[]; useWhen: string[];
doNotUseWhen?: string[]; doNotUseWhen?: string[];
domains?: string[];
intents?: string[];
requiredSlots?: string[];
optionalSlots?: string[];
examples?: string[];
} }
export interface ExternalToolApi { export interface ExternalToolApi {

View File

@ -36,6 +36,11 @@ const EMPTY_API: ToolApiFormValue = {
summary: '', summary: '',
useWhen: [''], useWhen: [''],
doNotUseWhen: [], doNotUseWhen: [],
domains: [],
intents: [],
requiredSlots: [],
optionalSlots: [],
examples: [],
}, },
}; };
@ -132,6 +137,11 @@ function normalizeRouting(value: ToolApiRoutingFormValue | undefined, apiName: s
summary: value.summary?.trim() || '', summary: value.summary?.trim() || '',
useWhen, useWhen,
doNotUseWhen: normalizeStringList(value.doNotUseWhen), doNotUseWhen: normalizeStringList(value.doNotUseWhen),
domains: normalizeStringList(value.domains),
intents: normalizeStringList(value.intents),
requiredSlots: normalizeStringList(value.requiredSlots),
optionalSlots: normalizeStringList(value.optionalSlots),
examples: normalizeStringList(value.examples),
}; };
} }
@ -249,6 +259,11 @@ export default function ExternalToolEditor({ open, agentId, plugin, onClose, onS
summary: item.routing?.summary || '', summary: item.routing?.summary || '',
useWhen: item.routing?.useWhen?.length ? item.routing.useWhen : [''], useWhen: item.routing?.useWhen?.length ? item.routing.useWhen : [''],
doNotUseWhen: item.routing?.doNotUseWhen || [], doNotUseWhen: item.routing?.doNotUseWhen || [],
domains: item.routing?.domains || [],
intents: item.routing?.intents || [],
requiredSlots: item.routing?.requiredSlots || [],
optionalSlots: item.routing?.optionalSlots || [],
examples: item.routing?.examples || [],
}, },
})), })),
} }
@ -469,6 +484,52 @@ export default function ExternalToolEditor({ open, agentId, plugin, onClose, onS
</Space> </Space>
)} )}
</Form.List> </Form.List>
<div className="agent-editor-tool-grid" style={{ marginTop: 16 }}>
<Form.Item label="domains领域" name={[field.name, 'routing', 'domains']}>
<Select mode="tags" placeholder="例如e-commerce, logistics" />
</Form.Item>
<Form.Item label="intents意图" name={[field.name, 'routing', 'intents']}>
<Select mode="tags" placeholder="例如query_order, cancel_order" />
</Form.Item>
</div>
<div className="agent-editor-tool-grid">
<Form.Item label="requiredSlots必填槽位" name={[field.name, 'routing', 'requiredSlots']}>
<Select mode="tags" placeholder="例如order_id, user_id" />
</Form.Item>
<Form.Item label="optionalSlots可选槽位" name={[field.name, 'routing', 'optionalSlots']}>
<Select mode="tags" placeholder="例如start_date, end_date" />
</Form.Item>
</div>
<Form.List name={[field.name, 'routing', 'examples']}>
{(exampleFields, { add: addExample, remove: removeExample }) => (
<Space direction="vertical" size={8} className="agent-editor-tool-list" style={{ marginTop: 16 }}>
<div className="agent-editor-tool-list-header">
<div>
<strong>examples</strong>
<div className="agent-editor-tool-help"></div>
</div>
<Button type="dashed" icon={<PlusOutlined />} onClick={() => addExample('')}>
</Button>
</div>
{exampleFields.map((exampleField) => (
<Space key={exampleField.key} align="start" className="agent-editor-tool-list">
<Form.Item
name={exampleField.name}
className="flex-1 mb-0"
rules={[{ validator: validateTrimmedText('示例不能为空') }]}
>
<Input placeholder="我想查一下最近的订单" style={{ width: 600 }} />
</Form.Item>
<Button danger type="text" icon={<MinusCircleOutlined />} onClick={() => removeExample(exampleField.name)} />
</Space>
))}
</Space>
)}
</Form.List>
</Card> </Card>
</> </>
) : null} ) : null}

View File

@ -227,7 +227,21 @@ export default function KnowledgeSettingsPanel({
), ),
children: ( children: (
<div className="agent-editor-api-detail"> <div className="agent-editor-api-detail">
<div>{api.description || '暂无描述'}</div> <div className="mb-2">{api.description || '暂无描述'}</div>
{api.routing?.requiredSlots?.length ? (
<div className="mb-3 p-2 bg-orange-50 border border-orange-100 rounded">
<div className="text-[10px] text-orange-400 mb-1 font-bold uppercase tracking-wider"></div>
<Space wrap size={[4, 4]}>
{api.routing.requiredSlots.map((slot) => (
<Tag key={slot} color="orange" className="m-0 border-none bg-orange-200 text-orange-700">
{slot}
</Tag>
))}
</Space>
</div>
) : null}
<div className="agent-editor-api-config-grid"> <div className="agent-editor-api-config-grid">
<div> <div>
<strong>headers</strong> <strong>headers</strong>
@ -238,6 +252,96 @@ export default function KnowledgeSettingsPanel({
<pre>{JSON.stringify(api.parametersSchema || {}, null, 2)}</pre> <pre>{JSON.stringify(api.parametersSchema || {}, null, 2)}</pre>
</div> </div>
</div> </div>
<div className="mt-3 pt-3 border-t border-gray-100">
<div className="text-[10px] text-gray-400 mb-2 font-bold uppercase tracking-wider">Routing ()</div>
<div className="space-y-3">
<div>
<div className="text-xs font-medium text-gray-600 mb-1">Summary</div>
<div className="text-xs text-gray-500">{api.routing?.summary || '-'}</div>
</div>
<div className="grid grid-cols-2 gap-4">
{api.routing?.domains?.length ? (
<div>
<div className="text-xs font-medium text-gray-600 mb-1">Domains</div>
<Space wrap size={[4, 4]}>
{api.routing.domains.map((d) => (
<Tag key={d} className="m-0 text-[10px] px-1 leading-4">
{d}
</Tag>
))}
</Space>
</div>
) : null}
{api.routing?.intents?.length ? (
<div>
<div className="text-xs font-medium text-gray-600 mb-1">Intents</div>
<Space wrap size={[4, 4]}>
{api.routing.intents.map((i) => (
<Tag key={i} className="m-0 text-[10px] px-1 leading-4">
{i}
</Tag>
))}
</Space>
</div>
) : null}
</div>
<div className="grid grid-cols-2 gap-4">
{api.routing?.useWhen?.length ? (
<div>
<div className="text-xs font-medium text-gray-600 mb-1">Use When</div>
<div className="text-[11px] text-gray-500 space-y-1">
{api.routing.useWhen.map((item, idx) => (
<div key={idx} className="flex gap-1">
<span>·</span>
<span>{item}</span>
</div>
))}
</div>
</div>
) : null}
{api.routing?.doNotUseWhen?.length ? (
<div>
<div className="text-xs font-medium text-gray-600 mb-1">Do Not Use When</div>
<div className="text-[11px] text-gray-500 space-y-1">
{api.routing.doNotUseWhen.map((item, idx) => (
<div key={idx} className="flex gap-1">
<span>·</span>
<span>{item}</span>
</div>
))}
</div>
</div>
) : null}
</div>
{api.routing?.optionalSlots?.length ? (
<div>
<div className="text-xs font-medium text-gray-600 mb-1">Optional Slots</div>
<Space wrap size={[4, 4]}>
{api.routing.optionalSlots.map((slot) => (
<Tag key={slot} className="m-0 text-[10px] px-1 leading-4">
{slot}
</Tag>
))}
</Space>
</div>
) : null}
{api.routing?.examples?.length ? (
<div>
<div className="text-xs font-medium text-gray-600 mb-1">Examples</div>
<div className="text-[11px] text-gray-500 italic space-y-1 bg-gray-50 p-2 rounded">
{api.routing.examples.map((ex, idx) => (
<div key={idx}>"{ex}"</div>
))}
</div>
</div>
) : null}
</div>
</div>
</div> </div>
), ),
})) || []} })) || []}