outline.parser.ts 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. /**
  2. * 大纲解析器
  3. * 容错解析LLM返回的大纲JSON
  4. */
  5. export interface OutlineData {
  6. mainTheme: string;
  7. structureLogic: string;
  8. chapters: Array<{
  9. number: number;
  10. title: string;
  11. summary: string;
  12. keyPoints: string[];
  13. estimatedWords: number;
  14. }>;
  15. }
  16. export function parseOutline(jsonStr: string): OutlineData | null {
  17. if (!jsonStr || typeof jsonStr !== 'string') {
  18. console.error('[OutlineParser] 输入为空或非字符串');
  19. return null;
  20. }
  21. try {
  22. let data: any;
  23. // 策略1:直接解析(最理想情况,LLM 直接输出纯 JSON)
  24. let cleanedStr = jsonStr.trim();
  25. // 清理思考标签
  26. cleanedStr = cleanedStr.replace(/^[\s\S]*?<\/?blockquote>[\s\S]*/gi, '');
  27. const thinkEndIdx = cleanedStr.indexOf('</think>');
  28. if (thinkEndIdx !== -1) {
  29. cleanedStr = cleanedStr.substring(thinkEndIdx + 8).trim();
  30. }
  31. try {
  32. data = JSON.parse(cleanedStr);
  33. } catch {
  34. // 策略2:提取 JSON 对象(处理 LLM 加了 markdown 标记或多余文字)
  35. const match = cleanedStr.match(/\{[\s\S]*\}/);
  36. if (!match) {
  37. console.error('[OutlineParser] 未找到 JSON 对象');
  38. return null;
  39. }
  40. try {
  41. data = JSON.parse(match[0]);
  42. } catch (parseErr) {
  43. console.error('[OutlineParser] JSON 解析失败:', parseErr);
  44. return null;
  45. }
  46. }
  47. // 验证必要字段
  48. if (!data || typeof data !== 'object') {
  49. console.error('[OutlineParser] 解析结果非对象');
  50. return null;
  51. }
  52. if (!Array.isArray(data.chapters)) {
  53. console.error('[OutlineParser] chapters 字段缺失或非数组');
  54. // 尝试兼容:若顶层就是章节数组
  55. if (Array.isArray(data)) {
  56. data = { chapters: data };
  57. } else {
  58. return null;
  59. }
  60. }
  61. return {
  62. mainTheme: data.mainTheme || '主题待定',
  63. structureLogic: data.structureLogic || '由浅入深',
  64. chapters: data.chapters.map((c: any, i: number) => ({
  65. number: c.number || i + 1,
  66. title: c.title || `第${i + 1}章`,
  67. summary: typeof c.summary === 'string' ? c.summary : '',
  68. keyPoints: Array.isArray(c.keyPoints) ? c.keyPoints : [],
  69. estimatedWords: typeof c.estimatedWords === 'number' ? c.estimatedWords : 1000,
  70. })),
  71. };
  72. } catch (err) {
  73. console.error('[OutlineParser] 未知错误:', err);
  74. return null;
  75. }
  76. }