[{"data":1,"prerenderedAt":1964},["ShallowReactive",2],{"navigation":3,"\u002Fllm\u002Feino2":144,"\u002Fllm\u002Feino2-surround":1959},[4,36,53,86,131],{"title":5,"path":6,"stem":7,"children":8,"icon":35},"Golang","\u002Fgolang","1.golang\u002F1.index",[9,11,15,19,23,27,31],{"title":10,"path":6,"stem":7},"golang-各种golang学习以及使用过程中记录",{"title":12,"path":13,"stem":14},"gopls-官方gopls内置mcp server的基本使用","\u002Fgolang\u002Fgopls_mcp_usages","1.golang\u002F2.gopls_mcp_usages",{"title":16,"path":17,"stem":18},"实践-(一)创建简单的http服务器","\u002Fgolang\u002Fgo_http_simple_server","1.golang\u002F3.go_http_simple_server",{"title":20,"path":21,"stem":22},"wails入门系列(一)环境安装与demo","\u002Fgolang\u002Fwails_start","1.golang\u002F4.wails_start",{"title":24,"path":25,"stem":26},"wails入门系列(二)无边框应用的菜单栏以及窗口拖拽","\u002Fgolang\u002Fwails_frameless","1.golang\u002F5.wails_frameless",{"title":28,"path":29,"stem":30},"go\u002Fredis-redis中大数字自动转换成指数形式的处理","\u002Fgolang\u002Fredis_big_num","1.golang\u002F6.redis_big_num",{"title":32,"path":33,"stem":34},"go\u002F方法记录-局部坐标与世界坐标间的相互转换(位置\u002F方向)","\u002Fgolang\u002Fworld_local_transform","1.golang\u002F7.world_local_transform",false,{"title":37,"icon":35,"path":38,"stem":39,"children":40,"page":35},"瞎折腾","\u002Ftinkering","2.tinkering",[41,45,49],{"title":42,"path":43,"stem":44},"mi50显卡ubuntu运行大模型开坑(一)显卡准备以及驱动安装","\u002Ftinkering\u002Fmi50_gpu_llm_1","2.tinkering\u002F1.mi50_gpu_llm_1",{"title":46,"path":47,"stem":48},"mi50显卡ubuntu运行大模型开坑(二)使用llama.cpp部署Qwen3系列","\u002Ftinkering\u002Fmi50_gpu_llm_2","2.tinkering\u002F2.mi50_gpu_llm_2",{"title":50,"path":51,"stem":52},"mi50显卡ubuntu运行大模型开坑(三)安装风扇并且控制转速","\u002Ftinkering\u002Fmi50_gpu_llm_3","2.tinkering\u002F3.mi50_gpu_llm_3",{"title":54,"icon":35,"path":55,"stem":56,"children":57,"page":35},"LLM","\u002Fllm","3.llm",[58,62,66,70,74,78,82],{"title":59,"path":60,"stem":61},"langchain入门-安装以及初次使用(deepseek api版本)","\u002Fllm\u002Flangchain1","3.llm\u002F01.langchain1",{"title":63,"path":64,"stem":65},"langchain入门-使用langchain调用本地部署的大模型(以llama.cpp以及ollama为例)","\u002Fllm\u002Flangchain2","3.llm\u002F02.langchain2",{"title":67,"path":68,"stem":69},"langchain入门-使用langchain编写一个简单的聊天机器人(DeepSeek API&命令行版本)","\u002Fllm\u002Flangchain3","3.llm\u002F03.langchain3",{"title":71,"path":72,"stem":73},"langchain入门-使用langchain构建一个拥有RAG能力的代码问答应用(DeepSeek API&本地bge-m3&命令行版本)","\u002Fllm\u002Flangchain4","3.llm\u002F04.langchain4",{"title":75,"path":76,"stem":77},"golang\u002Feino eino框架的基础使用 Message以及ChatModel入门","\u002Fllm\u002Feino1","3.llm\u002F05.eino1",{"title":79,"path":80,"stem":81},"golang\u002Feino eino框架的基础使用 在ChatModel中使用工具","\u002Fllm\u002Feino2","3.llm\u002F06.eino2",{"title":83,"path":84,"stem":85},"llm\u002Fagent agent-zero初步搭建与使用","\u002Fllm\u002Fagent_zero_start","3.llm\u002F07.agent_zero_start",{"title":87,"icon":35,"path":88,"stem":89,"children":90,"page":35},"Verilog","\u002Fverilog","4.verilog",[91,95,99,103,107,111,115,119,123,127],{"title":92,"path":93,"stem":94},"31条指令单周期cpu设计(Verilog)-(一)相关软件","\u002Fverilog\u002Fmips1","4.verilog\u002F01.mips1",{"title":96,"path":97,"stem":98},"31条指令单周期cpu设计(Verilog)-(二)总体设计","\u002Fverilog\u002Fmips2","4.verilog\u002F02.mips2",{"title":100,"path":101,"stem":102},"31条指令单周期cpu设计(Verilog)-(三)指令分析","\u002Fverilog\u002Fmips3","4.verilog\u002F03.mips3",{"title":104,"path":105,"stem":106},"31条指令单周期cpu设计(Verilog)-(四)数据输入输出关系表","\u002Fverilog\u002Fmips4","4.verilog\u002F04.mips4",{"title":108,"path":109,"stem":110},"31条指令单周期cpu设计(Verilog)-(五)整体数据通路图设计","\u002Fverilog\u002Fmips5","4.verilog\u002F05.mips5",{"title":112,"path":113,"stem":114},"31条指令单周期cpu设计(Verilog)-(六)指令操作时间表设计","\u002Fverilog\u002Fmips6","4.verilog\u002F06.mips6",{"title":116,"path":117,"stem":118},"31条指令单周期cpu设计(Verilog)-(七)整体代码结构","\u002Fverilog\u002Fmips7","4.verilog\u002F07.mips7",{"title":120,"path":121,"stem":122},"31条指令单周期cpu设计(Verilog)-(八)上代码→指令译码以及控制器","\u002Fverilog\u002Fmips8","4.verilog\u002F08.mips8",{"title":124,"path":125,"stem":126},"31条指令单周期cpu设计(Verilog)-(九)上代码→基础模块实现","\u002Fverilog\u002Fmips9","4.verilog\u002F09.mips9",{"title":128,"path":129,"stem":130},"31条指令单周期cpu设计(Verilog)-(十)上代码→顶层模块设计&总结","\u002Fverilog\u002Fmips10","4.verilog\u002F10.mips10",{"title":132,"icon":35,"path":133,"stem":134,"children":135,"page":35},"Rust","\u002Frust","5.rust",[136,140],{"title":137,"path":138,"stem":139},"egui(一)从编译运行template开始","\u002Frust\u002Fegui1","5.rust\u002F01.egui1",{"title":141,"path":142,"stem":143},"egui(二)看看template的main函数：日志输出以及eframe run_native","\u002Frust\u002Fegui2","5.rust\u002F02.egui2",{"id":145,"title":79,"body":146,"description":1951,"extension":1952,"links":1953,"meta":1954,"navigation":54,"path":80,"seo":1955,"stem":81,"__hash__":1958},"docs\u002F3.llm\u002F06.eino2.md",{"type":147,"value":148,"toc":1943},"minimark",[149,154,253,257,398,402,475,478,609,612,1568,1939],[150,151,153],"h2",{"id":152},"tool","Tool",[155,156,157,167,242,250],"ul",{},[158,159,160],"li",{},[161,162,166],"a",{"href":163,"rel":164},"https:\u002F\u002Fwww.cloudwego.io\u002Fzh\u002Fdocs\u002Feino\u002Fcore_modules\u002Fcomponents\u002Ftools_node_guide\u002Fhow_to_create_a_tool\u002F",[165],"nofollow","参考",[158,168,169,170],{},"定义",[171,172,177],"pre",{"className":173,"code":174,"language":175,"meta":176,"style":176},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F BaseTool provides the metadata that a ChatModel uses to decide whether and\n\u002F\u002F how to call a tool. Info returns a [schema.ToolInfo] containing the tool\n\u002F\u002F name, description, and parameter JSON schema.\n\u002F\u002F\n\u002F\u002F BaseTool alone is sufficient when passing tool definitions to a ChatModel\n\u002F\u002F via WithTools — the model only needs the schema to generate tool calls.\n\u002F\u002F To also execute the tool, implement [InvokableTool] or [StreamableTool].\ntype BaseTool interface {\n    Info(ctx context.Context) (*schema.ToolInfo, error)\n}\n","go","",[178,179,180,188,194,200,206,212,218,224,230,236],"code",{"__ignoreMap":176},[181,182,185],"span",{"class":183,"line":184},"line",1,[181,186,187],{},"\u002F\u002F BaseTool provides the metadata that a ChatModel uses to decide whether and\n",[181,189,191],{"class":183,"line":190},2,[181,192,193],{},"\u002F\u002F how to call a tool. Info returns a [schema.ToolInfo] containing the tool\n",[181,195,197],{"class":183,"line":196},3,[181,198,199],{},"\u002F\u002F name, description, and parameter JSON schema.\n",[181,201,203],{"class":183,"line":202},4,[181,204,205],{},"\u002F\u002F\n",[181,207,209],{"class":183,"line":208},5,[181,210,211],{},"\u002F\u002F BaseTool alone is sufficient when passing tool definitions to a ChatModel\n",[181,213,215],{"class":183,"line":214},6,[181,216,217],{},"\u002F\u002F via WithTools — the model only needs the schema to generate tool calls.\n",[181,219,221],{"class":183,"line":220},7,[181,222,223],{},"\u002F\u002F To also execute the tool, implement [InvokableTool] or [StreamableTool].\n",[181,225,227],{"class":183,"line":226},8,[181,228,229],{},"type BaseTool interface {\n",[181,231,233],{"class":183,"line":232},9,[181,234,235],{},"    Info(ctx context.Context) (*schema.ToolInfo, error)\n",[181,237,239],{"class":183,"line":238},10,[181,240,241],{},"}\n",[158,243,244,245],{},"大模型如何调用工具",[246,247],"mermaid",{":config":248,"code":249},"config","sequenceDiagram%0A%20%20%20%20%20%20%20%20participant%20User%20as%20%E7%94%A8%E6%88%B7%0A%20%20%20%20%20%20%20%20participant%20Eino%20as%20Eino%20%E6%A1%86%E6%9E%B6%20%2F%20%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%0A%20%20%20%20%20%20%20%20participant%20LLM%20as%20%E5%A4%A7%E6%A8%A1%E5%9E%8B%20(ChatModel)%0A%20%20%20%20%20%20%20%20participant%20Tool%20as%20%E5%B7%A5%E5%85%B7%20(InvokableTool)%0A%0A%20%20%20%20%20%20%20%20User-%3E%3EEino%3A%20%E5%8F%91%E8%B5%B7%E8%AF%B7%E6%B1%82%20(Query)%0A%20%20%20%20%20%20%20%20Eino-%3E%3ELLM%3A%20%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF%20%2B%20%E5%B7%A5%E5%85%B7%E5%AE%9A%E4%B9%89%20(Messages%20%2B%20ToolInfos)%0A%20%20%20%20%20%20%20%20Note%20over%20LLM%3A%20%E6%80%9D%E8%80%83%EF%BC%9A%E6%98%AF%E5%90%A6%E9%9C%80%E8%A6%81%E8%B0%83%E7%94%A8%E5%B7%A5%E5%85%B7%EF%BC%9F%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20alt%20%E9%9C%80%E8%A6%81%E8%B0%83%E7%94%A8%E5%B7%A5%E5%85%B7%0A%20%20%20%20%20%20%20%20%20%20%20%20LLM--%3E%3EEino%3A%20%E8%BF%94%E5%9B%9E%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%AF%B7%E6%B1%82%20(Tool%20Call%3A%20name%2C%20arguments)%0A%20%20%20%20%20%20%20%20%20%20%20%20Eino-%3E%3ETool%3A%20%E6%89%A7%E8%A1%8C%E5%B7%A5%E5%85%B7%20(Invoke%2FStream)%0A%20%20%20%20%20%20%20%20%20%20%20%20Tool--%3E%3EEino%3A%20%E8%BF%94%E5%9B%9E%E6%89%A7%E8%A1%8C%E7%BB%93%E6%9E%9C%20(Tool%20Result)%0A%20%20%20%20%20%20%20%20%20%20%20%20Eino-%3E%3ELLM%3A%20%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF%20(%E5%8E%86%E5%8F%B2%E6%B6%88%E6%81%AF%20%2B%20%E5%B7%A5%E5%85%B7%E7%BB%93%E6%9E%9C)%0A%20%20%20%20%20%20%20%20%20%20%20%20LLM--%3E%3EEino%3A%20%E7%94%9F%E6%88%90%E6%9C%80%E7%BB%88%E5%9B%9E%E7%AD%94%20(Final%20Response)%0A%20%20%20%20%20%20%20%20else%20%E4%B8%8D%E9%9C%80%E8%A6%81%E8%B0%83%E7%94%A8%E5%B7%A5%E5%85%B7%0A%20%20%20%20%20%20%20%20%20%20%20%20LLM--%3E%3EEino%3A%20%E7%9B%B4%E6%8E%A5%E7%94%9F%E6%88%90%E5%9B%9E%E7%AD%94%20(Response)%0A%20%20%20%20%20%20%20%20end%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20Eino-%3E%3EUser%3A%20%E8%BF%94%E5%9B%9E%E6%9C%80%E7%BB%88%E7%BB%93%E6%9E%9C",[158,251,252],{},"简单来说，就是将工具描述成字符串，告诉大模型这个工具是做什么的，调用这个工具需要哪些参数，然后大模型返回一条消息，告诉我们要调用这个工具，参数也发过来。然后本地代码执行工具完成之后将结果返回给大模型。",[254,255,256],"h3",{"id":256},"创建工具",[155,258,259,392],{},[158,260,261,262,325,326],{},"在eino中，有几种基本的tool类型\n",[171,263,265],{"className":173,"code":264,"language":175,"meta":176,"style":176},"type InvokableTool interface {\n    BaseTool\n\n    \u002F\u002F InvokableRun call function with arguments in JSON format\n    InvokableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (string, error)\n}\n\ntype StreamableTool interface {\n    BaseTool\n\n    StreamableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (*schema.StreamReader[string], error)\n}\n",[178,266,267,272,277,283,288,293,297,301,306,310,314,320],{"__ignoreMap":176},[181,268,269],{"class":183,"line":184},[181,270,271],{},"type InvokableTool interface {\n",[181,273,274],{"class":183,"line":190},[181,275,276],{},"    BaseTool\n",[181,278,279],{"class":183,"line":196},[181,280,282],{"emptyLinePlaceholder":281},true,"\n",[181,284,285],{"class":183,"line":202},[181,286,287],{},"    \u002F\u002F InvokableRun call function with arguments in JSON format\n",[181,289,290],{"class":183,"line":208},[181,291,292],{},"    InvokableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (string, error)\n",[181,294,295],{"class":183,"line":214},[181,296,241],{},[181,298,299],{"class":183,"line":220},[181,300,282],{"emptyLinePlaceholder":281},[181,302,303],{"class":183,"line":226},[181,304,305],{},"type StreamableTool interface {\n",[181,307,308],{"class":183,"line":232},[181,309,276],{},[181,311,312],{"class":183,"line":238},[181,313,282],{"emptyLinePlaceholder":281},[181,315,317],{"class":183,"line":316},11,[181,318,319],{},"    StreamableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (*schema.StreamReader[string], error)\n",[181,321,323],{"class":183,"line":322},12,[181,324,241],{},"\n增强型工具接口，支持多模态\n",[171,327,329],{"className":173,"code":328,"language":175,"meta":176,"style":176},"\u002F\u002F EnhancedInvokableTool 是支持返回结构化多模态结果的工具接口\n\u002F\u002F 与返回字符串的 InvokableTool 不同，此接口返回 *schema.ToolResult\n\u002F\u002F 可以包含文本、图片、音频、视频和文件\ntype EnhancedInvokableTool interface {\n    BaseTool\n    InvokableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.ToolResult, error)\n}\n\n\u002F\u002F EnhancedStreamableTool 是支持返回结构化多模态结果的流式工具接口\ntype EnhancedStreamableTool interface {\n    BaseTool\n    StreamableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.StreamReader[*schema.ToolResult], error)\n}\n\n",[178,330,331,336,341,346,351,355,360,364,368,373,378,382,387],{"__ignoreMap":176},[181,332,333],{"class":183,"line":184},[181,334,335],{},"\u002F\u002F EnhancedInvokableTool 是支持返回结构化多模态结果的工具接口\n",[181,337,338],{"class":183,"line":190},[181,339,340],{},"\u002F\u002F 与返回字符串的 InvokableTool 不同，此接口返回 *schema.ToolResult\n",[181,342,343],{"class":183,"line":196},[181,344,345],{},"\u002F\u002F 可以包含文本、图片、音频、视频和文件\n",[181,347,348],{"class":183,"line":202},[181,349,350],{},"type EnhancedInvokableTool interface {\n",[181,352,353],{"class":183,"line":208},[181,354,276],{},[181,356,357],{"class":183,"line":214},[181,358,359],{},"    InvokableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.ToolResult, error)\n",[181,361,362],{"class":183,"line":220},[181,363,241],{},[181,365,366],{"class":183,"line":226},[181,367,282],{"emptyLinePlaceholder":281},[181,369,370],{"class":183,"line":232},[181,371,372],{},"\u002F\u002F EnhancedStreamableTool 是支持返回结构化多模态结果的流式工具接口\n",[181,374,375],{"class":183,"line":238},[181,376,377],{},"type EnhancedStreamableTool interface {\n",[181,379,380],{"class":183,"line":316},[181,381,276],{},[181,383,384],{"class":183,"line":322},[181,385,386],{},"    StreamableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.StreamReader[*schema.ToolResult], error)\n",[181,388,390],{"class":183,"line":389},13,[181,391,241],{},[158,393,394,395],{},"了解接口定义之后，除了工具本身的逻辑实现，最麻烦的是如何构造",[178,396,397],{},"schema.ToolInfo",[254,399,401],{"id":400},"构造toolinfo","构造ToolInfo",[155,403,404,410,417,443],{},[158,405,406,407],{},"官方文档描述了两种实现方式，这里主要使用第二种，即使用",[178,408,409],{},"JSON Schema",[158,411,412,413,416],{},"eino提供了",[178,414,415],{},"func GoStruct2ParamsOneOf[T any](opts ...Option) (*schema.ParamsOneOf, error)","来封装这个过程，只需要将工具的参数定义用结构体表示，然后调用这个函数即可。",[158,418,419,420],{},"在定义结构体时，有几个tag需要注意\n",[155,421,422,427,432,437],{},[158,423,424],{},[178,425,426],{},"jsonschema_description:\"xxx\"",[158,428,429],{},[178,430,431],{},"jsonschema:\"enum=xxx,enum=yyy,enum=zzz\"",[158,433,434],{},[178,435,436],{},"jsonschema:\"required\"",[158,438,439,442],{},[178,440,441],{},"jsonschema:\"xxx,omitempty\""," => 代表非 required",[158,444,445,446],{},"贴个官方的示例\n",[171,447,449],{"className":173,"code":448,"language":175,"meta":176,"style":176},"type User struct {\n    Name   string `json:\"name\" jsonschema_description:\"the name of the user\" jsonschema:\"required\"`\n    Age    int    `json:\"age\" jsonschema_description:\"the age of the user\"`\n    Gender string `json:\"gender\" jsonschema:\"enum=male,enum=female\"`\n}\n",[178,450,451,456,461,466,471],{"__ignoreMap":176},[181,452,453],{"class":183,"line":184},[181,454,455],{},"type User struct {\n",[181,457,458],{"class":183,"line":190},[181,459,460],{},"    Name   string `json:\"name\" jsonschema_description:\"the name of the user\" jsonschema:\"required\"`\n",[181,462,463],{"class":183,"line":196},[181,464,465],{},"    Age    int    `json:\"age\" jsonschema_description:\"the age of the user\"`\n",[181,467,468],{"class":183,"line":202},[181,469,470],{},"    Gender string `json:\"gender\" jsonschema:\"enum=male,enum=female\"`\n",[181,472,473],{"class":183,"line":208},[181,474,241],{},[150,476,477],{"id":477},"限流处理",[155,479,480],{},[158,481,482,483],{},"在使用第三方的大模型时，可能会遇到限流错误，即429错误。可以添加重试机制来处理。\n",[171,484,486],{"className":173,"code":485,"language":175,"meta":176,"style":176},"\u002F\u002F streamWithRetry calls cm.Stream with exponential backoff on 429 errors.\nfunc streamWithRetry(ctx context.Context, cm model.ToolCallingChatModel, messages []*schema.Message) (*schema.StreamReader[*schema.Message], error) {\n    const maxRetries = 5\n    const baseDelay = time.Second\n\n    for attempt := range maxRetries {\n        stream, err := cm.Stream(ctx, messages)\n        if err == nil {\n            return stream, nil\n        }\n        if !strings.Contains(err.Error(), \"429\") || attempt == maxRetries-1 {\n            return nil, err\n        }\n        delay := time.Duration(math.Pow(2, float64(attempt))) * baseDelay\n        fmt.Fprintf(os.Stderr, \"\\n[rate limited, retrying in %s]\\n[Assistant] \", delay)\n        select {\n        case \u003C-time.After(delay):\n        case \u003C-ctx.Done():\n            return nil, ctx.Err()\n        }\n    }\n    panic(\"unreachable\")\n}\n",[178,487,488,493,498,503,508,512,517,522,527,532,537,542,547,551,557,563,569,575,581,587,592,598,604],{"__ignoreMap":176},[181,489,490],{"class":183,"line":184},[181,491,492],{},"\u002F\u002F streamWithRetry calls cm.Stream with exponential backoff on 429 errors.\n",[181,494,495],{"class":183,"line":190},[181,496,497],{},"func streamWithRetry(ctx context.Context, cm model.ToolCallingChatModel, messages []*schema.Message) (*schema.StreamReader[*schema.Message], error) {\n",[181,499,500],{"class":183,"line":196},[181,501,502],{},"    const maxRetries = 5\n",[181,504,505],{"class":183,"line":202},[181,506,507],{},"    const baseDelay = time.Second\n",[181,509,510],{"class":183,"line":208},[181,511,282],{"emptyLinePlaceholder":281},[181,513,514],{"class":183,"line":214},[181,515,516],{},"    for attempt := range maxRetries {\n",[181,518,519],{"class":183,"line":220},[181,520,521],{},"        stream, err := cm.Stream(ctx, messages)\n",[181,523,524],{"class":183,"line":226},[181,525,526],{},"        if err == nil {\n",[181,528,529],{"class":183,"line":232},[181,530,531],{},"            return stream, nil\n",[181,533,534],{"class":183,"line":238},[181,535,536],{},"        }\n",[181,538,539],{"class":183,"line":316},[181,540,541],{},"        if !strings.Contains(err.Error(), \"429\") || attempt == maxRetries-1 {\n",[181,543,544],{"class":183,"line":322},[181,545,546],{},"            return nil, err\n",[181,548,549],{"class":183,"line":389},[181,550,536],{},[181,552,554],{"class":183,"line":553},14,[181,555,556],{},"        delay := time.Duration(math.Pow(2, float64(attempt))) * baseDelay\n",[181,558,560],{"class":183,"line":559},15,[181,561,562],{},"        fmt.Fprintf(os.Stderr, \"\\n[rate limited, retrying in %s]\\n[Assistant] \", delay)\n",[181,564,566],{"class":183,"line":565},16,[181,567,568],{},"        select {\n",[181,570,572],{"class":183,"line":571},17,[181,573,574],{},"        case \u003C-time.After(delay):\n",[181,576,578],{"class":183,"line":577},18,[181,579,580],{},"        case \u003C-ctx.Done():\n",[181,582,584],{"class":183,"line":583},19,[181,585,586],{},"            return nil, ctx.Err()\n",[181,588,590],{"class":183,"line":589},20,[181,591,536],{},[181,593,595],{"class":183,"line":594},21,[181,596,597],{},"    }\n",[181,599,601],{"class":183,"line":600},22,[181,602,603],{},"    panic(\"unreachable\")\n",[181,605,607],{"class":183,"line":606},23,[181,608,241],{},[150,610,611],{"id":611},"交互式完整示例",[171,613,615],{"className":173,"code":614,"language":175,"meta":176,"style":176},"package main\n\nimport (\n    \"bufio\"\n    \"cc\u002Ftools\"\n    \"context\"\n    \"errors\"\n    \"flag\"\n    \"fmt\"\n    \"io\"\n    \"math\"\n    \"os\"\n    \"strings\"\n    \"time\"\n\n    \"github.com\u002Fcloudwego\u002Feino-ext\u002Fcomponents\u002Fmodel\u002Fopenai\"\n    \"github.com\u002Fcloudwego\u002Feino\u002Fcomponents\u002Fmodel\"\n    \"github.com\u002Fcloudwego\u002Feino\u002Fschema\"\n)\n\nfunc main() {\n    var instruction string\n    flag.StringVar(&instruction, \"instruction\", \"You are a helpful assistant.\", \"\")\n    flag.Parse()\n\n    ctx := context.Background()\n\n    globTool := tools.GlobTool{}\n    toolInfo, err := globTool.Info(ctx)\n    if err != nil {\n        _, _ = fmt.Fprintln(os.Stderr, err)\n        os.Exit(1)\n    }\n\n    \u002F\u002F tool registry: name → InvokableRun\n    toolRegistry := map[string]func(context.Context, string) (string, error){\n        globTool.Name(): func(c context.Context, args string) (string, error) {\n            return globTool.InvokableRun(c, args)\n        },\n    }\n\n    cm, err := newChatModel(ctx)\n    if err != nil {\n        _, _ = fmt.Fprintln(os.Stderr, err)\n        os.Exit(1)\n    }\n\n    cm, _ = cm.WithTools([]*schema.ToolInfo{toolInfo})\n\n    fmt.Println(\"Chat session started. Type 'exit' or 'quit' to end.\")\n    scanner := bufio.NewScanner(os.Stdin)\n\n    messages := []*schema.Message{\n        schema.SystemMessage(instruction),\n    }\n    for {\n        fmt.Print(\"\\n[User] \")\n        if !scanner.Scan() {\n            break\n        }\n\n        query := strings.TrimSpace(scanner.Text())\n        if query == \"\" {\n            continue\n        }\n\n        if query == \"exit\" || query == \"quit\" {\n            break\n        }\n\n        messages = append(messages, schema.UserMessage(query))\n\n        fmt.Print(\"[Assistant] \")\n\n        \u002F\u002F Agentic loop: keep calling the model until it stops issuing tool calls.\n        for {\n            stream, err := streamWithRetry(ctx, cm, messages)\n            if err != nil {\n                fmt.Fprintf(os.Stderr, \"\\nError: %v\\n\", err)\n                break\n            }\n\n            var textContent string\n            var toolCallFrames []*schema.Message\n            for {\n                frame, err := stream.Recv()\n                if errors.Is(err, io.EOF) {\n                    break\n                }\n                if err != nil {\n                    fmt.Fprintf(os.Stderr, \"\\nStream error: %v\\n\", err)\n                    break\n                }\n                if frame == nil {\n                    continue\n                }\n                if frame.Content != \"\" {\n                    textContent += frame.Content\n                    fmt.Print(frame.Content)\n                }\n                if len(frame.ToolCalls) > 0 {\n                    toolCallFrames = append(toolCallFrames, frame)\n                }\n            }\n            stream.Close()\n\n            if len(toolCallFrames) == 0 {\n                \u002F\u002F No tool calls — we have the final answer.\n                messages = append(messages, schema.AssistantMessage(textContent, nil))\n                break\n            }\n\n            \u002F\u002F Concatenate streaming tool-call chunks into a single message.\n            assistantMsg, err := schema.ConcatMessages(toolCallFrames)\n            if err != nil {\n                fmt.Fprintf(os.Stderr, \"\\nError concatenating tool calls: %v\\n\", err)\n                break\n            }\n            messages = append(messages, assistantMsg)\n\n            \u002F\u002F Execute each tool call and append its result.\n            for _, tc := range assistantMsg.ToolCalls {\n                fn, ok := toolRegistry[tc.Function.Name]\n                if !ok {\n                    fmt.Fprintf(os.Stderr, \"\\nUnknown tool: %s\\n\", tc.Function.Name)\n                    messages = append(messages, schema.ToolMessage(\n                        fmt.Sprintf(\"error: unknown tool %q\", tc.Function.Name), tc.ID,\n                    ))\n                    continue\n                }\n                result, err := fn(ctx, tc.Function.Arguments)\n                if err != nil {\n                    result = fmt.Sprintf(\"error: %v\", err)\n                }\n                fmt.Printf(\"\\n[Tool: %s] %s\\n[Assistant] \", tc.Function.Name, result)\n                messages = append(messages, schema.ToolMessage(result, tc.ID))\n            }\n            \u002F\u002F Continue loop — ask model for next response.\n        }\n\n        fmt.Println()\n    }\n}\n\n\u002F\u002F streamWithRetry calls cm.Stream with exponential backoff on 429 errors.\nfunc streamWithRetry(ctx context.Context, cm model.ToolCallingChatModel, messages []*schema.Message) (*schema.StreamReader[*schema.Message], error) {\n    const maxRetries = 5\n    const baseDelay = time.Second\n\n    for attempt := range maxRetries {\n        stream, err := cm.Stream(ctx, messages)\n        if err == nil {\n            return stream, nil\n        }\n        if !strings.Contains(err.Error(), \"429\") || attempt == maxRetries-1 {\n            return nil, err\n        }\n        delay := time.Duration(math.Pow(2, float64(attempt))) * baseDelay\n        fmt.Fprintf(os.Stderr, \"\\n[rate limited, retrying in %s]\\n[Assistant] \", delay)\n        select {\n        case \u003C-time.After(delay):\n        case \u003C-ctx.Done():\n            return nil, ctx.Err()\n        }\n    }\n    panic(\"unreachable\")\n}\n\nfunc newChatModel(ctx context.Context) (model.ToolCallingChatModel, error) {\n    return openai.NewChatModel(ctx, &openai.ChatModelConfig{\n        APIKey:  \"\",\n        Model:   \"qwen\u002Fqwen3.6-plus:free\",\n        BaseURL: \"https:\u002F\u002Fopenrouter.ai\u002Fapi\u002Fv1\",\n    })\n}\n\n",[178,616,617,622,626,631,636,641,646,651,656,661,666,671,676,681,686,690,695,700,705,710,714,719,724,729,735,740,746,751,757,763,769,775,781,786,791,797,803,809,815,821,826,831,837,842,847,852,857,862,868,873,879,885,890,896,902,907,913,919,925,931,936,941,947,953,959,964,969,975,980,985,990,996,1001,1007,1012,1018,1024,1030,1036,1042,1048,1054,1059,1065,1071,1077,1083,1089,1095,1101,1107,1113,1118,1123,1129,1135,1140,1146,1152,1158,1163,1169,1175,1180,1185,1191,1196,1202,1208,1214,1219,1224,1229,1235,1241,1246,1252,1257,1262,1268,1273,1279,1285,1291,1297,1303,1309,1315,1321,1326,1331,1337,1342,1348,1353,1359,1365,1370,1376,1381,1386,1392,1397,1402,1407,1412,1417,1422,1427,1432,1437,1442,1447,1452,1457,1462,1467,1472,1477,1482,1487,1492,1497,1502,1507,1512,1517,1522,1527,1533,1539,1545,1551,1557,1563],{"__ignoreMap":176},[181,618,619],{"class":183,"line":184},[181,620,621],{},"package main\n",[181,623,624],{"class":183,"line":190},[181,625,282],{"emptyLinePlaceholder":281},[181,627,628],{"class":183,"line":196},[181,629,630],{},"import (\n",[181,632,633],{"class":183,"line":202},[181,634,635],{},"    \"bufio\"\n",[181,637,638],{"class":183,"line":208},[181,639,640],{},"    \"cc\u002Ftools\"\n",[181,642,643],{"class":183,"line":214},[181,644,645],{},"    \"context\"\n",[181,647,648],{"class":183,"line":220},[181,649,650],{},"    \"errors\"\n",[181,652,653],{"class":183,"line":226},[181,654,655],{},"    \"flag\"\n",[181,657,658],{"class":183,"line":232},[181,659,660],{},"    \"fmt\"\n",[181,662,663],{"class":183,"line":238},[181,664,665],{},"    \"io\"\n",[181,667,668],{"class":183,"line":316},[181,669,670],{},"    \"math\"\n",[181,672,673],{"class":183,"line":322},[181,674,675],{},"    \"os\"\n",[181,677,678],{"class":183,"line":389},[181,679,680],{},"    \"strings\"\n",[181,682,683],{"class":183,"line":553},[181,684,685],{},"    \"time\"\n",[181,687,688],{"class":183,"line":559},[181,689,282],{"emptyLinePlaceholder":281},[181,691,692],{"class":183,"line":565},[181,693,694],{},"    \"github.com\u002Fcloudwego\u002Feino-ext\u002Fcomponents\u002Fmodel\u002Fopenai\"\n",[181,696,697],{"class":183,"line":571},[181,698,699],{},"    \"github.com\u002Fcloudwego\u002Feino\u002Fcomponents\u002Fmodel\"\n",[181,701,702],{"class":183,"line":577},[181,703,704],{},"    \"github.com\u002Fcloudwego\u002Feino\u002Fschema\"\n",[181,706,707],{"class":183,"line":583},[181,708,709],{},")\n",[181,711,712],{"class":183,"line":589},[181,713,282],{"emptyLinePlaceholder":281},[181,715,716],{"class":183,"line":594},[181,717,718],{},"func main() {\n",[181,720,721],{"class":183,"line":600},[181,722,723],{},"    var instruction string\n",[181,725,726],{"class":183,"line":606},[181,727,728],{},"    flag.StringVar(&instruction, \"instruction\", \"You are a helpful assistant.\", \"\")\n",[181,730,732],{"class":183,"line":731},24,[181,733,734],{},"    flag.Parse()\n",[181,736,738],{"class":183,"line":737},25,[181,739,282],{"emptyLinePlaceholder":281},[181,741,743],{"class":183,"line":742},26,[181,744,745],{},"    ctx := context.Background()\n",[181,747,749],{"class":183,"line":748},27,[181,750,282],{"emptyLinePlaceholder":281},[181,752,754],{"class":183,"line":753},28,[181,755,756],{},"    globTool := tools.GlobTool{}\n",[181,758,760],{"class":183,"line":759},29,[181,761,762],{},"    toolInfo, err := globTool.Info(ctx)\n",[181,764,766],{"class":183,"line":765},30,[181,767,768],{},"    if err != nil {\n",[181,770,772],{"class":183,"line":771},31,[181,773,774],{},"        _, _ = fmt.Fprintln(os.Stderr, err)\n",[181,776,778],{"class":183,"line":777},32,[181,779,780],{},"        os.Exit(1)\n",[181,782,784],{"class":183,"line":783},33,[181,785,597],{},[181,787,789],{"class":183,"line":788},34,[181,790,282],{"emptyLinePlaceholder":281},[181,792,794],{"class":183,"line":793},35,[181,795,796],{},"    \u002F\u002F tool registry: name → InvokableRun\n",[181,798,800],{"class":183,"line":799},36,[181,801,802],{},"    toolRegistry := map[string]func(context.Context, string) (string, error){\n",[181,804,806],{"class":183,"line":805},37,[181,807,808],{},"        globTool.Name(): func(c context.Context, args string) (string, error) {\n",[181,810,812],{"class":183,"line":811},38,[181,813,814],{},"            return globTool.InvokableRun(c, args)\n",[181,816,818],{"class":183,"line":817},39,[181,819,820],{},"        },\n",[181,822,824],{"class":183,"line":823},40,[181,825,597],{},[181,827,829],{"class":183,"line":828},41,[181,830,282],{"emptyLinePlaceholder":281},[181,832,834],{"class":183,"line":833},42,[181,835,836],{},"    cm, err := newChatModel(ctx)\n",[181,838,840],{"class":183,"line":839},43,[181,841,768],{},[181,843,845],{"class":183,"line":844},44,[181,846,774],{},[181,848,850],{"class":183,"line":849},45,[181,851,780],{},[181,853,855],{"class":183,"line":854},46,[181,856,597],{},[181,858,860],{"class":183,"line":859},47,[181,861,282],{"emptyLinePlaceholder":281},[181,863,865],{"class":183,"line":864},48,[181,866,867],{},"    cm, _ = cm.WithTools([]*schema.ToolInfo{toolInfo})\n",[181,869,871],{"class":183,"line":870},49,[181,872,282],{"emptyLinePlaceholder":281},[181,874,876],{"class":183,"line":875},50,[181,877,878],{},"    fmt.Println(\"Chat session started. Type 'exit' or 'quit' to end.\")\n",[181,880,882],{"class":183,"line":881},51,[181,883,884],{},"    scanner := bufio.NewScanner(os.Stdin)\n",[181,886,888],{"class":183,"line":887},52,[181,889,282],{"emptyLinePlaceholder":281},[181,891,893],{"class":183,"line":892},53,[181,894,895],{},"    messages := []*schema.Message{\n",[181,897,899],{"class":183,"line":898},54,[181,900,901],{},"        schema.SystemMessage(instruction),\n",[181,903,905],{"class":183,"line":904},55,[181,906,597],{},[181,908,910],{"class":183,"line":909},56,[181,911,912],{},"    for {\n",[181,914,916],{"class":183,"line":915},57,[181,917,918],{},"        fmt.Print(\"\\n[User] \")\n",[181,920,922],{"class":183,"line":921},58,[181,923,924],{},"        if !scanner.Scan() {\n",[181,926,928],{"class":183,"line":927},59,[181,929,930],{},"            break\n",[181,932,934],{"class":183,"line":933},60,[181,935,536],{},[181,937,939],{"class":183,"line":938},61,[181,940,282],{"emptyLinePlaceholder":281},[181,942,944],{"class":183,"line":943},62,[181,945,946],{},"        query := strings.TrimSpace(scanner.Text())\n",[181,948,950],{"class":183,"line":949},63,[181,951,952],{},"        if query == \"\" {\n",[181,954,956],{"class":183,"line":955},64,[181,957,958],{},"            continue\n",[181,960,962],{"class":183,"line":961},65,[181,963,536],{},[181,965,967],{"class":183,"line":966},66,[181,968,282],{"emptyLinePlaceholder":281},[181,970,972],{"class":183,"line":971},67,[181,973,974],{},"        if query == \"exit\" || query == \"quit\" {\n",[181,976,978],{"class":183,"line":977},68,[181,979,930],{},[181,981,983],{"class":183,"line":982},69,[181,984,536],{},[181,986,988],{"class":183,"line":987},70,[181,989,282],{"emptyLinePlaceholder":281},[181,991,993],{"class":183,"line":992},71,[181,994,995],{},"        messages = append(messages, schema.UserMessage(query))\n",[181,997,999],{"class":183,"line":998},72,[181,1000,282],{"emptyLinePlaceholder":281},[181,1002,1004],{"class":183,"line":1003},73,[181,1005,1006],{},"        fmt.Print(\"[Assistant] \")\n",[181,1008,1010],{"class":183,"line":1009},74,[181,1011,282],{"emptyLinePlaceholder":281},[181,1013,1015],{"class":183,"line":1014},75,[181,1016,1017],{},"        \u002F\u002F Agentic loop: keep calling the model until it stops issuing tool calls.\n",[181,1019,1021],{"class":183,"line":1020},76,[181,1022,1023],{},"        for {\n",[181,1025,1027],{"class":183,"line":1026},77,[181,1028,1029],{},"            stream, err := streamWithRetry(ctx, cm, messages)\n",[181,1031,1033],{"class":183,"line":1032},78,[181,1034,1035],{},"            if err != nil {\n",[181,1037,1039],{"class":183,"line":1038},79,[181,1040,1041],{},"                fmt.Fprintf(os.Stderr, \"\\nError: %v\\n\", err)\n",[181,1043,1045],{"class":183,"line":1044},80,[181,1046,1047],{},"                break\n",[181,1049,1051],{"class":183,"line":1050},81,[181,1052,1053],{},"            }\n",[181,1055,1057],{"class":183,"line":1056},82,[181,1058,282],{"emptyLinePlaceholder":281},[181,1060,1062],{"class":183,"line":1061},83,[181,1063,1064],{},"            var textContent string\n",[181,1066,1068],{"class":183,"line":1067},84,[181,1069,1070],{},"            var toolCallFrames []*schema.Message\n",[181,1072,1074],{"class":183,"line":1073},85,[181,1075,1076],{},"            for {\n",[181,1078,1080],{"class":183,"line":1079},86,[181,1081,1082],{},"                frame, err := stream.Recv()\n",[181,1084,1086],{"class":183,"line":1085},87,[181,1087,1088],{},"                if errors.Is(err, io.EOF) {\n",[181,1090,1092],{"class":183,"line":1091},88,[181,1093,1094],{},"                    break\n",[181,1096,1098],{"class":183,"line":1097},89,[181,1099,1100],{},"                }\n",[181,1102,1104],{"class":183,"line":1103},90,[181,1105,1106],{},"                if err != nil {\n",[181,1108,1110],{"class":183,"line":1109},91,[181,1111,1112],{},"                    fmt.Fprintf(os.Stderr, \"\\nStream error: %v\\n\", err)\n",[181,1114,1116],{"class":183,"line":1115},92,[181,1117,1094],{},[181,1119,1121],{"class":183,"line":1120},93,[181,1122,1100],{},[181,1124,1126],{"class":183,"line":1125},94,[181,1127,1128],{},"                if frame == nil {\n",[181,1130,1132],{"class":183,"line":1131},95,[181,1133,1134],{},"                    continue\n",[181,1136,1138],{"class":183,"line":1137},96,[181,1139,1100],{},[181,1141,1143],{"class":183,"line":1142},97,[181,1144,1145],{},"                if frame.Content != \"\" {\n",[181,1147,1149],{"class":183,"line":1148},98,[181,1150,1151],{},"                    textContent += frame.Content\n",[181,1153,1155],{"class":183,"line":1154},99,[181,1156,1157],{},"                    fmt.Print(frame.Content)\n",[181,1159,1161],{"class":183,"line":1160},100,[181,1162,1100],{},[181,1164,1166],{"class":183,"line":1165},101,[181,1167,1168],{},"                if len(frame.ToolCalls) > 0 {\n",[181,1170,1172],{"class":183,"line":1171},102,[181,1173,1174],{},"                    toolCallFrames = append(toolCallFrames, frame)\n",[181,1176,1178],{"class":183,"line":1177},103,[181,1179,1100],{},[181,1181,1183],{"class":183,"line":1182},104,[181,1184,1053],{},[181,1186,1188],{"class":183,"line":1187},105,[181,1189,1190],{},"            stream.Close()\n",[181,1192,1194],{"class":183,"line":1193},106,[181,1195,282],{"emptyLinePlaceholder":281},[181,1197,1199],{"class":183,"line":1198},107,[181,1200,1201],{},"            if len(toolCallFrames) == 0 {\n",[181,1203,1205],{"class":183,"line":1204},108,[181,1206,1207],{},"                \u002F\u002F No tool calls — we have the final answer.\n",[181,1209,1211],{"class":183,"line":1210},109,[181,1212,1213],{},"                messages = append(messages, schema.AssistantMessage(textContent, nil))\n",[181,1215,1217],{"class":183,"line":1216},110,[181,1218,1047],{},[181,1220,1222],{"class":183,"line":1221},111,[181,1223,1053],{},[181,1225,1227],{"class":183,"line":1226},112,[181,1228,282],{"emptyLinePlaceholder":281},[181,1230,1232],{"class":183,"line":1231},113,[181,1233,1234],{},"            \u002F\u002F Concatenate streaming tool-call chunks into a single message.\n",[181,1236,1238],{"class":183,"line":1237},114,[181,1239,1240],{},"            assistantMsg, err := schema.ConcatMessages(toolCallFrames)\n",[181,1242,1244],{"class":183,"line":1243},115,[181,1245,1035],{},[181,1247,1249],{"class":183,"line":1248},116,[181,1250,1251],{},"                fmt.Fprintf(os.Stderr, \"\\nError concatenating tool calls: %v\\n\", err)\n",[181,1253,1255],{"class":183,"line":1254},117,[181,1256,1047],{},[181,1258,1260],{"class":183,"line":1259},118,[181,1261,1053],{},[181,1263,1265],{"class":183,"line":1264},119,[181,1266,1267],{},"            messages = append(messages, assistantMsg)\n",[181,1269,1271],{"class":183,"line":1270},120,[181,1272,282],{"emptyLinePlaceholder":281},[181,1274,1276],{"class":183,"line":1275},121,[181,1277,1278],{},"            \u002F\u002F Execute each tool call and append its result.\n",[181,1280,1282],{"class":183,"line":1281},122,[181,1283,1284],{},"            for _, tc := range assistantMsg.ToolCalls {\n",[181,1286,1288],{"class":183,"line":1287},123,[181,1289,1290],{},"                fn, ok := toolRegistry[tc.Function.Name]\n",[181,1292,1294],{"class":183,"line":1293},124,[181,1295,1296],{},"                if !ok {\n",[181,1298,1300],{"class":183,"line":1299},125,[181,1301,1302],{},"                    fmt.Fprintf(os.Stderr, \"\\nUnknown tool: %s\\n\", tc.Function.Name)\n",[181,1304,1306],{"class":183,"line":1305},126,[181,1307,1308],{},"                    messages = append(messages, schema.ToolMessage(\n",[181,1310,1312],{"class":183,"line":1311},127,[181,1313,1314],{},"                        fmt.Sprintf(\"error: unknown tool %q\", tc.Function.Name), tc.ID,\n",[181,1316,1318],{"class":183,"line":1317},128,[181,1319,1320],{},"                    ))\n",[181,1322,1324],{"class":183,"line":1323},129,[181,1325,1134],{},[181,1327,1329],{"class":183,"line":1328},130,[181,1330,1100],{},[181,1332,1334],{"class":183,"line":1333},131,[181,1335,1336],{},"                result, err := fn(ctx, tc.Function.Arguments)\n",[181,1338,1340],{"class":183,"line":1339},132,[181,1341,1106],{},[181,1343,1345],{"class":183,"line":1344},133,[181,1346,1347],{},"                    result = fmt.Sprintf(\"error: %v\", err)\n",[181,1349,1351],{"class":183,"line":1350},134,[181,1352,1100],{},[181,1354,1356],{"class":183,"line":1355},135,[181,1357,1358],{},"                fmt.Printf(\"\\n[Tool: %s] %s\\n[Assistant] \", tc.Function.Name, result)\n",[181,1360,1362],{"class":183,"line":1361},136,[181,1363,1364],{},"                messages = append(messages, schema.ToolMessage(result, tc.ID))\n",[181,1366,1368],{"class":183,"line":1367},137,[181,1369,1053],{},[181,1371,1373],{"class":183,"line":1372},138,[181,1374,1375],{},"            \u002F\u002F Continue loop — ask model for next response.\n",[181,1377,1379],{"class":183,"line":1378},139,[181,1380,536],{},[181,1382,1384],{"class":183,"line":1383},140,[181,1385,282],{"emptyLinePlaceholder":281},[181,1387,1389],{"class":183,"line":1388},141,[181,1390,1391],{},"        fmt.Println()\n",[181,1393,1395],{"class":183,"line":1394},142,[181,1396,597],{},[181,1398,1400],{"class":183,"line":1399},143,[181,1401,241],{},[181,1403,1405],{"class":183,"line":1404},144,[181,1406,282],{"emptyLinePlaceholder":281},[181,1408,1410],{"class":183,"line":1409},145,[181,1411,492],{},[181,1413,1415],{"class":183,"line":1414},146,[181,1416,497],{},[181,1418,1420],{"class":183,"line":1419},147,[181,1421,502],{},[181,1423,1425],{"class":183,"line":1424},148,[181,1426,507],{},[181,1428,1430],{"class":183,"line":1429},149,[181,1431,282],{"emptyLinePlaceholder":281},[181,1433,1435],{"class":183,"line":1434},150,[181,1436,516],{},[181,1438,1440],{"class":183,"line":1439},151,[181,1441,521],{},[181,1443,1445],{"class":183,"line":1444},152,[181,1446,526],{},[181,1448,1450],{"class":183,"line":1449},153,[181,1451,531],{},[181,1453,1455],{"class":183,"line":1454},154,[181,1456,536],{},[181,1458,1460],{"class":183,"line":1459},155,[181,1461,541],{},[181,1463,1465],{"class":183,"line":1464},156,[181,1466,546],{},[181,1468,1470],{"class":183,"line":1469},157,[181,1471,536],{},[181,1473,1475],{"class":183,"line":1474},158,[181,1476,556],{},[181,1478,1480],{"class":183,"line":1479},159,[181,1481,562],{},[181,1483,1485],{"class":183,"line":1484},160,[181,1486,568],{},[181,1488,1490],{"class":183,"line":1489},161,[181,1491,574],{},[181,1493,1495],{"class":183,"line":1494},162,[181,1496,580],{},[181,1498,1500],{"class":183,"line":1499},163,[181,1501,586],{},[181,1503,1505],{"class":183,"line":1504},164,[181,1506,536],{},[181,1508,1510],{"class":183,"line":1509},165,[181,1511,597],{},[181,1513,1515],{"class":183,"line":1514},166,[181,1516,603],{},[181,1518,1520],{"class":183,"line":1519},167,[181,1521,241],{},[181,1523,1525],{"class":183,"line":1524},168,[181,1526,282],{"emptyLinePlaceholder":281},[181,1528,1530],{"class":183,"line":1529},169,[181,1531,1532],{},"func newChatModel(ctx context.Context) (model.ToolCallingChatModel, error) {\n",[181,1534,1536],{"class":183,"line":1535},170,[181,1537,1538],{},"    return openai.NewChatModel(ctx, &openai.ChatModelConfig{\n",[181,1540,1542],{"class":183,"line":1541},171,[181,1543,1544],{},"        APIKey:  \"\",\n",[181,1546,1548],{"class":183,"line":1547},172,[181,1549,1550],{},"        Model:   \"qwen\u002Fqwen3.6-plus:free\",\n",[181,1552,1554],{"class":183,"line":1553},173,[181,1555,1556],{},"        BaseURL: \"https:\u002F\u002Fopenrouter.ai\u002Fapi\u002Fv1\",\n",[181,1558,1560],{"class":183,"line":1559},174,[181,1561,1562],{},"    })\n",[181,1564,1566],{"class":183,"line":1565},175,[181,1567,241],{},[171,1569,1573],{"className":1570,"code":1571,"language":1572,"meta":176,"style":176},"language-shell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","Chat session started. Type 'exit' or 'quit' to end.\n\n[User] 帮我分析下当前目录的结构\n[Assistant] \n[rate limited, retrying in 1s]\n[Assistant]\n[Tool: Glob] go.sum\ngo.mod\ndocs\u002F01.md\ndocs\u002F02.md\ntools\u002Fglob.go\nmain.go\n\n[Assistant] 当前目录结构如下：\n\n```\n.\n├── main.go          # 主入口文件\n├── go.mod           # Go 模块依赖文件\n├── go.sum           # Go 依赖校验文件（自动生成）\n├── docs\u002F            # 文档目录\n│   ├── 01.md\n│   └── 02.md\n└── tools\u002F           # 工具包目录\n    └── glob.go      # glob 工具源码\n```\n\n**说明**：\n- **Go 项目**：这是一个标准的 Go 项目，包含 `go.mod` 和 `go.sum` 依赖管理文件，以及一个可执行的入口文件 `main.go`\n- **docs\u002F**：存放了一些文档文件（01.md, 02.md），可能是使用说明或笔记\n- **tools\u002Fglob.go**：一个工具包，从文件名来看，似乎是与文件 glob 匹配功能相关的代码\n\n需要我查看某个具体文件的内容吗？\n\n[User]\n","shell",[178,1574,1575,1617,1621,1636,1648,1658,1666,1678,1683,1688,1693,1698,1703,1707,1718,1722,1727,1733,1745,1755,1765,1775,1783,1790,1801,1812,1816,1820,1825,1881,1898,1918,1922,1927,1931],{"__ignoreMap":176},[181,1576,1577,1581,1585,1588,1591,1595,1598,1601,1604,1606,1609,1611,1614],{"class":183,"line":184},[181,1578,1580],{"class":1579},"sBMFI","Chat",[181,1582,1584],{"class":1583},"sfazB"," session",[181,1586,1587],{"class":1583}," started.",[181,1589,1590],{"class":1583}," Type",[181,1592,1594],{"class":1593},"sMK4o"," '",[181,1596,1597],{"class":1583},"exit",[181,1599,1600],{"class":1593},"'",[181,1602,1603],{"class":1583}," or",[181,1605,1594],{"class":1593},[181,1607,1608],{"class":1583},"quit",[181,1610,1600],{"class":1593},[181,1612,1613],{"class":1583}," to",[181,1615,1616],{"class":1583}," end.\n",[181,1618,1619],{"class":183,"line":190},[181,1620,282],{"emptyLinePlaceholder":281},[181,1622,1623,1626,1630,1633],{"class":183,"line":196},[181,1624,1625],{"class":1593},"[",[181,1627,1629],{"class":1628},"sTEyZ","User",[181,1631,1632],{"class":1593},"]",[181,1634,1635],{"class":1628}," 帮我分析下当前目录的结构\n",[181,1637,1638,1640,1643,1645],{"class":183,"line":202},[181,1639,1625],{"class":1593},[181,1641,1642],{"class":1628},"Assistant",[181,1644,1632],{"class":1593},[181,1646,1647],{"class":1628}," \n",[181,1649,1650,1652,1655],{"class":183,"line":208},[181,1651,1625],{"class":1593},[181,1653,1654],{"class":1628},"rate limited, retrying in 1s",[181,1656,1657],{"class":1593},"]\n",[181,1659,1660,1662,1664],{"class":183,"line":214},[181,1661,1625],{"class":1593},[181,1663,1642],{"class":1628},[181,1665,1657],{"class":1593},[181,1667,1668,1670,1673,1675],{"class":183,"line":220},[181,1669,1625],{"class":1593},[181,1671,1672],{"class":1628},"Tool: Glob",[181,1674,1632],{"class":1593},[181,1676,1677],{"class":1628}," go.sum\n",[181,1679,1680],{"class":183,"line":226},[181,1681,1682],{"class":1579},"go.mod\n",[181,1684,1685],{"class":183,"line":232},[181,1686,1687],{"class":1579},"docs\u002F01.md\n",[181,1689,1690],{"class":183,"line":238},[181,1691,1692],{"class":1579},"docs\u002F02.md\n",[181,1694,1695],{"class":183,"line":316},[181,1696,1697],{"class":1579},"tools\u002Fglob.go\n",[181,1699,1700],{"class":183,"line":322},[181,1701,1702],{"class":1579},"main.go\n",[181,1704,1705],{"class":183,"line":389},[181,1706,282],{"emptyLinePlaceholder":281},[181,1708,1709,1711,1713,1715],{"class":183,"line":553},[181,1710,1625],{"class":1593},[181,1712,1642],{"class":1628},[181,1714,1632],{"class":1593},[181,1716,1717],{"class":1628}," 当前目录结构如下：\n",[181,1719,1720],{"class":183,"line":559},[181,1721,282],{"emptyLinePlaceholder":281},[181,1723,1724],{"class":183,"line":565},[181,1725,1726],{"class":1593},"```\n",[181,1728,1729],{"class":183,"line":571},[181,1730,1732],{"class":1731},"s2Zo4",".\n",[181,1734,1735,1738,1741],{"class":183,"line":577},[181,1736,1737],{"class":1579},"├──",[181,1739,1740],{"class":1583}," main.go          ",[181,1742,1744],{"class":1743},"sHwdD","# 主入口文件\n",[181,1746,1747,1749,1752],{"class":183,"line":583},[181,1748,1737],{"class":1579},[181,1750,1751],{"class":1583}," go.mod           ",[181,1753,1754],{"class":1743},"# Go 模块依赖文件\n",[181,1756,1757,1759,1762],{"class":183,"line":589},[181,1758,1737],{"class":1579},[181,1760,1761],{"class":1583}," go.sum           ",[181,1763,1764],{"class":1743},"# Go 依赖校验文件（自动生成）\n",[181,1766,1767,1769,1772],{"class":183,"line":594},[181,1768,1737],{"class":1579},[181,1770,1771],{"class":1583}," docs\u002F            ",[181,1773,1774],{"class":1743},"# 文档目录\n",[181,1776,1777,1780],{"class":183,"line":600},[181,1778,1779],{"class":1579},"│",[181,1781,1782],{"class":1583},"   ├── 01.md\n",[181,1784,1785,1787],{"class":183,"line":606},[181,1786,1779],{"class":1579},[181,1788,1789],{"class":1583},"   └── 02.md\n",[181,1791,1792,1795,1798],{"class":183,"line":731},[181,1793,1794],{"class":1579},"└──",[181,1796,1797],{"class":1583}," tools\u002F           ",[181,1799,1800],{"class":1743},"# 工具包目录\n",[181,1802,1803,1806,1809],{"class":183,"line":737},[181,1804,1805],{"class":1579},"    └──",[181,1807,1808],{"class":1583}," glob.go      ",[181,1810,1811],{"class":1743},"# glob 工具源码\n",[181,1813,1814],{"class":183,"line":742},[181,1815,1726],{"class":1593},[181,1817,1818],{"class":183,"line":748},[181,1819,282],{"emptyLinePlaceholder":281},[181,1821,1822],{"class":183,"line":753},[181,1823,1824],{"class":1579},"**说明**：\n",[181,1826,1827,1830,1833,1836,1839,1842,1845,1848,1851,1854,1857,1860,1863,1865,1868,1870,1873,1875,1878],{"class":183,"line":759},[181,1828,1829],{"class":1579},"-",[181,1831,1832],{"class":1628}," **",[181,1834,1835],{"class":1583},"Go",[181,1837,1838],{"class":1583}," 项目",[181,1840,1841],{"class":1628},"**",[181,1843,1844],{"class":1583},"：这是一个标准的",[181,1846,1847],{"class":1583}," Go",[181,1849,1850],{"class":1583}," 项目，包含",[181,1852,1853],{"class":1593}," `",[181,1855,1856],{"class":1579},"go.mod",[181,1858,1859],{"class":1593},"`",[181,1861,1862],{"class":1579}," 和",[181,1864,1853],{"class":1593},[181,1866,1867],{"class":1579},"go.sum",[181,1869,1859],{"class":1593},[181,1871,1872],{"class":1579}," 依赖管理文件，以及一个可执行的入口文件",[181,1874,1853],{"class":1593},[181,1876,1877],{"class":1579},"main.go",[181,1879,1880],{"class":1593},"`\n",[181,1882,1883,1885,1887,1890,1892,1895],{"class":183,"line":765},[181,1884,1829],{"class":1579},[181,1886,1832],{"class":1628},[181,1888,1889],{"class":1583},"docs\u002F",[181,1891,1841],{"class":1628},[181,1893,1894],{"class":1583},"：存放了一些文档文件（01.md,",[181,1896,1897],{"class":1583}," 02.md），可能是使用说明或笔记\n",[181,1899,1900,1902,1904,1907,1909,1912,1915],{"class":183,"line":771},[181,1901,1829],{"class":1579},[181,1903,1832],{"class":1628},[181,1905,1906],{"class":1583},"tools\u002Fglob.go",[181,1908,1841],{"class":1628},[181,1910,1911],{"class":1583},"：一个工具包，从文件名来看，似乎是与文件",[181,1913,1914],{"class":1583}," glob",[181,1916,1917],{"class":1583}," 匹配功能相关的代码\n",[181,1919,1920],{"class":183,"line":777},[181,1921,282],{"emptyLinePlaceholder":281},[181,1923,1924],{"class":183,"line":783},[181,1925,1926],{"class":1579},"需要我查看某个具体文件的内容吗？\n",[181,1928,1929],{"class":183,"line":788},[181,1930,282],{"emptyLinePlaceholder":281},[181,1932,1933,1935,1937],{"class":183,"line":793},[181,1934,1625],{"class":1593},[181,1936,1629],{"class":1628},[181,1938,1657],{"class":1593},[1940,1941,1942],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":176,"searchDepth":184,"depth":190,"links":1944},[1945,1949,1950],{"id":152,"depth":190,"text":153,"children":1946},[1947,1948],{"id":256,"depth":196,"text":256},{"id":400,"depth":196,"text":401},{"id":477,"depth":190,"text":477},{"id":611,"depth":190,"text":611},"字节跳动 Eino 框架工具调用教程，介绍 Tool 接口定义、ToolInfo 构造方法、JSON Schema 参数定义、限流重试处理以及完整的交互式工具调用示例。","md",null,{},{"title":1956,"description":1957},"【llm\u002Feino】Eino框架工具调用 - ChatModel集成Tool完整指南","字节跳动 Eino 框架工具调用完整教程，详细介绍 Tool 接口定义、ToolInfo 构造、JSON Schema 参数映射、429 限流重试机制以及 Agentic Loop 交互式工具调用实现。","Omv3B-6Zjx1-9HM9v8-dOjwUIAbQAlQ6UaTKuuLHs5Y",[1960,1962],{"title":75,"path":76,"stem":77,"description":1961,"children":-1},"字节跳动 Eino Go 语言 AI 应用开发框架入门教程，介绍 Message 消息结构、RoleType 角色类型、ChatModel 对话模型接口以及 Generate\u002FStream 流式调用方法。",{"title":83,"path":84,"stem":85,"description":1963,"children":-1},"Agent-Zero AI Agent 框架入门教程，介绍 Docker 部署方式、模型配置、工具调用以及初步使用体验，包括搜索功能测试与问题排查。",1776616490416]