elksmmx 1 день назад
Родитель
Сommit
5c60c7d7d1

+ 2 - 0
examples/process_pipeline/prompts/apply_to_grounding.prompt

@@ -20,6 +20,8 @@
 - 优先以最能代表 query 核心语义的真实 category_path 为基准;如果实质和形式都存在,通常优先以实质路径为基准,形式路径只作为命名和细化参考。
 - 从根往下逐层检查基准 category_path 的每一层,判断该层是否仍然准确描述了 query 的语义。
 - 找到第一个"不够精确或有偏差"的层级,从该层级开始续写(替换该层及其后的所有层)。续写的风格应当与路径中的其他层级相符合,至少词性应当一致。
+- suggest_apply_to 的所有层级必须保持同一种内容类型,不能在一条路径里混入另一套语义体系。
+- suggest_apply_to 必须满足根节点已决定的 source_type 分类:实质路径中不能出现形式类词汇,形式路径中不能出现实质类词汇;实质和形式各自有不同的命名规则、用词习惯和层级粒度,生成时必须仔细观察同 source_type 的真实路径后再续写。
 - 如果基准 category_path 所有层级都准确,且 query 没有更细的信息,则 suggest_apply_to = 基准 category_path。
 - 如果基准 category_path 所有层级都准确,但 query 还有更细的信息未体现,则在末尾续写 1-3 个层级。
 - 续写部分命名风格参考下方"邻近路径参考"(两字名词、层级粒度保持一致)。

+ 43 - 68
examples/process_pipeline/ui/app.js

@@ -2269,9 +2269,9 @@ window.renderStructuredData = function(items, type, parentItem = null) {
         
         // Tree node tags (from apply_to keys) or unstructured_what fallback
         const getApplyToField = (it) => {
-            if (it.apply_to_grounding) return { key: 'apply_to_grounding', val: it.apply_to_grounding };
-            if (it.apply_to_draft) return { key: 'apply_to_draft', val: it.apply_to_draft };
-            if (it.apply_to) return { key: 'apply_to', val: it.apply_to };
+            if (it.apply_to_grounding) return { key: 'apply_to_grounding', val: it.apply_to_grounding, suggest: it.suggest_apply_to };
+            if (it.apply_to_draft) return { key: 'apply_to_draft', val: it.apply_to_draft, suggest: null };
+            if (it.apply_to) return { key: 'apply_to', val: it.apply_to, suggest: it.suggest_apply_to };
             return null;
         };
         const applyToData = getApplyToField(item);
@@ -2310,17 +2310,32 @@ window.renderStructuredData = function(items, type, parentItem = null) {
             </div>
         `;
         
-        const renderApplyToVal = (valObj, isIdeal = false) => {
+        const renderApplyToVal = (valObj, suggestApplyTo = null) => {
             if (!valObj || typeof valObj !== 'object') return '-';
+            const escapeApplyToText = (s) => String(s).replace(/</g, '&lt;').replace(/>/g, '&gt;');
+            const renderPathParts = (pathValue, highlight = false) => {
+                const pathStr = String(pathValue || '').trim();
+                if (!pathStr) return '';
+                const parts = pathStr.split('/');
+                const leaf = parts.pop();
+                const prefix = parts.length > 0 ? parts.join('/') + '/' : '';
+                const leafStyle = highlight
+                    ? 'background:#eff6ff; color:#2563eb; border:1px solid #bfdbfe;'
+                    : '';
+                return `
+                    ${prefix ? `<span class="apply-to-path-prefix">${escapeApplyToText(prefix)}</span>` : ''}
+                    <span class="apply-to-path-leaf" style="${leafStyle}">${escapeApplyToText(leaf)}</span>
+                `;
+            };
             let res = '<div style="display:flex; flex-direction:column; gap:6px;">';
+            let hasRows = false;
             Object.entries(valObj).forEach(([k, v]) => {
                 if (Array.isArray(v) && v.length > 0) {
+                    hasRows = true;
                     res += `<div class="apply-to-subrow">
-                        <span class="apply-to-key-badge" style="background: rgba(0,0,0,0.05); color: #475569;">${k}</span>
+                        <span class="apply-to-key-badge" style="background: rgba(0,0,0,0.05); color: #475569;">${escapeApplyToText(k)}</span>
                         <div class="apply-to-values" style="display:flex; flex-wrap:wrap; gap:4px;">`;
-                    
-                    let idealBadges = [];
-                    
+
                     v.forEach(pathObj => {
                         let pathStr = '';
                         let elementStr = '';
@@ -2336,87 +2351,46 @@ window.renderStructuredData = function(items, type, parentItem = null) {
                             tooltipHtml = `
                                 <div class="apply-to-tooltip">
                                     ${pathObj.category_id ? `<span class="tooltip-id">id: ${pathObj.category_id}</span>` : ''}
-                                    <span class="tooltip-rationale">${pathObj.rationale.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</span>
+                                    <span class="tooltip-rationale">${escapeApplyToText(pathObj.rationale)}</span>
                                 </div>
                             `;
                         }
 
                         let htmlParts = '';
                         if (pathStr && elementStr) {
-                            htmlParts = `<span class="apply-to-path-prefix">${pathStr}</span><span class="apply-to-path-leaf" style="margin-left: 4px;">${elementStr}</span>`;
+                            htmlParts = `<span class="apply-to-path-prefix">${escapeApplyToText(pathStr)}</span><span class="apply-to-path-leaf" style="margin-left: 4px;">${escapeApplyToText(elementStr)}</span>`;
                         } else if (pathStr) {
-                            const parts = pathStr.split('/');
-                            const leaf = parts.pop();
-                            const prefix = parts.length > 0 ? parts.join('/') + '/' : '';
-                            htmlParts = `
-                                ${prefix ? `<span class="apply-to-path-prefix">${prefix}</span>` : ''}
-                                <span class="apply-to-path-leaf">${leaf}</span>
-                            `;
+                            htmlParts = renderPathParts(pathStr);
                         }
                         
                         if (htmlParts) {
                             res += `<span class="apply-to-path-item has-tooltip">${htmlParts}${tooltipHtml}</span>`;
                         }
-                        
-                        if (typeof pathObj === 'object' && pathObj !== null && pathObj.ideal_path) {
-                            let fullNormalPath = pathStr || '';
-                            if (elementStr) {
-                                if (!fullNormalPath.endsWith('/')) fullNormalPath += '/';
-                                fullNormalPath += elementStr;
-                            }
-                            
-                            if (fullNormalPath !== pathObj.ideal_path) {
-                                const normalParts = fullNormalPath.split('/');
-                                const idealParts = pathObj.ideal_path.split('/');
-                                const idealLeaf = idealParts.pop();
-                                
-                                let prefixHtml = '';
-                                if (idealParts.length > 0) {
-                                    prefixHtml += `<span class="apply-to-path-prefix">`;
-                                    for (let i = 0; i < idealParts.length; i++) {
-                                        if (i === 0 && idealParts[0] === '') {
-                                            prefixHtml += '/';
-                                            continue;
-                                        }
-                                        const p = idealParts[i];
-                                        const isNew = i >= normalParts.length || p !== normalParts[i];
-                                        if (isNew) {
-                                            prefixHtml += `<span style="color:#3b82f6; font-weight:600;">${p}</span>/`;
-                                        } else {
-                                            prefixHtml += `${p}/`;
-                                        }
-                                    }
-                                    prefixHtml += `</span>`;
-                                }
-                                
-                                const isLeafNew = idealParts.length >= normalParts.length || idealLeaf !== normalParts[idealParts.length];
-                                
-                                const leafHtml = isLeafNew 
-                                    ? `<span class="apply-to-path-leaf" style="background:#eff6ff; color:#2563eb; border:1px solid #bfdbfe;">${idealLeaf}</span>`
-                                    : `<span class="apply-to-path-leaf" style="background:#f1f5f9; color:#475569; border:1px solid #cbd5e1;">${idealLeaf}</span>`;
-
-                                const idealHtml = `${prefixHtml}${leafHtml}`;
-                                idealBadges.push(`<span class="apply-to-path-item has-tooltip" style="border: 2px dashed #94a3b8; background: transparent; margin-top: 4px;">${idealHtml}${tooltipHtml}</span>`);
-                            }
-                        }
                     });
-                    
-                    if (idealBadges.length > 0) {
-                        res += `<div style="flex-basis: 100%; height: 0; margin: 0;"></div>` + idealBadges.join('');
-                    }
-                    
+
                     res += `</div></div>`;
                 }
             });
+
+            if (typeof suggestApplyTo === 'string' && suggestApplyTo.trim()) {
+                hasRows = true;
+                res += `<div class="apply-to-subrow">
+                    <span class="apply-to-key-badge" style="background:#eff6ff; color:#2563eb; border-color:#bfdbfe;">最优</span>
+                    <div class="apply-to-values" style="display:flex; flex-wrap:wrap; gap:4px;">
+                        <span class="apply-to-path-item" style="border: 2px dashed #94a3b8; background: transparent;">${renderPathParts(suggestApplyTo, true)}</span>
+                    </div>
+                </div>`;
+            }
+
             res += `</div>`;
-            return res === '<div style="display:flex; flex-direction:column; gap:6px;"></div>' ? '-' : res;
+            return hasRows ? res : '-';
         };
 
         // Render apply_to / apply_to_grounding at workflow level (if it exists)
         if (applyToData && typeof applyToData.val === 'object' && Object.keys(applyToData.val).length > 0) {
             html += `<div class="structured-row">
                 <div class="structured-label">${applyToData.key}</div>
-                <div class="structured-value">${renderApplyToVal(applyToData.val)}</div>
+                <div class="structured-value">${renderApplyToVal(applyToData.val, applyToData.suggest)}</div>
             </div>`;
         }
         
@@ -2653,6 +2627,7 @@ window.renderStructuredData = function(items, type, parentItem = null) {
             const matchedFragments = new Set();
             const renderFragmentColumns = (fragment) => {
                 const applyTo = fragment && (fragment.apply_to_draft || fragment.apply_to_grounding || fragment.apply_to);
+                const suggestApplyTo = fragment && fragment.apply_to_draft ? null : fragment && fragment.suggest_apply_to;
                 return `
                     <td class="fragment-cell" style="font-family: monospace;">
                         <span class="row-expand-icon">▶</span>
@@ -2662,7 +2637,7 @@ window.renderStructuredData = function(items, type, parentItem = null) {
                     <td class="fragment-cell">${fragment && fragment.inputs && fragment.inputs.length > 0 ? renderDataObjList(fragment.inputs) : '-'}</td>
                     <td class="fragment-cell">${fragment && fragment.outputs && fragment.outputs.length > 0 ? renderDataObjList(fragment.outputs) : '-'}</td>
                     <td class="fragment-cell"><div class="fragment-clamp">${fragment ? renderEffects(fragment.effects) : '-'}</div></td>
-                    <td class="fragment-cell" style="font-size:0.9em;"><div class="fragment-clamp">${applyTo ? renderApplyToVal(applyTo) : '-'}</div></td>
+                    <td class="fragment-cell" style="font-size:0.9em;"><div class="fragment-clamp">${applyTo ? renderApplyToVal(applyTo, suggestApplyTo) : '-'}</div></td>
                     <td class="fragment-cell"><div class="fragment-clamp">${fragment ? renderTools(fragment.tools) : '-'}</div></td>
                     <td class="fragment-cell"><div class="fragment-clamp fragment-text">${fragment && fragment.body ? escapeHtml(fragment.body) : '-'}</div></td>
                 `;
@@ -2793,7 +2768,7 @@ window.renderStructuredData = function(items, type, parentItem = null) {
         // Dynamic fallback for any other unhandled keys
         const handledKeys = [
             'method', 'name', 'action', 'unstructured_what', 'apply_to_grounding', 'apply_to_draft', 'apply_to', 
-            'stage', 'effects', 'body', 'steps', 'inputs', 'outputs', 'tools'
+            'suggest_apply_to', 'stage', 'effects', 'body', 'steps', 'inputs', 'outputs', 'tools'
         ];
         
         Object.keys(item).forEach(k => {

+ 23 - 8
examples/process_pipeline/ui/scratchpad.js

@@ -22,10 +22,26 @@ function renderStructuredData(items, type) {
         
         // Render apply_to or apply_to_draft
         const applyTo = item.apply_to_draft || item.apply_to;
+        const suggestApplyTo = item.apply_to_draft ? null : item.suggest_apply_to;
         if (applyTo && typeof applyTo === 'object' && Object.keys(applyTo).length > 0) {
             html += `<div class="structured-row">
                 <div class="structured-label">apply_to</div>
                 <div class="structured-value" style="display:flex; flex-direction:column; gap:6px;">`;
+
+            const renderPathBadge = (path, highlight = false) => {
+                const pathStr = typeof path === 'object' && path !== null
+                    ? (path.element || path.category_path || path.path || '')
+                    : String(path || '');
+                if (!pathStr) return '';
+                const parts = pathStr.split('/');
+                const leaf = parts.pop();
+                const prefix = parts.length > 0 ? parts.join('/') + '/' : '';
+                const leafStyle = highlight ? 'background:#eff6ff; color:#2563eb; border:1px solid #bfdbfe;' : '';
+                return `<span class="apply-to-path-item" ${highlight ? 'style="border: 2px dashed #94a3b8; background: transparent;"' : ''}>
+                    ${prefix ? `<span class="apply-to-path-prefix">${prefix}</span>` : ''}
+                    <span class="apply-to-path-leaf" style="${leafStyle}">${leaf}</span>
+                </span>`;
+            };
             
             Object.entries(applyTo).forEach(([k, v]) => {
                 if (Array.isArray(v) && v.length > 0) {
@@ -33,18 +49,17 @@ function renderStructuredData(items, type) {
                         <span class="apply-to-key-badge">${k}</span>
                         <div class="apply-to-values">`;
                     v.forEach(path => {
-                        // split path to style the leaf node
-                        const parts = path.split('/');
-                        const leaf = parts.pop();
-                        const prefix = parts.length > 0 ? parts.join('/') + '/' : '';
-                        html += `<span class="apply-to-path-item">
-                            ${prefix ? `<span class="apply-to-path-prefix">${prefix}</span>` : ''}
-                            <span class="apply-to-path-leaf">${leaf}</span>
-                        </span>`;
+                        html += renderPathBadge(path);
                     });
                     html += `</div></div>`;
                 }
             });
+            if (typeof suggestApplyTo === 'string' && suggestApplyTo.trim()) {
+                html += `<div class="apply-to-subrow">
+                    <span class="apply-to-key-badge">最优</span>
+                    <div class="apply-to-values">${renderPathBadge(suggestApplyTo, true)}</div>
+                </div>`;
+            }
             html += `</div></div>`;
         }