# models.py

from typing import List, Optional
from pydantic import BaseModel, Field

# 定义子章节的数据模型
class Subsection(BaseModel):
    subsection_title: str = Field(..., title="子章节标题")
    description: str = Field(..., title="子章节内容")

    @property
    def as_str(self) -> str:
        return f"### {self.subsection_title}\n\n{self.description}".strip()

# 定义章节的数据模型
class Section(BaseModel):
    section_title: str = Field(..., title="章节标题")
    description: str = Field(..., title="章节内容")
    subsections: Optional[List[Subsection]] = Field(
        default=None,
        title="维基百科页面中每个子章节的标题和描述。",
    )

    @property
    def as_str(self) -> str:
        subsections = "\n\n".join(
            subsection.as_str for subsection in self.subsections or []
        )
        return f"## {self.section_title}\n\n{self.description}\n\n{subsections}".strip()

# 定义大纲的数据模型
class Outline(BaseModel):
    page_title: str = Field(..., title="维基百科页面的标题")
    sections: List[Section] = Field(
        default_factory=list,
        title="维基百科页面中每个章节的标题和描述。",
    )

    @property
    def as_str(self) -> str:
        sections = "\n\n".join(section.as_str for section in self.sections)
        return f"# {self.page_title}\n\n{sections}".strip()

# 定义编辑者的数据模型
class Editor(BaseModel):
    affiliation: str = Field(
        description="编辑者的主要隶属机构。",
    )
    name: str = Field(
        description="编辑者的姓名。"
    )
    role: str = Field(
        description="编辑者在该主题背景下的角色。",
    )
    description: str = Field(
        description="对编辑者关注点、关切和动机的描述。",
    )

    @property
    def persona(self) -> str:
        return (
            f"姓名：{self.name}\n"
            f"角色：{self.role}\n"
            f"隶属机构：{self.affiliation}\n"
            f"描述：{self.description}\n"
        )

# 定义视角的数据模型
class Perspectives(BaseModel):
    editors: List[Editor] = Field(
        description="包含编辑者及其角色和隶属机构的全面列表。",
    )


---

### 2. interview.py

# interview.py

from typing import Annotated, Optional, List, Dict
from typing_extensions import TypedDict
from models import Editor
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import AnyMessage, AIMessage, HumanMessage, ToolMessage
from langchain_core.runnables import chain as as_runnable, RunnableLambda
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.documents import Document

from langgraph.graph import StateGraph, END, START

from langchain_community.utilities.duckduckgo_search import DuckDuckGoSearchAPIWrapper

# 使用适当的模型
fast_llm = ChatOpenAI(model="deepseek-chat", temperature=0)

# 定义采访的状态
def add_messages(left, right):
    if not isinstance(left, list):
        left = [left]
    if not isinstance(right, list):
        right = [right]
    return left + right

def update_references(references, new_references):
    if not references:
        references = {}
    references.update(new_references)
    return references

def update_editor(editor, new_editor):
    if not editor:
        return new_editor
    return editor

class InterviewState(TypedDict):
    messages: Annotated[List[AnyMessage], add_messages]
    references: Annotated[Optional[dict], update_references]
    editor: Annotated[Optional[Editor], update_editor]

# 定义生成问题的提示模板
gen_qn_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            """你是一名经验丰富的维基百科作者，正在编辑一个特定的页面。除了你维基百科作者的身份，你在研究该主题时有一个特定的关注点。现在，你正在与一位专家交谈以获取信息。请提出好的问题以获取更多有用的信息。

当你没有更多问题要问时，请说“非常感谢您的帮助！”来结束对话。请一次只问一个问题，不要重复已经问过的问题。你的问题应该与您想要撰写的主题相关。

请全面且有好奇心地提问，从专家那里获取尽可能多的独特见解。

坚持你的特定视角：

{persona}""",
        ),
        MessagesPlaceholder(variable_name="messages", optional=True),
    ]
)

# 定义辅助函数
def tag_with_name(ai_message: AIMessage, name: str):
    ai_message.name = name
    return ai_message

def swap_roles(state: InterviewState, name: str):
    converted = []
    for message in state["messages"]:
        if isinstance(message, AIMessage) and message.name != name:
            # 提取内容和附加参数
            content = message.content
            additional_kwargs = message.additional_kwargs
            # 创建新的 HumanMessage 对象
            message = HumanMessage(content=content, additional_kwargs=additional_kwargs)
        converted.append(message)
    return {"messages": converted}

@as_runnable
async def generate_question(state: InterviewState):
    editor = state["editor"]
    gn_chain = (
        RunnableLambda(swap_roles).bind(name=editor.name)
        | gen_qn_prompt.partial(persona=editor.persona)
        | fast_llm
        | RunnableLambda(tag_with_name).bind(name=editor.name)
    )
    result = await gn_chain.ainvoke(state)
    return {"messages": [result]}

# 定义搜索查询的数据模型
from pydantic import BaseModel, Field

class Queries(BaseModel):
    queries: List[str] = Field(
        description="回答用户问题所需的搜索引擎查询的全面列表。",
    )

# 定义生成搜索查询的提示模板
gen_queries_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "你是一名乐于助人的研究助理。为了回答用户的问题，请查询搜索引擎。",
        ),
        MessagesPlaceholder(variable_name="messages", optional=True),
    ]
)

# 创建生成搜索查询的链
gen_queries_chain = gen_queries_prompt | ChatOpenAI(
    model="deepseek-chat", temperature=0
).with_structured_output(Queries, include_raw=True)

# 定义答案和引用的数据模型
class AnswerWithCitations(BaseModel):
    answer: str = Field(
        description="对用户问题的全面回答，包含引用。",
    )
    cited_urls: List[str] = Field(
        description="答案中引用的URL列表。",
    )

    @property
    def as_str(self) -> str:
        return f"{self.answer}\n\n引用：\n\n" + "\n".join(
            f"[{i+1}]: {url}" for i, url in enumerate(self.cited_urls)
        )

# 定义生成答案的提示模板
gen_answer_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            """你是一位能有效使用信息的专家。你正在与一位想要撰写相关主题维基百科页面的维基百科作者聊天。你已经收集了相关信息，现在将使用这些信息形成回复。

使你的回复尽可能信息丰富，并确保每句话都由收集到的信息支持。
每个回复都必须由可靠来源的引用支持，格式为脚注，回复后复述URL。""",
        ),
        MessagesPlaceholder(variable_name="messages", optional=True),
    ]
)

gen_answer_chain = gen_answer_prompt | fast_llm.with_structured_output(
    AnswerWithCitations, include_raw=True
).with_config(run_name="GenerateAnswer")

# 初始化 DuckDuckGo 搜索引擎
search_engine_wrapper = DuckDuckGoSearchAPIWrapper()

@tool
async def search_engine(query: str):
    """互联网搜索引擎。"""
    results = search_engine_wrapper._ddgs_text(query)
    return [{"content": r["body"], "url": r["href"]} for r in results]

import json

from langchain_core.runnables import RunnableConfig

async def gen_answer(
    state: InterviewState,
    config: Optional[RunnableConfig] = None,
    name: str = "Subject_Matter_Expert",
    max_str_len: int = 15000,
):
    swapped_state = swap_roles(state, name)
    queries = await gen_queries_chain.ainvoke(swapped_state)
    query_results = await search_engine.abatch(
        queries["parsed"].queries, config, return_exceptions=True
    )
    successful_results = [
        res for res in query_results if not isinstance(res, Exception)
    ]
    all_query_results = {
        res["url"]: res["content"] for results in successful_results for res in results
    }
    dumped = json.dumps(all_query_results, ensure_ascii=False)[:max_str_len]
    ai_message: AIMessage = queries["raw"]
    tool_call = queries["raw"].tool_calls[0]
    tool_id = tool_call["id"]
    tool_message = ToolMessage(tool_call_id=tool_id, content=dumped)
    swapped_state["messages"].extend([ai_message, tool_message])
    generated = await gen_answer_chain.ainvoke(swapped_state)
    cited_urls = set(generated["parsed"].cited_urls)
    cited_references = {k: v for k, v in all_query_results.items() if k in cited_urls}
    formatted_message = AIMessage(name=name, content=generated["parsed"].as_str)
    return {"messages": [formatted_message], "references": cited_references}

# 定义最大的轮数
max_num_turns = 5

def route_messages(state: InterviewState, name: str = "Subject_Matter_Expert"):
    messages = state["messages"]
    num_responses = len(
        [m for m in messages if isinstance(m, AIMessage) and m.name == name]
    )
    if num_responses >= max_num_turns:
        return END
    if len(messages) >= 2:
        last_question = messages[-2]
        if last_question.content.endswith("非常感谢您的帮助！"):
            return END
    return "ask_question"

def create_interview_graph():
    builder = StateGraph(InterviewState)

    builder.add_node("ask_question", generate_question)
    builder.add_node("answer_question", gen_answer)
    builder.add_conditional_edges("answer_question", route_messages)
    builder.add_edge("ask_question", "answer_question")

    builder.add_edge(START, "ask_question")
    interview_graph = builder.compile().with_config(run_name="Conduct Interviews")
    return interview_graph


---

### 3. main.py

# main.py

import os
import asyncio
from typing import List, Optional, Dict

from pydantic import BaseModel, Field

from models import Editor, Outline, Section, Subsection, Perspectives  # 从 models.py 导入

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.messages import AnyMessage, AIMessage, HumanMessage, ToolMessage
from langchain_core.runnables import chain as as_runnable
from langchain_core.prompts import MessagesPlaceholder
from langchain_core.runnables import RunnableLambda
from langchain_core.tools import tool
from langchain_core.documents import Document
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableConfig

from langchain_community.retrievers import WikipediaRetriever
from langchain_community.vectorstores import SKLearnVectorStore
from langchain_community.utilities.duckduckgo_search import DuckDuckGoSearchAPIWrapper

from langgraph.graph import END, StateGraph, START
from langgraph.checkpoint.memory import MemorySaver

from typing_extensions import TypedDict

# 设置 OpenAI API 密钥
if not os.environ.get("OPENAI_API_KEY"):
    os.environ["OPENAI_API_KEY"] = input("请输入您的 OpenAI API 密钥：")

# 初始化聊天模型，设置 temperature=0 以减少随机性，限制最大输出长度
fast_llm = ChatOpenAI(model="deepseek-chat", temperature=0, max_tokens=4000)
long_context_llm = ChatOpenAI(model="deepseek-chat", temperature=0, max_tokens=8000)

# 生成大纲的提示模板（需要转义花括号）
direct_gen_outline_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            """你是一名维基百科作者。请为用户提供的主题编写一个维基百科页面的大纲。大纲应是一个 JSON 对象，符合以下结构：

{{
  "page_title": "维基百科页面的标题",
  "sections": [
    {{
      "section_title": "章节标题",
      "description": "章节内容",
      "subsections": [
        {{
          "subsection_title": "子章节标题",
          "description": "子章节内容"
        }}
      ]
    }}
  ]
}}

请确保每个章节和子章节都包含标题和描述。不要包含任何额外的文本或说明。

主题：{topic}""",
        ),
    ]
)

# 创建生成大纲的链
generate_outline_direct = direct_gen_outline_prompt | fast_llm.with_structured_output(
    Outline, include_raw=True
)
# 定义示例主题
example_topic = "百万以上令牌上下文窗口语言模型对 RAG 的影响"

# 定义生成相关主题的提示模板，限制主题数量
gen_related_topics_prompt = ChatPromptTemplate.from_template(
    """你是一个助理，帮助识别不超过20个与给定主题相关的维基百科主题。你需要提供一个全面的相关主题列表，用于背景研究。

请以 JSON 格式输出，格式如下：

{{
  "topics": ["主题1", "主题2", "主题3", ...]
}}

请确保：
- 只提供与主题高度相关的条目，不超过20个。
- 不要重复主题。
- 不要包含任何解释或额外的文本。

感兴趣的主题：{topic}"""
)

# 定义相关主题的数据模型
class RelatedSubjects(BaseModel):
    topics: List[str] = Field(
        description="作为背景研究的相关主题的全面列表。",
    )

# 创建生成相关主题的链
expand_chain = gen_related_topics_prompt | fast_llm.with_structured_output(
    RelatedSubjects, include_raw=True
)

# Wikipedia 检索器
wikipedia_retriever = WikipediaRetriever(load_all_available_meta=True, top_k_results=1)

# 定义格式化文档的函数
def format_doc(doc, max_length=1000):
    related = "- ".join(doc.metadata.get("categories", []))
    return f"### {doc.metadata.get('title', '')}\n\n总结: {doc.page_content}\n\n相关\n{related}"[
        :max_length
    ]

def format_docs(docs):
    return "\n\n".join(format_doc(doc) for doc in docs)

# 定义异步函数获取编辑者视角
@as_runnable
async def survey_subjects(topic: str):
    max_attempts = 3
    for attempt in range(max_attempts):
        try:
            related_subjects = await expand_chain.ainvoke({"topic": topic})
            if related_subjects['parsing_error'] is None:
                return related_subjects
            else:
                print(f"Parsing error on attempt {attempt + 1}: {related_subjects['parsing_error']}")
        except Exception as e:
            print(f"Exception on attempt {attempt + 1}: {e}")
        print("Retrying...")
    raise ValueError("Failed to get valid related subjects after multiple attempts.")

# 定义研究状态的数据模型
class ResearchState(TypedDict):
    topic: str
    outline: Outline
    editors: List[str]
    interview_results: List[Dict]
    sections: List[Section]
    article: str
    vectorstore: SKLearnVectorStore

import asyncio

async def initialize_research(state: ResearchState):
    topic = state["topic"]
    try:
        raw_outline_output = await generate_outline_direct.ainvoke({"topic": topic})
        print("LLM Output for Outline:")
        print(raw_outline_output)
        outline = raw_outline_output['parsed']
        if outline is None:
            raise ValueError("Failed to parse outline.")
    except Exception as e:
        print("Error in generate_outline_direct.ainvoke:")
        print(e)
        # 处理错误
        raise e

    try:
        related_subjects = await survey_subjects.ainvoke(topic)
        print("Related Subjects:")
        print(related_subjects)
        related_topics = related_subjects['parsed'].topics if related_subjects['parsed'] else None
        if not related_topics:
            raise ValueError("Failed to parse related subjects.")
    except Exception as e:
        print("Error in survey_subjects.ainvoke:")
        print(e)
        raise e

    return {
        **state,
        "outline": outline,
        "editors": related_topics,
    }

from interview import (
    InterviewState,
    generate_question,
    gen_queries_chain,
    gen_answer_chain,
    gen_answer,
    route_messages,
    create_interview_graph,
)

async def conduct_interviews(state: ResearchState):
    topic = state["topic"]
    interview_graph = create_interview_graph()
    initial_states = [
        {
            "editor": Editor(name=editor_name, affiliation="", role="", description=""),
            "messages": [
                AIMessage(
                    content=f"所以你说你正在撰写关于{topic}的文章？",
                    name="Subject_Matter_Expert",
                )
            ],
        }
        for editor_name in state["editors"]
    ]
    interview_results = await interview_graph.abatch(initial_states)
    return {
        **state,
        "interview_results": interview_results,
    }

def format_conversation(interview_state):
    messages = interview_state["messages"]
    convo = "\n".join(f"{m.name}: {m.content}" for m in messages)
    return f'与 {interview_state["editor"].name} 的对话\n\n' + convo

async def refine_outline(state: ResearchState):
    convos = "\n\n".join(
        [
            format_conversation(interview_state)
            for interview_state in state["interview_results"]
        ]
    )

    refine_outline_prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                """你是一名维基百科作者。你已经从专家和搜索引擎中收集了信息。现在，你正在细化维基百科页面的大纲。
你需要确保大纲是全面且具体的。
你正在撰写的主题：{topic}

旧大纲：

{old_outline}""",
            ),
            (
                "user",
                "根据你与主题专家的对话来细化大纲：\n\n对话：\n\n{conversations}\n\n写出细化后的维基百科大纲：",
            ),
        ]
    )

    # 使用带有长上下文的模型生成细化的大纲
    refine_outline_chain = refine_outline_prompt | long_context_llm.with_structured_output(
        Outline
    )

    updated_outline = await refine_outline_chain.ainvoke(
        {
            "topic": state["topic"],
            "old_outline": state["outline"].as_str,
            "conversations": convos,
        }
    )
    return {**state, "outline": updated_outline}

async def index_references(state: ResearchState):
    embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")
    vectorstore = SKLearnVectorStore(embedding=embeddings)
    all_docs = []
    for interview_state in state["interview_results"]:
        reference_docs = [
            Document(page_content=v, metadata={"source": k})
            for k, v in interview_state["references"].items()
        ]
        all_docs.extend(reference_docs)
    await vectorstore.aadd_documents(all_docs)
    state["vectorstore"] = vectorstore
    return state

async def write_sections(state: ResearchState):
    outline = state["outline"]
    vectorstore = state["vectorstore"]

    # 定义章节写作的提示模板
    section_writer_prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                "你是一名经验丰富的维基百科作者。请根据以下大纲完成指定的维基百科章节：\n\n"
                "{outline}\n\n使用以下参考资料进行引用：\n\n<Documents>\n{docs}\n<Documents>",
            ),
            ("user", "请为 {section} 章节撰写完整的维基百科章节内容。"),
        ]
    )

    # 定义检索函数
    async def retrieve(inputs: dict):
        retriever = vectorstore.as_retriever(k=10)
        docs = await retriever.ainvoke(inputs["topic"] + ": " + inputs["section"])
        formatted = "\n".join(
            [
                f'<Document href="{doc.metadata["source"]}"/>\n{doc.page_content}\n</Document>'
                for doc in docs
            ]
        )
        return {"docs": formatted, **inputs}

    # 定义章节写作的链
    section_writer = (
        retrieve
        | section_writer_prompt
        | long_context_llm.with_structured_output(Section)
    )

    sections = await section_writer.abatch(
        [
            {
                "outline": outline.as_str,
                "section": section.section_title,
                "topic": state["topic"],
            }
            for section in outline.sections
        ]
    )
    return {
        **state,
        "sections": sections,
    }

async def write_article(state: ResearchState):
    topic = state["topic"]
    sections = state["sections"]
    draft = "\n\n".join([section.as_str for section in sections])

    writer_prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                "你是一名经验丰富的维基百科作者。请使用以下章节草稿为 {topic} 撰写完整的维基百科文章：\n\n"
                "{draft}\n\n严格遵循维基百科格式指南。",
            ),
            (
                "user",
                '请使用 Markdown 格式撰写完整的维基百科文章。使用类似"[1]"的脚注组织引用，在页脚中包含 URL，避免重复。',
            ),
        ]
    )

    # 定义文章写作的链
    writer = writer_prompt | long_context_llm | StrOutputParser()

    article = await writer.invoke({"topic": topic, "draft": draft})
    return {
        **state,
        "article": article,
    }

# 构建整个流程图
builder_of_storm = StateGraph(ResearchState)

nodes = [
    ("init_research", initialize_research),
    ("conduct_interviews", conduct_interviews),
    ("refine_outline", refine_outline),
    ("index_references", index_references),
    ("write_sections", write_sections),
    ("write_article", write_article),
]
for i in range(len(nodes)):
    name, node = nodes[i]
    builder_of_storm.add_node(name, node)
    if i > 0:
        builder_of_storm.add_edge(nodes[i - 1][0], name)

builder_of_storm.add_edge(START, nodes[0][0])
builder_of_storm.add_edge(nodes[-1][0], END)
storm = builder_of_storm.compile(checkpointer=MemorySaver())

# 主函数
async def main():
    state = {
        "topic": example_topic,
    }
    config = {"configurable": {"thread_id": "my-thread"}}
    try:
        async for step in storm.astream(state, config):
            name = next(iter(step))
            print(f"Current step: {name}")
            print("--", str(step[name])[:300])
    except Exception as e:
        print("An error occurred:", e)

    checkpoint = storm.get_state(config)
    article = checkpoint.values.get("article", "")
    print("\n---\n")
    print("生成的维基百科文章：")
    print(article)

# 运行主函数
if __name__ == "__main__":
  try:
      asyncio.run(main())
  except Exception as e:
      print(f"An error occurred: {e}")
      import traceback
      traceback.print_exc()
      
我运行代码后，报错：
LLM Output for Outline:
{'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_0_7e8bed36-0ac2-4a71-a623-e8aab8ab3b74', 'function': {'arguments': '{"page_title": "百万以上令牌上下文窗口语言模型对 RAG 的影响", "sections": [{"section_title": " 引言", "description": "介绍百万以上令牌上下文窗口语言模型的概念及其在RAG（Retrieval-Augmented Generation）中的应用。", "subsections": null}, {"section_title": "背景", "description": "讨论RAG的基本原理及其在自然语言处理中的重要性。", "subsections": [{"subsection_title": "RAG的基本原理", "description": "解释RAG如何结合检索和生成来增强语言模型的性能。"}, {"subsection_title": "百万以上令牌上下文窗口语言模型的定义", "description": "定义和解释百万以上令牌上下文窗口语言模型的特点和 优势。"}]}, {"section_title": "影响分析", "description": "分析百万以上令牌上下文窗口语言模型对RAG性能的具体影响。", "subsections": [{"subsection_title": "性能提升", "description": "讨论这些模型如何提高RAG的检索和生成性能。"}, {"subsection_title": "挑战与限制", "description": "探讨在实际应用中可能遇到的挑战和限制。"}]}, {"section_title": "应用案例", "description": "展示百万以上令牌上下文窗口语言模型在实际RAG应用中的案例。", "subsections": [{"subsection_title": "案例一", "description": "详细描述第一个应用案例及其效果。"}, {"subsection_title": "案例二", "description": "详细描述第二个应用案例及其 效果。"}]}, {"section_title": "未来展望", "description": "探讨百万以上令牌上下文窗口语言模型在RAG领域的未来发展方向。", "subsections": [{"subsection_title": "技术进步", "description": "讨论可能的技术进步及其对RAG的影响。"}, {"subsection_title": "应用扩展", "description": "探讨这些模型在其他领域的潜在应用。"}]}]}', 'name': 'Outline'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 453, 'prompt_tokens': 555, 'total_tokens': 1008, 'completion_tokens_details': None, 'prompt_cache_hit_tokens': 512, 'prompt_cache_miss_tokens': 43}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_1c141eb703', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-6baa84f9-8679-4a60-ba5d-d4e67eb12295-0', tool_calls=[{'name': 'Outline', 'args': {'page_title': '百万以上令牌上下文窗口语言模型对 RAG 的影响', 'sections': [{'section_title': '引言', 'description': '介绍百万以上令牌上下文窗口语言模型的概念 及其在RAG（Retrieval-Augmented Generation）中的应用。', 'subsections': None}, {'section_title': '背景', 'description': '讨论RAG的基本原理及其在自然语言处理中的重要性。', 'subsections': [{'subsection_title': 'RAG的基本原理', 'description': '解释RAG如何结合检索和生成来增强语言模型的性能。'}, {'subsection_title': '百万以上令牌上下文窗口语言模型的定义', 'description': '定义和解释百万以上令牌上下文窗口语言模型的特点和优势。'}]}, {'section_title': '影响分析', 'description': '分析百万以上令牌上下文窗口语言模型对RAG性能的具体影响。', 'subsections': [{'subsection_title': '性能提升', 'description': '讨 论这些模型如何提高RAG的检索和生成性能。'}, {'subsection_title': '挑战与限制', 'description': '探讨在实际应用中可能遇到的挑战和限制。'}]}, {'section_title': '应用案例', 'description': '展示百万以上令牌上下文窗口语言模型在实际RAG应用中的案例 。', 'subsections': [{'subsection_title': '案例一', 'description': '详细描述第一个应用案例及其效果。'}, {'subsection_title': '案例二', 'description': '详细描述第二个应用案例及其效果。'}]}, {'section_title': '未来展望', 'description': '探讨 百万以上令牌上下文窗口语言模型在RAG领域的未来发展方向。', 'subsections': [{'subsection_title': '技术进步', 'description': '讨论可能的技术进步及其对RAG的影响。'}, {'subsection_title': '应用扩展', 'description': '探讨这些模型在其他领域的潜在 应用。'}]}]}, 'id': 'call_0_7e8bed36-0ac2-4a71-a623-e8aab8ab3b74', 'type': 'tool_call'}], usage_metadata={'input_tokens': 555, 'output_tokens': 453, 'total_tokens': 1008}), 'parsed': Outline(page_title='百万以上令牌上下文窗口语言模型对 RAG 的影响', sections=[Section(section_title='引言', description='介绍百万以上令牌上下文窗口语言模型的概念及其在RAG（Retrieval-Augmented Generation）中的应用。', subsections=None), Section(section_title='背景', description='讨论RAG的基本原理及 其在自然语言处理中的重要性。', subsections=[Subsection(subsection_title='RAG的基本原理', description='解释RAG如何结合检 索和生成来增强语言模型的性能。'), Subsection(subsection_title='百万以上令牌上下文窗口语言模型的定义', description='定义 和解释百万以上令牌上下文窗口语言模型的特点和优势。')]), Section(section_title='影响分析', description='分析百万以上令牌 上下文窗口语言模型对RAG性能的具体影响。', subsections=[Subsection(subsection_title='性能提升', description='讨论这些模型如何提高RAG的检索和生成性能。'), Subsection(subsection_title='挑战与限制', description='探讨在实际应用中可能遇到的挑战和限制。')]), Section(section_title='应用案例', description='展示百万以上令牌上下文窗口语言模型在实际RAG应用中的案例。', subsections=[Subsection(subsection_title='案例一', description='详细描述第一个应用案例及其效果。'), Subsection(subsection_title='案例二', description='详细描述第二个应用案例及其效果。')]), Section(section_title='未来展望', description='探讨 百万以上令牌上下文窗口语言模型在RAG领域的未来发展方向。', subsections=[Subsection(subsection_title='技术进步', description='讨论可能的技术进步及其对RAG的影响。'), Subsection(subsection_title='应用扩展', description='探讨这些模型在其他领域的潜在应用。')])]), 'parsing_error': None}
Related Subjects:
{'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_0_9a281392-eb0e-493a-ac92-263e3ccdd653', 'function': {'arguments': '{"topics": ["Retrieval-augmented generation", "Large language models", "Context window", "Transformer models", "Natural language processing", "Deep learning", "Artificial intelligence", "Machine learning", "GPT-3", "BERT", "T5", "Attention mechanism", "Tokenization", "Semantic search", "Information retrieval", "Knowledge graphs", "Prompt engineering", "Fine-tuning", "Pre-training", "Model scaling"]}', 'name': 'RelatedSubjects'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 108, 'prompt_tokens': 279, 'total_tokens': 387, 'completion_tokens_details': None, 'prompt_cache_hit_tokens': 256, 'prompt_cache_miss_tokens': 23}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_1c141eb703', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-24da5c1d-47ef-404c-9d06-00f1c0f8cc2b-0', tool_calls=[{'name': 'RelatedSubjects', 'args': {'topics': ['Retrieval-augmented generation', 'Large language models', 'Context window', 'Transformer models', 'Natural language processing', 'Deep learning', 'Artificial intelligence', 'Machine learning', 'GPT-3', 'BERT', 'T5', 'Attention mechanism', 'Tokenization', 'Semantic search', 'Information retrieval', 'Knowledge graphs', 'Prompt engineering', 'Fine-tuning', 'Pre-training', 'Model scaling']}, 'id': 'call_0_9a281392-eb0e-493a-ac92-263e3ccdd653', 'type': 'tool_call'}], usage_metadata={'input_tokens': 279, 'output_tokens': 108, 'total_tokens': 387}), 'parsed': RelatedSubjects(topics=['Retrieval-augmented generation', 'Large language models', 'Context window', 'Transformer models', 'Natural language processing', 'Deep learning', 'Artificial intelligence', 'Machine learning', 'GPT-3', 'BERT', 'T5', 'Attention mechanism', 'Tokenization', 'Semantic search', 'Information retrieval', 'Knowledge graphs', 'Prompt engineering', 'Fine-tuning', 'Pre-training', 'Model scaling']), 'parsing_error': None}
Current step: init_research
-- {'topic': '百万以上令牌上下文窗口语言模型对 RAG 的影响', 'outline': Outline(page_title='百万以上令牌上下文窗口语言模 型对 RAG 的影响', sections=[Section(section_title='引言', description='介绍百万以上令牌上下文窗口语言模型的概念及其在RAG（Retrieval-Augmented Generation）中的应用。', subsections=None), Section(section_title='背景', description='讨论RAG的基本原理及其在自然语言处理中的重要性。',
An error occurred: Error code: 400 - {'error': {'message': "An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. (insufficient tool messages following tool_calls message)", 'type': 'invalid_request_error', 'param': None, 'code': 'invalid_request_error'}}

---

生成的维基百科文章：

Press any key to continue . . .
请帮忙修复，中文回答