Created
February 25, 2023 10:34
-
-
Save kishida/1f6a61679dc3c9b4ecf2514aef81c02c to your computer and use it in GitHub Desktop.
Clever chat bot than ChatGPT(overblown)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package naoki.openai; | |
import java.io.IOException; | |
import java.io.UncheckedIOException; | |
import java.util.Map; | |
import org.jsoup.Jsoup; | |
import org.jsoup.nodes.Element; | |
public class ArticleFinder { | |
private record Score(int vol, double score, Element elm){ | |
double rate() { | |
if (vol == 0) return 0; | |
return score * score / vol; | |
} | |
} | |
private static final double RATE = 0.7; | |
private static final Map<String, Double> customRate = Map.of("p", 1., "li", 0.3); | |
private Score longest; | |
private ArticleFinder() { | |
} | |
/** | |
* @see https://chiral.hatenablog.com/entry/20111114/1321293238 | |
*/ | |
private Element findLongest(Element elm) { | |
longest = null; | |
calc(elm); | |
return longest == null ? null : longest.elm(); | |
} | |
private Score calc(Element elm) { | |
int textlen = elm.ownText().length(); | |
double score = textlen; | |
for (Element child : elm.children()) { | |
var chScore = calc(child); | |
textlen += chScore.vol(); | |
score += chScore.score() * customRate.getOrDefault(child.tagName().toLowerCase(), RATE); | |
} | |
var result = new Score(textlen, score, elm); | |
if (longest == null || longest.rate() < result.rate()) { | |
longest = result; | |
} | |
return result; | |
} | |
public static String findArticle(String url) { | |
try { | |
var doc = Jsoup.connect(url).get(); | |
var body = doc.body(); | |
var elm = findArticleElement(body); | |
return elm == null ? "" : elm.text(); | |
} catch (IOException ex) { | |
throw new UncheckedIOException(ex); | |
} | |
} | |
public static Element findArticleElement(Element elm) { | |
var fa = new ArticleFinder(); | |
return fa.findLongest(elm); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package naoki.openai; | |
import com.fasterxml.jackson.databind.DeserializationFeature; | |
import com.fasterxml.jackson.databind.ObjectMapper; | |
import java.io.IOException; | |
import java.io.UncheckedIOException; | |
import java.net.URL; | |
import java.net.URLEncoder; | |
import java.util.List; | |
public class GoogleSearch { | |
static String getApiKey() { | |
String key = System.getenv("GOOGLE_API_KEY"); | |
if (key == null) { | |
throw new IllegalArgumentException("GOOGLE_API_KEY not found"); | |
} | |
return key.trim(); | |
} | |
static String getCx() { | |
String cx = System.getenv("GOOGLE_SEARCH_CX"); | |
if (cx == null) { | |
throw new IllegalArgumentException("GOOGLE_SEARCH_CX not found"); | |
} | |
return cx.trim(); | |
} | |
record SearchItem(String kind, String title, String htmlTitle, String link) {} | |
record SearchUrl(String type, String template){} | |
record SearchResult(String kind, SearchUrl url, List<SearchItem> items){} | |
static SearchItem search(String keyword) { | |
try { | |
var om = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); | |
var url = "https://www.googleapis.com/customsearch/v1?key=%s&cx=%s&q=%s".formatted( | |
getApiKey(), getCx(), URLEncoder.encode(keyword, "utf-8")); | |
SearchResult result = om.readValue(new URL(url), SearchResult.class); | |
return result.items().get(0); | |
} catch(IOException ex) { | |
throw new UncheckedIOException(ex); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
<modelVersion>4.0.0</modelVersion> | |
<groupId>naoki</groupId> | |
<artifactId>aisearch</artifactId> | |
<version>1.0</version> | |
<packaging>jar</packaging> | |
<dependencies> | |
<dependency> | |
<groupId>com.theokanning.openai-gpt3-java</groupId> | |
<artifactId>service</artifactId> | |
<version>0.10.0</version> | |
</dependency> | |
<dependency> | |
<groupId>org.jsoup</groupId> | |
<artifactId>jsoup</artifactId> | |
<version>1.15.3</version> | |
</dependency> | |
<dependency> | |
<groupId>com.fasterxml.jackson.core</groupId> | |
<artifactId>jackson-databind</artifactId> | |
<version>2.14.2</version> | |
</dependency> | |
</dependencies> | |
<properties> | |
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
<maven.compiler.source>19</maven.compiler.source> | |
<maven.compiler.target>19</maven.compiler.target> | |
<exec.mainClass>naoki.mavenproject1openai.Mavenproject1openai</exec.mainClass> | |
</properties> | |
</project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package naoki.openai; | |
import com.theokanning.openai.service.OpenAiService; | |
import com.theokanning.openai.completion.CompletionRequest; | |
import com.theokanning.openai.completion.CompletionResult; | |
import com.theokanning.openai.edit.EditRequest; | |
import com.theokanning.openai.edit.EditResult; | |
import java.awt.BorderLayout; | |
import java.time.Duration; | |
import javax.swing.JButton; | |
import javax.swing.JFrame; | |
import javax.swing.JPanel; | |
import javax.swing.JScrollPane; | |
import javax.swing.JTextArea; | |
import javax.swing.JTextField; | |
import static naoki.openai.Mavenproject1openai.getToken; | |
public class SearchAI { | |
static String getToken() { | |
var token = System.getenv("OPENAI_TOKEN"); | |
if (token == null) { | |
throw new IllegalArgumentException("no token env for OPENAI_TOKEN"); | |
} | |
return token; | |
} | |
static void out(JTextArea ta, String format, Object... params) { | |
var t = format.formatted(params); | |
System.out.print(t); | |
ta.append(t); | |
} | |
static void search(OpenAiService service, String text, JTextArea ta) { | |
// 検索キーワードを得る | |
var editReq = EditRequest.builder().input(text) | |
.instruction("質問に答えるためのWeb検索キーワードに変換") | |
.temperature(0.4) | |
.model("text-davinci-edit-001").build(); | |
EditResult keyResult = service.createEdit(editReq); | |
var keyword = keyResult.getChoices().get(0).getText().trim(); | |
out(ta, "[%s]%n", keyword); | |
// 検索を行う | |
var item = GoogleSearch.search(keyword); | |
out(ta, "%s%n%s%n", item.title(), item.link()); | |
// 検索トップのサイトのテキストを得る | |
var article = ArticleFinder.findArticle(item.link()); | |
out(ta, "%d characters in the article%n", article.length()); | |
// 要約する | |
var cArticle = article.substring(0, Math.min(article.length(), 1800)); | |
CompletionRequest completionRequest = CompletionRequest.builder() | |
.model("text-davinci-003") // davinci-003, curry, babbage, ada | |
.prompt("「%s」という質問に答えるよう要約してください\n\n%s\n".formatted(text, cArticle)) | |
.echo(false) | |
.temperature(0.5) | |
.user("testing") | |
.maxTokens(500) | |
.build(); | |
CompletionResult result = service.createCompletion(completionRequest); | |
out(ta, "%s%n", result.getChoices().get(0).getText()); | |
} | |
public static void main(String[] args) { | |
var f = new JFrame("AI検索"); | |
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | |
f.setSize(800, 600); | |
var p = new JPanel(new BorderLayout()); | |
f.add(p, BorderLayout.NORTH); | |
var t = new JTextField(30); | |
p.add(t); | |
var b = new JButton("検索"); | |
p.add(b, BorderLayout.EAST); | |
var out = new JTextArea(); | |
out.setLineWrap(true); | |
f.add(new JScrollPane(out)); | |
f.setVisible(true); | |
OpenAiService service = new OpenAiService(getToken(), Duration.ZERO); | |
b.addActionListener(al -> { | |
b.setEnabled(false); | |
new Thread(() -> {try { | |
search(service, t.getText(), out); | |
} finally { | |
b.setEnabled(true); | |
}}).start(); | |
}); | |
} | |
} |
Author
kishida
commented
Feb 25, 2023
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment