import { useEffect, useRef, useState } from 'react' import { Link } from 'wouter' import CodeEditor from '@/components/CodeEditor' import { Heading } from '@/components/Heading' import { useLanguage } from '@/i18n/context' // --------------------------------------------------------------------------- // Data: each section is a typed object for easy extension // --------------------------------------------------------------------------- interface TroubleshootingSection { id: string title: { en: string; zh: string } symptom: { en: string; zh: string } color: 'red' | 'amber' | 'orange' | 'violet' content: (isZh: boolean) => React.ReactNode } const SECTIONS: TroubleshootingSection[] = [ { id: 'format-errors', title: { en: 'Model Response Format Errors', zh: '模型返回格式错误' }, symptom: { en: 'The model returns malformed tool calls, plain text, or unexpected JSON instead of structured actions.', zh: '模型返回了格式错误的 tool call、纯文本或非预期的 JSON,而非结构化的操作指令。', }, color: 'amber', content: FormatErrorsContent, }, { id: 'low-success-rate', title: { en: 'Low Task Success Rate', zh: '任务成功率低' }, symptom: { en: 'The agent appears to understand the task but frequently fails to complete it, or produces incorrect results.', zh: 'Agent 似乎理解了任务,但频繁执行失败或产生不正确的结果。', }, color: 'amber', content: LowSuccessRateContent, }, { id: 'wrong-element', title: { en: "Can't Hit Target Elements", zh: '无法点击目标元素' }, symptom: { en: 'The agent repeatedly retries but keeps interacting with the wrong element, or fails to locate the correct one.', zh: 'Agent 反复重试,但始终点击在错误的元素上,或无法定位到正确的目标元素。', }, color: 'amber', content: WrongElementContent, }, { id: 'api-errors', title: { en: 'API Request Errors', zh: 'API 请求错误' }, symptom: { en: 'HTTP 400 Bad Request or similar errors when calling the LLM API.', zh: '调用 LLM API 时出现 HTTP 400 Bad Request 或类似的参数错误。', }, color: 'amber', content: ApiErrorsContent, }, ] // --------------------------------------------------------------------------- // Section content components // --------------------------------------------------------------------------- function FormatErrorsContent(isZh: boolean) { return (
  1. {isZh ? '确认模型是否支持' : 'Verify model compatibility'}

    {isZh ? '并非所有模型都能正确处理 page-agent 的 tool 定义。请查看' : 'Not all models can handle page-agent tool definitions correctly. Check the '} {isZh ? '已测试模型列表' : 'tested models list'} {isZh ? '。' : '.'}

  2. {isZh ? '检查代理/网关的参数转发' : 'Check proxy/gateway parameter forwarding'}

    {isZh ? '如果使用了 API 代理或网关,请确保请求中的 ' : 'If using an API proxy or gateway, make sure the '} tools {isZh ? ' 字段被完整、无修改地转发给模型供应商。部分代理可能会剥离或修改此字段。' : ' parameter is forwarded to the model provider intact. Some proxies may strip or alter this field.'}

  3. {isZh ? '寻求社区帮助' : 'Get community help'}

    {isZh ? ( <> 如果以上步骤无法解决问题,欢迎在{' '} GitHub Discussions {' '} 中反馈,附上模型名称和错误信息。 ) : ( <> If the above steps don't help, join the{' '} GitHub Discussions {' '} with your model name and error details. )}

) } function LowSuccessRateContent(isZh: boolean) { return ( <>

{isZh ? '按以下顺序逐步排查,从最简单的情况开始:' : 'Follow this diagnostic funnel from simplest to most advanced:'}

  1. {isZh ? '先从简单指令开始' : 'Start with a simple instruction'}

    {isZh ? '给一个具体的、单步的简单指令(如"点击登录按钮"),看 Agent 能否完成。如果连简单操作都失败了,问题可能不在模型能力上。' : 'Give a concrete, single-step instruction (e.g. "click the login button"). If even simple actions fail, the issue is likely not model capability.'}

  2. {isZh ? '尝试最强模型' : 'Try the strongest model available'}

    {isZh ? '切换到你能获取到的最先进、最大的模型,以排除是否是模型智能水平不足导致的问题。' : "Switch to the most capable model you have access to, to isolate whether it's a model intelligence issue."}

  3. {isZh ? '优化指令质量' : 'Improve instruction quality'}

    {isZh ? '给出尽可能具体的指令。对于复杂任务,建议使用另一个 LLM 来预先拆分和细化用户的需求,然后逐步执行。' : "Be as specific as possible. For complex tasks, consider using another LLM to decompose and refine the user's request before execution."}

  4. {isZh ? '提供充足的上下文' : 'Provide sufficient context'}

    {isZh ? '通过 instructions 配置注入网站背景描述、关键术语解释等上下文信息,帮助 Agent 更好地理解页面。' : 'Use the instructions config to inject website descriptions, key terminology, and background context to help the agent understand the page.'}

  5. {isZh ? '检查 HTML 清洗结果' : 'Check HTML sanitization output'}

    {isZh ? '使用开发者工具检查清洗后的 HTML,确认关键信息、文本和可操作元素是否被正确保留。' : 'Inspect the sanitized HTML in dev tools to confirm that key information, text, and interactive elements are preserved correctly.'}

) } function WrongElementContent(isZh: boolean) { return (
  1. {isZh ? '了解现实局限' : 'Understand the reality'}

    {isZh ? '并非所有网站都提供了完善的语义化 HTML 和 accessibility 标签。对于此类网站,DOM 清洗可能无法产出足够好的结果。' : 'Not all websites provide proper semantic HTML and accessibility labels. For such sites, DOM sanitization may not produce good enough results.'}

  2. {isZh ? '检查目标元素类型' : 'Check target element type'}

    {isZh ? '确认目标元素是否为图片、Canvas、或需要复杂交互(如拖拽、基于坐标的点击)的元素。这些本身就超出了当前的能力范围。' : 'Verify if the target is an image, Canvas, or requires complex interactions (drag-and-drop, coordinate-based clicking). These are beyond current capabilities.'}

  3. {isZh ? '检查清洗后的 HTML' : 'Inspect sanitized HTML'}

    {isZh ? '检查清洗结果中是否存在关键信息丢失、可操作元素未被编号等问题。' : 'Look for missing key information or unnumbered interactive elements in the sanitized output.'}

  4. {isZh ? '注入 accessibility 增强' : 'Inject accessibility improvements'}

    {isZh ? '通过注入脚本为网站添加 aria-label、语义化标签等 accessibility 属性,改善 DOM 清洗质量。' : 'Inject scripts to add aria-labels, semantic attributes, and other a11y improvements to enhance DOM sanitization quality.'}

  5. {isZh ? '开发专用 Tool' : 'Build a custom Tool'}

    {isZh ? ( <> 对于特定的、持续难以操作的元素,考虑开发{' '} 自定义 Tool {' '} 来直接操作这些元素。 ) : ( <> For consistently difficult elements, consider building a{' '} custom Tool {' '} to interact with them directly. )}

) } function ApiErrorsContent(isZh: boolean) { return (

{isZh ? '一些 LLM 供应商使用了与 OpenAI 不完全兼容的参数格式,导致请求参数校验失败。' : 'Some LLM providers use parameter formats that are not fully compatible with the OpenAI spec, causing request validation failures.'}

{isZh ? '解决方案:使用 customFetch' : 'Solution: use customFetch'}

{isZh ? '通过 customFetch 配置拦截请求,在发送前调整参数格式以适配目标供应商的要求。' : 'Use the customFetch config to intercept requests and adapt parameters before sending them to the target provider.'}

{ // Adapt parameters for your provider const body = JSON.parse(init.body) delete body.tool_choice const bodyStr = JSON.stringify(body) return fetch(url, { ...init, body: bodyStr }) }, })`} />

{isZh ? '参见 ' : 'See '} PageAgentCore API {isZh ? ' 了解 customFetch 的完整用法。' : ' for full customFetch documentation.'}

) } // --------------------------------------------------------------------------- // Color mapping for symptom callouts // --------------------------------------------------------------------------- const SYMPTOM_COLORS = { red: 'border-red-400 bg-red-50 dark:bg-red-900/15 text-red-800 dark:text-red-200', amber: 'border-amber-400 bg-amber-50 dark:bg-amber-900/15 text-amber-800 dark:text-amber-200', orange: 'border-orange-400 bg-orange-50 dark:bg-orange-900/15 text-orange-800 dark:text-orange-200', violet: 'border-violet-400 bg-violet-50 dark:bg-violet-900/15 text-violet-800 dark:text-violet-200', } as const // --------------------------------------------------------------------------- // Right-side TOC with IntersectionObserver // --------------------------------------------------------------------------- function useActiveSection(ids: string[]) { const [activeId, setActiveId] = useState(ids[0]) const observerRef = useRef(null) useEffect(() => { observerRef.current?.disconnect() const visibleEntries = new Map() observerRef.current = new IntersectionObserver( (entries) => { for (const entry of entries) { if (entry.isIntersecting) { visibleEntries.set(entry.target.id, entry.intersectionRatio) } else { visibleEntries.delete(entry.target.id) } } // Pick the first visible section in document order const firstVisible = ids.find((id) => visibleEntries.has(id)) if (firstVisible) setActiveId(firstVisible) }, { rootMargin: '-80px 0px -60% 0px', threshold: [0, 0.25] } ) for (const id of ids) { const el = document.getElementById(id) if (el) observerRef.current.observe(el) } return () => observerRef.current?.disconnect() }, [ids]) return activeId } // --------------------------------------------------------------------------- // Page component // --------------------------------------------------------------------------- export default function TroubleshootingPage() { const { isZh } = useLanguage() const sectionIds = SECTIONS.map((s) => s.id) const activeId = useActiveSection(sectionIds) return (
{/* Header */}

Troubleshooting

{/* Two-column: content + TOC */}
{/* Main content */}
{SECTIONS.map((section) => (
{isZh ? section.title.zh : section.title.en} {/* Symptom callout */}
{isZh ? '症状' : 'Symptom'}

{isZh ? section.symptom.zh : section.symptom.en}

{/* Diagnostic steps */}
{section.content(isZh)}
))}
{/* Right TOC */}
) }