moritalous commited on
Commit
2787ed9
·
verified ·
1 Parent(s): 12a4393

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +199 -0
  2. requirements.txt +3 -0
app.py ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # https://github.com/anthropics/anthropic-cookbook/blob/main/third_party/Brave/web_search_using_brave.ipynb
2
+ import asyncio
3
+ import html
4
+ import json
5
+ import os
6
+ from typing import List
7
+
8
+ import boto3
9
+ import requests
10
+ import streamlit as st
11
+ from googleapiclient.discovery import build
12
+
13
+ st.title("Qiitaに聞いた!!")
14
+
15
+ if "client" not in st.session_state:
16
+ st.session_state.client = boto3.client("bedrock-runtime")
17
+ client = st.session_state.client
18
+
19
+
20
+ # 検索クエリを生成する関数
21
+ def generate_search_queries(question: str) -> List[str]:
22
+ """
23
+ Google 検索エンジン用の検索クエリを生成する
24
+ """
25
+ GENERATE_QUERIES = """
26
+ User question: {{question}}
27
+
28
+ Format: {"queries": ["query_1", "query_2", "query_3"]}
29
+ """
30
+
31
+ response = client.invoke_model(
32
+ modelId="anthropic.claude-3-haiku-20240307-v1:0",
33
+ body=json.dumps(
34
+ {
35
+ "anthropic_version": "bedrock-2023-05-31",
36
+ "max_tokens": 1024,
37
+ "system": "You are an expert at generating search queries for the Google search engine. Generate two search queries that are relevant to this question in Japanese. Output only valid JSON.",
38
+ "messages": [
39
+ {
40
+ "role": "user",
41
+ "content": [
42
+ {
43
+ "type": "text",
44
+ "text": GENERATE_QUERIES.replace(
45
+ "{{question}}", question
46
+ ),
47
+ }
48
+ ],
49
+ },
50
+ ],
51
+ "temperature": 0,
52
+ }
53
+ ),
54
+ )
55
+
56
+ result = json.loads(response.get("body").read())
57
+ search_queries = result["content"][0]["text"]
58
+ search_queries = json.loads(search_queries)
59
+
60
+ return search_queries
61
+
62
+
63
+ # Qiitaを検索する関数
64
+ def search_qiita(search_query: str) -> list:
65
+ """
66
+ 指定された検索クエリでQiitaを検索する
67
+ """
68
+ service = build("customsearch", "v1", developerKey=os.environ.get("GOOGLE_API_KEY"))
69
+ cse = service.cse()
70
+ res = cse.list(
71
+ q=f"{search_query} site:qiita.com",
72
+ cx=os.environ.get("GOOGLE_CSE_ID"),
73
+ num=3,
74
+ ).execute()
75
+
76
+ documents = list(
77
+ map(
78
+ lambda x: {
79
+ "title": x["title"],
80
+ "link": x["link"],
81
+ "snippet": x["snippet"],
82
+ },
83
+ res["items"],
84
+ )
85
+ )
86
+
87
+ return documents
88
+
89
+
90
+ # 検索結果にマークダウンを追加する非同期関数
91
+ async def add_markdown(search_result: dict) -> dict:
92
+ """
93
+ 検索結果にマークダウンを追加する
94
+ """
95
+ url = search_result["link"]
96
+ response = requests.get(f"{url}.md")
97
+ markdown = response.text
98
+ search_result["markdown"] = html.escape(markdown)
99
+
100
+ return search_result
101
+
102
+
103
+ # 検索結果をXML形式のドキュメントに変換する関数
104
+ def create_xml_documents(documents: list) -> str:
105
+ """
106
+ 検索結果をXML形式のドキュメントに変換する
107
+ """
108
+ xml_documents = ""
109
+ xml_doc = list(
110
+ map(
111
+ lambda x: f'<doc title="{x["title"]}"><link>{x["link"]}</link><markdown>{x["markdown"]}</markdown></doc>',
112
+ documents,
113
+ )
114
+ )
115
+ xml_documents = f"<documents>{''.join(xml_doc)}</documents>"
116
+ return xml_documents
117
+
118
+
119
+ # 質問に対する回答を生成する関数
120
+ def generate_answer(question: str, documents: dict):
121
+ """
122
+ 検索結果から質問に対する回答を生成する
123
+ """
124
+ xml_docs = create_xml_documents(documents=documents)
125
+
126
+ ANSWER_QUESTION = f"""I have provided you with the following search results:
127
+ {xml_docs}
128
+
129
+ Please answer the user's question using only information from the search results.
130
+ Keep your answer concise.
131
+ Answer is olways in Japanese!
132
+
133
+ User's question: {question}
134
+ """
135
+
136
+ response = client.invoke_model(
137
+ modelId="anthropic.claude-3-haiku-20240307-v1:0",
138
+ body=json.dumps(
139
+ {
140
+ "anthropic_version": "bedrock-2023-05-31",
141
+ "max_tokens": 1024,
142
+ "messages": [
143
+ {
144
+ "role": "user",
145
+ "content": [
146
+ {
147
+ "type": "text",
148
+ "text": ANSWER_QUESTION,
149
+ }
150
+ ],
151
+ },
152
+ ],
153
+ "temperature": 0.1,
154
+ }
155
+ ),
156
+ )
157
+
158
+ result = json.loads(response.get("body").read())
159
+ return result["content"][0]["text"]
160
+
161
+
162
+ # メイン関数
163
+ async def main():
164
+ with st.form("Form"):
165
+ question = st.text_input("質問")
166
+
167
+ if st.form_submit_button("質問する"):
168
+ with st.status("処理中...", expanded=True) as status:
169
+ search_queries = generate_search_queries(question=question)
170
+ st.write("検索クエリ: " + str(search_queries["queries"]))
171
+
172
+ documents = []
173
+ for search_query in search_queries["queries"]:
174
+ search_results = search_qiita(search_query=search_query)
175
+ result = await asyncio.gather(
176
+ *[add_markdown(x) for x in search_results]
177
+ )
178
+ documents.extend(result)
179
+ st.write("検索完了")
180
+
181
+ st.write("回答生成中...")
182
+ answer = generate_answer(question=question, documents=documents)
183
+
184
+ status.update(label="complete!", state="complete", expanded=False)
185
+
186
+ st.markdown(answer)
187
+
188
+ st.divider()
189
+
190
+ st.markdown("参照ドキュメント")
191
+
192
+ for document in documents:
193
+ st.markdown(
194
+ f'[{document["title"]}]({document["link"]}) by {document["link"].split("/")[3]}'
195
+ )
196
+
197
+
198
+ if __name__ == "__main__":
199
+ asyncio.run(main())
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ boto3
2
+ streamlit
3
+ google-api-python-client