Tio Boot DocsTio Boot Docs
Home
  • java-db
  • api-table
  • Enjoy
  • Tio Boot Admin
  • ai_agent
  • translator
  • knowlege_base
  • ai-search
  • 案例
Abount
  • Github
  • Gitee
Home
  • java-db
  • api-table
  • Enjoy
  • Tio Boot Admin
  • ai_agent
  • translator
  • knowlege_base
  • ai-search
  • 案例
Abount
  • Github
  • Gitee
  • 01_tio-boot 简介

    • tio-boot:新一代高性能 Java Web 开发框架
    • tio-boot 入门示例
    • Tio-Boot 配置 : 现代化的配置方案
    • tio-boot 整合 Logback
    • tio-boot 整合 hotswap-classloader 实现热加载
    • 自行编译 tio-boot
    • 最新版本
    • 开发规范
  • 02_部署

    • 使用 Maven Profile 实现分环境打包 tio-boot 项目
    • Maven 项目配置详解:依赖与 Profiles 配置
    • tio-boot 打包成 FastJar
    • 使用 GraalVM 构建 tio-boot Native 程序
    • 使用 Docker 部署 tio-boot
    • 部署到 Fly.io
    • 部署到 AWS Lambda
    • 到阿里云云函数
    • 使用 Deploy 工具部署
    • 胖包与瘦包的打包与部署
    • 使用 Jenkins 部署 Tio-Boot 项目
    • 使用 Nginx 反向代理 Tio-Boot
    • 使用 Supervisor 管理 Java 应用
  • 03_配置

    • 配置参数
    • 服务器监听器
    • 内置缓存系统 AbsCache
    • 使用 Redis 作为内部 Cache
    • 静态文件处理器
    • 基于域名的静态资源隔离
    • DecodeExceptionHandler
  • 04_原理

    • 生命周期
    • 请求处理流程
    • 重要的类
  • 05_json

    • Json
    • 接受 JSON 和响应 JSON
    • 响应实体类
  • 06_web

    • 概述
    • 文件上传
    • 接收请求参数
    • 接收日期参数
    • 接收数组参数
    • 返回字符串
    • 返回文本数据
    • 返回网页
    • 请求和响应字节
    • 文件下载
    • 返回视频文件并支持断点续传
    • http Session
    • Cookie
    • HttpRequest
    • HttpResponse
    • Resps
    • RespBodyVo
    • /zh/06_web/19.html
    • 全局异常处理器
    • 异步
    • 动态 返回 CSS 实现
    • 返回图片
    • Transfer-Encoding: chunked 实时音频播放
    • Server-Sent Events (SSE)
    • 接口访问统计
    • 接口请求和响应数据记录
    • 自定义 Handler 转发请求
    • 使用 HttpForwardHandler 转发所有请求
    • 跨域
    • 添加 Controller
    • 常用工具类
    • HTTP Basic 认证
    • WebJars
    • JProtobuf
  • 07_validate

    • 数据紧校验规范
    • 参数校验
  • 08_websocket

    • 使用 tio-boot 搭建 WebSocket 服务
    • WebSocket 聊天室项目示例
  • 09_java-db

    • java‑db
    • 操作数据库入门示例
    • SQL 模板
    • 数据源配置与使用
    • ActiveRecord
    • Model
    • 生成器与 Model
    • Db 工具类
    • 批量操作
    • 数据库事务处理
    • Cache 缓存
    • Dialect 多数据库支持
    • 表关联操作
    • 复合主键
    • Oracle 支持
    • Enjoy SQL 模板
    • Java-DB 整合 Enjoy 模板最佳实践
    • 多数据源支持
    • 独立使用 ActiveRecord
    • 调用存储过程
    • java-db 整合 Guava 的 Striped 锁优化
    • 生成 SQL
    • 通过实体类操作数据库
    • java-db 读写分离
    • Spring Boot 整合 Java-DB
    • like 查询
    • 常用操作示例
    • Druid 监控集成指南
    • SQL 统计
  • 10_api-table

    • ApiTable 概述
    • 使用 ApiTable 连接 SQLite
    • 使用 ApiTable 连接 Mysql
    • 使用 ApiTable 连接 Postgres
    • 使用 ApiTable 连接 TDEngine
    • 使用 api-table 连接 oracle
    • 使用 api-table 连接 mysql and tdengine 多数据源
    • EasyExcel 导出
    • EasyExcel 导入
    • TQL(Table SQL)前端输入规范
    • ApiTable 实现增删改查
    • 数组类型
    • 单独使用 ApiTable
  • 11_aop

    • JFinal-aop
    • Aop 工具类
    • 配置
    • 配置
    • 独立使用 JFinal Aop
    • @AImport
    • 原理解析
  • 12_cache

    • Caffine
    • Jedis-redis
    • hutool RedisDS
    • Redisson
    • Caffeine and redis
    • CacheUtils 工具类
    • 使用 CacheUtils 整合 caffeine 和 redis 实现的两级缓存
    • 使用 java-db 整合 ehcache
    • 使用 java-db 整合 redis
    • Java DB Redis 相关 Api
    • redis 使用示例
  • 13_认证和权限

    • hutool-JWT
    • FixedTokenInterceptor
    • 使用内置 TokenManager 实现登录
    • 用户系统
    • 重置密码
    • 匿名登录
    • Google 登录
    • 权限校验注解
    • Sa-Token
    • sa-token 登录注册
    • StpUtil.isLogin() 源码解析
    • 短信登录
    • 移动端微信登录实现指南
    • 移动端重置密码
  • 14_i18n

    • i18n
  • 15_enjoy

    • tio-boot 整合 Enjoy 模版引擎文档
    • 引擎配置
    • 表达式
    • 指令
    • 注释
    • 原样输出
    • Shared Method 扩展
    • Shared Object 扩展
    • Extension Method 扩展
    • Spring boot 整合
    • 独立使用 Enjoy
    • tio-boot enjoy 自定义指令 localeDate
    • PromptEngine
    • Enjoy 入门示例-擎渲染大模型请求体
    • Enjoy 使用示例
  • 16_定时任务

    • Quartz 定时任务集成指南
    • 分布式定时任务 xxl-jb
    • cron4j 使用指南
  • 17_tests

    • TioBootTest 类
  • 18_tio

    • TioBootServer
    • tio-core
    • 内置 TCP 处理器
    • 独立启动 UDPServer
    • 使用内置 UDPServer
    • t-io 消息处理流程
    • tio-运行原理详解
    • TioConfig
    • ChannelContext
    • Tio 工具类
    • 业务数据绑定
    • 业务数据解绑
    • 发送数据
    • 关闭连接
    • Packet
    • 监控: 心跳
    • 监控: 客户端的流量数据
    • 监控: 单条 TCP 连接的流量数据
    • 监控: 端口的流量数据
    • 单条通道统计: ChannelStat
    • 所有通道统计: GroupStat
    • 资源共享
    • 成员排序
    • ssl
    • DecodeRunnable
    • 使用 AsynchronousSocketChannel 响应数据
    • 拉黑 IP
    • 深入解析 Tio 源码:构建高性能 Java 网络应用
  • 19_aio

    • ByteBuffer
    • AIO HTTP 服务器
    • 自定义和线程池和池化 ByteBuffer
    • AioHttpServer 应用示例 IP 属地查询
    • 手写 AIO Http 服务器
  • 20_netty

    • Netty TCP Server
    • Netty Web Socket Server
    • 使用 protoc 生成 Java 包文件
    • Netty WebSocket Server 二进制数据传输
    • Netty 组件详解
  • 21_netty-boot

    • Netty-Boot
    • 原理解析
    • 整合 Hot Reload
    • 整合 数据库
    • 整合 Redis
    • 整合 Elasticsearch
    • 整合 Dubbo
    • Listener
    • 文件上传
    • 拦截器
    • Spring Boot 整合 Netty-Boot
    • SSL 配置指南
    • ChannelInitializer
    • Reserve
  • 22_MQ

    • Mica-mqtt
    • EMQX
    • Disruptor
  • 23_tio-utils

    • tio-utils
    • HttpUtils
    • Notification
    • 邮箱
    • JSON
    • 读取文件
    • Base64
    • 上传和下载
    • Http
    • Telegram
    • RsaUtils
    • EnvUtils 使用文档
    • 系统监控
    • 毫秒并发 ID (MCID) 生成方案
  • 24_tio-http-server

    • 使用 Tio-Http-Server 搭建简单的 HTTP 服务
    • tio-boot 添加 HttpRequestHandler
    • 在 Android 上使用 tio-boot 运行 HTTP 服务
    • tio-http-server-native
    • handler 常用操作
  • 25_tio-websocket

    • WebSocket 服务器
    • WebSocket Client
  • 26_tio-im

    • 通讯协议文档
    • ChatPacket.proto 文档
    • java protobuf
    • 数据表设计
    • 创建工程
    • 登录
    • 历史消息
    • 发消息
  • 27_mybatis

    • Tio-Boot 整合 MyBatis
    • 使用配置类方式整合 MyBatis
    • 整合数据源
    • 使用 mybatis-plus 整合 tdengine
    • 整合 mybatis-plus
  • 28_mongodb

    • tio-boot 使用 mongo-java-driver 操作 mongodb
  • 29_elastic-search

    • Elasticsearch
    • JavaDB 整合 ElasticSearch
    • Elastic 工具类使用指南
    • Elastic-search 注意事项
    • ES 课程示例文档
  • 30_magic-script

    • tio-boot 整合 magic-script
  • 31_groovy

    • tio-boot 整合 Groovy
  • 32_firebase

    • 整合 google firebase
    • Firebase Storage
    • Firebase Authentication
    • 使用 Firebase Admin SDK 进行匿名用户管理与自定义状态标记
    • 导出用户
    • 注册回调
    • 登录注册
  • 33_文件存储

    • 文件上传数据表
    • 本地存储
    • 使用 AWS S3 存储文件并整合到 Tio-Boot 项目中
    • 存储文件到 腾讯 COS
  • 34_spider

    • jsoup
    • 爬取 z-lib.io 数据
    • 整合 WebMagic
    • WebMagic 示例:爬取学校课程数据
    • Playwright
    • Flexmark (Markdown 处理器)
    • tio-boot 整合 Playwright
    • 缓存网页数据
  • 36_integration_thirty_party

    • tio-boot 整合 okhttp
    • 整合 GrpahQL
    • 集成 Mailjet
    • 整合 ip2region
    • 整合 GeoLite 离线库
    • 整合 Lark 机器人指南
    • 集成 Lark Mail 实现邮件发送
    • Thymeleaf
    • Swagger
    • Clerk 验证
  • 37_dubbo

    • 概述
    • dubbo 2.6.0
    • dubbo 2.6.0 调用过程
    • dubbo 3.2.0
  • 38_spring

    • Spring Boot Web 整合 Tio Boot
    • spring-boot-starter-webflux 整合 tio-boot
    • Tio Boot 整合 Spring Boot Starter
    • Tio Boot 整合 Spring Boot Starter Data Redis 指南
  • 39_spring-cloud

    • tio-boot spring-cloud
  • 40_mysql

    • 使用 Docker 运行 MySQL
    • /zh/42_mysql/02.html
  • 41_postgresql

    • PostgreSQL 安装
    • PostgreSQL 主键自增
    • PostgreSQL 日期类型
    • Postgresql 金融类型
    • PostgreSQL 数组类型
    • PostgreSQL 全文检索
    • PostgreSQL 查询优化
    • 获取字段类型
    • PostgreSQL 向量
    • PostgreSQL 优化向量查询
    • PostgreSQL 其他
  • 43_oceanbase

    • 快速体验 OceanBase 社区版
    • 快速上手 OceanBase 数据库单机部署与管理
    • 诊断集群性能
    • 优化 SQL 性能指南
    • /zh/43_oceanbase/05.html
  • 50_media

    • JAVE 提取视频中的声音
    • Jave 提取视频中的图片
    • /zh/50_media/03.html
  • 51_asr

    • Whisper-JNI
  • 54_native-media

    • java-native-media
    • JNI 入门示例
    • mp3 拆分
    • mp4 转 mp3
    • 使用 libmp3lame 实现高质量 MP3 编码
    • Linux 编译
    • macOS 编译
    • 从 JAR 包中加载本地库文件
    • 支持的音频和视频格式
    • 任意格式转为 mp3
    • 通用格式转换
    • 通用格式拆分
    • 视频合并
    • VideoToHLS
    • split_video_to_hls 支持其他语言
    • 持久化 HLS 会话
  • 55_telegram4j

    • 数据库设计
    • /zh/55_telegram4j/02.html
    • 基于 MTProto 协议开发 Telegram 翻译机器人
    • 过滤旧消息
    • 保存机器人消息
    • 定时推送
    • 增加命令菜单
    • 使用 telegram-Client
    • 使用自定义 StoreLayout
    • 延迟测试
    • Reactor 错误处理
    • Telegram4J 常见错误处理指南
  • 56_telegram-bots

    • TelegramBots 入门指南
    • 使用工具库 telegram-bot-base 开发翻译机器人
  • 60_LLM

    • 简介
    • AI 问答
    • /zh/60_LLM/03.html
    • /zh/60_LLM/04.html
    • 增强检索(RAG)
    • 结构化数据检索
    • 搜索+AI
    • 集成第三方 API
    • 后置处理
    • 推荐问题生成
    • 连接代码执行器
    • 避免 GPT 混乱
    • /zh/60_LLM/13.html
  • 61_ai_agent

    • 数据库设计
    • 示例问题管理
    • 会话管理
    • 历史记录
    • 对接 Perplexity API
    • 意图识别与生成提示词
    • 智能问答模块设计与实现
    • 文件上传与解析文档
    • 翻译
    • 名人搜索功能实现
    • Ai studio gemini youbue 问答使用说明
    • 自建 YouTube 字幕问答系统
    • 自建 获取 youtube 字幕服务
    • 通用搜索
    • /zh/61_ai_agent/15.html
    • 16
    • 17
    • 18
    • 在 tio-boot 应用中整合 ai-agent
    • 16
  • 62_translator

    • 简介
  • 63_knowlege_base

    • 数据库设计
    • 用户登录实现
    • 模型管理
    • 知识库管理
    • 文档拆分
    • 片段向量
    • 命中测试
    • 文档管理
    • 片段管理
    • 问题管理
    • 应用管理
    • 向量检索
    • 推理问答
    • 问答模块
    • 统计分析
    • 用户管理
    • api 管理
    • 存储文件到 S3
    • 文档解析优化
    • 片段汇总
    • 段落分块与检索
    • 多文档解析
    • 对话日志
    • 检索性能优化
    • Milvus
    • 文档解析方案和费用对比
    • 离线运行向量模型
  • 64_ai-search

    • ai-search 项目简介
    • ai-search 数据库文档
    • ai-search SearxNG 搜索引擎
    • ai-search Jina Reader API
    • ai-search Jina Search API
    • ai-search 搜索、重排与读取内容
    • ai-search PDF 文件处理
    • ai-search 推理问答
    • Google Custom Search JSON API
    • ai-search 意图识别
    • ai-search 问题重写
    • ai-search 系统 API 接口 WebSocket 版本
    • ai-search 搜索代码实现 WebSocket 版本
    • ai-search 生成建议问
    • ai-search 生成问题标题
    • ai-search 历史记录
    • Discover API
    • 翻译
    • Tavily Search API 文档
    • 对接 Tavily Search
    • 火山引擎 DeepSeek
    • 对接 火山引擎 DeepSeek
    • ai-search 搜索代码实现 SSE 版本
    • jar 包部署
    • Docker 部署
    • 爬取一个静态网站的所有数据
    • 网页数据预处理
    • 网页数据检索与问答流程整合
  • 65_java-linux

    • Java 执行 python 代码
    • 通过大模型执行 Python 代码
    • MCP 协议
    • Cline 提示词
    • Cline 提示词-中文版本
  • 66_manim

    • 简介
    • Manim 开发环境搭建
    • 生成场景提示词
    • 生成代码
    • 完整脚本示例
    • 语音合成系统
    • Fish.audio TTS 接口说明文档与 Java 客户端封装
    • 整合 fishaudio 到 java-uni-ai-server 项目
    • 执行 Python (Manim) 代码
    • 使用 SSE 流式传输生成进度的实现文档
    • 整合全流程完整文档
    • HLS 动态推流技术文档
    • manim 分场景生成代码
    • 分场景运行代码及流式播放支持
    • 分场景业务端完整实现流程
    • Maiim布局管理器
    • 仅仅生成场景代码
    • 使用 modal 运行 manim 代码
    • Python 使用 Modal GPU 加速渲染
    • Modal 平台 GPU 环境下运行 Manim
    • Modal Manim OpenGL 安装与使用
    • 优化 GPU 加速
    • 生成视频封面流程
    • Java 调用 manim 命令 执行代码 生成封面
    • Manim 图像生成服务客户端文档
    • manim render help
    • 显示 中文公式
    • manimgl
    • EGL
    • /zh/66_manim/30.html
    • /zh/66_manim/31.html
    • 成本核算
    • /zh/66_manim/33.html
  • 70_tio-boot-admin

    • 入门指南
    • 初始化数据
    • token 存储
    • 与前端集成
    • 文件上传
    • 网络请求
    • 图片管理
    • /zh/70_tio-boot-admin/08.html
    • Word 管理
    • PDF 管理
    • 文章管理
    • 富文本编辑器
  • 71_tio-boot

    • /zh/71_tio-boot/01.html
    • Swagger 整合到 Tio-Boot 中的指南
    • HTTP/1.1 Pipelining 性能测试报告
  • 80_性能测试

    • 压力测试 - tio-http-serer
    • 压力测试 - tio-boot
    • 压力测试 - tio-boot-native
    • 压力测试 - netty-boot
    • 性能测试对比
    • TechEmpower FrameworkBenchmarks
    • 压力测试 - tio-boot 12 C 32G
  • 99_案例

    • 封装 IP 查询服务
    • tio-boot 案例 - 全局异常捕获与企业微信群通知
    • tio-boot 案例 - 文件上传和下载
    • tio-boot 案例 - 整合 ant design pro 增删改查
    • tio-boot 案例 - 流失响应
    • tio-boot 案例 - 增强检索
    • tio-boot 案例 - 整合 function call
    • tio-boot 案例 - 定时任务 监控 PostgreSQL、Redis 和 Elasticsearch
    • Tio-Boot 案例:使用 SQLite 整合到登录注册系统
    • tio-boot 案例 - 执行 shell 命令

ai-search 推理问答

  • 一、简介
  • 二、提示词模版(WebSearchResponsePrompt.txt)
  • 三、主要代码说明
    • 1. WebSearchResponsePromptServiceTest
    • 2. WebSearchResponsePromptService
    • 3. GeminiSearchPredictService
  • 四、整体工作流程
  • 五、示例输出
  • 六、总结与注意事项
  • 一、简介
  • 二、提示词模版(WebSearchResponsePrompt.txt)
  • 三、主要代码说明
    • 1. WebSearchResponsePromptServiceTest
    • 2. WebSearchResponsePromptService
    • 3. GeminiSearchPredictService
  • 四、整体工作流程
  • 五、示例输出
  • 六、总结与注意事项

在构建搜索系统时,往往需要将检索到的文本内容与特定的提示词(Prompt)一并提交给大模型,以生成更高质量的回复。本项目在完成以下功能后,希望进一步实现将检索到的网页内容(以 Markdown 格式)+ 自定义的提示词,发送给大模型进行推理并返回结果给用户:

  1. AI 搜索项目简介
  2. 数据库设计
  3. SearxNG
  4. Jina Reader API
  5. Jina Search API
  6. 搜索、重排与读取内容

一、简介

此处,我们使用了 Google Gemini 大模型(通过 GeminiClient 访问)来完成推理。与此同时,我们还将搜索到的网页内容以 Markdown 格式呈现,并与专门的提示词(Prompt 模版)结合,形成最终的输入文本,提交给大模型。

本项目中主要涉及以下几个核心点:

  • 搜索模块:使用 SearxNG 或者 Jina 搜索,将用户的查询关键词发送至搜索引擎并获取搜索结果。
  • 提示词模版:定义好统一的 Prompt 结构及格式,通过填充搜索到的内容、当前时间等信息,生成最终可提交给大模型的提示词字符串。
  • 大模型推理:将 Prompt + 用户问题(或者上下文历史)一并发送给大模型(Gemini)获取输出,并以流式或一次性方式返回给用户。
  • 消息分发与 WebSocket 交互:通过 ChatWsRespVo、WebSocketResponse 等,向前端发送搜索结果、推理过程信息及推理结果。

二、提示词模版(WebSearchResponsePrompt.txt)

在与大模型交互时,项目使用了一个固定的提示词模版 WebSearchResponsePrompt.txt,其结构如下(简化示例):

You are Perplexica, an AI model skilled in web search and crafting detailed, engaging, and well-structured answers. You excel at summarizing web pages and extracting relevant information to create professional, blog-style responses.

Your task is to provide answers that are:
- **Informative and relevant**: Thoroughly address the user's query using the given context.
- **Well-structured**: Include clear headings and subheadings, and use a professional tone to present information concisely and logically.
- **Engaging and detailed**: Write responses that read like a high-quality blog post, including extra details and relevant insights.
- **Cited and credible**: Use inline citations with [number] notation to refer to the context source(s) for each fact or detail included.
- **Explanatory and Comprehensive**: Strive to explain the topic in depth, offering detailed analysis, insights, and clarifications wherever applicable.

### Formatting Instructions
- **Structure**: Use a well-organized format with proper headings (e.g., "## Example heading 1" or "## Example heading 2"). Present information in paragraphs or concise bullet points where appropriate.
- **Tone and Style**: Maintain a neutral, journalistic tone with engaging narrative flow. Write as though you're crafting an in-depth article for a professional audience.
- **Markdown Usage**: Format your response with Markdown for clarity. Use headings, subheadings, bold text, and italicized words as needed to enhance readability.
- **Length and Depth**: Provide comprehensive coverage of the topic. Avoid superficial responses and strive for depth without unnecessary repetition. Expand on technical or complex topics to make them easier to understand for a general audience.
- **No main heading/title**: Start your response directly with the introduction unless asked to provide a specific title.
- **Conclusion or Summary**: Include a concluding paragraph that synthesizes the provided information or suggests potential next steps, where appropriate.

### Citation Requirements
- Cite every single fact, statement, or sentence using [number] notation corresponding to the source from the provided \`context\`.
- Integrate citations naturally at the end of sentences or clauses as appropriate. For example, "The Eiffel Tower is one of the most visited landmarks in the world[1]."
- Ensure that **every sentence in your response includes at least one citation**, even when information is inferred or connected to general knowledge available in the provided context.
- Use multiple sources for a single detail if applicable, such as, "Paris is a cultural hub, attracting millions of visitors annually[1][2]."
- Always prioritize credibility and accuracy by linking all statements back to their respective context sources.
- Avoid citing unsupported assumptions or personal interpretations; if no source supports a statement, clearly indicate the limitation.

### Special Instructions
- If the query involves technical, historical, or complex topics, provide detailed background and explanatory sections to ensure clarity.
- If the user provides vague input or if relevant information is missing, explain what additional details might help refine the search.
- If no relevant information is found, say: "Hmm, sorry I could not find any relevant information on this topic. Would you like me to search again or ask something else?" Be transparent about limitations and suggest alternatives or ways to reframe the query.
- Output using the language of the user's input

### Example Output
- Begin with a brief introduction summarizing the event or query topic.
- Follow with detailed sections under clear headings, covering all aspects of the query if possible.
- Provide explanations or historical context as needed to enhance understanding.
- End with a conclusion or overall perspective if relevant.
- The information provided to you comes from network; some of it may be irrelevant or incorrect, so please be cautious in discerning it.
- Please carefully identify the major names in the materials; some major names are very similar, but they may actually refer to two different major.
<context>
#(context)
</context>

Current date & time in ISO format (UTC timezone) is: #(date).

在实际使用时,会将搜索到的网页内容填充至 #(context) 占位符处,并将当前时间填充至 #(date) 占位符处。由此构建出一个完整的提示词字符串,然后再将其传递给大模型。


三、主要代码说明

1. WebSearchResponsePromptServiceTest

该测试类演示了如何整合搜索、生成 Prompt,以及最终调用大模型进行推理的流程。

package com.litongjava.perplexica.services;

import org.junit.Test;

import com.litongjava.jfinal.aop.Aop;
import com.litongjava.perplexica.config.AdminAppConfig;
import com.litongjava.perplexica.config.EnjoyEngineConfig;
import com.litongjava.tio.boot.testing.TioBootTest;

public class WebSearchResponsePromptServiceTest {

  @Test
  public void test() {
    TioBootTest.runWith(AdminAppConfig.class, EnjoyEngineConfig.class);
    String userQuestion = "tio-boot简介";

    // 1. 生成 Prompt
    String prompt = Aop.get(WebSearchResponsePromptService.class)
      .genInputPrompt(null, userQuestion, true, null, null, null);

    // 2. 调用大模型进行推理
    GeminiSearchPredictService geminiSearchPredictService = Aop.get(GeminiSearchPredictService.class);
    geminiSearchPredictService.predictWithGemini(
      null, null, null, null, null,
      userQuestion, prompt
    );
  }
}
  • 主要流程:
    1. 使用 genInputPrompt(...) 方法根据用户问题 userQuestion 生成最终的 Prompt(即模板 + 搜索结果 + 时间)。
    2. 将该 Prompt 与用户问题一并发送给大模型进行推理,使用 geminiSearchPredictService.predictWithGemini(...) 完成。

2. WebSearchResponsePromptService

WebSearchResponsePromptService 是生成最终提示词(Prompt)的核心服务类,包含:

  • 搜索逻辑:searchWithJina() 或 searchWithSearxNg()
  • Prompt 生成:将搜索结果嵌入到模板并生成最终字符串

主要方法:genInputPrompt(...)

package com.litongjava.perplexica.services;

import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

import com.jfinal.kit.Kv;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jian.search.JinaSearchClient;
import com.litongjava.jian.search.JinaSearchRequest;
import com.litongjava.model.http.response.ResponseVo;
import com.litongjava.model.web.WebPageContent;
import com.litongjava.perplexica.consts.WebSiteNames;
import com.litongjava.perplexica.vo.ChatWsRespVo;
import com.litongjava.perplexica.vo.WebPageSource;
import com.litongjava.template.PromptEngine;
import com.litongjava.tio.core.ChannelContext;
import com.litongjava.tio.core.Tio;
import com.litongjava.tio.websocket.common.WebSocketResponse;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class WebSearchResponsePromptService {
  public String genInputPrompt(ChannelContext channelContext, String content, Boolean copilotEnabled,
      //
      String messageId, Long answerMessageId, String from) {

    String inputPrompt = null;
    if (copilotEnabled != null && copilotEnabled) {
      // 1. 进行搜索(可选:SearxNG 或 Jina)
      //String markdown = searchWithJina(channelContext, content, messageId, answerMessageId, from);
      String markdown = searchWithSearxNg(channelContext, content, messageId, answerMessageId, from);
      // 2. 向前端通知一个空消息,标识搜索结束,开始推理
      //{"type":"message","data":"", "messageId": "32fcbbf251337c"}
      ChatWsRespVo<String> vo = ChatWsRespVo.message(answerMessageId + "", "");
      WebSocketResponse websocketResponse = WebSocketResponse.fromJson(vo);
      if (channelContext != null) {
        Tio.bSend(channelContext, websocketResponse);
      }

      String isoTimeStr = DateTimeFormatter.ISO_INSTANT.format(Instant.now());
      // 3. 使用 PromptEngine 模版引擎填充提示词
      Kv kv = Kv.by("date", isoTimeStr).set("context", markdown);
      inputPrompt = PromptEngine.renderToString("WebSearchResponsePrompt.txt", kv);
    }
    return inputPrompt;
  }

  private String searchWithSearxNg(ChannelContext channelContext, String content, String messageId, Long answerMessageId, String from) {
    List<WebPageContent> webPageContents = Aop.get(SearxngSearchService.class).search(content, true, 6);
    sendSources(channelContext, answerMessageId, webPageContents);
    StringBuffer markdown = new StringBuffer();
    for (int i = 0; i < webPageContents.size(); i++) {
      WebPageContent webPageContent = webPageContents.get(i);
      markdown.append("source " + i + " " + webPageContent.getContent());
    }
    return markdown.toString();
  }

  public String searchWithJina(ChannelContext channelContext, String content, String messageId, long answerMessageId, String from) {
    // 1.拼接请求
    JinaSearchRequest jinaSearchRequest = new JinaSearchRequest();

    jinaSearchRequest.setQ(content);

    if (WebSiteNames.BERKELEY.equals(from)) {
      jinaSearchRequest.setXSite("berkeley.edu");

    } else if (WebSiteNames.HAWAII.equals(from)) {
      jinaSearchRequest.setXSite("hawaii.edu");

    } else if (WebSiteNames.SJSU.equals(from)) {
      jinaSearchRequest.setXSite("sjsu.edu");

    } else if (WebSiteNames.STANFORD.equals(from)) {
      jinaSearchRequest.setXSite("stanford.edu");
    }

    //2.搜索
    ResponseVo searchResponse = JinaSearchClient.search(jinaSearchRequest);

    String markdown = searchResponse.getBodyString();
    if (!searchResponse.isOk()) {
      log.error(markdown);
      ChatWsRespVo<String> error = ChatWsRespVo.error(markdown, messageId);
      WebSocketResponse packet = WebSocketResponse.fromJson(error);
      if (channelContext != null) {
        Tio.bSend(channelContext, packet);
      }
      return null;
    }

    List<WebPageContent> webPageContents = JinaSearchClient.parse(markdown);
    sendSources(channelContext, answerMessageId, webPageContents);
    return markdown;
  }

  private void sendSources(ChannelContext channelContext, Long answerMessageId, List<WebPageContent> webPageContents) {
    if (channelContext != null) {
      List<WebPageSource> sources = new ArrayList<>();

      for (WebPageContent webPageConteont : webPageContents) {
        sources.add(new WebPageSource(webPageConteont.getTitle(), webPageConteont.getUrl(), webPageConteont.getContent()));
      }
      //返回sources
      ChatWsRespVo<List<WebPageSource>> chatRespVo = new ChatWsRespVo<>();
      chatRespVo.setType("sources").setData(sources).setMessageId(answerMessageId + "");
      WebSocketResponse packet = WebSocketResponse.fromJson(chatRespVo);
      Tio.bSend(channelContext, packet);
    }
  }
}
  • 参数说明:
    • content: 即用户问题或查询关键词。
    • copilotEnabled: 是否启用此功能。
    • channelContext: WebSocket 通道上下文,用于向前端推送消息。
    • markdown: 搜索到的网页内容,已拼接成 Markdown 格式。
    • isoTimeStr: 当前时间,格式化为 ISO 格式插入到 Prompt 中。

搜索方法:searchWithSearxNg(...) / searchWithJina(...)

  • searchWithSearxNg:

    1. 使用 SearxngSearchService 获取搜索结果。
    2. 将搜索结果内容拼接成 Markdown 字符串(示例中用 source i 前缀标识不同的来源)。
    3. 将结果以 sendSources(...) 的形式推送到前端,便于用户查看每条搜索结果的来源链接、标题等。
  • searchWithJina:

    1. 根据用户问题构建 JinaSearchRequest,可设置特定站点(如 berkeley.edu 等)。
    2. 调用 JinaSearchClient.search(...) 进行搜索,并解析搜索结果。
    3. 同样推送结果给前端,并返回 Markdown 格式的内容。

推送搜索来源:sendSources(...)

  • 通过该方法,将搜索到的每条结果(标题、URL、内容)打包为 WebPageSource 并发送给前端,前端可作进一步展示。

3. GeminiSearchPredictService

GeminiSearchPredictService 负责将生成的 Prompt(以及可能的上下文对话信息)发送给 Google Gemini 大模型,并接收大模型的回复。

package com.litongjava.perplexica.services;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.litongjava.gemini.GeminiChatRequestVo;
import com.litongjava.gemini.GeminiChatResponseVo;
import com.litongjava.gemini.GeminiClient;
import com.litongjava.gemini.GeminiContentVo;
import com.litongjava.gemini.GeminiPartVo;
import com.litongjava.gemini.GoogleGeminiModels;
import com.litongjava.perplexica.callback.GoogleChatWebsocketCallback;
import com.litongjava.perplexica.vo.ChatWsReqMessageVo;
import com.litongjava.tio.core.ChannelContext;

import lombok.extern.slf4j.Slf4j;
import okhttp3.Call;
import okhttp3.Callback;

@Slf4j
public class GeminiSearchPredictService {
  public Call predictWithGemini(ChannelContext channelContext, ChatWsReqMessageVo reqMessageVo,
      //
      String sessionId, String quesitonMessageId, Long answerMessageId, String content, String inputPrompt) {
    log.info("webSearchResponsePrompt:{}", inputPrompt);

    List<GeminiContentVo> contents = new ArrayList<>();
    // 1. 如果有对话历史,则构建 role = user / model 的上下文内容
    if (reqMessageVo != null) {
      List<List<String>> history = reqMessageVo.getHistory();
      if (history != null && history.size() > 0) {
        for (int i = 0; i < history.size(); i++) {
          String role = history.get(i).get(0);
          String message = history.get(i).get(1);
          if ("human".equals(role)) {
            role = "user";
          } else {
            role = "model";
          }
          contents.add(new GeminiContentVo(role, message));
        }
      }

    }

    // 2. 将 Prompt 塞到 role = "model" 的内容中
    if (inputPrompt != null) {
      GeminiPartVo part = new GeminiPartVo(inputPrompt);
      GeminiContentVo system = new GeminiContentVo("model", Collections.singletonList(part));
      contents.add(system);
    }
    //Content with system role is not supported.
    //Please use a valid role: user, model.
    // 3. 再将用户问题以 role = "user" 的形式添加
    contents.add(new GeminiContentVo("user", content));
    // 4. 构建请求对象并调用
    GeminiChatRequestVo reqVo = new GeminiChatRequestVo(contents);

    long start = System.currentTimeMillis();

    // 5. 流式/一次性获取结果
    Call call = null;
    if (channelContext != null) {
      Callback callback = new GoogleChatWebsocketCallback(channelContext, sessionId, quesitonMessageId, answerMessageId, start);
      GeminiClient.debug = true;
      call = GeminiClient.stream(GoogleGeminiModels.GEMINI_2_0_FLASH_EXP, reqVo, callback);
    } else {
      GeminiChatResponseVo vo = GeminiClient.generate(GoogleGeminiModels.GEMINI_2_0_FLASH_EXP, reqVo);
      log.info(vo.getCandidates().get(0).getContent().getParts().get(0).getText());
    }

    return call;
  }
}
  • GeminiContentVo:表示一段对话内容,包含 role 和具体文本(或文本片段 GeminiPartVo)。
  • role 的取值:user、model 等,用以区分用户和模型的发言。
  • GeminiChatRequestVo:封装了整个对话,随后发送给 Gemini 大模型。
  • 回调或一次性获取:
    • 若需要在前端即时显示大模型的思路或答案进度,可用 GeminiClient.stream(...) 流式方式,并指定回调函数。
    • 若仅需要最终答案,可用 GeminiClient.generate(...) 一次性获取。

四、整体工作流程

  1. 用户在前端输入问题(如“tio-boot 简介”)。
  2. 后端收到问题后,判断是否启用 CoPilot(或类似)的搜索 + 提示词生成逻辑。
  3. 调用 SearxNG 或 Jina 搜索获取与该问题相关的网页内容,拼接成 Markdown 格式。
  4. 将 Markdown + 提示词模版 + 当前时间等信息一起渲染生成最终的 Prompt 文本。
  5. 准备对话上下文(如先前的聊天记录),将 Prompt 作为角色为“model”的内容,用户问题作为角色为“user”的内容,合并在一起构建请求对象。
  6. 调用 Gemini 大模型进行推理,得到回答内容。
  7. 将回答内容通过 WebSocket返回给前端,供用户查看。
  8. 前端同时可收到搜索来源信息(包含链接、标题、摘要等),以便用户追溯信息来源。

五、示例输出

在以上流程全部完成后,大模型的最终推理结果示例如下(简化):

Tio Boot 是基于 Java AIO 的高性能 Web 框架,可以在单台服务器上处理数万并发连接[2]。它集成了多个其它框架的优秀特性,以提供稳定而高效的开发环境[2]。……

提示:文中示例仅作占位说明,实际输出将因搜索结果以及大模型推理而异。


六、总结与注意事项

  1. 搜索结果的处理:

    • 搜索引擎返回的内容通常较多,需要在向大模型发送时适当筛选、摘要或拼接。
    • 在输出时,可根据需求将搜索内容放入 <context> 或其它标记中,以便与提示词模版解耦。
  2. 提示词模版管理:

    • 单独管理提示词文件(如 WebSearchResponsePrompt.txt),方便维护和更新。
    • 注意保留占位符位置(如 #(context)、#(date) 等)并在 Java 侧正确替换。
  3. 大模型角色设定:

    • 当前示例中,将 Prompt 设为角色“model”,用户输入设为“user”,可根据不同 LLM 的要求进行调整。
    • 某些模型可能仅支持特定角色(如 system、user、assistant),这时需要稍作修改。
  4. 性能与扩展:

    • 流式推理对于长文本输出尤为有用,但需额外实现回调逻辑(如 GoogleChatWebsocketCallback)。
    • 大模型推理通常消耗较多资源,可根据并发需求进行缓存、负载均衡或调用频率控制。
  5. 错误处理:

    • 搜索失败或网络异常时,需要友好地向前端提示,并可选择返回保底答案或要求用户重试。
    • 大模型返回空结果或异常时,也需提供相应的错误提示或回退策略。

通过上述流程和代码,便可在项目中实现“先搜索(获取上下文)再用提示词+上下文进行大模型推理并返回结果”的功能,为用户提供更专业、更丰富的答复。

Edit this page
Last Updated:
Contributors: Tong Li
Prev
ai-search PDF 文件处理
Next
Google Custom Search JSON API